foundation_urtypes/registry/
eckey.rs

1// SPDX-FileCopyrightText: © 2023 Foundation Devices, Inc. <hello@foundationdevices.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use minicbor::{
5    data::{Tag, Type},
6    decode::Error,
7    encode::Write,
8    Decode, Decoder, Encode, Encoder,
9};
10
11/// Elliptic Curve (EC) key.
12#[doc(alias("crypto-eckey"))]
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct ECKey<'a> {
15    /// The curve type.
16    pub curve: u64,
17    /// Private key?
18    pub is_private: bool,
19    /// The key material.
20    pub data: &'a [u8],
21}
22
23impl<'a> ECKey<'a> {
24    /// The CBOR tag used when [`ECKey`] is embedded in other CBOR types.
25    pub const TAG: Tag = Tag::new(306);
26
27    /// `secp256k1` curve type.
28    pub const SECP256K1: u64 = 0;
29}
30
31impl<'b, C> Decode<'b, C> for ECKey<'b> {
32    fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result<Self, Error> {
33        let mut curve = Self::SECP256K1;
34        let mut is_private = false;
35        let mut data = None;
36
37        let mut len = d.map()?;
38        loop {
39            match len {
40                Some(0) => break,
41                Some(n) => len = Some(n - 1),
42                None => {
43                    if d.datatype()? == Type::Break {
44                        break;
45                    }
46                }
47            }
48
49            match d.u32()? {
50                1 => curve = d.u64()?,
51                2 => is_private = d.bool()?,
52                3 => data = Some(d.bytes()?),
53                _ => return Err(Error::message("unknown map entry")),
54            }
55        }
56
57        Ok(Self {
58            curve,
59            is_private,
60            data: data.ok_or_else(|| Error::message("data is missing"))?,
61        })
62    }
63}
64
65impl<'a, C> Encode<C> for ECKey<'a> {
66    fn encode<W: Write>(
67        &self,
68        e: &mut Encoder<W>,
69        _ctx: &mut C,
70    ) -> Result<(), minicbor::encode::Error<W::Error>> {
71        let is_not_default_curve = self.curve != Self::SECP256K1;
72        let len = is_not_default_curve as u64 + self.is_private as u64 + 1;
73        e.map(len)?;
74
75        if is_not_default_curve {
76            e.u8(1)?.u64(self.curve)?;
77        }
78
79        if self.is_private {
80            e.u8(2)?.bool(self.is_private)?;
81        }
82
83        e.u8(3)?.bytes(self.data)?;
84
85        Ok(())
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    use foundation_test_vectors::{URVector, UR};
94
95    #[test]
96    fn test_roundtrip() {
97        let vectors = URVector::new();
98
99        for vector in vectors.iter().filter(|v| matches!(v.ur, UR::ECKey(_))) {
100            let crypto_eckey_vector = vector.ur.unwrap_eckey();
101
102            let crypto_eckey = ECKey {
103                curve: ECKey::SECP256K1,
104                is_private: crypto_eckey_vector.is_private,
105                data: &crypto_eckey_vector.data,
106            };
107
108            let cbor = minicbor::to_vec(&crypto_eckey).unwrap();
109            assert_eq!(vector.as_cbor, cbor);
110        }
111    }
112}