ssh_key/public/
ecdsa.rs

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