diff --git a/Cargo.lock b/Cargo.lock index 12ca4e3c..9064eacd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -585,7 +585,7 @@ dependencies = [ "bitcoin", "flexible-transcript", "hex", - "k256 0.12.0", + "k256", "lazy_static", "modular-frost", "rand_core 0.6.4", @@ -735,6 +735,19 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc6d6292be3a19e6379786dac800f551e5865a5bb51ebbe3064ab80433f403" +dependencies = [ + "ff 0.13.0", + "group 0.13.0", + "pairing", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "bounded-collections" version = "0.1.5" @@ -1017,15 +1030,15 @@ version = "0.3.0" dependencies = [ "dalek-ff-group", "digest 0.10.6", - "elliptic-curve 0.12.3", - "ff 0.12.1", + "elliptic-curve 0.13.2", + "ff 0.13.0", "ff-group-tests", "flexible-transcript", - "group 0.12.1", + "group 0.13.0", "hex", - "k256 0.12.0", + "k256", "minimal-ed448", - "p256 0.12.0", + "p256 0.13.0", "rand_core 0.6.4", "sha2 0.10.6", "sha3", @@ -1141,7 +1154,7 @@ dependencies = [ "digest 0.10.6", "getrandom 0.2.8", "hmac 0.12.1", - "k256 0.13.0", + "k256", "lazy_static", "serde", "sha2 0.10.6", @@ -1641,9 +1654,9 @@ dependencies = [ "crypto-bigint 0.5.1", "curve25519-dalek 3.2.0", "digest 0.10.6", - "ff 0.12.1", + "ff 0.13.0", "ff-group-tests", - "group 0.12.1", + "group 0.13.0", "rand_core 0.6.4", "sha2 0.9.9", "subtle", @@ -1975,11 +1988,11 @@ dependencies = [ "blake2", "dalek-ff-group", "digest 0.10.6", - "ff 0.12.1", + "ff 0.13.0", "flexible-transcript", - "group 0.12.1", + "group 0.13.0", "hex-literal", - "k256 0.12.0", + "k256", "multiexp", "rand_core 0.6.4", "thiserror", @@ -2049,18 +2062,6 @@ dependencies = [ "signature 1.6.4", ] -[[package]] -name = "ecdsa" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 2.0.0", -] - [[package]] name = "ecdsa" version = "0.16.1" @@ -2190,7 +2191,7 @@ dependencies = [ "base64 0.13.1", "bytes", "hex", - "k256 0.13.0", + "k256", "log", "rand 0.8.5", "rlp", @@ -2323,8 +2324,8 @@ dependencies = [ "ethers", "ethers-solc", "eyre", - "group 0.12.1", - "k256 0.12.0", + "group 0.13.0", + "k256", "modular-frost", "rand_core 0.6.4", "serde", @@ -2455,7 +2456,7 @@ dependencies = [ "generic-array 0.14.6", "getrandom 0.2.8", "hex", - "k256 0.13.0", + "k256", "num_enum", "once_cell", "open-fastrlp", @@ -2677,7 +2678,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ - "bitvec 1.0.1", "rand_core 0.6.4", "subtle", ] @@ -2688,18 +2688,22 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ + "bitvec 1.0.1", "rand_core 0.6.4", "subtle", ] [[package]] name = "ff-group-tests" -version = "0.12.2" +version = "0.13.0" dependencies = [ - "group 0.12.1", - "k256 0.12.0", - "p256 0.12.0", + "bls12_381", + "group 0.13.0", + "k256", + "p256 0.13.0", + "pasta_curves", "rand_core 0.6.4", + "subtle", ] [[package]] @@ -2960,7 +2964,7 @@ dependencies = [ "frame-metadata", "frame-support-procedural", "impl-trait-for-tuples", - "k256 0.13.0", + "k256", "log", "once_cell", "parity-scale-codec", @@ -4119,25 +4123,10 @@ dependencies = [ "tracing", ] -[[package]] -name = "k256" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a55e0ff3b72c262bcf041d9e97f1b84492b68f1c1a384de2323d3dc9403397" -dependencies = [ - "cfg-if", - "ecdsa 0.15.1", - "elliptic-curve 0.12.3", - "once_cell", - "sha2 0.10.6", - "signature 2.0.0", -] - [[package]] name = "k256" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955890845095ccf31ef83ad41a05aabb4d8cc23dc3cac5a9f5c89cf26dd0da75" +source = "git+https://github.com/RustCrypto/elliptic-curves?rev=43d95961b77ad18e54d2874403052fdee248076c#43d95961b77ad18e54d2874403052fdee248076c" dependencies = [ "cfg-if", "ecdsa 0.16.1", @@ -4226,6 +4215,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "lazycell" @@ -5047,11 +5039,10 @@ name = "minimal-ed448" version = "0.3.0" dependencies = [ "crypto-bigint 0.5.1", - "dalek-ff-group", - "ff 0.12.1", + "ff 0.13.0", "ff-group-tests", "generic-array 0.14.6", - "group 0.12.1", + "group 0.13.0", "hex", "lazy_static", "rand_core 0.6.4", @@ -5169,7 +5160,7 @@ version = "0.3.0" dependencies = [ "curve25519-dalek 3.2.0", "dalek-ff-group", - "group 0.12.1", + "group 0.13.0", "lazy_static", "sha3", "subtle", @@ -5207,7 +5198,7 @@ dependencies = [ "dleq", "flexible-transcript", "futures", - "group 0.12.1", + "group 0.13.0", "hex", "hex-literal", "lazy_static", @@ -5283,9 +5274,9 @@ name = "multiexp" version = "0.3.1" dependencies = [ "dalek-ff-group", - "ff 0.12.1", - "group 0.12.1", - "k256 0.12.0", + "ff 0.13.0", + "group 0.13.0", + "k256", "rand_core 0.6.4", "zeroize", ] @@ -5759,12 +5750,11 @@ dependencies = [ [[package]] name = "p256" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c124b3cbce43bcbac68c58ec181d98ed6cc7e6d0aa7c3ba97b2563410b0e55" +version = "0.13.0" +source = "git+https://github.com/RustCrypto/elliptic-curves?rev=43d95961b77ad18e54d2874403052fdee248076c#43d95961b77ad18e54d2874403052fdee248076c" dependencies = [ - "ecdsa 0.15.1", - "elliptic-curve 0.12.3", + "ecdsa 0.16.1", + "elliptic-curve 0.13.2", "primeorder", "sha2 0.10.6", ] @@ -5790,6 +5780,15 @@ dependencies = [ "libm 0.1.4", ] +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group 0.13.0", +] + [[package]] name = "pallet-assets" version = "4.0.0-dev" @@ -6104,6 +6103,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "pasta_curves" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" +dependencies = [ + "blake2b_simd", + "ff 0.13.0", + "group 0.13.0", + "lazy_static", + "rand 0.8.5", + "static_assertions", + "subtle", +] + [[package]] name = "paste" version = "1.0.12" @@ -6479,11 +6493,10 @@ dependencies = [ [[package]] name = "primeorder" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b54f7131b3dba65a2f414cf5bd25b66d4682e4608610668eae785750ba4c5b2" +version = "0.13.0" +source = "git+https://github.com/RustCrypto/elliptic-curves?rev=43d95961b77ad18e54d2874403052fdee248076c#43d95961b77ad18e54d2874403052fdee248076c" dependencies = [ - "elliptic-curve 0.12.3", + "elliptic-curve 0.13.2", ] [[package]] @@ -6554,9 +6567,9 @@ dependencies = [ "env_logger", "flexible-transcript", "futures", - "group 0.12.1", + "group 0.13.0", "hex", - "k256 0.12.0", + "k256", "lazy_static", "log", "modular-frost", @@ -11899,23 +11912,22 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "57090580b8b26d9fd2288c4ac982b3b3c6446cd8e91112bcf672d5ddb2f17441" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure", + "syn 2.0.10", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 03d87df9..8b21bb09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,3 +60,7 @@ monero-serai = { opt-level = 3 } [profile.release] panic = "unwind" + +[patch.crates-io] +k256 = { git = "https://github.com/RustCrypto/elliptic-curves", rev = "43d95961b77ad18e54d2874403052fdee248076c" } +p256 = { git = "https://github.com/RustCrypto/elliptic-curves", rev = "43d95961b77ad18e54d2874403052fdee248076c" } diff --git a/coins/bitcoin/Cargo.toml b/coins/bitcoin/Cargo.toml index 7f872930..83c0adcd 100644 --- a/coins/bitcoin/Cargo.toml +++ b/coins/bitcoin/Cargo.toml @@ -19,7 +19,7 @@ sha2 = "0.10" secp256k1 = { version = "0.24", features = ["global-context"] } bitcoin = { version = "0.29", features = ["serde"] } -k256 = { version = "0.12", features = ["arithmetic"] } +k256 = { version = "0.13", features = ["arithmetic"] } transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", features = ["recommended"] } frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.7", features = ["secp256k1"] } diff --git a/coins/bitcoin/src/crypto.rs b/coins/bitcoin/src/crypto.rs index ac07652b..827530d2 100644 --- a/coins/bitcoin/src/crypto.rs +++ b/coins/bitcoin/src/crypto.rs @@ -71,7 +71,7 @@ impl HramTrait for Hram { data.update(x(A)); data.update(m); - Scalar::from_uint_reduced(U256::from_be_slice(&data.finalize())) + Scalar::reduce(U256::from_be_slice(&data.finalize())) } } diff --git a/coins/ethereum/Cargo.toml b/coins/ethereum/Cargo.toml index 4c947b1a..eacc602b 100644 --- a/coins/ethereum/Cargo.toml +++ b/coins/ethereum/Cargo.toml @@ -22,8 +22,8 @@ serde = "1" sha2 = "0.10" sha3 = "0.10" -group = "0.12" -k256 = { version = "0.12", features = ["arithmetic", "ecdsa"] } +group = "0.13" +k256 = { version = "0.13", features = ["arithmetic", "ecdsa"] } frost = { package = "modular-frost", path = "../../crypto/frost", features = ["secp256k1", "tests"] } eyre = "0.6" diff --git a/coins/ethereum/src/crypto.rs b/coins/ethereum/src/crypto.rs index b661fdc0..5f43e7dd 100644 --- a/coins/ethereum/src/crypto.rs +++ b/coins/ethereum/src/crypto.rs @@ -2,7 +2,9 @@ use sha3::{Digest, Keccak256}; use group::Group; use k256::{ - elliptic_curve::{bigint::ArrayEncoding, ops::Reduce, sec1::ToEncodedPoint, DecompressPoint}, + elliptic_curve::{ + bigint::ArrayEncoding, ops::Reduce, point::DecompressPoint, sec1::ToEncodedPoint, + }, AffinePoint, ProjectivePoint, Scalar, U256, }; @@ -13,7 +15,7 @@ pub fn keccak256(data: &[u8]) -> [u8; 32] { } pub fn hash_to_scalar(data: &[u8]) -> Scalar { - Scalar::from_uint_reduced(U256::from_be_slice(&keccak256(data))) + Scalar::reduce(U256::from_be_slice(&keccak256(data))) } pub fn address(point: &ProjectivePoint) -> [u8; 20] { @@ -56,7 +58,7 @@ impl Hram for EthereumHram { let mut data = address(R).to_vec(); data.append(&mut a_encoded); data.append(&mut m.to_vec()); - Scalar::from_uint_reduced(U256::from_be_slice(&keccak256(&data))) + Scalar::reduce(U256::from_be_slice(&keccak256(&data))) } } @@ -92,7 +94,7 @@ pub fn process_signature_for_contract( ) -> ProcessedSignature { let encoded_pk = A.to_encoded_point(true); let px = &encoded_pk.as_ref()[1 .. 33]; - let px_scalar = Scalar::from_uint_reduced(U256::from_be_slice(px)); + let px_scalar = Scalar::reduce(U256::from_be_slice(px)); let e = EthereumHram::hram(R, A, &[chain_id.to_be_byte_array().as_slice(), &m].concat()); ProcessedSignature { s, diff --git a/coins/ethereum/tests/crypto.rs b/coins/ethereum/tests/crypto.rs index cc8359ed..e531e4b5 100644 --- a/coins/ethereum/tests/crypto.rs +++ b/coins/ethereum/tests/crypto.rs @@ -19,7 +19,7 @@ fn test_ecrecover() { const MESSAGE: &[u8] = b"Hello, World!"; let (sig, recovery_id) = private .as_nonzero_scalar() - .try_sign_prehashed_rfc6979::(Keccak256::digest(MESSAGE), b"") + .try_sign_prehashed_rfc6979::(&Keccak256::digest(MESSAGE), b"") .unwrap(); #[allow(clippy::unit_cmp)] // Intended to assert this wasn't changed to Result { @@ -68,7 +68,7 @@ fn test_ecrecover_hack() { let group_key = keys[&Participant::new(1).unwrap()].group_key(); let group_key_encoded = group_key.to_encoded_point(true); let group_key_compressed = group_key_encoded.as_ref(); - let group_key_x = Scalar::from_uint_reduced(U256::from_be_slice(&group_key_compressed[1 .. 33])); + let group_key_x = Scalar::reduce(U256::from_be_slice(&group_key_compressed[1 .. 33])); const MESSAGE: &[u8] = b"Hello, World!"; let hashed_message = keccak256(MESSAGE); diff --git a/coins/monero/Cargo.toml b/coins/monero/Cargo.toml index 7a15da68..c95f60da 100644 --- a/coins/monero/Cargo.toml +++ b/coins/monero/Cargo.toml @@ -30,7 +30,7 @@ sha3 = "0.10" curve25519-dalek = { version = "^3.2", features = ["std"] } -group = "0.12" +group = "0.13" dalek-ff-group = { path = "../../crypto/dalek-ff-group", version = "0.3" } multiexp = { path = "../../crypto/multiexp", version = "0.3", features = ["batch"] } diff --git a/coins/monero/generators/Cargo.toml b/coins/monero/generators/Cargo.toml index c1a4a3be..089acffb 100644 --- a/coins/monero/generators/Cargo.toml +++ b/coins/monero/generators/Cargo.toml @@ -20,5 +20,5 @@ sha3 = "0.10" curve25519-dalek = { version = "3", features = ["std"] } -group = "0.12" +group = "0.13" dalek-ff-group = { path = "../../../crypto/dalek-ff-group", version = "0.3" } diff --git a/coins/monero/generators/src/hash_to_point.rs b/coins/monero/generators/src/hash_to_point.rs index 7f2116ba..3f9156b9 100644 --- a/coins/monero/generators/src/hash_to_point.rs +++ b/coins/monero/generators/src/hash_to_point.rs @@ -13,7 +13,7 @@ pub fn hash_to_point(bytes: [u8; 32]) -> EdwardsPoint { let A = FieldElement::from(486662u64); let v = FieldElement::from_square(hash(&bytes)).double(); - let w = v + FieldElement::one(); + let w = v + FieldElement::ONE; let x = w.square() + (-A.square() * v); // This isn't the complete X, yet its initial value diff --git a/coins/monero/src/ringct/bulletproofs/core.rs b/coins/monero/src/ringct/bulletproofs/core.rs index cbe9c3cf..46641ade 100644 --- a/coins/monero/src/ringct/bulletproofs/core.rs +++ b/coins/monero/src/ringct/bulletproofs/core.rs @@ -80,8 +80,8 @@ pub(crate) fn bit_decompose(commitments: &[Commitment]) -> (ScalarVector, Scalar if j < sv.len() { bit = Choice::from((sv[j][i / 8] >> (i % 8)) & 1); } - aL.0[(j * N) + i] = Scalar::conditional_select(&Scalar::zero(), &Scalar::one(), bit); - aR.0[(j * N) + i] = Scalar::conditional_select(&-Scalar::one(), &Scalar::zero(), bit); + aL.0[(j * N) + i] = Scalar::conditional_select(&Scalar::ZERO, &Scalar::ONE, bit); + aR.0[(j * N) + i] = Scalar::conditional_select(&-Scalar::ONE, &Scalar::ZERO, bit); } } @@ -129,7 +129,7 @@ lazy_static! { } pub(crate) fn challenge_products(w: &[Scalar], winv: &[Scalar]) -> Vec { - let mut products = vec![Scalar::zero(); 1 << w.len()]; + let mut products = vec![Scalar::ZERO; 1 << w.len()]; products[0] = winv[0]; products[1] = w[0]; for j in 1 .. w.len() { diff --git a/coins/monero/src/ringct/bulletproofs/original.rs b/coins/monero/src/ringct/bulletproofs/original.rs index 21abb44f..0dda32a8 100644 --- a/coins/monero/src/ringct/bulletproofs/original.rs +++ b/coins/monero/src/ringct/bulletproofs/original.rs @@ -15,7 +15,7 @@ use crate::{Commitment, ringct::bulletproofs::core::*}; include!(concat!(env!("OUT_DIR"), "/generators.rs")); lazy_static! { - static ref ONE_N: ScalarVector = ScalarVector(vec![Scalar::one(); N]); + static ref ONE_N: ScalarVector = ScalarVector(vec![Scalar::ONE; N]); static ref IP12: Scalar = inner_product(&ONE_N, &TWO_N); } @@ -250,7 +250,7 @@ impl OriginalStruct { proof.push((z3, *H)); proof.push((-Scalar(self.mu), G)); - proof.push((Scalar::one(), A)); + proof.push((Scalar::ONE, A)); proof.push((x, S)); { diff --git a/coins/monero/src/ringct/bulletproofs/plus.rs b/coins/monero/src/ringct/bulletproofs/plus.rs index 4a23420c..cd07fa55 100644 --- a/coins/monero/src/ringct/bulletproofs/plus.rs +++ b/coins/monero/src/ringct/bulletproofs/plus.rs @@ -31,7 +31,7 @@ fn hash_plus>(commitments: C) -> (Scalar, Vec // d[j*N+i] = z**(2*(j+1)) * 2**i fn d(z: Scalar, M: usize, MN: usize) -> (ScalarVector, ScalarVector) { let zpow = ScalarVector::even_powers(z, 2 * M); - let mut d = vec![Scalar::zero(); MN]; + let mut d = vec![Scalar::ZERO; MN]; for j in 0 .. M { for i in 0 .. N { d[(j * N) + i] = zpow[j] * TWO_N[i]; @@ -239,7 +239,7 @@ impl PlusStruct { // Invert B, instead of the Scalar, as the latter is only 2x as expensive yet enables reduction // to a single addition under vartime for the first BP verified in the batch, which is expected // to be much more significant - proof.push((Scalar::one(), -B)); + proof.push((Scalar::ONE, -B)); proof.push((-e, A1)); proof.push((minus_esq, A)); proof.push((Scalar(self.d1), G)); diff --git a/coins/monero/src/ringct/bulletproofs/scalar_vector.rs b/coins/monero/src/ringct/bulletproofs/scalar_vector.rs index 39cde8b2..739e6b77 100644 --- a/coins/monero/src/ringct/bulletproofs/scalar_vector.rs +++ b/coins/monero/src/ringct/bulletproofs/scalar_vector.rs @@ -48,14 +48,14 @@ math_op!(Mul, mul, |(a, b): (&Scalar, &Scalar)| *a * *b); impl ScalarVector { pub(crate) fn new(len: usize) -> ScalarVector { - ScalarVector(vec![Scalar::zero(); len]) + ScalarVector(vec![Scalar::ZERO; len]) } pub(crate) fn powers(x: Scalar, len: usize) -> ScalarVector { debug_assert!(len != 0); let mut res = Vec::with_capacity(len); - res.push(Scalar::one()); + res.push(Scalar::ONE); for i in 1 .. len { res.push(res[i - 1] * x); } diff --git a/coins/monero/src/ringct/clsag/multisig.rs b/coins/monero/src/ringct/clsag/multisig.rs index 78e44fe9..bf71a638 100644 --- a/coins/monero/src/ringct/clsag/multisig.rs +++ b/coins/monero/src/ringct/clsag/multisig.rs @@ -304,7 +304,7 @@ impl Algorithm for ClsagMultisig { Ok(vec![ (share, dfg::EdwardsPoint::generator()), (dfg::Scalar(interim.p), verification_share), - (-dfg::Scalar::one(), nonces[0][0]), + (-dfg::Scalar::ONE, nonces[0][0]), ]) } } diff --git a/coins/monero/src/wallet/send/multisig.rs b/coins/monero/src/wallet/send/multisig.rs index 94814cb8..5e3a8c6a 100644 --- a/coins/monero/src/wallet/send/multisig.rs +++ b/coins/monero/src/wallet/send/multisig.rs @@ -148,7 +148,7 @@ impl SignableTransaction { let clsag = ClsagMultisig::new(transcript.clone(), input.key(), inputs[i].clone()); key_images.push(( clsag.H, - keys.current_offset().unwrap_or_else(dfg::Scalar::zero).0 + self.inputs[i].key_offset(), + keys.current_offset().unwrap_or(dfg::Scalar::ZERO).0 + self.inputs[i].key_offset(), )); clsags.push(AlgorithmMachine::new(clsag, offset)); } diff --git a/crypto/ciphersuite/Cargo.toml b/crypto/ciphersuite/Cargo.toml index 8db1312b..4c8d6008 100644 --- a/crypto/ciphersuite/Cargo.toml +++ b/crypto/ciphersuite/Cargo.toml @@ -23,21 +23,21 @@ transcript = { package = "flexible-transcript", path = "../transcript", version sha2 = { version = "0.10", optional = true } sha3 = { version = "0.10", optional = true } -ff = { version = "0.12", features = ["bits"] } -group = "0.12" +ff = { version = "0.13", features = ["bits"] } +group = "0.13" dalek-ff-group = { path = "../dalek-ff-group", version = "0.3", optional = true } -elliptic-curve = { version = "0.12", features = ["hash2curve"], optional = true } -p256 = { version = "0.12", features = ["arithmetic", "bits", "hash2curve"], optional = true } -k256 = { version = "0.12", features = ["arithmetic", "bits", "hash2curve"], optional = true } +elliptic-curve = { version = "0.13", features = ["hash2curve"], optional = true } +p256 = { version = "0.13", features = ["arithmetic", "bits", "hash2curve"], optional = true } +k256 = { version = "0.13", features = ["arithmetic", "bits", "hash2curve"], optional = true } minimal-ed448 = { path = "../ed448", version = "0.3", optional = true } [dev-dependencies] hex = "0.4" -ff-group-tests = { version = "0.12", path = "../ff-group-tests" } +ff-group-tests = { version = "0.13", path = "../ff-group-tests" } [features] std = [] diff --git a/crypto/ciphersuite/src/kp256.rs b/crypto/ciphersuite/src/kp256.rs index 34e37f12..e97a856b 100644 --- a/crypto/ciphersuite/src/kp256.rs +++ b/crypto/ciphersuite/src/kp256.rs @@ -2,11 +2,11 @@ use zeroize::Zeroize; use sha2::Sha256; -use group::ff::{Field, PrimeField}; +use group::ff::PrimeField; use elliptic_curve::{ generic_array::GenericArray, - bigint::{CheckedAdd, Encoding, U384}, + bigint::{NonZero, CheckedAdd, Encoding, U384}, hash2curve::{Expander, ExpandMsg, ExpandMsgXmd}, }; @@ -61,7 +61,7 @@ macro_rules! kp_curve { let mut modulus = [0; L]; // The byte repr of scalars will be 32 big-endian bytes // Set the lower 32 bytes of our 48-byte array accordingly - modulus[16 ..].copy_from_slice(&(Self::F::zero() - Self::F::one()).to_bytes()); + modulus[16 ..].copy_from_slice(&(Self::F::ZERO - Self::F::ONE).to_bytes()); // Use a checked_add + unwrap since this addition cannot fail (being a 32-byte value with // 48-bytes of space) // While a non-panicking saturating_add/wrapping_add could be used, they'd likely be less @@ -71,11 +71,12 @@ macro_rules! kp_curve { // The defined P-256 and secp256k1 ciphersuites both use expand_message_xmd let mut wide = U384::from_be_bytes({ let mut bytes = [0; 48]; - ExpandMsgXmd::::expand_message(&[msg], dst, 48).unwrap().fill_bytes(&mut bytes); + ExpandMsgXmd::::expand_message(&[msg], &[dst], 48) + .unwrap() + .fill_bytes(&mut bytes); bytes }) - .reduce(&modulus) - .unwrap() + .rem(&NonZero::new(modulus).unwrap()) .to_be_bytes(); // Now that this has been reduced back to a 32-byte value, grab the lower 32-bytes diff --git a/crypto/ciphersuite/src/lib.rs b/crypto/ciphersuite/src/lib.rs index 7b504b27..97ed2a7c 100644 --- a/crypto/ciphersuite/src/lib.rs +++ b/crypto/ciphersuite/src/lib.rs @@ -79,7 +79,7 @@ pub trait Ciphersuite: let mut res; while { res = Self::F::random(&mut *rng); - res.ct_eq(&Self::F::zero()).into() + res.ct_eq(&Self::F::ZERO).into() } {} res } diff --git a/crypto/dalek-ff-group/Cargo.toml b/crypto/dalek-ff-group/Cargo.toml index 859397cc..243c7405 100644 --- a/crypto/dalek-ff-group/Cargo.toml +++ b/crypto/dalek-ff-group/Cargo.toml @@ -19,8 +19,8 @@ digest = "0.10" zeroize = { version = "^1.5", features = ["zeroize_derive"] } subtle = "^2.4" -ff = { version = "0.12", features = ["bits"] } -group = "0.12" +ff = "0.13" +group = "0.13" crypto-bigint = "0.5" diff --git a/crypto/dalek-ff-group/src/field.rs b/crypto/dalek-ff-group/src/field.rs index 9b78b895..51a2e3a2 100644 --- a/crypto/dalek-ff-group/src/field.rs +++ b/crypto/dalek-ff-group/src/field.rs @@ -1,4 +1,7 @@ -use core::ops::{DerefMut, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign}; +use core::{ + ops::{DerefMut, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign}, + iter::{Sum, Product}, +}; use zeroize::Zeroize; use rand_core::RngCore; @@ -12,7 +15,7 @@ use crypto_bigint::{Integer, NonZero, Encoding, U256, U512}; use group::ff::{Field, PrimeField, FieldBits, PrimeFieldBits}; -use crate::{u8_from_bool, constant_time, math, from_uint}; +use crate::{u8_from_bool, constant_time, math_op, math, from_wrapper, from_uint}; // 2^255 - 19 // Uses saturating_sub because checked_sub isn't available at compile time @@ -107,18 +110,15 @@ impl<'a> Neg for &'a FieldElement { } impl Field for FieldElement { + const ZERO: Self = Self(U256::ZERO); + const ONE: Self = Self(U256::ONE); + fn random(mut rng: impl RngCore) -> Self { let mut bytes = [0; 64]; rng.fill_bytes(&mut bytes); FieldElement(reduce(U512::from_le_bytes(bytes))) } - fn zero() -> Self { - Self(U256::ZERO) - } - fn one() -> Self { - Self(U256::ONE) - } fn square(&self) -> Self { FieldElement(reduce(self.0.square())) } @@ -138,12 +138,68 @@ impl Field for FieldElement { let candidate = Self::conditional_select(&tv2, &tv1, tv1.square().ct_eq(self)); CtOption::new(candidate, candidate.square().ct_eq(self)) } + + fn sqrt_ratio(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) { + let i = SQRT_M1; + + let u = *u; + let v = *v; + + let v3 = v.square() * v; + let v7 = v3.square() * v; + let mut r = (u * v3) * (u * v7).pow(MOD_5_8); + + let check = v * r.square(); + let correct_sign = check.ct_eq(&u); + let flipped_sign = check.ct_eq(&(-u)); + let flipped_sign_i = check.ct_eq(&((-u) * i)); + + r.conditional_assign(&(r * i), flipped_sign | flipped_sign_i); + + let r_is_negative = r.is_odd(); + r.conditional_negate(r_is_negative); + + (correct_sign | flipped_sign, r) + } } impl PrimeField for FieldElement { type Repr = [u8; 32]; + + // Big endian representation of the modulus + const MODULUS: &'static str = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"; + const NUM_BITS: u32 = 255; const CAPACITY: u32 = 254; + + // 2.invert() + const TWO_INV: Self = FieldElement(U256::from_be_hex( + "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7", + )); + + // This was calculated with the method from the ff crate docs + // SageMath GF(modulus).primitive_element() + const MULTIPLICATIVE_GENERATOR: Self = Self(U256::from_u8(2)); + // This was set per the specification in the ff crate docs + // The number of leading zero bits in the little-endian bit representation of (modulus - 1) + const S: u32 = 2; + + // This was calculated via the formula from the ff crate docs + // Self::MULTIPLICATIVE_GENERATOR ** ((modulus - 1) >> Self::S) + const ROOT_OF_UNITY: Self = FieldElement(U256::from_be_hex( + "2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0", + )); + // Self::ROOT_OF_UNITY.invert() + const ROOT_OF_UNITY_INV: Self = FieldElement(U256::from_be_hex( + "547cdb7fb03e20f4d4b2ff66c2042858d0bce7f952d01b873b11e4d8b5f15f3d", + )); + + // This was calculated via the formula from the ff crate docs + // Self::MULTIPLICATIVE_GENERATOR ** (2 ** Self::S) + const DELTA: Self = FieldElement(U256::from_be_hex( + "0000000000000000000000000000000000000000000000000000000000000010", + )); + fn from_repr(bytes: [u8; 32]) -> CtOption { let res = Self(U256::from_le_bytes(bytes)); CtOption::new(res, res.0.ct_lt(&MODULUS)) @@ -152,23 +208,12 @@ impl PrimeField for FieldElement { self.0.to_le_bytes() } - // This was set per the specification in the ff crate docs - // The number of leading zero bits in the little-endian bit representation of (modulus - 1) - const S: u32 = 2; fn is_odd(&self) -> Choice { self.0.is_odd() } - fn multiplicative_generator() -> Self { - // This was calculated with the method from the ff crate docs - // SageMath GF(modulus).primitive_element() - 2u64.into() - } - fn root_of_unity() -> Self { - // This was calculated via the formula from the ff crate docs - // Self::multiplicative_generator() ** ((modulus - 1) >> Self::S) - FieldElement(U256::from_be_hex( - "2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0", - )) + + fn from_u128(num: u128) -> Self { + Self::from(num) } } @@ -193,13 +238,13 @@ impl FieldElement { /// Perform an exponentation. pub fn pow(&self, other: FieldElement) -> FieldElement { - let mut table = [FieldElement::one(); 16]; + let mut table = [FieldElement::ONE; 16]; table[1] = *self; for i in 2 .. 16 { table[i] = table[i - 1] * self; } - let mut res = FieldElement::one(); + let mut res = FieldElement::ONE; let mut bits = 0; for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() { bits <<= 1; @@ -257,6 +302,38 @@ impl FieldElement { } } +impl Sum for FieldElement { + fn sum>(iter: I) -> FieldElement { + let mut res = FieldElement::ZERO; + for item in iter { + res += item; + } + res + } +} + +impl<'a> Sum<&'a FieldElement> for FieldElement { + fn sum>(iter: I) -> FieldElement { + iter.cloned().sum() + } +} + +impl Product for FieldElement { + fn product>(iter: I) -> FieldElement { + let mut res = FieldElement::ONE; + for item in iter { + res *= item; + } + res + } +} + +impl<'a> Product<&'a FieldElement> for FieldElement { + fn product>(iter: I) -> FieldElement { + iter.cloned().product() + } +} + #[test] fn test_wide_modulus() { let mut wide = [0; 64]; @@ -275,9 +352,8 @@ fn test_sqrt_m1() { // 2 ** ((MODULUS - 1) // 4) % MODULUS assert_eq!( SQRT_M1, - FieldElement::from(2u8).pow(FieldElement( - (FieldElement::zero() - FieldElement::one()).0.wrapping_div(&U256::from(4u8)) - )) + FieldElement::from(2u8) + .pow(FieldElement((FieldElement::ZERO - FieldElement::ONE).0.wrapping_div(&U256::from(4u8)))) ); } diff --git a/crypto/dalek-ff-group/src/lib.rs b/crypto/dalek-ff-group/src/lib.rs index 8bc71213..2c1faf8d 100644 --- a/crypto/dalek-ff-group/src/lib.rs +++ b/crypto/dalek-ff-group/src/lib.rs @@ -1,11 +1,11 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![no_std] +#![no_std] // Prevents writing new code, in what should be a simple wrapper, which requires std #![doc = include_str!("../README.md")] use core::{ borrow::Borrow, ops::{Deref, DerefMut, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign}, - iter::{Iterator, Sum}, + iter::{Iterator, Sum, Product}, hash::{Hash, Hasher}, }; @@ -30,7 +30,7 @@ use dalek::{ pub use constants::{ED25519_BASEPOINT_TABLE, RISTRETTO_BASEPOINT_TABLE}; use group::{ - ff::{Field, PrimeField, FieldBits, PrimeFieldBits}, + ff::{Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic}, Group, GroupEncoding, prime::PrimeGroup, }; @@ -90,8 +90,6 @@ macro_rules! deref_borrow { }; } -#[doc(hidden)] -#[macro_export] macro_rules! constant_time { ($Value: ident, $Inner: ident) => { impl ConstantTimeEq for $Value { @@ -107,9 +105,8 @@ macro_rules! constant_time { } }; } +pub(crate) use constant_time; -#[doc(hidden)] -#[macro_export] macro_rules! math_op { ( $Value: ident, @@ -144,9 +141,8 @@ macro_rules! math_op { } }; } +pub(crate) use math_op; -#[doc(hidden)] -#[macro_export(local_inner_macros)] macro_rules! math { ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => { math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add); @@ -154,9 +150,8 @@ macro_rules! math { math_op!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul); }; } +pub(crate) use math; -#[doc(hidden)] -#[macro_export(local_inner_macros)] macro_rules! math_neg { ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => { math!($Value, $Factor, $add, $sub, $mul); @@ -170,8 +165,6 @@ macro_rules! math_neg { }; } -#[doc(hidden)] -#[macro_export] macro_rules! from_wrapper { ($wrapper: ident, $inner: ident, $uint: ident) => { impl From<$uint> for $wrapper { @@ -181,17 +174,18 @@ macro_rules! from_wrapper { } }; } +pub(crate) use from_wrapper; -#[doc(hidden)] -#[macro_export(local_inner_macros)] macro_rules! from_uint { ($wrapper: ident, $inner: ident) => { from_wrapper!($wrapper, $inner, u8); from_wrapper!($wrapper, $inner, u16); from_wrapper!($wrapper, $inner, u32); from_wrapper!($wrapper, $inner, u64); + from_wrapper!($wrapper, $inner, u128); }; } +pub(crate) use from_uint; /// Wrapper around the dalek Scalar type. #[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)] @@ -207,13 +201,13 @@ const MODULUS: U256 = impl Scalar { pub fn pow(&self, other: Scalar) -> Scalar { - let mut table = [Scalar::one(); 16]; + let mut table = [Scalar::ONE; 16]; table[1] = *self; for i in 2 .. 16 { table[i] = table[i - 1] * self; } - let mut res = Scalar::one(); + let mut res = Scalar::ONE; let mut bits = 0; for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() { bits <<= 1; @@ -250,18 +244,19 @@ impl Scalar { } impl Field for Scalar { + const ZERO: Scalar = Scalar(DScalar::from_bits([0; 32])); + const ONE: Scalar = Scalar(DScalar::from_bits({ + let mut bytes = [0; 32]; + bytes[0] = 1; + bytes + })); + fn random(mut rng: impl RngCore) -> Self { let mut r = [0; 64]; rng.fill_bytes(&mut r); Self(DScalar::from_bytes_mod_order_wide(&r)) } - fn zero() -> Self { - Self(DScalar::zero()) - } - fn one() -> Self { - Self(DScalar::one()) - } fn square(&self) -> Self { *self * self } @@ -271,36 +266,77 @@ impl Field for Scalar { fn invert(&self) -> CtOption { CtOption::new(Self(self.0.invert()), !self.is_zero()) } + fn sqrt(&self) -> CtOption { let mod_3_8 = MODULUS.saturating_add(&U256::from_u8(3)).wrapping_div(&U256::from_u8(8)); let mod_3_8 = Scalar::from_repr(mod_3_8.to_le_bytes()).unwrap(); let sqrt_m1 = MODULUS.saturating_sub(&U256::from_u8(1)).wrapping_div(&U256::from_u8(4)); - let sqrt_m1 = Scalar::one().double().pow(Scalar::from_repr(sqrt_m1.to_le_bytes()).unwrap()); + let sqrt_m1 = Scalar::from(2u8).pow(Scalar::from_repr(sqrt_m1.to_le_bytes()).unwrap()); let tv1 = self.pow(mod_3_8); let tv2 = tv1 * sqrt_m1; let candidate = Self::conditional_select(&tv2, &tv1, tv1.square().ct_eq(self)); CtOption::new(candidate, candidate.square().ct_eq(self)) } + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + sqrt_ratio_generic(num, div) + } } impl PrimeField for Scalar { type Repr = [u8; 32]; + + const MODULUS: &'static str = "1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed"; + const NUM_BITS: u32 = 253; const CAPACITY: u32 = 252; + + // 2.invert() + const TWO_INV: Scalar = Scalar(DScalar::from_bits([ + 247, 233, 122, 46, 141, 49, 9, 44, 107, 206, 123, 81, 239, 124, 111, 10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, + ])); + + // This was calculated with the method from the ff crate docs + // SageMath GF(modulus).primitive_element() + const MULTIPLICATIVE_GENERATOR: Scalar = Scalar(DScalar::from_bits({ + let mut bytes = [0; 32]; + bytes[0] = 2; + bytes + })); + // This was set per the specification in the ff crate docs + // The number of leading zero bits in the little-endian bit representation of (modulus - 1) + const S: u32 = 2; + + // This was calculated via the formula from the ff crate docs + // Self::MULTIPLICATIVE_GENERATOR ** ((modulus - 1) >> Self::S) + const ROOT_OF_UNITY: Scalar = Scalar(DScalar::from_bits([ + 212, 7, 190, 235, 223, 117, 135, 190, 254, 131, 206, 66, 83, 86, 240, 14, 122, 194, 193, 171, + 96, 109, 61, 125, 231, 129, 121, 224, 16, 115, 74, 9, + ])); + // Self::ROOT_OF_UNITY.invert() + const ROOT_OF_UNITY_INV: Scalar = Scalar(DScalar::from_bits([ + 25, 204, 55, 113, 58, 237, 138, 153, 215, 24, 41, 96, 139, 163, 238, 5, 134, 61, 62, 84, 159, + 146, 194, 130, 24, 126, 134, 31, 239, 140, 181, 6, + ])); + + // This was calculated via the formula from the ff crate docs + // Self::MULTIPLICATIVE_GENERATOR ** (2 ** Self::S) + const DELTA: Scalar = Scalar(DScalar::from_bits([ + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ])); + fn from_repr(bytes: [u8; 32]) -> CtOption { let scalar = DScalar::from_canonical_bytes(bytes); - // TODO: This unwrap_or isn't constant time, yet we don't exactly have an alternative... - CtOption::new(Scalar(scalar.unwrap_or_else(DScalar::zero)), choice(scalar.is_some())) + // TODO: This unwrap_or_else isn't constant time, yet we don't exactly have an alternative... + CtOption::new(Scalar(scalar.unwrap_or_else(DScalar::zero)), choice(black_box(scalar).is_some())) } fn to_repr(&self) -> [u8; 32] { self.0.to_bytes() } - // This was set per the specification in the ff crate docs - // The number of leading zero bits in the little-endian bit representation of (modulus - 1) - const S: u32 = 2; fn is_odd(&self) -> Choice { // This is probably overkill? Yet it's better safe than sorry since this is a complete // decomposition of the scalar @@ -315,19 +351,9 @@ impl PrimeField for Scalar { } res } - fn multiplicative_generator() -> Self { - // This was calculated with the method from the ff crate docs - // SageMath GF(modulus).primitive_element() - 2u64.into() - } - fn root_of_unity() -> Self { - // This was calculated via the formula from the ff crate docs - // Self::multiplicative_generator() ** ((modulus - 1) >> Self::S) - Scalar::from_repr([ - 212, 7, 190, 235, 223, 117, 135, 190, 254, 131, 206, 66, 83, 86, 240, 14, 122, 194, 193, 171, - 96, 109, 61, 125, 231, 129, 121, 224, 16, 115, 74, 9, - ]) - .unwrap() + + fn from_u128(num: u128) -> Self { + Self::from(num) } } @@ -339,7 +365,7 @@ impl PrimeFieldBits for Scalar { } fn char_le_bits() -> FieldBits { - let mut bytes = (Scalar::zero() - Scalar::one()).to_repr(); + let mut bytes = (Scalar::ZERO - Scalar::ONE).to_repr(); bytes[0] += 1; debug_assert_eq!(DScalar::from_bytes_mod_order(bytes), DScalar::zero()); bytes.into() @@ -352,6 +378,24 @@ impl Sum for Scalar { } } +impl<'a> Sum<&'a Scalar> for Scalar { + fn sum>(iter: I) -> Scalar { + Self(DScalar::sum(iter)) + } +} + +impl Product for Scalar { + fn product>(iter: I) -> Scalar { + Self(DScalar::product(iter)) + } +} + +impl<'a> Product<&'a Scalar> for Scalar { + fn product>(iter: I) -> Scalar { + Self(DScalar::product(iter)) + } +} + macro_rules! dalek_group { ( $Point: ident, @@ -420,7 +464,10 @@ macro_rules! dalek_group { let decompressed = $DCompressed(*bytes).decompress(); // TODO: Same note on unwrap_or as above let point = decompressed.unwrap_or($DPoint::identity()); - CtOption::new($Point(point), choice(decompressed.is_some()) & choice($torsion_free(point))) + CtOption::new( + $Point(point), + choice(black_box(decompressed).is_some()) & choice($torsion_free(point)), + ) } fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { diff --git a/crypto/dkg/src/encryption.rs b/crypto/dkg/src/encryption.rs index 55fd041d..99b039b8 100644 --- a/crypto/dkg/src/encryption.rs +++ b/crypto/dkg/src/encryption.rs @@ -190,7 +190,7 @@ impl EncryptedMessage { #[cfg(test)] pub(crate) fn invalidate_pop(&mut self) { - self.pop.s += C::F::one(); + self.pop.s += C::F::ONE; } #[cfg(test)] @@ -250,7 +250,7 @@ impl EncryptedMessage { use ciphersuite::group::ff::PrimeField; // Assumes the share isn't randomly 1 - let repr = C::F::one().to_repr(); + let repr = C::F::ONE.to_repr(); self.msg.as_mut().as_mut().copy_from_slice(repr.as_ref()); *self = encrypt(rng, context, from, to, self.msg.clone()); } diff --git a/crypto/dkg/src/frost.rs b/crypto/dkg/src/frost.rs index 95c2f718..868a7109 100644 --- a/crypto/dkg/src/frost.rs +++ b/crypto/dkg/src/frost.rs @@ -156,8 +156,8 @@ fn polynomial( ) -> Zeroizing { let l = F::from(u64::from(u16::from(l))); // This should never be reached since Participant is explicitly non-zero - assert!(l != F::zero(), "zero participant passed to polynomial"); - let mut share = Zeroizing::new(F::zero()); + assert!(l != F::ZERO, "zero participant passed to polynomial"); + let mut share = Zeroizing::new(F::ZERO); for (idx, coefficient) in coefficients.iter().rev().enumerate() { *share += coefficient.deref(); if idx != (coefficients.len() - 1) { @@ -366,7 +366,7 @@ impl Zeroize for KeyMachine { fn exponential(i: Participant, values: &[C::G]) -> Vec<(C::F, C::G)> { let i = C::F::from(u16::from(i).into()); let mut res = Vec::with_capacity(values.len()); - (0 .. values.len()).fold(C::F::one(), |exp, l| { + (0 .. values.len()).fold(C::F::ONE, |exp, l| { res.push((exp, values[l])); exp * i }); @@ -389,7 +389,7 @@ fn share_verification_statements( // converts whatever we give to an iterator and then builds a Vec internally, welcoming copies let neg_share_pub = C::generator() * -*share; share.zeroize(); - values.push((C::F::one(), neg_share_pub)); + values.push((C::F::ONE, neg_share_pub)); values } diff --git a/crypto/dkg/src/lib.rs b/crypto/dkg/src/lib.rs index 27fb5670..266628f0 100644 --- a/crypto/dkg/src/lib.rs +++ b/crypto/dkg/src/lib.rs @@ -176,8 +176,8 @@ impl ThresholdParams { pub fn lagrange(i: Participant, included: &[Participant]) -> F { let i_f = F::from(u64::from(u16::from(i))); - let mut num = F::one(); - let mut denom = F::one(); + let mut num = F::ONE; + let mut denom = F::ONE; for l in included { if i == *l { continue; @@ -405,7 +405,7 @@ impl ThresholdKeys { // Carry any existing offset // Enables schemes like Monero's subaddresses which have a per-subaddress offset and then a // one-time-key offset - res.offset = Some(offset + res.offset.unwrap_or_else(C::F::zero)); + res.offset = Some(offset + res.offset.unwrap_or(C::F::ZERO)); res } @@ -426,7 +426,7 @@ impl ThresholdKeys { /// Return the group key, with any offset applied. pub fn group_key(&self) -> C::G { - self.core.group_key + (C::generator() * self.offset.unwrap_or_else(C::F::zero)) + self.core.group_key + (C::generator() * self.offset.unwrap_or(C::F::ZERO)) } /// Return all participants' verification shares without any offsetting. @@ -457,7 +457,7 @@ impl ThresholdKeys { } // The offset is included by adding it to the participant with the lowest ID - let offset = self.offset.unwrap_or_else(C::F::zero); + let offset = self.offset.unwrap_or(C::F::ZERO); if included[0] == self.params().i() { *secret_share += offset; } diff --git a/crypto/dkg/src/tests/mod.rs b/crypto/dkg/src/tests/mod.rs index 250d7a59..4acb6a5b 100644 --- a/crypto/dkg/src/tests/mod.rs +++ b/crypto/dkg/src/tests/mod.rs @@ -36,7 +36,7 @@ pub fn recover_key(keys: &HashMap> assert!(keys.len() >= first.params().t().into(), "not enough keys provided"); let included = keys.keys().cloned().collect::>(); - let group_private = keys.iter().fold(C::F::zero(), |accum, (i, keys)| { + let group_private = keys.iter().fold(C::F::ZERO, |accum, (i, keys)| { accum + (lagrange::(*i, &included) * keys.secret_share().deref()) }); assert_eq!(C::generator() * group_private, first.group_key(), "failed to recover keys"); diff --git a/crypto/dleq/Cargo.toml b/crypto/dleq/Cargo.toml index c582f502..1a34f525 100644 --- a/crypto/dleq/Cargo.toml +++ b/crypto/dleq/Cargo.toml @@ -21,8 +21,8 @@ digest = "0.10" transcript = { package = "flexible-transcript", path = "../transcript", version = "0.3" } -ff = "0.12" -group = "0.12" +ff = "0.13" +group = "0.13" multiexp = { path = "../multiexp", version = "0.3", features = ["batch"], optional = true } @@ -31,7 +31,7 @@ hex-literal = "0.3" blake2 = "0.10" -k256 = { version = "0.12", features = ["arithmetic", "bits"] } +k256 = { version = "0.13", features = ["arithmetic", "bits"] } dalek-ff-group = { path = "../dalek-ff-group" } transcript = { package = "flexible-transcript", path = "../transcript", features = ["recommended"] } diff --git a/crypto/dleq/src/cross_group/aos.rs b/crypto/dleq/src/cross_group/aos.rs index e59e8a2b..4cba3c89 100644 --- a/crypto/dleq/src/cross_group/aos.rs +++ b/crypto/dleq/src/cross_group/aos.rs @@ -42,7 +42,7 @@ impl Re { } pub(crate) fn e_default() -> Re { - Re::e(G0::Scalar::zero()) + Re::e(G0::Scalar::ZERO) } } @@ -114,7 +114,7 @@ where debug_assert!((RING_LEN == 2) || (RING_LEN == 4)); debug_assert_eq!(RING_LEN, ring.len()); - let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); RING_LEN]; + let mut s = [(G0::Scalar::ZERO, G1::Scalar::ZERO); RING_LEN]; let mut r = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng)); #[allow(non_snake_case)] @@ -178,8 +178,8 @@ where let mut statements = Self::R_batch(generators, *self.s.last().unwrap(), *ring.last().unwrap(), e); - statements.0.push((G0::Scalar::one(), R0_0)); - statements.1.push((G1::Scalar::one(), R1_0)); + statements.0.push((G0::Scalar::ONE, R0_0)); + statements.1.push((G1::Scalar::ONE, R1_0)); batch.0.queue(&mut *rng, (), statements.0); batch.1.queue(&mut *rng, (), statements.1); } @@ -239,7 +239,7 @@ where Re::e(ref mut e) => *e = read_scalar(r)?, } - let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); RING_LEN]; + let mut s = [(G0::Scalar::ZERO, G1::Scalar::ZERO); RING_LEN]; for s in s.iter_mut() { *s = (read_scalar(r)?, read_scalar(r)?); } diff --git a/crypto/dleq/src/cross_group/mod.rs b/crypto/dleq/src/cross_group/mod.rs index c9bd113a..4ee867d7 100644 --- a/crypto/dleq/src/cross_group/mod.rs +++ b/crypto/dleq/src/cross_group/mod.rs @@ -269,15 +269,15 @@ where SchnorrPoK::::prove(rng, transcript, generators.1.primary, &f.1), ); - let mut blinding_key_total = (G0::Scalar::zero(), G1::Scalar::zero()); + let mut blinding_key_total = (G0::Scalar::ZERO, G1::Scalar::ZERO); let mut blinding_key = |rng: &mut R, last| { let blinding_key = ( Self::blinding_key(&mut *rng, &mut blinding_key_total.0, last), Self::blinding_key(&mut *rng, &mut blinding_key_total.1, last), ); if last { - debug_assert_eq!(blinding_key_total.0, G0::Scalar::zero()); - debug_assert_eq!(blinding_key_total.1, G1::Scalar::zero()); + debug_assert_eq!(blinding_key_total.0, G0::Scalar::ZERO); + debug_assert_eq!(blinding_key_total.1, G1::Scalar::ZERO); } blinding_key }; diff --git a/crypto/dleq/src/cross_group/scalar.rs b/crypto/dleq/src/cross_group/scalar.rs index 14ef71c9..1b8eb4e5 100644 --- a/crypto/dleq/src/cross_group/scalar.rs +++ b/crypto/dleq/src/cross_group/scalar.rs @@ -18,8 +18,8 @@ pub fn scalar_normalize( #[cfg(feature = "secure_capacity_difference")] assert!((F0::CAPACITY.max(F1::CAPACITY) - mutual_capacity) <= 4); - let mut res1 = F0::zero(); - let mut res2 = F1::zero(); + let mut res1 = F0::ZERO; + let mut res2 = F1::ZERO; // Uses the bits API to ensure a consistent endianess let mut bits = scalar.to_le_bits(); scalar.zeroize(); @@ -66,7 +66,7 @@ pub fn mutual_scalar_from_bytes= capacity); - let mut accum = F0::zero(); + let mut accum = F0::ZERO; for b in 0 .. capacity { accum = accum.double(); accum += F0::from(((bytes[b / 8] >> (b % 8)) & 1).into()); diff --git a/crypto/dleq/src/cross_group/schnorr.rs b/crypto/dleq/src/cross_group/schnorr.rs index b1d61d5f..ec560388 100644 --- a/crypto/dleq/src/cross_group/schnorr.rs +++ b/crypto/dleq/src/cross_group/schnorr.rs @@ -72,7 +72,7 @@ where (), [ (-self.s, generator), - (G::Scalar::one(), self.R), + (G::Scalar::ONE, self.R), (Self::hra(transcript, generator, self.R, public_key), public_key), ], ); diff --git a/crypto/dleq/src/lib.rs b/crypto/dleq/src/lib.rs index b99a1de7..40f578b9 100644 --- a/crypto/dleq/src/lib.rs +++ b/crypto/dleq/src/lib.rs @@ -32,7 +32,7 @@ pub(crate) fn challenge(transcript: &mut T) -> F { // and loading it in // 3: Iterating over each byte and manually doubling/adding. This is simplest - let mut challenge = F::zero(); + let mut challenge = F::ZERO; // Get a wide amount of bytes to safely reduce without bias // In most cases, <=1.5x bytes is enough. 2x is still standard and there's some theoretical @@ -105,13 +105,19 @@ pub enum DLEqError { /// A proof that points have the same discrete logarithm across generators. #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] -pub struct DLEqProof { +pub struct DLEqProof +where + G::Scalar: Zeroize, +{ c: G::Scalar, s: G::Scalar, } #[allow(non_snake_case)] -impl DLEqProof { +impl DLEqProof +where + G::Scalar: Zeroize, +{ fn transcript(transcript: &mut T, generator: G, nonce: G, point: G) { transcript.append_message(b"generator", generator.to_bytes()); transcript.append_message(b"nonce", nonce.to_bytes()); @@ -125,10 +131,7 @@ impl DLEqProof { transcript: &mut T, generators: &[G], scalar: &Zeroizing, - ) -> DLEqProof - where - G::Scalar: Zeroize, - { + ) -> DLEqProof { let r = Zeroizing::new(G::Scalar::random(rng)); transcript.domain_separate(b"dleq"); @@ -210,14 +213,20 @@ impl DLEqProof { /// across some generators, yet with a smaller overall proof size. #[cfg(feature = "std")] #[derive(Clone, PartialEq, Eq, Debug, Zeroize)] -pub struct MultiDLEqProof { +pub struct MultiDLEqProof +where + G::Scalar: Zeroize, +{ c: G::Scalar, s: Vec, } #[cfg(feature = "std")] #[allow(non_snake_case)] -impl MultiDLEqProof { +impl MultiDLEqProof +where + G::Scalar: Zeroize, +{ /// Prove for each scalar that the series of points created by multiplying it against its /// matching generators share a discrete logarithm. /// This function panics if `generators.len() != scalars.len()`. @@ -226,10 +235,7 @@ impl MultiDLEqProof { transcript: &mut T, generators: &[Vec], scalars: &[Zeroizing], - ) -> MultiDLEqProof - where - G::Scalar: Zeroize, - { + ) -> MultiDLEqProof { assert_eq!( generators.len(), scalars.len(), diff --git a/crypto/dleq/src/tests/cross_group/aos.rs b/crypto/dleq/src/tests/cross_group/aos.rs index 05003d26..69139a93 100644 --- a/crypto/dleq/src/tests/cross_group/aos.rs +++ b/crypto/dleq/src/tests/cross_group/aos.rs @@ -21,7 +21,7 @@ fn test_aos_serialization(proof: Aos, R fn test_aos(default: Re) { let generators = generators(); - let mut ring_keys = [(::Scalar::zero(), ::Scalar::zero()); RING_LEN]; + let mut ring_keys = [(::Scalar::ZERO, ::Scalar::ZERO); RING_LEN]; // Side-effect of G0 being a type-alias with identity() deprecated #[allow(deprecated)] let mut ring = [(G0::identity(), G1::identity()); RING_LEN]; diff --git a/crypto/dleq/src/tests/cross_group/mod.rs b/crypto/dleq/src/tests/cross_group/mod.rs index 373cf1bf..7586b964 100644 --- a/crypto/dleq/src/tests/cross_group/mod.rs +++ b/crypto/dleq/src/tests/cross_group/mod.rs @@ -154,7 +154,7 @@ test_dleq!( #[test] fn test_rejection_sampling() { - let mut pow_2 = Scalar::one(); + let mut pow_2 = Scalar::ONE; for _ in 0 .. dfg::Scalar::CAPACITY { pow_2 = pow_2.double(); } @@ -179,7 +179,7 @@ fn test_remainder() { // This will ignore any unused bits, ensuring every remaining one is set let keys = mutual_scalar_from_bytes::(&[0xFF; 32]); let keys = (Zeroizing::new(keys.0), Zeroizing::new(keys.1)); - assert_eq!(Scalar::one() + keys.0.deref(), Scalar::from(2u64).pow_vartime([255])); + assert_eq!(Scalar::ONE + keys.0.deref(), Scalar::from(2u64).pow_vartime([255])); assert_eq!(keys.0, keys.1); let (proof, res) = ConciseLinearDLEq::prove_without_bias( diff --git a/crypto/dleq/src/tests/cross_group/scalar.rs b/crypto/dleq/src/tests/cross_group/scalar.rs index a44ffb53..07d9d457 100644 --- a/crypto/dleq/src/tests/cross_group/scalar.rs +++ b/crypto/dleq/src/tests/cross_group/scalar.rs @@ -10,13 +10,13 @@ use crate::cross_group::scalar::{scalar_normalize, scalar_convert}; #[test] fn test_scalar() { assert_eq!( - scalar_normalize::<_, DalekScalar>(K256Scalar::zero()), - (K256Scalar::zero(), DalekScalar::zero()) + scalar_normalize::<_, DalekScalar>(K256Scalar::ZERO), + (K256Scalar::ZERO, DalekScalar::ZERO) ); assert_eq!( - scalar_normalize::<_, DalekScalar>(K256Scalar::one()), - (K256Scalar::one(), DalekScalar::one()) + scalar_normalize::<_, DalekScalar>(K256Scalar::ONE), + (K256Scalar::ONE, DalekScalar::ONE) ); let mut initial; diff --git a/crypto/ed448/Cargo.toml b/crypto/ed448/Cargo.toml index 9ba2686d..f0715cdf 100644 --- a/crypto/ed448/Cargo.toml +++ b/crypto/ed448/Cargo.toml @@ -20,14 +20,12 @@ rand_core = "0.6" zeroize = { version = "^1.5", features = ["zeroize_derive"] } subtle = "^2.4" -ff = { version = "0.12", features = ["bits"] } -group = "0.12" +ff = "0.13" +group = "0.13" generic-array = "0.14" crypto-bigint = { version = "0.5", features = ["zeroize"] } -dalek-ff-group = { path = "../dalek-ff-group", version = "0.3" } - [dev-dependencies] hex = "0.4" diff --git a/crypto/ed448/README.md b/crypto/ed448/README.md index 1d9d248a..ed2189a9 100644 --- a/crypto/ed448/README.md +++ b/crypto/ed448/README.md @@ -3,8 +3,7 @@ Inefficient, barebones implementation of Ed448 bound to the ff/group API, rejecting torsion to achieve a PrimeGroup definition. This likely should not be used and was only done so another library under Serai could confirm its -completion. It is minimally tested, yet should be correct for what it has. The -functions it doesn't have are marked `unimplemented!()`. This has not undergone -auditing. +completion. It is minimally tested, yet should be correct for what it has. This +has not undergone auditing. constant time and no_std. diff --git a/crypto/ed448/src/backend.rs b/crypto/ed448/src/backend.rs index 03e4993e..a4811cb0 100644 --- a/crypto/ed448/src/backend.rs +++ b/crypto/ed448/src/backend.rs @@ -23,11 +23,70 @@ pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 { res } -#[doc(hidden)] -#[macro_export] +macro_rules! math_op { + ( + $Value: ident, + $Other: ident, + $Op: ident, + $op_fn: ident, + $Assign: ident, + $assign_fn: ident, + $function: expr + ) => { + impl $Op<$Other> for $Value { + type Output = $Value; + fn $op_fn(self, other: $Other) -> Self::Output { + Self($function(self.0, other.0)) + } + } + impl $Assign<$Other> for $Value { + fn $assign_fn(&mut self, other: $Other) { + self.0 = $function(self.0, other.0); + } + } + impl<'a> $Op<&'a $Other> for $Value { + type Output = $Value; + fn $op_fn(self, other: &'a $Other) -> Self::Output { + Self($function(self.0, other.0)) + } + } + impl<'a> $Assign<&'a $Other> for $Value { + fn $assign_fn(&mut self, other: &'a $Other) { + self.0 = $function(self.0, other.0); + } + } + }; +} + +macro_rules! from_wrapper { + ($wrapper: ident, $inner: ident, $uint: ident) => { + impl From<$uint> for $wrapper { + fn from(a: $uint) -> $wrapper { + Self($inner::from(a)) + } + } + }; +} + macro_rules! field { - ($FieldName: ident, $MODULUS: ident, $WIDE_MODULUS: ident, $NUM_BITS: literal) => { - use core::ops::{DerefMut, Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign}; + ( + $FieldName: ident, + + $MODULUS_STR: ident, + $MODULUS: ident, + $WIDE_MODULUS: ident, + + $NUM_BITS: literal, + + $TWO_INV: expr, + $MULTIPLICATIVE_GENERATOR: literal, + $ROOT_OF_UNITY_INV: expr, + $DELTA: expr, + ) => { + use core::{ + ops::{DerefMut, Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign}, + iter::{Sum, Product}, + }; use subtle::{Choice, CtOption, ConstantTimeEq, ConstantTimeLess, ConditionallySelectable}; use rand_core::RngCore; @@ -35,12 +94,7 @@ macro_rules! field { use generic_array::{typenum::U57, GenericArray}; use crypto_bigint::{Integer, NonZero, Encoding}; - use group::ff::{Field, PrimeField, FieldBits, PrimeFieldBits}; - - // Needed to publish for some reason? Yet not actually needed - #[allow(unused_imports)] - use dalek_ff_group::{from_wrapper, math_op}; - use dalek_ff_group::{constant_time, from_uint, math}; + use ff::{Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic}; use $crate::backend::u8_from_bool; @@ -48,15 +102,37 @@ macro_rules! field { U512::from_le_slice(&x.rem(&NonZero::new($WIDE_MODULUS).unwrap()).to_le_bytes()[.. 64]) } - constant_time!($FieldName, U512); - math!( - $FieldName, - $FieldName, - |x, y| U512::add_mod(&x, &y, &$MODULUS.0), - |x, y| U512::sub_mod(&x, &y, &$MODULUS.0), - |x, y| reduce(U1024::from(U512::mul_wide(&x, &y))) - ); - from_uint!($FieldName, U512); + impl ConstantTimeEq for $FieldName { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } + } + + impl ConditionallySelectable for $FieldName { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + $FieldName(U512::conditional_select(&a.0, &b.0, choice)) + } + } + + math_op!($FieldName, $FieldName, Add, add, AddAssign, add_assign, |x, y| U512::add_mod( + &x, + &y, + &$MODULUS.0 + )); + math_op!($FieldName, $FieldName, Sub, sub, SubAssign, sub_assign, |x, y| U512::sub_mod( + &x, + &y, + &$MODULUS.0 + )); + math_op!($FieldName, $FieldName, Mul, mul, MulAssign, mul_assign, |x, y| reduce(U1024::from( + U512::mul_wide(&x, &y) + ))); + + from_wrapper!($FieldName, U512, u8); + from_wrapper!($FieldName, U512, u16); + from_wrapper!($FieldName, U512, u32); + from_wrapper!($FieldName, U512, u64); + from_wrapper!($FieldName, U512, u128); impl Neg for $FieldName { type Output = $FieldName; @@ -104,18 +180,15 @@ macro_rules! field { } impl Field for $FieldName { + const ZERO: Self = Self(U512::ZERO); + const ONE: Self = Self(U512::ONE); + fn random(mut rng: impl RngCore) -> Self { let mut bytes = [0; 128]; rng.fill_bytes(&mut bytes); $FieldName(reduce(U1024::from_le_slice(bytes.as_ref()))) } - fn zero() -> Self { - Self(U512::ZERO) - } - fn one() -> Self { - Self(U512::ONE) - } fn square(&self) -> Self { *self * self } @@ -134,12 +207,32 @@ macro_rules! field { let res = self.pow(MOD_1_4); CtOption::new(res, res.square().ct_eq(self)) } + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + sqrt_ratio_generic(num, div) + } } impl PrimeField for $FieldName { type Repr = GenericArray; + + const MODULUS: &'static str = $MODULUS_STR; + const NUM_BITS: u32 = $NUM_BITS; const CAPACITY: u32 = $NUM_BITS - 1; + + const TWO_INV: Self = $FieldName(U512::from_le_hex($TWO_INV)); + + const MULTIPLICATIVE_GENERATOR: Self = Self(U512::from_u8($MULTIPLICATIVE_GENERATOR)); + // True for both the Ed448 Scalar field and FieldElement field + const S: u32 = 1; + + // Both fields have their root of unity as -1 + const ROOT_OF_UNITY: Self = Self($MODULUS.0.saturating_sub(&U512::from_u8(1))); + const ROOT_OF_UNITY_INV: Self = $FieldName(U512::from_le_hex($ROOT_OF_UNITY_INV)); + + const DELTA: Self = $FieldName(U512::from_le_hex($DELTA)); + fn from_repr(bytes: Self::Repr) -> CtOption { let res = $FieldName(U512::from_le_slice(&[bytes.as_ref(), [0; 7].as_ref()].concat())); CtOption::new(res, res.0.ct_lt(&$MODULUS.0)) @@ -150,17 +243,9 @@ macro_rules! field { repr } - // True for both the Ed448 Scalar field and FieldElement field - const S: u32 = 1; fn is_odd(&self) -> Choice { self.0.is_odd() } - fn multiplicative_generator() -> Self { - unimplemented!() - } - fn root_of_unity() -> Self { - unimplemented!() - } } impl PrimeFieldBits for $FieldName { @@ -176,5 +261,37 @@ macro_rules! field { MODULUS.to_le_bits() } } + + impl Sum<$FieldName> for $FieldName { + fn sum>(iter: I) -> $FieldName { + let mut res = $FieldName::ZERO; + for item in iter { + res += item; + } + res + } + } + + impl<'a> Sum<&'a $FieldName> for $FieldName { + fn sum>(iter: I) -> $FieldName { + iter.cloned().sum() + } + } + + impl Product<$FieldName> for $FieldName { + fn product>(iter: I) -> $FieldName { + let mut res = $FieldName::ONE; + for item in iter { + res *= item; + } + res + } + } + + impl<'a> Product<&'a $FieldName> for $FieldName { + fn product>(iter: I) -> $FieldName { + iter.cloned().product() + } + } }; } diff --git a/crypto/ed448/src/field.rs b/crypto/ed448/src/field.rs index 4ec62f75..5766ee8b 100644 --- a/crypto/ed448/src/field.rs +++ b/crypto/ed448/src/field.rs @@ -2,12 +2,15 @@ use zeroize::Zeroize; use crypto_bigint::{U512, U1024}; -use crate::field; - /// Ed448 field element. #[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)] pub struct FieldElement(pub(crate) U512); +const MODULUS_STR: &str = concat!( + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", +); + // 2**448 - 2**224 - 1 pub(crate) const MODULUS: FieldElement = FieldElement(U512::from_be_hex(concat!( "00000000000000", @@ -22,16 +25,34 @@ const WIDE_MODULUS: U1024 = U1024::from_be_hex(concat!( "00000000000000", "00", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff" )); pub(crate) const Q_4: FieldElement = FieldElement(MODULUS.0.saturating_add(&U512::ONE).wrapping_div(&U512::from_u8(4))); -field!(FieldElement, MODULUS, WIDE_MODULUS, 448); +field!( + FieldElement, + MODULUS_STR, + MODULUS, + WIDE_MODULUS, + 448, + concat!( + "00000000000000000000000000000000000000000000000000000080ffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000", + ), + 7, + concat!( + "fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000", + ), + concat!( + "3100000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + ), +); #[test] fn test_field() { - // TODO: Move to test_prime_field_bits once the impl is finished - ff_group_tests::prime_field::test_prime_field::<_, FieldElement>(&mut rand_core::OsRng); + ff_group_tests::prime_field::test_prime_field_bits::<_, FieldElement>(&mut rand_core::OsRng); } diff --git a/crypto/ed448/src/lib.rs b/crypto/ed448/src/lib.rs index 558533fe..ea315053 100644 --- a/crypto/ed448/src/lib.rs +++ b/crypto/ed448/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![doc = include_str!("../README.md")] +#[macro_use] mod backend; mod scalar; diff --git a/crypto/ed448/src/point.rs b/crypto/ed448/src/point.rs index 0267187e..ee755d1d 100644 --- a/crypto/ed448/src/point.rs +++ b/crypto/ed448/src/point.rs @@ -37,13 +37,13 @@ fn recover_x(y: FieldElement) -> CtOption { let ysq = y.square(); #[allow(non_snake_case)] let D_ysq = D * ysq; - (D_ysq - FieldElement::one()).invert().and_then(|inverted| { - let temp = (ysq - FieldElement::one()) * inverted; + (D_ysq - FieldElement::ONE).invert().and_then(|inverted| { + let temp = (ysq - FieldElement::ONE) * inverted; let mut x = temp.pow(Q_4); x.conditional_negate(x.is_odd()); let xsq = x.square(); - CtOption::new(x, (xsq + ysq).ct_eq(&(FieldElement::one() + (xsq * D_ysq)))) + CtOption::new(x, (xsq + ysq).ct_eq(&(FieldElement::ONE + (xsq * D_ysq)))) }) } @@ -56,7 +56,7 @@ pub struct Point { } lazy_static! { - static ref G: Point = Point { x: recover_x(G_Y).unwrap(), y: G_Y, z: FieldElement::one() }; + static ref G: Point = Point { x: recover_x(G_Y).unwrap(), y: G_Y, z: FieldElement::ONE }; } impl ConstantTimeEq for Point { @@ -180,7 +180,7 @@ impl Group for Point { } } fn identity() -> Self { - Point { x: FieldElement::zero(), y: FieldElement::one(), z: FieldElement::one() } + Point { x: FieldElement::ZERO, y: FieldElement::ONE, z: FieldElement::ONE } } fn generator() -> Self { *G @@ -291,7 +291,7 @@ impl GroupEncoding for Point { recover_x(y).and_then(|mut x| { x.conditional_negate(x.is_odd().ct_eq(&!sign)); let not_negative_zero = !(x.is_zero() & sign); - let point = Point { x, y, z: FieldElement::one() }; + let point = Point { x, y, z: FieldElement::ONE }; CtOption::new(point, not_negative_zero & point.is_torsion_free()) }) }) @@ -317,22 +317,7 @@ impl PrimeGroup for Point {} #[test] fn test_group() { - // TODO: Move to test_prime_group_bits once the impl is finished - use ff_group_tests::group::*; - - test_eq::(); - test_identity::(); - test_generator::(); - test_double::(); - test_add::(); - test_sum::(); - test_neg::(); - test_sub::(); - test_mul::(); - test_order::(); - test_random::<_, Point>(&mut rand_core::OsRng); - - test_encoding::(); + ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng); } #[test] @@ -350,7 +335,7 @@ fn torsion() { .unwrap(), )) .unwrap(); - let old = Point { x: -recover_x(old_y).unwrap(), y: old_y, z: FieldElement::one() }; + let old = Point { x: -recover_x(old_y).unwrap(), y: old_y, z: FieldElement::ONE }; assert!(bool::from(!old.is_torsion_free())); } diff --git a/crypto/ed448/src/scalar.rs b/crypto/ed448/src/scalar.rs index 3821385c..ff3dc7a3 100644 --- a/crypto/ed448/src/scalar.rs +++ b/crypto/ed448/src/scalar.rs @@ -2,12 +2,15 @@ use zeroize::Zeroize; use crypto_bigint::{U512, U1024}; -use crate::field; - /// Ed448 Scalar field element. #[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)] pub struct Scalar(pub(crate) U512); +const MODULUS_STR: &str = concat!( + "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3", +); + // 2**446 - 13818066809895115352007386748515426880336692474882178609894547503885 pub(crate) const MODULUS: Scalar = Scalar(U512::from_be_hex(concat!( "00000000000000", @@ -25,7 +28,26 @@ const WIDE_MODULUS: U1024 = U1024::from_be_hex(concat!( "7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3", )); -field!(Scalar, MODULUS, WIDE_MODULUS, 446); +field!( + Scalar, + MODULUS_STR, + MODULUS, + WIDE_MODULUS, + 446, + concat!( + "7a22ac554961bc91aac7e2463961b610481b6bd7a46d27e2f41165beffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffff1f0000000000000000", + ), + 2, + concat!( + "f24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffff3f0000000000000000", + ), + concat!( + "0400000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + ), +); impl Scalar { /// Perform a wide reduction to obtain a non-biased Scalar. @@ -35,7 +57,6 @@ impl Scalar { } #[test] -fn test_scalar_field() { - // TODO: Move to test_prime_field_bits once the impl is finished - ff_group_tests::prime_field::test_prime_field::<_, Scalar>(&mut rand_core::OsRng); +fn test_scalar() { + ff_group_tests::prime_field::test_prime_field_bits::<_, Scalar>(&mut rand_core::OsRng); } diff --git a/crypto/ff-group-tests/Cargo.toml b/crypto/ff-group-tests/Cargo.toml index 21efc182..143ebc63 100644 --- a/crypto/ff-group-tests/Cargo.toml +++ b/crypto/ff-group-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ff-group-tests" -version = "0.12.2" +version = "0.13.0" description = "A collection of sanity tests for implementors of ff/group APIs" license = "MIT" repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ff-group-tests" @@ -14,8 +14,15 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] rand_core = "0.6" -group = "0.12" + +subtle = "^2.4" + +group = "0.13" [dev-dependencies] -k256 = { version = "0.12", features = ["bits"] } -p256 = { version = "0.12", features = ["bits"] } +k256 = { version = "0.13", features = ["bits"] } +p256 = { version = "0.13", features = ["bits"] } + +bls12_381 = "0.8" + +pasta_curves = "0.5" diff --git a/crypto/ff-group-tests/src/field.rs b/crypto/ff-group-tests/src/field.rs index eeadd4c0..e34f4c81 100644 --- a/crypto/ff-group-tests/src/field.rs +++ b/crypto/ff-group-tests/src/field.rs @@ -1,109 +1,183 @@ use rand_core::RngCore; +use subtle::Choice; use group::ff::Field; /// Perform basic tests on equality. pub fn test_eq() { - let zero = F::zero(); - let one = F::one(); + let zero = F::ZERO; + let one = F::ONE; assert!(zero != one, "0 == 1"); assert!(!bool::from(zero.ct_eq(&one)), "0 ct_eq 1"); - assert_eq!(zero, F::zero(), "0 != 0"); - assert!(bool::from(zero.ct_eq(&F::zero())), "0 !ct_eq 0"); + assert_eq!(zero, F::ZERO, "0 != 0"); + assert!(bool::from(zero.ct_eq(&F::ZERO)), "0 !ct_eq 0"); - assert_eq!(one, F::one(), "1 != 1"); - assert!(bool::from(one.ct_eq(&F::one())), "1 !ct_eq 1"); + assert_eq!(one, F::ONE, "1 != 1"); + assert!(bool::from(one.ct_eq(&F::ONE)), "1 !ct_eq 1"); } /// Verify conditional selection works. Doesn't verify it's actually constant time. pub fn test_conditional_select() { - let zero = F::zero(); - let one = F::one(); + let zero = F::ZERO; + let one = F::ONE; assert_eq!(F::conditional_select(&zero, &one, 0.into()), zero, "couldn't select when false"); assert_eq!(F::conditional_select(&zero, &one, 1.into()), one, "couldn't select when true"); } /// Perform basic tests on addition. pub fn test_add() { - assert_eq!(F::zero() + F::zero(), F::zero(), "0 + 0 != 0"); - assert_eq!(F::zero() + F::one(), F::one(), "0 + 1 != 1"); - assert_eq!(F::one() + F::zero(), F::one(), "1 + 0 != 1"); + assert_eq!(F::ZERO + F::ZERO, F::ZERO, "0 + 0 != 0"); + assert_eq!(F::ZERO + F::ONE, F::ONE, "0 + 1 != 1"); + assert_eq!(F::ONE + F::ZERO, F::ONE, "1 + 0 != 1"); // Only PrimeField offers From // Accordingly, we assume either double or addition is correct // They either have to be matchingly correct or matchingly incorrect, yet we can't // reliably determine that here - assert_eq!(F::one() + F::one(), F::one().double(), "1 + 1 != 2"); + assert_eq!(F::ONE + F::ONE, F::ONE.double(), "1 + 1 != 2"); +} + +/// Perform basic tests on sum. +pub fn test_sum() { + assert_eq!((&[] as &[F]).iter().sum::(), F::ZERO, "[].sum() != 0"); + assert_eq!([F::ZERO].iter().sum::(), F::ZERO, "[0].sum() != 0"); + assert_eq!([F::ONE].iter().sum::(), F::ONE, "[1].sum() != 1"); + + let two = F::ONE + F::ONE; + assert_eq!([F::ONE, F::ONE].iter().sum::(), two, "[1, 1].sum() != 2"); + assert_eq!([two, F::ONE].iter().sum::(), two + F::ONE, "[2, 1].sum() != 3"); + assert_eq!([two, F::ZERO, F::ONE].iter().sum::(), two + F::ONE, "[2, 0, 1].sum() != 3"); } /// Perform basic tests on subtraction. pub fn test_sub() { - assert_eq!(F::zero() - F::zero(), F::zero(), "0 - 0 != 0"); - assert_eq!(F::one() - F::zero(), F::one(), "1 - 0 != 1"); - assert_eq!(F::one() - F::one(), F::zero(), "1 - 1 != 0"); + #[allow(clippy::eq_op)] + let expr = F::ZERO - F::ZERO; + assert_eq!(expr, F::ZERO, "0 - 0 != 0"); + assert_eq!(F::ONE - F::ZERO, F::ONE, "1 - 0 != 1"); + #[allow(clippy::eq_op)] + let expr = F::ONE - F::ONE; + assert_eq!(expr, F::ZERO, "1 - 1 != 0"); } /// Perform basic tests on negation. pub fn test_neg() { - assert_eq!(-F::zero(), F::zero(), "-0 != 0"); - assert_eq!(-(-F::one()), F::one(), "-(-1) != 1"); - assert_eq!(F::one() + (-F::one()), F::zero(), "1 + -1 != 0"); - assert_eq!(F::one() - (-F::one()), F::one().double(), "1 - -1 != 2"); + assert_eq!(-F::ZERO, F::ZERO, "-0 != 0"); + assert_eq!(-(-F::ONE), F::ONE, "-(-1) != 1"); + assert_eq!(F::ONE + (-F::ONE), F::ZERO, "1 + -1 != 0"); + assert_eq!(F::ONE - (-F::ONE), F::ONE.double(), "1 - -1 != 2"); } /// Perform basic tests on multiplication. pub fn test_mul() { - assert_eq!(F::zero() * F::zero(), F::zero(), "0 * 0 != 0"); - assert_eq!(F::one() * F::zero(), F::zero(), "1 * 0 != 0"); - assert_eq!(F::one() * F::one(), F::one(), "1 * 1 != 1"); - let two = F::one().double(); - assert_eq!(two * (two + F::one()), two + two + two, "2 * 3 != 6"); + assert_eq!(F::ZERO * F::ZERO, F::ZERO, "0 * 0 != 0"); + assert_eq!(F::ONE * F::ZERO, F::ZERO, "1 * 0 != 0"); + assert_eq!(F::ONE * F::ONE, F::ONE, "1 * 1 != 1"); + let two = F::ONE.double(); + assert_eq!(two * (two + F::ONE), two + two + two, "2 * 3 != 6"); +} + +/// Perform basic tests on product. +pub fn test_product() { + assert_eq!((&[] as &[F]).iter().product::(), F::ONE, "[].product() != 1"); + assert_eq!([F::ZERO].iter().product::(), F::ZERO, "[0].product() != 0"); + assert_eq!([F::ONE].iter().product::(), F::ONE, "[1].product() != 1"); + + assert_eq!([F::ONE, F::ONE].iter().product::(), F::ONE, "[1, 1].product() != 2"); + let two = F::ONE + F::ONE; + assert_eq!([two, F::ONE].iter().product::(), two, "[2, 1].product() != 2"); + assert_eq!([two, two].iter().product::(), two + two, "[2, 2].product() != 4"); + assert_eq!([two, two, F::ONE].iter().product::(), two + two, "[2, 2, 1].product() != 4"); + assert_eq!([two, F::ZERO, F::ONE].iter().product::(), F::ZERO, "[2, 0, 1].product() != 0"); } /// Perform basic tests on the square function. pub fn test_square() { - assert_eq!(F::zero().square(), F::zero(), "0^2 != 0"); - assert_eq!(F::one().square(), F::one(), "1^2 != 1"); - let two = F::one().double(); + assert_eq!(F::ZERO.square(), F::ZERO, "0^2 != 0"); + assert_eq!(F::ONE.square(), F::ONE, "1^2 != 1"); + let two = F::ONE.double(); assert_eq!(two.square(), two + two, "2^2 != 4"); - let three = two + F::one(); + let three = two + F::ONE; assert_eq!(three.square(), three * three, "3^2 != 9"); } /// Perform basic tests on the invert function. pub fn test_invert() { - assert!(bool::from(F::zero().invert().is_none()), "0.invert() is some"); - assert_eq!(F::one().invert().unwrap(), F::one(), "1.invert() != 1"); + assert!(bool::from(F::ZERO.invert().is_none()), "0.invert() is some"); + assert_eq!(F::ONE.invert().unwrap(), F::ONE, "1.invert() != 1"); - let two = F::one().double(); - let three = two + F::one(); + let two = F::ONE.double(); + let three = two + F::ONE; assert_eq!(two * three.invert().unwrap() * three, two, "2 * 3.invert() * 3 != 2"); } -/// Perform basic tests on the sqrt function. +/// Perform basic tests on the sqrt functions. pub fn test_sqrt() { - assert_eq!(F::zero().sqrt().unwrap(), F::zero(), "sqrt(0) != 0"); - assert_eq!(F::one().sqrt().unwrap(), F::one(), "sqrt(1) != 1"); + assert_eq!(F::ZERO.sqrt().unwrap(), F::ZERO, "sqrt(0) != 0"); + assert!( + (F::ONE.sqrt().unwrap() == F::ONE) || (F::ONE.sqrt().unwrap() == -F::ONE), + "sqrt(1) != 1" + ); - let mut has_root = F::one().double(); + let mut has_root = F::ONE.double(); while bool::from(has_root.sqrt().is_none()) { - has_root += F::one(); + has_root += F::ONE; } + + // The following code doesn't assume which root is returned, yet it does assume a consistent root + // is returned let root = has_root.sqrt().unwrap(); assert_eq!(root * root, has_root, "sqrt(x)^2 != x"); + + let check = |value: (_, _), expected: (_, F), msg| { + assert_eq!(bool::from(value.0), bool::from(expected.0), "{}", msg); + assert!((value.1 == expected.1) || (value.1 == -expected.1), "{}", msg); + }; + check( + F::sqrt_ratio(&has_root, &F::ONE), + (Choice::from(1), root), + "sqrt_ratio didn't return the root with a divisor of 1", + ); + check( + F::sqrt_ratio(&(has_root * F::ONE.double()), &F::ONE.double()), + (Choice::from(1), root), + "sqrt_ratio didn't return the root with a divisor of 2", + ); + + check(F::sqrt_alt(&F::ZERO), F::sqrt_ratio(&F::ZERO, &F::ONE), "sqrt_alt(0) != sqrt_ratio(0, 1)"); + check(F::sqrt_alt(&F::ONE), F::sqrt_ratio(&F::ONE, &F::ONE), "sqrt_alt(1) != sqrt_ratio(1, 1)"); + check(F::sqrt_alt(&has_root), (Choice::from(1), root), "sqrt_alt(square) != (1, root)"); + + // Check 0 divisors are properly implemented + check( + F::sqrt_ratio(&has_root, &F::ZERO), + (Choice::from(0), F::ZERO), + "sqrt_ratio didn't return the right value for a 0 divisor", + ); + + // Check non-squares are appropriately marked + let mut no_root = has_root + F::ONE; + while bool::from(no_root.sqrt().is_some()) { + no_root += F::ONE; + } + assert!( + !bool::from(F::sqrt_ratio(&no_root, &F::ONE).0), + "sqrt_ratio claimed non-square had root" + ); + assert!(!bool::from(F::sqrt_alt(&no_root).0), "sqrt_alt claimed non-square had root"); } /// Perform basic tests on the is_zero functions. pub fn test_is_zero() { - assert!(bool::from(F::zero().is_zero()), "0 is not 0"); - assert!(F::zero().is_zero_vartime(), "0 is not 0"); + assert!(bool::from(F::ZERO.is_zero()), "0 is not 0"); + assert!(F::ZERO.is_zero_vartime(), "0 is not 0"); } /// Perform basic tests on the cube function. pub fn test_cube() { - assert_eq!(F::zero().cube(), F::zero(), "0^3 != 0"); - assert_eq!(F::one().cube(), F::one(), "1^3 != 1"); - let two = F::one().double(); + assert_eq!(F::ZERO.cube(), F::ZERO, "0^3 != 0"); + assert_eq!(F::ONE.cube(), F::ONE, "1^3 != 1"); + let two = F::ONE.double(); assert_eq!(two.cube(), two * two * two, "2^3 != 8"); } @@ -130,14 +204,22 @@ pub fn test_random(rng: &mut R) { pub fn test_field(rng: &mut R) { test_eq::(); test_conditional_select::(); + test_add::(); + test_sum::(); + test_sub::(); test_neg::(); + test_mul::(); + test_product::(); + test_square::(); test_invert::(); test_sqrt::(); test_is_zero::(); + test_cube::(); + test_random::(rng); } diff --git a/crypto/ff-group-tests/src/group.rs b/crypto/ff-group-tests/src/group.rs index e0fddb69..50fc6ee6 100644 --- a/crypto/ff-group-tests/src/group.rs +++ b/crypto/ff-group-tests/src/group.rs @@ -108,7 +108,7 @@ pub fn test_mul() { /// Test `((order - 1) * G) + G == identity`. pub fn test_order() { - let minus_one = G::generator() * (G::Scalar::zero() - G::Scalar::one()); + let minus_one = G::generator() * (G::Scalar::ZERO - G::Scalar::ONE); assert!(minus_one != G::identity(), "(modulus - 1) * G was identity"); assert_eq!(minus_one + G::generator(), G::identity(), "((modulus - 1) * G) + G wasn't identity"); } @@ -203,3 +203,15 @@ fn test_k256() { fn test_p256() { test_prime_group_bits::<_, p256::ProjectivePoint>(&mut rand_core::OsRng); } + +#[test] +fn test_bls12_381() { + test_prime_group_bits::<_, bls12_381::G1Projective>(&mut rand_core::OsRng); + test_prime_group_bits::<_, bls12_381::G2Projective>(&mut rand_core::OsRng); +} + +#[test] +fn test_pallas_vesta() { + test_prime_group_bits::<_, pasta_curves::pallas::Point>(&mut rand_core::OsRng); + test_prime_group_bits::<_, pasta_curves::vesta::Point>(&mut rand_core::OsRng); +} diff --git a/crypto/ff-group-tests/src/prime_field.rs b/crypto/ff-group-tests/src/prime_field.rs index e62259fe..607f9738 100644 --- a/crypto/ff-group-tests/src/prime_field.rs +++ b/crypto/ff-group-tests/src/prime_field.rs @@ -6,17 +6,28 @@ use crate::field::test_field; // Ideally, this and test_one would be under Field, yet these tests require access to From /// Test zero returns F::from(0). pub fn test_zero() { - assert_eq!(F::zero(), F::from(0u64), "0 != 0"); + assert_eq!(F::ZERO, F::from(0u64), "0 != 0"); } /// Test one returns F::from(1). pub fn test_one() { - assert_eq!(F::one(), F::from(1u64), "1 != 1"); + assert_eq!(F::ONE, F::from(1u64), "1 != 1"); } /// Test `From` for F works. pub fn test_from_u64() { - assert_eq!(F::one().double(), F::from(2u64), "2 != 2"); + assert_eq!(F::ZERO, F::from(0u64), "0 != 0u64"); + assert_eq!(F::ONE, F::from(1u64), "1 != 1u64"); + assert_eq!(F::ONE.double(), F::from(2u64), "2 != 2u64"); + assert_eq!(F::ONE.double() + F::ONE, F::from(3u64), "3 != 3u64"); +} + +/// Test from_u128 for F works. +pub fn test_from_u128() { + assert_eq!(F::ZERO, F::from_u128(0u128), "0 != 0u128"); + assert_eq!(F::ONE, F::from_u128(1u128), "1 != 1u128"); + assert_eq!(F::from(2u64), F::from_u128(2u128), "2u64 != 2u128"); + assert_eq!(F::from(3u64), F::from_u128(3u128), "3u64 != 3u128"); } /// Test is_odd/is_even works. @@ -24,14 +35,14 @@ pub fn test_from_u64() { /// Accordingly, this test doesn't support fields alternatively defined. /// TODO: Improve in the future. pub fn test_is_odd() { - assert_eq!(F::zero().is_odd().unwrap_u8(), 0, "0 was odd"); - assert_eq!(F::zero().is_even().unwrap_u8(), 1, "0 wasn't even"); + assert_eq!(F::ZERO.is_odd().unwrap_u8(), 0, "0 was odd"); + assert_eq!(F::ZERO.is_even().unwrap_u8(), 1, "0 wasn't even"); - assert_eq!(F::one().is_odd().unwrap_u8(), 1, "1 was even"); - assert_eq!(F::one().is_even().unwrap_u8(), 0, "1 wasn't odd"); + assert_eq!(F::ONE.is_odd().unwrap_u8(), 1, "1 was even"); + assert_eq!(F::ONE.is_even().unwrap_u8(), 0, "1 wasn't odd"); // Make sure an odd value added to an odd value is even - let two = F::one().double(); + let two = F::ONE.double(); assert_eq!(two.is_odd().unwrap_u8(), 0, "2 was odd"); assert_eq!(two.is_even().unwrap_u8(), 1, "2 wasn't even"); @@ -40,7 +51,7 @@ pub fn test_is_odd() { assert_eq!(four.is_odd().unwrap_u8(), 0, "4 was odd"); assert_eq!(four.is_even().unwrap_u8(), 1, "4 wasn't even"); - let neg_one = -F::one(); + let neg_one = -F::ONE; assert_eq!(neg_one.is_odd().unwrap_u8(), 0, "-1 was odd"); assert_eq!(neg_one.is_even().unwrap_u8(), 1, "-1 wasn't even"); @@ -66,13 +77,13 @@ pub fn test_encoding() { "canonical encoding decoded produced distinct encoding" ); }; - test(F::zero(), "0"); - test(F::one(), "1"); - test(F::one() + F::one(), "2"); - test(-F::one(), "-1"); + test(F::ZERO, "0"); + test(F::ONE, "1"); + test(F::ONE + F::ONE, "2"); + test(-F::ONE, "-1"); // Also check if a non-canonical encoding is possible - let mut high = (F::zero() - F::one()).to_repr(); + let mut high = (F::ZERO - F::ONE).to_repr(); let mut possible_non_canon = false; for byte in high.as_mut() { // The fact a bit isn't set in the highest possible value suggests there's unused bits @@ -97,6 +108,7 @@ pub fn test_prime_field(rng: &mut R) { test_zero::(); test_one::(); test_from_u64::(); + test_from_u128::(); test_is_odd::(); // Do a sanity check on the CAPACITY. A full test can't be done at this time @@ -109,12 +121,12 @@ pub fn test_prime_field(rng: &mut R) { // This test assumes that the modulus is at least 4. pub fn test_to_le_bits() { { - let bits = F::zero().to_le_bits(); + let bits = F::ZERO.to_le_bits(); assert_eq!(bits.iter().filter(|bit| **bit).count(), 0, "0 had bits set"); } { - let bits = F::one().to_le_bits(); + let bits = F::ONE.to_le_bits(); assert!(bits[0], "1 didn't have its least significant bit set"); assert_eq!(bits.iter().filter(|bit| **bit).count(), 1, "1 had multiple bits set"); } @@ -140,20 +152,20 @@ pub fn test_char_le_bits() { assert!(F::char_le_bits().iter().any(|bit| *bit), "char_le_bits contained 0"); // Test this is the bit pattern of the modulus by reconstructing the modulus from it - let mut bit = F::one(); - let mut modulus = F::zero(); + let mut bit = F::ONE; + let mut modulus = F::ZERO; for set in F::char_le_bits() { if set { modulus += bit; } bit = bit.double(); } - assert_eq!(modulus, F::zero(), "char_le_bits did not contain the field's modulus"); + assert_eq!(modulus, F::ZERO, "char_le_bits did not contain the field's modulus"); } /// Test NUM_BITS is accurate. pub fn test_num_bits() { - let mut val = F::one(); + let mut val = F::ONE; let mut bit = 0; while ((bit + 1) < val.to_le_bits().len()) && val.double().to_le_bits()[bit + 1] { val = val.double(); @@ -171,11 +183,11 @@ pub fn test_num_bits() { pub fn test_capacity() { assert!(F::CAPACITY <= F::NUM_BITS, "capacity exceeded number of bits"); - let mut val = F::one(); + let mut val = F::ONE; assert!(val.to_le_bits()[0], "1 didn't have its least significant bit set"); for b in 1 .. F::CAPACITY { val = val.double(); - val += F::one(); + val += F::ONE; for i in 0 ..= b { assert!( val.to_le_bits()[usize::try_from(i).unwrap()], @@ -192,7 +204,7 @@ pub fn test_capacity() { F::CAPACITY, "field has a power of two modulus yet CAPACITY doesn't equal NUM_BITS", ); - assert_eq!(val + F::one(), F::zero()); + assert_eq!(val + F::ONE, F::ZERO, "CAPACITY set bits, + 1, != zero for a binary field"); return; } @@ -200,7 +212,7 @@ pub fn test_capacity() { } fn pow(base: F, exp: F) -> F { - let mut res = F::one(); + let mut res = F::ONE; for bit in exp.to_le_bits().iter().rev() { res *= res; if *bit { @@ -214,22 +226,20 @@ fn pow(base: F, exp: F) -> F { /// Perform basic tests on the pow functions, even when passed non-canonical inputs. pub fn test_pow() { // Sanity check the local pow algorithm. Does not have assert messages as these shouldn't fail - assert_eq!(pow(F::one(), F::zero()), F::one()); - assert_eq!(pow(F::one().double(), F::zero()), F::one()); - assert_eq!(pow(F::one(), F::one()), F::one()); + assert_eq!(pow(F::ONE, F::ZERO), F::ONE); + assert_eq!(pow(F::ONE.double(), F::ZERO), F::ONE); + assert_eq!(pow(F::ONE, F::ONE), F::ONE); - let two = F::one().double(); - assert_eq!(pow(two, F::one()), two); + let two = F::ONE.double(); + assert_eq!(pow(two, F::ONE), two); assert_eq!(pow(two, two), two.double()); - let three = two + F::one(); - assert_eq!(pow(three, F::one()), three); + let three = two + F::ONE; + assert_eq!(pow(three, F::ONE), three); assert_eq!(pow(three, two), three * three); assert_eq!(pow(three, three), three * three * three); - // TODO: Test against Field::pow once updated to ff 0.13 - // Choose a small base without a notably uniform bit pattern - let bit_0 = F::one(); + let bit_0 = F::ONE; let base = { let bit_1 = bit_0.double(); let bit_2 = bit_1.double(); @@ -241,36 +251,53 @@ pub fn test_pow() { bit_7 + bit_6 + bit_5 + bit_2 + bit_0 }; - // Ensure pow_vartime returns 1 when the base is raised to 0, handling malleated inputs - assert_eq!(base.pow_vartime([]), F::one(), "pow_vartime x^0 ([]) != 1"); - assert_eq!(base.pow_vartime([0]), F::one(), "pow_vartime x^0 ([0]) != 1"); - assert_eq!(base.pow_vartime([0, 0]), F::one(), "pow_vartime x^0 ([0, 0]) != 1"); + // Ensure pow/pow_vartime return 1 when the base is raised to 0, handling malleated inputs + assert_eq!(base.pow([]), F::ONE, "pow x^0 ([]) != 1"); + assert_eq!(base.pow_vartime([]), F::ONE, "pow x^0 ([]) != 1"); + assert_eq!(base.pow([0]), F::ONE, "pow_vartime x^0 ([0]) != 1"); + assert_eq!(base.pow_vartime([0]), F::ONE, "pow_vartime x^0 ([0]) != 1"); + assert_eq!(base.pow([0, 0]), F::ONE, "pow x^0 ([0, 0]) != 1"); + assert_eq!(base.pow_vartime([0, 0]), F::ONE, "pow_vartime x^0 ([0, 0]) != 1"); - // Ensure pow_vartime returns the base when raised to 1, handling malleated inputs - assert_eq!(base.pow_vartime([1]), base, "pow_vartime x^1 ([1]) != x"); + // Ensure pow/pow_vartime return the base when raised to 1, handling malleated inputs + assert_eq!(base.pow([1]), base, "pow x^1 ([1]) != x"); + assert_eq!(base.pow_vartime([1, 0]), base, "pow_vartime x^1 ([1, 0]) != x"); + assert_eq!(base.pow([1]), base, "pow x^1 ([1]) != x"); assert_eq!(base.pow_vartime([1, 0]), base, "pow_vartime x^1 ([1, 0]) != x"); - // Ensure pow_vartime can handle multiple u64s properly + // Ensure pow/pow_vartime can handle multiple u64s properly // Create a scalar which exceeds u64 let mut bit_64 = bit_0; for _ in 0 .. 64 { bit_64 = bit_64.double(); } // Run the tests + assert_eq!(base.pow([0, 1]), pow(base, bit_64), "pow x^(2^64) != x^(2^64)"); assert_eq!(base.pow_vartime([0, 1]), pow(base, bit_64), "pow_vartime x^(2^64) != x^(2^64)"); + assert_eq!(base.pow([1, 1]), pow(base, bit_64 + F::ONE), "pow x^(2^64 + 1) != x^(2^64 + 1)"); assert_eq!( base.pow_vartime([1, 1]), - pow(base, bit_64 + F::one()), + pow(base, bit_64 + F::ONE), "pow_vartime x^(2^64 + 1) != x^(2^64 + 1)" ); } +/// Test the inverted constants are correct. +pub fn test_inv_consts() { + assert_eq!(F::TWO_INV, F::from(2u64).invert().unwrap(), "F::TWO_INV != 2.invert()"); + assert_eq!( + F::ROOT_OF_UNITY_INV, + F::ROOT_OF_UNITY.invert().unwrap(), + "F::ROOT_OF_UNITY_INV != F::ROOT_OF_UNITY.invert()" + ); +} + /// Test S is correct. pub fn test_s() { // "This is the number of leading zero bits in the little-endian bit representation of // `modulus - 1`." let mut s = 0; - for b in (F::zero() - F::one()).to_le_bits() { + for b in (F::ZERO - F::ONE).to_le_bits() { if b { break; } @@ -285,14 +312,14 @@ pub fn test_root_of_unity() { // `t = (modulus - 1) >> Self::S`." // Get the bytes to shift - let mut bits = (F::zero() - F::one()).to_le_bits().iter().map(|bit| *bit).collect::>(); + let mut bits = (F::ZERO - F::ONE).to_le_bits().iter().map(|bit| *bit).collect::>(); for _ in 0 .. F::S { bits.remove(0); } // Construct t - let mut bit = F::one(); - let mut t = F::zero(); + let mut bit = F::ONE; + let mut t = F::ZERO; for set in bits { if set { t += bit; @@ -301,11 +328,20 @@ pub fn test_root_of_unity() { } assert!(bool::from(t.is_odd()), "t wasn't odd"); - assert_eq!(pow(F::multiplicative_generator(), t), F::root_of_unity(), "incorrect root of unity"); + assert_eq!(pow(F::MULTIPLICATIVE_GENERATOR, t), F::ROOT_OF_UNITY, "incorrect root of unity"); assert_eq!( - pow(F::root_of_unity(), pow(F::from(2u64), F::from(F::S.into()))), - F::one(), - "root of unity raised to 2^S wasn't 1" + pow(F::ROOT_OF_UNITY, pow(F::from(2u64), F::from(F::S.into()))), + F::ONE, + "root of unity raised to 2^S wasn't 1", + ); +} + +/// Test DELTA is correct. +pub fn test_delta() { + assert_eq!( + pow(F::MULTIPLICATIVE_GENERATOR, pow(F::from(2u64), F::from(u64::from(F::S)))), + F::DELTA, + "F::DELTA is incorrect" ); } @@ -317,8 +353,11 @@ pub fn test_prime_field_bits(rng: &mut R) { test_char_le_bits::(); test_pow::(); + + test_inv_consts::(); test_s::(); test_root_of_unity::(); + test_delta::(); test_num_bits::(); test_capacity::(); diff --git a/crypto/frost/src/curve/mod.rs b/crypto/frost/src/curve/mod.rs index ec91a99f..1fe0c22f 100644 --- a/crypto/frost/src/curve/mod.rs +++ b/crypto/frost/src/curve/mod.rs @@ -92,7 +92,7 @@ pub trait Curve: Ciphersuite { while { seed.extend(repr.as_ref()); res = Zeroizing::new(::hash_to_F(b"nonce", seed.deref())); - res.ct_eq(&Self::F::zero()).into() + res.ct_eq(&Self::F::ZERO).into() } { seed = Zeroizing::new(vec![0; 32]); rng.fill_bytes(&mut seed); diff --git a/crypto/frost/src/sign.rs b/crypto/frost/src/sign.rs index 66eea880..5d43f3fa 100644 --- a/crypto/frost/src/sign.rs +++ b/crypto/frost/src/sign.rs @@ -200,7 +200,7 @@ impl SignatureShare { pub(crate) fn invalidate(&mut self) { use ciphersuite::group::ff::Field; - self.0 += C::F::one(); + self.0 += C::F::ONE; } } @@ -252,6 +252,7 @@ pub struct AlgorithmSignMachine> { params: Params, seed: CachedPreprocess, + #[zeroize(skip)] commitments_challenge: ::Challenge, pub(crate) nonces: Vec>, // Skips the preprocess due to being too large a bound to feasibly enforce on users diff --git a/crypto/frost/src/tests/nonces.rs b/crypto/frost/src/tests/nonces.rs index a4fdd19e..0091d97d 100644 --- a/crypto/frost/src/tests/nonces.rs +++ b/crypto/frost/src/tests/nonces.rs @@ -106,7 +106,7 @@ impl Algorithm for MultiNonce { self.nonces = Some(nonce_sums.to_vec()); // Sum the nonces so we can later check they actually have a relationship to nonce_sums - let mut res = C::F::zero(); + let mut res = C::F::ZERO; // Weight each nonce // This is probably overkill, since their unweighted forms would practically still require diff --git a/crypto/multiexp/Cargo.toml b/crypto/multiexp/Cargo.toml index 77523fc7..6e17de6a 100644 --- a/crypto/multiexp/Cargo.toml +++ b/crypto/multiexp/Cargo.toml @@ -15,15 +15,15 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] zeroize = { version = "^1.5", features = ["zeroize_derive"] } -ff = "0.12" -group = "0.12" +ff = "0.13" +group = "0.13" rand_core = { version = "0.6", optional = true } [dev-dependencies] rand_core = "0.6" -k256 = { version = "0.12", features = ["bits"] } +k256 = { version = "0.13", features = ["bits"] } dalek-ff-group = { path = "../dalek-ff-group" } [features] diff --git a/crypto/multiexp/src/batch.rs b/crypto/multiexp/src/batch.rs index 2895867b..7e6155ad 100644 --- a/crypto/multiexp/src/batch.rs +++ b/crypto/multiexp/src/batch.rs @@ -47,7 +47,7 @@ where ) { // Define a unique scalar factor for this set of variables so individual items can't overlap let u = if self.0.is_empty() { - G::Scalar::one() + G::Scalar::ONE } else { let mut weight; while { diff --git a/crypto/multiexp/src/tests/batch.rs b/crypto/multiexp/src/tests/batch.rs index 02331a7b..5c6cd581 100644 --- a/crypto/multiexp/src/tests/batch.rs +++ b/crypto/multiexp/src/tests/batch.rs @@ -34,14 +34,13 @@ where valid(batch); // Test a batch with one set of statements - let valid_statements = - vec![(-G::Scalar::one(), G::generator()), (G::Scalar::one(), G::generator())]; + let valid_statements = vec![(-G::Scalar::ONE, G::generator()), (G::Scalar::ONE, G::generator())]; let mut batch = BatchVerifier::new(1); batch.queue(&mut OsRng, 0, valid_statements.clone()); valid(batch); // Test a batch with an invalid set of statements fails properly - let invalid_statements = vec![(-G::Scalar::one(), G::generator())]; + let invalid_statements = vec![(-G::Scalar::ONE, G::generator())]; let mut batch = BatchVerifier::new(1); batch.queue(&mut OsRng, 0, invalid_statements.clone()); invalid(batch, 0); diff --git a/crypto/multiexp/src/tests/mod.rs b/crypto/multiexp/src/tests/mod.rs index 550da1c0..4a5b4ca9 100644 --- a/crypto/multiexp/src/tests/mod.rs +++ b/crypto/multiexp/src/tests/mod.rs @@ -110,8 +110,8 @@ where test(&[], G::identity()); // Test an multiexp of identity/zero elements is identity - test(&[(G::Scalar::zero(), G::generator())], G::identity()); - test(&[(G::Scalar::one(), G::identity())], G::identity()); + test(&[(G::Scalar::ZERO, G::generator())], G::identity()); + test(&[(G::Scalar::ONE, G::identity())], G::identity()); // Test a variety of multiexp sizes let mut pairs = Vec::with_capacity(1000); diff --git a/crypto/schnorr/src/aggregate.rs b/crypto/schnorr/src/aggregate.rs index 011d17f9..007cb4d8 100644 --- a/crypto/schnorr/src/aggregate.rs +++ b/crypto/schnorr/src/aggregate.rs @@ -22,7 +22,7 @@ fn weight(digest: &mut DigestTran // This should be guaranteed thanks to SecureDigest debug_assert!(bytes.len() >= 32); - let mut res = F::zero(); + let mut res = F::ZERO; let mut i = 0; // Derive a scalar from enough bits of entropy that bias is < 2^128 @@ -139,12 +139,18 @@ impl SchnorrAggregate { /// A signature aggregator capable of consuming signatures in order to produce an aggregate. #[allow(non_snake_case)] -#[derive(Clone, Debug, Zeroize)] +#[derive(Clone, Debug)] pub struct SchnorrAggregator { digest: DigestTranscript, sigs: Vec>, } +impl Zeroize for SchnorrAggregator { + fn zeroize(&mut self) { + self.sigs.zeroize(); + } +} + impl SchnorrAggregator { /// Create a new aggregator. /// @@ -168,8 +174,7 @@ impl SchnorrAggregator { return None; } - let mut aggregate = - SchnorrAggregate { Rs: Vec::with_capacity(self.sigs.len()), s: C::F::zero() }; + let mut aggregate = SchnorrAggregate { Rs: Vec::with_capacity(self.sigs.len()), s: C::F::ZERO }; for i in 0 .. self.sigs.len() { aggregate.Rs.push(self.sigs[i].R); aggregate.s += self.sigs[i].s * weight::<_, C::F>(&mut self.digest); diff --git a/crypto/schnorr/src/lib.rs b/crypto/schnorr/src/lib.rs index ab1e70c4..c7fd6278 100644 --- a/crypto/schnorr/src/lib.rs +++ b/crypto/schnorr/src/lib.rs @@ -82,7 +82,7 @@ impl SchnorrSignature { // R + cA - sG == 0 [ // R - (C::F::one(), self.R), + (C::F::ONE, self.R), // cA (challenge, public_key), // -sG diff --git a/crypto/schnorr/src/tests/mod.rs b/crypto/schnorr/src/tests/mod.rs index fc434b97..42509ffd 100644 --- a/crypto/schnorr/src/tests/mod.rs +++ b/crypto/schnorr/src/tests/mod.rs @@ -28,7 +28,7 @@ pub(crate) fn sign() { // This verifies invalid signatures don't pass, using zero signatures, which should effectively be // random pub(crate) fn verify() { - assert!(!SchnorrSignature:: { R: C::G::identity(), s: C::F::zero() } + assert!(!SchnorrSignature:: { R: C::G::identity(), s: C::F::ZERO } .verify(C::generator() * C::random_nonzero_F(&mut OsRng), C::random_nonzero_F(&mut OsRng))); } @@ -62,10 +62,10 @@ pub(crate) fn batch_verify() { let mut batch = BatchVerifier::new(5); for (i, mut sig) in sigs.clone().drain(..).enumerate() { if i == 1 { - sig.s += C::F::one(); + sig.s += C::F::ONE; } if i == 2 { - sig.s -= C::F::one(); + sig.s -= C::F::ONE; } sig.batch_verify(&mut OsRng, &mut batch, i, C::generator() * keys[i].deref(), challenges[i]); } diff --git a/deny.toml b/deny.toml index a7b43dff..1d254718 100644 --- a/deny.toml +++ b/deny.toml @@ -81,4 +81,5 @@ unknown-git = "deny" allow-registry = ["https://github.com/rust-lang/crates.io-index"] allow-git = [ "https://github.com/serai-dex/substrate", + "https://github.com/RustCrypto/elliptic-curves" ] diff --git a/processor/Cargo.toml b/processor/Cargo.toml index 1c4dbb53..70756995 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -32,14 +32,14 @@ bincode = "1" serde_json = "1" # Cryptography -group = "0.12" +group = "0.13" transcript = { package = "flexible-transcript", path = "../crypto/transcript" } frost = { package = "modular-frost", path = "../crypto/frost" } # Bitcoin secp256k1 = { version = "0.24", features = ["global-context", "rand-std"], optional = true } -k256 = { version = "0.12", features = ["arithmetic"], optional = true } +k256 = { version = "0.13", features = ["arithmetic"], optional = true } bitcoin-serai = { path = "../coins/bitcoin", optional = true } # Monero diff --git a/processor/src/coins/monero.rs b/processor/src/coins/monero.rs index 7b72f84a..6939c5cb 100644 --- a/processor/src/coins/monero.rs +++ b/processor/src/coins/monero.rs @@ -188,7 +188,7 @@ impl Monero { #[cfg(test)] fn test_view_pair() -> ViewPair { - ViewPair::new(*EdwardsPoint::generator(), Zeroizing::new(Scalar::one().0)) + ViewPair::new(*EdwardsPoint::generator(), Zeroizing::new(Scalar::ONE.0)) } #[cfg(test)] @@ -398,7 +398,7 @@ impl Coin for Monero { } else if outputs == 1 { plan.payments.push(Payment { address: Address::new( - ViewPair::new(EdwardsPoint::generator().0, Zeroizing::new(Scalar::one().0)) + ViewPair::new(EdwardsPoint::generator().0, Zeroizing::new(Scalar::ONE.0)) .address(Network::Mainnet, AddressSpec::Standard), ) .unwrap(), @@ -584,7 +584,7 @@ impl Coin for Monero { self.rpc.get_fee().await.unwrap(), ) .unwrap() - .sign(&mut OsRng, &self.rpc, &Zeroizing::new(Scalar::one().0)) + .sign(&mut OsRng, &self.rpc, &Zeroizing::new(Scalar::ONE.0)) .await .unwrap();