Skip to main content

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