Skip to main content

ssh_key/public/
key_data.rs

1//! Public key data.
2
3use super::{Ed25519PublicKey, SkEd25519};
4use crate::{Algorithm, Error, Fingerprint, HashAlg, Result};
5use encoding::{CheckedSum, Decode, Encode, Reader, Writer};
6
7#[cfg(feature = "alloc")]
8use {
9    super::{DsaPublicKey, OpaquePublicKey, RsaPublicKey},
10    crate::Certificate,
11    alloc::boxed::Box,
12};
13
14#[cfg(feature = "ecdsa")]
15use super::{EcdsaPublicKey, SkEcdsaSha2NistP256};
16
17/// Public key data.
18#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
19#[non_exhaustive]
20pub enum KeyData {
21    /// Digital Signature Algorithm (DSA) public key data.
22    #[cfg(feature = "alloc")]
23    Dsa(DsaPublicKey),
24
25    /// Elliptic Curve Digital Signature Algorithm (ECDSA) public key data.
26    #[cfg(feature = "ecdsa")]
27    Ecdsa(EcdsaPublicKey),
28
29    /// Ed25519 public key data.
30    Ed25519(Ed25519PublicKey),
31
32    /// RSA public key data.
33    #[cfg(feature = "alloc")]
34    Rsa(RsaPublicKey),
35
36    /// Security Key (FIDO/U2F) using ECDSA/NIST P-256 as specified in [PROTOCOL.u2f].
37    ///
38    /// [PROTOCOL.u2f]: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.u2f?annotate=HEAD
39    #[cfg(feature = "ecdsa")]
40    SkEcdsaSha2NistP256(SkEcdsaSha2NistP256),
41
42    /// Security Key (FIDO/U2F) using Ed25519 as specified in [PROTOCOL.u2f].
43    ///
44    /// [PROTOCOL.u2f]: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.u2f?annotate=HEAD
45    SkEd25519(SkEd25519),
46
47    /// Certificate key as specified in [draft-miller-ssh-cert]
48    /// (and [PROTOCOL.certkeys], now deprecated in favor of the IETF draft).
49    ///
50    /// [draft-miller-ssh-cert]: https://datatracker.ietf.org/doc/draft-miller-ssh-cert
51    /// [PROTOCOL.certkeys]: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
52    #[cfg(feature = "alloc")]
53    Certificate(Box<Certificate>),
54
55    /// Opaque public key data.
56    #[cfg(feature = "alloc")]
57    Other(OpaquePublicKey),
58}
59
60impl KeyData {
61    /// Get the [`Algorithm`] for this public key.
62    #[must_use]
63    pub fn algorithm(&self) -> Algorithm {
64        match self {
65            #[cfg(feature = "alloc")]
66            Self::Dsa(_) => Algorithm::Dsa,
67            #[cfg(feature = "ecdsa")]
68            Self::Ecdsa(key) => key.algorithm(),
69            Self::Ed25519(_) => Algorithm::Ed25519,
70            #[cfg(feature = "alloc")]
71            Self::Rsa(_) => Algorithm::Rsa { hash: None },
72            #[cfg(feature = "ecdsa")]
73            Self::SkEcdsaSha2NistP256(_) => Algorithm::SkEcdsaSha2NistP256,
74            Self::SkEd25519(_) => Algorithm::SkEd25519,
75            #[cfg(feature = "alloc")]
76            Self::Certificate(cert) => cert.algorithm(),
77            #[cfg(feature = "alloc")]
78            Self::Other(key) => key.algorithm(),
79        }
80    }
81
82    /// Get DSA public key if this key is the correct type.
83    #[cfg(feature = "alloc")]
84    #[must_use]
85    pub fn dsa(&self) -> Option<&DsaPublicKey> {
86        match self {
87            Self::Dsa(key) => Some(key),
88            _ => None,
89        }
90    }
91
92    /// Get ECDSA public key if this key is the correct type.
93    #[cfg(feature = "ecdsa")]
94    #[must_use]
95    pub fn ecdsa(&self) -> Option<&EcdsaPublicKey> {
96        match self {
97            Self::Ecdsa(key) => Some(key),
98            _ => None,
99        }
100    }
101
102    /// Get Ed25519 public key if this key is the correct type.
103    #[must_use]
104    pub fn ed25519(&self) -> Option<&Ed25519PublicKey> {
105        match self {
106            Self::Ed25519(key) => Some(key),
107            #[allow(unreachable_patterns)]
108            _ => None,
109        }
110    }
111
112    /// Compute key fingerprint.
113    ///
114    /// Use [`Default::default()`] to use the default hash function (SHA-256).
115    #[must_use]
116    pub fn fingerprint(&self, hash_alg: HashAlg) -> Fingerprint {
117        Fingerprint::new(hash_alg, self)
118    }
119
120    /// Get RSA public key if this key is the correct type.
121    #[cfg(feature = "alloc")]
122    #[must_use]
123    pub fn rsa(&self) -> Option<&RsaPublicKey> {
124        match self {
125            Self::Rsa(key) => Some(key),
126            _ => None,
127        }
128    }
129
130    /// Get FIDO/U2F ECDSA/NIST P-256 public key if this key is the correct type.
131    #[cfg(feature = "ecdsa")]
132    #[must_use]
133    pub fn sk_ecdsa_p256(&self) -> Option<&SkEcdsaSha2NistP256> {
134        match self {
135            Self::SkEcdsaSha2NistP256(sk) => Some(sk),
136            _ => None,
137        }
138    }
139
140    /// Get FIDO/U2F Ed25519 public key if this key is the correct type.
141    #[must_use]
142    pub fn sk_ed25519(&self) -> Option<&SkEd25519> {
143        match self {
144            Self::SkEd25519(sk) => Some(sk),
145            _ => None,
146        }
147    }
148
149    /// Get the custom, opaque public key if this key is the correct type.
150    #[cfg(feature = "alloc")]
151    #[must_use]
152    pub fn other(&self) -> Option<&OpaquePublicKey> {
153        match self {
154            Self::Other(key) => Some(key),
155            _ => None,
156        }
157    }
158
159    /// Get the certificate if this key is the correct type.
160    #[cfg(feature = "alloc")]
161    #[must_use]
162    pub fn certificate(&self) -> Option<&Certificate> {
163        match self {
164            Self::Certificate(certificate) => Some(certificate.as_ref()),
165            _ => None,
166        }
167    }
168
169    /// Get the certificate, consuming the [`KeyData`], if this key is the correct type.
170    #[cfg(feature = "alloc")]
171    #[must_use]
172    pub fn into_certificate(self) -> Option<Certificate> {
173        match self {
174            Self::Certificate(certificate) => Some(*certificate),
175            _ => None,
176        }
177    }
178
179    /// Is this key a DSA key?
180    #[cfg(feature = "alloc")]
181    #[must_use]
182    pub fn is_dsa(&self) -> bool {
183        matches!(self, Self::Dsa(_))
184    }
185
186    /// Is this key an ECDSA key?
187    #[cfg(feature = "ecdsa")]
188    #[must_use]
189    pub fn is_ecdsa(&self) -> bool {
190        matches!(self, Self::Ecdsa(_))
191    }
192
193    /// Is this key an Ed25519 key?
194    #[must_use]
195    pub fn is_ed25519(&self) -> bool {
196        matches!(self, Self::Ed25519(_))
197    }
198
199    /// Is this key an RSA key?
200    #[cfg(feature = "alloc")]
201    #[must_use]
202    pub fn is_rsa(&self) -> bool {
203        matches!(self, Self::Rsa(_))
204    }
205
206    /// Is this key a FIDO/U2F ECDSA/NIST P-256 key?
207    #[cfg(feature = "ecdsa")]
208    #[must_use]
209    pub fn is_sk_ecdsa_p256(&self) -> bool {
210        matches!(self, Self::SkEcdsaSha2NistP256(_))
211    }
212
213    /// Is this key a FIDO/U2F Ed25519 key?
214    #[must_use]
215    pub fn is_sk_ed25519(&self) -> bool {
216        matches!(self, Self::SkEd25519(_))
217    }
218
219    /// Is this a key with a custom algorithm?
220    #[cfg(feature = "alloc")]
221    #[must_use]
222    pub fn is_other(&self) -> bool {
223        matches!(self, Self::Other(_))
224    }
225
226    /// Is this a certificate?
227    #[cfg(feature = "alloc")]
228    #[must_use]
229    pub fn is_certificate(&self) -> bool {
230        matches!(self, Self::Certificate(_))
231    }
232
233    /// Decode [`KeyData`] for the specified algorithm.
234    ///
235    /// # Errors
236    /// - Returns [`Error::AlgorithmUnknown`] if the provided `algorithm` is unknown or unsupported
237    ///   by this library.
238    /// - Returns [`Error::Encoding`] in the event of an encoding error.
239    pub fn decode_as(reader: &mut impl Reader, algorithm: Algorithm) -> Result<Self> {
240        match algorithm {
241            #[cfg(feature = "alloc")]
242            Algorithm::Dsa => DsaPublicKey::decode(reader).map(Self::Dsa),
243            #[cfg(feature = "ecdsa")]
244            Algorithm::Ecdsa { curve } => match EcdsaPublicKey::decode(reader)? {
245                key if key.curve() == curve => Ok(Self::Ecdsa(key)),
246                _ => Err(Error::AlgorithmUnknown),
247            },
248            Algorithm::Ed25519 => Ed25519PublicKey::decode(reader).map(Self::Ed25519),
249            #[cfg(feature = "alloc")]
250            Algorithm::Rsa { .. } => RsaPublicKey::decode(reader).map(Self::Rsa),
251            #[cfg(feature = "ecdsa")]
252            Algorithm::SkEcdsaSha2NistP256 => {
253                SkEcdsaSha2NistP256::decode(reader).map(Self::SkEcdsaSha2NistP256)
254            }
255            Algorithm::SkEd25519 => SkEd25519::decode(reader).map(Self::SkEd25519),
256            #[cfg(feature = "alloc")]
257            Algorithm::Other(_) => OpaquePublicKey::decode_as(reader, algorithm).map(Self::Other),
258            #[allow(unreachable_patterns)]
259            _ => Err(Error::AlgorithmUnknown),
260        }
261    }
262
263    /// Decode [`KeyData`] as a certificate with the specified algorithm.
264    ///
265    /// # Errors
266    /// Propagates errors from [`Certificate::decode_as`].
267    #[cfg(feature = "alloc")]
268    pub fn decode_as_certificate(reader: &mut impl Reader, algorithm: Algorithm) -> Result<Self> {
269        Certificate::decode_as(reader, algorithm).map(|cert| Self::Certificate(Box::new(cert)))
270    }
271
272    /// Get the encoded length of this key data without a leading algorithm
273    /// identifier.
274    pub(crate) fn encoded_key_data_len(&self) -> encoding::Result<usize> {
275        match self {
276            #[cfg(feature = "alloc")]
277            Self::Dsa(key) => key.encoded_len(),
278            #[cfg(feature = "ecdsa")]
279            Self::Ecdsa(key) => key.encoded_len(),
280            Self::Ed25519(key) => key.encoded_len(),
281            #[cfg(feature = "alloc")]
282            Self::Rsa(key) => key.encoded_len(),
283            #[cfg(feature = "ecdsa")]
284            Self::SkEcdsaSha2NistP256(sk) => sk.encoded_len(),
285            Self::SkEd25519(sk) => sk.encoded_len(),
286            #[cfg(feature = "alloc")]
287            Self::Certificate(cert) => cert.encoded_len(),
288            #[cfg(feature = "alloc")]
289            Self::Other(other) => other.key.encoded_len(),
290        }
291    }
292
293    /// Encode the key data without a leading algorithm identifier.
294    pub(crate) fn encode_key_data(&self, writer: &mut impl Writer) -> encoding::Result<()> {
295        match self {
296            #[cfg(feature = "alloc")]
297            Self::Dsa(key) => key.encode(writer),
298            #[cfg(feature = "ecdsa")]
299            Self::Ecdsa(key) => key.encode(writer),
300            Self::Ed25519(key) => key.encode(writer),
301            #[cfg(feature = "alloc")]
302            Self::Rsa(key) => key.encode(writer),
303            #[cfg(feature = "ecdsa")]
304            Self::SkEcdsaSha2NistP256(sk) => sk.encode(writer),
305            Self::SkEd25519(sk) => sk.encode(writer),
306            #[cfg(feature = "alloc")]
307            Self::Certificate(cert) => cert.encode(writer),
308            #[cfg(feature = "alloc")]
309            Self::Other(other) => other.key.encode(writer),
310        }
311    }
312}
313
314impl Decode for KeyData {
315    type Error = Error;
316
317    fn decode(reader: &mut impl Reader) -> Result<Self> {
318        let algorithm = Algorithm::decode(reader)?;
319        #[cfg(feature = "alloc")]
320        if let Algorithm::Other(name) = &algorithm {
321            if let Ok(certificate_algorithm) = Algorithm::new_certificate(name.as_str()) {
322                return Self::decode_as_certificate(reader, certificate_algorithm);
323            }
324        }
325        Self::decode_as(reader, algorithm)
326    }
327}
328
329impl Encode for KeyData {
330    fn encoded_len(&self) -> encoding::Result<usize> {
331        #[cfg(feature = "alloc")]
332        if self.is_certificate() {
333            // Certificate encodes its own Algorithm
334            return self.encoded_key_data_len();
335        }
336
337        [
338            self.algorithm().encoded_len()?,
339            self.encoded_key_data_len()?,
340        ]
341        .checked_sum()
342    }
343
344    fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
345        #[cfg(feature = "alloc")]
346        if self.is_certificate() {
347            // Certificate encodes its own Algorithm
348            return self.encode_key_data(writer);
349        }
350
351        self.algorithm().encode(writer)?;
352        self.encode_key_data(writer)
353    }
354}
355
356#[cfg(feature = "alloc")]
357impl From<DsaPublicKey> for KeyData {
358    fn from(public_key: DsaPublicKey) -> KeyData {
359        Self::Dsa(public_key)
360    }
361}
362
363#[cfg(feature = "ecdsa")]
364impl From<EcdsaPublicKey> for KeyData {
365    fn from(public_key: EcdsaPublicKey) -> KeyData {
366        Self::Ecdsa(public_key)
367    }
368}
369
370impl From<Ed25519PublicKey> for KeyData {
371    fn from(public_key: Ed25519PublicKey) -> KeyData {
372        Self::Ed25519(public_key)
373    }
374}
375
376#[cfg(feature = "alloc")]
377impl From<RsaPublicKey> for KeyData {
378    fn from(public_key: RsaPublicKey) -> KeyData {
379        Self::Rsa(public_key)
380    }
381}
382
383#[cfg(feature = "ecdsa")]
384impl From<SkEcdsaSha2NistP256> for KeyData {
385    fn from(public_key: SkEcdsaSha2NistP256) -> KeyData {
386        Self::SkEcdsaSha2NistP256(public_key)
387    }
388}
389
390impl From<SkEd25519> for KeyData {
391    fn from(public_key: SkEd25519) -> KeyData {
392        Self::SkEd25519(public_key)
393    }
394}
395
396#[cfg(feature = "alloc")]
397impl From<Certificate> for KeyData {
398    fn from(certificate: Certificate) -> KeyData {
399        Self::Certificate(Box::new(certificate))
400    }
401}