diff --git a/CHANGELOG.md b/CHANGELOG.md index 42ad34c..ff9095a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ ## v0.3.3 * Unify encoding of integer=0 across backends [#45] * Deserialize returns an error on invalid radix, instead of panicking [#45] +* Use `crypto-primes` for primality test instead of `glass_pumpkin` on num-bigint backend [#46] [#45]: https://github.com/LFDT-Lockness/fast-paillier/pull/45 +[#46]: https://github.com/LFDT-Lockness/fast-paillier/pull/46 ## v0.3.2 * Make `random_below`, `random_below_ref`, `random_bits`, `random_bits_signed`, diff --git a/Cargo.lock b/Cargo.lock index 9bed866..1195f9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "block-buffer" version = "0.10.4" @@ -135,13 +141,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] -name = "core2" -version = "0.4.0" +name = "cmov" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" [[package]] name = "criterion" @@ -179,12 +188,6 @@ dependencies = [ "itertools", ] -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -216,6 +219,19 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "crypto-bigint" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271" +dependencies = [ + "cpubits", + "ctutils", + "num-traits", + "rand_core 0.10.1", + "serdect", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -226,6 +242,25 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-primes" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" +dependencies = [ + "crypto-bigint", + "rand_core 0.10.1", +] + +[[package]] +name = "ctutils" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574" +dependencies = [ + "cmov", +] + [[package]] name = "digest" version = "0.9.0" @@ -258,14 +293,15 @@ dependencies = [ "bytemuck", "ciborium", "criterion", - "glass_pumpkin", + "crypto-bigint", + "crypto-primes", "libpaillier", "num-bigint", "num-integer", "num-traits", "quickcheck", "rand", - "rand_core", + "rand_core 0.6.4", "rand_dev", "rug", "serde", @@ -294,20 +330,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "glass_pumpkin" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17567f20aa6eef73871963dfa01cde07c6300d46f4ab4ed82681100fa0a6012c" -dependencies = [ - "core2", - "num-bigint", - "num-integer", - "num-traits", - "once_cell", - "rand_core", -] - [[package]] name = "gmp-mpfr-sys" version = "1.6.8" @@ -448,10 +470,6 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -dependencies = [ - "critical-section", - "portable-atomic", -] [[package]] name = "oorandom" @@ -487,12 +505,6 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -537,7 +549,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -547,7 +559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -559,6 +571,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_dev" version = "0.1.1" @@ -567,7 +585,7 @@ checksum = "cbee97c27dada05f03db49ffe6516872f6c926e0fd525f9ce0cb3c051adf145c" dependencies = [ "hex", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -707,6 +725,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index 4586565..50823f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ bytemuck = { version = "1.5", features = ["derive"], optional = true } num-bigint = { version = "0.4.6", optional = true, default-features = false, features = ["rand"] } num-traits = { version = "0.2.19", optional = true, default-features = false } num-integer = { version = "0.1.46", optional = true, default-features = false } -glass_pumpkin = { version = "1.8", optional = true, default-features = false } +crypto-bigint = { version = "0.7", default-features = false, features = ["alloc"], optional = true } +crypto-primes = { version = "0.7", default-features = false, optional = true } rand = { version = "0.8", optional = true, default-features = false } rand_core = "0.6" @@ -45,14 +46,17 @@ libpaillier = { version = "0.5", default-features = false, features = ["gmp"] } [features] default = ["backend-num-bigint", "std"] -std = ["glass_pumpkin?/std"] -no_std = ["glass_pumpkin?/no_std"] +std = [] +no_std = [] serde = ["dep:serde"] quickcheck = ["dep:quickcheck"] backend-rug = ["dep:rug", "dep:bytemuck"] -backend-num-bigint = ["dep:num-bigint", "dep:num-traits", "dep:num-integer", "dep:glass_pumpkin", "dep:rand"] +backend-num-bigint = [ + "dep:num-bigint", "dep:num-traits", "dep:num-integer", "dep:rand", "dep:crypto-bigint", + "dep:crypto-primes", +] [[bench]] name = "comparison" @@ -63,4 +67,8 @@ harness = false num-bigint.opt-level = 3 num-traits.opt-level = 3 num-integer.opt-level = 3 -glass_pumpkin.opt-level = 3 + +# See https://docs.rs/cpubits/0.1.1/cpubits/#lint-configuration-for-cfgcpubits +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = ['cfg(cpubits, values("16", "32", "64"))'] diff --git a/src/backend/num_bigint.rs b/src/backend/num_bigint.rs index 7025f49..07ce86b 100644 --- a/src/backend/num_bigint.rs +++ b/src/backend/num_bigint.rs @@ -215,6 +215,11 @@ impl Integer { self.invert_ref(modulo) } pub fn invert_ref(&self, modulo: &Self) -> Option { + // Nbi returns wrong result if modulo = 1, and self is negative. Correct response is `0`, + // but nbi gives `1`. + if self.cmp0().is_lt() && modulo.is_one() { + return Some(Self::zero()); + } let r = self.0.modinv(&modulo.0).map(Integer)?; // Rug always produces a positive result here. Num-bigint always // produces a result between zero and modulo. When modulo is negative, @@ -293,12 +298,22 @@ impl Integer { self } - // TODO reps is unused here, need to unify with rug - pub fn is_probably_prime(&self, _reps: u32, rng: &mut impl rand_core::RngCore) -> IsPrime { + // TODO neither reps nor rng are used here, maybe we should get rid of them + pub fn is_probably_prime(&self, _reps: u32, _rng: &mut impl rand_core::RngCore) -> IsPrime { + fn boxed_uint(n: &num_bigint::BigUint) -> crypto_bigint::BoxedUint { + crypto_bigint::cpubits! { + 16 | 32 => { crypto_bigint::BoxedUint::from_words(n.iter_u32_digits()) } + 64 => { crypto_bigint::BoxedUint::from_words(n.iter_u64_digits()) } + } + } + if self.cmp0().is_le() { IsPrime::No - } else if glass_pumpkin::prime::check_with(self.0.magnitude(), rng) { - IsPrime::Yes + } else if crypto_primes::is_prime( + crypto_primes::Flavor::Any, + &boxed_uint(self.0.magnitude()), + ) { + IsPrime::Probably } else { IsPrime::No } @@ -310,7 +325,10 @@ impl Integer { x.assign_random_bits(bit_size, rng); x.set_bit(bit_size - 1, true); x |= 1u32; - if glass_pumpkin::prime::check_with(x.0.magnitude(), rng) { + if matches!( + x.is_probably_prime(0, rng), + IsPrime::Probably | IsPrime::Yes + ) { break x; } } diff --git a/tests/integration/backend.rs b/tests/integration/backend.rs index 6a9cf80..24a5b97 100644 --- a/tests/integration/backend.rs +++ b/tests/integration/backend.rs @@ -20,12 +20,15 @@ macro_rules! make_quickcheck { #[allow(unused_mut)] let mut rug = RugInteger::from_bytes_msf_signed(&bytes, sign); + let r1 = { // Since Arg::to_nbi takes mutable reference to the arg, we clone them, // so the `rug` method below receives the same args as provided by // quickcheck without any modification + eprintln!("int: {nbi:?}"); $( let mut $arg = $arg.clone(); + eprintln!("arg: {:?}", $arg); )* nbi.$method( $( @@ -125,7 +128,6 @@ make_quickcheck!(Self::random_bits(bits: SmallU32, rng: Prng)); make_quickcheck!(Self::random_bits_signed(bits: SmallU32, rng: Prng)); make_quickcheck!(assign_random_below(self: NbiInteger, modulo: Positive, rng: Prng)); make_quickcheck!(assign_random_bits(self: NbiInteger, bits: SmallU32, rng: Prng)); -make_quickcheck!(is_probably_prime(self: NbiInteger, const25: Const<25>, rng: Prng)); make_quickcheck!(jacobi(self: NbiInteger, n: OddPositive)); make_quickcheck!( combine( @@ -163,6 +165,21 @@ quickcheck::quickcheck! { let r2 = serde_json::to_string(&rug).unwrap(); r1 == r2 } + + fn is_probably_prime(nbi: NbiInteger, rng: Prng) -> bool { + use fast_paillier::backend::IsPrime; + + let mut rng = rng; + let (bytes, sign) = nbi.to_bytes_msf_signed(); + let rug = RugInteger::from_bytes_msf_signed(&bytes, sign); + + let r1 = nbi.is_probably_prime(25, &mut rng.rng); + let r2 = rug.is_probably_prime(25, &mut rng.rng); + + // It doesn't matter if one backend returns `Yes`, and the other returns `Probably`. + // It matters to check if one returns `Yes|Probably`, and the other returns `No` + matches!((r1, r2), (IsPrime::Yes | IsPrime::Probably, IsPrime::Yes | IsPrime::Probably) | (IsPrime::No, IsPrime::No)) + } } ///// Helper traits for macro ///// @@ -343,26 +360,6 @@ impl quickcheck::Arbitrary for SmallU32 { } } -/// Helper quickcheck newtype: generates a constant u32. Convenient for the -/// macros above, since they don't accept constant values -#[derive(Clone, Debug)] -struct Const; -impl Arg<'_> for Const { - type NbiArg = u32; - type RugArg = u32; - fn to_nbi(&mut self) -> Self::NbiArg { - N - } - fn to_rug(&mut self) -> Self::RugArg { - N - } -} -impl quickcheck::Arbitrary for Const { - fn arbitrary(_: &mut quickcheck::Gen) -> Self { - Const - } -} - /// Helper quickcheck newtype: generates a non-zero integral value #[derive(Clone, Debug)] struct NonZero(T);