ed25519_dalek_xkeypair/
key.rs1use 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#[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 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 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 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 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}