ssh_key/public/
sk.rs

1//! Security Key (FIDO/U2F) public keys as described in [PROTOCOL.u2f].
2//!
3//! [PROTOCOL.u2f]: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.u2f?annotate=HEAD
4
5use super::Ed25519PublicKey;
6use crate::{
7    checked::CheckedSum, decode::Decode, encode::Encode, reader::Reader, writer::Writer, Result,
8};
9
10#[cfg(feature = "alloc")]
11use alloc::{borrow::ToOwned, string::String};
12
13#[cfg(feature = "ecdsa")]
14use {
15    super::ecdsa::EcdsaNistP256PublicKey,
16    crate::{EcdsaCurve, Error},
17};
18
19/// Default FIDO/U2F Security Key application string.
20const DEFAULT_APPLICATION_STRING: &str = "ssh:";
21
22/// Security Key (FIDO/U2F) ECDSA/NIST P-256 public key as specified in
23/// [PROTOCOL.u2f](https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.u2f?annotate=HEAD).
24#[cfg(feature = "ecdsa")]
25#[cfg_attr(docsrs, doc(cfg(feature = "ecdsa")))]
26#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
27pub struct SkEcdsaSha2NistP256 {
28    /// Elliptic curve point representing a public key.
29    ec_point: EcdsaNistP256PublicKey,
30
31    /// FIDO/U2F application (typically `ssh:`)
32    #[cfg(feature = "alloc")]
33    application: String,
34}
35
36#[cfg(feature = "ecdsa")]
37impl SkEcdsaSha2NistP256 {
38    /// Get the elliptic curve point for this Security Key.
39    pub fn ec_point(&self) -> &EcdsaNistP256PublicKey {
40        &self.ec_point
41    }
42
43    /// Get the FIDO/U2F application (typically `ssh:`).
44    #[cfg(not(feature = "alloc"))]
45    pub fn application(&self) -> &str {
46        DEFAULT_APPLICATION_STRING
47    }
48
49    /// Get the FIDO/U2F application (typically `ssh:`).
50    #[cfg(feature = "alloc")]
51    pub fn application(&self) -> &str {
52        &self.application
53    }
54}
55
56#[cfg(feature = "ecdsa")]
57impl Decode for SkEcdsaSha2NistP256 {
58    fn decode(reader: &mut impl Reader) -> Result<Self> {
59        if EcdsaCurve::decode(reader)? != EcdsaCurve::NistP256 {
60            return Err(Error::Crypto);
61        }
62
63        let mut buf = [0u8; 65];
64        let ec_point = EcdsaNistP256PublicKey::from_bytes(reader.read_byten(&mut buf)?)?;
65
66        // application string (e.g. `ssh:`)
67        #[cfg(not(feature = "alloc"))]
68        reader.drain_prefixed()?;
69
70        Ok(Self {
71            ec_point,
72
73            #[cfg(feature = "alloc")]
74            application: String::decode(reader)?,
75        })
76    }
77}
78
79#[cfg(feature = "ecdsa")]
80impl Encode for SkEcdsaSha2NistP256 {
81    fn encoded_len(&self) -> Result<usize> {
82        [
83            EcdsaCurve::NistP256.encoded_len()?,
84            self.ec_point.as_bytes().encoded_len()?,
85            self.application().encoded_len()?,
86        ]
87        .checked_sum()
88    }
89
90    fn encode(&self, writer: &mut impl Writer) -> Result<()> {
91        EcdsaCurve::NistP256.encode(writer)?;
92        self.ec_point.as_bytes().encode(writer)?;
93        self.application().encode(writer)
94    }
95}
96
97#[cfg(feature = "ecdsa")]
98impl From<EcdsaNistP256PublicKey> for SkEcdsaSha2NistP256 {
99    fn from(ec_point: EcdsaNistP256PublicKey) -> SkEcdsaSha2NistP256 {
100        SkEcdsaSha2NistP256 {
101            ec_point,
102            #[cfg(feature = "alloc")]
103            application: DEFAULT_APPLICATION_STRING.to_owned(),
104        }
105    }
106}
107
108#[cfg(feature = "ecdsa")]
109impl From<SkEcdsaSha2NistP256> for EcdsaNistP256PublicKey {
110    fn from(sk: SkEcdsaSha2NistP256) -> EcdsaNistP256PublicKey {
111        sk.ec_point
112    }
113}
114
115/// Security Key (FIDO/U2F) Ed25519 public key as specified in
116/// [PROTOCOL.u2f](https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.u2f?annotate=HEAD).
117#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
118pub struct SkEd25519 {
119    /// Ed25519 public key.
120    public_key: Ed25519PublicKey,
121
122    /// FIDO/U2F application (typically `ssh:`)
123    #[cfg(feature = "alloc")]
124    application: String,
125}
126
127impl SkEd25519 {
128    /// Get the Ed25519 private key for this security key.
129    pub fn public_key(&self) -> &Ed25519PublicKey {
130        &self.public_key
131    }
132
133    /// Get the FIDO/U2F application (typically `ssh:`).
134    #[cfg(not(feature = "alloc"))]
135    pub fn application(&self) -> &str {
136        DEFAULT_APPLICATION_STRING
137    }
138
139    /// Get the FIDO/U2F application (typically `ssh:`).
140    #[cfg(feature = "alloc")]
141    pub fn application(&self) -> &str {
142        &self.application
143    }
144}
145
146impl Decode for SkEd25519 {
147    fn decode(reader: &mut impl Reader) -> Result<Self> {
148        let public_key = Ed25519PublicKey::decode(reader)?;
149
150        // application string (e.g. `ssh:`)
151        #[cfg(not(feature = "alloc"))]
152        reader.drain_prefixed()?;
153
154        Ok(Self {
155            public_key,
156
157            #[cfg(feature = "alloc")]
158            application: String::decode(reader)?,
159        })
160    }
161}
162
163impl Encode for SkEd25519 {
164    fn encoded_len(&self) -> Result<usize> {
165        [
166            self.public_key.encoded_len()?,
167            self.application().encoded_len()?,
168        ]
169        .checked_sum()
170    }
171
172    fn encode(&self, writer: &mut impl Writer) -> Result<()> {
173        self.public_key.encode(writer)?;
174        self.application().encode(writer)
175    }
176}
177
178impl From<Ed25519PublicKey> for SkEd25519 {
179    fn from(public_key: Ed25519PublicKey) -> SkEd25519 {
180        SkEd25519 {
181            public_key,
182            #[cfg(feature = "alloc")]
183            application: DEFAULT_APPLICATION_STRING.to_owned(),
184        }
185    }
186}
187
188impl From<SkEd25519> for Ed25519PublicKey {
189    fn from(sk: SkEd25519) -> Ed25519PublicKey {
190        sk.public_key
191    }
192}