ssh_key/private/
ed25519.rs

1//! Ed25519 private keys.
2//!
3//! Edwards Digital Signature Algorithm (EdDSA) over Curve25519.
4
5use crate::{
6    checked::CheckedSum, decode::Decode, encode::Encode, public::Ed25519PublicKey, reader::Reader,
7    writer::Writer, Error, Result,
8};
9use core::fmt;
10use zeroize::{Zeroize, Zeroizing};
11
12#[cfg(feature = "rand_core")]
13use rand_core::{CryptoRng, RngCore};
14
15#[cfg(feature = "subtle")]
16use subtle::{Choice, ConstantTimeEq};
17
18/// Ed25519 private key.
19// TODO(tarcieri): use `ed25519::PrivateKey`? (doesn't exist yet)
20#[derive(Clone)]
21pub struct Ed25519PrivateKey([u8; Self::BYTE_SIZE]);
22
23impl Ed25519PrivateKey {
24    /// Size of an Ed25519 private key in bytes.
25    pub const BYTE_SIZE: usize = 32;
26
27    /// Generate a random Ed25519 private key.
28    #[cfg(feature = "rand_core")]
29    #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
30    pub fn random(mut rng: impl CryptoRng + RngCore) -> Self {
31        let mut key_bytes = [0u8; Self::BYTE_SIZE];
32        rng.fill_bytes(&mut key_bytes);
33        Self(key_bytes)
34    }
35
36    /// Parse Ed25519 private key from bytes.
37    pub fn from_bytes(bytes: &[u8; Self::BYTE_SIZE]) -> Self {
38        Self(*bytes)
39    }
40
41    /// Convert to the inner byte array.
42    pub fn to_bytes(&self) -> [u8; Self::BYTE_SIZE] {
43        self.0
44    }
45}
46
47impl AsRef<[u8; Self::BYTE_SIZE]> for Ed25519PrivateKey {
48    fn as_ref(&self) -> &[u8; Self::BYTE_SIZE] {
49        &self.0
50    }
51}
52
53impl Drop for Ed25519PrivateKey {
54    fn drop(&mut self) {
55        self.0.zeroize();
56    }
57}
58
59impl TryFrom<&[u8]> for Ed25519PrivateKey {
60    type Error = Error;
61
62    fn try_from(bytes: &[u8]) -> Result<Self> {
63        Ok(Ed25519PrivateKey::from_bytes(bytes.try_into()?))
64    }
65}
66
67impl fmt::Debug for Ed25519PrivateKey {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        f.debug_struct("Ed25519PrivateKey").finish_non_exhaustive()
70    }
71}
72
73impl fmt::LowerHex for Ed25519PrivateKey {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        for byte in self.as_ref() {
76            write!(f, "{:02x}", byte)?;
77        }
78        Ok(())
79    }
80}
81
82impl fmt::UpperHex for Ed25519PrivateKey {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        for byte in self.as_ref() {
85            write!(f, "{:02X}", byte)?;
86        }
87        Ok(())
88    }
89}
90
91#[cfg(feature = "ed25519")]
92#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
93impl From<Ed25519PrivateKey> for ed25519_dalek::SecretKey {
94    fn from(key: Ed25519PrivateKey) -> ed25519_dalek::SecretKey {
95        ed25519_dalek::SecretKey::from(&key)
96    }
97}
98
99#[cfg(feature = "ed25519")]
100#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
101impl From<&Ed25519PrivateKey> for ed25519_dalek::SecretKey {
102    fn from(key: &Ed25519PrivateKey) -> ed25519_dalek::SecretKey {
103        ed25519_dalek::SecretKey::from_bytes(key.as_ref()).expect("invalid Ed25519 key")
104    }
105}
106
107#[cfg(feature = "ed25519")]
108#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
109impl From<ed25519_dalek::SecretKey> for Ed25519PrivateKey {
110    fn from(key: ed25519_dalek::SecretKey) -> Ed25519PrivateKey {
111        Ed25519PrivateKey::from(&key)
112    }
113}
114
115#[cfg(feature = "ed25519")]
116#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
117impl From<&ed25519_dalek::SecretKey> for Ed25519PrivateKey {
118    fn from(key: &ed25519_dalek::SecretKey) -> Ed25519PrivateKey {
119        Ed25519PrivateKey(key.to_bytes())
120    }
121}
122
123#[cfg(feature = "ed25519")]
124#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
125impl From<Ed25519PrivateKey> for Ed25519PublicKey {
126    fn from(private: Ed25519PrivateKey) -> Ed25519PublicKey {
127        Ed25519PublicKey::from(&private)
128    }
129}
130
131#[cfg(feature = "ed25519")]
132#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
133impl From<&Ed25519PrivateKey> for Ed25519PublicKey {
134    fn from(private: &Ed25519PrivateKey) -> Ed25519PublicKey {
135        let secret = ed25519_dalek::SecretKey::from(private);
136        ed25519_dalek::PublicKey::from(&secret).into()
137    }
138}
139
140#[cfg(feature = "subtle")]
141#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
142impl ConstantTimeEq for Ed25519PrivateKey {
143    fn ct_eq(&self, other: &Self) -> Choice {
144        self.as_ref().ct_eq(other.as_ref())
145    }
146}
147
148#[cfg(feature = "subtle")]
149#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
150impl PartialEq for Ed25519PrivateKey {
151    fn eq(&self, other: &Self) -> bool {
152        self.ct_eq(other).into()
153    }
154}
155
156#[cfg(feature = "subtle")]
157#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
158impl Eq for Ed25519PrivateKey {}
159
160/// Ed25519 private/public keypair.
161#[derive(Clone)]
162pub struct Ed25519Keypair {
163    /// Public key.
164    pub public: Ed25519PublicKey,
165
166    /// Private key.
167    pub private: Ed25519PrivateKey,
168}
169
170impl Ed25519Keypair {
171    /// Size of an Ed25519 keypair in bytes.
172    pub const BYTE_SIZE: usize = 64;
173
174    /// Generate a random Ed25519 private keypair.
175    #[cfg(feature = "ed25519")]
176    #[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
177    pub fn random(rng: impl CryptoRng + RngCore) -> Self {
178        Ed25519PrivateKey::random(rng).into()
179    }
180
181    /// Expand a keypair from a 32-byte seed value.
182    #[cfg(feature = "ed25519")]
183    #[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
184    pub fn from_seed(seed: &[u8; Ed25519PrivateKey::BYTE_SIZE]) -> Self {
185        Ed25519PrivateKey::from_bytes(seed).into()
186    }
187
188    /// Parse Ed25519 keypair from 64-bytes which comprise the serialized
189    /// private and public keys.
190    pub fn from_bytes(bytes: &[u8; Self::BYTE_SIZE]) -> Result<Self> {
191        let (priv_bytes, pub_bytes) = bytes.split_at(Ed25519PrivateKey::BYTE_SIZE);
192        let private = Ed25519PrivateKey::try_from(priv_bytes)?;
193        let public = Ed25519PublicKey::try_from(pub_bytes)?;
194
195        // Validate the public key if possible
196        #[cfg(feature = "ed25519")]
197        if Ed25519PublicKey::from(&private) != public {
198            return Err(Error::Crypto);
199        }
200
201        Ok(Ed25519Keypair { private, public })
202    }
203
204    /// Serialize an Ed25519 keypair as bytes.
205    pub fn to_bytes(&self) -> [u8; Self::BYTE_SIZE] {
206        let mut result = [0u8; Self::BYTE_SIZE];
207        result[..(Self::BYTE_SIZE / 2)].copy_from_slice(self.private.as_ref());
208        result[(Self::BYTE_SIZE / 2)..].copy_from_slice(self.public.as_ref());
209        result
210    }
211}
212
213impl Decode for Ed25519Keypair {
214    fn decode(reader: &mut impl Reader) -> Result<Self> {
215        // Decode private key
216        let public = Ed25519PublicKey::decode(reader)?;
217
218        // The OpenSSH serialization of Ed25519 keys is repetitive and includes
219        // a serialization of `private_key[32] || public_key[32]` immediately
220        // following the public key.
221        let mut bytes = Zeroizing::new([0u8; Self::BYTE_SIZE]);
222        reader.read_nested(|reader| reader.read(&mut *bytes))?;
223
224        let keypair = Self::from_bytes(&*bytes)?;
225
226        // Ensure public key matches the one one the keypair
227        if keypair.public == public {
228            Ok(keypair)
229        } else {
230            Err(Error::Crypto)
231        }
232    }
233}
234
235impl Encode for Ed25519Keypair {
236    fn encoded_len(&self) -> Result<usize> {
237        [4, self.public.encoded_len()?, Self::BYTE_SIZE].checked_sum()
238    }
239
240    fn encode(&self, writer: &mut impl Writer) -> Result<()> {
241        self.public.encode(writer)?;
242        Zeroizing::new(self.to_bytes()).as_ref().encode(writer)
243    }
244}
245
246impl From<Ed25519Keypair> for Ed25519PublicKey {
247    fn from(keypair: Ed25519Keypair) -> Ed25519PublicKey {
248        keypair.public
249    }
250}
251
252impl From<&Ed25519Keypair> for Ed25519PublicKey {
253    fn from(keypair: &Ed25519Keypair) -> Ed25519PublicKey {
254        keypair.public
255    }
256}
257
258impl TryFrom<&[u8]> for Ed25519Keypair {
259    type Error = Error;
260
261    fn try_from(bytes: &[u8]) -> Result<Self> {
262        Ed25519Keypair::from_bytes(bytes.try_into()?)
263    }
264}
265
266impl fmt::Debug for Ed25519Keypair {
267    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268        f.debug_struct("Ed25519Keypair")
269            .field("public", &self.public)
270            .finish_non_exhaustive()
271    }
272}
273
274#[cfg(feature = "ed25519")]
275#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
276impl From<Ed25519PrivateKey> for Ed25519Keypair {
277    fn from(private: Ed25519PrivateKey) -> Ed25519Keypair {
278        let secret = ed25519_dalek::SecretKey::from(&private);
279        let public = ed25519_dalek::PublicKey::from(&secret);
280
281        Ed25519Keypair {
282            private,
283            public: public.into(),
284        }
285    }
286}
287
288#[cfg(feature = "ed25519")]
289#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
290impl TryFrom<Ed25519Keypair> for ed25519_dalek::Keypair {
291    type Error = Error;
292
293    fn try_from(key: Ed25519Keypair) -> Result<ed25519_dalek::Keypair> {
294        Ok(ed25519_dalek::Keypair {
295            secret: key.private.into(),
296            public: key.public.try_into()?,
297        })
298    }
299}
300
301#[cfg(feature = "ed25519")]
302#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
303impl TryFrom<&Ed25519Keypair> for ed25519_dalek::Keypair {
304    type Error = Error;
305
306    fn try_from(key: &Ed25519Keypair) -> Result<ed25519_dalek::Keypair> {
307        Ok(ed25519_dalek::Keypair {
308            secret: (&key.private).into(),
309            public: key.public.try_into()?,
310        })
311    }
312}
313
314#[cfg(feature = "ed25519")]
315#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
316impl From<ed25519_dalek::Keypair> for Ed25519Keypair {
317    fn from(key: ed25519_dalek::Keypair) -> Ed25519Keypair {
318        Ed25519Keypair {
319            private: key.secret.into(),
320            public: key.public.into(),
321        }
322    }
323}
324
325#[cfg(feature = "ed25519")]
326#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
327impl From<&ed25519_dalek::Keypair> for Ed25519Keypair {
328    fn from(key: &ed25519_dalek::Keypair) -> Ed25519Keypair {
329        Ed25519Keypair {
330            private: (&key.secret).into(),
331            public: key.public.into(),
332        }
333    }
334}
335
336#[cfg(feature = "subtle")]
337#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
338impl ConstantTimeEq for Ed25519Keypair {
339    fn ct_eq(&self, other: &Self) -> Choice {
340        Choice::from((self.public == other.public) as u8) & self.private.ct_eq(&other.private)
341    }
342}
343
344#[cfg(feature = "subtle")]
345#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
346impl PartialEq for Ed25519Keypair {
347    fn eq(&self, other: &Self) -> bool {
348        self.ct_eq(other).into()
349    }
350}
351
352#[cfg(feature = "subtle")]
353#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
354impl Eq for Ed25519Keypair {}