ssh_key/private/
ecdsa.rs

1//! Elliptic Curve Digital Signature Algorithm (ECDSA) private keys.
2
3use crate::{public::EcdsaPublicKey, Algorithm, EcdsaCurve, Error, Result};
4use core::fmt;
5use encoding::{CheckedSum, Decode, Encode, Reader, Writer};
6use sec1::consts::{U32, U48, U66};
7use subtle::{Choice, ConstantTimeEq};
8use zeroize::Zeroize;
9
10#[cfg(feature = "rand_core")]
11use rand_core::CryptoRngCore;
12
13/// Elliptic Curve Digital Signature Algorithm (ECDSA) private key.
14#[derive(Clone)]
15pub struct EcdsaPrivateKey<const SIZE: usize> {
16    /// Byte array containing serialized big endian private scalar.
17    bytes: [u8; SIZE],
18}
19
20impl<const SIZE: usize> EcdsaPrivateKey<SIZE> {
21    /// Borrow the inner byte array as a slice.
22    pub fn as_slice(&self) -> &[u8] {
23        self.bytes.as_ref()
24    }
25
26    /// Convert to the inner byte array.
27    pub fn into_bytes(self) -> [u8; SIZE] {
28        self.bytes
29    }
30
31    /// Does this private key need to be prefixed with a leading zero?
32    fn needs_leading_zero(&self) -> bool {
33        self.bytes[0] >= 0x80
34    }
35}
36
37impl<const SIZE: usize> Decode for EcdsaPrivateKey<SIZE> {
38    type Error = Error;
39
40    fn decode(reader: &mut impl Reader) -> Result<Self> {
41        reader.read_prefixed(|reader| {
42            if reader.remaining_len() == SIZE.checked_add(1).ok_or(encoding::Error::Length)? {
43                // Strip leading zero
44                // TODO(tarcieri): make sure leading zero was necessary
45                if u8::decode(reader)? != 0 {
46                    return Err(Error::FormatEncoding);
47                }
48            }
49
50            let mut bytes = [0u8; SIZE];
51            reader.read(&mut bytes)?;
52            Ok(Self { bytes })
53        })
54    }
55}
56
57impl<const SIZE: usize> Encode for EcdsaPrivateKey<SIZE> {
58    fn encoded_len(&self) -> encoding::Result<usize> {
59        [4, self.needs_leading_zero().into(), SIZE].checked_sum()
60    }
61
62    fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
63        [self.needs_leading_zero().into(), SIZE]
64            .checked_sum()?
65            .encode(writer)?;
66
67        if self.needs_leading_zero() {
68            writer.write(&[0])?;
69        }
70
71        writer.write(&self.bytes)?;
72        Ok(())
73    }
74}
75
76impl<const SIZE: usize> AsRef<[u8; SIZE]> for EcdsaPrivateKey<SIZE> {
77    fn as_ref(&self) -> &[u8; SIZE] {
78        &self.bytes
79    }
80}
81
82impl<const SIZE: usize> ConstantTimeEq for EcdsaPrivateKey<SIZE> {
83    fn ct_eq(&self, other: &Self) -> Choice {
84        self.as_ref().ct_eq(other.as_ref())
85    }
86}
87
88impl<const SIZE: usize> PartialEq for EcdsaPrivateKey<SIZE> {
89    fn eq(&self, other: &Self) -> bool {
90        self.ct_eq(other).into()
91    }
92}
93
94impl<const SIZE: usize> Eq for EcdsaPrivateKey<SIZE> {}
95
96impl<const SIZE: usize> fmt::Debug for EcdsaPrivateKey<SIZE> {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        f.debug_struct("EcdsaPrivateKey").finish_non_exhaustive()
99    }
100}
101
102impl<const SIZE: usize> fmt::LowerHex for EcdsaPrivateKey<SIZE> {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        for byte in self.as_ref() {
105            write!(f, "{byte:02x}")?;
106        }
107        Ok(())
108    }
109}
110
111impl<const SIZE: usize> fmt::UpperHex for EcdsaPrivateKey<SIZE> {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        for byte in self.as_ref() {
114            write!(f, "{byte:02X}")?;
115        }
116        Ok(())
117    }
118}
119
120impl<const SIZE: usize> Drop for EcdsaPrivateKey<SIZE> {
121    fn drop(&mut self) {
122        self.bytes.zeroize();
123    }
124}
125
126#[cfg(feature = "p256")]
127impl From<p256::SecretKey> for EcdsaPrivateKey<32> {
128    fn from(sk: p256::SecretKey) -> EcdsaPrivateKey<32> {
129        EcdsaPrivateKey {
130            bytes: sk.to_bytes().into(),
131        }
132    }
133}
134
135#[cfg(feature = "p384")]
136impl From<p384::SecretKey> for EcdsaPrivateKey<48> {
137    fn from(sk: p384::SecretKey) -> EcdsaPrivateKey<48> {
138        EcdsaPrivateKey {
139            bytes: sk.to_bytes().into(),
140        }
141    }
142}
143
144#[cfg(feature = "p521")]
145impl From<p521::SecretKey> for EcdsaPrivateKey<66> {
146    fn from(sk: p521::SecretKey) -> EcdsaPrivateKey<66> {
147        // TODO(tarcieri): clean this up when migrating to hybrid-array
148        let mut bytes = [0u8; 66];
149        bytes.copy_from_slice(&sk.to_bytes());
150        EcdsaPrivateKey { bytes }
151    }
152}
153
154/// Elliptic Curve Digital Signature Algorithm (ECDSA) private/public keypair.
155#[derive(Clone, Debug)]
156pub enum EcdsaKeypair {
157    /// NIST P-256 ECDSA keypair.
158    NistP256 {
159        /// Public key.
160        public: sec1::EncodedPoint<U32>,
161
162        /// Private key.
163        private: EcdsaPrivateKey<32>,
164    },
165
166    /// NIST P-384 ECDSA keypair.
167    NistP384 {
168        /// Public key.
169        public: sec1::EncodedPoint<U48>,
170
171        /// Private key.
172        private: EcdsaPrivateKey<48>,
173    },
174
175    /// NIST P-521 ECDSA keypair.
176    NistP521 {
177        /// Public key.
178        public: sec1::EncodedPoint<U66>,
179
180        /// Private key.
181        private: EcdsaPrivateKey<66>,
182    },
183}
184
185impl EcdsaKeypair {
186    /// Generate a random ECDSA private key.
187    #[cfg(feature = "rand_core")]
188    #[allow(unused_variables)]
189    pub fn random(rng: &mut impl CryptoRngCore, curve: EcdsaCurve) -> Result<Self> {
190        match curve {
191            #[cfg(feature = "p256")]
192            EcdsaCurve::NistP256 => {
193                let private = p256::SecretKey::random(rng);
194                let public = private.public_key();
195                Ok(EcdsaKeypair::NistP256 {
196                    private: private.into(),
197                    public: public.into(),
198                })
199            }
200            #[cfg(feature = "p384")]
201            EcdsaCurve::NistP384 => {
202                let private = p384::SecretKey::random(rng);
203                let public = private.public_key();
204                Ok(EcdsaKeypair::NistP384 {
205                    private: private.into(),
206                    public: public.into(),
207                })
208            }
209            #[cfg(feature = "p521")]
210            EcdsaCurve::NistP521 => {
211                let private = p521::SecretKey::random(rng);
212                let public = private.public_key();
213                Ok(EcdsaKeypair::NistP521 {
214                    private: private.into(),
215                    public: public.into(),
216                })
217            }
218            #[cfg(not(all(feature = "p256", feature = "p384", feature = "p521")))]
219            _ => Err(Error::AlgorithmUnknown),
220        }
221    }
222
223    /// Get the [`Algorithm`] for this public key type.
224    pub fn algorithm(&self) -> Algorithm {
225        Algorithm::Ecdsa {
226            curve: self.curve(),
227        }
228    }
229
230    /// Get the [`EcdsaCurve`] for this key.
231    pub fn curve(&self) -> EcdsaCurve {
232        match self {
233            Self::NistP256 { .. } => EcdsaCurve::NistP256,
234            Self::NistP384 { .. } => EcdsaCurve::NistP384,
235            Self::NistP521 { .. } => EcdsaCurve::NistP521,
236        }
237    }
238
239    /// Get the bytes representing the public key.
240    pub fn public_key_bytes(&self) -> &[u8] {
241        match self {
242            Self::NistP256 { public, .. } => public.as_ref(),
243            Self::NistP384 { public, .. } => public.as_ref(),
244            Self::NistP521 { public, .. } => public.as_ref(),
245        }
246    }
247
248    /// Get the bytes representing the private key.
249    pub fn private_key_bytes(&self) -> &[u8] {
250        match self {
251            Self::NistP256 { private, .. } => private.as_ref(),
252            Self::NistP384 { private, .. } => private.as_ref(),
253            Self::NistP521 { private, .. } => private.as_ref(),
254        }
255    }
256}
257
258impl ConstantTimeEq for EcdsaKeypair {
259    fn ct_eq(&self, other: &Self) -> Choice {
260        let public_eq =
261            Choice::from((EcdsaPublicKey::from(self) == EcdsaPublicKey::from(other)) as u8);
262
263        let private_key_a = match self {
264            Self::NistP256 { private, .. } => private.as_slice(),
265            Self::NistP384 { private, .. } => private.as_slice(),
266            Self::NistP521 { private, .. } => private.as_slice(),
267        };
268
269        let private_key_b = match other {
270            Self::NistP256 { private, .. } => private.as_slice(),
271            Self::NistP384 { private, .. } => private.as_slice(),
272            Self::NistP521 { private, .. } => private.as_slice(),
273        };
274
275        public_eq & private_key_a.ct_eq(private_key_b)
276    }
277}
278
279impl Eq for EcdsaKeypair {}
280
281impl PartialEq for EcdsaKeypair {
282    fn eq(&self, other: &Self) -> bool {
283        self.ct_eq(other).into()
284    }
285}
286
287impl Decode for EcdsaKeypair {
288    type Error = Error;
289
290    fn decode(reader: &mut impl Reader) -> Result<Self> {
291        match EcdsaPublicKey::decode(reader)? {
292            EcdsaPublicKey::NistP256(public) => {
293                let private = EcdsaPrivateKey::<32>::decode(reader)?;
294                Ok(Self::NistP256 { public, private })
295            }
296            EcdsaPublicKey::NistP384(public) => {
297                let private = EcdsaPrivateKey::<48>::decode(reader)?;
298                Ok(Self::NistP384 { public, private })
299            }
300            EcdsaPublicKey::NistP521(public) => {
301                let private = EcdsaPrivateKey::<66>::decode(reader)?;
302                Ok(Self::NistP521 { public, private })
303            }
304        }
305    }
306}
307
308impl Encode for EcdsaKeypair {
309    fn encoded_len(&self) -> encoding::Result<usize> {
310        let public_len = EcdsaPublicKey::from(self).encoded_len()?;
311
312        let private_len = match self {
313            Self::NistP256 { private, .. } => private.encoded_len()?,
314            Self::NistP384 { private, .. } => private.encoded_len()?,
315            Self::NistP521 { private, .. } => private.encoded_len()?,
316        };
317
318        [public_len, private_len].checked_sum()
319    }
320
321    fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
322        EcdsaPublicKey::from(self).encode(writer)?;
323
324        match self {
325            Self::NistP256 { private, .. } => private.encode(writer)?,
326            Self::NistP384 { private, .. } => private.encode(writer)?,
327            Self::NistP521 { private, .. } => private.encode(writer)?,
328        }
329
330        Ok(())
331    }
332}
333
334impl From<EcdsaKeypair> for EcdsaPublicKey {
335    fn from(keypair: EcdsaKeypair) -> EcdsaPublicKey {
336        EcdsaPublicKey::from(&keypair)
337    }
338}
339
340impl From<&EcdsaKeypair> for EcdsaPublicKey {
341    fn from(keypair: &EcdsaKeypair) -> EcdsaPublicKey {
342        match keypair {
343            EcdsaKeypair::NistP256 { public, .. } => EcdsaPublicKey::NistP256(*public),
344            EcdsaKeypair::NistP384 { public, .. } => EcdsaPublicKey::NistP384(*public),
345            EcdsaKeypair::NistP521 { public, .. } => EcdsaPublicKey::NistP521(*public),
346        }
347    }
348}