From df15414e9c1efe3550c676653331849e1376e5df Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 1 Sep 2020 07:34:33 -0700 Subject: [PATCH] ecdsa: use FromDigest trait; RFC6979 fixups - Uses the `FromDigest` trait to automatically convert message digests into a `C::Scalar` value - Updates `SignPrimitive` and `VerifyPrimitive` to accept a scalar - Updates the `dev` module accordingly with the above - Exposes the `ecdsa::signer::rfc6979` module and makes the `generate_k` method `pub` --- ecdsa/src/dev.rs | 45 ++++++++++++++++++------- ecdsa/src/hazmat.rs | 16 ++++----- ecdsa/src/lib.rs | 2 +- ecdsa/src/signer.rs | 67 ++++++++++++++++++------------------- ecdsa/src/signer/rfc6979.rs | 33 +++++------------- ecdsa/src/verifier.rs | 7 ++-- 6 files changed, 88 insertions(+), 82 deletions(-) diff --git a/ecdsa/src/dev.rs b/ecdsa/src/dev.rs index 8c283876..20221de0 100644 --- a/ecdsa/src/dev.rs +++ b/ecdsa/src/dev.rs @@ -31,19 +31,28 @@ pub struct TestVector { #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] macro_rules! new_signing_test { - ($vectors:expr) => { + ($curve:path, $vectors:expr) => { use core::convert::TryInto; - use $crate::generic_array::GenericArray; + use $crate::{ + elliptic_curve::{Arithmetic, FromBytes}, + generic_array::GenericArray, + hazmat::SignPrimitive, + }; #[test] fn ecdsa_signing() { for vector in $vectors { - let d = Scalar::from_bytes(vector.d.try_into().unwrap()).unwrap(); - let k = Scalar::from_bytes(vector.k.try_into().unwrap()).unwrap(); - let sig = d - .try_sign_prehashed(&k, GenericArray::from_slice(vector.m)) + let d = <$curve as Arithmetic>::Scalar::from_bytes(vector.d.try_into().unwrap()) .unwrap(); + let k = <$curve as Arithmetic>::Scalar::from_bytes(vector.k.try_into().unwrap()) + .unwrap(); + + let z = <$curve as Arithmetic>::Scalar::from_bytes(vector.m.try_into().unwrap()) + .unwrap(); + + let sig = d.try_sign_prehashed(&k, &z).unwrap(); + assert_eq!(vector.r, sig.r().as_slice()); assert_eq!(vector.s, sig.s().as_slice()); } @@ -55,8 +64,14 @@ macro_rules! new_signing_test { #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] macro_rules! new_verification_test { - ($vectors:expr) => { - use $crate::elliptic_curve::sec1::EncodedPoint; + ($curve:path, $vectors:expr) => { + use core::convert::TryInto; + use $crate::{ + elliptic_curve::{sec1::EncodedPoint, Arithmetic, FromBytes}, + generic_array::GenericArray, + hazmat::VerifyPrimitive, + Signature, + }; #[test] fn ecdsa_verify_success() { @@ -67,14 +82,17 @@ macro_rules! new_verification_test { false, ); - let q: AffinePoint = q_encoded.decode().unwrap(); + let q: <$curve as Arithmetic>::AffinePoint = q_encoded.decode().unwrap(); + + let z = <$curve as Arithmetic>::Scalar::from_bytes(vector.m.try_into().unwrap()) + .unwrap(); let sig = Signature::from_scalars( GenericArray::from_slice(vector.r), GenericArray::from_slice(vector.s), ); - let result = q.verify_prehashed(GenericArray::from_slice(vector.m), &sig); + let result = q.verify_prehashed(&z, &sig); assert!(result.is_ok()); } } @@ -88,7 +106,10 @@ macro_rules! new_verification_test { false, ); - let q: AffinePoint = q_encoded.decode().unwrap(); + let q: <$curve as Arithmetic>::AffinePoint = q_encoded.decode().unwrap(); + + let z = <$curve as Arithmetic>::Scalar::from_bytes(vector.m.try_into().unwrap()) + .unwrap(); // Flip a bit in `s` let mut s_tweaked = GenericArray::clone_from_slice(vector.s); @@ -96,7 +117,7 @@ macro_rules! new_verification_test { let sig = Signature::from_scalars(GenericArray::from_slice(vector.r), &s_tweaked); - let result = q.verify_prehashed(GenericArray::from_slice(vector.m), &sig); + let result = q.verify_prehashed(&z, &sig); assert!(result.is_err()); } } diff --git a/ecdsa/src/hazmat.rs b/ecdsa/src/hazmat.rs index b63ffc57..278f8626 100644 --- a/ecdsa/src/hazmat.rs +++ b/ecdsa/src/hazmat.rs @@ -13,9 +13,7 @@ use crate::{Signature, SignatureSize}; use core::borrow::Borrow; -use elliptic_curve::{ - generic_array::ArrayLength, ops::Invert, weierstrass::Curve, Arithmetic, ElementBytes, -}; +use elliptic_curve::{generic_array::ArrayLength, ops::Invert, weierstrass::Curve, Arithmetic}; use signature::Error; #[cfg(feature = "digest")] @@ -36,12 +34,12 @@ where /// Accepts the following arguments: /// /// - `ephemeral_scalar`: ECDSA `k` value. MUST BE UNIFORMLY RANDOM!!! - /// - `hashed_msg`: hashed message digest to be signed. + /// - `hashed_msg`: scalar computed from a hashed message digest to be signed. /// MUST BE OUTPUT OF A CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!! fn try_sign_prehashed + Invert>( &self, ephemeral_scalar: &K, - hashed_msg: &ElementBytes, + hashed_msg: &C::Scalar, ) -> Result, Error>; } @@ -64,7 +62,7 @@ where fn try_sign_recoverable_prehashed + Invert>( &self, ephemeral_scalar: &K, - hashed_msg: &ElementBytes, + hashed_msg: &C::Scalar, ) -> Result; } @@ -77,7 +75,7 @@ where fn try_sign_prehashed + Invert>( &self, ephemeral_scalar: &K, - hashed_msg: &ElementBytes, + hashed_msg: &C::Scalar, ) -> Result, Error> { self.try_sign_recoverable_prehashed(ephemeral_scalar, hashed_msg) .map(Into::into) @@ -91,7 +89,7 @@ where /// particular curve's `AffinePoint` type. pub trait VerifyPrimitive where - C: Curve, + C: Curve + Arithmetic, SignatureSize: ArrayLength, { /// Verify the prehashed message against the provided signature @@ -103,7 +101,7 @@ where /// - `signature`: signature to be verified against the key and message fn verify_prehashed( &self, - hashed_msg: &ElementBytes, + hashed_msg: &C::Scalar, signature: &Signature, ) -> Result<(), Error>; } diff --git a/ecdsa/src/lib.rs b/ecdsa/src/lib.rs index a11a83b4..ae8d70c2 100644 --- a/ecdsa/src/lib.rs +++ b/ecdsa/src/lib.rs @@ -21,7 +21,7 @@ #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] #![forbid(unsafe_code)] -#![warn(missing_docs, rust_2018_idioms, intra_doc_link_resolution_failure)] +#![warn(missing_docs, rust_2018_idioms)] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png", html_root_url = "https://docs.rs/ecdsa/0.7.2" diff --git a/ecdsa/src/signer.rs b/ecdsa/src/signer.rs index 764c507b..4e073be3 100644 --- a/ecdsa/src/signer.rs +++ b/ecdsa/src/signer.rs @@ -5,7 +5,7 @@ // TODO(tarcieri): support for hardware crypto accelerators -mod rfc6979; +pub mod rfc6979; use crate::{ hazmat::{DigestPrimitive, SignPrimitive}, @@ -13,7 +13,7 @@ use crate::{ }; use elliptic_curve::{ generic_array::ArrayLength, ops::Invert, scalar::NonZeroScalar, weierstrass::Curve, - zeroize::Zeroize, Arithmetic, ElementBytes, FromBytes, FromDigest, SecretKey, + zeroize::Zeroize, Arithmetic, FromBytes, FromDigest, SecretKey, }; use signature::{ digest::{BlockInput, Digest, FixedOutput, Reset, Update}, @@ -21,16 +21,19 @@ use signature::{ }; #[cfg(feature = "rand")] -use signature::{ - rand_core::{CryptoRng, RngCore}, - RandomizedDigestSigner, RandomizedSigner, +use { + elliptic_curve::ElementBytes, + signature::{ + rand_core::{CryptoRng, RngCore}, + RandomizedDigestSigner, RandomizedSigner, + }, }; /// ECDSA signer pub struct Signer where C: Curve + Arithmetic, - C::Scalar: Invert + SignPrimitive + Zeroize, + C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, SignatureSize: ArrayLength, { secret_scalar: NonZeroScalar, @@ -39,7 +42,7 @@ where impl Signer where C: Curve + Arithmetic, - C::Scalar: Invert + SignPrimitive + Zeroize, + C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, SignatureSize: ArrayLength, { /// Create a new signer @@ -48,9 +51,7 @@ where // TODO(tarcieri): replace with into conversion when available (see subtle#73) if scalar.is_some().into() { - Ok(Self { - secret_scalar: scalar.unwrap(), - }) + Ok(Self::from(scalar.unwrap())) } else { Err(Error::new()) } @@ -61,13 +62,7 @@ impl DigestSigner> for Signer where C: Curve + Arithmetic, C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, - D: BlockInput - + FixedOutput - + Clone - + Default - + Reset - + Update, - ElementBytes: Zeroize, + D: FixedOutput + BlockInput + Clone + Default + Reset + Update, SignatureSize: ArrayLength, { /// Sign message prehash using a deterministic ephemeral scalar (`k`) @@ -75,17 +70,17 @@ where /// fn try_sign_digest(&self, digest: D) -> Result, Error> { let ephemeral_scalar = rfc6979::generate_k(&self.secret_scalar, digest.clone(), &[]); + let msg_scalar = C::Scalar::from_digest(digest); self.secret_scalar - .as_ref() - .try_sign_prehashed(ephemeral_scalar.as_ref(), &digest.finalize()) + .try_sign_prehashed(ephemeral_scalar.as_ref(), &msg_scalar) } } impl signature::Signer> for Signer where C: Curve + Arithmetic + DigestPrimitive, - C::Scalar: Invert + SignPrimitive + Zeroize, + C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, SignatureSize: ArrayLength, Self: DigestSigner>, { @@ -100,13 +95,7 @@ impl RandomizedDigestSigner> for Signer where C: Curve + Arithmetic, C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, - D: BlockInput - + FixedOutput - + Clone - + Default - + Reset - + Update, - ElementBytes: Zeroize, + D: FixedOutput + BlockInput + Clone + Default + Reset + Update, SignatureSize: ArrayLength, { /// Sign message prehash using an ephemeral scalar (`k`) derived according @@ -123,9 +112,10 @@ where let ephemeral_scalar = rfc6979::generate_k(&self.secret_scalar, digest.clone(), &added_entropy); + let msg_scalar = C::Scalar::from_digest(digest); + self.secret_scalar - .as_ref() - .try_sign_prehashed(ephemeral_scalar.as_ref(), &digest.finalize()) + .try_sign_prehashed(ephemeral_scalar.as_ref(), &msg_scalar) } } @@ -134,7 +124,7 @@ where impl RandomizedSigner> for Signer where C: Curve + Arithmetic + DigestPrimitive, - C::Scalar: Invert + SignPrimitive + Zeroize, + C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, SignatureSize: ArrayLength, Self: RandomizedDigestSigner>, { @@ -147,11 +137,21 @@ where } } -impl Zeroize for Signer +impl From> for Signer where C: Curve + Arithmetic, - C::Scalar: Invert + SignPrimitive + Zeroize, + C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, + SignatureSize: ArrayLength, +{ + fn from(secret_scalar: NonZeroScalar) -> Self { + Self { secret_scalar } + } +} +impl Zeroize for Signer +where + C: Curve + Arithmetic, + C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, SignatureSize: ArrayLength, { fn zeroize(&mut self) { @@ -162,8 +162,7 @@ where impl Drop for Signer where C: Curve + Arithmetic, - C::Scalar: Invert + SignPrimitive + Zeroize, - + C::Scalar: FromDigest + Invert + SignPrimitive + Zeroize, SignatureSize: ArrayLength, { fn drop(&mut self) { diff --git a/ecdsa/src/signer/rfc6979.rs b/ecdsa/src/signer/rfc6979.rs index e9a7b163..df75d549 100644 --- a/ecdsa/src/signer/rfc6979.rs +++ b/ecdsa/src/signer/rfc6979.rs @@ -14,7 +14,7 @@ use hmac::{Hmac, Mac, NewMac}; /// Generate ephemeral scalar `k` from the secret scalar and a digest of the /// input message. -pub(super) fn generate_k( +pub fn generate_k( secret_scalar: &NonZeroScalar, msg_digest: D, additional_data: &[u8], @@ -22,17 +22,12 @@ pub(super) fn generate_k( where C: Arithmetic, C::Scalar: FromDigest + Invert + Zeroize, - D: BlockInput - + FixedOutput - + Clone - + Default - + Reset - + Update, - ElementBytes: Zeroize, + D: FixedOutput + BlockInput + Clone + Default + Reset + Update, { - let x = Zeroizing::new(secret_scalar.to_bytes()); + let mut x = secret_scalar.to_bytes(); let h1: ElementBytes = C::Scalar::from_digest(msg_digest).into(); - let mut hmac_drbg = HmacDrbg::::new(&*x, &h1, additional_data); + let mut hmac_drbg = HmacDrbg::::new(&x, &h1, additional_data); + x.zeroize(); loop { let k = NonZeroScalar::from_bytes(&hmac_drbg.next()); @@ -51,12 +46,7 @@ where // TODO(tarcieri): use `hmac-drbg` crate when sorpaas/rust-hmac-drbg#3 is merged struct HmacDrbg where - D: BlockInput::OutputSize> - + FixedOutput - + Clone - + Default - + Reset - + Update, + D: BlockInput + FixedOutput + Clone + Default + Reset + Update, { /// HMAC key `K` (see RFC 6979 Section 3.2.c) k: Hmac, @@ -67,12 +57,7 @@ where impl HmacDrbg where - D: BlockInput::OutputSize> - + FixedOutput - + Clone - + Default - + Reset - + Update, + D: BlockInput + FixedOutput + Clone + Default + Reset + Update, { /// Initialize `HMAC_DRBG` pub fn new(entropy_input: &[u8], nonce: &[u8], additional_data: &[u8]) -> Self { @@ -89,7 +74,7 @@ where k.update(entropy_input); k.update(nonce); k.update(additional_data); - k = Hmac::new(&k.finalize().into_bytes()); + k = Hmac::new_varkey(&k.finalize().into_bytes()).unwrap(); // Steps 3.2.e,g: v = HMAC_k(v) k.update(&v); @@ -106,7 +91,7 @@ where self.k.update(&t); self.k.update(&[0x00]); - self.k = Hmac::new(&self.k.finalize_reset().into_bytes()); + self.k = Hmac::new_varkey(&self.k.finalize_reset().into_bytes()).unwrap(); self.k.update(&t); self.v = self.k.finalize_reset().into_bytes(); diff --git a/ecdsa/src/verifier.rs b/ecdsa/src/verifier.rs index 6d9df933..d8f5b9ea 100644 --- a/ecdsa/src/verifier.rs +++ b/ecdsa/src/verifier.rs @@ -13,7 +13,7 @@ use elliptic_curve::{ generic_array::ArrayLength, sec1::{EncodedPoint, FromEncodedPoint, UncompressedPointSize, UntaggedPointSize}, weierstrass::Curve, - Arithmetic, + Arithmetic, FromDigest, }; use signature::{digest::Digest, DigestVerifier}; @@ -26,6 +26,7 @@ impl Verifier where C: Curve + Arithmetic, C::AffinePoint: VerifyPrimitive + FromEncodedPoint, + C::Scalar: FromDigest, UntaggedPointSize: Add + ArrayLength, UncompressedPointSize: ArrayLength, SignatureSize: ArrayLength, @@ -49,11 +50,12 @@ where C: Curve + Arithmetic, D: Digest, C::AffinePoint: VerifyPrimitive, + C::Scalar: FromDigest, SignatureSize: ArrayLength, { fn verify_digest(&self, digest: D, signature: &Signature) -> Result<(), Error> { self.public_key - .verify_prehashed(&digest.finalize(), signature) + .verify_prehashed(&C::Scalar::from_digest(digest), signature) } } @@ -62,6 +64,7 @@ where C: Curve + Arithmetic + DigestPrimitive, C::AffinePoint: VerifyPrimitive, C::Digest: Digest, + C::Scalar: FromDigest, SignatureSize: ArrayLength, { fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {