schnorrkel/
derive.rs

1// -*- mode: rust; -*-
2//
3// This file is part of schnorrkel.
4// Copyright (c) 2019 Web 3 Foundation
5// See LICENSE for licensing information.
6//
7// Authors:
8// - Jeffrey Burdges <jeff@web3.foundation>
9
10//! ### Implementation of "hierarchical deterministic key derivation" (HDKD) for Schnorr signatures on Ristretto
11//!
12//! *Warning*  We warn that our VRF construction in vrf.rs supports
13//! malleable VRF outputs via the `Malleable` type, which becomes
14//! insecure when used in conjunction with our hierarchical key
15//! derivation methods here.
16//! Attackers could translate malleable VRF outputs from one soft subkey
17//! to another soft subkey, gaining early knowledge of the VRF output.
18//! We think most VRF applications for which HDKH sounds suitable
19//! benefit from using implicit certificates instead of HDKD anyways,
20//! which should also be secure in combination with HDKH.
21//! We always use non-malleable VRF inputs in our convenience methods.
22
23//! We suggest using implicit certificates instead of HDKD when
24//! using VRFs.
25//!
26//!
27
28// use curve25519_dalek::digest::generic_array::typenum::U64;
29// use curve25519_dalek::digest::Digest;
30
31use curve25519_dalek::constants;
32use curve25519_dalek::scalar::Scalar;
33
34use super::*;
35use crate::context::{SigningTranscript};
36
37/// Length in bytes of our chain codes.
38///
39/// In fact, only 16 bytes sounds safe, but this never appears on chain,
40/// so no downsides to using 32 bytes.
41pub const CHAIN_CODE_LENGTH: usize = 32;
42
43/// We cannot assume the original public key is secret and additional
44/// inputs might have low entropy, like `i` in BIP32.  As in BIP32,
45/// chain codes fill this gap by being a high entropy secret shared
46/// between public and private key holders.  These are produced by
47/// key derivations and can be incorporated into subsequence key
48/// derivations.
49/// See https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys
50#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
51pub struct ChainCode(pub [u8; CHAIN_CODE_LENGTH]);
52
53/// Key types that support "hierarchical deterministic" key derivation
54pub trait Derivation: Sized {
55    /// Derive key with subkey identified by a byte array
56    /// presented via a `SigningTranscript`, and a chain code.
57    fn derived_key<T>(&self, t: T, cc: ChainCode) -> (Self, ChainCode)
58    where
59        T: SigningTranscript;
60
61    /// Derive key with subkey identified by a byte array
62    /// and a chain code.  We do not include a context here
63    /// because the chain code could serve this purpose.
64    fn derived_key_simple<B: AsRef<[u8]>>(&self, cc: ChainCode, i: B) -> (Self, ChainCode) {
65        let mut t = merlin::Transcript::new(b"SchnorrRistrettoHDKD");
66        t.append_message(b"sign-bytes", i.as_ref());
67        self.derived_key(t, cc)
68    }
69
70    /// Derive key with subkey identified by a byte array
71    /// and a chain code, and with external randomnesses.
72    fn derived_key_simple_rng<B, R>(&self, cc: ChainCode, i: B, rng: R) -> (Self, ChainCode)
73    where
74        B: AsRef<[u8]>,
75        R: RngCore + CryptoRng,
76    {
77        let mut t = merlin::Transcript::new(b"SchnorrRistrettoHDKD");
78        t.append_message(b"sign-bytes", i.as_ref());
79        self.derived_key(super::context::attach_rng(t, rng), cc)
80    }
81}
82
83impl PublicKey {
84    /// Derive a mutating scalar and new chain code from a public key and chain code.
85    ///
86    /// If `i` is the "index", `c` is the chain code, and `pk` the public key,
87    /// then we compute `H(i ++ c ++ pk)` and define our mutating scalar
88    /// to be the 512 bits of output reduced mod l, and define the next chain
89    /// code to be next 256 bits.  
90    ///
91    /// We update the signing transcript as a side effect.
92    fn derive_scalar_and_chaincode<T>(&self, t: &mut T, cc: ChainCode) -> (Scalar, ChainCode)
93    where
94        T: SigningTranscript,
95    {
96        t.commit_bytes(b"chain-code", &cc.0);
97        t.commit_point(b"public-key", self.as_compressed());
98
99        let scalar = t.challenge_scalar(b"HDKD-scalar");
100
101        let mut chaincode = [0u8; 32];
102        t.challenge_bytes(b"HDKD-chaincode", &mut chaincode);
103
104        (scalar, ChainCode(chaincode))
105    }
106}
107
108impl SecretKey {
109    /// Vaguely BIP32-like "hard" derivation of a `MiniSecretKey` from a `SecretKey`
110    ///
111    /// We do not envision any "good reasons" why these "hard"
112    /// derivations should ever be used after the soft `Derivation`
113    /// trait.  We similarly do not believe hard derivations
114    /// make any sense for `ChainCode`s or `ExtendedKey`s types.
115    /// Yet, some existing BIP32 workflows might do these things,
116    /// due to BIP32's de facto standardization and poor design.
117    /// In consequence, we provide this method to do "hard" derivations
118    /// in a way that should work with all BIP32 workflows and any
119    /// permissible mutations of `SecretKey`.  This means only that
120    /// we hash the `SecretKey`'s scalar, but not its nonce because
121    /// the secret key remains valid if the nonce is changed.
122    pub fn hard_derive_mini_secret_key<B: AsRef<[u8]>>(
123        &self,
124        cc: Option<ChainCode>,
125        i: B,
126    ) -> (MiniSecretKey, ChainCode) {
127        let mut t = merlin::Transcript::new(b"SchnorrRistrettoHDKD");
128        t.append_message(b"sign-bytes", i.as_ref());
129
130        if let Some(c) = cc {
131            t.append_message(b"chain-code", &c.0);
132        }
133        t.append_message(b"secret-key", &self.key.to_bytes() as &[u8]);
134
135        let mut msk = [0u8; MINI_SECRET_KEY_LENGTH];
136        t.challenge_bytes(b"HDKD-hard", &mut msk);
137
138        let mut chaincode = [0u8; 32];
139        t.challenge_bytes(b"HDKD-chaincode", &mut chaincode);
140
141        (MiniSecretKey(msk), ChainCode(chaincode))
142    }
143}
144
145impl MiniSecretKey {
146    /// Vaguely BIP32-like "hard" derivation of a `MiniSecretKey` from a `SecretKey`
147    ///
148    /// We do not envision any "good reasons" why these "hard"
149    /// derivations should ever be used after the soft `Derivation`
150    /// trait.  We similarly do not believe hard derivations
151    /// make any sense for `ChainCode`s or `ExtendedKey`s types.
152    /// Yet, some existing BIP32 workflows might do these things,
153    /// due to BIP32's de facto standardization and poor design.
154    /// In consequence, we provide this method to do "hard" derivations
155    /// in a way that should work with all BIP32 workflows and any
156    /// permissible mutations of `SecretKey`.  This means only that
157    /// we hash the `SecretKey`'s scalar, but not its nonce because
158    /// the secret key remains valid if the nonce is changed.
159    pub fn hard_derive_mini_secret_key<B: AsRef<[u8]>>(
160        &self,
161        cc: Option<ChainCode>,
162        i: B,
163        mode: ExpansionMode,
164    ) -> (MiniSecretKey, ChainCode) {
165        self.expand(mode).hard_derive_mini_secret_key(cc, i)
166    }
167}
168
169impl Keypair {
170    /// Vaguely BIP32-like "hard" derivation of a `MiniSecretKey` from a `SecretKey`
171    ///
172    /// We do not envision any "good reasons" why these "hard"
173    /// derivations should ever be used after the soft `Derivation`
174    /// trait.  We similarly do not believe hard derivations
175    /// make any sense for `ChainCode`s or `ExtendedKey`s types.
176    /// Yet, some existing BIP32 workflows might do these things,
177    /// due to BIP32's de facto standardization and poor design.
178    /// In consequence, we provide this method to do "hard" derivations
179    /// in a way that should work with all BIP32 workflows and any
180    /// permissible mutations of `SecretKey`.  This means only that
181    /// we hash the `SecretKey`'s scalar, but not its nonce because
182    /// the secret key remains valid if the nonce is changed.
183    pub fn hard_derive_mini_secret_key<B: AsRef<[u8]>>(
184        &self,
185        cc: Option<ChainCode>,
186        i: B,
187    ) -> (MiniSecretKey, ChainCode) {
188        self.secret.hard_derive_mini_secret_key(cc, i)
189    }
190
191    /// Derive a secret key and new chain code from a key pair and chain code.
192    ///
193    /// We expect the trait methods of `Keypair as Derivation` to be
194    /// more useful since signing anything requires the public key too.
195    pub fn derive_secret_key<T>(&self, mut t: T, cc: ChainCode) -> (SecretKey, ChainCode)
196    where
197        T: SigningTranscript,
198    {
199        let (scalar, chaincode) = self.public.derive_scalar_and_chaincode(&mut t, cc);
200
201        // We can define the nonce however we like here since it only protects
202        // the signature from bad random number generators.  It need not be
203        // specified by any specification or standard.  It must however be
204        // independent from the mutating scalar and new chain code.
205        // We employ the witness mechanism here so that CSPRNG associated to our
206        // `SigningTranscript` makes our new nonce seed independent from everything.
207        let mut nonce = [0u8; 32];
208        t.witness_bytes(
209            b"HDKD-nonce",
210            &mut nonce,
211            &[&self.secret.nonce, &self.secret.to_bytes() as &[u8]],
212        );
213
214        (SecretKey { key: self.secret.key + scalar, nonce }, chaincode)
215    }
216}
217
218impl Derivation for Keypair {
219    fn derived_key<T>(&self, t: T, cc: ChainCode) -> (Keypair, ChainCode)
220    where
221        T: SigningTranscript,
222    {
223        let (secret, chaincode) = self.derive_secret_key(t, cc);
224        let public = secret.to_public();
225        (Keypair { secret, public }, chaincode)
226    }
227}
228
229impl Derivation for SecretKey {
230    fn derived_key<T>(&self, t: T, cc: ChainCode) -> (SecretKey, ChainCode)
231    where
232        T: SigningTranscript,
233    {
234        self.clone().to_keypair().derive_secret_key(t, cc)
235    }
236}
237
238impl Derivation for PublicKey {
239    fn derived_key<T>(&self, mut t: T, cc: ChainCode) -> (PublicKey, ChainCode)
240    where
241        T: SigningTranscript,
242    {
243        let (scalar, chaincode) = self.derive_scalar_and_chaincode(&mut t, cc);
244        let point = self.as_point() + (&scalar * constants::RISTRETTO_BASEPOINT_TABLE);
245        (PublicKey::from_point(point), chaincode)
246    }
247}
248
249/// A convenience wraper that combines derivable key and a chain code.
250#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
251pub struct ExtendedKey<K> {
252    /// Appropriate key type
253    pub key: K,
254    /// We cannot assume the original public key is secret and additional
255    /// inputs might have low entropy, like `i` in BIP32.  As in BIP32,
256    /// chain codes fill this gap by being a high entropy secret shared
257    /// between public and private key holders.  These are produced by
258    /// key derivations and can be incorporated into subsequence key
259    /// derivations.  
260    pub chaincode: ChainCode,
261}
262// TODO: Serialization
263
264impl<K: Derivation> ExtendedKey<K> {
265    /// Derive key with subkey identified by a byte array
266    /// presented as a hash, and a chain code.
267    pub fn derived_key<T>(&self, t: T) -> ExtendedKey<K>
268    where
269        T: SigningTranscript,
270    {
271        let (key, chaincode) = self.key.derived_key(t, self.chaincode);
272        ExtendedKey { key, chaincode }
273    }
274
275    /// Derive key with subkey identified by a byte array and
276    /// a chain code in the extended key.
277    pub fn derived_key_simple<B: AsRef<[u8]>>(&self, i: B) -> ExtendedKey<K> {
278        let (key, chaincode) = self.key.derived_key_simple(self.chaincode, i);
279        ExtendedKey { key, chaincode }
280    }
281}
282
283impl ExtendedKey<SecretKey> {
284    /// Vaguely BIP32-like "hard" derivation of a `MiniSecretKey` from a `SecretKey`
285    ///
286    /// We do not envision any "good reasons" why these "hard"
287    /// derivations should ever be used after the soft `Derivation`
288    /// trait.  We similarly do not believe hard derivations
289    /// make any sense for `ChainCode`s or `ExtendedKey`s types.
290    /// Yet, some existing BIP32 workflows might do these things,
291    /// due to BIP32's de facto standardization and poor design.
292    /// In consequence, we provide this method to do "hard" derivations
293    /// in a way that should work with all BIP32 workflows and any
294    /// permissible mutations of `SecretKey`.  This means only that
295    /// we hash the `SecretKey`'s scalar, but not its nonce because
296    /// the secret key remains valid if the nonce is changed.
297    pub fn hard_derive_mini_secret_key<B: AsRef<[u8]>>(
298        &self,
299        i: B,
300        mode: ExpansionMode,
301    ) -> ExtendedKey<SecretKey> {
302        let (key, chaincode) = self.key.hard_derive_mini_secret_key(Some(self.chaincode), i);
303        let key = key.expand(mode);
304        ExtendedKey { key, chaincode }
305    }
306}
307
308#[cfg(test)]
309mod tests {
310    use sha3::digest::{Update}; // ExtendableOutput,XofReader
311    use sha3::{Shake128};
312
313    use super::*;
314
315    #[cfg(feature = "getrandom")]
316    #[test]
317    fn derive_key_public_vs_private_paths() {
318        let chaincode = ChainCode([0u8; CHAIN_CODE_LENGTH]);
319        let msg: &'static [u8] = b"Just some test message!";
320        let mut h = Shake128::default().chain(msg);
321
322        let mut csprng = rand_core::OsRng;
323        let key = Keypair::generate_with(&mut csprng);
324
325        let mut extended_public_key = ExtendedKey { key: key.public.clone(), chaincode };
326        let mut extended_keypair = ExtendedKey { key, chaincode };
327
328        let ctx = signing_context(b"testing testing 1 2 3");
329
330        for i in 0..30 {
331            let extended_keypair1 = extended_keypair.derived_key_simple(msg);
332            let extended_public_key1 = extended_public_key.derived_key_simple(msg);
333            assert_eq!(
334                extended_keypair1.chaincode, extended_public_key1.chaincode,
335                "Chain code derivation failed!"
336            );
337            assert_eq!(
338                extended_keypair1.key.public, extended_public_key1.key,
339                "Public and secret key derivation missmatch!"
340            );
341            extended_keypair = extended_keypair1;
342            extended_public_key = extended_public_key1;
343
344            h.update(b"Another");
345
346            if i % 5 == 0 {
347                let good_sig = extended_keypair.key.sign(ctx.xof(h.clone()));
348                let h_bad = h.clone().chain(b"oops");
349                let bad_sig = extended_keypair.key.sign(ctx.xof(h_bad.clone()));
350
351                assert!(
352                    extended_public_key.key.verify(ctx.xof(h.clone()), &good_sig).is_ok(),
353                    "Verification of a valid signature failed!"
354                );
355                assert!(
356                    !extended_public_key.key.verify(ctx.xof(h.clone()), &bad_sig).is_ok(),
357                    "Verification of a signature on a different message passed!"
358                );
359                assert!(
360                    !extended_public_key.key.verify(ctx.xof(h_bad), &good_sig).is_ok(),
361                    "Verification of a signature on a different message passed!"
362                );
363            }
364        }
365    }
366}