Skip to main content

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