diff --git a/Cargo.lock b/Cargo.lock index 3a8852c2..0f8f0031 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,7 +148,7 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.17.0-pre.1" +version = "0.17.0-pre.2" dependencies = [ "der 0.8.0-pre.0", "digest", diff --git a/ecdsa/src/hazmat.rs b/ecdsa/src/hazmat.rs index b2b13532..e4f4d880 100644 --- a/ecdsa/src/hazmat.rs +++ b/ecdsa/src/hazmat.rs @@ -98,7 +98,7 @@ where ) -> Result<(Signature, Option)> where Self: From> + Invert>, - D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, + D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset, { let k = Scalar::::from_repr(rfc6979::generate_k::( &self.to_repr(), diff --git a/rfc6979/src/ct_cmp.rs b/rfc6979/src/ct_cmp.rs index 45213048..4cca7adc 100644 --- a/rfc6979/src/ct_cmp.rs +++ b/rfc6979/src/ct_cmp.rs @@ -1,14 +1,14 @@ //! Constant-time comparison helpers for [`ByteArray`]. -use crate::{ArraySize, ByteArray}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -/// Constant-time equals. -pub(crate) fn ct_eq(a: &ByteArray, b: &ByteArray) -> Choice { +/// Constant-time test that a given byte slice contains only zeroes. +#[inline] +pub(crate) fn ct_is_zero(n: &[u8]) -> Choice { let mut ret = Choice::from(1); - for (a, b) in a.iter().zip(b.iter()) { - ret.conditional_assign(&Choice::from(0), !a.ct_eq(b)); + for byte in n { + ret.conditional_assign(&Choice::from(0), byte.ct_ne(&0)); } ret @@ -17,7 +17,10 @@ pub(crate) fn ct_eq(a: &ByteArray, b: &ByteArray) -> Choice /// Constant-time less than. /// /// Inputs are interpreted as big endian integers. -pub(crate) fn ct_lt(a: &ByteArray, b: &ByteArray) -> Choice { +#[inline] +pub(crate) fn ct_lt(a: &[u8], b: &[u8]) -> Choice { + debug_assert_eq!(a.len(), b.len()); + let mut borrow = 0; // Perform subtraction with borrow a byte-at-a-time, interpreting a @@ -40,48 +43,39 @@ mod tests { const F: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF]; #[test] - fn ct_eq() { - use super::ct_eq; - - assert_eq!(ct_eq(&A.into(), &A.into()).unwrap_u8(), 1); - assert_eq!(ct_eq(&B.into(), &B.into()).unwrap_u8(), 1); - assert_eq!(ct_eq(&C.into(), &C.into()).unwrap_u8(), 1); - assert_eq!(ct_eq(&D.into(), &D.into()).unwrap_u8(), 1); - assert_eq!(ct_eq(&E.into(), &E.into()).unwrap_u8(), 1); - assert_eq!(ct_eq(&F.into(), &F.into()).unwrap_u8(), 1); - - assert_eq!(ct_eq(&A.into(), &B.into()).unwrap_u8(), 0); - assert_eq!(ct_eq(&C.into(), &D.into()).unwrap_u8(), 0); - assert_eq!(ct_eq(&E.into(), &F.into()).unwrap_u8(), 0); + fn ct_is_zero() { + use super::ct_is_zero; + assert_eq!(ct_is_zero(&A).unwrap_u8(), 1); + assert_eq!(ct_is_zero(&B).unwrap_u8(), 0); } #[test] fn ct_lt() { use super::ct_lt; - assert_eq!(ct_lt(&A.into(), &A.into()).unwrap_u8(), 0); - assert_eq!(ct_lt(&B.into(), &B.into()).unwrap_u8(), 0); - assert_eq!(ct_lt(&C.into(), &C.into()).unwrap_u8(), 0); - assert_eq!(ct_lt(&D.into(), &D.into()).unwrap_u8(), 0); - assert_eq!(ct_lt(&E.into(), &E.into()).unwrap_u8(), 0); - assert_eq!(ct_lt(&F.into(), &F.into()).unwrap_u8(), 0); - - assert_eq!(ct_lt(&A.into(), &B.into()).unwrap_u8(), 1); - assert_eq!(ct_lt(&A.into(), &C.into()).unwrap_u8(), 1); - assert_eq!(ct_lt(&B.into(), &A.into()).unwrap_u8(), 0); - assert_eq!(ct_lt(&C.into(), &A.into()).unwrap_u8(), 0); - - assert_eq!(ct_lt(&B.into(), &C.into()).unwrap_u8(), 1); - assert_eq!(ct_lt(&B.into(), &D.into()).unwrap_u8(), 1); - assert_eq!(ct_lt(&C.into(), &B.into()).unwrap_u8(), 0); - assert_eq!(ct_lt(&D.into(), &B.into()).unwrap_u8(), 0); - - assert_eq!(ct_lt(&C.into(), &D.into()).unwrap_u8(), 1); - assert_eq!(ct_lt(&C.into(), &E.into()).unwrap_u8(), 1); - assert_eq!(ct_lt(&D.into(), &C.into()).unwrap_u8(), 0); - assert_eq!(ct_lt(&E.into(), &C.into()).unwrap_u8(), 0); - - assert_eq!(ct_lt(&E.into(), &F.into()).unwrap_u8(), 1); - assert_eq!(ct_lt(&F.into(), &E.into()).unwrap_u8(), 0); + assert_eq!(ct_lt(&A, &A).unwrap_u8(), 0); + assert_eq!(ct_lt(&B, &B).unwrap_u8(), 0); + assert_eq!(ct_lt(&C, &C).unwrap_u8(), 0); + assert_eq!(ct_lt(&D, &D).unwrap_u8(), 0); + assert_eq!(ct_lt(&E, &E).unwrap_u8(), 0); + assert_eq!(ct_lt(&F, &F).unwrap_u8(), 0); + + assert_eq!(ct_lt(&A, &B).unwrap_u8(), 1); + assert_eq!(ct_lt(&A, &C).unwrap_u8(), 1); + assert_eq!(ct_lt(&B, &A).unwrap_u8(), 0); + assert_eq!(ct_lt(&C, &A).unwrap_u8(), 0); + + assert_eq!(ct_lt(&B, &C).unwrap_u8(), 1); + assert_eq!(ct_lt(&B, &D).unwrap_u8(), 1); + assert_eq!(ct_lt(&C, &B).unwrap_u8(), 0); + assert_eq!(ct_lt(&D, &B).unwrap_u8(), 0); + + assert_eq!(ct_lt(&C, &D).unwrap_u8(), 1); + assert_eq!(ct_lt(&C, &E).unwrap_u8(), 1); + assert_eq!(ct_lt(&D, &C).unwrap_u8(), 0); + assert_eq!(ct_lt(&E, &C).unwrap_u8(), 0); + + assert_eq!(ct_lt(&E, &F).unwrap_u8(), 1); + assert_eq!(ct_lt(&F, &E).unwrap_u8(), 0); } } diff --git a/rfc6979/src/lib.rs b/rfc6979/src/lib.rs index 67d8022e..220c5631 100644 --- a/rfc6979/src/lib.rs +++ b/rfc6979/src/lib.rs @@ -50,9 +50,6 @@ use hmac::{ SimpleHmac, }; -/// Array of bytes representing a scalar serialized as a big endian integer. -pub type ByteArray = Array; - /// Deterministically generate ephemeral scalar `k`. /// /// Accepts the following parameters and inputs: @@ -63,24 +60,48 @@ pub type ByteArray = Array; /// - `data`: additional associated data, e.g. CSRNG output used as added entropy #[inline] pub fn generate_k( - x: &ByteArray, - n: &ByteArray, - h: &ByteArray, + x: &Array, + n: &Array, + h: &Array, data: &[u8], -) -> ByteArray +) -> Array where - D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset, + D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset, N: ArraySize, { + let mut k = Array::default(); + generate_k_mut::(x, n, h, data, &mut k); + k +} + +/// Deterministically generate ephemeral scalar `k` by writing it into the provided output buffer. +/// +/// This is an API which accepts dynamically sized inputs intended for use cases where the sizes +/// are determined at runtime, such as the legacy Digital Signature Algorithm (DSA). +/// +/// Accepts the following parameters and inputs: +/// +/// - `x`: secret key +/// - `n`: field modulus +/// - `h`: hash/digest of input message: must be reduced modulo `n` in advance +/// - `data`: additional associated data, e.g. CSRNG output used as added entropy +#[inline] +pub fn generate_k_mut(x: &[u8], n: &[u8], h: &[u8], data: &[u8], k: &mut [u8]) +where + D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset, +{ + assert_eq!(k.len(), x.len()); + assert_eq!(k.len(), n.len()); + assert_eq!(k.len(), h.len()); + let mut hmac_drbg = HmacDrbg::::new(x, h, data); loop { - let mut k = ByteArray::::default(); - hmac_drbg.fill_bytes(&mut k); + hmac_drbg.fill_bytes(k); - let k_is_zero = ct_cmp::ct_eq(&k, &ByteArray::default()); - if (!k_is_zero & ct_cmp::ct_lt(&k, n)).into() { - return k; + let k_is_zero = ct_cmp::ct_is_zero(k); + if (!k_is_zero & ct_cmp::ct_lt(k, n)).into() { + return; } } }