diff --git a/Cargo.lock b/Cargo.lock index f878b590..113df3c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2458,6 +2458,7 @@ version = "0.4.6" dependencies = [ "ciphersuite 0.4.2", "crypto-bigint 0.5.5", + "crypto-bigint 0.6.1", "curve25519-dalek", "digest 0.10.7", "ff-group-tests", @@ -6005,6 +6006,7 @@ name = "minimal-ed448" version = "0.4.2" dependencies = [ "ciphersuite 0.4.2", + "crypto-bigint 0.6.1", "ff-group-tests", "hex", "prime-field", diff --git a/crypto/dalek-ff-group/Cargo.toml b/crypto/dalek-ff-group/Cargo.toml index 46d32f51..215386a9 100644 --- a/crypto/dalek-ff-group/Cargo.toml +++ b/crypto/dalek-ff-group/Cargo.toml @@ -28,7 +28,8 @@ sha2 = { version = "0.11.0-rc.0", default-features = false } prime-field = { path = "../prime-field", default-features = false } ciphersuite = { version = "0.4.2", path = "../ciphersuite", default-features = false } -crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] } +crypto-bigint-05 = { package = "crypto-bigint", version = "0.5", default-features = false, features = ["zeroize"] } +crypto-bigint = { version = "0.6", default-features = false, features = ["zeroize"] } curve25519-dalek = { version = ">= 4.0, < 4.2", default-features = false, features = ["zeroize", "digest", "group", "precomputed-tables"] } diff --git a/crypto/dalek-ff-group/src/lib.rs b/crypto/dalek-ff-group/src/lib.rs index 74b38887..dbe4d3e2 100644 --- a/crypto/dalek-ff-group/src/lib.rs +++ b/crypto/dalek-ff-group/src/lib.rs @@ -500,8 +500,18 @@ impl FieldElement { /// /// This will reduce the `U256` by the modulus, into a member of the field. #[deprecated] - pub const fn from_u256(u256: &crypto_bigint::U256) -> Self { - FieldElement::from(&prime_field::crypto_bigint::U256::from_words(*u256.as_words())) + pub const fn from_u256(u256: &crypto_bigint_05::U256) -> Self { + const MODULUS: crypto_bigint::U256 = crypto_bigint::U256::from_be_hex( + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", + ); + let mut u256 = crypto_bigint::U256::from_words(*u256.as_words()); + loop { + let result = FieldElement::from_bytes(&u256.to_le_bytes()); + if let Some(result) = result { + return result; + } + u256 = u256.wrapping_sub(&MODULUS); + } } /// Create a `FieldElement` from the reduction of a 512-bit number. diff --git a/crypto/ed448/Cargo.toml b/crypto/ed448/Cargo.toml index 2a698230..4c78ea86 100644 --- a/crypto/ed448/Cargo.toml +++ b/crypto/ed448/Cargo.toml @@ -21,6 +21,7 @@ zeroize = { version = "1", default-features = false, features = ["zeroize_derive sha3 = { version = "0.11.0-rc.0", default-features = false } +crypto-bigint = { version = "0.6", default-features = false, features = ["zeroize"] } prime-field = { path = "../prime-field", default-features = false } ciphersuite = { path = "../ciphersuite", default-features = false } @@ -32,6 +33,6 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] } ff-group-tests = { path = "../ff-group-tests" } [features] -alloc = ["zeroize/alloc", "sha3/alloc", "prime-field/alloc", "ciphersuite/alloc"] +alloc = ["zeroize/alloc", "sha3/alloc", "crypto-bigint/alloc", "prime-field/alloc", "ciphersuite/alloc"] std = ["alloc", "zeroize/std", "prime-field/std", "ciphersuite/std"] default = ["std"] diff --git a/crypto/ed448/src/point.rs b/crypto/ed448/src/point.rs index 95552663..8fb1fa7a 100644 --- a/crypto/ed448/src/point.rs +++ b/crypto/ed448/src/point.rs @@ -7,8 +7,8 @@ use prime_field::{ subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable}, zeroize::Zeroize, rand_core::RngCore, - crypto_bigint::U512, }; +use crypto_bigint::U512; use ciphersuite::group::{ ff::{Field, PrimeField, PrimeFieldBits}, @@ -18,17 +18,37 @@ use ciphersuite::group::{ use crate::{u8_from_bool, Scalar, FieldElement}; -const G_Y: FieldElement = FieldElement::from(&U512::from_be_hex(concat!( - "0000000000000000", - "693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e", - "05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14", -))); +const G_Y: FieldElement = { + let bytes = U512::from_be_hex(concat!( + "0000000000000000", + "693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e", + "05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14", + )) + .to_le_bytes(); + let mut dest = [0; 57]; + let mut i = 0; + while i < dest.len() { + dest[i] = bytes[i]; + i += 1; + } + FieldElement::from_bytes(&dest).unwrap() +}; -const G_X: FieldElement = FieldElement::from(&U512::from_be_hex(concat!( - "0000000000000000", - "4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324", - "a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e", -))); +const G_X: FieldElement = { + let bytes = U512::from_be_hex(concat!( + "0000000000000000", + "4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324", + "a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e", + )) + .to_le_bytes(); + let mut dest = [0; 57]; + let mut i = 0; + while i < dest.len() { + dest[i] = bytes[i]; + i += 1; + } + FieldElement::from_bytes(&dest).unwrap() +}; fn recover_x(y: FieldElement) -> CtOption { #[allow(non_snake_case)] diff --git a/crypto/prime-field/src/lib.rs b/crypto/prime-field/src/lib.rs index 46124194..6733fc85 100644 --- a/crypto/prime-field/src/lib.rs +++ b/crypto/prime-field/src/lib.rs @@ -5,11 +5,11 @@ pub use subtle; pub use zeroize; pub use rand_core; -pub use crypto_bigint; pub use ff; #[doc(hidden)] pub mod __prime_field_private { + pub use crypto_bigint; pub use paste; #[cfg(feature = "std")] pub use ff_group_tests; @@ -94,6 +94,8 @@ pub mod __prime_field_private { /// less than `modulus_as_be_hex`. /// /// `big_endian` controls if the encoded representation will be big-endian or not. +/// +/// `repr` must satisfy the requirements for `PrimeField::Repr`. #[doc(hidden)] #[macro_export] macro_rules! odd_prime_field_with_specific_repr { @@ -116,13 +118,15 @@ macro_rules! odd_prime_field_with_specific_repr { }, zeroize::Zeroize, rand_core::RngCore, - crypto_bigint::{ - Limb, Encoding, Integer, Uint, - modular::{ConstMontyParams, ConstMontyForm}, - impl_modulus, - }, ff::*, - __prime_field_private::*, + __prime_field_private::{ + crypto_bigint::{ + Limb, Encoding, Integer, Uint, + modular::{ConstMontyParams, ConstMontyForm}, + impl_modulus, + }, + *, + }, }; const MODULUS_WITHOUT_PREFIX: &str = hex_str_without_prefix($modulus_as_be_hex); @@ -180,7 +184,7 @@ macro_rules! odd_prime_field_with_specific_repr { const MODULUS_MINUS_TWO: UnderlyingUint = MODULUS.wrapping_sub(&UnderlyingUint::from_u8(2)); const T: UnderlyingUint = MODULUS_MINUS_ONE.shr_vartime($name::S); - /// A field automatically generated with `short-weierstrass`. + /// A field automatically generated with `prime-field`. #[derive(Clone, Copy, Eq, Debug)] pub struct $name(Underlying); @@ -192,9 +196,47 @@ macro_rules! odd_prime_field_with_specific_repr { impl $name { /// Create a `$name` from the `Uint` type underlying it. - pub const fn from(value: &UnderlyingUint) -> Self { + const fn from(value: &UnderlyingUint) -> Self { $name(Underlying::new(value)) } + + /// Create a `$name` from bytes within a `const` context. + /// + /// This function executes in variable time. `<$name as PrimeField>::from_repr` SHOULD + /// be used instead. + pub const fn from_bytes(value: &[u8; MODULUS_BYTES]) -> Option { + let mut expanded_repr = [0; UnderlyingUint::BYTES]; + let repr: &[u8] = value.as_slice(); + let (uint, repr) = if $big_endian { + let start = UnderlyingUint::BYTES - MODULUS_BYTES; + let mut i = 0; + while i < repr.len() { + expanded_repr[start + i] = repr[i]; + i += 1; + } + let uint = Underlying::new(&UnderlyingUint::from_be_slice(&expanded_repr)); + (uint, uint.retrieve().to_be_bytes()) + } else { + let mut i = 0; + while i < repr.len() { + expanded_repr[i] = repr[i]; + i += 1; + } + let uint = Underlying::new(&UnderlyingUint::from_le_slice(&expanded_repr)); + (uint, uint.retrieve().to_le_bytes()) + }; + + // Ensure the representations match + let mut i = 0; + while i < expanded_repr.len() { + if repr[i] != expanded_repr[i] { + return None; + } + i += 1; + } + + Some(Self(uint)) + } } impl From for $name { fn from(value: u8) -> Self { @@ -416,6 +458,7 @@ macro_rules! odd_prime_field_with_specific_repr { } /// The encoded representation of a `$name`. + // This is required to be bespoke to satisfy `Default`. #[derive(Clone, Copy)] pub struct Repr([u8; MODULUS_BYTES]); impl Default for Repr {