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}