ssh_key/private/
ed25519.rs
1use 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#[derive(Clone)]
21pub struct Ed25519PrivateKey([u8; Self::BYTE_SIZE]);
22
23impl Ed25519PrivateKey {
24 pub const BYTE_SIZE: usize = 32;
26
27 #[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 pub fn from_bytes(bytes: &[u8; Self::BYTE_SIZE]) -> Self {
38 Self(*bytes)
39 }
40
41 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#[derive(Clone)]
162pub struct Ed25519Keypair {
163 pub public: Ed25519PublicKey,
165
166 pub private: Ed25519PrivateKey,
168}
169
170impl Ed25519Keypair {
171 pub const BYTE_SIZE: usize = 64;
173
174 #[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 #[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 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 #[cfg(feature = "ed25519")]
197 if Ed25519PublicKey::from(&private) != public {
198 return Err(Error::Crypto);
199 }
200
201 Ok(Ed25519Keypair { private, public })
202 }
203
204 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 let public = Ed25519PublicKey::decode(reader)?;
217
218 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 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 {}