mirror of
https://github.com/serai-dex/serai.git
synced 2024-11-16 17:07:35 +00:00
ff 0.13 (#269)
* Partial move to ff 0.13 It turns out the newly released k256 0.12 isn't on ff 0.13, preventing further work at this time. * Update all crates to work on ff 0.13 The provided curves still need to be expanded to fit the new API. * Finish adding dalek-ff-group ff 0.13 constants * Correct FieldElement::product definition Also stops exporting macros. * Test most new parts of ff 0.13 * Additionally test ff-group-tests with BLS12-381 and the pasta curves We only tested curves from RustCrypto. Now we test a curve offered by zk-crypto, the group behind ff/group, and the pasta curves, which is by Zcash (though Zcash developers are also behind zk-crypto). * Finish Ed448 Fully specifies all constants, passes all tests in ff-group-tests, and finishes moving to ff-0.13. * Add RustCrypto/elliptic-curves to allowed git repos Needed due to k256/p256 incorrectly defining product. * Finish writing ff 0.13 tests * Add additional comments to dalek * Further comments * Update ethereum-serai to ff 0.13
This commit is contained in:
parent
a9f6300e86
commit
79aff5d4c8
59 changed files with 865 additions and 429 deletions
162
Cargo.lock
generated
162
Cargo.lock
generated
|
@ -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]]
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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"] }
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ impl HramTrait<Secp256k1> 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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<Secp256k1> 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,
|
||||
|
|
|
@ -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::<Sha256>(Keccak256::digest(MESSAGE), b"")
|
||||
.try_sign_prehashed_rfc6979::<Sha256>(&Keccak256::digest(MESSAGE), b"")
|
||||
.unwrap();
|
||||
#[allow(clippy::unit_cmp)] // Intended to assert this wasn't changed to Result<bool>
|
||||
{
|
||||
|
@ -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);
|
||||
|
|
|
@ -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"] }
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Scalar> {
|
||||
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() {
|
||||
|
|
|
@ -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));
|
||||
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ fn hash_plus<C: IntoIterator<Item = DalekPoint>>(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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -304,7 +304,7 @@ impl Algorithm<Ed25519> 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]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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::<Sha256>::expand_message(&[msg], dst, 48).unwrap().fill_bytes(&mut bytes);
|
||||
ExpandMsgXmd::<Sha256>::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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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<Self> {
|
||||
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<FieldElement> for FieldElement {
|
||||
fn sum<I: Iterator<Item = FieldElement>>(iter: I) -> FieldElement {
|
||||
let mut res = FieldElement::ZERO;
|
||||
for item in iter {
|
||||
res += item;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a FieldElement> for FieldElement {
|
||||
fn sum<I: Iterator<Item = &'a FieldElement>>(iter: I) -> FieldElement {
|
||||
iter.cloned().sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl Product<FieldElement> for FieldElement {
|
||||
fn product<I: Iterator<Item = FieldElement>>(iter: I) -> FieldElement {
|
||||
let mut res = FieldElement::ONE;
|
||||
for item in iter {
|
||||
res *= item;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Product<&'a FieldElement> for FieldElement {
|
||||
fn product<I: Iterator<Item = &'a FieldElement>>(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))))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Self> {
|
||||
CtOption::new(Self(self.0.invert()), !self.is_zero())
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
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<Self> {
|
||||
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<Self::ReprBits> {
|
||||
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<Scalar> for Scalar {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a Scalar> for Scalar {
|
||||
fn sum<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
|
||||
Self(DScalar::sum(iter))
|
||||
}
|
||||
}
|
||||
|
||||
impl Product<Scalar> for Scalar {
|
||||
fn product<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
|
||||
Self(DScalar::product(iter))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Product<&'a Scalar> for Scalar {
|
||||
fn product<I: Iterator<Item = &'a Scalar>>(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<Self> {
|
||||
|
|
|
@ -190,7 +190,7 @@ impl<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
|
|||
|
||||
#[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<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
|
|||
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());
|
||||
}
|
||||
|
|
|
@ -156,8 +156,8 @@ fn polynomial<F: PrimeField + Zeroize>(
|
|||
) -> Zeroizing<F> {
|
||||
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<C: Ciphersuite> Zeroize for KeyMachine<C> {
|
|||
fn exponential<C: Ciphersuite>(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<C: Ciphersuite>(
|
|||
// 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
|
||||
}
|
||||
|
|
|
@ -176,8 +176,8 @@ impl ThresholdParams {
|
|||
pub fn lagrange<F: PrimeField>(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<C: Ciphersuite> ThresholdKeys<C> {
|
|||
// 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<C: Ciphersuite> ThresholdKeys<C> {
|
|||
|
||||
/// 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<C: Ciphersuite> ThresholdKeys<C> {
|
|||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ pub fn recover_key<C: Ciphersuite>(keys: &HashMap<Participant, ThresholdKeys<C>>
|
|||
assert!(keys.len() >= first.params().t().into(), "not enough keys provided");
|
||||
let included = keys.keys().cloned().collect::<Vec<_>>();
|
||||
|
||||
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::<C::F>(*i, &included) * keys.secret_share().deref())
|
||||
});
|
||||
assert_eq!(C::generator() * group_private, first.group_key(), "failed to recover keys");
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -42,7 +42,7 @@ impl<G0: PrimeGroup, G1: PrimeGroup> Re<G0, G1> {
|
|||
}
|
||||
|
||||
pub(crate) fn e_default() -> Re<G0, G1> {
|
||||
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)?);
|
||||
}
|
||||
|
|
|
@ -269,15 +269,15 @@ where
|
|||
SchnorrPoK::<G1>::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
|
||||
};
|
||||
|
|
|
@ -18,8 +18,8 @@ pub fn scalar_normalize<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
|||
#[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<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits
|
|||
let capacity = usize::try_from(F0::CAPACITY.min(F1::CAPACITY)).unwrap();
|
||||
debug_assert!((bytes.len() * 8) >= 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());
|
||||
|
|
|
@ -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),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -32,7 +32,7 @@ pub(crate) fn challenge<T: Transcript, F: PrimeField>(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<G: PrimeGroup> {
|
||||
pub struct DLEqProof<G: PrimeGroup>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
c: G::Scalar,
|
||||
s: G::Scalar,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl<G: PrimeGroup> DLEqProof<G> {
|
||||
impl<G: PrimeGroup> DLEqProof<G>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
fn transcript<T: 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<G: PrimeGroup> DLEqProof<G> {
|
|||
transcript: &mut T,
|
||||
generators: &[G],
|
||||
scalar: &Zeroizing<G::Scalar>,
|
||||
) -> DLEqProof<G>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
) -> DLEqProof<G> {
|
||||
let r = Zeroizing::new(G::Scalar::random(rng));
|
||||
|
||||
transcript.domain_separate(b"dleq");
|
||||
|
@ -210,14 +213,20 @@ impl<G: PrimeGroup> DLEqProof<G> {
|
|||
/// across some generators, yet with a smaller overall proof size.
|
||||
#[cfg(feature = "std")]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub struct MultiDLEqProof<G: PrimeGroup> {
|
||||
pub struct MultiDLEqProof<G: PrimeGroup>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
c: G::Scalar,
|
||||
s: Vec<G::Scalar>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(non_snake_case)]
|
||||
impl<G: PrimeGroup> MultiDLEqProof<G> {
|
||||
impl<G: PrimeGroup> MultiDLEqProof<G>
|
||||
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<G: PrimeGroup> MultiDLEqProof<G> {
|
|||
transcript: &mut T,
|
||||
generators: &[Vec<G>],
|
||||
scalars: &[Zeroizing<G::Scalar>],
|
||||
) -> MultiDLEqProof<G>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
) -> MultiDLEqProof<G> {
|
||||
assert_eq!(
|
||||
generators.len(),
|
||||
scalars.len(),
|
||||
|
|
|
@ -21,7 +21,7 @@ fn test_aos_serialization<const RING_LEN: usize>(proof: Aos<G0, G1, RING_LEN>, R
|
|||
fn test_aos<const RING_LEN: usize>(default: Re<G0, G1>) {
|
||||
let generators = generators();
|
||||
|
||||
let mut ring_keys = [(<G0 as Group>::Scalar::zero(), <G1 as Group>::Scalar::zero()); RING_LEN];
|
||||
let mut ring_keys = [(<G0 as Group>::Scalar::ZERO, <G1 as Group>::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];
|
||||
|
|
|
@ -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::<Scalar, Scalar>(&[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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<u8, U57>;
|
||||
|
||||
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<Self> {
|
||||
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<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
|
||||
let mut res = $FieldName::ZERO;
|
||||
for item in iter {
|
||||
res += item;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a $FieldName> for $FieldName {
|
||||
fn sum<I: Iterator<Item = &'a $FieldName>>(iter: I) -> $FieldName {
|
||||
iter.cloned().sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl Product<$FieldName> for $FieldName {
|
||||
fn product<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
|
||||
let mut res = $FieldName::ONE;
|
||||
for item in iter {
|
||||
res *= item;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Product<&'a $FieldName> for $FieldName {
|
||||
fn product<I: Iterator<Item = &'a $FieldName>>(iter: I) -> $FieldName {
|
||||
iter.cloned().product()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#![no_std]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
#[macro_use]
|
||||
mod backend;
|
||||
|
||||
mod scalar;
|
||||
|
|
|
@ -37,13 +37,13 @@ fn recover_x(y: FieldElement) -> CtOption<FieldElement> {
|
|||
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::<Point>();
|
||||
test_identity::<Point>();
|
||||
test_generator::<Point>();
|
||||
test_double::<Point>();
|
||||
test_add::<Point>();
|
||||
test_sum::<Point>();
|
||||
test_neg::<Point>();
|
||||
test_sub::<Point>();
|
||||
test_mul::<Point>();
|
||||
test_order::<Point>();
|
||||
test_random::<_, Point>(&mut rand_core::OsRng);
|
||||
|
||||
test_encoding::<Point>();
|
||||
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()));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,109 +1,183 @@
|
|||
use rand_core::RngCore;
|
||||
use subtle::Choice;
|
||||
use group::ff::Field;
|
||||
|
||||
/// Perform basic tests on equality.
|
||||
pub fn test_eq<F: Field>() {
|
||||
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<F: Field>() {
|
||||
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<F: Field>() {
|
||||
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<u64>
|
||||
// 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<F: Field>() {
|
||||
assert_eq!((&[] as &[F]).iter().sum::<F>(), F::ZERO, "[].sum() != 0");
|
||||
assert_eq!([F::ZERO].iter().sum::<F>(), F::ZERO, "[0].sum() != 0");
|
||||
assert_eq!([F::ONE].iter().sum::<F>(), F::ONE, "[1].sum() != 1");
|
||||
|
||||
let two = F::ONE + F::ONE;
|
||||
assert_eq!([F::ONE, F::ONE].iter().sum::<F>(), two, "[1, 1].sum() != 2");
|
||||
assert_eq!([two, F::ONE].iter().sum::<F>(), two + F::ONE, "[2, 1].sum() != 3");
|
||||
assert_eq!([two, F::ZERO, F::ONE].iter().sum::<F>(), two + F::ONE, "[2, 0, 1].sum() != 3");
|
||||
}
|
||||
|
||||
/// Perform basic tests on subtraction.
|
||||
pub fn test_sub<F: Field>() {
|
||||
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<F: Field>() {
|
||||
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<F: Field>() {
|
||||
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<F: Field>() {
|
||||
assert_eq!((&[] as &[F]).iter().product::<F>(), F::ONE, "[].product() != 1");
|
||||
assert_eq!([F::ZERO].iter().product::<F>(), F::ZERO, "[0].product() != 0");
|
||||
assert_eq!([F::ONE].iter().product::<F>(), F::ONE, "[1].product() != 1");
|
||||
|
||||
assert_eq!([F::ONE, F::ONE].iter().product::<F>(), F::ONE, "[1, 1].product() != 2");
|
||||
let two = F::ONE + F::ONE;
|
||||
assert_eq!([two, F::ONE].iter().product::<F>(), two, "[2, 1].product() != 2");
|
||||
assert_eq!([two, two].iter().product::<F>(), two + two, "[2, 2].product() != 4");
|
||||
assert_eq!([two, two, F::ONE].iter().product::<F>(), two + two, "[2, 2, 1].product() != 4");
|
||||
assert_eq!([two, F::ZERO, F::ONE].iter().product::<F>(), F::ZERO, "[2, 0, 1].product() != 0");
|
||||
}
|
||||
|
||||
/// Perform basic tests on the square function.
|
||||
pub fn test_square<F: Field>() {
|
||||
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<F: Field>() {
|
||||
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<F: Field>() {
|
||||
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<F: Field>() {
|
||||
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<F: Field>() {
|
||||
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<R: RngCore, F: Field>(rng: &mut R) {
|
|||
pub fn test_field<R: RngCore, F: Field>(rng: &mut R) {
|
||||
test_eq::<F>();
|
||||
test_conditional_select::<F>();
|
||||
|
||||
test_add::<F>();
|
||||
test_sum::<F>();
|
||||
|
||||
test_sub::<F>();
|
||||
test_neg::<F>();
|
||||
|
||||
test_mul::<F>();
|
||||
test_product::<F>();
|
||||
|
||||
test_square::<F>();
|
||||
test_invert::<F>();
|
||||
test_sqrt::<F>();
|
||||
test_is_zero::<F>();
|
||||
|
||||
test_cube::<F>();
|
||||
|
||||
test_random::<R, F>(rng);
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ pub fn test_mul<G: Group>() {
|
|||
|
||||
/// Test `((order - 1) * G) + G == identity`.
|
||||
pub fn test_order<G: Group>() {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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<u64>
|
||||
/// Test zero returns F::from(0).
|
||||
pub fn test_zero<F: PrimeField>() {
|
||||
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<F: PrimeField>() {
|
||||
assert_eq!(F::one(), F::from(1u64), "1 != 1");
|
||||
assert_eq!(F::ONE, F::from(1u64), "1 != 1");
|
||||
}
|
||||
|
||||
/// Test `From<u64>` for F works.
|
||||
pub fn test_from_u64<F: PrimeField>() {
|
||||
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<F: PrimeField>() {
|
||||
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<F: PrimeField>() {
|
|||
/// Accordingly, this test doesn't support fields alternatively defined.
|
||||
/// TODO: Improve in the future.
|
||||
pub fn test_is_odd<F: PrimeField>() {
|
||||
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<F: PrimeField>() {
|
|||
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<F: PrimeField>() {
|
|||
"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<R: RngCore, F: PrimeField>(rng: &mut R) {
|
|||
test_zero::<F>();
|
||||
test_one::<F>();
|
||||
test_from_u64::<F>();
|
||||
test_from_u128::<F>();
|
||||
test_is_odd::<F>();
|
||||
|
||||
// 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<R: RngCore, F: PrimeField>(rng: &mut R) {
|
|||
// This test assumes that the modulus is at least 4.
|
||||
pub fn test_to_le_bits<F: PrimeField + PrimeFieldBits>() {
|
||||
{
|
||||
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<F: PrimeField + PrimeFieldBits>() {
|
|||
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<F: PrimeField + PrimeFieldBits>() {
|
||||
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<F: PrimeField + PrimeFieldBits>() {
|
|||
pub fn test_capacity<F: PrimeField + PrimeFieldBits>() {
|
||||
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: PrimeField + PrimeFieldBits>() {
|
|||
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<F: PrimeField + PrimeFieldBits>() {
|
|||
}
|
||||
|
||||
fn pow<F: PrimeFieldBits>(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<F: PrimeFieldBits>(base: F, exp: F) -> F {
|
|||
/// Perform basic tests on the pow functions, even when passed non-canonical inputs.
|
||||
pub fn test_pow<F: PrimeFieldBits>() {
|
||||
// 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<F: PrimeFieldBits>() {
|
|||
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<F: PrimeFieldBits>() {
|
||||
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<F: PrimeFieldBits>() {
|
||||
// "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<F: PrimeFieldBits>() {
|
|||
// `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::<Vec<_>>();
|
||||
let mut bits = (F::ZERO - F::ONE).to_le_bits().iter().map(|bit| *bit).collect::<Vec<_>>();
|
||||
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<F: PrimeFieldBits>() {
|
|||
}
|
||||
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<F: PrimeFieldBits>() {
|
||||
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<R: RngCore, F: PrimeFieldBits>(rng: &mut R) {
|
|||
test_char_le_bits::<F>();
|
||||
|
||||
test_pow::<F>();
|
||||
|
||||
test_inv_consts::<F>();
|
||||
test_s::<F>();
|
||||
test_root_of_unity::<F>();
|
||||
test_delta::<F>();
|
||||
|
||||
test_num_bits::<F>();
|
||||
test_capacity::<F>();
|
||||
|
|
|
@ -92,7 +92,7 @@ pub trait Curve: Ciphersuite {
|
|||
while {
|
||||
seed.extend(repr.as_ref());
|
||||
res = Zeroizing::new(<Self as Curve>::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);
|
||||
|
|
|
@ -200,7 +200,7 @@ impl<C: Curve> SignatureShare<C> {
|
|||
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<C: Curve, A: Algorithm<C>> {
|
|||
params: Params<C, A>,
|
||||
seed: CachedPreprocess,
|
||||
|
||||
#[zeroize(skip)]
|
||||
commitments_challenge: <A::Transcript as Transcript>::Challenge,
|
||||
pub(crate) nonces: Vec<Nonce<C>>,
|
||||
// Skips the preprocess due to being too large a bound to feasibly enforce on users
|
||||
|
|
|
@ -106,7 +106,7 @@ impl<C: Curve> Algorithm<C> for MultiNonce<C> {
|
|||
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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -22,7 +22,7 @@ fn weight<D: Send + Clone + SecureDigest, F: PrimeField>(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<C: Ciphersuite> SchnorrAggregate<C> {
|
|||
|
||||
/// 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<C: Ciphersuite> {
|
||||
digest: DigestTranscript<C::H>,
|
||||
sigs: Vec<SchnorrSignature<C>>,
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> Zeroize for SchnorrAggregator<C> {
|
||||
fn zeroize(&mut self) {
|
||||
self.sigs.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> SchnorrAggregator<C> {
|
||||
/// Create a new aggregator.
|
||||
///
|
||||
|
@ -168,8 +174,7 @@ impl<C: Ciphersuite> SchnorrAggregator<C> {
|
|||
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);
|
||||
|
|
|
@ -82,7 +82,7 @@ impl<C: Ciphersuite> SchnorrSignature<C> {
|
|||
// R + cA - sG == 0
|
||||
[
|
||||
// R
|
||||
(C::F::one(), self.R),
|
||||
(C::F::ONE, self.R),
|
||||
// cA
|
||||
(challenge, public_key),
|
||||
// -sG
|
||||
|
|
|
@ -28,7 +28,7 @@ pub(crate) fn sign<C: Ciphersuite>() {
|
|||
// This verifies invalid signatures don't pass, using zero signatures, which should effectively be
|
||||
// random
|
||||
pub(crate) fn verify<C: Ciphersuite>() {
|
||||
assert!(!SchnorrSignature::<C> { R: C::G::identity(), s: C::F::zero() }
|
||||
assert!(!SchnorrSignature::<C> { 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<C: Ciphersuite>() {
|
|||
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]);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in a new issue