Skip to main content

near_slip10/
lib.rs

1#![no_std]
2
3extern crate alloc;
4
5pub mod path;
6
7pub use crate::path::BIP32Path;
8
9use alloc::vec::Vec;
10use core::convert::TryInto;
11use core::fmt;
12
13use ed25519_dalek::{SigningKey, VerifyingKey};
14use hmac::{crypto_mac::Output, Hmac, Mac, NewMac};
15use sha2::Sha512;
16
17pub(crate) const HARDEND: u32 = 1 << 31;
18
19#[derive(Debug)]
20pub enum Error {
21    InvalidIndex,
22}
23
24impl fmt::Display for Error {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        match self {
27            Error::InvalidIndex => "Invalid index provided".fmt(f),
28        }
29    }
30}
31
32//impl core::error::Error for Error {}
33
34// Create alias for HMAC-SHA256
35type HmacSha256 = Hmac<Sha512>;
36
37/// Derives an extended private key for the curve from seed and path as outlined by SLIP-10.
38pub fn derive_key_from_path(seed: &[u8], curve: Curve, path: &BIP32Path) -> Result<Key, Error> {
39    let master: Result<Key, Error> = Ok(Key::new(seed, curve));
40
41    path.0.iter().fold(master, |key, index| match key {
42        Ok(k) => Ok(k.generate_child_key(*index)?),
43        Err(e) => Err(e),
44    })
45}
46
47#[derive(Clone, Copy, Debug)]
48pub enum Curve {
49    Ed25519,
50}
51
52impl Curve {
53    fn seedkey(&self) -> &[u8] {
54        match self {
55            Curve::Ed25519 => b"ed25519 seed",
56        }
57    }
58
59    fn validate_child_index(&self, index: u32) -> bool {
60        match self {
61            Curve::Ed25519 => index < HARDEND,
62        }
63    }
64
65    fn public_key(&self, key: &[u8; 32]) -> Vec<u8> {
66        match self {
67            Curve::Ed25519 => {
68                let signing_key: SigningKey = SigningKey::from_bytes(key);
69                let public: VerifyingKey = signing_key.verifying_key();
70                let mut result = Vec::new();
71                result.push(0);
72                public.to_bytes().iter().for_each(|i| result.push(*i));
73                result
74            }
75        }
76    }
77}
78
79/// A SLIP-10 extended private key.
80pub struct Key {
81    pub key: [u8; 32],
82    pub chain_code: [u8; 32],
83    pub curve: Curve,
84}
85
86impl Key {
87    /// Creates a new master private extended key for the curve from a seed.
88    pub fn new(seed: &[u8], curve: Curve) -> Self {
89        // Calculate I = HMAC-SHA512(Key = Curve, Data = seed)
90        let inter = hmac_sha256(curve.seedkey(), seed).into_bytes();
91
92        // Split I into two 32-byte sequences, I_L and I_R
93        // Use parse256(I_L) as secret key, and I_R as chain code.
94        let key: [u8; 32] = inter[..32].try_into().unwrap();
95        let chain_code: [u8; 32] = inter[32..].try_into().unwrap();
96
97        Self {
98            key,
99            chain_code,
100            curve,
101        }
102    }
103
104    /// Compute corresponding public key.
105    pub fn public_key(&self) -> [u8; 33] {
106        let mut key = [0u8; 33];
107        key.copy_from_slice(&self.curve.public_key(&self.key));
108        key
109    }
110
111    fn generate_child_key(&self, index: u32) -> Result<Key, Error> {
112        if self.curve.validate_child_index(index) {
113            return Err(Error::InvalidIndex);
114        }
115
116        let inter = self.get_intermediary(index).into_bytes();
117
118        // Split I into two 32-byte sequences, I_L and I_R
119        let key: [u8; 32] = inter[..32].try_into().unwrap();
120        let chain_code: [u8; 32] = inter[32..].try_into().unwrap();
121
122        // Compute the private key from I_L and k_par
123
124        Ok(Key {
125            key,
126            chain_code,
127            curve: self.curve,
128        })
129    }
130
131    fn get_intermediary(&self, index: u32) -> Output<HmacSha256> {
132        let mut data = Vec::new();
133        if index < HARDEND {
134            data.append(&mut self.curve.public_key(&self.key));
135        } else {
136            data.push(0u8);
137            self.key.iter().for_each(|i| data.push(*i));
138        }
139        index.to_be_bytes().iter().for_each(|i| data.push(*i));
140
141        hmac_sha256(&self.chain_code, &data)
142    }
143}
144
145fn hmac_sha256(key: &[u8], data: &[u8]) -> Output<HmacSha256> {
146    // Create HMAC-SHA256 instance which implements `Mac` trait
147    let mut mac = HmacSha256::new_varkey(key).expect("HMAC can take key of any size");
148    mac.update(data);
149
150    // `result` has type `Output` which is a thin wrapper around array of
151    // bytes for providing constant time equality check
152    mac.finalize()
153}