From 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:41:41 +0200 Subject: Merging upstream version 1.70.0+dfsg2. Signed-off-by: Daniel Baumann --- vendor/elliptic-curve/src/secret_key.rs | 395 ++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 vendor/elliptic-curve/src/secret_key.rs (limited to 'vendor/elliptic-curve/src/secret_key.rs') diff --git a/vendor/elliptic-curve/src/secret_key.rs b/vendor/elliptic-curve/src/secret_key.rs new file mode 100644 index 000000000..0fc3dafb1 --- /dev/null +++ b/vendor/elliptic-curve/src/secret_key.rs @@ -0,0 +1,395 @@ +//! Secret keys for elliptic curves (i.e. private scalars). +//! +//! The [`SecretKey`] type is a wrapper around a secret scalar value which is +//! designed to prevent unintentional exposure (e.g. via `Debug` or other +//! logging). It also handles zeroing the secret value out of memory securely +//! on drop. + +#[cfg(all(feature = "pkcs8", feature = "sec1"))] +mod pkcs8; + +use crate::{Curve, Error, FieldBytes, Result, ScalarCore}; +use core::fmt::{self, Debug}; +use crypto_bigint::Encoding; +use generic_array::GenericArray; +use subtle::{Choice, ConstantTimeEq}; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +#[cfg(all(feature = "alloc", feature = "arithmetic"))] +use { + crate::{ + sec1::{FromEncodedPoint, ToEncodedPoint}, + AffinePoint, + }, + alloc::vec::Vec, + der::Encode, + zeroize::Zeroizing, +}; + +#[cfg(feature = "arithmetic")] +use crate::{ + rand_core::{CryptoRng, RngCore}, + NonZeroScalar, ProjectiveArithmetic, PublicKey, +}; + +#[cfg(feature = "jwk")] +use crate::jwk::{JwkEcKey, JwkParameters}; + +#[cfg(all(feature = "arithmetic", any(feature = "jwk", feature = "pem")))] +use alloc::string::String; + +#[cfg(all(feature = "arithmetic", feature = "jwk"))] +use alloc::string::ToString; + +#[cfg(feature = "pem")] +use pem_rfc7468 as pem; + +#[cfg(feature = "sec1")] +use crate::{ + sec1::{EncodedPoint, ModulusSize, ValidatePublicKey}, + FieldSize, +}; + +#[cfg(all(docsrs, feature = "pkcs8"))] +use {crate::pkcs8::DecodePrivateKey, core::str::FromStr}; + +/// Type label for PEM-encoded SEC1 private keys. +#[cfg(feature = "pem")] +pub(crate) const SEC1_PEM_TYPE_LABEL: &str = "EC PRIVATE KEY"; + +/// Elliptic curve secret keys. +/// +/// This type wraps a secret scalar value, helping to prevent accidental +/// exposure and securely erasing the value from memory when dropped. +/// +/// # Parsing PKCS#8 Keys +/// +/// PKCS#8 is a commonly used format for encoding secret keys (especially ones +/// generated by OpenSSL). +/// +/// Keys in PKCS#8 format are either binary (ASN.1 BER/DER), or PEM encoded +/// (ASCII) and begin with the following: +/// +/// ```text +/// -----BEGIN PRIVATE KEY----- +/// ``` +/// +/// To decode an elliptic curve private key from PKCS#8, enable the `pkcs8` +/// feature of this crate (or the `pkcs8` feature of a specific RustCrypto +/// elliptic curve crate) and use the [`DecodePrivateKey`] trait to parse it. +/// +/// When the `pem` feature of this crate (or a specific RustCrypto elliptic +/// curve crate) is enabled, a [`FromStr`] impl is also available. +#[derive(Clone)] +pub struct SecretKey { + /// Scalar value + inner: ScalarCore, +} + +impl SecretKey +where + C: Curve, +{ + /// Generate a random [`SecretKey`]. + #[cfg(feature = "arithmetic")] + #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] + pub fn random(rng: impl CryptoRng + RngCore) -> Self + where + C: ProjectiveArithmetic, + { + Self { + inner: NonZeroScalar::::random(rng).into(), + } + } + + /// Create a new secret key from a scalar value. + pub fn new(scalar: ScalarCore) -> Self { + Self { inner: scalar } + } + + /// Borrow the inner secret [`ScalarCore`] value. + /// + /// # ⚠️ Warning + /// + /// This value is key material. + /// + /// Please treat it with the care it deserves! + pub fn as_scalar_core(&self) -> &ScalarCore { + &self.inner + } + + /// Get the secret [`NonZeroScalar`] value for this key. + /// + /// # ⚠️ Warning + /// + /// This value is key material. + /// + /// Please treat it with the care it deserves! + #[cfg(feature = "arithmetic")] + #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] + pub fn to_nonzero_scalar(&self) -> NonZeroScalar + where + C: Curve + ProjectiveArithmetic, + { + self.into() + } + + /// Get the [`PublicKey`] which corresponds to this secret key + #[cfg(feature = "arithmetic")] + #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] + pub fn public_key(&self) -> PublicKey + where + C: Curve + ProjectiveArithmetic, + { + PublicKey::from_secret_scalar(&self.to_nonzero_scalar()) + } + + /// Deserialize raw secret scalar as a big endian integer. + pub fn from_be_bytes(bytes: &[u8]) -> Result { + if bytes.len() != C::UInt::BYTE_SIZE { + return Err(Error); + } + + let inner: ScalarCore = Option::from(ScalarCore::from_be_bytes( + GenericArray::clone_from_slice(bytes), + )) + .ok_or(Error)?; + + if inner.is_zero().into() { + return Err(Error); + } + + Ok(Self { inner }) + } + + /// Serialize raw secret scalar as a big endian integer. + pub fn to_be_bytes(&self) -> FieldBytes { + self.inner.to_be_bytes() + } + + /// Deserialize secret key encoded in the SEC1 ASN.1 DER `ECPrivateKey` format. + #[cfg(all(feature = "sec1"))] + #[cfg_attr(docsrs, doc(cfg(feature = "sec1")))] + pub fn from_sec1_der(der_bytes: &[u8]) -> Result + where + C: Curve + ValidatePublicKey, + FieldSize: ModulusSize, + { + sec1::EcPrivateKey::try_from(der_bytes)? + .try_into() + .map_err(|_| Error) + } + + /// Serialize secret key in the SEC1 ASN.1 DER `ECPrivateKey` format. + #[cfg(all(feature = "alloc", feature = "arithmetic", feature = "sec1"))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "alloc", feature = "arithmetic", feature = "sec1"))) + )] + pub fn to_sec1_der(&self) -> der::Result>> + where + C: Curve + ProjectiveArithmetic, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: ModulusSize, + { + // TODO(tarcieri): wrap `secret_key_bytes` in `Zeroizing` + let mut private_key_bytes = self.to_be_bytes(); + let public_key_bytes = self.public_key().to_encoded_point(false); + + let ec_private_key = Zeroizing::new( + sec1::EcPrivateKey { + private_key: &private_key_bytes, + parameters: None, + public_key: Some(public_key_bytes.as_bytes()), + } + .to_vec()?, + ); + + // TODO(tarcieri): wrap `private_key_bytes` in `Zeroizing` + private_key_bytes.zeroize(); + + Ok(ec_private_key) + } + + /// Parse [`SecretKey`] from PEM-encoded SEC1 `ECPrivateKey` format. + /// + /// PEM-encoded SEC1 keys can be identified by the leading delimiter: + /// + /// ```text + /// -----BEGIN EC PRIVATE KEY----- + /// ``` + #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + pub fn from_sec1_pem(s: &str) -> Result + where + C: Curve + ValidatePublicKey, + FieldSize: ModulusSize, + { + let (label, der_bytes) = pem::decode_vec(s.as_bytes()).map_err(|_| Error)?; + + if label != SEC1_PEM_TYPE_LABEL { + return Err(Error); + } + + Self::from_sec1_der(&*der_bytes).map_err(|_| Error) + } + + /// Serialize private key as self-zeroizing PEM-encoded SEC1 `ECPrivateKey` + /// with the given [`pem::LineEnding`]. + /// + /// Pass `Default::default()` to use the OS's native line endings. + #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + pub fn to_pem(&self, line_ending: pem::LineEnding) -> Result> + where + C: Curve + ProjectiveArithmetic, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: ModulusSize, + { + self.to_sec1_der() + .ok() + .and_then(|der| pem::encode_string(SEC1_PEM_TYPE_LABEL, line_ending, &der).ok()) + .map(Zeroizing::new) + .ok_or(Error) + } + + /// Parse a [`JwkEcKey`] JSON Web Key (JWK) into a [`SecretKey`]. + #[cfg(feature = "jwk")] + #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] + pub fn from_jwk(jwk: &JwkEcKey) -> Result + where + C: JwkParameters + ValidatePublicKey, + FieldSize: ModulusSize, + { + Self::try_from(jwk) + } + + /// Parse a string containing a JSON Web Key (JWK) into a [`SecretKey`]. + #[cfg(feature = "jwk")] + #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] + pub fn from_jwk_str(jwk: &str) -> Result + where + C: JwkParameters + ValidatePublicKey, + FieldSize: ModulusSize, + { + jwk.parse::().and_then(|jwk| Self::from_jwk(&jwk)) + } + + /// Serialize this secret key as [`JwkEcKey`] JSON Web Key (JWK). + #[cfg(all(feature = "arithmetic", feature = "jwk"))] + #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] + #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] + pub fn to_jwk(&self) -> JwkEcKey + where + C: Curve + JwkParameters + ProjectiveArithmetic, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: ModulusSize, + { + self.into() + } + + /// Serialize this secret key as JSON Web Key (JWK) string. + #[cfg(all(feature = "arithmetic", feature = "jwk"))] + #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] + #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] + pub fn to_jwk_string(&self) -> Zeroizing + where + C: Curve + JwkParameters + ProjectiveArithmetic, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: ModulusSize, + { + Zeroizing::new(self.to_jwk().to_string()) + } +} + +impl ConstantTimeEq for SecretKey +where + C: Curve, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.inner.ct_eq(&other.inner) + } +} + +impl Debug for SecretKey +where + C: Curve, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // TODO(tarcieri): use `debug_struct` and `finish_non_exhaustive` when stable + write!(f, "SecretKey<{:?}>{{ ... }}", C::default()) + } +} + +impl ZeroizeOnDrop for SecretKey where C: Curve {} + +impl Drop for SecretKey +where + C: Curve, +{ + fn drop(&mut self) { + self.inner.zeroize(); + } +} + +impl Eq for SecretKey {} + +impl PartialEq for SecretKey +where + C: Curve, +{ + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +#[cfg(all(feature = "sec1"))] +#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))] +impl TryFrom> for SecretKey +where + C: Curve + ValidatePublicKey, + FieldSize: ModulusSize, +{ + type Error = der::Error; + + fn try_from(sec1_private_key: sec1::EcPrivateKey<'_>) -> der::Result { + let secret_key = Self::from_be_bytes(sec1_private_key.private_key) + .map_err(|_| der::Tag::Sequence.value_error())?; + + // TODO(tarcieri): validate `sec1_private_key.params`? + if let Some(pk_bytes) = sec1_private_key.public_key { + let pk = EncodedPoint::::from_bytes(pk_bytes) + .map_err(|_| der::Tag::BitString.value_error())?; + + if C::validate_public_key(&secret_key, &pk).is_err() { + return Err(der::Tag::BitString.value_error()); + } + } + + Ok(secret_key) + } +} + +#[cfg(feature = "arithmetic")] +#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] +impl From> for SecretKey +where + C: Curve + ProjectiveArithmetic, +{ + fn from(scalar: NonZeroScalar) -> SecretKey { + SecretKey::from(&scalar) + } +} + +#[cfg(feature = "arithmetic")] +#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] +impl From<&NonZeroScalar> for SecretKey +where + C: Curve + ProjectiveArithmetic, +{ + fn from(scalar: &NonZeroScalar) -> SecretKey { + SecretKey { + inner: scalar.into(), + } + } +} -- cgit v1.2.3