1#[cfg(all(feature = "pkcs8", feature = "sec1"))]
9mod pkcs8;
10
11use crate::{Curve, Error, FieldBytes, Result, ScalarValue, bigint::ByteOrder};
12use array::typenum::Unsigned;
13use common::{Generate, InvalidKey, KeySizeUser, TryKeyInit};
14use core::fmt::{self, Debug};
15use rand_core::{CryptoRng, TryCryptoRng};
16use subtle::{Choice, ConstantTimeEq, CtOption};
17use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
18
19#[cfg(feature = "ecdh")]
20use crate::ecdh;
21#[cfg(feature = "arithmetic")]
22use crate::{CurveArithmetic, NonZeroScalar, PublicKey};
23#[cfg(all(feature = "arithmetic", feature = "pem"))]
24use alloc::string::String;
25#[cfg(feature = "pem")]
26use pem_rfc7468::{self as pem, PemLabel};
27#[cfg(all(feature = "alloc", feature = "arithmetic", feature = "sec1"))]
28use {
29 crate::{
30 AffinePoint,
31 sec1::{FromSec1Point, ToSec1Point},
32 },
33 alloc::vec::Vec,
34 sec1::der::Encode,
35};
36#[cfg(feature = "sec1")]
37use {
38 crate::{
39 DecodeError, DecodeResult, FieldBytesSize,
40 sec1::{ModulusSize, Sec1Point, ValidatePublicKey},
41 },
42 sec1::der::{self, Decode, oid::AssociatedOid},
43};
44
45#[cfg(all(doc, feature = "pkcs8"))]
46use {crate::pkcs8::DecodePrivateKey, core::str::FromStr};
47
48#[derive(Clone)]
74pub struct SecretKey<C: Curve> {
75 inner: ScalarValue<C>,
77}
78
79impl<C> SecretKey<C>
80where
81 C: Curve,
82{
83 pub const MIN_SIZE: usize = 24;
87
88 pub fn from_scalar(scalar: impl Into<ScalarValue<C>>) -> CtOption<Self> {
94 let inner = scalar.into();
95 CtOption::new(Self { inner }, !inner.is_zero())
96 }
97
98 pub fn as_scalar_value(&self) -> &ScalarValue<C> {
106 &self.inner
107 }
108
109 #[cfg(feature = "arithmetic")]
117 pub fn to_nonzero_scalar(&self) -> NonZeroScalar<C>
118 where
119 C: CurveArithmetic,
120 {
121 self.into()
122 }
123
124 #[cfg(feature = "arithmetic")]
126 pub fn public_key(&self) -> PublicKey<C>
127 where
128 C: CurveArithmetic,
129 {
130 PublicKey::from_secret_scalar(&self.to_nonzero_scalar())
131 }
132
133 pub fn from_bytes(bytes: &FieldBytes<C>) -> Result<Self> {
138 let inner = ScalarValue::<C>::from_bytes(bytes)
139 .into_option()
140 .ok_or(Error)?;
141
142 if inner.is_zero().into() {
143 return Err(Error);
144 }
145
146 Ok(Self { inner })
147 }
148
149 pub fn from_slice(slice: &[u8]) -> Result<Self> {
165 if let Ok(field_bytes) = <&FieldBytes<C>>::try_from(slice) {
166 Self::from_bytes(field_bytes)
167 } else if (Self::MIN_SIZE..C::FieldBytesSize::USIZE).contains(&slice.len()) {
168 let mut bytes = Zeroizing::new(FieldBytes::<C>::default());
169
170 match C::FIELD_ENDIANNESS {
171 ByteOrder::BigEndian => {
172 let offset = C::FieldBytesSize::USIZE - slice.len();
173 bytes[offset..].copy_from_slice(slice);
174 }
175 ByteOrder::LittleEndian => bytes[..slice.len()].copy_from_slice(slice),
176 }
177
178 Self::from_bytes(&bytes)
179 } else {
180 Err(Error)
181 }
182 }
183
184 pub fn to_bytes(&self) -> FieldBytes<C> {
186 self.inner.to_bytes()
187 }
188
189 #[cfg(feature = "ecdh")]
193 pub fn diffie_hellman(&self, public_key: &PublicKey<C>) -> ecdh::SharedSecret<C>
194 where
195 C: CurveArithmetic,
196 {
197 ecdh::diffie_hellman(self.to_nonzero_scalar(), public_key.as_affine())
198 }
199
200 #[cfg(any(feature = "pkcs8", feature = "sec1"))]
214 #[allow(clippy::missing_panics_doc, reason = "should not panic")]
215 pub fn from_der(der_bytes: &[u8]) -> DecodeResult<Self>
216 where
217 C: AssociatedOid + Curve + ValidatePublicKey,
218 FieldBytesSize<C>: ModulusSize,
219 {
220 #[allow(unused_assignments)]
221 let mut err: Option<DecodeError> = None;
222
223 #[cfg(feature = "pkcs8")]
224 match ::pkcs8::DecodePrivateKey::from_pkcs8_der(der_bytes) {
225 Ok(sk) => return Ok(sk),
226 Err(e) => err = Some(e.into()),
227 }
228
229 #[cfg(feature = "sec1")]
230 match Self::from_sec1_der(der_bytes) {
231 Ok(sk) => return Ok(sk),
232 Err(e) => {
233 let _ = err.get_or_insert(e);
235 }
236 }
237
238 Err(err.expect("should be set"))
239 }
240
241 #[cfg(feature = "pem")]
252 pub fn from_pem(pem: &str) -> DecodeResult<Self>
253 where
254 C: AssociatedOid + Curve + ValidatePublicKey,
255 FieldBytesSize<C>: ModulusSize,
256 {
257 let label = pem_rfc7468::decode_label(pem.as_bytes()).map_err(DecodeError::Pem)?;
258
259 if ::pkcs8::PrivateKeyInfoRef::validate_pem_label(label).is_ok() {
260 return ::pkcs8::DecodePrivateKey::from_pkcs8_pem(pem).map_err(DecodeError::Pkcs8);
261 } else if ::sec1::EcPrivateKey::validate_pem_label(label).is_ok() {
262 return ::sec1::DecodeEcPrivateKey::from_sec1_pem(pem).map_err(DecodeError::Sec1);
263 }
264
265 Err(pem_rfc7468::Error::Label.into())
266 }
267
268 #[cfg(feature = "sec1")]
274 pub fn from_sec1_der(der_bytes: &[u8]) -> DecodeResult<Self>
275 where
276 C: AssociatedOid + Curve + ValidatePublicKey,
277 FieldBytesSize<C>: ModulusSize,
278 {
279 let sec1_key = sec1::EcPrivateKey::try_from(der_bytes)?;
280 Self::try_from(sec1_key).map_err(|e| DecodeError::Sec1(e.into()))
281 }
282
283 #[cfg(all(feature = "alloc", feature = "arithmetic", feature = "sec1"))]
288 pub fn to_sec1_der(&self) -> der::Result<Zeroizing<Vec<u8>>>
289 where
290 C: AssociatedOid + CurveArithmetic,
291 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
292 FieldBytesSize<C>: ModulusSize,
293 {
294 let private_key_bytes = Zeroizing::new(self.to_bytes());
295 let public_key_bytes = self.public_key().to_sec1_point(false);
296 let parameters = sec1::EcParameters::NamedCurve(C::OID);
297
298 let ec_private_key = Zeroizing::new(
299 sec1::EcPrivateKey {
300 private_key: &private_key_bytes,
301 parameters: Some(parameters),
302 public_key: Some(public_key_bytes.as_bytes()),
303 }
304 .to_der()?,
305 );
306
307 Ok(ec_private_key)
308 }
309
310 #[cfg(feature = "pem")]
322 pub fn from_sec1_pem(s: &str) -> DecodeResult<Self>
323 where
324 C: AssociatedOid + Curve + ValidatePublicKey,
325 FieldBytesSize<C>: ModulusSize,
326 {
327 let (label, der_bytes) = pem::decode_vec(s.as_bytes()).map_err(DecodeError::Pem)?;
328
329 if label != sec1::EcPrivateKey::PEM_LABEL {
330 return Err(pem_rfc7468::Error::Label.into());
331 }
332
333 Self::from_sec1_der(&der_bytes)
334 }
335
336 #[cfg(feature = "pem")]
344 pub fn to_sec1_pem(&self, line_ending: pem::LineEnding) -> Result<Zeroizing<String>>
345 where
346 C: AssociatedOid + CurveArithmetic,
347 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
348 FieldBytesSize<C>: ModulusSize,
349 {
350 self.to_sec1_der()
351 .ok()
352 .and_then(|der| {
353 pem::encode_string(sec1::EcPrivateKey::PEM_LABEL, line_ending, &der).ok()
354 })
355 .map(Zeroizing::new)
356 .ok_or(Error)
357 }
358
359 #[deprecated(since = "0.14.0", note = "use the `Generate` trait instead")]
361 pub fn random<R: CryptoRng + ?Sized>(rng: &mut R) -> Self {
362 Self::generate_from_rng(rng)
363 }
364}
365
366impl<C> ConstantTimeEq for SecretKey<C>
367where
368 C: Curve,
369{
370 fn ct_eq(&self, other: &Self) -> Choice {
371 self.inner.ct_eq(&other.inner)
372 }
373}
374
375impl<C> Debug for SecretKey<C>
376where
377 C: Curve,
378{
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 f.debug_struct(core::any::type_name::<Self>())
381 .finish_non_exhaustive()
382 }
383}
384
385impl<C> Drop for SecretKey<C>
386where
387 C: Curve,
388{
389 fn drop(&mut self) {
390 self.inner.zeroize();
391 }
392}
393impl<C> ZeroizeOnDrop for SecretKey<C> where C: Curve {}
394
395impl<C: Curve> Eq for SecretKey<C> {}
396
397impl<C> PartialEq for SecretKey<C>
398where
399 C: Curve,
400{
401 fn eq(&self, other: &Self) -> bool {
402 self.ct_eq(other).into()
403 }
404}
405
406impl<C> Generate for SecretKey<C>
407where
408 C: Curve,
409{
410 fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(
411 rng: &mut R,
412 ) -> core::result::Result<Self, R::Error> {
413 Ok(Self {
414 inner: ScalarValue::<C>::try_generate_from_rng(rng)?,
415 })
416 }
417}
418
419impl<C> KeySizeUser for SecretKey<C>
420where
421 C: Curve,
422{
423 type KeySize = C::FieldBytesSize;
424}
425
426impl<C> TryKeyInit for SecretKey<C>
427where
428 C: Curve,
429{
430 fn new(key_bytes: &FieldBytes<C>) -> core::result::Result<Self, InvalidKey> {
431 Self::from_bytes(key_bytes).map_err(|_| InvalidKey)
432 }
433}
434
435#[cfg(feature = "sec1")]
436impl<C> sec1::DecodeEcPrivateKey for SecretKey<C>
437where
438 C: AssociatedOid + Curve + ValidatePublicKey,
439 FieldBytesSize<C>: ModulusSize,
440{
441 fn from_sec1_der(bytes: &[u8]) -> sec1::Result<Self> {
442 Ok(sec1::EcPrivateKey::from_der(bytes)?.try_into()?)
443 }
444}
445
446#[cfg(all(feature = "alloc", feature = "arithmetic", feature = "sec1"))]
447impl<C> sec1::EncodeEcPrivateKey for SecretKey<C>
448where
449 C: AssociatedOid + CurveArithmetic,
450 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
451 FieldBytesSize<C>: ModulusSize,
452{
453 fn to_sec1_der(&self) -> sec1::Result<der::SecretDocument> {
454 let private_key_bytes = Zeroizing::new(self.to_bytes());
455 let public_key_bytes = self.public_key().to_sec1_point(false);
456
457 Ok(der::SecretDocument::encode_msg(&sec1::EcPrivateKey {
458 private_key: &private_key_bytes,
459 parameters: Some(C::OID.into()),
460 public_key: Some(public_key_bytes.as_bytes()),
461 })?)
462 }
463}
464
465#[cfg(feature = "sec1")]
466impl<C> TryFrom<sec1::EcPrivateKey<'_>> for SecretKey<C>
467where
468 C: AssociatedOid + Curve + ValidatePublicKey,
469 FieldBytesSize<C>: ModulusSize,
470{
471 type Error = der::Error;
472
473 fn try_from(sec1_private_key: sec1::EcPrivateKey<'_>) -> der::Result<Self> {
474 if let Some(sec1::EcParameters::NamedCurve(curve_oid)) = sec1_private_key.parameters {
475 if C::OID != curve_oid {
476 return Err(der::Tag::ObjectIdentifier.value_error().into());
477 }
478 }
479
480 let secret_key = Self::from_slice(sec1_private_key.private_key)
481 .map_err(|_| der::Tag::OctetString.value_error())?;
482
483 if let Some(pk_bytes) = sec1_private_key.public_key {
484 let pk = Sec1Point::<C>::from_bytes(pk_bytes)
485 .map_err(|_| der::Tag::BitString.value_error())?;
486
487 if C::validate_public_key(&secret_key, &pk).is_err() {
488 return Err(der::Tag::BitString.value_error().into());
489 }
490 }
491
492 Ok(secret_key)
493 }
494}
495
496#[cfg(feature = "arithmetic")]
497impl<C> From<NonZeroScalar<C>> for SecretKey<C>
498where
499 C: CurveArithmetic,
500{
501 fn from(scalar: NonZeroScalar<C>) -> SecretKey<C> {
502 SecretKey::from(&scalar)
503 }
504}
505
506#[cfg(feature = "arithmetic")]
507impl<C> From<&NonZeroScalar<C>> for SecretKey<C>
508where
509 C: CurveArithmetic,
510{
511 fn from(scalar: &NonZeroScalar<C>) -> SecretKey<C> {
512 SecretKey {
513 inner: scalar.into(),
514 }
515 }
516}