foundation_urtypes/registry/
hdkey.rs

1// SPDX-FileCopyrightText: © 2023 Foundation Devices, Inc. <hello@foundationdevices.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#[cfg(feature = "alloc")]
5use alloc::string::String;
6use core::num::NonZeroU32;
7
8use minicbor::{
9    bytes::DecodeBytes, data::Tag, data::Type, decode::Error, encode::Write, Decode, Decoder,
10    Encode, Encoder,
11};
12
13#[cfg(feature = "alloc")]
14use crate::registry::Keypath;
15use crate::registry::{CoinInfo, KeypathRef};
16
17/// HD Key (non owned, zero copy).
18#[doc(alias("hd-key"))]
19#[derive(Debug, Clone, PartialEq)]
20pub enum HDKeyRef<'a> {
21    /// Master key.
22    MasterKey(MasterKey),
23    /// Derived key.
24    DerivedKey(DerivedKeyRef<'a>),
25}
26
27impl<'a> HDKeyRef<'a> {
28    /// The CBOR tag used when [`HDKeyRef`] is embedded in other CBOR types.
29    pub const TAG: Tag = Tag::new(303);
30}
31
32#[cfg(feature = "bitcoin")]
33impl<'a> TryFrom<&'a bitcoin::bip32::Xpriv> for HDKeyRef<'a> {
34    type Error = InterpretExtendedKeyError;
35
36    fn try_from(xprv: &'a bitcoin::bip32::Xpriv) -> Result<Self, Self::Error> {
37        use crate::registry::CoinType;
38
39        if xprv.depth == 0 {
40            Ok(Self::MasterKey(MasterKey {
41                key_data: xprv.private_key.secret_bytes(),
42                chain_code: xprv.chain_code.to_bytes(),
43            }))
44        } else {
45            let mut key_data = [0u8; 33];
46            key_data[0] = 0;
47            key_data[1..].copy_from_slice(&xprv.private_key.secret_bytes());
48
49            Ok(Self::DerivedKey(DerivedKeyRef {
50                is_private: true,
51                key_data,
52                chain_code: Some(xprv.chain_code.to_bytes()),
53                use_info: Some(CoinInfo::new(
54                    CoinType::BTC,
55                    match xprv.network {
56                        bitcoin::Network::Bitcoin => CoinInfo::NETWORK_MAINNET,
57                        bitcoin::Network::Testnet => CoinInfo::NETWORK_BTC_TESTNET,
58                        _ => return Err(InterpretExtendedKeyError),
59                    },
60                )),
61                origin: None,
62                children: None,
63                parent_fingerprint: NonZeroU32::new(u32::from_be_bytes(
64                    xprv.parent_fingerprint.to_bytes(),
65                )),
66                name: None,
67                note: None,
68            }))
69        }
70    }
71}
72
73#[cfg(feature = "bitcoin")]
74impl<'a> TryFrom<&'a bitcoin::bip32::Xpub> for HDKeyRef<'a> {
75    type Error = InterpretExtendedKeyError;
76
77    fn try_from(xpub: &'a bitcoin::bip32::Xpub) -> Result<Self, Self::Error> {
78        use crate::registry::CoinType;
79
80        Ok(Self::DerivedKey(DerivedKeyRef {
81            is_private: false,
82            key_data: xpub.public_key.serialize(),
83            chain_code: Some(xpub.chain_code.to_bytes()),
84            use_info: Some(CoinInfo::new(
85                CoinType::BTC,
86                match xpub.network {
87                    bitcoin::Network::Bitcoin => CoinInfo::NETWORK_MAINNET,
88                    bitcoin::Network::Testnet => CoinInfo::NETWORK_BTC_TESTNET,
89                    _ => return Err(InterpretExtendedKeyError),
90                },
91            )),
92            origin: None,
93            children: None,
94            parent_fingerprint: NonZeroU32::new(u32::from_be_bytes(
95                xpub.parent_fingerprint.to_bytes(),
96            )),
97            name: None,
98            note: None,
99        }))
100    }
101}
102
103#[cfg(feature = "bitcoin")]
104#[derive(Debug)]
105pub struct InterpretExtendedKeyError;
106
107impl<'b, C> Decode<'b, C> for HDKeyRef<'b> {
108    fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, Error> {
109        if MasterKey::decode(&mut d.probe(), ctx).is_ok() {
110            return Ok(HDKeyRef::MasterKey(MasterKey::decode(d, ctx)?));
111        }
112
113        if DerivedKeyRef::decode(&mut d.probe(), ctx).is_ok() {
114            return Ok(HDKeyRef::DerivedKey(DerivedKeyRef::decode(d, ctx)?));
115        }
116
117        Err(Error::message(
118            "couldn't decode as master-key or derived-key",
119        ))
120    }
121}
122
123impl<'a, C> Encode<C> for HDKeyRef<'a> {
124    fn encode<W: Write>(
125        &self,
126        e: &mut Encoder<W>,
127        ctx: &mut C,
128    ) -> Result<(), minicbor::encode::Error<W::Error>> {
129        match self {
130            HDKeyRef::MasterKey(master_key) => master_key.encode(e, ctx),
131            HDKeyRef::DerivedKey(derived_key) => derived_key.encode(e, ctx),
132        }
133    }
134}
135
136/// HD Key.
137#[doc(alias("hd-key"))]
138#[cfg(feature = "alloc")]
139#[derive(Debug, Clone, PartialEq)]
140pub enum HDKey {
141    MasterKey(MasterKey),
142    DerivedKey(DerivedKey),
143}
144
145#[cfg(feature = "alloc")]
146impl<'a> From<HDKeyRef<'a>> for HDKey {
147    fn from(hdkey: HDKeyRef<'a>) -> Self {
148        match hdkey {
149            HDKeyRef::MasterKey(m) => Self::MasterKey(m),
150            HDKeyRef::DerivedKey(d) => Self::DerivedKey(DerivedKey::from(d)),
151        }
152    }
153}
154
155#[cfg(feature = "alloc")]
156impl<'b, C> Decode<'b, C> for HDKey {
157    fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, Error> {
158        HDKeyRef::decode(d, ctx).map(HDKey::from)
159    }
160}
161
162/// A master key.
163#[doc(alias("master-key"))]
164#[derive(Debug, Clone, Eq, PartialEq)]
165pub struct MasterKey {
166    /// Key date bytes.
167    pub key_data: [u8; 32],
168    /// Chain code bytes.
169    pub chain_code: [u8; 32],
170}
171
172impl<'b, C> Decode<'b, C> for MasterKey {
173    fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, Error> {
174        let mut is_master = None;
175        let mut key_data = None;
176        let mut chain_code = None;
177
178        let mut len = d.map()?;
179        loop {
180            match len {
181                Some(0) => break,
182                Some(n) => len = Some(n - 1),
183                None => {
184                    if d.datatype()? == Type::Break {
185                        break;
186                    }
187                }
188            }
189
190            match d.u32()? {
191                1 => is_master = Some(d.bool()?),
192                3 => {
193                    let mut data = [0; 32];
194
195                    let bytes: [u8; 33] = DecodeBytes::decode_bytes(d, ctx)?;
196                    data.copy_from_slice(&bytes[..32]);
197                    key_data = Some(data)
198                }
199                4 => chain_code = Some(DecodeBytes::decode_bytes(d, ctx)?),
200                _ => return Err(Error::message("unknown map entry")),
201            }
202        }
203
204        match is_master {
205            Some(true) => (),
206            Some(false) => return Err(Error::message("is-master is false")),
207            None => return Err(Error::message("is-master is not present")),
208        }
209
210        Ok(Self {
211            key_data: key_data.ok_or_else(|| Error::message("key-data is not present"))?,
212            chain_code: chain_code.ok_or_else(|| Error::message("chain-code is not present"))?,
213        })
214    }
215}
216
217impl<C> Encode<C> for MasterKey {
218    fn encode<W: Write>(
219        &self,
220        e: &mut Encoder<W>,
221        _ctx: &mut C,
222    ) -> Result<(), minicbor::encode::Error<W::Error>> {
223        let mut key_data = [0; 33];
224        key_data[0] = 0;
225        key_data[1..].copy_from_slice(&self.key_data);
226
227        e.map(3)?
228            .u8(1)?
229            .bool(true)?
230            .u8(3)?
231            .bytes(&key_data)?
232            .u8(4)?
233            .bytes(&self.chain_code)?;
234
235        Ok(())
236    }
237}
238
239/// A derived key (non-owned, zero copy).
240#[doc(alias("derived-key"))]
241#[derive(Debug, Clone, PartialEq)]
242pub struct DerivedKeyRef<'a> {
243    /// `true` if key is private, `false` if public.
244    pub is_private: bool,
245    /// Key data bytes.
246    pub key_data: [u8; 33],
247    /// Optional chain code.
248    pub chain_code: Option<[u8; 32]>,
249    /// How the key is to be used.
250    pub use_info: Option<CoinInfo>,
251    /// How the key was derived.
252    pub origin: Option<KeypathRef<'a>>,
253    /// What children should/can be derived from this.
254    pub children: Option<KeypathRef<'a>>,
255    /// The fingerprint of this key's direct ancestor.
256    pub parent_fingerprint: Option<NonZeroU32>,
257    /// A short name for this key.
258    pub name: Option<&'a str>,
259    /// An arbitrary amount of text describing the key.
260    pub note: Option<&'a str>,
261}
262
263impl<'b, C> Decode<'b, C> for DerivedKeyRef<'b> {
264    fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, Error> {
265        let mut is_private = false;
266        let mut key_data = None;
267        let mut chain_code = None;
268        let mut use_info = None;
269        let mut origin = None;
270        let mut children = None;
271        let mut parent_fingerprint = None;
272        let mut name = None;
273        let mut note = None;
274
275        let mut len = d.map()?;
276        loop {
277            match len {
278                Some(0) => break,
279                Some(n) => len = Some(n - 1),
280                None => {
281                    if d.datatype()? == Type::Break {
282                        break;
283                    }
284                }
285            }
286
287            const TAGGED_COININFO: Tag = Tag::new(40305);
288            const TAGGED_KEYPATH: Tag = Tag::new(40304);
289
290            match d.u32()? {
291                2 => is_private = d.bool()?,
292                3 => key_data = Some(DecodeBytes::decode_bytes(d, ctx)?),
293                4 => chain_code = Some(DecodeBytes::decode_bytes(d, ctx)?),
294                5 => match d.tag()? {
295                    TAGGED_COININFO => use_info = Some(CoinInfo::decode(d, ctx)?),
296                    _ => return Err(Error::message("invalid tag for coininfo")),
297                },
298                6 => match d.tag()? {
299                    TAGGED_KEYPATH => origin = Some(KeypathRef::decode(d, ctx)?),
300                    _ => return Err(Error::message("invalid tag for keypath")),
301                },
302                7 => match d.tag()? {
303                    TAGGED_KEYPATH => children = Some(KeypathRef::decode(d, ctx)?),
304                    _ => return Err(Error::message("invalid tag for keypath")),
305                },
306                8 => {
307                    parent_fingerprint = Some(
308                        NonZeroU32::new(d.u32()?)
309                            .ok_or_else(|| Error::message("parent-fingerprint is zero"))?,
310                    )
311                }
312                9 => name = Some(d.str()?),
313                10 => note = Some(d.str()?),
314                _ => return Err(Error::message("unknown map entry")),
315            }
316        }
317
318        Ok(Self {
319            is_private,
320            key_data: key_data.ok_or_else(|| Error::message("key-data is not present"))?,
321            chain_code,
322            use_info,
323            origin,
324            children,
325            parent_fingerprint,
326            name,
327            note,
328        })
329    }
330}
331
332impl<'a, C> Encode<C> for DerivedKeyRef<'a> {
333    fn encode<W: Write>(
334        &self,
335        e: &mut Encoder<W>,
336        ctx: &mut C,
337    ) -> Result<(), minicbor::encode::Error<W::Error>> {
338        let len = self.is_private as u64
339            + 1
340            + self.chain_code.is_some() as u64
341            + self.use_info.is_some() as u64
342            + self.origin.is_some() as u64
343            + self.children.is_some() as u64
344            + self.parent_fingerprint.is_some() as u64
345            + self.name.is_some() as u64
346            + self.note.is_some() as u64;
347
348        e.map(len)?;
349
350        if self.is_private {
351            e.u8(2)?.bool(self.is_private)?;
352        }
353
354        e.u8(3)?.bytes(&self.key_data)?;
355
356        if let Some(ref chain_code) = self.chain_code {
357            e.u8(4)?.bytes(chain_code)?;
358        }
359
360        if let Some(ref use_info) = self.use_info {
361            e.u8(5)?.tag(Tag::new(40305))?;
362            use_info.encode(e, ctx)?;
363        }
364
365        if let Some(ref origin) = self.origin {
366            e.u8(6)?.tag(Tag::new(40304))?;
367            origin.encode(e, ctx)?;
368        }
369
370        if let Some(ref children) = self.children {
371            e.u8(7)?.tag(Tag::new(40304))?;
372            children.encode(e, ctx)?;
373        }
374
375        if let Some(parent_fingerprint) = self.parent_fingerprint {
376            e.u8(8)?.u32(parent_fingerprint.get())?;
377        }
378
379        if let Some(name) = self.name {
380            e.u8(9)?.str(name)?;
381        }
382
383        if let Some(note) = self.note {
384            e.u8(10)?.str(note)?;
385        }
386
387        Ok(())
388    }
389}
390
391/// A derived key.
392#[doc(alias("derived-key"))]
393#[cfg(feature = "alloc")]
394#[derive(Debug, Clone, PartialEq)]
395pub struct DerivedKey {
396    /// `true` if key is private, `false` if public.
397    pub is_private: bool,
398    /// Key data bytes.
399    pub key_data: [u8; 33],
400    /// Optional chain code.
401    pub chain_code: Option<[u8; 32]>,
402    /// How the key is to be used.
403    pub use_info: Option<CoinInfo>,
404    /// How the key was derived.
405    pub origin: Option<Keypath>,
406    /// What children should/can be derived from this.
407    pub children: Option<Keypath>,
408    /// The fingerprint of this key's direct ancestor.
409    pub parent_fingerprint: Option<NonZeroU32>,
410    /// A short name for this key.
411    pub name: Option<String>,
412    /// An arbitrary amount of text describing the key.
413    pub note: Option<String>,
414}
415
416#[cfg(feature = "alloc")]
417impl<'a> From<DerivedKeyRef<'a>> for DerivedKey {
418    fn from(derived_key: DerivedKeyRef<'a>) -> Self {
419        Self {
420            is_private: derived_key.is_private,
421            key_data: derived_key.key_data,
422            chain_code: derived_key.chain_code,
423            use_info: derived_key.use_info,
424            origin: derived_key.origin.map(Keypath::from),
425            children: derived_key.children.map(Keypath::from),
426            parent_fingerprint: derived_key.parent_fingerprint,
427            name: derived_key.name.map(String::from),
428            note: derived_key.note.map(String::from),
429        }
430    }
431}