ssh_key/private/
ecdsa.rs

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