ed25519_dalek_xkeypair/
key.rs

1// Licensed under either of Apache License, Version 2.0 or MIT license at your option.
2// Copyright 2021 Hwakyeom Kim(=just-do-halee)
3
4use super::errors::*;
5use super::private::*;
6use super::types::*;
7
8use hmac::{Hmac, Mac, NewMac};
9use ripemd160::{Digest, Ripemd160};
10use sha2::{Sha256, Sha512};
11use zeroize::Zeroize;
12
13type HmacSha512 = Hmac<Sha512>;
14
15const ED25519_DOMAIN_NAME: &str = "ed25519 seed";
16pub const SEED_SIZE_LIST: &[usize] = &[16, 32, 64];
17
18/// simply represented by `extended secret key`
19#[derive(Debug)]
20pub struct ExtendedKeypair {
21    prefix: ExtPrefix,
22    attrs: ExtAttributes,
23    pair: DalekKeypair,
24}
25
26impl PartialEq for ExtendedKeypair {
27    fn eq(&self, other: &Self) -> bool {
28        (self.prefix == other.prefix)
29            && (self.attrs == other.attrs)
30            && (self.pair.secret.as_bytes() == other.pair.secret.as_bytes())
31            && (self.pair.public.as_bytes() == other.pair.public.as_bytes())
32    }
33}
34impl Eq for ExtendedKeypair {}
35
36impl ExtendedKeypair {
37    /// 78 (extended secret key)
38    pub const LENGTH: usize = consts::TOTAL_LENGTH;
39    pub const BASE58MAX_LENGTH: usize = 112;
40
41    pub fn prefix(&self) -> &ExtPrefix {
42        &self.prefix
43    }
44    pub fn attrs(&self) -> &ExtAttributes {
45        &self.attrs
46    }
47    pub fn pair(&self) -> &DalekKeypair {
48        &self.pair
49    }
50
51    pub fn secret_to_hex(&self) -> String {
52        hex::encode(&self.pair.secret)
53    }
54    pub fn public_to_hex(&self) -> String {
55        hex::encode(&self.pair.public)
56    }
57    pub fn chaincode_to_hex(&self) -> String {
58        hex::encode(&self.attrs.chain_code)
59    }
60    /// base58check(`extended secret key`)
61    pub fn to_base58check(&self) -> String {
62        bs58::encode(&self.to_bytes()).with_check().into_string()
63    }
64
65    pub fn dalekpair_from_secret_bytes(bytes: &[u8]) -> Result<DalekKeypair> {
66        let bytes = match bytes.len() {
67            consts::KEY_LENGTH => bytes,
68            consts::EXTKEY_LENGTH => &bytes[1..],
69            _ => {
70                return errbang!(
71                    err::InvalidLenSize,
72                    "{}, must be {} or {}.",
73                    bytes.len(),
74                    consts::KEY_LENGTH,
75                    consts::EXTKEY_LENGTH
76                )
77            }
78        };
79        let secret = errcast!(SecretKey::from_bytes(bytes), err::Parser);
80        let public = PublicKey::from(&secret);
81        Ok(DalekKeypair { secret, public })
82    }
83
84    #[inline(always)]
85    fn convert_to_bytes(xkey: &Self) -> [u8; Self::LENGTH] {
86        let mut bytes = [0u8; Self::LENGTH];
87        for (src, dst) in bytes.iter_mut().zip(
88            xkey.prefix.to_bytes().iter().chain(
89                xkey.attrs
90                    .to_bytes()
91                    .iter()
92                    .chain([0u8].iter().chain(xkey.pair.secret.as_bytes().iter())),
93            ),
94        ) {
95            *src = *dst;
96        }
97        bytes
98    }
99
100    #[inline(always)]
101    pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
102        Self::convert_to_bytes(self)
103    }
104    #[inline(always)]
105    pub fn into_bytes(self) -> [u8; Self::LENGTH] {
106        self.to_bytes()
107    }
108
109    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
110        if bytes.len() != Self::LENGTH {
111            return errbang!(
112                err::InvalidLenSize,
113                "{}, must be {}.",
114                bytes.len(),
115                Self::LENGTH
116            );
117        }
118        let prefix = ExtPrefix::from_bytes(&bytes[..ExtPrefix::LENGTH])?;
119        let attrs_end_len = ExtPrefix::LENGTH + ExtAttributes::LENGTH;
120        let attrs = ExtAttributes::from_bytes(&bytes[ExtPrefix::LENGTH..attrs_end_len])?;
121        let pair = Self::dalekpair_from_secret_bytes(&bytes[attrs_end_len..])?;
122        Ok(Self {
123            prefix,
124            attrs,
125            pair,
126        })
127    }
128
129    /// base58check(`extended secret key`)
130    pub fn write_base58check<'a>(
131        &self,
132        output: &'a mut [u8; Self::BASE58MAX_LENGTH],
133    ) -> Result<&'a str> {
134        let mut buf = Self::convert_to_bytes(self);
135        let base58_len = errcast!(
136            bs58::encode(&buf).with_check().into(output.as_mut()),
137            err::Parser
138        );
139        buf.zeroize();
140
141        Ok(errcast!(str::from_utf8(&output[..base58_len]), err::Parser))
142    }
143
144    pub fn from_seed_with_domain(
145        domain_name: &str,
146        seed: &[u8],
147        prefix: ExtPrefix,
148    ) -> Result<Self> {
149        if !SEED_SIZE_LIST.contains(&seed.len()) {
150            return errbang!(
151                err::InvalidLenSize,
152                "{}, must be included in {:?}",
153                seed.len(),
154                SEED_SIZE_LIST
155            );
156        }
157        let mut mac = errcast!(HmacSha512::new_from_slice(domain_name.as_ref()), err::Hmac);
158
159        mac.update(seed);
160        let bytes = mac.finalize().into_bytes();
161
162        let (child_key, chain_code) = bytes.split_at(consts::KEY_LENGTH);
163
164        let pair = Self::dalekpair_from_secret_bytes(child_key)?;
165        let attrs = {
166            let depth = 0;
167            let parent_fingerprint = ParentFingerprint::default();
168            let child_index = ChildIndex::Normal(0);
169            let chain_code = chain_code.try_into().unwrap();
170            ExtAttributes {
171                depth,
172                parent_fingerprint,
173                child_index,
174                chain_code,
175            }
176        };
177
178        Ok(Self {
179            prefix,
180            attrs,
181            pair,
182        })
183    }
184
185    #[inline(always)]
186    pub fn from_seed(seed: &[u8], prefix: ExtPrefix) -> Result<Self> {
187        Self::from_seed_with_domain(ED25519_DOMAIN_NAME, seed, prefix)
188    }
189
190    pub fn derive<P: AsRef<[ChildIndex]>>(&self, path: &P) -> Result<Self> {
191        let mut path = path.as_ref().iter();
192        let mut next = match path.next() {
193            Some(index) if index.is_hardened() => self.derive_child(index.to_u32())?,
194            Some(_) => return errbang!(err::Parser, "must be hardened index only."),
195            None => self.clone(),
196        };
197        for index in path {
198            if index.is_hardened() {
199                next = next.derive_child(index.to_u32())?;
200            } else {
201                return errbang!(err::Parser, "must be hardened index only.");
202            }
203        }
204        Ok(next)
205    }
206
207    pub fn derive_child(&self, index: u32) -> Result<Self> {
208        let depth = match self.attrs.depth.checked_add(1) {
209            Some(v) => v,
210            None => return errbang!(err::Overflow),
211        };
212
213        let mut mac = errcast!(
214            HmacSha512::new_from_slice(&self.attrs.chain_code),
215            err::Hmac
216        );
217
218        mac.update(&[0u8]);
219        mac.update(self.pair.secret.as_bytes());
220        mac.update(&ChildIndex::Hardened(index).to_bits().to_be_bytes());
221        let bytes = mac.finalize().into_bytes();
222
223        let (child_key, chain_code) = bytes.split_at(consts::KEY_LENGTH);
224
225        let pair = Self::dalekpair_from_secret_bytes(child_key)?;
226        let attrs = {
227            let parent_fingerprint = Ripemd160::digest(&Sha256::digest(pair.public.as_bytes()))
228                [..4]
229                .try_into()
230                .unwrap();
231            let child_index = ChildIndex::Hardened(index);
232            let chain_code = chain_code.try_into().unwrap();
233            ExtAttributes {
234                depth,
235                parent_fingerprint,
236                child_index,
237                chain_code,
238            }
239        };
240
241        Ok(Self {
242            prefix: self.prefix,
243            attrs,
244            pair,
245        })
246    }
247}
248
249impl Clone for ExtendedKeypair {
250    #[inline]
251    fn clone(&self) -> Self {
252        Self {
253            prefix: self.prefix,
254            attrs: self.attrs.clone(),
255            pair: DalekKeypair {
256                secret: SecretKey::from_bytes(self.pair.secret.as_bytes()).unwrap(),
257                public: self.pair.public,
258            },
259        }
260    }
261}
262
263impl Display for ExtendedKeypair {
264    // + to_string()
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        let mut buf = [0u8; Self::BASE58MAX_LENGTH];
267        self.write_base58check(&mut buf)
268            .map_err(|_| fmt::Error)
269            .and_then(|base58| f.write_str(base58))
270    }
271}
272
273impl FromStr for ExtendedKeypair {
274    type Err = Error;
275    fn from_str(base58check: &str) -> Result<Self> {
276        let mut bytes = [0u8; Self::LENGTH + consts::CHECKSUM_LENGTH];
277        let decoded_len = errcast!(
278            bs58::decode(base58check).with_check(None).into(&mut bytes),
279            err::Parser
280        );
281
282        if decoded_len != Self::LENGTH {
283            return errbang!(err::Parser);
284        }
285
286        let out = Self::from_bytes(&bytes[..Self::LENGTH])?;
287        bytes.zeroize();
288        Ok(out)
289    }
290}