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 T: SigningTranscript;
59
60    /// Derive key with subkey identified by a byte array 
61    /// and a chain code.  We do not include a context here
62    /// because the chain code could serve this purpose.
63    fn derived_key_simple<B: AsRef<[u8]>>(&self, cc: ChainCode, i: B) -> (Self, ChainCode) {
64        let mut t = merlin::Transcript::new(b"SchnorrRistrettoHDKD");
65        t.append_message(b"sign-bytes", i.as_ref());
66        self.derived_key(t, cc)
67    }
68
69    /// Derive key with subkey identified by a byte array
70    /// and a chain code, and with external randomnesses.
71    fn derived_key_simple_rng<B,R>(&self, cc: ChainCode, i: B, rng: R) -> (Self, ChainCode)
72    where B: AsRef<[u8]>, R: RngCore+CryptoRng
73    {
74        let mut t = merlin::Transcript::new(b"SchnorrRistrettoHDKD");
75        t.append_message(b"sign-bytes", i.as_ref());
76        self.derived_key(super::context::attach_rng(t,rng), cc)
77    }
78}
79
80impl PublicKey {
81    /// Derive a mutating scalar and new chain code from a public key and chain code.
82    ///
83    /// If `i` is the "index", `c` is the chain code, and `pk` the public key,
84    /// then we compute `H(i ++ c ++ pk)` and define our mutating scalar
85    /// to be the 512 bits of output reduced mod l, and define the next chain
86    /// code to be next 256 bits.  
87    ///
88    /// We update the signing transcript as a side effect.
89    fn derive_scalar_and_chaincode<T>(&self, t: &mut T, cc: ChainCode) -> (Scalar, ChainCode)
90    where T: SigningTranscript
91    {
92        t.commit_bytes(b"chain-code",&cc.0);
93        t.commit_point(b"public-key",self.as_compressed());
94
95        let scalar = t.challenge_scalar(b"HDKD-scalar");
96
97        let mut chaincode = [0u8; 32];
98        t.challenge_bytes(b"HDKD-chaincode", &mut chaincode);
99
100        (scalar, ChainCode(chaincode))
101    }
102}
103
104impl SecretKey {
105    /// Vaguely BIP32-like "hard" derivation of a `MiniSecretKey` from a `SecretKey`
106    ///
107    /// We do not envision any "good reasons" why these "hard"
108    /// derivations should ever be used after the soft `Derivation`
109    /// trait.  We similarly do not believe hard derivations
110    /// make any sense for `ChainCode`s or `ExtendedKey`s types.
111    /// Yet, some existing BIP32 workflows might do these things,
112    /// due to BIP32's de facto standardization and poor design.
113    /// In consequence, we provide this method to do "hard" derivations
114    /// in a way that should work with all BIP32 workflows and any
115    /// permissible mutations of `SecretKey`.  This means only that
116    /// we hash the `SecretKey`'s scalar, but not its nonce because
117    /// the secret key remains valid if the nonce is changed.
118    pub fn hard_derive_mini_secret_key<B: AsRef<[u8]>>(&self, cc: Option<ChainCode>, i: B)
119     -> (MiniSecretKey,ChainCode)
120    {
121        let mut t = merlin::Transcript::new(b"SchnorrRistrettoHDKD");
122        t.append_message(b"sign-bytes", i.as_ref());
123
124        if let Some(c) = cc { t.append_message(b"chain-code", &c.0); }
125        t.append_message(b"secret-key",& self.key.to_bytes() as &[u8]);
126
127        let mut msk = [0u8; MINI_SECRET_KEY_LENGTH]; 
128        t.challenge_bytes(b"HDKD-hard",&mut msk);
129
130        let mut chaincode = [0u8; 32];
131        t.challenge_bytes(b"HDKD-chaincode", &mut chaincode);
132
133        (MiniSecretKey(msk), ChainCode(chaincode))
134    }
135}
136
137impl MiniSecretKey {
138    /// Vaguely BIP32-like "hard" derivation of a `MiniSecretKey` from a `SecretKey`
139    ///
140    /// We do not envision any "good reasons" why these "hard"
141    /// derivations should ever be used after the soft `Derivation`
142    /// trait.  We similarly do not believe hard derivations
143    /// make any sense for `ChainCode`s or `ExtendedKey`s types.
144    /// Yet, some existing BIP32 workflows might do these things,
145    /// due to BIP32's de facto standardization and poor design.
146    /// In consequence, we provide this method to do "hard" derivations
147    /// in a way that should work with all BIP32 workflows and any
148    /// permissible mutations of `SecretKey`.  This means only that
149    /// we hash the `SecretKey`'s scalar, but not its nonce because
150    /// the secret key remains valid if the nonce is changed.
151    pub fn hard_derive_mini_secret_key<B: AsRef<[u8]>>(&self, cc: Option<ChainCode>, i: B, mode: ExpansionMode)
152     -> (MiniSecretKey,ChainCode)
153    {
154        self.expand(mode).hard_derive_mini_secret_key(cc,i)
155    }
156}
157
158impl Keypair {
159    /// Vaguely BIP32-like "hard" derivation of a `MiniSecretKey` from a `SecretKey`
160    ///
161    /// We do not envision any "good reasons" why these "hard"
162    /// derivations should ever be used after the soft `Derivation`
163    /// trait.  We similarly do not believe hard derivations
164    /// make any sense for `ChainCode`s or `ExtendedKey`s types.
165    /// Yet, some existing BIP32 workflows might do these things,
166    /// due to BIP32's de facto standardization and poor design.
167    /// In consequence, we provide this method to do "hard" derivations
168    /// in a way that should work with all BIP32 workflows and any
169    /// permissible mutations of `SecretKey`.  This means only that
170    /// we hash the `SecretKey`'s scalar, but not its nonce because
171    /// the secret key remains valid if the nonce is changed.
172    pub fn hard_derive_mini_secret_key<B: AsRef<[u8]>>(&self, cc: Option<ChainCode>, i: B)
173     -> (MiniSecretKey,ChainCode) {
174        self.secret.hard_derive_mini_secret_key(cc,i)
175    }
176
177    /// Derive a secret key and new chain code from a key pair and chain code.
178    ///
179    /// We expect the trait methods of `Keypair as Derivation` to be
180    /// more useful since signing anything requires the public key too.
181    pub fn derive_secret_key<T>(&self, mut t: T, cc: ChainCode) -> (SecretKey, ChainCode)
182    where T: SigningTranscript
183    {
184        let (scalar, chaincode) = self.public.derive_scalar_and_chaincode(&mut t, cc);
185
186        // We can define the nonce however we like here since it only protects
187        // the signature from bad random number generators.  It need not be
188        // specified by any specification or standard.  It must however be
189        // independent from the mutating scalar and new chain code.
190        // We employ the witness mechanism here so that CSPRNG associated to our
191        // `SigningTranscript` makes our new nonce seed independent from everything.
192        let mut nonce = [0u8; 32];
193        t.witness_bytes(b"HDKD-nonce", &mut nonce, &[&self.secret.nonce, &self.secret.to_bytes() as &[u8]]);
194
195        (SecretKey {
196            key: self.secret.key + scalar,
197            nonce,
198        }, chaincode)
199    }
200}
201
202impl Derivation for Keypair {
203    fn derived_key<T>(&self, t: T, cc: ChainCode) -> (Keypair, ChainCode)
204    where T: SigningTranscript
205    {
206        let (secret, chaincode) = self.derive_secret_key(t, cc);
207        let public = secret.to_public();
208        (Keypair { secret, public }, chaincode)
209    }
210}
211
212impl Derivation for SecretKey {
213    fn derived_key<T>(&self, t: T, cc: ChainCode) -> (SecretKey, ChainCode)
214    where T: SigningTranscript
215    {
216        self.clone().to_keypair().derive_secret_key(t, cc)
217    }
218}
219
220impl Derivation for PublicKey {
221    fn derived_key<T>(&self, mut t: T, cc: ChainCode) -> (PublicKey, ChainCode)
222    where T: SigningTranscript
223    {
224        let (scalar, chaincode) = self.derive_scalar_and_chaincode(&mut t, cc);
225        let point = self.as_point() + (&scalar * constants::RISTRETTO_BASEPOINT_TABLE);
226        (PublicKey::from_point(point), chaincode)
227    }
228}
229
230/// A convenience wraper that combines derivable key and a chain code.
231#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
232pub struct ExtendedKey<K> {
233    /// Appropriate key type
234    pub key: K,
235    /// We cannot assume the original public key is secret and additional
236    /// inputs might have low entropy, like `i` in BIP32.  As in BIP32,
237    /// chain codes fill this gap by being a high entropy secret shared
238    /// between public and private key holders.  These are produced by
239    /// key derivations and can be incorporated into subsequence key
240    /// derivations.  
241    pub chaincode: ChainCode,
242}
243// TODO: Serialization
244
245impl<K: Derivation> ExtendedKey<K> {
246    /// Derive key with subkey identified by a byte array
247    /// presented as a hash, and a chain code.
248    pub fn derived_key<T>(&self, t: T) -> ExtendedKey<K>
249    where T: SigningTranscript
250    {
251        let (key, chaincode) = self.key.derived_key(t, self.chaincode);
252        ExtendedKey { key, chaincode }
253    }
254
255    /// Derive key with subkey identified by a byte array and 
256    /// a chain code in the extended key.
257    pub fn derived_key_simple<B: AsRef<[u8]>>(&self, i: B) -> ExtendedKey<K>
258    {
259        let (key, chaincode) = self.key.derived_key_simple(self.chaincode, i);
260        ExtendedKey { key, chaincode }
261    }
262}
263
264impl ExtendedKey<SecretKey> {
265    /// Vaguely BIP32-like "hard" derivation of a `MiniSecretKey` from a `SecretKey`
266    ///
267    /// We do not envision any "good reasons" why these "hard"
268    /// derivations should ever be used after the soft `Derivation`
269    /// trait.  We similarly do not believe hard derivations
270    /// make any sense for `ChainCode`s or `ExtendedKey`s types.
271    /// Yet, some existing BIP32 workflows might do these things,
272    /// due to BIP32's de facto standardization and poor design.
273    /// In consequence, we provide this method to do "hard" derivations
274    /// in a way that should work with all BIP32 workflows and any
275    /// permissible mutations of `SecretKey`.  This means only that
276    /// we hash the `SecretKey`'s scalar, but not its nonce because
277    /// the secret key remains valid if the nonce is changed.
278    pub fn hard_derive_mini_secret_key<B: AsRef<[u8]>>(&self, i: B, mode: ExpansionMode)
279     -> ExtendedKey<SecretKey> 
280     {
281        let (key,chaincode) = self.key.hard_derive_mini_secret_key(Some(self.chaincode), i);
282        let key = key.expand(mode);
283        ExtendedKey { key, chaincode }
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    use sha3::digest::{Update}; // ExtendableOutput,XofReader
290    use sha3::{Shake128};
291
292    use super::*;
293
294    #[cfg(feature = "getrandom")]
295    #[test]
296    fn derive_key_public_vs_private_paths() {
297        let chaincode = ChainCode([0u8; CHAIN_CODE_LENGTH]);
298        let msg : &'static [u8] = b"Just some test message!";
299        let mut h = Shake128::default().chain(msg);
300
301        let mut csprng = rand_core::OsRng;
302        let key = Keypair::generate_with(&mut csprng);
303
304        let mut extended_public_key = ExtendedKey {
305            key: key.public.clone(),
306            chaincode,
307        };
308        let mut extended_keypair = ExtendedKey { key, chaincode, };
309
310        let ctx = signing_context(b"testing testing 1 2 3");
311
312        for i in 0..30 {
313            let extended_keypair1 = extended_keypair.derived_key_simple(msg);
314            let extended_public_key1 = extended_public_key.derived_key_simple(msg);
315            assert_eq!(
316                extended_keypair1.chaincode, extended_public_key1.chaincode,
317                "Chain code derivation failed!"
318            );
319            assert_eq!(
320                extended_keypair1.key.public, extended_public_key1.key,
321                "Public and secret key derivation missmatch!"
322            );
323            extended_keypair = extended_keypair1;
324            extended_public_key = extended_public_key1;
325
326            h.update(b"Another");
327
328            if i % 5 == 0 {
329                let good_sig = extended_keypair.key.sign(ctx.xof(h.clone()));
330                let h_bad = h.clone().chain(b"oops");
331                let bad_sig = extended_keypair.key.sign(ctx.xof(h_bad.clone()));
332
333                assert!(
334                    extended_public_key.key.verify(ctx.xof(h.clone()), &good_sig).is_ok(),
335                    "Verification of a valid signature failed!"
336                );
337                assert!(
338                    ! extended_public_key.key.verify(ctx.xof(h.clone()), &bad_sig).is_ok(),
339                    "Verification of a signature on a different message passed!"
340                );
341                assert!(
342                    ! extended_public_key.key.verify(ctx.xof(h_bad), &good_sig).is_ok(),
343                    "Verification of a signature on a different message passed!"
344                );
345            }
346        }
347    }
348}