1use crate::{
78 bases::b58::ToBase58,
79 hashs::{Hash, Hash64},
80 keys::{inner::KeyPairInner, KeyPair as KeyPairTrait, PublicKey as _},
81 mnemonic::Mnemonic,
82 seeds::Seed32,
83 utils::U31,
84};
85use arrayvec::ArrayVec;
86use std::fmt::Display;
87use thiserror::Error;
88use zeroize::Zeroize;
89
90const CHAIN_CODE_SIZE: usize = 32;
91const EXTENDED_SECRET_KEY_SIZE: usize = 64;
92
93pub type ChainCode = [u8; 32];
95
96#[derive(Clone, Copy, Debug, Error)]
97#[error("The account index is not compatible with the account type.")]
98pub struct InvalidAccountIndex;
100
101#[derive(Clone, Copy, Debug, Error)]
102pub enum PublicDerivationError {
104 #[error("Invalid addition")]
106 InvalidAddition,
107 #[error("Expected soft derivation")]
109 ExpectedSoftDerivation,
110}
111
112impl From<ed25519_bip32::DerivationError> for PublicDerivationError {
113 fn from(e: ed25519_bip32::DerivationError) -> Self {
114 match e {
115 ed25519_bip32::DerivationError::InvalidAddition => Self::InvalidAddition,
116 ed25519_bip32::DerivationError::ExpectedSoftDerivation => Self::ExpectedSoftDerivation,
117 }
118 }
119}
120
121#[derive(Clone, Copy, Debug, Eq, PartialEq)]
122struct DerivationIndex(u32);
124
125impl From<DerivationIndex> for u32 {
126 fn from(val: DerivationIndex) -> Self {
127 val.0
128 }
129}
130
131impl DerivationIndex {
132 pub const HARD_ZERO: Self = DerivationIndex(0x80000000);
134 pub const HARD_ONE: Self = DerivationIndex(0x80000001);
136
137 fn hard(index: U31) -> Self {
139 Self(index.into_u32() | 0x80000000)
140 }
141 fn soft(index: U31) -> Self {
143 Self(index.into_u32())
144 }
145}
146
147#[derive(Clone, Debug)]
148pub struct PrivateDerivationPath(ArrayVec<DerivationIndex, 3>);
150
151impl PrivateDerivationPath {
152 pub fn transparent(account_index: U31) -> Result<Self, InvalidAccountIndex> {
154 if account_index.into_u32() % 3 == 0 {
155 let mut avec = ArrayVec::new();
156 avec.push(DerivationIndex::hard(account_index));
157 Ok(Self(avec))
158 } else {
159 Err(InvalidAccountIndex)
160 }
161 }
162 pub fn semi_opaque_internal(
164 account_index: U31,
165 address_index_opt: Option<U31>,
166 ) -> Result<Self, InvalidAccountIndex> {
167 if account_index.into_u32() % 3 == 1 {
168 let mut avec = ArrayVec::new();
169 avec.push(DerivationIndex::hard(account_index));
170 avec.push(DerivationIndex::HARD_ONE);
171 if let Some(address_index) = address_index_opt {
172 avec.push(DerivationIndex::soft(address_index))
173 }
174 Ok(Self(avec))
175 } else {
176 Err(InvalidAccountIndex)
177 }
178 }
179 pub fn semi_opaque_external(account_index: U31) -> Result<Self, InvalidAccountIndex> {
181 if account_index.into_u32() % 3 == 1 {
182 let mut avec = ArrayVec::new();
183 avec.push(DerivationIndex::hard(account_index));
184 avec.push(DerivationIndex::HARD_ZERO);
185 Ok(Self(avec))
186 } else {
187 Err(InvalidAccountIndex)
188 }
189 }
190 pub fn opaque(
192 account_index: U31,
193 external: bool,
194 address_index_opt: Option<U31>,
195 ) -> Result<Self, InvalidAccountIndex> {
196 if account_index.into_u32() % 3 == 2 {
197 let mut avec = ArrayVec::new();
198 avec.push(DerivationIndex::hard(account_index));
199 if external {
200 avec.push(DerivationIndex::HARD_ZERO);
201 } else {
202 avec.push(DerivationIndex::HARD_ONE);
203 }
204 if let Some(address_index) = address_index_opt {
205 avec.push(DerivationIndex::soft(address_index))
206 }
207 Ok(Self(avec))
208 } else {
209 Err(InvalidAccountIndex)
210 }
211 }
212 fn into_iter(self) -> impl Iterator<Item = DerivationIndex> {
213 self.0.into_iter()
214 }
215}
216
217#[derive(Clone, Copy, Debug, PartialEq)]
220pub struct PublicKeyWithChainCode {
221 pub public_key: super::PublicKey,
223 pub chain_code: ChainCode,
225}
226
227impl PublicKeyWithChainCode {
228 pub fn derive(&self, derivation_index: U31) -> Result<Self, PublicDerivationError> {
235 let xpub =
236 ed25519_bip32::XPub::from_pk_and_chaincode(&self.public_key.datas, &self.chain_code);
237 let xpub_derived = xpub.derive(
238 ed25519_bip32::DerivationScheme::V2,
239 derivation_index.into_u32(),
240 )?;
241 Ok(Self {
242 public_key: super::PublicKey::from_32_bytes_array(xpub_derived.public_key()),
243 chain_code: xpub_derived.chain_code(),
244 })
245 }
246}
247
248#[derive(Clone, Debug, PartialEq, Eq, Zeroize)]
250#[zeroize(drop)]
251pub struct KeyPair {
252 chain_code: [u8; CHAIN_CODE_SIZE],
253 extended_secret_key: [u8; EXTENDED_SECRET_KEY_SIZE],
254}
255
256impl Display for KeyPair {
257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258 write!(f, "({}, hidden)", self.public_key().to_base58())
259 }
260}
261
262impl KeyPair {
263 pub fn chain_code(&self) -> ChainCode {
265 self.chain_code
266 }
267 pub fn derive(&self, derivation_path: PrivateDerivationPath) -> Self {
269 let mut kp = self.to_owned();
270 for derivation_index in derivation_path.into_iter() {
271 kp = kp.derive_inner(derivation_index);
272 }
273 kp
274 }
275 pub fn from_mnemonic(mnemonic: &Mnemonic) -> Self {
277 Self::from_seed(crate::mnemonic::mnemonic_to_seed(&mnemonic))
278 }
279 fn derive_inner(&self, derivation_index: DerivationIndex) -> Self {
280 let xprv = ed25519_bip32::XPrv::from_extended_and_chaincode(
281 &self.extended_secret_key,
282 &self.chain_code,
283 );
284 let xprv_derived =
285 xprv.derive(ed25519_bip32::DerivationScheme::V2, derivation_index.into());
286 Self {
287 chain_code: xprv_derived.chain_code(),
288 extended_secret_key: xprv_derived.extended_secret_key(),
289 }
290 }
291}
292
293impl KeyPairInner for KeyPair {
294 fn scalar_bytes_without_normalization(&self) -> [u8; 32] {
295 let mut scalar_bytes = [0; 32];
296 scalar_bytes.copy_from_slice(&self.extended_secret_key[..32]);
297 scalar_bytes
298 }
299}
300
301impl KeyPairTrait for KeyPair {
302 type Seed = Seed32;
303 type Signator = Signator;
304
305 fn generate_signator(&self) -> Self::Signator {
306 Signator {
307 extended_secret_key: self.extended_secret_key,
308 }
309 }
310
311 fn from_seed(seed: Seed32) -> Self {
313 let digest = Hash64::sha512(seed.as_ref());
314 let mut extended_secret_key = [0u8; EXTENDED_SECRET_KEY_SIZE];
315 extended_secret_key.copy_from_slice(digest.as_ref());
316 normalize_bytes_ed25519_force3rd(&mut extended_secret_key);
317
318 Self {
319 chain_code: gen_root_chain_code(&seed),
320 extended_secret_key,
321 }
322 }
323
324 fn public_key(&self) -> super::PublicKey {
325 super::PublicKey::from_32_bytes_array(cryptoxide::ed25519::to_public(
326 &self.extended_secret_key,
327 ))
328 }
329
330 fn verify(
331 &self,
332 message: &[u8],
333 signature: &super::Signature,
334 ) -> Result<(), crate::keys::SigError> {
335 self.public_key().verify(message, signature)
336 }
337
338 fn upcast(self) -> super::super::KeyPairEnum {
339 super::super::KeyPairEnum::Bip32Ed25519(self)
340 }
341}
342
343#[derive(Clone, Debug, PartialEq, Eq, Zeroize)]
345#[zeroize(drop)]
346pub struct Signator {
347 extended_secret_key: [u8; EXTENDED_SECRET_KEY_SIZE],
348}
349
350impl super::super::Signator for Signator {
351 type PublicKey = super::PublicKey;
352
353 fn public_key(&self) -> Self::PublicKey {
354 super::PublicKey::from_32_bytes_array(cryptoxide::ed25519::to_public(
355 &self.extended_secret_key,
356 ))
357 }
358
359 fn sign(&self, message: &[u8]) -> <Self::PublicKey as super::super::PublicKey>::Signature {
360 super::Signature(cryptoxide::ed25519::signature_extended(
361 message,
362 &self.extended_secret_key,
363 ))
364 }
365}
366
367fn gen_root_chain_code(seed: &Seed32) -> ChainCode {
371 Hash::compute_multipart(&[&[0x01], seed.as_ref()]).0
372}
373
374fn normalize_bytes_ed25519_force3rd(bytes: &mut [u8; EXTENDED_SECRET_KEY_SIZE]) {
378 bytes[0] &= 0b1111_1000;
379 bytes[31] &= 0b0001_1111;
380 bytes[31] |= 0b0100_0000;
381}
382
383#[cfg(test)]
384mod tests {
385 use crate::keys::{PublicKey, Signator};
386
387 use super::*;
388 use unwrap::unwrap;
389
390 #[test]
391 fn test_derivation_index() {
392 let u31_zero = unwrap!(U31::new(0));
393 let index: u32 = DerivationIndex::hard(u31_zero).into();
394 assert_eq!(index, 0x80_00_00_00);
395
396 let u31_max = unwrap!(U31::new(0x7F_FF_FF_FF));
397 let index: u32 = DerivationIndex::hard(u31_max).into();
398 assert_eq!(index, u32::MAX);
399 }
400
401 #[test]
402 fn test_public_key_derivation() -> Result<(), InvalidAccountIndex> {
403 let mnemonic = unwrap!(crate::mnemonic::Mnemonic::from_phrase(
404 "acquire flat utility climb filter device liberty beyond matrix satisfy metal essence",
405 crate::mnemonic::Language::English
406 ));
407
408 println!("mnemonic={:?}", mnemonic.phrase());
409 let seed = crate::mnemonic::mnemonic_to_seed(&mnemonic);
410 println!("seed={:?}", hex::encode(seed.as_ref()));
411 let master_kp = KeyPair::from_seed(seed);
412
413 let account_index = unwrap!(U31::new(2));
414 let address_index = unwrap!(U31::new(3));
415
416 let external_chain_kp =
417 master_kp.derive(PrivateDerivationPath::opaque(account_index, true, None)?);
418 let external_chain_public_key = external_chain_kp.public_key();
419 let external_chain_code = external_chain_kp.chain_code();
420 println!(
421 "external_chain_public_key={:?}",
422 external_chain_public_key.to_base58()
423 );
424 println!(
425 "external_chain_code={:?}",
426 bs58::encode(external_chain_code).into_string()
427 );
428
429 println!(
430 "address(m/2'/0'/3)= {}",
431 master_kp
432 .derive(PrivateDerivationPath::opaque(
433 account_index,
434 true,
435 Some(address_index)
436 )?)
437 .public_key()
438 );
439
440 assert_eq!(
441 unwrap!(PublicKeyWithChainCode {
442 public_key: external_chain_public_key,
443 chain_code: external_chain_code,
444 }
445 .derive(address_index))
446 .public_key,
447 master_kp
448 .derive(PrivateDerivationPath::opaque(
449 account_index,
450 true,
451 Some(address_index)
452 )?)
453 .public_key()
454 );
455
456 Ok(())
457 }
458
459 #[test]
460 fn test_sign_and_verify() {
461 let seed = unwrap!(Seed32::from_base58(
462 "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"
463 ));
464 let kp = KeyPair::from_seed(seed);
465 let public_key = kp.public_key();
466
467 let message = "toto";
468 let wrong_message = "titi";
469 let sig = kp.generate_signator().sign(message.as_bytes());
470 let wrong_sig = kp.generate_signator().sign(wrong_message.as_bytes());
471
472 assert!(public_key.verify(message.as_bytes(), &sig).is_ok());
473 assert!(public_key.verify(wrong_message.as_bytes(), &sig).is_err());
474 assert!(public_key.verify(message.as_bytes(), &wrong_sig).is_err());
475 }
476
477 #[test]
478 fn test_derivation_index_consts() -> Result<(), crate::utils::U31Error> {
479 assert_eq!(
480 DerivationIndex::HARD_ZERO,
481 DerivationIndex::hard(U31::new(0)?)
482 );
483 assert_eq!(
484 DerivationIndex::HARD_ONE,
485 DerivationIndex::hard(U31::new(1)?)
486 );
487 Ok(())
488 }
489}