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