From d2709b463a867944e954782386399304bdcd9382 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Wed, 16 Oct 2024 18:17:06 -0700 Subject: [PATCH] Return all RustCrypto libs but crypto-bigint to stable versions --- CHANGELOG.md | 5 +- synedrion/Cargo.toml | 20 ++++---- synedrion/src/cggmp21/params.rs | 76 +++++++++++++++++++++++++++---- synedrion/src/curve/arithmetic.rs | 33 +++++++++----- synedrion/src/curve/ecdsa.rs | 2 +- synedrion/src/uint.rs | 6 +-- synedrion/src/uint/traits.rs | 44 +----------------- 7 files changed, 108 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f903c5d..cbf1334a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `FirstRound::Context` renamed to `Inputs`. ([#102]) - `Payload` and `Artifact` values are hidden in wrapper types where they were previously exposed. ([#102]) -- A number of crates set to their `pre` releases hinging on the `pre` release of `crypto-bigint`. +- A number of crates set to their `pre` releases hinging on the `pre` release of `crypto-bigint`. ([#120]) +- Signature and elliptic curve dependencies reset back to stable versions. (#[154]) ### Added @@ -20,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#96]: https://github.com/entropyxyz/synedrion/pull/96 [#102]: https://github.com/entropyxyz/synedrion/pull/102 +[#120]: https://github.com/entropyxyz/synedrion/pull/120 +[#154]: https://github.com/entropyxyz/synedrion/pull/154 ## [0.1.0] - 2023-12-07 diff --git a/synedrion/Cargo.toml b/synedrion/Cargo.toml index d38fd1d6..28800cb5 100644 --- a/synedrion/Cargo.toml +++ b/synedrion/Cargo.toml @@ -10,22 +10,22 @@ readme = "README.md" categories = ["cryptography", "no-std"] [dependencies] -signature = { version = "2.3.0-pre.4", default-features = false, features = ["alloc"] } -k256 = {version = "0.14.0-pre.2", default-features = false, features = ["ecdsa", "arithmetic"]} +signature = { version = "2", default-features = false, features = ["alloc"] } +k256 = { version = "0.13", default-features = false, features = ["ecdsa", "arithmetic"] } rand_core = { version = "0.6.4", default-features = false, features = ["getrandom"] } -sha2 = { version = "0.11.0-pre.4", default-features = false } -sha3 = { version = "0.11.0-pre.4", default-features = false } -digest = { version = "0.11.0-pre.9", default-features = false, features = ["alloc"]} +sha2 = { version = "0.10", default-features = false } +sha3 = { version = "0.10", default-features = false } +digest = { version = "0.10", default-features = false, features = ["alloc"]} hex = { version = "0.4", default-features = false, features = ["alloc"] } -base64 = { version = "0.22.1", default-features = false, features = ["alloc"] } -hashing-serializer = { version = "0.2.0-pre.0", default-features = false } +base64 = { version = "0.22", default-features = false, features = ["alloc"] } +hashing-serializer = { version = "0.1", default-features = false } secrecy = { version = "0.9.0-pre.0", default-features = false, features = ["serde"] } zeroize = { version = "1.8", default-features = false, features = ["alloc", "zeroize_derive"] } -bip32 = { version = "0.6.0-pre.0", default-features = false, features = ["alloc", "secp256k1", "k256"] } +bip32 = { version = "0.5", default-features = false, features = ["alloc", "secp256k1", "k256"] } # Note: `alloc` is needed for `crytpto-bigint`'s dependency `serdect` to be able # to serialize Uints in human-readable formats. -crypto-bigint = { version = "0.6.0-rc.2", features = ["serde", "alloc", "rand_core"] } +crypto-bigint = { version = "0.6.0-rc.2", features = ["serde", "alloc", "rand_core", "zeroize"] } crypto-primes = "0.6.0-pre.1" serde = { version = "1", default-features = false, features = ["derive"] } @@ -43,7 +43,7 @@ serde_assert = "0.8" tokio = { version = "1", features = ["rt", "sync", "time", "macros"] } rand = "0.8" criterion = "0.5" -k256 = {version = "0.14.0-pre.2", default-features = false, features = ["ecdsa", "arithmetic", "pem", "serde"]} +k256 = {version = "0.13", default-features = false, features = ["ecdsa", "arithmetic", "pem", "serde"]} impls = "1" [features] diff --git a/synedrion/src/cggmp21/params.rs b/synedrion/src/cggmp21/params.rs index 38621fce..a5d455f2 100644 --- a/synedrion/src/cggmp21/params.rs +++ b/synedrion/src/cggmp21/params.rs @@ -4,15 +4,38 @@ use crate::curve::{Curve, Scalar, ORDER}; use crate::paillier::PaillierParams; use crate::tools::hashing::{Chain, HashableType}; use crate::uint::{ - subtle::ConditionallySelectable, upcast_uint, Bounded, Encoding, NonZero, Signed, U1024Mod, - U2048Mod, U4096Mod, U512Mod, Zero, U1024, U2048, U4096, U512, U8192, + subtle::ConditionallySelectable, Bounded, Encoding, NonZero, Signed, U1024Mod, U2048Mod, + U4096Mod, U512Mod, Uint, Zero, U1024, U2048, U4096, U512, U8192, }; +// We're depending on a pre-release `crypto-bigint` version, +// and `k256` depends on the released one. +// So as long as that is the case, `k256` `Uint` is separate +// from the one used throughout the crate. +use k256::elliptic_curve::bigint::Uint as K256Uint; use serde::{Deserialize, Serialize}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PaillierTest; +const fn upcast_uint(value: K256Uint) -> K256Uint { + assert!( + N2 >= N1, + "Upcast target must be bigger than the upcast candidate" + ); + let mut result_words = [0; N2]; + let mut i = 0; + while i < N1 { + result_words[i] = value.as_words()[i]; + i += 1; + } + K256Uint::from_words(result_words) +} + +const fn convert_uint(value: K256Uint) -> Uint { + Uint::from_words(value.to_words()) +} + impl PaillierParams for PaillierTest { /* The prime size is chosen to be minimal for which the `TestSchemeParams` still work. @@ -115,8 +138,7 @@ pub trait SchemeParams: Debug + Clone + Send + PartialEq + Eq + Send + Sync + 's fn bounded_from_scalar( value: &Scalar, ) -> Option::Uint>> { - const ORDER_BITS: u32 = ORDER.bits_vartime(); - Bounded::new(Self::uint_from_scalar(value), ORDER_BITS) + Bounded::new(Self::uint_from_scalar(value), ORDER.bits_vartime() as u32) } /// Converts a curve scalar to the associated integer type, wrapped in `Signed`. @@ -194,9 +216,13 @@ impl SchemeParams for TestParams { const EPS_BOUND: usize = 320; type Paillier = PaillierTest; const CURVE_ORDER: NonZero<::Uint> = - upcast_uint(ORDER).to_nz().expect("Correct by construction"); + convert_uint(upcast_uint(ORDER)) + .to_nz() + .expect("Correct by construction"); const CURVE_ORDER_WIDE: NonZero<::WideUint> = - upcast_uint(ORDER).to_nz().expect("Correct by construction"); + convert_uint(upcast_uint(ORDER)) + .to_nz() + .expect("Correct by construction"); } /// Production strength parameters. @@ -210,7 +236,41 @@ impl SchemeParams for ProductionParams { const EPS_BOUND: usize = Self::L_BOUND * 2; type Paillier = PaillierProduction; const CURVE_ORDER: NonZero<::Uint> = - upcast_uint(ORDER).to_nz().expect("Correct by construction"); + convert_uint(upcast_uint(ORDER)) + .to_nz() + .expect("Correct by construction"); const CURVE_ORDER_WIDE: NonZero<::WideUint> = - upcast_uint(ORDER).to_nz().expect("Correct by construction"); + convert_uint(upcast_uint(ORDER)) + .to_nz() + .expect("Correct by construction"); +} + +#[cfg(test)] +mod tests { + use k256::elliptic_curve::bigint::{U256, U64}; + + use super::upcast_uint; + + #[test] + fn upcast_uint_results_in_a_bigger_type() { + let n = U64::from_u8(10); + let expected = U256::from_u8(10); + let bigger_n: U256 = upcast_uint(n); + + assert_eq!(bigger_n, expected); + } + + #[test] + #[should_panic(expected = "Upcast target must be bigger than the upcast candidate")] + fn upcast_uint_panics_in_test_if_actually_attempting_downcast() { + let n256 = U256::from_u8(8); + let _n: U64 = upcast_uint(n256); + } + + #[test] + fn upcast_uint_allows_casting_to_same_size() { + let n256 = U256::from_u8(8); + let n: U256 = upcast_uint(n256); + assert_eq!(n, n256) + } } diff --git a/synedrion/src/curve/arithmetic.rs b/synedrion/src/curve/arithmetic.rs index 21b7348d..9afa3bf5 100644 --- a/synedrion/src/curve/arithmetic.rs +++ b/synedrion/src/curve/arithmetic.rs @@ -7,8 +7,8 @@ use core::ops::{Add, Mul, Neg, Sub}; use digest::Digest; use k256::elliptic_curve::group::ff::PrimeField; use k256::elliptic_curve::{ - array::{typenum::marker_traits::Unsigned, Array}, bigint::U256, // Note that this type is different from typenum::U256 + generic_array::{typenum::marker_traits::Unsigned, GenericArray}, ops::Reduce, point::AffineCoordinates, sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint}, @@ -40,7 +40,18 @@ pub(crate) const ORDER: U256 = Secp256k1::ORDER; impl HashableType for Curve { fn chain_type(digest: C) -> C { - digest.chain(&ORDER).chain(&Point::GENERATOR) + let mut digest = digest; + + // TODO: `k256 0.14` depends on `crypto-bigint` that supports `Serialize` for `Uint`'s, + // so we can just chain `ORDER`. For now we have to do it manually. + // Note that since only `to_words` is available, we need to chain it + // so that the result is the same on 32- and 64-bit targets - that is, in low-endian order. + let words = ORDER.to_words(); + for word in words { + digest = digest.chain(&word.to_le_bytes()); + } + + digest.chain(&Point::GENERATOR) } } @@ -81,7 +92,7 @@ impl Scalar { /// SEC1 specifies to subtract the secp256k1 modulus when the byte array /// is larger than the modulus. pub fn from_reduced_bytes(bytes: &[u8; 32]) -> Self { - let arr = Array::>::from(*bytes); + let arr = GenericArray::>::from(*bytes); Self(>::reduce_bytes(&arr)) } @@ -107,8 +118,9 @@ impl Scalar { } pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result { - let arr = Array::>::try_from_iter(bytes.iter().cloned()) - .map_err(|e| format!("Invalid length of a curve scalar: {:?}", e))?; + let arr = + GenericArray::>::from_exact_iter(bytes.iter().cloned()) + .ok_or("Invalid length of a curve scalar")?; BackendScalar::from_repr_vartime(arr) .map(Self) @@ -200,13 +212,10 @@ impl Point { .ok_or_else(|| "Invalid curve point representation".into()) } - pub(crate) fn to_compressed_array(self) -> Array { - self.0 - .to_affine() - .to_encoded_point(true) - .as_bytes() - .try_into() - .expect("An AffinePoint is composed of elements of the correct size and their slice repr fits in the `CompressedPointSize`-sized array.") + pub(crate) fn to_compressed_array(self) -> GenericArray { + GenericArray::::from_exact_iter( + self.0.to_affine().to_encoded_point(true).as_bytes().iter().cloned(), + ).expect("An AffinePoint is composed of elements of the correct size and their slice repr fits in the `CompressedPointSize`-sized array.") } pub(crate) fn to_backend(self) -> BackendPoint { diff --git a/synedrion/src/curve/ecdsa.rs b/synedrion/src/curve/ecdsa.rs index 3e88dbbd..c915c8a8 100644 --- a/synedrion/src/curve/ecdsa.rs +++ b/synedrion/src/curve/ecdsa.rs @@ -21,7 +21,7 @@ impl RecoverableSignature { // Normalize the `s` component. // `BackendSignature`'s constructor does not require `s` to be normalized, // but consequent usage of it may fail otherwise. - let signature = signature.normalize_s(); + let signature = signature.normalize_s().unwrap_or(signature); let message_bytes = message.to_bytes(); let recovery_id = RecoveryId::trial_recovery_from_prehash( diff --git a/synedrion/src/uint.rs b/synedrion/src/uint.rs index 8c76c84a..f5cbf65b 100644 --- a/synedrion/src/uint.rs +++ b/synedrion/src/uint.rs @@ -4,13 +4,13 @@ mod traits; pub(crate) use crypto_bigint::{ modular::Retrieve, subtle, CheckedAdd, CheckedMul, CheckedSub, Encoding, Integer, Invert, - NonZero, PowBoundedExp, RandomMod, ShlVartime, WrappingSub, Zero, U1024, U2048, U4096, U512, - U8192, + NonZero, PowBoundedExp, RandomMod, ShlVartime, Uint, WrappingSub, Zero, U1024, U2048, U4096, + U512, U8192, }; pub(crate) use crypto_primes::RandomPrimeWithRng; pub(crate) use bounded::Bounded; pub(crate) use signed::Signed; pub(crate) use traits::{ - upcast_uint, Exponentiable, HasWide, ToMontgomery, U1024Mod, U2048Mod, U4096Mod, U512Mod, + Exponentiable, HasWide, ToMontgomery, U1024Mod, U2048Mod, U4096Mod, U512Mod, }; diff --git a/synedrion/src/uint/traits.rs b/synedrion/src/uint/traits.rs index 265c2ff8..26525c1f 100644 --- a/synedrion/src/uint/traits.rs +++ b/synedrion/src/uint/traits.rs @@ -2,26 +2,12 @@ use crypto_bigint::{ modular::MontyForm, nlimbs, subtle::{ConditionallySelectable, CtOption}, - Bounded, Encoding, Integer, Invert, PowBoundedExp, RandomMod, Square, Uint, Zero, U1024, U2048, + Bounded, Encoding, Integer, Invert, PowBoundedExp, RandomMod, Square, Zero, U1024, U2048, U4096, U512, U8192, }; use crate::uint::Signed; -pub(crate) const fn upcast_uint(value: Uint) -> Uint { - assert!( - N2 >= N1, - "Upcast target must be bigger than the upcast candidate" - ); - let mut result_words = [0; N2]; - let mut i = 0; - while i < N1 { - result_words[i] = value.as_words()[i]; - i += 1; - } - Uint::from_words(result_words) -} - pub trait ToMontgomery: Integer { fn to_montgomery( self, @@ -252,31 +238,3 @@ impl Exponentiable for U512Mod {} impl Exponentiable for U1024Mod {} impl Exponentiable for U2048Mod {} impl Exponentiable for U4096Mod {} - -#[cfg(test)] -mod tests { - use super::upcast_uint; - use crypto_bigint::{U256, U64}; - #[test] - fn upcast_uint_results_in_a_bigger_type() { - let n = U64::from_u8(10); - let expected = U256::from_u8(10); - let bigger_n: U256 = upcast_uint(n); - - assert_eq!(bigger_n, expected); - } - - #[test] - #[should_panic(expected = "Upcast target must be bigger than the upcast candidate")] - fn upcast_uint_panics_in_test_if_actually_attempting_downcast() { - let n256 = U256::from_u8(8); - let _n: U64 = upcast_uint(n256); - } - - #[test] - fn upcast_uint_allows_casting_to_same_size() { - let n256 = U256::from_u8(8); - let n: U256 = upcast_uint(n256); - assert_eq!(n, n256) - } -}