diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
commit | 2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch) | |
tree | 033cc839730fda84ff08db877037977be94e5e3a /vendor/rfc6979/src | |
parent | Initial commit. (diff) | |
download | cargo-upstream.tar.xz cargo-upstream.zip |
Adding upstream version 0.70.1+ds1.upstream/0.70.1+ds1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/rfc6979/src')
-rw-r--r-- | vendor/rfc6979/src/ct_cmp.rs | 87 | ||||
-rw-r--r-- | vendor/rfc6979/src/lib.rs | 149 |
2 files changed, 236 insertions, 0 deletions
diff --git a/vendor/rfc6979/src/ct_cmp.rs b/vendor/rfc6979/src/ct_cmp.rs new file mode 100644 index 0000000..8b216e6 --- /dev/null +++ b/vendor/rfc6979/src/ct_cmp.rs @@ -0,0 +1,87 @@ +//! Constant-time comparison helpers for [`ByteArray`]. + +use crate::{ArrayLength, ByteArray}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +/// Constant-time equals. +pub(crate) fn ct_eq<N: ArrayLength<u8>>(a: &ByteArray<N>, b: &ByteArray<N>) -> 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)); + } + + ret +} + +/// Constant-time less than. +/// +/// Inputs are interpreted as big endian integers. +pub(crate) fn ct_lt<N: ArrayLength<u8>>(a: &ByteArray<N>, b: &ByteArray<N>) -> Choice { + let mut borrow = 0; + + // Perform subtraction with borrow a byte-at-a-time, interpreting a + // no-borrow condition as the less-than case + for (&a, &b) in a.iter().zip(b.iter()).rev() { + let c = (b as u16).wrapping_add(borrow >> (u8::BITS - 1)); + borrow = (a as u16).wrapping_sub(c) >> u8::BITS as u8; + } + + !borrow.ct_eq(&0) +} + +#[cfg(test)] +mod tests { + const A: [u8; 4] = [0, 0, 0, 0]; + const B: [u8; 4] = [0, 0, 0, 1]; + const C: [u8; 4] = [0xFF, 0, 0, 0]; + const D: [u8; 4] = [0xFF, 0, 0, 1]; + const E: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFE]; + 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); + } + + #[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); + } +} diff --git a/vendor/rfc6979/src/lib.rs b/vendor/rfc6979/src/lib.rs new file mode 100644 index 0000000..817dda1 --- /dev/null +++ b/vendor/rfc6979/src/lib.rs @@ -0,0 +1,149 @@ +#![no_std] +#![doc = include_str!("../README.md")] +#![forbid(unsafe_code, clippy::unwrap_used)] +#![warn(missing_docs, rust_2018_idioms)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" +)] + +//! ## Usage +//! +//! See also: the documentation for the [`generate_k`] function. +//! +//! ``` +//! use hex_literal::hex; +//! use rfc6979::consts::U32; +//! use sha2::{Digest, Sha256}; +//! +//! // NIST P-256 field modulus +//! const NIST_P256_MODULUS: [u8; 32] = +//! hex!("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); +//! +//! // Public key for RFC6979 NIST P256/SHA256 test case +//! const RFC6979_KEY: [u8; 32] = +//! hex!("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721"); +//! +//! // Test message for RFC6979 NIST P256/SHA256 test case +//! const RFC6979_MSG: &[u8; 6] = b"sample"; +//! +//! // Expected K for RFC6979 NIST P256/SHA256 test case +//! const RFC6979_EXPECTED_K: [u8; 32] = +//! hex!("A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60"); +//! +//! let h = Sha256::digest(RFC6979_MSG); +//! let aad = b""; +//! let k = rfc6979::generate_k::<Sha256, U32>(&RFC6979_KEY.into(), &NIST_P256_MODULUS.into(), &h, aad); +//! assert_eq!(k.as_slice(), &RFC6979_EXPECTED_K); +//! ``` + +mod ct_cmp; + +pub use hmac::digest::generic_array::typenum::consts; + +use hmac::{ + digest::{ + core_api::BlockSizeUser, + generic_array::{ArrayLength, GenericArray}, + Digest, FixedOutput, FixedOutputReset, Mac, + }, + SimpleHmac, +}; + +/// Array of bytes representing a scalar serialized as a big endian integer. +pub type ByteArray<Size> = GenericArray<u8, Size>; + +/// Deterministically generate ephemeral scalar `k`. +/// +/// 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<D, N>( + x: &ByteArray<N>, + n: &ByteArray<N>, + h: &ByteArray<N>, + data: &[u8], +) -> ByteArray<N> +where + D: Digest + BlockSizeUser + FixedOutput<OutputSize = N> + FixedOutputReset, + N: ArrayLength<u8>, +{ + let mut hmac_drbg = HmacDrbg::<D>::new(x, h, data); + + loop { + let mut k = ByteArray::<N>::default(); + hmac_drbg.fill_bytes(&mut 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; + } + } +} + +/// Internal implementation of `HMAC_DRBG` as described in NIST SP800-90A. +/// +/// <https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final> +/// +/// This is a HMAC-based deterministic random bit generator used compute a +/// deterministic ephemeral scalar `k`. +pub struct HmacDrbg<D> +where + D: Digest + BlockSizeUser + FixedOutputReset, +{ + /// HMAC key `K` (see RFC 6979 Section 3.2.c) + k: SimpleHmac<D>, + + /// Chaining value `V` (see RFC 6979 Section 3.2.c) + v: GenericArray<u8, D::OutputSize>, +} + +impl<D> HmacDrbg<D> +where + D: Digest + BlockSizeUser + FixedOutputReset, +{ + /// Initialize `HMAC_DRBG` + pub fn new(entropy_input: &[u8], nonce: &[u8], additional_data: &[u8]) -> Self { + let mut k = SimpleHmac::new(&Default::default()); + let mut v = GenericArray::default(); + + for b in &mut v { + *b = 0x01; + } + + for i in 0..=1 { + k.update(&v); + k.update(&[i]); + k.update(entropy_input); + k.update(nonce); + k.update(additional_data); + k = SimpleHmac::new_from_slice(&k.finalize().into_bytes()).expect("HMAC error"); + + // Steps 3.2.e,g: v = HMAC_k(v) + k.update(&v); + v = k.finalize_reset().into_bytes(); + } + + Self { k, v } + } + + /// Write the next `HMAC_DRBG` output to the given byte slice. + pub fn fill_bytes(&mut self, out: &mut [u8]) { + for out_chunk in out.chunks_mut(self.v.len()) { + self.k.update(&self.v); + self.v = self.k.finalize_reset().into_bytes(); + out_chunk.copy_from_slice(&self.v[..out_chunk.len()]); + } + + self.k.update(&self.v); + self.k.update(&[0x00]); + self.k = + SimpleHmac::new_from_slice(&self.k.finalize_reset().into_bytes()).expect("HMAC error"); + self.k.update(&self.v); + self.v = self.k.finalize_reset().into_bytes(); + } +} |