ssh_key/public/
ecdsa.rs

1//! Elliptic Curve Digital Signature Algorithm (ECDSA) public keys.
2
3use crate::{Algorithm, EcdsaCurve, Error, Result};
4use core::fmt;
5use encoding::{CheckedSum, Decode, Encode, Reader, Writer};
6use sec1::consts::{U32, U48, U66};
7
8/// ECDSA/NIST P-256 public key.
9pub type EcdsaNistP256PublicKey = sec1::EncodedPoint<U32>;
10
11/// ECDSA/NIST P-384 public key.
12pub type EcdsaNistP384PublicKey = sec1::EncodedPoint<U48>;
13
14/// ECDSA/NIST P-521 public key.
15pub type EcdsaNistP521PublicKey = sec1::EncodedPoint<U66>;
16
17/// Elliptic Curve Digital Signature Algorithm (ECDSA) public key.
18///
19/// Public keys are represented as [`sec1::EncodedPoint`] and require the
20/// `sec1` feature of this crate is enabled (which it is by default).
21///
22/// Described in [FIPS 186-4](https://csrc.nist.gov/publications/detail/fips/186/4/final).
23#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
24pub enum EcdsaPublicKey {
25    /// NIST P-256 ECDSA public key.
26    NistP256(EcdsaNistP256PublicKey),
27
28    /// NIST P-384 ECDSA public key.
29    NistP384(EcdsaNistP384PublicKey),
30
31    /// NIST P-521 ECDSA public key.
32    NistP521(EcdsaNistP521PublicKey),
33}
34
35impl EcdsaPublicKey {
36    /// Maximum size of a SEC1-encoded ECDSA public key (i.e. curve point).
37    ///
38    /// This is the size of 2 * P-521 field elements (2 * 66 = 132) which
39    /// represent the affine coordinates of a curve point plus one additional
40    /// byte for the SEC1 "tag" identifying the curve point encoding.
41    const MAX_SIZE: usize = 133;
42
43    /// Parse an ECDSA public key from a SEC1-encoded point.
44    ///
45    /// Determines the key type from the SEC1 tag byte and length.
46    pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
47        match bytes {
48            [tag, rest @ ..] => {
49                let point_size = match sec1::point::Tag::from_u8(*tag)? {
50                    sec1::point::Tag::CompressedEvenY | sec1::point::Tag::CompressedOddY => {
51                        rest.len()
52                    }
53                    sec1::point::Tag::Uncompressed => rest.len() / 2,
54                    _ => return Err(Error::AlgorithmUnknown),
55                };
56
57                match point_size {
58                    32 => Ok(Self::NistP256(EcdsaNistP256PublicKey::from_bytes(bytes)?)),
59                    48 => Ok(Self::NistP384(EcdsaNistP384PublicKey::from_bytes(bytes)?)),
60                    66 => Ok(Self::NistP521(EcdsaNistP521PublicKey::from_bytes(bytes)?)),
61                    _ => Err(encoding::Error::Length.into()),
62                }
63            }
64            _ => Err(encoding::Error::Length.into()),
65        }
66    }
67
68    /// Borrow the SEC1-encoded key data as bytes.
69    pub fn as_sec1_bytes(&self) -> &[u8] {
70        match self {
71            EcdsaPublicKey::NistP256(point) => point.as_bytes(),
72            EcdsaPublicKey::NistP384(point) => point.as_bytes(),
73            EcdsaPublicKey::NistP521(point) => point.as_bytes(),
74        }
75    }
76
77    /// Get the [`Algorithm`] for this public key type.
78    pub fn algorithm(&self) -> Algorithm {
79        Algorithm::Ecdsa {
80            curve: self.curve(),
81        }
82    }
83
84    /// Get the [`EcdsaCurve`] for this key.
85    pub fn curve(&self) -> EcdsaCurve {
86        match self {
87            EcdsaPublicKey::NistP256(_) => EcdsaCurve::NistP256,
88            EcdsaPublicKey::NistP384(_) => EcdsaCurve::NistP384,
89            EcdsaPublicKey::NistP521(_) => EcdsaCurve::NistP521,
90        }
91    }
92}
93
94impl AsRef<[u8]> for EcdsaPublicKey {
95    fn as_ref(&self) -> &[u8] {
96        self.as_sec1_bytes()
97    }
98}
99
100impl Decode for EcdsaPublicKey {
101    type Error = Error;
102
103    fn decode(reader: &mut impl Reader) -> Result<Self> {
104        let curve = EcdsaCurve::decode(reader)?;
105
106        let mut buf = [0u8; Self::MAX_SIZE];
107        let key = Self::from_sec1_bytes(reader.read_byten(&mut buf)?)?;
108
109        if key.curve() == curve {
110            Ok(key)
111        } else {
112            Err(Error::AlgorithmUnknown)
113        }
114    }
115}
116
117impl Encode for EcdsaPublicKey {
118    fn encoded_len(&self) -> encoding::Result<usize> {
119        [
120            self.curve().encoded_len()?,
121            4, // uint32 length prefix
122            self.as_ref().len(),
123        ]
124        .checked_sum()
125    }
126
127    fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
128        self.curve().encode(writer)?;
129        self.as_ref().encode(writer)?;
130        Ok(())
131    }
132}
133
134impl fmt::Display for EcdsaPublicKey {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        write!(f, "{self:X}")
137    }
138}
139
140impl fmt::LowerHex for EcdsaPublicKey {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        for byte in self.as_sec1_bytes() {
143            write!(f, "{byte:02x}")?;
144        }
145        Ok(())
146    }
147}
148
149impl fmt::UpperHex for EcdsaPublicKey {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        for byte in self.as_sec1_bytes() {
152            write!(f, "{byte:02X}")?;
153        }
154        Ok(())
155    }
156}
157
158macro_rules! impl_ecdsa_for_curve {
159    ($krate:ident, $feature:expr, $curve:ident) => {
160        #[cfg(feature = $feature)]
161        impl TryFrom<EcdsaPublicKey> for $krate::ecdsa::VerifyingKey {
162            type Error = Error;
163
164            fn try_from(key: EcdsaPublicKey) -> Result<$krate::ecdsa::VerifyingKey> {
165                $krate::ecdsa::VerifyingKey::try_from(&key)
166            }
167        }
168
169        #[cfg(feature = $feature)]
170        impl TryFrom<&EcdsaPublicKey> for $krate::ecdsa::VerifyingKey {
171            type Error = Error;
172
173            fn try_from(public_key: &EcdsaPublicKey) -> Result<$krate::ecdsa::VerifyingKey> {
174                match public_key {
175                    EcdsaPublicKey::$curve(key) => {
176                        $krate::ecdsa::VerifyingKey::from_encoded_point(key)
177                            .map_err(|_| Error::Crypto)
178                    }
179                    _ => Err(Error::AlgorithmUnknown),
180                }
181            }
182        }
183
184        #[cfg(feature = $feature)]
185        impl From<$krate::ecdsa::VerifyingKey> for EcdsaPublicKey {
186            fn from(key: $krate::ecdsa::VerifyingKey) -> EcdsaPublicKey {
187                EcdsaPublicKey::from(&key)
188            }
189        }
190
191        #[cfg(feature = $feature)]
192        impl From<&$krate::ecdsa::VerifyingKey> for EcdsaPublicKey {
193            fn from(key: &$krate::ecdsa::VerifyingKey) -> EcdsaPublicKey {
194                EcdsaPublicKey::$curve(key.to_encoded_point(false))
195            }
196        }
197    };
198}
199
200impl_ecdsa_for_curve!(p256, "p256", NistP256);
201impl_ecdsa_for_curve!(p384, "p384", NistP384);
202impl_ecdsa_for_curve!(p521, "p521", NistP521);