/rust/registry/src/index.crates.io-1949cf8c6b5b557f/elliptic-curve-0.13.8/src/secret_key.rs
Line | Count | Source |
1 | | //! Secret keys for elliptic curves (i.e. private scalars). |
2 | | //! |
3 | | //! The [`SecretKey`] type is a wrapper around a secret scalar value which is |
4 | | //! designed to prevent unintentional exposure (e.g. via `Debug` or other |
5 | | //! logging). It also handles zeroing the secret value out of memory securely |
6 | | //! on drop. |
7 | | |
8 | | #[cfg(all(feature = "pkcs8", feature = "sec1"))] |
9 | | mod pkcs8; |
10 | | |
11 | | use crate::{Curve, Error, FieldBytes, Result, ScalarPrimitive}; |
12 | | use core::fmt::{self, Debug}; |
13 | | use generic_array::typenum::Unsigned; |
14 | | use subtle::{Choice, ConstantTimeEq}; |
15 | | use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; |
16 | | |
17 | | #[cfg(feature = "arithmetic")] |
18 | | use crate::{rand_core::CryptoRngCore, CurveArithmetic, NonZeroScalar, PublicKey}; |
19 | | |
20 | | #[cfg(feature = "jwk")] |
21 | | use crate::jwk::{JwkEcKey, JwkParameters}; |
22 | | |
23 | | #[cfg(feature = "pem")] |
24 | | use pem_rfc7468::{self as pem, PemLabel}; |
25 | | |
26 | | #[cfg(feature = "sec1")] |
27 | | use { |
28 | | crate::{ |
29 | | sec1::{EncodedPoint, ModulusSize, ValidatePublicKey}, |
30 | | FieldBytesSize, |
31 | | }, |
32 | | sec1::der, |
33 | | }; |
34 | | |
35 | | #[cfg(all(feature = "alloc", feature = "arithmetic", feature = "sec1"))] |
36 | | use { |
37 | | crate::{ |
38 | | sec1::{FromEncodedPoint, ToEncodedPoint}, |
39 | | AffinePoint, |
40 | | }, |
41 | | alloc::vec::Vec, |
42 | | sec1::der::Encode, |
43 | | }; |
44 | | |
45 | | #[cfg(all(feature = "arithmetic", any(feature = "jwk", feature = "pem")))] |
46 | | use alloc::string::String; |
47 | | |
48 | | #[cfg(all(feature = "arithmetic", feature = "jwk"))] |
49 | | use alloc::string::ToString; |
50 | | |
51 | | #[cfg(all(doc, feature = "pkcs8"))] |
52 | | use {crate::pkcs8::DecodePrivateKey, core::str::FromStr}; |
53 | | |
54 | | /// Elliptic curve secret keys. |
55 | | /// |
56 | | /// This type wraps a secret scalar value, helping to prevent accidental |
57 | | /// exposure and securely erasing the value from memory when dropped. |
58 | | /// |
59 | | /// # Parsing PKCS#8 Keys |
60 | | /// |
61 | | /// PKCS#8 is a commonly used format for encoding secret keys (especially ones |
62 | | /// generated by OpenSSL). |
63 | | /// |
64 | | /// Keys in PKCS#8 format are either binary (ASN.1 BER/DER), or PEM encoded |
65 | | /// (ASCII) and begin with the following: |
66 | | /// |
67 | | /// ```text |
68 | | /// -----BEGIN PRIVATE KEY----- |
69 | | /// ``` |
70 | | /// |
71 | | /// To decode an elliptic curve private key from PKCS#8, enable the `pkcs8` |
72 | | /// feature of this crate (or the `pkcs8` feature of a specific RustCrypto |
73 | | /// elliptic curve crate) and use the [`DecodePrivateKey`] trait to parse it. |
74 | | /// |
75 | | /// When the `pem` feature of this crate (or a specific RustCrypto elliptic |
76 | | /// curve crate) is enabled, a [`FromStr`] impl is also available. |
77 | | #[derive(Clone)] |
78 | | pub struct SecretKey<C: Curve> { |
79 | | /// Scalar value |
80 | | inner: ScalarPrimitive<C>, |
81 | | } |
82 | | |
83 | | impl<C> SecretKey<C> |
84 | | where |
85 | | C: Curve, |
86 | | { |
87 | | /// Minimum allowed size of an elliptic curve secret key in bytes. |
88 | | /// |
89 | | /// This provides the equivalent of 96-bits of symmetric security. |
90 | | const MIN_SIZE: usize = 24; |
91 | | |
92 | | /// Generate a random [`SecretKey`]. |
93 | | #[cfg(feature = "arithmetic")] |
94 | | pub fn random(rng: &mut impl CryptoRngCore) -> Self |
95 | | where |
96 | | C: CurveArithmetic, |
97 | | { |
98 | | Self { |
99 | | inner: NonZeroScalar::<C>::random(rng).into(), |
100 | | } |
101 | | } |
102 | | |
103 | | /// Create a new secret key from a scalar value. |
104 | | pub fn new(scalar: ScalarPrimitive<C>) -> Self { |
105 | | Self { inner: scalar } |
106 | | } |
107 | | |
108 | | /// Borrow the inner secret [`ScalarPrimitive`] value. |
109 | | /// |
110 | | /// # ⚠️ Warning |
111 | | /// |
112 | | /// This value is key material. |
113 | | /// |
114 | | /// Please treat it with the care it deserves! |
115 | 83 | pub fn as_scalar_primitive(&self) -> &ScalarPrimitive<C> { |
116 | 83 | &self.inner |
117 | 83 | } |
118 | | |
119 | | /// Get the secret [`NonZeroScalar`] value for this key. |
120 | | /// |
121 | | /// # ⚠️ Warning |
122 | | /// |
123 | | /// This value is key material. |
124 | | /// |
125 | | /// Please treat it with the care it deserves! |
126 | | #[cfg(feature = "arithmetic")] |
127 | 83 | pub fn to_nonzero_scalar(&self) -> NonZeroScalar<C> |
128 | 83 | where |
129 | 83 | C: CurveArithmetic, |
130 | | { |
131 | 83 | self.into() |
132 | 83 | } |
133 | | |
134 | | /// Get the [`PublicKey`] which corresponds to this secret key |
135 | | #[cfg(feature = "arithmetic")] |
136 | | pub fn public_key(&self) -> PublicKey<C> |
137 | | where |
138 | | C: CurveArithmetic, |
139 | | { |
140 | | PublicKey::from_secret_scalar(&self.to_nonzero_scalar()) |
141 | | } |
142 | | |
143 | | /// Deserialize secret key from an encoded secret scalar. |
144 | 83 | pub fn from_bytes(bytes: &FieldBytes<C>) -> Result<Self> { |
145 | 83 | let inner: ScalarPrimitive<C> = |
146 | 83 | Option::from(ScalarPrimitive::from_bytes(bytes)).ok_or(Error)?; |
147 | | |
148 | 83 | if inner.is_zero().into() { |
149 | 0 | return Err(Error); |
150 | 83 | } |
151 | | |
152 | 83 | Ok(Self { inner }) |
153 | 83 | } |
154 | | |
155 | | /// Deserialize secret key from an encoded secret scalar passed as a byte slice. |
156 | | /// |
157 | | /// The slice is expected to be a minimum of 24-bytes (192-byts) and at most `C::FieldBytesSize` |
158 | | /// bytes in length. |
159 | | /// |
160 | | /// Byte slices shorter than the field size are handled by zero padding the input. |
161 | 83 | pub fn from_slice(slice: &[u8]) -> Result<Self> { |
162 | 83 | if slice.len() == C::FieldBytesSize::USIZE { |
163 | 83 | Self::from_bytes(FieldBytes::<C>::from_slice(slice)) |
164 | 0 | } else if (Self::MIN_SIZE..C::FieldBytesSize::USIZE).contains(&slice.len()) { |
165 | 0 | let mut bytes = Zeroizing::new(FieldBytes::<C>::default()); |
166 | 0 | let offset = C::FieldBytesSize::USIZE.saturating_sub(slice.len()); |
167 | 0 | bytes[offset..].copy_from_slice(slice); |
168 | 0 | Self::from_bytes(&bytes) |
169 | | } else { |
170 | 0 | Err(Error) |
171 | | } |
172 | 83 | } |
173 | | |
174 | | /// Serialize raw secret scalar as a big endian integer. |
175 | | pub fn to_bytes(&self) -> FieldBytes<C> { |
176 | | self.inner.to_bytes() |
177 | | } |
178 | | |
179 | | /// Deserialize secret key encoded in the SEC1 ASN.1 DER `ECPrivateKey` format. |
180 | | #[cfg(feature = "sec1")] |
181 | | pub fn from_sec1_der(der_bytes: &[u8]) -> Result<Self> |
182 | | where |
183 | | C: Curve + ValidatePublicKey, |
184 | | FieldBytesSize<C>: ModulusSize, |
185 | | { |
186 | | sec1::EcPrivateKey::try_from(der_bytes)? |
187 | | .try_into() |
188 | | .map_err(|_| Error) |
189 | | } |
190 | | |
191 | | /// Serialize secret key in the SEC1 ASN.1 DER `ECPrivateKey` format. |
192 | | #[cfg(all(feature = "alloc", feature = "arithmetic", feature = "sec1"))] |
193 | | pub fn to_sec1_der(&self) -> der::Result<Zeroizing<Vec<u8>>> |
194 | | where |
195 | | C: CurveArithmetic, |
196 | | AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, |
197 | | FieldBytesSize<C>: ModulusSize, |
198 | | { |
199 | | let private_key_bytes = Zeroizing::new(self.to_bytes()); |
200 | | let public_key_bytes = self.public_key().to_encoded_point(false); |
201 | | |
202 | | let ec_private_key = Zeroizing::new( |
203 | | sec1::EcPrivateKey { |
204 | | private_key: &private_key_bytes, |
205 | | parameters: None, |
206 | | public_key: Some(public_key_bytes.as_bytes()), |
207 | | } |
208 | | .to_der()?, |
209 | | ); |
210 | | |
211 | | Ok(ec_private_key) |
212 | | } |
213 | | |
214 | | /// Parse [`SecretKey`] from PEM-encoded SEC1 `ECPrivateKey` format. |
215 | | /// |
216 | | /// PEM-encoded SEC1 keys can be identified by the leading delimiter: |
217 | | /// |
218 | | /// ```text |
219 | | /// -----BEGIN EC PRIVATE KEY----- |
220 | | /// ``` |
221 | | #[cfg(feature = "pem")] |
222 | | pub fn from_sec1_pem(s: &str) -> Result<Self> |
223 | | where |
224 | | C: Curve + ValidatePublicKey, |
225 | | FieldBytesSize<C>: ModulusSize, |
226 | | { |
227 | | let (label, der_bytes) = pem::decode_vec(s.as_bytes()).map_err(|_| Error)?; |
228 | | |
229 | | if label != sec1::EcPrivateKey::PEM_LABEL { |
230 | | return Err(Error); |
231 | | } |
232 | | |
233 | | Self::from_sec1_der(&der_bytes).map_err(|_| Error) |
234 | | } |
235 | | |
236 | | /// Serialize private key as self-zeroizing PEM-encoded SEC1 `ECPrivateKey` |
237 | | /// with the given [`pem::LineEnding`]. |
238 | | /// |
239 | | /// Pass `Default::default()` to use the OS's native line endings. |
240 | | #[cfg(feature = "pem")] |
241 | | pub fn to_sec1_pem(&self, line_ending: pem::LineEnding) -> Result<Zeroizing<String>> |
242 | | where |
243 | | C: CurveArithmetic, |
244 | | AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, |
245 | | FieldBytesSize<C>: ModulusSize, |
246 | | { |
247 | | self.to_sec1_der() |
248 | | .ok() |
249 | | .and_then(|der| { |
250 | | pem::encode_string(sec1::EcPrivateKey::PEM_LABEL, line_ending, &der).ok() |
251 | | }) |
252 | | .map(Zeroizing::new) |
253 | | .ok_or(Error) |
254 | | } |
255 | | |
256 | | /// Parse a [`JwkEcKey`] JSON Web Key (JWK) into a [`SecretKey`]. |
257 | | #[cfg(feature = "jwk")] |
258 | | pub fn from_jwk(jwk: &JwkEcKey) -> Result<Self> |
259 | | where |
260 | | C: JwkParameters + ValidatePublicKey, |
261 | | FieldBytesSize<C>: ModulusSize, |
262 | | { |
263 | | Self::try_from(jwk) |
264 | | } |
265 | | |
266 | | /// Parse a string containing a JSON Web Key (JWK) into a [`SecretKey`]. |
267 | | #[cfg(feature = "jwk")] |
268 | | pub fn from_jwk_str(jwk: &str) -> Result<Self> |
269 | | where |
270 | | C: JwkParameters + ValidatePublicKey, |
271 | | FieldBytesSize<C>: ModulusSize, |
272 | | { |
273 | | jwk.parse::<JwkEcKey>().and_then(|jwk| Self::from_jwk(&jwk)) |
274 | | } |
275 | | |
276 | | /// Serialize this secret key as [`JwkEcKey`] JSON Web Key (JWK). |
277 | | #[cfg(all(feature = "arithmetic", feature = "jwk"))] |
278 | | pub fn to_jwk(&self) -> JwkEcKey |
279 | | where |
280 | | C: CurveArithmetic + JwkParameters, |
281 | | AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, |
282 | | FieldBytesSize<C>: ModulusSize, |
283 | | { |
284 | | self.into() |
285 | | } |
286 | | |
287 | | /// Serialize this secret key as JSON Web Key (JWK) string. |
288 | | #[cfg(all(feature = "arithmetic", feature = "jwk"))] |
289 | | pub fn to_jwk_string(&self) -> Zeroizing<String> |
290 | | where |
291 | | C: CurveArithmetic + JwkParameters, |
292 | | AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, |
293 | | FieldBytesSize<C>: ModulusSize, |
294 | | { |
295 | | Zeroizing::new(self.to_jwk().to_string()) |
296 | | } |
297 | | } |
298 | | |
299 | | impl<C> ConstantTimeEq for SecretKey<C> |
300 | | where |
301 | | C: Curve, |
302 | | { |
303 | | fn ct_eq(&self, other: &Self) -> Choice { |
304 | | self.inner.ct_eq(&other.inner) |
305 | | } |
306 | | } |
307 | | |
308 | | impl<C> Debug for SecretKey<C> |
309 | | where |
310 | | C: Curve, |
311 | | { |
312 | | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
313 | | f.debug_struct(core::any::type_name::<Self>()) |
314 | | .finish_non_exhaustive() |
315 | | } |
316 | | } |
317 | | |
318 | | impl<C> ZeroizeOnDrop for SecretKey<C> where C: Curve {} |
319 | | |
320 | | impl<C> Drop for SecretKey<C> |
321 | | where |
322 | | C: Curve, |
323 | | { |
324 | 83 | fn drop(&mut self) { |
325 | 83 | self.inner.zeroize(); |
326 | 83 | } |
327 | | } |
328 | | |
329 | | impl<C: Curve> Eq for SecretKey<C> {} |
330 | | |
331 | | impl<C> PartialEq for SecretKey<C> |
332 | | where |
333 | | C: Curve, |
334 | | { |
335 | | fn eq(&self, other: &Self) -> bool { |
336 | | self.ct_eq(other).into() |
337 | | } |
338 | | } |
339 | | |
340 | | #[cfg(feature = "sec1")] |
341 | | impl<C> TryFrom<sec1::EcPrivateKey<'_>> for SecretKey<C> |
342 | | where |
343 | | C: Curve + ValidatePublicKey, |
344 | | FieldBytesSize<C>: ModulusSize, |
345 | | { |
346 | | type Error = der::Error; |
347 | | |
348 | | fn try_from(sec1_private_key: sec1::EcPrivateKey<'_>) -> der::Result<Self> { |
349 | | let secret_key = Self::from_slice(sec1_private_key.private_key) |
350 | | .map_err(|_| der::Tag::Sequence.value_error())?; |
351 | | |
352 | | // TODO(tarcieri): validate `sec1_private_key.params`? |
353 | | if let Some(pk_bytes) = sec1_private_key.public_key { |
354 | | let pk = EncodedPoint::<C>::from_bytes(pk_bytes) |
355 | | .map_err(|_| der::Tag::BitString.value_error())?; |
356 | | |
357 | | if C::validate_public_key(&secret_key, &pk).is_err() { |
358 | | return Err(der::Tag::BitString.value_error()); |
359 | | } |
360 | | } |
361 | | |
362 | | Ok(secret_key) |
363 | | } |
364 | | } |
365 | | |
366 | | #[cfg(feature = "arithmetic")] |
367 | | impl<C> From<NonZeroScalar<C>> for SecretKey<C> |
368 | | where |
369 | | C: CurveArithmetic, |
370 | | { |
371 | | fn from(scalar: NonZeroScalar<C>) -> SecretKey<C> { |
372 | | SecretKey::from(&scalar) |
373 | | } |
374 | | } |
375 | | |
376 | | #[cfg(feature = "arithmetic")] |
377 | | impl<C> From<&NonZeroScalar<C>> for SecretKey<C> |
378 | | where |
379 | | C: CurveArithmetic, |
380 | | { |
381 | | fn from(scalar: &NonZeroScalar<C>) -> SecretKey<C> { |
382 | | SecretKey { |
383 | | inner: scalar.into(), |
384 | | } |
385 | | } |
386 | | } |