diff --git a/Cargo.lock b/Cargo.lock index 2aa117e87..dd7930a7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -355,6 +355,7 @@ dependencies = [ "num-bigint", "num-traits", "proptest", + "rand_core", "sha2", ] @@ -461,6 +462,7 @@ dependencies = [ "hex", "hex-literal", "proptest", + "rand_core", "sha2", ] diff --git a/k256/Cargo.toml b/k256/Cargo.toml index eb108c7a5..fff247207 100644 --- a/k256/Cargo.toml +++ b/k256/Cargo.toml @@ -18,13 +18,14 @@ elliptic-curve = { version = "= 0.5.0-pre", default-features = false, features = sha2 = { version = "0.9", optional = true, default-features = false } [dev-dependencies] +criterion = "0.3" ecdsa-core = { version = "= 0.7.0-pre", package = "ecdsa", default-features = false, features = ["dev"] } hex = "0.4" # TODO: switch to hex-literal hex-literal = "0.2" -proptest = "0.10" num-bigint = "0.3" num-traits = "0.2" -criterion = "0.3" +proptest = "0.10" +rand_core = { version = "0.5", features = ["getrandom"] } [features] default = ["arithmetic", "oid", "std"] diff --git a/k256/src/arithmetic.rs b/k256/src/arithmetic.rs index 84184783e..6fe6bf55a 100644 --- a/k256/src/arithmetic.rs +++ b/k256/src/arithmetic.rs @@ -19,6 +19,9 @@ use crate::{CompressedPoint, PublicKey, Secp256k1, UncompressedPoint}; use field::FieldElement; use scalar::{NonZeroScalar, Scalar}; +#[cfg(feature = "zeroize")] +use elliptic_curve::zeroize::Zeroize; + const CURVE_EQUATION_B_SINGLE: u32 = 7u32; #[rustfmt::skip] @@ -205,6 +208,14 @@ impl FromPublicKey for AffinePoint { } } +#[cfg(feature = "zeroize")] +impl Zeroize for AffinePoint { + fn zeroize(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + } +} + /// A point on the secp256k1 curve in projective coordinates. #[derive(Clone, Copy, Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] diff --git a/k256/src/arithmetic/field.rs b/k256/src/arithmetic/field.rs index fdcdda8f5..87c6b5bd1 100644 --- a/k256/src/arithmetic/field.rs +++ b/k256/src/arithmetic/field.rs @@ -33,6 +33,9 @@ use crate::ElementBytes; use core::ops::{Add, AddAssign, Mul, MulAssign}; use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +#[cfg(feature = "zeroize")] +use elliptic_curve::zeroize::Zeroize; + #[cfg(test)] use num_bigint::{BigUint, ToBigUint}; @@ -292,6 +295,13 @@ impl MulAssign for FieldElement { } } +#[cfg(feature = "zeroize")] +impl Zeroize for FieldElement { + fn zeroize(&mut self) { + self.0.zeroize(); + } +} + #[cfg(test)] mod tests { use num_bigint::{BigUint, ToBigUint}; diff --git a/k256/src/arithmetic/field/field_10x26.rs b/k256/src/arithmetic/field/field_10x26.rs index cae5bb79b..d2cad480e 100644 --- a/k256/src/arithmetic/field/field_10x26.rs +++ b/k256/src/arithmetic/field/field_10x26.rs @@ -4,6 +4,9 @@ use crate::ElementBytes; use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +#[cfg(feature = "zeroize")] +use elliptic_curve::zeroize::Zeroize; + /// Scalars modulo SECP256k1 modulus (2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1). /// Uses 10 32-bit limbs (little-endian), where in the normalized form /// first 9 contain 26 bits of the value each, and the last one contains 22 bits. @@ -696,3 +699,10 @@ impl ConstantTimeEq for FieldElement10x26 { & self.0[9].ct_eq(&other.0[9]) } } + +#[cfg(feature = "zeroize")] +impl Zeroize for FieldElement10x26 { + fn zeroize(&mut self) { + self.0.zeroize(); + } +} diff --git a/k256/src/arithmetic/field/field_5x52.rs b/k256/src/arithmetic/field/field_5x52.rs index 74d450afa..c823a83d3 100644 --- a/k256/src/arithmetic/field/field_5x52.rs +++ b/k256/src/arithmetic/field/field_5x52.rs @@ -4,6 +4,9 @@ use crate::ElementBytes; use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +#[cfg(feature = "zeroize")] +use elliptic_curve::zeroize::Zeroize; + /// Scalars modulo SECP256k1 modulus (2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1). /// Uses 5 64-bit limbs (little-endian), where in the normalized form /// first 4 contain 52 bits of the value each, and the last one contains 48 bits. @@ -483,3 +486,10 @@ impl ConstantTimeEq for FieldElement5x52 { & self.0[4].ct_eq(&other.0[4]) } } + +#[cfg(feature = "zeroize")] +impl Zeroize for FieldElement5x52 { + fn zeroize(&mut self) { + self.0.zeroize(); + } +} diff --git a/k256/src/arithmetic/field/field_impl.rs b/k256/src/arithmetic/field/field_impl.rs index a9fa4106f..10f795f70 100644 --- a/k256/src/arithmetic/field/field_impl.rs +++ b/k256/src/arithmetic/field/field_impl.rs @@ -6,6 +6,9 @@ use crate::ElementBytes; use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use cfg_if::cfg_if; +#[cfg(feature = "zeroize")] +use elliptic_curve::zeroize::Zeroize; + cfg_if! { if #[cfg(any(target_pointer_width = "32", feature = "force-32-bit"))] { use super::field_10x26::FieldElement10x26 as FieldElementUnsafeImpl; @@ -158,3 +161,12 @@ impl ConstantTimeEq for FieldElementImpl { & Choice::from((self.normalized == other.normalized) as u8) } } + +#[cfg(feature = "zeroize")] +impl Zeroize for FieldElementImpl { + fn zeroize(&mut self) { + self.value.zeroize(); + self.magnitude.zeroize(); + self.normalized.zeroize(); + } +} diff --git a/k256/src/arithmetic/field/field_montgomery.rs b/k256/src/arithmetic/field/field_montgomery.rs index d1743b6d2..67c2c3666 100644 --- a/k256/src/arithmetic/field/field_montgomery.rs +++ b/k256/src/arithmetic/field/field_montgomery.rs @@ -3,6 +3,9 @@ use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::{arithmetic::util::{adc64, mac64, mac64_typemax, sbb64}, ElementBytes}; +#[cfg(feature = "zeroize")] +use elliptic_curve::zeroize::Zeroize; + const fn bytes_to_u64(b: &[u8; 8]) -> u64 { ((b[0] as u64) << 56) | ((b[1] as u64) << 48) @@ -411,3 +414,10 @@ impl Default for FieldElementMontgomery { Self::zero() } } + +#[cfg(feature = "zeroize")] +impl Zeroize for FieldElementMontgomery { + fn zeroize(&mut self) { + self.0.zeroize(); + } +} diff --git a/k256/src/ecdsa.rs b/k256/src/ecdsa.rs index 42bd0a959..a2559f8e0 100644 --- a/k256/src/ecdsa.rs +++ b/k256/src/ecdsa.rs @@ -22,9 +22,10 @@ //! # { //! use k256::{ //! ecdsa::{Signer, Signature, signature::RandomizedSigner}, -//! elliptic_curve::{Generate, rand_core::OsRng}, +//! elliptic_curve::{Generate}, //! SecretKey, //! }; +//! use rand_core::OsRng; // requires 'getrandom' feature //! //! // Signing //! let secret_key = SecretKey::generate(&mut OsRng); diff --git a/p256/Cargo.toml b/p256/Cargo.toml index ba9464215..70a34cf53 100644 --- a/p256/Cargo.toml +++ b/p256/Cargo.toml @@ -21,6 +21,7 @@ ecdsa-core = { version = "= 0.7.0-pre", package = "ecdsa", default-features = fa hex = "0.4" # TODO: switch to hex-literal hex-literal = "0.2" proptest = "0.10" +rand_core = { version = "0.5", features = ["getrandom"] } [features] default = ["arithmetic", "std"] diff --git a/p256/src/arithmetic.rs b/p256/src/arithmetic.rs index 261963715..85bbb0cd4 100644 --- a/p256/src/arithmetic.rs +++ b/p256/src/arithmetic.rs @@ -18,6 +18,9 @@ use crate::{CompressedPoint, NistP256, PublicKey, UncompressedPoint}; use field::{FieldElement, MODULUS}; use scalar::{NonZeroScalar, Scalar}; +#[cfg(feature = "zeroize")] +use elliptic_curve::zeroize::Zeroize; + /// a = -3 const CURVE_EQUATION_A: FieldElement = FieldElement::zero() .subtract(&FieldElement::one()) @@ -195,6 +198,14 @@ impl Neg for AffinePoint { } } +#[cfg(feature = "zeroize")] +impl Zeroize for AffinePoint { + fn zeroize(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + } +} + /// A point on the secp256r1 curve in projective coordinates. #[derive(Clone, Copy, Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] diff --git a/p256/src/arithmetic/field.rs b/p256/src/arithmetic/field.rs index 8d9443c09..8793f9ede 100644 --- a/p256/src/arithmetic/field.rs +++ b/p256/src/arithmetic/field.rs @@ -8,6 +8,9 @@ use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, Ct #[cfg(feature = "rand")] use elliptic_curve::rand_core::{CryptoRng, RngCore}; +#[cfg(feature = "zeroize")] +use elliptic_curve::zeroize::Zeroize; + use super::util::{adc, mac, sbb}; /// The number of 64-bit limbs used to represent a [`FieldElement`]. @@ -499,6 +502,13 @@ impl<'a> Neg for &'a FieldElement { } } +#[cfg(feature = "zeroize")] +impl Zeroize for FieldElement { + fn zeroize(&mut self) { + self.0.zeroize(); + } +} + #[cfg(test)] mod tests { use super::FieldElement; diff --git a/p256/src/ecdsa.rs b/p256/src/ecdsa.rs index 93fc97d05..5ee6ebf30 100644 --- a/p256/src/ecdsa.rs +++ b/p256/src/ecdsa.rs @@ -1,4 +1,53 @@ //! Elliptic Curve Digital Signature Algorithm (ECDSA) +//! +//! This module contains support for computing and verifying ECDSA signatures. +//! To use it, you will need to enable one of the two following Cargo features: +//! +//! - `ecdsa-core`: provides only the [`Signature`] type (which represents an +//! ECDSA/P-256 signature). Does not require the `arithmetic` feature. +//! This is useful for 3rd-party crates which wish to use the `Signature` +//! type for interoperability purposes (particularly in conjunction with the +//! [`signature::Signer`] trait. Example use cases for this include other +//! software implementations of ECDSA/P-256 and wrappers for cloud KMS +//! services or hardware devices (HSM or crypto hardware wallet). +//! - `ecdsa`: provides the [`Signature`], [`Signer`], and [`Verifier`] types +//! which natively implement ECDSA/P-256 signing and verification. +//! +//! ## Signing/Verification Example +//! +//! This example requires the `ecdsa` Cargo feature is enabled: +//! +//! ``` +//! # #[cfg(feature = "ecdsa")] +//! # { +//! use p256::{ +//! ecdsa::{Signer, Signature, signature::RandomizedSigner}, +//! elliptic_curve::{Generate}, +//! SecretKey, +//! }; +//! use rand_core::OsRng; // requires 'getrandom' feature +//! +//! // Signing +//! let secret_key = SecretKey::generate(&mut OsRng); +//! let signer = Signer::new(&secret_key).expect("secret key invalid"); +//! let message = b"ECDSA proves knowledge of a secret number in the context of a single message"; +//! +//! // Note: the signature type must be annotated or otherwise inferrable as +//! // `Signer` has many impls of the `RandomizedSigner` trait (for both +//! // regular and recoverable signature types). +//! let signature: Signature = signer.sign_with_rng(&mut OsRng, message); +//! +//! // Verification +//! use p256::{PublicKey, ecdsa::{Verifier, signature::Verifier as _}}; +//! +//! let public_key = PublicKey::from_secret_key(&secret_key, true).expect("secret key invalid"); +//! let verifier = Verifier::new(&public_key).expect("public key invalid"); +//! +//! assert!(verifier.verify(message, &signature).is_ok()); +//! # } +//! ``` + +pub use ecdsa_core::signature::{self, Error}; use super::NistP256; use core::borrow::Borrow; @@ -6,10 +55,7 @@ use core::borrow::Borrow; #[cfg(feature = "ecdsa")] use { crate::{AffinePoint, ElementBytes, ProjectivePoint, Scalar}, - ecdsa_core::{ - hazmat::{SignPrimitive, VerifyPrimitive}, - Error, - }, + ecdsa_core::hazmat::{SignPrimitive, VerifyPrimitive}, elliptic_curve::{ops::Invert, subtle::CtOption, FromBytes}, };