From 795da47f6633815e15f7c762e3b840b380930183 Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Tue, 27 Jul 2021 16:55:22 +0300 Subject: [PATCH] Upgrade `curv` interface (#120) * Impl From for BigInt * Add Polynomial * Add the proof * Update travis config * Bump version * Rename coef0 to const_term + update docs * Add sample_exact and sample_exact_with_fixed_const * Rename LdeiProveError -> LdeiProofError * Move polynomial into secret_sharing module * LDEI prove takes witness and statement * Comment out crypto primitives for a while * Update traits, add wrappers, and upgrade secp256k1 implementation * Update polynomial * Do not access wrappers internal state directly, improve docs * Add generator wrapper * Add secp256k1 test * Implement Serialize, Deserialize for Point(Z) * Update feldman_vss * Add {to,from}_bytes functions to PointZ * Update hashing * Change curve_name method with associated constant * Deref stylistic fix * Optimise generator multiplication * Update docs * Update commitments * Update low_degree_exponent_interpolation * Update sigma_correct_homomorphic_elgamal_enc * Update sigma_correct_homomorphic_elgamal_encryption_of_dlog * Wrappers: rename methods, implement PartialEq * Update sigma_dlog * Update sigma_ec_ddh * Rename curve_order to group_order * Restrict the points to be of group order * Split a large wrappers module into several files * Update sigma_valid_pedersen * Update sigma_valid_pedersen_blind * Fix derived Serialize/Deserialize traits * Update coin_flip_optimal_rounds * Update dh_key_exchange * Update dh_key_exchange_variant_with_pok_comm * Update diffie_hellman_key_exchange example * Update pedersen_commitment * Update proof_of_knowledge_of_dlog example * Update verifiable_secret_sharing example * Fix warning * Fix clippy warnings * Update doc * Improve hashing * Write doc * Fix typos * Update travis config * Update P-256 curve implementation * Add tests * Update Ed25519 curve * Update Ristretto curve * Ristretto: x=hash(y) * Add the first BLS curve * Default implementation for ECPoint::is_zero method * Add tests, fix ed25519 negation * Uncomment proofs of base_point2 picked randomly * Add the second BLS curve * Write doc * Add pairing, update docs * Rename package * Update readme * Add changelog * Merge Point&PointZ, Scalar&ScalarZ * Serialize always succeeds * Update doc * Fix clippy warning * VerifiableSS returns polynomial used to derive secret shares * Fix documentation * Optimize elliptic curves implementations * Add serialize/deserialize to Scalars * Use scalar/point `serialize` functions to implement serde serialization instead of bigint * Add serde tests * Add more EC serialize/deserialize tests * Update examples * Update Cargo.toml * Update docs * Update vss to use u16 instead of usize fix * Resolve TODO * Fix doc * Impl iter::{Sum, Product} for wrappers * Ristretto: return x=None instead of x=hash(y) * Add documentation for the rest of the curves * Improve point serialization * Improve scalar serialization * Move serde-related stuff to dedicated module * Improve serde tests * Remove PointRef wrapper * Move actual unsafe code to a single function * Finalization Co-authored-by: Elichai Turkel --- .travis.yml | 28 +- CHANGELOG.md | 14 + Cargo.toml | 33 +- README.md | 16 +- examples/diffie_hellman_key_exchange.rs | 25 +- examples/pedersen_commitment.rs | 29 +- examples/proof_of_knowledge_of_dlog.rs | 25 +- examples/verifiable_secret_sharing.rs | 44 +- src/arithmetic/big_gmp.rs | 6 + src/arithmetic/big_native.rs | 6 + src/arithmetic/mod.rs | 2 +- src/arithmetic/samplable.rs | 2 +- src/arithmetic/traits.rs | 25 +- .../commitments/pedersen_commitment.rs | 21 +- .../hashing/blake2b512.rs | 106 +- src/cryptographic_primitives/hashing/ext.rs | 327 ++++++ .../hashing/hash_sha256.rs | 32 +- .../hashing/hash_sha512.rs | 34 +- .../hashing/hmac_sha512.rs | 4 +- .../hashing/merkle_tree.rs | 48 +- src/cryptographic_primitives/hashing/mod.rs | 4 + .../hashing/traits.rs | 6 +- .../low_degree_exponent_interpolation.rs | 213 ++++ src/cryptographic_primitives/proofs/mod.rs | 1 + .../sigma_correct_homomorphic_elgamal_enc.rs | 210 ++-- ..._homomorphic_elgamal_encryption_of_dlog.rs | 167 ++- .../proofs/sigma_dlog.rs | 89 +- .../proofs/sigma_ec_ddh.rs | 138 +-- .../proofs/sigma_valid_pedersen.rs | 116 +- .../proofs/sigma_valid_pedersen_blind.rs | 106 +- .../secret_sharing/feldman_vss.rs | 421 +++---- .../secret_sharing/mod.rs | 3 + .../secret_sharing/polynomial.rs | 330 ++++++ .../twoparty/coin_flip_optimal_rounds.rs | 100 +- .../twoparty/dh_key_exchange.rs | 103 +- .../dh_key_exchange_variant_with_pok_comm.rs | 164 +-- src/cryptographic_primitives/twoparty/mod.rs | 4 +- src/elliptic/curves/bls12_381/g1.rs | 788 +++---------- src/elliptic/curves/bls12_381/g2.rs | 795 +++---------- src/elliptic/curves/bls12_381/mod.rs | 84 +- src/elliptic/curves/bls12_381/pairing.rs | 96 ++ src/elliptic/curves/bls12_381/scalar.rs | 185 +++ src/elliptic/curves/curve_ristretto.rs | 690 ++++------- src/elliptic/curves/ed25519.rs | 862 +++++--------- src/elliptic/curves/mod.rs | 107 +- src/elliptic/curves/p256.rs | 963 ++++----------- src/elliptic/curves/secp256_k1.rs | 1028 ++++++----------- src/elliptic/curves/test.rs | 360 ++++++ src/elliptic/curves/traits.rs | 288 ++++- src/elliptic/curves/wrappers/arithmetic.rs | 491 ++++++++ src/elliptic/curves/wrappers/encoded_point.rs | 21 + .../curves/wrappers/encoded_scalar.rs | 29 + src/elliptic/curves/wrappers/error.rs | 75 ++ src/elliptic/curves/wrappers/generator.rs | 84 ++ src/elliptic/curves/wrappers/mod.rs | 13 + src/elliptic/curves/wrappers/point.rs | 309 +++++ src/elliptic/curves/wrappers/scalar.rs | 208 ++++ src/elliptic/curves/wrappers/serde_support.rs | 345 ++++++ src/lib.rs | 21 +- 59 files changed, 6005 insertions(+), 4839 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 src/cryptographic_primitives/hashing/ext.rs create mode 100644 src/cryptographic_primitives/proofs/low_degree_exponent_interpolation.rs create mode 100644 src/cryptographic_primitives/secret_sharing/polynomial.rs create mode 100644 src/elliptic/curves/bls12_381/pairing.rs create mode 100644 src/elliptic/curves/bls12_381/scalar.rs create mode 100644 src/elliptic/curves/test.rs create mode 100644 src/elliptic/curves/wrappers/arithmetic.rs create mode 100644 src/elliptic/curves/wrappers/encoded_point.rs create mode 100644 src/elliptic/curves/wrappers/encoded_scalar.rs create mode 100644 src/elliptic/curves/wrappers/error.rs create mode 100644 src/elliptic/curves/wrappers/generator.rs create mode 100644 src/elliptic/curves/wrappers/mod.rs create mode 100644 src/elliptic/curves/wrappers/point.rs create mode 100644 src/elliptic/curves/wrappers/scalar.rs create mode 100644 src/elliptic/curves/wrappers/serde_support.rs diff --git a/.travis.yml b/.travis.yml index e99fd798..9adf6e41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,25 @@ language: rust cache: cargo -rust: - - stable +rust: stable +virt: lxd -before_script: - - rustup component add rustfmt-preview clippy - - cargo fmt --all -- --check - - cargo clippy -- -D clippy::all +env: + - BIGINT_BACKEND=rust-gmp-kzen + - BIGINT_BACKEND=num-bigint + +before_install: + - rustup component add rustfmt clippy script: - - cargo build --verbose - - cargo test --verbose + - cargo build --verbose --no-default-features --features $BIGINT_BACKEND + - cargo test --verbose --no-default-features --features $BIGINT_BACKEND + - if [[ "$BIGINT_BACKEND" = "rust-gmp-kzen" ]]; then cargo fmt --all -- --check; fi + - if [[ "$BIGINT_BACKEND" = "rust-gmp-kzen" ]]; then cargo clippy -- -D clippy::all; fi + +deploy: + provider: cargo + token: + secure: "FE6A1XRyJtTK92rV3y5e0go+FDKn1HpJbYkHOacDqabTkUluXrCTw3ERFcQQ13QZdc9xkxoAs7roKp8ivl0Xg1IJCzI+yLb0ZR6YcYebKEqd06YFbBmejjvMsyyZHKPTQmroe+tBwcA1IqLLcAY8vmY5EGhJTsGUhovIomw1RvqM6gu9yUwII/sF0a5gqY761cJd4QoLlWTb1Er7DqZxoU9drhWAJQP7sLsspjLu6dOyWzb0A2mTmnek+iuVnt9mGPtjGk4FcNPGbEmNu3UPOVuXcyibFPIALEWrH0ouZB7E9k312g45LucSeKSimgQYQBNAzdsnkKyBwyTpGuaosGnMbI7mhoi3visV21RTbw61N05dmZTYb4VAMcx+93TslKMDv5nmIlUmKxULNRBSTPPtrg0/X7KuKaoHVstrxx0ohd8GFwGYQBB64mQaOxFBhoy//prpHjhFl+1cti4JHyaHFSV/PfaryvUfRg4q2Dlq1HP+ey5cPRPbwfpSO1RmXlIDWe21ncRnKSpgMHTPBzYNtil+gZyzHl5X4ZLvLCaHsZwZQPMFB+otlabFaS1caqkk1F1fHMrj8NMak/snb2IyUJqXgQivqzEn38G3k9/QHeQXhNVwyGDtdWV51P9XfXFpxrEuuWlXF56ABiWcF7bY7Y3DeCbnFNLkVkGZYvY=" + on: + tags: true + condition: '"$TRAVIS_TAG" =~ ^v[0-9].+$ && "$BIGINT_BACKEND" = "rust-gmp-kzen"' diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d0312c3f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog + +## v0.8.0-rc1 +* Elliptic curve API has been significantly changed [#120] + + In particular: ECPoint, ECScalar traits were redesigned. They remain, + but are not supposed to be used directly anymore. In replacement, + we introduce structures Point, Scalar representing elliptic point and + scalar. See curv::elliptic::curves module-level documentation to learn + more. +* Add low degree exponent interpolation proof [#119] + +[#119]: https://github.com/ZenGo-X/curv/pull/119 +[#120]: https://github.com/ZenGo-X/curv/pull/120 diff --git a/Cargo.toml b/Cargo.toml index 196ee0dd..cfa8df9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,35 +1,39 @@ [package] -name = "curv" -version = "0.7.0" +name = "curv-kzen" +version = "0.8.0-rc1" edition = "2018" -authors = ["Omer Shlomovits"] +authors = [ + "Omer Shlomovits", + "Denis Varlakov", +] license = "MIT" description = "Curv contains an extremly simple interface to onboard new elliptic curves. Use this library for general purpose elliptic curve cryptography" repository = "https://github.com/ZenGo-X/curv" [lib] +name = "curv" crate-type = ["lib"] [dependencies] blake2b_simd = "0.5.7" cryptoxide = "0.1.2" -curve25519-dalek = "1.2.3" -derivative = "2.2" +curve25519-dalek = "3" digest = "0.8.1" ff-zeroize = "0.6.3" -funty = "=1.1.0" -generic-array = "0.14" -hex = "0.4" +hex = { version = "0.4", features = ["serde"] } hmac = "0.7.1" +thiserror = "1" merkle-sha3 = "^0.1" -lazy_static = "1.4.0" +lazy_static = "1.4" num-traits = "0.2" num-integer = "0.1" pairing-plus = "0.19" -rand = "0.6" +rand = "0.7" +rand_legacy = { package = "rand", version = "0.6" } ring-algorithm = "0.2.3" rust-crypto = "^0.2" serde = { version = "1.0", features = ["derive"] } +serde_bytes = "0.11" serde_derive = "1.0" sha2 = "0.8.0" sha3 = "0.8.2" @@ -40,15 +44,14 @@ num-bigint = { version = "0.4", features = ["serde"], optional = true } [dependencies.secp256k1] version = "0.20" -features = ["serde", "rand-std"] +features = ["serde", "rand-std", "global-context"] [dependencies.p256] -version = "0.5" -features = ["ecdsa"] +version = "0.9" +features = ["ecdsa", "ecdsa-core", "zeroize"] [dev-dependencies] -bincode = "1.1" -serde_json = "1.0" +serde_test = "1.0" paste = "1.0.2" proptest = "0.10" proptest-derive = "0.2" diff --git a/README.md b/README.md index 039e0ac3..a19b7c71 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,19 @@ The library has a built in support for some useful operations/primitives such as schemes, zero knowledge proofs, and simple two party protocols such as ECDH and coin flip. The library comes with serialize/deserialize support to be used in higher level code to implement networking. +### Usage + +To use `curv` crate, add the following to your Cargo.toml: +```toml +[dependencies] +curv-kzen = "0.8" +``` + +The crate will be available under `curv` name, e.g.: +```rust +use curv::elliptic::curves::*; +``` + ### Currently Supported Elliptic Curves | Curve | low level library | curve description | @@ -44,8 +57,7 @@ You can choose any one which you prefer by specifying a feature: * **num-bigint**, Rust's pure implementation of big integer. In order to use it, put in Cargo.toml: ```toml [dependencies.curv] - git = "https://github.com/ZenGo-X/curv" - tag = "v0.6.0" + version = "0.8" default-features = false features = ["num-bigint"] ``` diff --git a/examples/diffie_hellman_key_exchange.rs b/examples/diffie_hellman_key_exchange.rs index a0a8bb4c..5671e54e 100644 --- a/examples/diffie_hellman_key_exchange.rs +++ b/examples/diffie_hellman_key_exchange.rs @@ -1,6 +1,4 @@ -use std::fmt::Debug; - -use curv::elliptic::curves::traits::ECPoint; +use curv::elliptic::curves::*; /// Diffie Hellman Key Exchange: /// TO RUN: @@ -11,17 +9,13 @@ use curv::elliptic::curves::traits::ECPoint; /// notice: this library includes also a more involved ECDH scheme. see /// dh_key_exchange_variant_with_pok_comm.rs -pub fn ecdh

() -where - P: ECPoint + Clone + Debug, - P::Scalar: Clone, -{ +pub fn ecdh() { use curv::cryptographic_primitives::twoparty::dh_key_exchange::{ compute_pubkey, Party1FirstMessage, Party2FirstMessage, }; - let (kg_party_one_first_message, kg_ec_key_pair_party1) = Party1FirstMessage::

::first(); - let (kg_party_two_first_message, kg_ec_key_pair_party2) = Party2FirstMessage::

::first(); + let (kg_party_one_first_message, kg_ec_key_pair_party1) = Party1FirstMessage::::first(); + let (kg_party_two_first_message, kg_ec_key_pair_party2) = Party2FirstMessage::::first(); assert_eq!( compute_pubkey( @@ -38,11 +32,12 @@ where fn main() { let curve_name = std::env::args().nth(1); match curve_name.as_deref() { - Some("secp256k1") => ecdh::(), - Some("ristretto") => ecdh::(), - Some("ed25519") => ecdh::(), - Some("bls12_381") => ecdh::(), - Some("p256") => ecdh::(), + Some("secp256k1") => ecdh::(), + Some("ristretto") => ecdh::(), + Some("ed25519") => ecdh::(), + Some("bls12_381_1") => ecdh::(), + Some("bls12_381_2") => ecdh::(), + Some("p256") => ecdh::(), Some(unknown_curve) => eprintln!("Unknown curve: {}", unknown_curve), None => eprintln!("Missing curve name"), } diff --git a/examples/pedersen_commitment.rs b/examples/pedersen_commitment.rs index cc15ce16..a489ccca 100644 --- a/examples/pedersen_commitment.rs +++ b/examples/pedersen_commitment.rs @@ -1,9 +1,7 @@ -use curv::arithmetic::{traits::*, BigInt}; -use curv::elliptic::curves::traits::ECPoint; +use curv::arithmetic::*; +use curv::elliptic::curves::*; -use std::fmt::Debug; - -/// Pedesen Commitment: +/// Pedersen Commitment: /// compute c = mG + rH /// where m is the commited value, G is the group generator, /// H is a random point and r is a blinding value. @@ -14,16 +12,13 @@ use std::fmt::Debug; /// /// notice: this library includes also hash based commitments -pub fn ped_com

(message: &BigInt) -where - P: ECPoint + Debug, -{ +pub fn ped_com(message: &BigInt) { use curv::cryptographic_primitives::commitments::pedersen_commitment::PedersenCommitment; use curv::cryptographic_primitives::commitments::traits::Commitment; let security_bits = 256; let blinding_factor = BigInt::sample(security_bits); - let com = PedersenCommitment::

::create_commitment_with_user_defined_randomness( + let com = PedersenCommitment::::create_commitment_with_user_defined_randomness( message, &blinding_factor, ); @@ -36,15 +31,15 @@ where fn main() { let message = "commit me!"; - let message_bytes = message.as_bytes(); - let _message_bn = BigInt::from_bytes(message_bytes); + let message_bn = BigInt::from_bytes(message.as_bytes()); let curve_name = std::env::args().nth(1); match curve_name.as_deref() { - Some("secp256k1") => ped_com::(&_message_bn), - Some("ristretto") => ped_com::(&_message_bn), - Some("ed25519") => ped_com::(&_message_bn), - Some("bls12_381") => ped_com::(&_message_bn), - Some("p256") => ped_com::(&_message_bn), + Some("secp256k1") => ped_com::(&message_bn), + Some("ristretto") => ped_com::(&message_bn), + Some("ed25519") => ped_com::(&message_bn), + Some("bls12_381_1") => ped_com::(&message_bn), + Some("bls12_381_2") => ped_com::(&message_bn), + Some("p256") => ped_com::(&message_bn), Some(unknown_curve) => eprintln!("Unknown curve: {}", unknown_curve), None => eprintln!("Missing curve name"), } diff --git a/examples/proof_of_knowledge_of_dlog.rs b/examples/proof_of_knowledge_of_dlog.rs index 0b3b1028..6e08da77 100644 --- a/examples/proof_of_knowledge_of_dlog.rs +++ b/examples/proof_of_knowledge_of_dlog.rs @@ -1,5 +1,4 @@ -use curv::elliptic::curves::traits::ECPoint; -use zeroize::Zeroize; +use curv::elliptic::curves::*; /// Sigma protocol for proof of knowledge of discrete log /// TO RUN: @@ -10,27 +9,23 @@ use zeroize::Zeroize; /// notice: this library includes other more complex sigma protocol. /// see proofs folder for more details -pub fn dlog_proof

() -where - P: ECPoint + Clone, - P::Scalar: Zeroize, -{ +pub fn dlog_proof() { use curv::cryptographic_primitives::proofs::sigma_dlog::*; - use curv::elliptic::curves::traits::ECScalar; - let witness: P::Scalar = ECScalar::new_random(); - let dlog_proof = DLogProof::

::prove(&witness); + let witness = Scalar::random(); + let dlog_proof = DLogProof::::prove(&witness); assert!(DLogProof::verify(&dlog_proof).is_ok()); } fn main() { let curve_name = std::env::args().nth(1); match curve_name.as_deref() { - Some("secp256k1") => dlog_proof::(), - Some("ristretto") => dlog_proof::(), - Some("ed25519") => dlog_proof::(), - Some("bls12_381") => dlog_proof::(), - Some("p256") => dlog_proof::(), + Some("secp256k1") => dlog_proof::(), + Some("ristretto") => dlog_proof::(), + Some("ed25519") => dlog_proof::(), + Some("bls12_381_1") => dlog_proof::(), + Some("bls12_381_2") => dlog_proof::(), + Some("p256") => dlog_proof::(), Some(unknown_curve) => eprintln!("Unknown curve: {}", unknown_curve), None => eprintln!("Missing curve name"), } diff --git a/examples/verifiable_secret_sharing.rs b/examples/verifiable_secret_sharing.rs index 870770e5..8eed3f2b 100644 --- a/examples/verifiable_secret_sharing.rs +++ b/examples/verifiable_secret_sharing.rs @@ -1,6 +1,4 @@ -use std::fmt::Debug; - -use curv::elliptic::curves::traits::ECPoint; +use curv::elliptic::curves::*; /// secret_sharing_3_out_of_5 /// Feldman VSS, based on Paul Feldman. 1987. A practical scheme for non-interactive verifiable secret sharing. @@ -13,17 +11,12 @@ use curv::elliptic::curves::traits::ECPoint; /// CURVE_NAME is any of the supported curves: i.e.: /// cargo run --example verifiable_secret_sharing -- ed25519 -pub fn secret_sharing_3_out_of_5

() -where - P: ECPoint + Clone, - P::Scalar: PartialEq + Clone + Debug, -{ +pub fn secret_sharing_3_out_of_5() { use curv::cryptographic_primitives::secret_sharing::feldman_vss::VerifiableSS; - use curv::elliptic::curves::traits::ECScalar; - let secret: P::Scalar = ECScalar::new_random(); + let secret = Scalar::random(); - let (vss_scheme, secret_shares) = VerifiableSS::

::share(3, 5, &secret); + let (vss_scheme, secret_shares) = VerifiableSS::::share(3, 5, &secret); let shares_vec = vec![ secret_shares[0].clone(), @@ -42,18 +35,18 @@ where assert!(valid3.is_ok()); assert!(valid1.is_ok()); - let g: P = ECPoint::generator(); - let share1_public = g * secret_shares[0].clone(); + let g = Point::generator(); + let share1_public = g * &secret_shares[0]; let valid1_public = vss_scheme.validate_share_public(&share1_public, 1); assert!(valid1_public.is_ok()); // test map (t,n) - (t',t') let s = &vec![0, 1, 2, 3, 4]; - let l0 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 0, &s); - let l1 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 1, &s); - let l2 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 2, &s); - let l3 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 3, &s); - let l4 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 4, &s); + let l0 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 0, &s); + let l1 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 1, &s); + let l2 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 2, &s); + let l3 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 3, &s); + let l4 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 4, &s); let w = l0 * secret_shares[0].clone() + l1 * secret_shares[1].clone() @@ -66,15 +59,12 @@ where fn main() { let curve_name = std::env::args().nth(1); match curve_name.as_deref() { - Some("secp256k1") => secret_sharing_3_out_of_5::(), - Some("ristretto") => { - secret_sharing_3_out_of_5::() - } - Some("ed25519") => secret_sharing_3_out_of_5::(), - Some("bls12_381") => { - secret_sharing_3_out_of_5::() - } - Some("p256") => secret_sharing_3_out_of_5::(), + Some("secp256k1") => secret_sharing_3_out_of_5::(), + Some("ristretto") => secret_sharing_3_out_of_5::(), + Some("ed25519") => secret_sharing_3_out_of_5::(), + Some("bls12_381_1") => secret_sharing_3_out_of_5::(), + Some("bls12_381_2") => secret_sharing_3_out_of_5::(), + Some("p256") => secret_sharing_3_out_of_5::(), Some(unknown_curve) => eprintln!("Unknown curve: {}", unknown_curve), None => eprintln!("Missing curve name"), } diff --git a/src/arithmetic/big_gmp.rs b/src/arithmetic/big_gmp.rs index 4526174f..1f90af67 100644 --- a/src/arithmetic/big_gmp.rs +++ b/src/arithmetic/big_gmp.rs @@ -389,6 +389,12 @@ impl ring_algorithm::RingNormalize for BigInt { crate::__bigint_impl_from! { u32, i32, u64 } +impl From for BigInt { + fn from(n: u16) -> Self { + BigInt::from(u64::from(n)) + } +} + /// Internal helper trait. Creates short-hand for wrapping Mpz into BigInt. trait Wrap { fn wrap(self) -> BigInt; diff --git a/src/arithmetic/big_native.rs b/src/arithmetic/big_native.rs index 8ccfd1d8..48706e2d 100644 --- a/src/arithmetic/big_native.rs +++ b/src/arithmetic/big_native.rs @@ -99,6 +99,12 @@ impl num_traits::Num for BigInt { crate::__bigint_impl_from! { u32, i32, u64 } +impl From for BigInt { + fn from(n: u16) -> Self { + BigInt::from(u64::from(n)) + } +} + impl BasicOps for BigInt { fn pow(&self, exponent: u32) -> Self { self.num.pow(exponent).wrap() diff --git a/src/arithmetic/mod.rs b/src/arithmetic/mod.rs index dbda76a7..fb254ea7 100644 --- a/src/arithmetic/mod.rs +++ b/src/arithmetic/mod.rs @@ -356,7 +356,7 @@ mod test { // Conversion traits for<'a> u64: std::convert::TryFrom<&'a BigInt>, for<'a> i64: std::convert::TryFrom<&'a BigInt>, - BigInt: From + From + From, + BigInt: From + From + From + From, // STD Operators BigInt: Add + Sub diff --git a/src/arithmetic/samplable.rs b/src/arithmetic/samplable.rs index 1b069b3a..8be7a47c 100644 --- a/src/arithmetic/samplable.rs +++ b/src/arithmetic/samplable.rs @@ -35,7 +35,7 @@ impl Samplable for BigInt { if bit_size == 0 { return BigInt::zero(); } - let mut rng = OsRng::new().unwrap(); + let mut rng = OsRng; let bytes = (bit_size - 1) / 8 + 1; let mut buf: Vec = vec![0; bytes]; rng.fill_bytes(&mut buf); diff --git a/src/arithmetic/traits.rs b/src/arithmetic/traits.rs index 544bc1c6..b4252281 100644 --- a/src/arithmetic/traits.rs +++ b/src/arithmetic/traits.rs @@ -48,6 +48,29 @@ pub trait Converter: Sized { /// ``` fn from_bytes(bytes: &[u8]) -> Self; + /// Returns bytes representation of the number in an array with length chosen by the user + /// if the array is larger than the bytes it pads it with zeros in the most significant bytes + /// If the array is too small for the integer it returns None. + /// + /// ## Examples + /// ``` + /// # use curv::arithmetic::{BigInt, Converter}; + /// assert_eq!(BigInt::from(31).to_bytes_array(), Some([31])); + /// assert_eq!(BigInt::from(31).to_bytes_array(), Some([0, 31])); + /// assert_eq!(BigInt::from(1_000_000).to_bytes_array(), Some([15, 66, 64])); + /// assert_eq!(BigInt::from(1_000_000).to_bytes_array::<2>(), None); + /// assert_eq!(BigInt::from(1_000_000).to_bytes_array(), Some([0, 15, 66, 64])); + /// ``` + fn to_bytes_array(&self) -> Option<[u8; N]> { + let bytes = self.to_bytes(); + if bytes.len() > N { + return None; + } + let mut array = [0u8; N]; + array[N - bytes.len()..].copy_from_slice(&bytes); + Some(array) + } + /// Converts BigInt to hex representation. /// /// If the number is negative, it will be serialized by absolute value, and minus character @@ -160,7 +183,7 @@ pub trait Samplable { pub trait NumberTests { /// Returns `true` if `n` is zero /// - /// Alternatively, [BasicOps::sign] method can be used to check sign of the number. + /// Alternatively, [BasicOps::sign] method can be used to check sign of the number. fn is_zero(n: &Self) -> bool; /// Returns `true` if `n` is negative /// diff --git a/src/cryptographic_primitives/commitments/pedersen_commitment.rs b/src/cryptographic_primitives/commitments/pedersen_commitment.rs index b48feb20..35ce96ff 100644 --- a/src/cryptographic_primitives/commitments/pedersen_commitment.rs +++ b/src/cryptographic_primitives/commitments/pedersen_commitment.rs @@ -11,33 +11,30 @@ use super::traits::Commitment; use super::SECURITY_BITS; use crate::arithmetic::traits::*; -use crate::elliptic::curves::traits::*; +use crate::elliptic::curves::{Curve, Point, Scalar}; use crate::BigInt; /// compute c = mG + rH /// where m is the commited value, G is the group generator, /// H is a random point and r is a blinding value. /// -pub struct PedersenCommitment

(PhantomData

); +pub struct PedersenCommitment(PhantomData); -impl

Commitment

for PedersenCommitment

-where - P: ECPoint, -{ +impl Commitment> for PedersenCommitment { fn create_commitment_with_user_defined_randomness( message: &BigInt, blinding_factor: &BigInt, - ) -> P { - let g: P = ECPoint::generator(); - let h = P::base_point2(); - let message_scalar: P::Scalar = ECScalar::from(message); - let blinding_scalar: P::Scalar = ECScalar::from(blinding_factor); + ) -> Point { + let g = Point::generator(); + let h = Point::base_point2(); + let message_scalar: Scalar = Scalar::from(message); + let blinding_scalar: Scalar = Scalar::from(blinding_factor); let mg = g * message_scalar; let rh = h * blinding_scalar; mg + rh } - fn create_commitment(message: &BigInt) -> (P, BigInt) { + fn create_commitment(message: &BigInt) -> (Point, BigInt) { let blinding_factor = BigInt::sample(SECURITY_BITS); let com = PedersenCommitment::create_commitment_with_user_defined_randomness( message, diff --git a/src/cryptographic_primitives/hashing/blake2b512.rs b/src/cryptographic_primitives/hashing/blake2b512.rs index 3b273858..9b0feae1 100644 --- a/src/cryptographic_primitives/hashing/blake2b512.rs +++ b/src/cryptographic_primitives/hashing/blake2b512.rs @@ -4,17 +4,50 @@ (https://github.com/KZen-networks/curv) License MIT: https://github.com/KZen-networks/curv/blob/master/LICENSE */ +use blake2b_simd::{Params, State}; + use crate::arithmetic::traits::*; -use crate::elliptic::curves::traits::{ECPoint, ECScalar}; +use crate::elliptic::curves::{Curve, Point, Scalar}; use crate::BigInt; -use blake2b_simd::Params; -pub struct Blake; +/// Wrapper over [blake2b_simd](blake2b_simd::State) exposing facilities to hash bigints, elliptic points, +/// and scalars +pub struct Blake { + state: State, +} impl Blake { + pub fn with_personal(persona: &[u8]) -> Self { + Self { + state: Params::new().hash_length(64).personal(persona).to_state(), + } + } + + pub fn chain_bigint(&mut self, n: &BigInt) -> &mut Self { + self.state.update(&n.to_bytes()); + self + } + + pub fn chain_point(&mut self, point: &Point) -> &mut Self { + self.state.update(&point.to_bytes(false)); + self + } + + pub fn result_bigint(&self) -> BigInt { + BigInt::from_bytes(self.state.finalize().as_ref()) + } + + pub fn result_scalar(&self) -> Scalar { + let n = self.result_bigint(); + Scalar::from_bigint(&n) + } + + #[deprecated( + since = "0.8.0", + note = "Blake API has been changed, this method is outdated" + )] pub fn create_hash(big_ints: &[&BigInt], persona: &[u8]) -> BigInt { let mut digest = Params::new().hash_length(64).personal(persona).to_state(); - // let mut digest = Blake2b::with_params(64, &[], &[], persona); for value in big_ints { digest.update(&BigInt::to_bytes(value)); } @@ -22,16 +55,20 @@ impl Blake { BigInt::from_bytes(digest.finalize().as_ref()) } - pub fn create_hash_from_ge(ge_vec: &[&P], persona: &[u8]) -> P::Scalar { + #[deprecated( + since = "0.8.0", + note = "Blake API has been changed, this method is outdated" + )] + pub fn create_hash_from_ge(ge_vec: &[&Point], persona: &[u8]) -> Scalar { let mut digest = Params::new().hash_length(64).personal(persona).to_state(); // let mut digest = Blake2b::with_params(64, &[], &[], persona); for value in ge_vec { - digest.update(&value.pk_to_key_slice()); + digest.update(&value.to_bytes(false)); } let result = BigInt::from_bytes(digest.finalize().as_ref()); - ECScalar::from(&result) + Scalar::from(&result) } } @@ -39,29 +76,58 @@ impl Blake { mod tests { use super::Blake; use crate::arithmetic::traits::*; - use crate::elliptic::curves::traits::{ECPoint, ECScalar}; + use crate::elliptic::curves::{Curve, Point}; use crate::BigInt; #[test] // Very basic test here, TODO: suggest better testing - fn create_hash_test() { + fn create_hash_test_legacy() { + #![allow(deprecated)] let result = Blake::create_hash(&[&BigInt::one(), &BigInt::zero()], b"Zcash_RedJubjubH"); assert!(result > BigInt::zero()); } + #[test] + // Very basic test here, TODO: suggest better testing + fn create_hash_test() { + let result = Blake::with_personal(b"Zcash_RedJubjubH") + .chain_bigint(&BigInt::one()) + .chain_bigint(&BigInt::zero()) + .result_bigint(); + assert!(result > BigInt::zero()); + } - crate::test_for_all_curves!(create_hash_from_ge_test); + crate::test_for_all_curves!(create_hash_from_ge_test_legacy); + fn create_hash_from_ge_test_legacy() { + #![allow(deprecated)] + let base_point2 = Point::::base_point2(); + let generator = Point::::generator(); + let result1 = + Blake::create_hash_from_ge::(&[base_point2, &generator], b"Zcash_RedJubjubH"); + assert!(result1.to_bigint().bit_length() > 240); + let result2 = Blake::create_hash_from_ge(&[&generator, base_point2], b"Zcash_RedJubjubH"); + assert_ne!(result1, result2); + let result3 = Blake::create_hash_from_ge(&[&generator, base_point2], b"Zcash_RedJubjubH"); + assert_eq!(result2, result3); + } - fn create_hash_from_ge_test

() - where - P: ECPoint, - P::Scalar: PartialEq + std::fmt::Debug, - { - let point = P::base_point2(); - let result1 = Blake::create_hash_from_ge(&[&point, &P::generator()], b"Zcash_RedJubjubH"); - assert!(result1.to_big_int().bit_length() > 240); - let result2 = Blake::create_hash_from_ge(&[&P::generator(), &point], b"Zcash_RedJubjubH"); + crate::test_for_all_curves!(create_hash_from_ge_test); + fn create_hash_from_ge_test() { + let base_point2 = Point::::base_point2(); + let generator = Point::::generator(); + let result1 = Blake::with_personal(b"Zcash_RedJubjubH") + .chain_point(base_point2) + .chain_point(&generator) + .result_scalar::(); + assert!(result1.to_bigint().bit_length() > 240); + let result2 = Blake::with_personal(b"Zcash_RedJubjubH") + .chain_point(&generator) + .chain_point(base_point2) + .result_scalar::(); assert_ne!(result1, result2); - let result3 = Blake::create_hash_from_ge(&[&P::generator(), &point], b"Zcash_RedJubjubH"); + let result3 = Blake::with_personal(b"Zcash_RedJubjubH") + .chain_point(&generator) + .chain_point(base_point2) + .result_scalar::(); assert_eq!(result2, result3); } } diff --git a/src/cryptographic_primitives/hashing/ext.rs b/src/cryptographic_primitives/hashing/ext.rs new file mode 100644 index 00000000..27c8efca --- /dev/null +++ b/src/cryptographic_primitives/hashing/ext.rs @@ -0,0 +1,327 @@ +use digest::Digest; +use hmac::crypto_mac::MacError; +use hmac::{Hmac, Mac}; + +use crate::arithmetic::*; +use crate::elliptic::curves::{Curve, Point, Scalar}; + +/// [Digest] extension allowing to hash elliptic points, scalars, and bigints +/// +/// Can be used with any hashing algorithm that implements `Digest` traits (e.g. [Sha256](sha2::Sha256), +/// [Sha512](sha2::Sha512), etc.) +/// +/// ## Example +/// +/// ```rust +/// use sha2::Sha256; +/// use curv::arithmetic::*; +/// use curv::cryptographic_primitives::hashing::{Digest, DigestExt}; +/// use curv::elliptic::curves::{Secp256k1, Point}; +/// +/// let hash = Sha256::new() +/// .chain_point(&Point::::generator()) +/// .chain_point(Point::::base_point2()) +/// .chain_bigint(&BigInt::from(10)) +/// .result_bigint(); +/// +/// assert_eq!(hash, BigInt::from_hex("73764f937fbe25092466b417fa66ad9c62607865e1f8151df253aa3a2fd7599b").unwrap()); +/// ``` +pub trait DigestExt { + fn input_bigint(&mut self, n: &BigInt); + fn input_point(&mut self, point: &Point); + fn input_scalar(&mut self, scalar: &Scalar); + + fn chain_bigint(mut self, n: &BigInt) -> Self + where + Self: Sized, + { + self.input_bigint(n); + self + } + fn chain_point(mut self, point: &Point) -> Self + where + Self: Sized, + { + self.input_point(point); + self + } + fn chain_points<'p, E: Curve>(mut self, points: impl IntoIterator>) -> Self + where + Self: Sized, + { + for point in points { + self.input_point(point) + } + self + } + fn chain_scalar(mut self, scalar: &Scalar) -> Self + where + Self: Sized, + { + self.input_scalar(scalar); + self + } + fn chain_scalars<'s, E: Curve>( + mut self, + scalars: impl IntoIterator>, + ) -> Self + where + Self: Sized, + { + for scalar in scalars { + self.input_scalar(scalar) + } + self + } + + fn result_bigint(self) -> BigInt; + fn result_scalar(self) -> Scalar; + + fn digest_bigint(bytes: &[u8]) -> BigInt; +} + +impl DigestExt for D +where + D: Digest, +{ + fn input_bigint(&mut self, n: &BigInt) { + self.input(&n.to_bytes()) + } + + fn input_point(&mut self, point: &Point) { + self.input(&point.to_bytes(false)[..]) + } + + fn input_scalar(&mut self, scalar: &Scalar) { + self.input(&scalar.to_bigint().to_bytes()) + } + + fn result_bigint(self) -> BigInt { + let result = self.result(); + BigInt::from_bytes(&result) + } + + fn result_scalar(self) -> Scalar { + let n = self.result_bigint(); + Scalar::from_bigint(&n) + } + + fn digest_bigint(bytes: &[u8]) -> BigInt { + Self::new().chain(bytes).result_bigint() + } +} + +/// [Hmac] extension allowing to use bigints to instantiate hmac, update, and finalize it. +pub trait HmacExt: Sized { + fn new_bigint(key: &BigInt) -> Self; + + fn input_bigint(&mut self, n: &BigInt); + + fn chain_bigint(mut self, n: &BigInt) -> Self + where + Self: Sized, + { + self.input_bigint(n); + self + } + + fn result_bigint(self) -> BigInt; + fn verify_bigint(self, code: &BigInt) -> Result<(), MacError>; +} + +impl HmacExt for Hmac +where + D: digest::Input + digest::BlockInput + digest::FixedOutput + digest::Reset + Default + Clone, +{ + fn new_bigint(key: &BigInt) -> Self { + let bytes = key.to_bytes(); + Self::new_varkey(&bytes).expect("HMAC must take a key of any length") + } + + fn input_bigint(&mut self, n: &BigInt) { + self.input(&n.to_bytes()) + } + + fn result_bigint(self) -> BigInt { + BigInt::from_bytes(&self.result().code()) + } + + fn verify_bigint(self, code: &BigInt) -> Result<(), MacError> { + self.verify(&code.to_bytes()) + } +} + +#[cfg(test)] +mod test { + use sha2::{Sha256, Sha512}; + + use super::*; + + // Test Vectors taken from: + // https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs + #[test] + fn vector_sha256_test() { + // Empty Message + let result: BigInt = Sha256::new().result_bigint(); + assert_eq!( + result.to_hex(), + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ); + + // 256 bit message + let result: BigInt = Sha256::new() + .chain_bigint( + &BigInt::from_hex( + "09fc1accc230a205e4a208e64a8f204291f581a12756392da4b8c0cf5ef02b95", + ) + .unwrap(), + ) + .result_bigint(); + assert_eq!( + result.to_hex(), + "4f44c1c7fbebb6f9601829f3897bfd650c56fa07844be76489076356ac1886a4" + ); + + // 2x128 bit messages + let result: BigInt = Sha256::new() + .chain_bigint(&BigInt::from_hex("09fc1accc230a205e4a208e64a8f2042").unwrap()) + .chain_bigint(&BigInt::from_hex("91f581a12756392da4b8c0cf5ef02b95").unwrap()) + .result_bigint(); + assert_eq!( + result.to_hex(), + "4f44c1c7fbebb6f9601829f3897bfd650c56fa07844be76489076356ac1886a4" + ); + + // 512 bit message + let result: BigInt = Sha256::new() + .chain_bigint(&BigInt::from_hex("5a86b737eaea8ee976a0a24da63e7ed7eefad18a101c1211e2b3650c5187c2a8a650547208251f6d4237e661c7bf4c77f335390394c37fa1a9f9be836ac28509").unwrap()) + .result_bigint(); + assert_eq!( + result.to_hex(), + "42e61e174fbb3897d6dd6cef3dd2802fe67b331953b06114a65c772859dfc1aa" + ); + } + + #[test] + // Test Vectors taken from: + // https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs + fn vector_sha512_test() { + // Empty message + let result: BigInt = Sha512::new().result_bigint(); + assert_eq!( + result.to_hex(), + "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" + ); + + // 2x256 bit message + let result: BigInt = Sha512::new() + .chain_bigint( + &BigInt::from_hex( + "c1ca70ae1279ba0b918157558b4920d6b7fba8a06be515170f202fafd36fb7f7", + ) + .unwrap(), + ) + .chain_bigint( + &BigInt::from_hex( + "9d69fad745dba6150568db1e2b728504113eeac34f527fc82f2200b462ecbf5d", + ) + .unwrap(), + ) + .result_bigint(); + assert_eq!( + result.to_hex(), + "46e46623912b3932b8d662ab42583423843206301b58bf20ab6d76fd47f1cbbcf421df536ecd7e56db5354e7e0f98822d2129c197f6f0f222b8ec5231f3967d" + ); + + // 512 bit message + let result: BigInt = Sha512::new() + .chain_bigint(&BigInt::from_hex( + "c1ca70ae1279ba0b918157558b4920d6b7fba8a06be515170f202fafd36fb7f79d69fad745dba6150568db1e2b728504113eeac34f527fc82f2200b462ecbf5d").unwrap()) + .result_bigint(); + assert_eq!( + result.to_hex(), + "46e46623912b3932b8d662ab42583423843206301b58bf20ab6d76fd47f1cbbcf421df536ecd7e56db5354e7e0f98822d2129c197f6f0f222b8ec5231f3967d" + ); + + // 1024 bit message + let result: BigInt = Sha512::new() + .chain_bigint(&BigInt::from_hex("fd2203e467574e834ab07c9097ae164532f24be1eb5d88f1af7748ceff0d2c67a21f4e4097f9d3bb4e9fbf97186e0db6db0100230a52b453d421f8ab9c9a6043aa3295ea20d2f06a2f37470d8a99075f1b8a8336f6228cf08b5942fc1fb4299c7d2480e8e82bce175540bdfad7752bc95b577f229515394f3ae5cec870a4b2f8").unwrap()) + .result_bigint(); + assert_eq!( + result.to_hex(), + "a21b1077d52b27ac545af63b32746c6e3c51cb0cb9f281eb9f3580a6d4996d5c9917d2a6e484627a9d5a06fa1b25327a9d710e027387fc3e07d7c4d14c6086cc" + ); + } + + crate::test_for_all_curves!(create_sha512_from_ge_test); + fn create_sha256_from_ge_test() { + let generator = Point::::generator(); + let base_point2 = Point::::base_point2(); + let result1 = Sha256::new() + .chain_point(&generator) + .chain_point(base_point2) + .result_scalar::(); + assert!(result1.to_bigint().bit_length() > 240); + let result2 = Sha256::new() + .chain_point(base_point2) + .chain_point(&generator) + .result_scalar::(); + assert_ne!(result1, result2); + let result3 = Sha256::new() + .chain_point(base_point2) + .chain_point(&generator) + .result_scalar::(); + assert_eq!(result2, result3); + } + + crate::test_for_all_curves!(create_sha256_from_ge_test); + fn create_sha512_from_ge_test() { + let generator = Point::::generator(); + let base_point2 = Point::::base_point2(); + let result1 = Sha512::new() + .chain_point(&generator) + .chain_point(base_point2) + .result_scalar::(); + assert!(result1.to_bigint().bit_length() > 240); + let result2 = Sha512::new() + .chain_point(base_point2) + .chain_point(&generator) + .result_scalar::(); + assert_ne!(result1, result2); + let result3 = Sha512::new() + .chain_point(base_point2) + .chain_point(&generator) + .result_scalar::(); + assert_eq!(result2, result3); + } + + #[test] + fn create_hmac_test() { + let key = BigInt::sample(512); + let result1 = Hmac::::new_bigint(&key) + .chain_bigint(&BigInt::from(10)) + .result_bigint(); + assert!(Hmac::::new_bigint(&key) + .chain_bigint(&BigInt::from(10)) + .verify_bigint(&result1) + .is_ok()); + + let key2 = BigInt::sample(512); + // same data , different key + let result2 = Hmac::::new_bigint(&key2) + .chain_bigint(&BigInt::from(10)) + .result_bigint(); + assert_ne!(result1, result2); + // same key , different data + let result3 = Hmac::::new_bigint(&key) + .chain_bigint(&BigInt::from(10)) + .chain_bigint(&BigInt::from(11)) + .result_bigint(); + assert_ne!(result1, result3); + // same key, same data + let result4 = Hmac::::new_bigint(&key) + .chain_bigint(&BigInt::from(10)) + .result_bigint(); + assert_eq!(result1, result4) + } +} diff --git a/src/cryptographic_primitives/hashing/hash_sha256.rs b/src/cryptographic_primitives/hashing/hash_sha256.rs index 09ebba36..a592b713 100644 --- a/src/cryptographic_primitives/hashing/hash_sha256.rs +++ b/src/cryptographic_primitives/hashing/hash_sha256.rs @@ -5,14 +5,18 @@ License MIT: https://github.com/KZen-networks/curv/blob/master/LICENSE */ +#![allow(deprecated)] + use super::traits::Hash; use crate::arithmetic::traits::*; -use crate::elliptic::curves::traits::{ECPoint, ECScalar}; +use crate::elliptic::curves::{Curve, Point, Scalar}; use digest::Digest; use sha2::Sha256; use crate::BigInt; + +#[deprecated(since = "0.8.0", note = "use DigestExt instead")] pub struct HSha256; impl Hash for HSha256 { @@ -27,15 +31,15 @@ impl Hash for HSha256 { BigInt::from_bytes(&result_hex[..]) } - fn create_hash_from_ge(ge_vec: &[&P]) -> P::Scalar { + fn create_hash_from_ge(ge_vec: &[&Point]) -> Scalar { let mut hasher = Sha256::new(); for value in ge_vec { - hasher.input(&value.pk_to_key_slice()); + hasher.input(&value.to_bytes(false)[..]); } let result_hex = hasher.result(); let result = BigInt::from_bytes(&result_hex[..]); - ECScalar::from(&result) + Scalar::from(&result) } fn create_hash_from_slice(byte_slice: &[u8]) -> BigInt { @@ -51,8 +55,7 @@ mod tests { use super::HSha256; use super::Hash; use crate::arithmetic::traits::*; - use crate::elliptic::curves::traits::ECPoint; - use crate::elliptic::curves::traits::ECScalar; + use crate::elliptic::curves::{Curve, Point}; use crate::BigInt; use sha2::Digest; use sha2::Sha256; @@ -111,17 +114,14 @@ mod tests { crate::test_for_all_curves!(create_sha256_from_ge_test); - fn create_sha256_from_ge_test

() - where - P: ECPoint, - P::Scalar: PartialEq + std::fmt::Debug, - { - let point = P::base_point2(); - let result1 = HSha256::create_hash_from_ge(&[&point, &P::generator()]); - assert!(result1.to_big_int().bit_length() > 240); - let result2 = HSha256::create_hash_from_ge(&[&P::generator(), &point]); + fn create_sha256_from_ge_test() { + let generator = Point::::generator(); + let base_point2 = Point::::base_point2(); + let result1 = HSha256::create_hash_from_ge::(&[base_point2, &generator]); + assert!(result1.to_bigint().bit_length() > 240); + let result2 = HSha256::create_hash_from_ge(&[&generator, base_point2]); assert_ne!(result1, result2); - let result3 = HSha256::create_hash_from_ge(&[&P::generator(), &point]); + let result3 = HSha256::create_hash_from_ge(&[&generator, base_point2]); assert_eq!(result2, result3); } } diff --git a/src/cryptographic_primitives/hashing/hash_sha512.rs b/src/cryptographic_primitives/hashing/hash_sha512.rs index bb6d3321..cdceda0a 100644 --- a/src/cryptographic_primitives/hashing/hash_sha512.rs +++ b/src/cryptographic_primitives/hashing/hash_sha512.rs @@ -5,15 +5,18 @@ License MIT: https://github.com/KZen-networks/curv/blob/master/LICENSE */ +#![allow(deprecated)] + use super::traits::Hash; use crate::arithmetic::traits::*; -use crate::elliptic::curves::traits::{ECPoint, ECScalar}; +use crate::elliptic::curves::{Curve, Point, Scalar}; use digest::Digest; use sha2::Sha512; use crate::BigInt; +#[deprecated(since = "0.8.0", note = "use DigestExt instead")] pub struct HSha512; impl Hash for HSha512 { @@ -28,15 +31,15 @@ impl Hash for HSha512 { BigInt::from_bytes(&result_hex[..]) } - fn create_hash_from_ge(ge_vec: &[&P]) -> P::Scalar { + fn create_hash_from_ge(ge_vec: &[&Point]) -> Scalar { let mut hasher = Sha512::new(); for value in ge_vec { - hasher.input(&value.pk_to_key_slice()); + hasher.input(&value.to_bytes(false)[..]); } let result_hex = hasher.result(); let result = BigInt::from_bytes(&result_hex[..]); - ECScalar::from(&result) + Scalar::from(&result) } fn create_hash_from_slice(byte_slice: &[u8]) -> BigInt { @@ -49,11 +52,11 @@ impl Hash for HSha512 { #[cfg(test)] mod tests { + use crate::arithmetic::*; + use crate::elliptic::curves::{Curve, Point}; + use super::HSha512; use super::Hash; - use crate::arithmetic::traits::*; - use crate::elliptic::curves::traits::{ECPoint, ECScalar}; - use crate::BigInt; #[test] // Test Vectors taken from: @@ -99,17 +102,14 @@ mod tests { crate::test_for_all_curves!(create_sha512_from_ge_test); - fn create_sha512_from_ge_test

() - where - P: ECPoint, - P::Scalar: PartialEq + std::fmt::Debug, - { - let point = P::base_point2(); - let result1 = HSha512::create_hash_from_ge(&[&point, &P::generator()]); - assert!(result1.to_big_int().bit_length() > 240); - let result2 = HSha512::create_hash_from_ge(&[&P::generator(), &point]); + fn create_sha512_from_ge_test() { + let generator = Point::::generator(); + let base_point2 = Point::::base_point2(); + let result1 = HSha512::create_hash_from_ge::(&[base_point2, &generator]); + assert!(result1.to_bigint().bit_length() > 240); + let result2 = HSha512::create_hash_from_ge(&[&generator, base_point2]); assert_ne!(result1, result2); - let result3 = HSha512::create_hash_from_ge(&[&P::generator(), &point]); + let result3 = HSha512::create_hash_from_ge(&[&generator, base_point2]); assert_eq!(result2, result3); } } diff --git a/src/cryptographic_primitives/hashing/hmac_sha512.rs b/src/cryptographic_primitives/hashing/hmac_sha512.rs index 16c2fd90..5064c65e 100644 --- a/src/cryptographic_primitives/hashing/hmac_sha512.rs +++ b/src/cryptographic_primitives/hashing/hmac_sha512.rs @@ -5,6 +5,8 @@ License MIT: https://github.com/KZen-networks/curv/blob/master/LICENSE */ +#![allow(deprecated)] + use crate::BigInt; use super::traits::KeyedHash; @@ -15,6 +17,7 @@ use sha2::Sha512; use zeroize::Zeroize; type HmacSha256type = Hmac; +#[deprecated(since = "0.8.0", note = "use HmacExt instead")] pub struct HMacSha512; impl KeyedHash for HMacSha512 { @@ -49,7 +52,6 @@ impl KeyedHash for HMacSha512 { #[cfg(test)] mod tests { - use super::HMacSha512; use crate::arithmetic::traits::*; use crate::cryptographic_primitives::hashing::traits::KeyedHash; diff --git a/src/cryptographic_primitives/hashing/merkle_tree.rs b/src/cryptographic_primitives/hashing/merkle_tree.rs index 3456a181..b5950788 100644 --- a/src/cryptographic_primitives/hashing/merkle_tree.rs +++ b/src/cryptographic_primitives/hashing/merkle_tree.rs @@ -12,26 +12,26 @@ use std::marker::PhantomData; use crypto::sha3::Sha3; use merkle::{MerkleTree, Proof}; -use crate::elliptic::curves::traits::ECPoint; +use crate::elliptic::curves::{Curve, Point}; /* pub struct MT256<'a> { tree: MerkleTree, root: & 'a Vec, } */ -pub struct MT256

{ +pub struct MT256 { tree: MerkleTree<[u8; 32]>, - _ph: PhantomData

, + _ph: PhantomData, } //impl <'a> MT256<'a>{ -impl MT256

{ - pub fn create_tree(vec: &[P]) -> MT256

{ +impl MT256 { + pub fn create_tree(vec: &[Point]) -> MT256 { let digest = Sha3::keccak256(); - let mut array = [0u8; 32]; let vec_bytes = (0..vec.len()) .map(|i| { - let bytes = vec[i].pk_to_key_slice(); + let mut array = [0u8; 32]; + let bytes = vec[i].to_bytes(false); array.copy_from_slice(&bytes[0..32]); array }) @@ -44,9 +44,9 @@ impl MT256

{ } } - pub fn gen_proof_for_ge(&self, value: &P) -> Proof<[u8; 32]> { + pub fn gen_proof_for_ge(&self, value: &Point) -> Proof<[u8; 32]> { let mut array = [0u8; 32]; - let pk_slice = value.pk_to_key_slice(); + let pk_slice = value.to_bytes(false); array.copy_from_slice(&pk_slice[0..32]); MerkleTree::gen_proof::<[u8; 32]>(&self.tree, array).expect("not found in tree") } @@ -68,38 +68,36 @@ impl MT256

{ #[cfg(test)] mod tests { use super::MT256; - use crate::elliptic::curves::traits::ECPoint; + use crate::elliptic::curves::{Curve, Point}; use crate::test_for_all_curves; test_for_all_curves!(test_mt_functionality_four_leaves); - fn test_mt_functionality_four_leaves() { - let ge1: P = ECPoint::generator(); - let ge2: P = ECPoint::generator(); - let ge3: P = ge1.add_point(&ge2.get_element()); - let ge4: P = ge1.add_point(&ge3.get_element()); - let ge_vec = vec![ge1, ge2, ge3, ge4]; + fn test_mt_functionality_four_leaves() { + let ge1: Point = Point::generator().to_point(); + let ge2: Point = ge1.clone(); + let ge3: Point = &ge1 + &ge2; + let ge4: Point = &ge1 + &ge3; + let ge_vec = vec![ge1.clone(), ge2, ge3, ge4]; let mt256 = MT256::create_tree(&ge_vec); - let ge1: P = ECPoint::generator(); let proof1 = mt256.gen_proof_for_ge(&ge1); let root = mt256.get_root(); - let valid_proof = MT256::

::validate_proof(&proof1, root).is_ok(); + let valid_proof = MT256::::validate_proof(&proof1, root).is_ok(); assert!(valid_proof); } test_for_all_curves!(test_mt_functionality_three_leaves); - fn test_mt_functionality_three_leaves() { - let ge1: P = ECPoint::generator(); - let ge2: P = ECPoint::generator(); - let ge3: P = ge1.add_point(&ge2.get_element()); + fn test_mt_functionality_three_leaves() { + let ge1: Point = Point::generator().to_point(); + let ge2: Point = ge1.clone(); + let ge3: Point = &ge1 + &ge2; - let ge_vec = vec![ge1, ge2, ge3]; + let ge_vec = vec![ge1.clone(), ge2, ge3]; let mt256 = MT256::create_tree(&ge_vec); - let ge1: P = ECPoint::generator(); let proof1 = mt256.gen_proof_for_ge(&ge1); let root = mt256.get_root(); - assert!(MT256::

::validate_proof(&proof1, root).is_ok()); + assert!(MT256::::validate_proof(&proof1, root).is_ok()); } } diff --git a/src/cryptographic_primitives/hashing/mod.rs b/src/cryptographic_primitives/hashing/mod.rs index a7943430..9de65bc0 100644 --- a/src/cryptographic_primitives/hashing/mod.rs +++ b/src/cryptographic_primitives/hashing/mod.rs @@ -10,3 +10,7 @@ pub mod hash_sha512; pub mod hmac_sha512; pub mod merkle_tree; pub mod traits; + +mod ext; +pub use digest::Digest; +pub use ext::*; diff --git a/src/cryptographic_primitives/hashing/traits.rs b/src/cryptographic_primitives/hashing/traits.rs index d2d3dd1f..8892ee96 100644 --- a/src/cryptographic_primitives/hashing/traits.rs +++ b/src/cryptographic_primitives/hashing/traits.rs @@ -5,15 +5,17 @@ License MIT: https://github.com/KZen-networks/curv/blob/master/LICENSE */ -use crate::elliptic::curves::traits::ECPoint; +use crate::elliptic::curves::{Curve, Point, Scalar}; use crate::BigInt; +#[deprecated(since = "0.8.0", note = "use DigestExt instead")] pub trait Hash { fn create_hash(big_ints: &[&BigInt]) -> BigInt; fn create_hash_from_slice(byte_slice: &[u8]) -> BigInt; - fn create_hash_from_ge(ge_vec: &[&P]) -> P::Scalar; + fn create_hash_from_ge(ge_vec: &[&Point]) -> Scalar; } +#[deprecated(since = "0.8.0", note = "use HmacExt instead")] pub trait KeyedHash { fn create_hmac(key: &BigInt, data: &[&BigInt]) -> BigInt; #[allow(clippy::result_unit_err)] diff --git a/src/cryptographic_primitives/proofs/low_degree_exponent_interpolation.rs b/src/cryptographic_primitives/proofs/low_degree_exponent_interpolation.rs new file mode 100644 index 00000000..b7a0193f --- /dev/null +++ b/src/cryptographic_primitives/proofs/low_degree_exponent_interpolation.rs @@ -0,0 +1,213 @@ +use digest::Digest; + +use thiserror::Error; + +use crate::cryptographic_primitives::hashing::DigestExt; +use crate::cryptographic_primitives::proofs::ProofError; +use crate::cryptographic_primitives::secret_sharing::Polynomial; +use crate::elliptic::curves::{Curve, Point, Scalar}; + +/// The prover private polynomial +#[derive(Clone, Debug)] +pub struct LdeiWitness { + pub w: Polynomial, +} + +/// Claims that there's polynomial `w(x)` of degree `deg(w) <= degree`, and +/// `forall i. x[i] = g[i] * alpha[i]` (and the prover knows `w(x)`) +#[derive(Clone, Debug)] +pub struct LdeiStatement { + pub alpha: Vec>, + pub g: Vec>, + pub x: Vec>, + pub d: u16, +} + +impl LdeiStatement { + /// Takes [witness](LdeiWitness) (ie. secret polynomial `w(x)`), list of scalars `alpha`, + /// list of generators `g`, number `d`. Produces LdeiStatement consisting of `alpha`, `g`, `d`, + /// and list `x` such as `x_i = g_i * w(alpha_i)` + pub fn new( + witness: &LdeiWitness, + alpha: Vec>, + g: Vec>, + d: u16, + ) -> Result { + if g.len() != alpha.len() { + return Err(InvalidLdeiStatement::AlphaLengthDoesntMatchG); + } + if witness.w.degree() > d { + return Err(InvalidLdeiStatement::PolynomialDegreeMoreThanD); + } + if !ensure_list_is_pairwise_distinct(&alpha) { + return Err(InvalidLdeiStatement::AlphaNotPairwiseDistinct); + } + Ok(Self { + x: g.iter() + .zip(&alpha) + .map(|(g, a)| g * witness.w.evaluate(a)) + .collect(), + alpha, + g, + d, + }) + } +} + +#[derive(Clone, Debug)] +pub struct LdeiProof { + pub a: Vec>, + pub e: Scalar, + pub z: Polynomial, +} + +impl LdeiProof { + /// Proves correctness of [LdeiStatement] + /// + /// ## Protocol + /// + /// The prover samples `u(X) ← Z_q[X]` with `deg(u) ≤ d` and computes `a_i = g_i^u(alpha_i)` + /// for all `i ∈ [m]`, in addition to `e = H(g_1,...,g_m,x_1,...,x_m,a_1,...,a_m)`, and + /// `z(X) = u(X) − e · w(X)`. The proof is `(a_1,...,a_m,e,z)`. + #[allow(clippy::many_single_char_names)] + pub fn prove( + witness: &LdeiWitness, + statement: &LdeiStatement, + ) -> Result, InvalidLdeiStatement> + where + H: Digest, + { + if statement.alpha.len() != statement.g.len() { + return Err(InvalidLdeiStatement::AlphaLengthDoesntMatchG); + } + if witness.w.degree() > statement.d { + return Err(InvalidLdeiStatement::PolynomialDegreeMoreThanD); + } + if !ensure_list_is_pairwise_distinct(&statement.alpha) { + return Err(InvalidLdeiStatement::AlphaNotPairwiseDistinct); + } + + let x_expected: Vec> = statement + .g + .iter() + .zip(&statement.alpha) + .map(|(g, a)| g * witness.w.evaluate(a)) + .collect(); + if statement.x != x_expected { + return Err(InvalidLdeiStatement::ListOfXDoesntMatchExpectedValue); + } + + let u = Polynomial::::sample_exact(statement.d); + let a: Vec> = statement + .g + .iter() + .zip(&statement.alpha) + .map(|(g, a)| g * u.evaluate(a)) + .collect(); + + let e = H::new() + .chain_points(&statement.g) + .chain_points(&statement.x) + .chain_points(&a) + .result_scalar(); + + let z = &u - &(&witness.w * &e); + + Ok(LdeiProof { a, e, z }) + } + + /// Verifies correctness of a statement + /// + /// ## Protocol + /// + /// The verifier checks that `e = H(g1,...,gm,x1,...,xm,a1,...,am)`, that + /// `deg(z) ≤ d`, and that `a_i = g_i^z(αlpha_i) * x_i^e` for all i, and accepts if all of this is + /// true, otherwise rejects. + pub fn verify(&self, statement: &LdeiStatement) -> Result<(), ProofError> + where + H: Digest, + { + let e = H::new() + .chain_points(&statement.g) + .chain_points(&statement.x) + .chain_points(&self.a) + .result_scalar(); + + if e != self.e { + return Err(ProofError); + } + if self.z.degree() > statement.d { + return Err(ProofError); + } + + let expected_a: Vec<_> = statement + .g + .iter() + .zip(&statement.alpha) + .zip(&statement.x) + .map(|((g, a), x)| g * self.z.evaluate(&a) + x * &e) + .collect(); + + if self.a == expected_a { + Ok(()) + } else { + Err(ProofError) + } + } +} + +/// Indicates that statement is not valid or doesn't match a witness +#[derive(Debug, Error)] +pub enum InvalidLdeiStatement { + #[error("`alpha`s are not pairwise distinct")] + AlphaNotPairwiseDistinct, + #[error("alpha.len() != g.len()")] + AlphaLengthDoesntMatchG, + #[error("deg(w) > d")] + PolynomialDegreeMoreThanD, + #[error("`statement.x` doesn't match expected value")] + ListOfXDoesntMatchExpectedValue, +} + +fn ensure_list_is_pairwise_distinct(list: &[S]) -> bool { + for (i, x1) in list.iter().enumerate() { + for (j, x2) in list.iter().enumerate() { + if i != j && x1 == x2 { + return false; + } + } + } + true +} + +#[cfg(test)] +mod tests { + use std::iter; + + use sha2::Sha256; + + use crate::elliptic::curves::{Curve, Scalar}; + use crate::test_for_all_curves; + + use super::*; + + test_for_all_curves!(correctly_proofs); + fn correctly_proofs() { + let d = 5; + let poly = Polynomial::::sample_exact(5); + let witness = LdeiWitness { w: poly }; + + let alpha: Vec> = (1..=10).map(|i| Scalar::from(i)).collect(); + let g: Vec> = iter::repeat_with(Scalar::random) + .map(|x| Point::generator() * x) + .take(10) + .collect(); + + let statement = LdeiStatement::new(&witness, alpha, g, d).unwrap(); + + let proof = LdeiProof::prove::(&witness, &statement).expect("failed to prove"); + proof + .verify::(&statement) + .expect("failed to validate proof"); + } +} diff --git a/src/cryptographic_primitives/proofs/mod.rs b/src/cryptographic_primitives/proofs/mod.rs index 4fe29589..a3a8b33c 100644 --- a/src/cryptographic_primitives/proofs/mod.rs +++ b/src/cryptographic_primitives/proofs/mod.rs @@ -8,6 +8,7 @@ use std::error::Error; use std::fmt; +pub mod low_degree_exponent_interpolation; pub mod sigma_correct_homomorphic_elgamal_enc; pub mod sigma_correct_homomorphic_elgamal_encryption_of_dlog; pub mod sigma_dlog; diff --git a/src/cryptographic_primitives/proofs/sigma_correct_homomorphic_elgamal_enc.rs b/src/cryptographic_primitives/proofs/sigma_correct_homomorphic_elgamal_enc.rs index 7d2d095f..556757c0 100644 --- a/src/cryptographic_primitives/proofs/sigma_correct_homomorphic_elgamal_enc.rs +++ b/src/cryptographic_primitives/proofs/sigma_correct_homomorphic_elgamal_enc.rs @@ -7,12 +7,14 @@ */ use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; + +use digest::Digest; +use sha2::Sha256; + +use crate::cryptographic_primitives::hashing::DigestExt; +use crate::elliptic::curves::{Curve, Point, Scalar}; use super::ProofError; -use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; -use crate::cryptographic_primitives::hashing::traits::Hash; -use crate::elliptic::curves::traits::*; /// This is a proof of knowledge that a pair of group elements {D, E} /// form a valid homomorphic ElGamal encryption (”in the exponent”) using public key Y . @@ -21,67 +23,70 @@ use crate::elliptic::curves::traits::*; /// The relation R outputs 1 if D = xH+rY , E = rG (for the case of G=H this is ElGamal) /// #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct HomoELGamalProof { - pub T: P, - pub A3: P, - pub z1: P::Scalar, - pub z2: P::Scalar, +#[serde(bound = "")] +pub struct HomoELGamalProof { + pub T: Point, + pub A3: Point, + pub z1: Scalar, + pub z2: Scalar, } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct HomoElGamalWitness { - pub r: S, - pub x: S, +#[serde(bound = "")] +pub struct HomoElGamalWitness { + pub r: Scalar, + pub x: Scalar, } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct HomoElGamalStatement

{ - pub G: P, - pub H: P, - pub Y: P, - pub D: P, - pub E: P, +#[serde(bound = "")] +pub struct HomoElGamalStatement { + pub G: Point, + pub H: Point, + pub Y: Point, + pub D: Point, + pub E: Point, } -impl

HomoELGamalProof

-where - P: ECPoint + Clone + Zeroize, - P::Scalar: PartialEq + Clone + Zeroize, -{ +impl HomoELGamalProof { pub fn prove( - w: &HomoElGamalWitness, - delta: &HomoElGamalStatement

, - ) -> HomoELGamalProof

{ - let mut s1: P::Scalar = ECScalar::new_random(); - let mut s2: P::Scalar = ECScalar::new_random(); - let mut A1 = delta.H.clone() * s1.clone(); - let mut A2 = delta.Y.clone() * s2.clone(); - let A3 = delta.G.clone() * s2.clone(); - let T = A1.clone() + A2.clone(); - let e = HSha256::create_hash_from_ge(&[ - &T, &A3, &delta.G, &delta.H, &delta.Y, &delta.D, &delta.E, - ]); + w: &HomoElGamalWitness, + delta: &HomoElGamalStatement, + ) -> HomoELGamalProof { + let s1: Scalar = Scalar::random(); + let s2: Scalar = Scalar::random(); + let A1 = &delta.H * &s1; + let A2 = &delta.Y * &s2; + let A3 = &delta.G * &s2; + let T = A1 + A2; + let e = Sha256::new() + .chain_point(&T) + .chain_point(&A3) + .chain_point(&delta.G) + .chain_point(&delta.H) + .chain_point(&delta.Y) + .chain_point(&delta.D) + .chain_point(&delta.E) + .result_scalar(); // dealing with zero field element - let z1 = if w.x != P::Scalar::zero() { - s1.clone() + w.x.clone() * e.clone() - } else { - s1.clone() - }; - let z2 = s2.clone() + w.r.clone() * e; - s1.zeroize(); - s2.zeroize(); - A1.zeroize(); - A2.zeroize(); + let z1 = &s1 + &w.x * &e; + let z2 = s2 + &w.r * e; HomoELGamalProof { T, A3, z1, z2 } } - pub fn verify(&self, delta: &HomoElGamalStatement

) -> Result<(), ProofError> { - let e = HSha256::create_hash_from_ge(&[ - &self.T, &self.A3, &delta.G, &delta.H, &delta.Y, &delta.D, &delta.E, - ]); - let z1H_plus_z2Y = delta.H.clone() * self.z1.clone() + delta.Y.clone() * self.z2.clone(); - let T_plus_eD = self.T.clone() + delta.D.clone() * e.clone(); - let z2G = delta.G.clone() * self.z2.clone(); - let A3_plus_eE = self.A3.clone() + delta.E.clone() * e; + pub fn verify(&self, delta: &HomoElGamalStatement) -> Result<(), ProofError> { + let e = Sha256::new() + .chain_point(&self.T) + .chain_point(&self.A3) + .chain_point(&delta.G) + .chain_point(&delta.H) + .chain_point(&delta.Y) + .chain_point(&delta.D) + .chain_point(&delta.E) + .result_scalar(); + let z1H_plus_z2Y = &delta.H * &self.z1 + &delta.Y * &self.z2; + let T_plus_eD = &self.T + &delta.D * &e; + let z2G = &delta.G * &self.z2; + let A3_plus_eE = &self.A3 + &delta.E * &e; if z1H_plus_z2Y == T_plus_eD && z2G == A3_plus_eE { Ok(()) } else { @@ -96,45 +101,43 @@ mod tests { use crate::test_for_all_curves; test_for_all_curves!(test_correct_general_homo_elgamal); - fn test_correct_general_homo_elgamal

() - where - P: ECPoint + Clone + Zeroize, - P::Scalar: PartialEq + Clone + Zeroize, - { - let witness = HomoElGamalWitness:: { - r: ECScalar::new_random(), - x: ECScalar::new_random(), + fn test_correct_general_homo_elgamal() { + let witness = HomoElGamalWitness:: { + r: Scalar::random(), + x: Scalar::random(), + }; + let G = Point::::generator(); + let h = Scalar::random(); + let H = G * h; + let y = Scalar::random(); + let Y = G * y; + let D = &H * &witness.x + &Y * &witness.r; + let E = G * &witness.r; + let delta = HomoElGamalStatement { + G: G.to_point(), + H, + Y, + D, + E, }; - let G: P = ECPoint::generator(); - let h: P::Scalar = ECScalar::new_random(); - let H = G.clone() * h; - let y: P::Scalar = ECScalar::new_random(); - let Y = G.clone() * y; - let D = H.clone() * witness.x.clone() + Y.clone() * witness.r.clone(); - let E = G.clone() * witness.r.clone(); - let delta = HomoElGamalStatement { G, H, Y, D, E }; let proof = HomoELGamalProof::prove(&witness, &delta); assert!(proof.verify(&delta).is_ok()); } test_for_all_curves!(test_correct_homo_elgamal); - fn test_correct_homo_elgamal() - where - P: ECPoint + Clone + Zeroize, - P::Scalar: PartialEq + Clone + Zeroize, - { + fn test_correct_homo_elgamal() { let witness = HomoElGamalWitness { - r: P::Scalar::new_random(), - x: P::Scalar::new_random(), + r: Scalar::random(), + x: Scalar::random(), }; - let G: P = ECPoint::generator(); - let y: P::Scalar = ECScalar::new_random(); - let Y = G.clone() * y; - let D = G.clone() * witness.x.clone() + Y.clone() * witness.r.clone(); - let E = G.clone() * witness.r.clone(); + let G = Point::::generator(); + let y = Scalar::random(); + let Y = G * y; + let D = G * &witness.x + &Y * &witness.r; + let E = G * &witness.r; let delta = HomoElGamalStatement { - G: G.clone(), - H: G, + G: G.to_point(), + H: G.to_point(), Y, D, E, @@ -143,29 +146,28 @@ mod tests { assert!(proof.verify(&delta).is_ok()); } - test_for_all_curves!( - #[should_panic] - test_wrong_homo_elgamal - ); - fn test_wrong_homo_elgamal() - where - P: ECPoint + Clone + Zeroize, - P::Scalar: PartialEq + Clone + Zeroize, - { + test_for_all_curves!(test_wrong_homo_elgamal); + fn test_wrong_homo_elgamal() { // test for E = (r+1)G - let witness = HomoElGamalWitness:: { - r: ECScalar::new_random(), - x: ECScalar::new_random(), + let witness = HomoElGamalWitness:: { + r: Scalar::random(), + x: Scalar::random(), + }; + let G = Point::::generator(); + let h = Scalar::random(); + let H = G * h; + let y = Scalar::random(); + let Y = G * y; + let D = &H * &witness.x + &Y * &witness.r; + let E = G * &witness.r + G; + let delta = HomoElGamalStatement { + G: G.to_point(), + H, + Y, + D, + E, }; - let G: P = ECPoint::generator(); - let h: P::Scalar = ECScalar::new_random(); - let H = G.clone() * h; - let y: P::Scalar = ECScalar::new_random(); - let Y = G.clone() * y; - let D = H.clone() * witness.x.clone() + Y.clone() * witness.r.clone(); - let E = G.clone() * witness.r.clone() + G.clone(); - let delta = HomoElGamalStatement { G, H, Y, D, E }; let proof = HomoELGamalProof::prove(&witness, &delta); - assert!(proof.verify(&delta).is_ok()); + assert!(!proof.verify(&delta).is_ok()); } } diff --git a/src/cryptographic_primitives/proofs/sigma_correct_homomorphic_elgamal_encryption_of_dlog.rs b/src/cryptographic_primitives/proofs/sigma_correct_homomorphic_elgamal_encryption_of_dlog.rs index d75a02a4..a4333523 100644 --- a/src/cryptographic_primitives/proofs/sigma_correct_homomorphic_elgamal_encryption_of_dlog.rs +++ b/src/cryptographic_primitives/proofs/sigma_correct_homomorphic_elgamal_encryption_of_dlog.rs @@ -7,12 +7,11 @@ */ use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; +use sha2::Sha256; use super::ProofError; -use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; -use crate::cryptographic_primitives::hashing::traits::Hash; -use crate::elliptic::curves::traits::*; +use crate::cryptographic_primitives::hashing::{Digest, DigestExt}; +use crate::elliptic::curves::{Curve, Point, Scalar}; /// This is a proof of knowledge that a pair of group elements {D, E} /// form a valid homomorphic ElGamal encryption (”in the exponent”) using public key Y . @@ -21,62 +20,62 @@ use crate::elliptic::curves::traits::*; /// The relation R outputs 1 if D = xG+rY , E = rG, Q = xG /// #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct HomoELGamalDlogProof { - pub A1: P, - pub A2: P, - pub A3: P, - pub z1: P::Scalar, - pub z2: P::Scalar, +#[serde(bound = "")] +pub struct HomoELGamalDlogProof { + pub A1: Point, + pub A2: Point, + pub A3: Point, + pub z1: Scalar, + pub z2: Scalar, } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct HomoElGamalDlogWitness { - pub r: S, - pub x: S, +#[serde(bound = "")] +pub struct HomoElGamalDlogWitness { + pub r: Scalar, + pub x: Scalar, } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct HomoElGamalDlogStatement { - pub G: P, - pub Y: P, - pub Q: P, - pub D: P, - pub E: P, +#[serde(bound = "")] +pub struct HomoElGamalDlogStatement { + pub G: Point, + pub Y: Point, + pub Q: Point, + pub D: Point, + pub E: Point, } -impl

HomoELGamalDlogProof

-where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, -{ +impl HomoELGamalDlogProof { pub fn prove( - w: &HomoElGamalDlogWitness, - delta: &HomoElGamalDlogStatement

, - ) -> HomoELGamalDlogProof

{ - let mut s1: P::Scalar = ECScalar::new_random(); - let mut s2: P::Scalar = ECScalar::new_random(); - let A1 = delta.G.clone() * s1.clone(); - let A2 = delta.Y.clone() * s2.clone(); - let A3 = delta.G.clone() * s2.clone(); - let e = - HSha256::create_hash_from_ge(&[&A1, &A2, &A3, &delta.G, &delta.Y, &delta.D, &delta.E]); - let z1 = s1.clone() + e.clone() * w.x.clone(); - let z2 = s2.clone() + e * w.r.clone(); - s1.zeroize(); - s2.zeroize(); + w: &HomoElGamalDlogWitness, + delta: &HomoElGamalDlogStatement, + ) -> HomoELGamalDlogProof { + let s1 = Scalar::::random(); + let s2 = Scalar::::random(); + let A1 = &delta.G * &s1; + let A2 = &delta.Y * &s2; + let A3 = &delta.G * &s2; + let e = Sha256::new() + .chain_points([&A1, &A2, &A3, &delta.G, &delta.Y, &delta.D, &delta.E]) + .result_scalar(); + let z1 = &s1 + &e * &w.x; + let z2 = &s2 + e * &w.r; HomoELGamalDlogProof { A1, A2, A3, z1, z2 } } - pub fn verify(&self, delta: &HomoElGamalDlogStatement

) -> Result<(), ProofError> { - let e = HSha256::create_hash_from_ge(&[ - &self.A1, &self.A2, &self.A3, &delta.G, &delta.Y, &delta.D, &delta.E, - ]); - let z1G = delta.G.clone() * self.z1.clone(); - let z2Y = delta.Y.clone() * self.z2.clone(); - let z2G = delta.G.clone() * self.z2.clone(); - let A1_plus_eQ = self.A1.clone() + delta.Q.clone() * e.clone(); - let A3_plus_eE = self.A3.clone() + delta.E.clone() * e.clone(); - let D_minus_Q = delta.D.sub_point(&delta.Q.get_element()); + pub fn verify(&self, delta: &HomoElGamalDlogStatement) -> Result<(), ProofError> { + let e = Sha256::new() + .chain_points([ + &self.A1, &self.A2, &self.A3, &delta.G, &delta.Y, &delta.D, &delta.E, + ]) + .result_scalar(); + let z1G = &delta.G * &self.z1; + let z2Y = &delta.Y * &self.z2; + let z2G = &delta.G * &self.z2; + let A1_plus_eQ = &self.A1 + &delta.Q * &e; + let A3_plus_eE = &self.A3 + &delta.E * &e; + let D_minus_Q = &delta.D - &delta.Q; let A2_plus_eDmQ = self.A2.clone() + D_minus_Q * e; if z1G == A1_plus_eQ && z2G == A3_plus_eE && z2Y == A2_plus_eDmQ { Ok(()) @@ -88,53 +87,53 @@ where #[cfg(test)] mod tests { - use crate::cryptographic_primitives::proofs::sigma_correct_homomorphic_elgamal_encryption_of_dlog::*; use crate::test_for_all_curves; + use super::*; + test_for_all_curves!(test_correct_homo_elgamal); - fn test_correct_homo_elgamal

() - where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, - { - let witness = HomoElGamalDlogWitness:: { - r: ECScalar::new_random(), - x: ECScalar::new_random(), + fn test_correct_homo_elgamal() { + let witness = HomoElGamalDlogWitness:: { + r: Scalar::random(), + x: Scalar::random(), + }; + let G = Point::::generator(); + let Y = G * Scalar::random(); + let D = G * &witness.x + &Y * &witness.r; + let E = G * &witness.r; + let Q = G * &witness.x; + let delta = HomoElGamalDlogStatement { + G: G.to_point(), + Y, + Q, + D, + E, }; - let G: P = ECPoint::generator(); - let y: P::Scalar = ECScalar::new_random(); - let Y = G.clone() * y; - let D = G.clone() * witness.x.clone() + Y.clone() * witness.r.clone(); - let E = G.clone() * witness.r.clone(); - let Q = G.clone() * witness.x.clone(); - let delta = HomoElGamalDlogStatement { G, Y, Q, D, E }; let proof = HomoELGamalDlogProof::prove(&witness, &delta); assert!(proof.verify(&delta).is_ok()); } // TODO: add more fail scenarios - test_for_all_curves!( - #[should_panic] - test_wrong_homo_elgamal - ); - fn test_wrong_homo_elgamal

() - where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, - { + test_for_all_curves!(test_wrong_homo_elgamal); + fn test_wrong_homo_elgamal() { // test for Q = (x+1)G - let witness = HomoElGamalDlogWitness:: { - r: ECScalar::new_random(), - x: ECScalar::new_random(), + let witness = HomoElGamalDlogWitness:: { + r: Scalar::random(), + x: Scalar::random(), + }; + let G = Point::::generator(); + let Y = G * Scalar::random(); + let D = G * &witness.x + &Y * &witness.r; + let E = G * &witness.r + G; + let Q = G * &witness.x + G; + let delta = HomoElGamalDlogStatement { + G: G.to_point(), + Y, + Q, + D, + E, }; - let G: P = ECPoint::generator(); - let y: P::Scalar = ECScalar::new_random(); - let Y = G.clone() * y; - let D = G.clone() * witness.x.clone() + Y.clone() * witness.r.clone(); - let E = G.clone() * witness.r.clone() + G.clone(); - let Q = G.clone() * witness.x.clone() + G.clone(); - let delta = HomoElGamalDlogStatement { G, Y, Q, D, E }; let proof = HomoELGamalDlogProof::prove(&witness, &delta); - assert!(proof.verify(&delta).is_ok()); + assert!(!proof.verify(&delta).is_ok()); } } diff --git a/src/cryptographic_primitives/proofs/sigma_dlog.rs b/src/cryptographic_primitives/proofs/sigma_dlog.rs index 06a45b74..5909f62c 100644 --- a/src/cryptographic_primitives/proofs/sigma_dlog.rs +++ b/src/cryptographic_primitives/proofs/sigma_dlog.rs @@ -6,11 +6,10 @@ */ use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; +use sha2::Sha256; -use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; -use crate::cryptographic_primitives::hashing::traits::Hash; -use crate::elliptic::curves::traits::*; +use crate::cryptographic_primitives::hashing::{Digest, DigestExt}; +use crate::elliptic::curves::{Curve, Point, Scalar}; use super::ProofError; @@ -18,40 +17,37 @@ use super::ProofError; /// sigma protocol for Proof of knowledge of the discrete log of an Elliptic-curve point: /// C.P. Schnorr. Efficient Identification and Signatures for Smart Cards. In /// CRYPTO 1989, Springer (LNCS 435), pages 239–252, 1990. -/// https://pdfs.semanticscholar.org/8d69/c06d48b618a090dd19185aea7a13def894a5.pdf. +/// . /// /// The protocol is using Fiat-Shamir Transform: Amos Fiat and Adi Shamir. /// How to prove yourself: Practical solutions to identification and signature problems. /// In Advances in Cryptology - CRYPTO ’86, Santa Barbara, California, USA, 1986, Proceedings, /// pages 186–194, 1986. #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct DLogProof { - pub pk: P, - pub pk_t_rand_commitment: P, - pub challenge_response: P::Scalar, +#[serde(bound = "")] +pub struct DLogProof { + pub pk: Point, + pub pk_t_rand_commitment: Point, + pub challenge_response: Scalar, } -impl

DLogProof

-where - P: ECPoint + Clone, - P::Scalar: Zeroize, -{ - pub fn prove(sk: &P::Scalar) -> DLogProof

{ - let base_point: P = ECPoint::generator(); - let generator_x = base_point.bytes_compressed_to_big_int(); - let mut sk_t_rand_commitment: P::Scalar = ECScalar::new_random(); - let pk_t_rand_commitment = base_point.scalar_mul(&sk_t_rand_commitment.get_element()); - let ec_point: P = ECPoint::generator(); - let pk = ec_point.scalar_mul(&sk.get_element()); - let challenge = HSha256::create_hash(&[ - &pk_t_rand_commitment.bytes_compressed_to_big_int(), - &generator_x, - &pk.bytes_compressed_to_big_int(), - ]); - let challenge_fe: P::Scalar = ECScalar::from(&challenge); - let challenge_mul_sk = challenge_fe.mul(&sk.get_element()); - let challenge_response = sk_t_rand_commitment.sub(&challenge_mul_sk.get_element()); - sk_t_rand_commitment.zeroize(); +impl DLogProof { + pub fn prove(sk: &Scalar) -> DLogProof { + let generator = Point::::generator(); + + let sk_t_rand_commitment = Scalar::random(); + let pk_t_rand_commitment = generator * &sk_t_rand_commitment; + + let pk = Point::generator() * sk; + + let challenge = Sha256::new() + .chain_point(&pk_t_rand_commitment) + .chain_point(&generator.to_point()) + .chain_point(&pk) + .result_scalar(); + + let challenge_mul_sk = challenge * sk; + let challenge_response = &sk_t_rand_commitment - &challenge_mul_sk; DLogProof { pk, pk_t_rand_commitment, @@ -59,23 +55,18 @@ where } } - pub fn verify(proof: &DLogProof

) -> Result<(), ProofError> { - let ec_point: P = ECPoint::generator(); - let challenge = HSha256::create_hash(&[ - &proof.pk_t_rand_commitment.bytes_compressed_to_big_int(), - &ec_point.bytes_compressed_to_big_int(), - &proof.pk.bytes_compressed_to_big_int(), - ]); - - let sk_challenge: P::Scalar = ECScalar::from(&challenge); - let pk = proof.pk.clone(); - let pk_challenge = pk.scalar_mul(&sk_challenge.get_element()); + pub fn verify(proof: &DLogProof) -> Result<(), ProofError> { + let generator = Point::::generator(); - let base_point: P = ECPoint::generator(); + let challenge = Sha256::new() + .chain_point(&proof.pk_t_rand_commitment) + .chain_point(&generator.to_point()) + .chain_point(&proof.pk) + .result_scalar(); - let mut pk_verifier = base_point.scalar_mul(&proof.challenge_response.get_element()); + let pk_challenge = &proof.pk * &challenge; - pk_verifier = pk_verifier.add_point(&pk_challenge.get_element()); + let pk_verifier = generator * &proof.challenge_response + pk_challenge; if pk_verifier == proof.pk_t_rand_commitment { Ok(()) @@ -90,13 +81,9 @@ mod tests { use super::*; crate::test_for_all_curves!(test_dlog_proof); - fn test_dlog_proof

() - where - P: ECPoint + Clone, - P::Scalar: Zeroize, - { - let witness: P::Scalar = ECScalar::new_random(); - let dlog_proof = DLogProof::

::prove(&witness); + fn test_dlog_proof() { + let witness = Scalar::random(); + let dlog_proof = DLogProof::::prove(&witness); assert!(DLogProof::verify(&dlog_proof).is_ok()); } } diff --git a/src/cryptographic_primitives/proofs/sigma_ec_ddh.rs b/src/cryptographic_primitives/proofs/sigma_ec_ddh.rs index 4219d89c..a64fd071 100644 --- a/src/cryptographic_primitives/proofs/sigma_ec_ddh.rs +++ b/src/cryptographic_primitives/proofs/sigma_ec_ddh.rs @@ -6,12 +6,12 @@ */ use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; +use sha2::Sha256; + +use crate::cryptographic_primitives::hashing::{Digest, DigestExt}; +use crate::elliptic::curves::{Curve, Point, Scalar}; use super::ProofError; -use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; -use crate::cryptographic_primitives::hashing::traits::Hash; -use crate::elliptic::curves::traits::*; /// This protocol is the elliptic curve form of the protocol from : /// D. Chaum, T. P. Pedersen. Transferred cash grows in size. In Advances in Cryptology, EUROCRYPT , volume 658 of Lecture Notes in Computer Science, pages 390 - 407, 1993. @@ -26,49 +26,56 @@ use crate::elliptic::curves::traits::*; /// /// verifier checks that zG1 = A1 + eH1, zG2 = A2 + eH2 #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct ECDDHProof { - pub a1: P, - pub a2: P, - pub z: P::Scalar, +#[serde(bound = "")] +pub struct ECDDHProof { + pub a1: Point, + pub a2: Point, + pub z: Scalar, } #[derive(Clone, PartialEq, Debug)] -pub struct ECDDHStatement { - pub g1: P, - pub h1: P, - pub g2: P, - pub h2: P, +pub struct ECDDHStatement { + pub g1: Point, + pub h1: Point, + pub g2: Point, + pub h2: Point, } #[derive(Clone, PartialEq, Debug)] -pub struct ECDDHWitness { - pub x: S, +pub struct ECDDHWitness { + pub x: Scalar, } -impl

ECDDHProof

-where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, -{ - pub fn prove(w: &ECDDHWitness, delta: &ECDDHStatement

) -> ECDDHProof

{ - let mut s: P::Scalar = ECScalar::new_random(); - let a1 = delta.g1.clone() * s.clone(); - let a2 = delta.g2.clone() * s.clone(); - let e = - HSha256::create_hash_from_ge(&[&delta.g1, &delta.h1, &delta.g2, &delta.h2, &a1, &a2]); - let z = s.clone() + e * w.x.clone(); - s.zeroize(); +impl ECDDHProof { + pub fn prove(w: &ECDDHWitness, delta: &ECDDHStatement) -> ECDDHProof { + let s = Scalar::random(); + let a1 = &delta.g1 * &s; + let a2 = &delta.g2 * &s; + let e = Sha256::new() + .chain_point(&delta.g1) + .chain_point(&delta.h1) + .chain_point(&delta.g2) + .chain_point(&delta.h2) + .chain_point(&a1) + .chain_point(&a2) + .result_scalar(); + let z = &s + e * &w.x; ECDDHProof { a1, a2, z } } - pub fn verify(&self, delta: &ECDDHStatement

) -> Result<(), ProofError> { - let e = HSha256::create_hash_from_ge(&[ - &delta.g1, &delta.h1, &delta.g2, &delta.h2, &self.a1, &self.a2, - ]); - let z_g1 = delta.g1.clone() * self.z.clone(); - let z_g2 = delta.g2.clone() * self.z.clone(); - let a1_plus_e_h1 = self.a1.clone() + delta.h1.clone() * e.clone(); - let a2_plus_e_h2 = self.a2.clone() + delta.h2.clone() * e; + pub fn verify(&self, delta: &ECDDHStatement) -> Result<(), ProofError> { + let e = Sha256::new() + .chain_point(&delta.g1) + .chain_point(&delta.h1) + .chain_point(&delta.g2) + .chain_point(&delta.h2) + .chain_point(&self.a1) + .chain_point(&self.a2) + .result_scalar(); + let z_g1 = &delta.g1 * &self.z; + let z_g2 = &delta.g2 * &self.z; + let a1_plus_e_h1 = &self.a1 + &delta.h1 * &e; + let a2_plus_e_h2 = &self.a2 + &delta.h2 * e; if z_g1 == a1_plus_e_h1 && z_g2 == a2_plus_e_h2 { Ok(()) } else { @@ -79,45 +86,44 @@ where #[cfg(test)] mod tests { - use crate::cryptographic_primitives::proofs::sigma_ec_ddh::*; - use crate::elliptic::curves::traits::{ECPoint, ECScalar}; use crate::test_for_all_curves; + use super::*; + test_for_all_curves!(test_ecddh_proof); - fn test_ecddh_proof

() - where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, - { - let x: P::Scalar = ECScalar::new_random(); - let g1: P = ECPoint::generator(); - let g2: P = ECPoint::base_point2(); - let h1 = g1.clone() * x.clone(); - let h2 = g2.clone() * x.clone(); - let delta = ECDDHStatement { g1, g2, h1, h2 }; + fn test_ecddh_proof() { + let x = Scalar::::random(); + let g1 = Point::generator(); + let g2 = Point::base_point2(); + let h1 = g1 * &x; + let h2 = g2 * &x; + let delta = ECDDHStatement { + g1: g1.to_point(), + g2: g2.clone(), + h1, + h2, + }; let w = ECDDHWitness { x }; let proof = ECDDHProof::prove(&w, &delta); assert!(proof.verify(&delta).is_ok()); } - test_for_all_curves!( - #[should_panic] - test_wrong_ecddh_proof - ); - fn test_wrong_ecddh_proof

() - where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, - { - let x: P::Scalar = ECScalar::new_random(); - let g1: P = ECPoint::generator(); - let g2: P = ECPoint::base_point2(); - let x2: P::Scalar = ECScalar::new_random(); - let h1 = g1.clone() * x.clone(); - let h2 = g2.clone() * x2; - let delta = ECDDHStatement { g1, g2, h1, h2 }; + test_for_all_curves!(test_wrong_ecddh_proof); + fn test_wrong_ecddh_proof() { + let x = Scalar::::random(); + let g1 = Point::generator(); + let g2 = Point::base_point2(); + let x2 = Scalar::::random(); + let h1 = g1 * &x; + let h2 = g2 * &x2; + let delta = ECDDHStatement { + g1: g1.to_point(), + g2: g2.clone(), + h1, + h2, + }; let w = ECDDHWitness { x }; let proof = ECDDHProof::prove(&w, &delta); - assert!(proof.verify(&delta).is_ok()); + assert!(!proof.verify(&delta).is_ok()); } } diff --git a/src/cryptographic_primitives/proofs/sigma_valid_pedersen.rs b/src/cryptographic_primitives/proofs/sigma_valid_pedersen.rs index d72859d6..2343cdc8 100644 --- a/src/cryptographic_primitives/proofs/sigma_valid_pedersen.rs +++ b/src/cryptographic_primitives/proofs/sigma_valid_pedersen.rs @@ -6,14 +6,14 @@ */ use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; +use sha2::Sha256; -use super::ProofError; use crate::cryptographic_primitives::commitments::pedersen_commitment::PedersenCommitment; use crate::cryptographic_primitives::commitments::traits::Commitment; -use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; -use crate::cryptographic_primitives::hashing::traits::Hash; -use crate::elliptic::curves::traits::*; +use crate::cryptographic_primitives::hashing::{Digest, DigestExt}; +use crate::elliptic::curves::{Curve, Point, Scalar}; + +use super::ProofError; /// protocol for proving that Pedersen commitment c was constructed correctly which is the same as /// proof of knowledge of (m,r) such that c = mG + rH. @@ -25,49 +25,38 @@ use crate::elliptic::curves::traits::*; /// /// verifier checks that z1*G + z2*H = A1 + A2 + ec #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct PedersenProof { - e: P::Scalar, - a1: P, - a2: P, - pub com: P, - z1: P::Scalar, - z2: P::Scalar, +#[serde(bound = "")] +pub struct PedersenProof { + e: Scalar, + a1: Point, + a2: Point, + pub com: Point, + z1: Scalar, + z2: Scalar, } -impl

PedersenProof

-where - P: ECPoint + Clone, - P::Scalar: Zeroize, -{ +impl PedersenProof { #[allow(clippy::many_single_char_names)] - pub fn prove(m: &P::Scalar, r: &P::Scalar) -> PedersenProof

{ - let g: P = ECPoint::generator(); - let h: P = ECPoint::base_point2(); - let mut s1: P::Scalar = ECScalar::new_random(); - let mut s2: P::Scalar = ECScalar::new_random(); - let a1 = g.scalar_mul(&s1.get_element()); - let a2 = h.scalar_mul(&s2.get_element()); - let com: P = PedersenCommitment::create_commitment_with_user_defined_randomness( - &m.to_big_int(), - &r.to_big_int(), + pub fn prove(m: &Scalar, r: &Scalar) -> PedersenProof { + let g = Point::::generator(); + let h = Point::::base_point2(); + let s1 = Scalar::random(); + let s2 = Scalar::random(); + let a1 = g * &s1; + let a2 = h * &s2; + let com: Point = PedersenCommitment::create_commitment_with_user_defined_randomness( + &m.to_bigint(), + &r.to_bigint(), ); - let g: P = ECPoint::generator(); - let challenge = HSha256::create_hash(&[ - &g.bytes_compressed_to_big_int(), - &h.bytes_compressed_to_big_int(), - &com.bytes_compressed_to_big_int(), - &a1.bytes_compressed_to_big_int(), - &a2.bytes_compressed_to_big_int(), - ]); - let e: P::Scalar = ECScalar::from(&challenge); + let e = Sha256::new() + .chain_points([&g.to_point(), h, &com, &a1, &a2]) + .result_scalar(); - let em = e.mul(&m.get_element()); - let z1 = s1.add(&em.get_element()); - let er = e.mul(&r.get_element()); - let z2 = s2.add(&er.get_element()); - s1.zeroize(); - s2.zeroize(); + let em = &e * m; + let z1 = &s1 + em; + let er = &e * r; + let z2 = &s2 + er; PedersenProof { e, @@ -79,25 +68,20 @@ where } } - pub fn verify(proof: &PedersenProof

) -> Result<(), ProofError> { - let g: P = ECPoint::generator(); - let h: P = ECPoint::base_point2(); - let challenge = HSha256::create_hash(&[ - &g.bytes_compressed_to_big_int(), - &h.bytes_compressed_to_big_int(), - &proof.com.bytes_compressed_to_big_int(), - &proof.a1.bytes_compressed_to_big_int(), - &proof.a2.bytes_compressed_to_big_int(), - ]); - let e: P::Scalar = ECScalar::from(&challenge); + pub fn verify(proof: &PedersenProof) -> Result<(), ProofError> { + let g = Point::::generator(); + let h = Point::::base_point2(); + + let e = Sha256::new() + .chain_points([&g.to_point(), &h, &proof.com, &proof.a1, &proof.a2]) + .result_scalar(); - let z1g = g.scalar_mul(&proof.z1.get_element()); - let z2h = h.scalar_mul(&proof.z2.get_element()); - let lhs = z1g.add_point(&z2h.get_element()); - let rhs = proof.a1.add_point(&proof.a2.get_element()); - let com_clone = proof.com.clone(); - let ecom = com_clone.scalar_mul(&e.get_element()); - let rhs = rhs.add_point(&ecom.get_element()); + let z1g = g * &proof.z1; + let z2h = h * &proof.z2; + let lhs = &z1g + &z2h; + let rhs = &proof.a1 + &proof.a2; + let ecom = &proof.com * &e; + let rhs = rhs + &ecom; if lhs == rhs { Ok(()) @@ -112,14 +96,10 @@ mod tests { use super::*; crate::test_for_all_curves!(test_pedersen_proof); - fn test_pedersen_proof

() - where - P: ECPoint + Clone, - P::Scalar: Zeroize, - { - let m: P::Scalar = ECScalar::new_random(); - let r: P::Scalar = ECScalar::new_random(); - let pedersen_proof = PedersenProof::

::prove(&m, &r); + fn test_pedersen_proof() { + let m = Scalar::random(); + let r = Scalar::random(); + let pedersen_proof = PedersenProof::::prove(&m, &r); PedersenProof::verify(&pedersen_proof).expect("error pedersen"); } } diff --git a/src/cryptographic_primitives/proofs/sigma_valid_pedersen_blind.rs b/src/cryptographic_primitives/proofs/sigma_valid_pedersen_blind.rs index 10df1cfe..3066616b 100644 --- a/src/cryptographic_primitives/proofs/sigma_valid_pedersen_blind.rs +++ b/src/cryptographic_primitives/proofs/sigma_valid_pedersen_blind.rs @@ -6,14 +6,14 @@ */ use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; +use sha2::Sha256; -use super::ProofError; use crate::cryptographic_primitives::commitments::pedersen_commitment::PedersenCommitment; use crate::cryptographic_primitives::commitments::traits::Commitment; -use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; -use crate::cryptographic_primitives::hashing::traits::Hash; -use crate::elliptic::curves::traits::*; +use crate::cryptographic_primitives::hashing::{Digest, DigestExt}; +use crate::elliptic::curves::{Curve, Point, Scalar}; + +use super::ProofError; /// protocol for proving that Pedersen commitment c was constructed correctly which is the same as /// proof of knowledge of (r) such that c = mG + rH. @@ -24,42 +24,34 @@ use crate::elliptic::curves::traits::*; /// prover sends pi = {e, m,A,c, z} /// verifier checks that emG + zH = A + ec #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct PedersenBlindingProof { - e: P::Scalar, - pub m: P::Scalar, - a: P, - pub com: P, - z: P::Scalar, +#[serde(bound = "")] +pub struct PedersenBlindingProof { + e: Scalar, + pub m: Scalar, + a: Point, + pub com: Point, + z: Scalar, } -impl

PedersenBlindingProof

-where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, -{ +impl PedersenBlindingProof { #[allow(clippy::many_single_char_names)] //TODO: add self verification to prover proof - pub fn prove(m: &P::Scalar, r: &P::Scalar) -> PedersenBlindingProof

{ - let h: P = ECPoint::base_point2(); - let mut s: P::Scalar = ECScalar::new_random(); - let a = h.scalar_mul(&s.get_element()); - let com: P = PedersenCommitment::create_commitment_with_user_defined_randomness( - &m.to_big_int(), - &r.to_big_int(), + pub fn prove(m: &Scalar, r: &Scalar) -> PedersenBlindingProof { + let h = Point::::base_point2(); + let s = Scalar::::random(); + let a = h * &s; + let com: Point = PedersenCommitment::create_commitment_with_user_defined_randomness( + &m.to_bigint(), + &r.to_bigint(), ); - let g: P = ECPoint::generator(); - let challenge = HSha256::create_hash(&[ - &g.bytes_compressed_to_big_int(), - &h.bytes_compressed_to_big_int(), - &com.bytes_compressed_to_big_int(), - &a.bytes_compressed_to_big_int(), - &m.to_big_int(), - ]); - let e: P::Scalar = ECScalar::from(&challenge); + let g = Point::::generator(); + let e = Sha256::new() + .chain_points([g.as_point(), h, &com, &a]) + .chain_scalar(&m) + .result_scalar(); - let er = e.mul(&r.get_element()); - let z = s.add(&er.get_element()); - s.zeroize(); + let er = &e * r; + let z = &s + &er; PedersenBlindingProof { e, m: m.clone(), @@ -69,26 +61,20 @@ where } } - pub fn verify(proof: &PedersenBlindingProof

) -> Result<(), ProofError> { - let g: P = ECPoint::generator(); - let h: P = ECPoint::base_point2(); - let challenge = HSha256::create_hash(&[ - &g.bytes_compressed_to_big_int(), - &h.bytes_compressed_to_big_int(), - &proof.com.bytes_compressed_to_big_int(), - &proof.a.bytes_compressed_to_big_int(), - &proof.m.to_big_int(), - ]); - - let e: P::Scalar = ECScalar::from(&challenge); + pub fn verify(proof: &PedersenBlindingProof) -> Result<(), ProofError> { + let g = Point::::generator(); + let h = Point::::base_point2(); + let e = Sha256::new() + .chain_points([g.as_point(), h, &proof.com, &proof.a]) + .chain_scalar(&proof.m) + .result_scalar(); - let zh = h.scalar_mul(&proof.z.get_element()); - let mg = g.scalar_mul(&proof.m.get_element()); - let emg = mg.scalar_mul(&e.get_element()); - let lhs = zh.add_point(&emg.get_element()); - let com_clone = proof.com.clone(); - let ecom = com_clone.scalar_mul(&e.get_element()); - let rhs = ecom.add_point(&proof.a.get_element()); + let zh = h * &proof.z; + let mg = g * &proof.m; + let emg = mg * &e; + let lhs = zh + emg; + let ecom = &proof.com * &e; + let rhs = ecom + &proof.a; if lhs == rhs { Ok(()) @@ -103,14 +89,10 @@ mod tests { use super::*; crate::test_for_all_curves!(test_pedersen_blind_proof); - fn test_pedersen_blind_proof

() - where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, - { - let m: P::Scalar = ECScalar::new_random(); - let r: P::Scalar = ECScalar::new_random(); - let pedersen_proof = PedersenBlindingProof::

::prove(&m, &r); + fn test_pedersen_blind_proof() { + let m = Scalar::random(); + let r = Scalar::random(); + let pedersen_proof = PedersenBlindingProof::::prove(&m, &r); let _verified = PedersenBlindingProof::verify(&pedersen_proof).expect("error pedersen blind"); } diff --git a/src/cryptographic_primitives/secret_sharing/feldman_vss.rs b/src/cryptographic_primitives/secret_sharing/feldman_vss.rs index 274a5954..e1bd78a8 100644 --- a/src/cryptographic_primitives/secret_sharing/feldman_vss.rs +++ b/src/cryptographic_primitives/secret_sharing/feldman_vss.rs @@ -6,49 +6,65 @@ License MIT: */ +use std::convert::{TryFrom, TryInto}; +use std::{fmt, ops}; + use serde::{Deserialize, Serialize}; -use crate::arithmetic::traits::*; -use crate::elliptic::curves::traits::*; -use crate::BigInt; +use crate::cryptographic_primitives::secret_sharing::Polynomial; +use crate::elliptic::curves::{Curve, Point, Scalar}; use crate::ErrorSS::{self, VerifyShareError}; #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] pub struct ShamirSecretSharing { - pub threshold: usize, //t - pub share_count: usize, //n + pub threshold: u16, //t + pub share_count: u16, //n } + /// Feldman VSS, based on Paul Feldman. 1987. A practical scheme for non-interactive verifiable secret sharing. /// In Foundations of Computer Science, 1987., 28th Annual Symposium on.IEEE, 427–43 /// /// implementation details: The code is using FE and GE. Each party is given an index from 1,..,n and a secret share of type FE. /// The index of the party is also the point on the polynomial where we treat this number as u32 but converting it to FE internally. #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct VerifiableSS

{ +#[serde(bound = "")] +pub struct VerifiableSS { pub parameters: ShamirSecretSharing, - pub commitments: Vec

, + pub commitments: Vec>, +} + +/// Shared secret produced by [VerifiableSS::share] +/// +/// After you shared your secret, you need to distribute `shares` among other parties, and erase +/// secret from your memory (SharedSecret zeroizes on drop). +/// +/// You can retrieve a [polynomial](Self::polynomial) that was used to derive secret shares. It is +/// only needed to combine with other proofs (e.g. [low degree exponent interpolation]). +/// +/// [low degree exponent interpolation]: crate::cryptographic_primitives::proofs::low_degree_exponent_interpolation +#[derive(Clone)] +pub struct SecretShares { + shares: Vec>, + polynomial: Polynomial, } -impl

VerifiableSS

-where - P: ECPoint + Clone, - P::Scalar: Clone, -{ - pub fn reconstruct_limit(&self) -> usize { +impl VerifiableSS { + pub fn reconstruct_limit(&self) -> u16 { self.parameters.threshold + 1 } // generate VerifiableSS from a secret - pub fn share(t: usize, n: usize, secret: &P::Scalar) -> (VerifiableSS

, Vec) { + pub fn share(t: u16, n: u16, secret: &Scalar) -> (VerifiableSS, SecretShares) { assert!(t < n); - let poly = VerifiableSS::

::sample_polynomial(t, secret); - let index_vec: Vec = (1..=n).collect(); - let secret_shares = VerifiableSS::

::evaluate_polynomial(&poly, &index_vec); - - let G: P = ECPoint::generator(); - let commitments = (0..poly.len()) - .map(|i| G.clone() * poly[i].clone()) - .collect::>(); + let polynomial = Polynomial::::sample_exact_with_fixed_const_term(t, secret.clone()); + let shares = polynomial.evaluate_many_bigint(1..=n).collect(); + + let g = Point::::generator(); + let commitments = polynomial + .coefficients() + .iter() + .map(|coef| g * coef) + .collect::>(); ( VerifiableSS { parameters: ShamirSecretSharing { @@ -57,23 +73,25 @@ where }, commitments, }, - secret_shares, + SecretShares { shares, polynomial }, ) } // takes given VSS and generates a new VSS for the same secret and a secret shares vector to match the new commitments - pub fn reshare(&self) -> (VerifiableSS

, Vec) { - let one: P::Scalar = ECScalar::from(&BigInt::one()); - let poly = VerifiableSS::

::sample_polynomial(self.parameters.threshold, &one); - let index_vec: Vec = (1..=self.parameters.share_count).collect(); - let secret_shares_biased = VerifiableSS::

::evaluate_polynomial(&poly, &index_vec); + pub fn reshare(&self) -> (VerifiableSS, Vec>) { + let t = self.parameters.threshold; + let n = self.parameters.share_count; + + let one = Scalar::::from(1); + let poly = Polynomial::::sample_exact_with_fixed_const_term(t, one.clone()); + let secret_shares_biased: Vec<_> = poly.evaluate_many_bigint(1..=n).collect(); let secret_shares: Vec<_> = (0..secret_shares_biased.len()) - .map(|i| secret_shares_biased[i].sub(&one.get_element())) + .map(|i| &secret_shares_biased[i] - &one) .collect(); - let G: P = ECPoint::generator(); + let g = Point::::generator(); let mut new_commitments = vec![self.commitments[0].clone()]; - for (poly, commitment) in poly.iter().zip(&self.commitments).skip(1) { - new_commitments.push((G.clone() * poly.clone()) + commitment.clone()) + for (poly, commitment) in poly.coefficients().iter().zip(&self.commitments).skip(1) { + new_commitments.push((g * poly) + commitment) } ( VerifiableSS { @@ -86,19 +104,24 @@ where // generate VerifiableSS from a secret and user defined x values (in case user wants to distribute point f(1), f(4), f(6) and not f(1),f(2),f(3)) pub fn share_at_indices( - t: usize, - n: usize, - secret: &P::Scalar, - index_vec: &[usize], - ) -> (VerifiableSS

, Vec) { - assert_eq!(n, index_vec.len()); - let poly = VerifiableSS::

::sample_polynomial(t, secret); - let secret_shares = VerifiableSS::

::evaluate_polynomial(&poly, index_vec); - - let G: P = ECPoint::generator(); - let commitments = (0..poly.len()) - .map(|i| G.clone() * poly[i].clone()) - .collect::>(); + t: u16, + n: u16, + secret: &Scalar, + index_vec: &[u16], + ) -> (VerifiableSS, SecretShares) { + assert_eq!(usize::from(n), index_vec.len()); + + let polynomial = Polynomial::::sample_exact_with_fixed_const_term(t, secret.clone()); + let shares = polynomial + .evaluate_many_bigint(index_vec.iter().cloned()) + .collect(); + + let g = Point::::generator(); + let commitments = polynomial + .coefficients() + .iter() + .map(|coef| g * coef) + .collect::>>(); ( VerifiableSS { parameters: ShamirSecretSharing { @@ -107,55 +130,42 @@ where }, commitments, }, - secret_shares, + SecretShares { shares, polynomial }, ) } // returns vector of coefficients - pub fn sample_polynomial(t: usize, coef0: &P::Scalar) -> Vec { - let mut coefficients = vec![coef0.clone()]; - // sample the remaining coefficients randomly using secure randomness - let random_coefficients: Vec = (0..t).map(|_| ECScalar::new_random()).collect(); - coefficients.extend(random_coefficients); - // return - coefficients + #[deprecated(since = "0.8.0", note = "please use Polynomial::sample instead")] + pub fn sample_polynomial(t: usize, coef0: &Scalar) -> Vec> { + Polynomial::::sample_exact_with_fixed_const_term(t.try_into().unwrap(), coef0.clone()) + .coefficients() + .to_vec() } - pub fn evaluate_polynomial(coefficients: &[P::Scalar], index_vec: &[usize]) -> Vec { - (0..index_vec.len()) - .map(|point| { - let point_bn = BigInt::from(index_vec[point] as u32); - - VerifiableSS::

::mod_evaluate_polynomial(coefficients, ECScalar::from(&point_bn)) - }) + #[deprecated( + since = "0.8.0", + note = "please use Polynomial::evaluate_many_bigint instead" + )] + pub fn evaluate_polynomial(coefficients: &[Scalar], index_vec: &[usize]) -> Vec> { + Polynomial::::from_coefficients(coefficients.to_vec()) + .evaluate_many_bigint(index_vec.iter().map(|&i| u64::try_from(i).unwrap())) .collect() } - pub fn mod_evaluate_polynomial(coefficients: &[P::Scalar], point: P::Scalar) -> P::Scalar { - // evaluate using Horner's rule - // - to combine with fold we consider the coefficients in reverse order - let mut reversed_coefficients = coefficients.iter().rev(); - // manually split due to fold insisting on an initial value - let head = reversed_coefficients.next().unwrap(); - let tail = reversed_coefficients; - tail.fold(head.clone(), |partial, coef| { - let partial_times_point = partial.mul(&point.get_element()); - partial_times_point.add(&coef.get_element()) - }) + #[deprecated(since = "0.8.0", note = "please use Polynomial::evaluate instead")] + pub fn mod_evaluate_polynomial(coefficients: &[Scalar], point: Scalar) -> Scalar { + Polynomial::::from_coefficients(coefficients.to_vec()).evaluate(&point) } - pub fn reconstruct(&self, indices: &[usize], shares: &[P::Scalar]) -> P::Scalar { + pub fn reconstruct(&self, indices: &[u16], shares: &[Scalar]) -> Scalar { assert_eq!(shares.len(), indices.len()); - assert!(shares.len() >= self.reconstruct_limit()); + assert!(shares.len() >= usize::from(self.reconstruct_limit())); // add one to indices to get points let points = indices .iter() - .map(|i| { - let index_bn = BigInt::from(*i as u32 + 1); - ECScalar::from(&index_bn) - }) - .collect::>(); - VerifiableSS::

::lagrange_interpolation_at_zero(&points, &shares) + .map(|i| Scalar::from(*i + 1)) + .collect::>(); + VerifiableSS::::lagrange_interpolation_at_zero(&points, &shares) } // Performs a Lagrange interpolation in field Zp at the origin @@ -168,50 +178,51 @@ where // This is obviously less general than `newton_interpolation_general` as we // only get a single value, but it is much faster. - pub fn lagrange_interpolation_at_zero(points: &[P::Scalar], values: &[P::Scalar]) -> P::Scalar { + pub fn lagrange_interpolation_at_zero(points: &[Scalar], values: &[Scalar]) -> Scalar { let vec_len = values.len(); assert_eq!(points.len(), vec_len); // Lagrange interpolation for point 0 // let mut acc = 0i64; - let lag_coef = (0..vec_len) - .map(|i| { - let xi = &points[i]; - let yi = &values[i]; - let num: P::Scalar = ECScalar::from(&BigInt::one()); - let denum: P::Scalar = ECScalar::from(&BigInt::one()); - let num = points.iter().zip(0..vec_len).fold(num, |acc, x| { - if i != x.1 { - acc * x.0.clone() - } else { - acc - } - }); - let denum = points.iter().zip(0..vec_len).fold(denum, |acc, x| { - if i != x.1 { - let xj_sub_xi = x.0.sub(&xi.get_element()); - acc * xj_sub_xi - } else { - acc - } - }); - let denum = denum.invert(); - num * denum * yi.clone() - }) - .collect::>(); + let lag_coef = + (0..vec_len) + .map(|i| { + let xi = &points[i]; + let yi = &values[i]; + let num = Scalar::from(1); + let denum = Scalar::from(1); + let num = points.iter().zip(0..vec_len).fold(num, |acc, x| { + if i != x.1 { + acc * x.0 + } else { + acc + } + }); + let denum = points.iter().zip(0..vec_len).fold(denum, |acc, x| { + if i != x.1 { + let xj_sub_xi = x.0 - xi; + acc * xj_sub_xi + } else { + acc + } + }); + let denum = denum.invert().unwrap(); + num * denum * yi + }) + .collect::>(); let mut lag_coef_iter = lag_coef.iter(); let head = lag_coef_iter.next().unwrap(); let tail = lag_coef_iter; - tail.fold(head.clone(), |acc, x| acc.add(&x.get_element())) + tail.fold(head.clone(), |acc, x| acc + x) } - pub fn validate_share(&self, secret_share: &P::Scalar, index: usize) -> Result<(), ErrorSS> { - let G: P = ECPoint::generator(); - let ss_point = G * secret_share.clone(); + pub fn validate_share(&self, secret_share: &Scalar, index: u16) -> Result<(), ErrorSS> { + let g = Point::generator(); + let ss_point = g * secret_share; self.validate_share_public(&ss_point, index) } - pub fn validate_share_public(&self, ss_point: &P, index: usize) -> Result<(), ErrorSS> { + pub fn validate_share_public(&self, ss_point: &Point, index: u16) -> Result<(), ErrorSS> { let comm_to_point = self.get_point_commitment(index); if *ss_point == comm_to_point { Ok(()) @@ -220,56 +231,72 @@ where } } - pub fn get_point_commitment(&self, index: usize) -> P { - let index_fe: P::Scalar = ECScalar::from(&BigInt::from(index as u32)); + pub fn get_point_commitment(&self, index: u16) -> Point { + let index_fe = Scalar::from(index); let mut comm_iterator = self.commitments.iter().rev(); let head = comm_iterator.next().unwrap(); let tail = comm_iterator; - tail.fold(head.clone(), |acc, x: &P| { - x.clone() + acc * index_fe.clone() - }) + tail.fold(head.clone(), |acc, x| x + acc * &index_fe) } //compute \lambda_{index,S}, a lagrangian coefficient that change the (t,n) scheme to (|S|,|S|) // used in http://stevengoldfeder.com/papers/GG18.pdf pub fn map_share_to_new_params( params: &ShamirSecretSharing, - index: usize, - s: &[usize], - ) -> P::Scalar { + index: u16, + s: &[u16], + ) -> Scalar { let s_len = s.len(); // assert!(s_len > self.reconstruct_limit()); // add one to indices to get points - let points: Vec = (0..params.share_count) - .map(|i| { - let index_bn = BigInt::from(i as u32 + 1); - ECScalar::from(&index_bn) - }) + let points: Vec> = (0..params.share_count) + .map(|i| Scalar::from(i + 1)) .collect(); - let xi = &points[index]; - let num: P::Scalar = ECScalar::from(&BigInt::one()); - let denum: P::Scalar = ECScalar::from(&BigInt::one()); + let xi = &points[usize::from(index)]; + let num = Scalar::from(1); + let denum = Scalar::from(1); let num = (0..s_len).fold(num, |acc, i| { if s[i] != index { - acc * points[s[i]].clone() + acc * &points[usize::from(s[i])] } else { acc } }); let denum = (0..s_len).fold(denum, |acc, i| { if s[i] != index { - let xj_sub_xi = points[s[i]].sub(&xi.get_element()); + let xj_sub_xi = &points[usize::from(s[i])] - xi; acc * xj_sub_xi } else { acc } }); - let denum = denum.invert(); + let denum = denum.invert().unwrap(); num * denum } } +impl SecretShares { + /// Polynomial that was used to derive secret shares + pub fn polynomial(&self) -> &Polynomial { + &self.polynomial + } +} + +impl fmt::Debug for SecretShares { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // blind sensitive data stored by the structure + write!(f, "SecretShares{{ ... }}") + } +} + +impl ops::Deref for SecretShares { + type Target = [Scalar]; + fn deref(&self) -> &Self::Target { + &self.shares + } +} + #[cfg(test)] mod tests { use super::*; @@ -277,15 +304,11 @@ mod tests { test_for_all_curves!(test_secret_sharing_3_out_of_5_at_indices); - fn test_secret_sharing_3_out_of_5_at_indices

() - where - P: ECPoint + Clone, - P::Scalar: Clone + PartialEq + std::fmt::Debug, - { - let secret: P::Scalar = ECScalar::new_random(); + fn test_secret_sharing_3_out_of_5_at_indices() { + let secret = Scalar::random(); let parties = [1, 2, 4, 5, 6]; let (vss_scheme, secret_shares) = - VerifiableSS::

::share_at_indices(3, 5, &secret, &parties); + VerifiableSS::::share_at_indices(3, 5, &secret, &parties); let shares_vec = vec![ secret_shares[0].clone(), @@ -302,14 +325,10 @@ mod tests { test_for_all_curves!(test_secret_sharing_3_out_of_5); - fn test_secret_sharing_3_out_of_5

() - where - P: ECPoint + Clone, - P::Scalar: Clone + PartialEq + std::fmt::Debug, - { - let secret: P::Scalar = ECScalar::new_random(); + fn test_secret_sharing_3_out_of_5() { + let secret = Scalar::random(); - let (vss_scheme, secret_shares) = VerifiableSS::

::share(3, 5, &secret); + let (vss_scheme, secret_shares) = VerifiableSS::::share(3, 5, &secret); let shares_vec = vec![ secret_shares[0].clone(), @@ -329,36 +348,32 @@ mod tests { assert!(valid3.is_ok()); assert!(valid1.is_ok()); - let g: P = ECPoint::generator(); - let share1_public = g * secret_shares[0].clone(); + let g = Point::generator(); + let share1_public = g * &secret_shares[0]; let valid1_public = vss_scheme.validate_share_public(&share1_public, 1); assert!(valid1_public.is_ok()); // test map (t,n) - (t',t') let s = &vec![0, 1, 2, 3, 4]; - let l0 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 0, &s); - let l1 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 1, &s); - let l2 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 2, &s); - let l3 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 3, &s); - let l4 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 4, &s); - let w = l0 * secret_shares[0].clone() - + l1 * secret_shares[1].clone() - + l2 * secret_shares[2].clone() - + l3 * secret_shares[3].clone() - + l4 * secret_shares[4].clone(); + let l0 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 0, &s); + let l1 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 1, &s); + let l2 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 2, &s); + let l3 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 3, &s); + let l4 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 4, &s); + let w = l0 * &secret_shares[0] + + l1 * &secret_shares[1] + + l2 * &secret_shares[2] + + l3 * &secret_shares[3] + + l4 * &secret_shares[4]; assert_eq!(w, secret_reconstructed); } test_for_all_curves!(test_secret_sharing_3_out_of_7); - fn test_secret_sharing_3_out_of_7

() - where - P: ECPoint + Clone, - P::Scalar: Clone + PartialEq + std::fmt::Debug, - { - let secret: P::Scalar = ECScalar::new_random(); + fn test_secret_sharing_3_out_of_7() { + let secret = Scalar::random(); - let (vss_scheme, secret_shares) = VerifiableSS::

::share(3, 7, &secret); + let (vss_scheme, secret_shares) = VerifiableSS::::share(3, 7, &secret); let shares_vec = vec![ secret_shares[0].clone(), @@ -379,30 +394,26 @@ mod tests { // test map (t,n) - (t',t') let s = &vec![0, 1, 3, 4, 6]; - let l0 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 0, &s); - let l1 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 1, &s); - let l3 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 3, &s); - let l4 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 4, &s); - let l6 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 6, &s); - - let w = l0 * secret_shares[0].clone() - + l1 * secret_shares[1].clone() - + l3 * secret_shares[3].clone() - + l4 * secret_shares[4].clone() - + l6 * secret_shares[6].clone(); + let l0 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 0, &s); + let l1 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 1, &s); + let l3 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 3, &s); + let l4 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 4, &s); + let l6 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 6, &s); + + let w = l0 * &secret_shares[0] + + l1 * &secret_shares[1] + + l3 * &secret_shares[3] + + l4 * &secret_shares[4] + + l6 * &secret_shares[6]; assert_eq!(w, secret_reconstructed); } test_for_all_curves!(test_secret_sharing_1_out_of_2); - fn test_secret_sharing_1_out_of_2

() - where - P: ECPoint + Clone, - P::Scalar: Clone + PartialEq + std::fmt::Debug, - { - let secret: P::Scalar = ECScalar::new_random(); + fn test_secret_sharing_1_out_of_2() { + let secret = Scalar::random(); - let (vss_scheme, secret_shares) = VerifiableSS::

::share(1, 2, &secret); + let (vss_scheme, secret_shares) = VerifiableSS::::share(1, 2, &secret); let shares_vec = vec![secret_shares[0].clone(), secret_shares[1].clone()]; @@ -418,34 +429,30 @@ mod tests { // test map (t,n) - (t',t') let s = &vec![0, 1]; - let l0 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 0, &s); - let l1 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 1, &s); - let w = l0 * secret_shares[0].clone() + l1 * secret_shares[1].clone(); + let l0 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 0, &s); + let l1 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 1, &s); + let w = l0 * &secret_shares[0] + l1 * &secret_shares[1]; assert_eq!(w, secret_reconstructed); } test_for_all_curves!(test_secret_sharing_1_out_of_3); - fn test_secret_sharing_1_out_of_3

() - where - P: ECPoint + Clone + std::fmt::Debug, - P::Scalar: Clone + PartialEq + std::fmt::Debug, - { - let secret: P::Scalar = ECScalar::new_random(); + fn test_secret_sharing_1_out_of_3() { + let secret = Scalar::random(); - let (vss_scheme, secret_shares) = VerifiableSS::

::share(1, 3, &secret); + let (vss_scheme, secret_shares) = VerifiableSS::::share(1, 3, &secret); let shares_vec = vec![secret_shares[0].clone(), secret_shares[1].clone()]; // test commitment to point and sum of commitments - let (vss_scheme2, secret_shares2) = VerifiableSS::

::share(1, 3, &secret); - let sum = secret_shares[0].clone() + secret_shares2[0].clone(); + let (vss_scheme2, secret_shares2) = VerifiableSS::::share(1, 3, &secret); + let sum = &secret_shares[0] + &secret_shares2[0]; let point_comm1 = vss_scheme.get_point_commitment(1); let point_comm2 = vss_scheme.get_point_commitment(2); - let g: P = ECPoint::generator(); - let g_sum = g.clone() * sum; - assert_eq!(g.clone() * secret_shares[0].clone(), point_comm1); - assert_eq!(g * secret_shares[1].clone(), point_comm2); + let g = Point::generator(); + let g_sum = g * sum; + assert_eq!(g * &secret_shares[0], point_comm1); + assert_eq!(g * &secret_shares[1], point_comm2); let point1_sum_com = vss_scheme.get_point_commitment(1) + vss_scheme2.get_point_commitment(1); assert_eq!(point1_sum_com, g_sum); @@ -462,28 +469,24 @@ mod tests { // test map (t,n) - (t',t') let s = &vec![0, 2]; - let l0 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 0, &s); - let l2 = VerifiableSS::

::map_share_to_new_params(&vss_scheme.parameters, 2, &s); + let l0 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 0, &s); + let l2 = VerifiableSS::::map_share_to_new_params(&vss_scheme.parameters, 2, &s); - let w = l0 * secret_shares[0].clone() + l2 * secret_shares[2].clone(); + let w = l0 * &secret_shares[0] + l2 * &secret_shares[2]; assert_eq!(w, secret_reconstructed); } test_for_all_curves!(test_secret_resharing); - fn test_secret_resharing

() - where - P: ECPoint + Clone + std::fmt::Debug, - P::Scalar: Clone + PartialEq + std::fmt::Debug, - { - let secret: P::Scalar = ECScalar::new_random(); + fn test_secret_resharing() { + let secret = Scalar::random(); - let (vss_scheme, secret_shares) = VerifiableSS::

::share(1, 3, &secret); + let (vss_scheme, secret_shares) = VerifiableSS::::share(1, 3, &secret); let (new_vss_scheme, zero_secret_shares) = vss_scheme.reshare(); - let new_share_party_1 = secret_shares[0].clone() + zero_secret_shares[0].clone(); - let new_share_party_2 = secret_shares[1].clone() + zero_secret_shares[1].clone(); - let new_share_party_3 = secret_shares[2].clone() + zero_secret_shares[2].clone(); + let new_share_party_1 = &secret_shares[0] + &zero_secret_shares[0]; + let new_share_party_2 = &secret_shares[1] + &zero_secret_shares[1]; + let new_share_party_3 = &secret_shares[2] + &zero_secret_shares[2]; let shares_vec = vec![new_share_party_1.clone(), new_share_party_3.clone()]; diff --git a/src/cryptographic_primitives/secret_sharing/mod.rs b/src/cryptographic_primitives/secret_sharing/mod.rs index 56ebfc91..f50534df 100644 --- a/src/cryptographic_primitives/secret_sharing/mod.rs +++ b/src/cryptographic_primitives/secret_sharing/mod.rs @@ -6,3 +6,6 @@ */ pub mod feldman_vss; +mod polynomial; + +pub use polynomial::Polynomial; diff --git a/src/cryptographic_primitives/secret_sharing/polynomial.rs b/src/cryptographic_primitives/secret_sharing/polynomial.rs new file mode 100644 index 00000000..96da82ed --- /dev/null +++ b/src/cryptographic_primitives/secret_sharing/polynomial.rs @@ -0,0 +1,330 @@ +use std::convert::TryFrom; +use std::{iter, ops}; + +use crate::elliptic::curves::{Curve, Scalar}; + +/// Polynomial of some degree `n` +/// +/// Polynomial has a form: `f(x) = a_0 + a_1 * x^1 + ... + a_(n-1) * x^(n-1) + a_n * x^n`. +/// +/// Coefficients `a_i` and indeterminate `x` are scalars in curve prime field, +/// ie. their type is `ECScalar` implementor. +#[derive(Clone, Debug)] +pub struct Polynomial { + coefficients: Vec>, +} + +impl Polynomial { + /// Constructs polynomial `f(x)` from list of coefficients `a` + /// + /// ## Order + /// + /// `a[i]` should corresponds to coefficient `a_i` of polynomial `f(x) = ... + a_i * x^i + ...` + /// + /// ## Polynomial degree + /// + /// Note that it's not guaranteed that constructed polynomial degree equals to `coefficients.len()-1` + /// as it's allowed to end with zero coefficients. Actual polynomial degree equals to index of last + /// non-zero coefficient or zero if all the coefficients are zero. + /// + /// ## Example + /// + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::{Scalar, Point, Secp256k1}; + /// + /// let coefs = vec![Scalar::random(), Scalar::random()]; + /// let poly = Polynomial::::from_coefficients(coefs.clone()); + /// + /// assert_eq!(coefs, poly.coefficients()); + /// ``` + pub fn from_coefficients(coefficients: Vec>) -> Self { + Self { coefficients } + } + + /// Sample a random polynomial of given degree + /// + /// ## Example + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::Secp256k1; + /// + /// let polynomial = Polynomial::::sample_exact(3); + /// assert_eq!(polynomial.degree(), 3); + /// ``` + pub fn sample_exact(degree: u16) -> Self { + Self::from_coefficients( + iter::repeat_with(Scalar::random) + .take(usize::from(degree + 1)) + .collect(), + ) + } + + /// Samples random polynomial of degree `n` with fixed constant term (ie. `a_0 = constant_term`) + /// + /// ## Example + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::{Secp256k1, Scalar}; + /// + /// let const_term = Scalar::::random(); + /// let polynomial = Polynomial::::sample_exact_with_fixed_const_term(3, const_term.clone()); + /// assert_eq!(polynomial.degree(), 3); + /// assert_eq!(polynomial.evaluate(&Scalar::zero()), const_term); + /// ``` + pub fn sample_exact_with_fixed_const_term(n: u16, const_term: Scalar) -> Self { + if n == 0 { + Self::from_coefficients(vec![const_term]) + } else { + let random_coefficients = iter::repeat_with(Scalar::random).take(usize::from(n)); + Self::from_coefficients(iter::once(const_term).chain(random_coefficients).collect()) + } + } + + /// Returns degree `d` of polynomial `f(x)`: `d = deg(f)` + /// + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::{Secp256k1, Scalar}; + /// + /// let polynomial = Polynomial::::from_coefficients(vec![ + /// Scalar::from(1), Scalar::from(2), + /// ]); + /// assert_eq!(polynomial.degree(), 1); + /// + /// let polynomial = Polynomial::::from_coefficients(vec![ + /// Scalar::from(1), Scalar::zero(), + /// ]); + /// assert_eq!(polynomial.degree(), 0); + /// ``` + pub fn degree(&self) -> u16 { + let i = self + .coefficients() + .iter() + .enumerate() + .rev() + .find(|(_, a)| !a.is_zero()) + .map(|(i, _)| i) + .unwrap_or(0); + u16::try_from(i).expect("polynomial degree guaranteed to fit into u16") + } + + /// Takes scalar `x` and evaluates `f(x)` + /// + /// ## Example + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::{Secp256k1, Scalar}; + /// + /// let polynomial = Polynomial::::sample_exact(2); + /// + /// let x = Scalar::from(10); + /// let y = polynomial.evaluate(&x); + /// + /// let a = polynomial.coefficients(); + /// assert_eq!(y, &a[0] + &a[1] * &x + &a[2] * &x*&x); + /// ``` + pub fn evaluate(&self, point_x: &Scalar) -> Scalar { + let mut reversed_coefficients = self.coefficients.iter().rev(); + let head = reversed_coefficients + .next() + .expect("at least one coefficient is guaranteed to be present"); + let tail = reversed_coefficients; + tail.fold(head.clone(), |partial, coef| { + let partial_times_point_x = partial * point_x; + partial_times_point_x + coef + }) + } + + /// Takes point `x` that's convertable to BigInt, and evaluates `f(x)` + /// + /// ## Example + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::{Secp256k1, Scalar}; + /// + /// let polynomial = Polynomial::::sample_exact(2); + /// + /// let x: u16 = 10; + /// let y: Scalar = polynomial.evaluate_bigint(x); + /// + /// let a = polynomial.coefficients(); + /// let x = Scalar::from(x); + /// assert_eq!(y, &a[0] + &a[1] * &x + &a[2] * &x*&x); + /// ``` + pub fn evaluate_bigint(&self, point_x: B) -> Scalar + where + Scalar: From, + { + self.evaluate(&Scalar::from(point_x)) + } + + /// Takes list of points `xs` and returns iterator over `f(xs[i])` + /// + /// ## Example + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::{Secp256k1, Scalar}; + /// + /// let polynomial = Polynomial::::sample_exact(2); + /// + /// let xs = &[Scalar::from(10), Scalar::from(11)]; + /// let ys = polynomial.evaluate_many(xs); + /// + /// let a = polynomial.coefficients(); + /// for (y, x) in ys.zip(xs) { + /// assert_eq!(y, &a[0] + &a[1] * x + &a[2] * x*x); + /// } + /// ``` + pub fn evaluate_many<'i, I>(&'i self, points_x: I) -> impl Iterator> + 'i + where + I: IntoIterator> + 'i, + { + points_x.into_iter().map(move |x| self.evaluate(x)) + } + + /// Takes a list of points `xs` that are convertable to BigInt, and returns iterator over + /// `f(xs[i])`. + /// + /// ## Example + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::{Secp256k1, Scalar}; + /// + /// let polynomial = Polynomial::::sample_exact(2); + /// + /// let xs: &[u16] = &[10, 11]; + /// let ys = polynomial.evaluate_many_bigint(xs.iter().copied()); + /// + /// let a = polynomial.coefficients(); + /// for (y, x) in ys.zip(xs) { + /// let x = Scalar::from(*x); + /// assert_eq!(y, &a[0] + &a[1] * &x + &a[2] * &x*&x); + /// } + /// ``` + pub fn evaluate_many_bigint<'i, B, I>( + &'i self, + points_x: I, + ) -> impl Iterator> + 'i + where + I: IntoIterator + 'i, + Scalar: From, + { + points_x.into_iter().map(move |x| self.evaluate_bigint(x)) + } + + /// Returns list of polynomial coefficients `a`: `a[i]` corresponds to coefficient `a_i` of + /// polynomial `f(x) = ... + a_i * x^i + ...` + /// + /// ## Example + /// + /// ```rust + /// # use curv::cryptographic_primitives::secret_sharing::Polynomial; + /// use curv::elliptic::curves::{Secp256k1, Scalar}; + /// + /// let polynomial = Polynomial::::sample_exact(3); + /// let a = polynomial.coefficients(); + /// let x = Scalar::::random(); + /// assert_eq!(polynomial.evaluate(&x), &a[0] + &a[1] * &x + &a[2] * &x*&x + &a[3] * &x*&x*&x); + /// ``` + pub fn coefficients(&self) -> &[Scalar] { + &self.coefficients + } +} + +/// Multiplies polynomial `f(x)` at scalar `s`, returning resulting polynomial `g(x) = s * f(x)` +/// +/// ## Example +/// +/// ```rust +/// # use curv::cryptographic_primitives::secret_sharing::Polynomial; +/// use curv::elliptic::curves::{Secp256k1, Scalar}; +/// +/// let f = Polynomial::::sample_exact(3); +/// +/// let s = Scalar::::random(); +/// let g = &f * &s; +/// +/// for (f_coef, g_coef) in f.coefficients().iter().zip(g.coefficients()) { +/// assert_eq!(&(f_coef * &s), g_coef); +/// } +/// ``` +impl ops::Mul<&Scalar> for &Polynomial { + type Output = Polynomial; + fn mul(self, scalar: &Scalar) -> Self::Output { + let coefficients = self.coefficients.iter().map(|c| c * scalar).collect(); + Polynomial::from_coefficients(coefficients) + } +} + +/// Adds two polynomial `f(x)` and `g(x)` returning resulting polynomial `h(x) = f(x) + g(x)` +/// +/// ## Example +/// +/// ```rust +/// # use curv::cryptographic_primitives::secret_sharing::Polynomial; +/// use curv::elliptic::curves::{Secp256k1, Scalar}; +/// +/// let f = Polynomial::::sample_exact(2); +/// let g = Polynomial::::sample_exact(3); +/// let h = &f + &g; +/// +/// let x = Scalar::::from(10); +/// assert_eq!(h.evaluate(&x), f.evaluate(&x) + g.evaluate(&x)); +/// ``` +impl ops::Add for &Polynomial { + type Output = Polynomial; + fn add(self, g: Self) -> Self::Output { + let len1 = self.coefficients.len(); + let len2 = g.coefficients.len(); + + let overlapped = self + .coefficients() + .iter() + .zip(g.coefficients()) + .map(|(f_coef, g_coef)| f_coef + g_coef); + let tail = if len1 < len2 { + &g.coefficients()[len1..] + } else { + &self.coefficients()[len2..] + }; + + Polynomial::from_coefficients(overlapped.chain(tail.iter().cloned()).collect()) + } +} + +/// Subtracts two polynomial `g(x)` from `f(x)` returning resulting polynomial `h(x) = f(x) - g(x)` +/// +/// ## Example +/// +/// ```rust +/// # use curv::cryptographic_primitives::secret_sharing::Polynomial; +/// use curv::elliptic::curves::{Secp256k1, Scalar}; +/// +/// let f = Polynomial::::sample_exact(2); +/// let g = Polynomial::::sample_exact(3); +/// let h = &f - &g; +/// +/// let x = Scalar::::from(10); +/// assert_eq!(h.evaluate(&x), f.evaluate(&x) - &g.evaluate(&x)); +/// ``` +impl ops::Sub for &Polynomial { + type Output = Polynomial; + fn sub(self, g: Self) -> Self::Output { + let len1 = self.coefficients.len(); + let len2 = g.coefficients.len(); + + let overlapped = self + .coefficients() + .iter() + .zip(g.coefficients()) + .map(|(f_coef, g_coef)| f_coef - g_coef); + let tail = if len1 < len2 { + g.coefficients()[len1..].iter().map(|x| -x).collect() + } else { + self.coefficients()[len2..].to_vec() + }; + + Polynomial::from_coefficients(overlapped.chain(tail.into_iter()).collect()) + } +} diff --git a/src/cryptographic_primitives/twoparty/coin_flip_optimal_rounds.rs b/src/cryptographic_primitives/twoparty/coin_flip_optimal_rounds.rs index b80c031d..749b9f0b 100644 --- a/src/cryptographic_primitives/twoparty/coin_flip_optimal_rounds.rs +++ b/src/cryptographic_primitives/twoparty/coin_flip_optimal_rounds.rs @@ -7,95 +7,75 @@ use std::fmt::Debug; -use derivative::Derivative; use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; use crate::cryptographic_primitives::proofs::sigma_valid_pedersen::PedersenProof; use crate::cryptographic_primitives::proofs::sigma_valid_pedersen_blind::PedersenBlindingProof; -use crate::elliptic::curves::traits::*; +use crate::elliptic::curves::{Curve, Point, Scalar}; /// based on How To Simulate It – A Tutorial on the Simulation /// Proof Technique. protocol 7.3: Multiple coin tossing. which provide simulatble constant round /// coin toss -#[derive(Derivative, Serialize, Deserialize)] -#[derivative(Clone(bound = "PedersenProof

: Clone"))] -#[derivative(Debug(bound = "PedersenProof

: Debug"))] -#[derivative(PartialEq(bound = "PedersenProof

: PartialEq"))] -#[serde(bound(serialize = "PedersenProof

: Serialize"))] -#[serde(bound(deserialize = "PedersenProof

: Deserialize<'de>"))] -pub struct Party1FirstMessage { - pub proof: PedersenProof

, +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct Party1FirstMessage { + pub proof: PedersenProof, } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct Party2FirstMessage { - pub seed: P::Scalar, +#[serde(bound = "")] +pub struct Party2FirstMessage { + pub seed: Scalar, } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct Party1SecondMessage { - pub proof: PedersenBlindingProof

, - pub seed: P::Scalar, +#[serde(bound = "")] +pub struct Party1SecondMessage { + pub proof: PedersenBlindingProof, + pub seed: Scalar, } -impl

Party1FirstMessage

-where - P: ECPoint + Clone, - P::Scalar: Zeroize, -{ - pub fn commit() -> (Party1FirstMessage

, P::Scalar, P::Scalar) { - let seed: P::Scalar = ECScalar::new_random(); - let blinding: P::Scalar = ECScalar::new_random(); +impl Party1FirstMessage { + pub fn commit() -> (Party1FirstMessage, Scalar, Scalar) { + let seed = Scalar::random(); + let blinding = Scalar::random(); let proof = PedersenProof::prove(&seed, &blinding); (Party1FirstMessage { proof }, seed, blinding) } } -impl

Party2FirstMessage

-where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, -{ - pub fn share(proof: &PedersenProof

) -> Party2FirstMessage

{ +impl Party2FirstMessage { + pub fn share(proof: &PedersenProof) -> Party2FirstMessage { PedersenProof::verify(&proof).expect("{(m,r),c} proof failed"); - let seed: P::Scalar = ECScalar::new_random(); + let seed = Scalar::random(); Party2FirstMessage { seed } } } -impl

Party1SecondMessage

-where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, -{ +impl Party1SecondMessage { pub fn reveal( - party2seed: &P::Scalar, - party1seed: &P::Scalar, - party1blinding: &P::Scalar, - ) -> (Party1SecondMessage

, P::Scalar) { - let proof = PedersenBlindingProof::

::prove(&party1seed, &party1blinding); - let coin_flip_result = &party1seed.to_big_int() ^ &party2seed.to_big_int(); + party2seed: &Scalar, + party1seed: &Scalar, + party1blinding: &Scalar, + ) -> (Party1SecondMessage, Scalar) { + let proof = PedersenBlindingProof::::prove(&party1seed, &party1blinding); + let coin_flip_result = &party1seed.to_bigint() ^ &party2seed.to_bigint(); ( Party1SecondMessage { proof, seed: party1seed.clone(), }, - ECScalar::from(&coin_flip_result), + Scalar::from(&coin_flip_result), ) } } // party2 finalize -pub fn finalize

( - proof: &PedersenBlindingProof

, - party2seed: &P::Scalar, - party1comm: &P, -) -> P::Scalar -where - P: ECPoint + Clone + Debug, - P::Scalar: Zeroize + Clone, -{ - PedersenBlindingProof::

::verify(&proof).expect("{r,(m,c)} proof failed"); +pub fn finalize( + proof: &PedersenBlindingProof, + party2seed: &Scalar, + party1comm: &Point, +) -> Scalar { + PedersenBlindingProof::::verify(&proof).expect("{r,(m,c)} proof failed"); assert_eq!(&proof.com, party1comm); - let coin_flip_result = &proof.m.to_big_int() ^ &party2seed.to_big_int(); - ECScalar::from(&coin_flip_result) + let coin_flip_result = &proof.m.to_bigint() ^ &party2seed.to_bigint(); + Scalar::from(&coin_flip_result) } #[cfg(test)] @@ -103,15 +83,11 @@ mod tests { use super::*; crate::test_for_all_curves!(test_coin_toss); - pub fn test_coin_toss

() - where - P: ECPoint + Clone + Debug, - P::Scalar: PartialEq + Clone + Debug + Zeroize, - { - let (party1_first_message, m1, r1) = Party1FirstMessage::

::commit(); + pub fn test_coin_toss() { + let (party1_first_message, m1, r1) = Party1FirstMessage::::commit(); let party2_first_message = Party2FirstMessage::share(&party1_first_message.proof); let (party1_second_message, random1) = - Party1SecondMessage::

::reveal(&party2_first_message.seed, &m1, &r1); + Party1SecondMessage::::reveal(&party2_first_message.seed, &m1, &r1); let random2 = finalize( &party1_second_message.proof, &party2_first_message.seed, diff --git a/src/cryptographic_primitives/twoparty/dh_key_exchange.rs b/src/cryptographic_primitives/twoparty/dh_key_exchange.rs index ed9a4a13..7baa02c5 100644 --- a/src/cryptographic_primitives/twoparty/dh_key_exchange.rs +++ b/src/cryptographic_primitives/twoparty/dh_key_exchange.rs @@ -12,37 +12,36 @@ use serde::{Deserialize, Serialize}; -use crate::elliptic::curves::traits::*; +use crate::elliptic::curves::{Curve, Point, Scalar}; #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcKeyPair { - pub public_share: P, - secret_share: P::Scalar, +#[serde(bound = "")] +pub struct EcKeyPair { + pub public_share: Point, + secret_share: Scalar, } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Party1FirstMessage { - pub public_share: P, +#[serde(bound = "")] +pub struct Party1FirstMessage { + pub public_share: Point, } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Party2FirstMessage { - pub public_share: P, +#[serde(bound = "")] +pub struct Party2FirstMessage { + pub public_share: Point, } #[derive(Debug, Serialize, Deserialize)] pub struct Party2SecondMessage {} -impl

Party1FirstMessage

-where - P: ECPoint + Clone, - P::Scalar: Clone, -{ - pub fn first() -> (Party1FirstMessage

, EcKeyPair

) { - let base: P = ECPoint::generator(); +impl Party1FirstMessage { + pub fn first() -> (Party1FirstMessage, EcKeyPair) { + let base = Point::::generator(); - let secret_share: P::Scalar = ECScalar::new_random(); + let secret_share = Scalar::random(); - let public_share = base * secret_share.clone(); + let public_share = base * &secret_share; let ec_key_pair = EcKeyPair { public_share: public_share.clone(), @@ -52,10 +51,9 @@ where } pub fn first_with_fixed_secret_share( - secret_share: P::Scalar, - ) -> (Party1FirstMessage

, EcKeyPair

) { - let base: P = ECPoint::generator(); - let public_share = base * secret_share.clone(); + secret_share: Scalar, + ) -> (Party1FirstMessage, EcKeyPair) { + let public_share = Point::generator() * secret_share.clone(); let ec_key_pair = EcKeyPair { public_share: public_share.clone(), @@ -65,15 +63,11 @@ where } } -impl

Party2FirstMessage

-where - P: ECPoint + Clone, - P::Scalar: Clone, -{ - pub fn first() -> (Party2FirstMessage

, EcKeyPair

) { - let base: P = ECPoint::generator(); - let secret_share: P::Scalar = ECScalar::new_random(); - let public_share = base * secret_share.clone(); +impl Party2FirstMessage { + pub fn first() -> (Party2FirstMessage, EcKeyPair) { + let base = Point::::generator(); + let secret_share = Scalar::random(); + let public_share = base * &secret_share; let ec_key_pair = EcKeyPair { public_share: public_share.clone(), secret_share, @@ -82,10 +76,9 @@ where } pub fn first_with_fixed_secret_share( - secret_share: P::Scalar, - ) -> (Party2FirstMessage

, EcKeyPair

) { - let base: P = ECPoint::generator(); - let public_share = base * secret_share.clone(); + secret_share: Scalar, + ) -> (Party2FirstMessage, EcKeyPair) { + let public_share = Point::generator() * &secret_share; let ec_key_pair = EcKeyPair { public_share: public_share.clone(), secret_share, @@ -94,32 +87,25 @@ where } } -pub fn compute_pubkey

(local_share: &EcKeyPair

, other_share_public_share: &P) -> P -where - P: ECPoint + Clone, - P::Scalar: Clone, -{ - other_share_public_share.clone() * local_share.secret_share.clone() +pub fn compute_pubkey( + local_share: &EcKeyPair, + other_share_public_share: &Point, +) -> Point { + other_share_public_share * &local_share.secret_share } #[cfg(test)] mod tests { - use std::fmt::Debug; - - use crate::arithmetic::traits::*; use crate::cryptographic_primitives::twoparty::dh_key_exchange::*; - use crate::elliptic::curves::traits::ECScalar; + use crate::elliptic::curves::Curve; use crate::test_for_all_curves; use crate::BigInt; + use std::convert::TryFrom; test_for_all_curves!(test_dh_key_exchange_random_shares); - fn test_dh_key_exchange_random_shares

() - where - P: ECPoint + Clone + Debug, - P::Scalar: Clone, - { - let (kg_party_one_first_message, kg_ec_key_pair_party1) = Party1FirstMessage::

::first(); - let (kg_party_two_first_message, kg_ec_key_pair_party2) = Party2FirstMessage::

::first(); + fn test_dh_key_exchange_random_shares() { + let (kg_party_one_first_message, kg_ec_key_pair_party1) = Party1FirstMessage::::first(); + let (kg_party_two_first_message, kg_ec_key_pair_party2) = Party2FirstMessage::::first(); assert_eq!( compute_pubkey( @@ -134,15 +120,11 @@ mod tests { } test_for_all_curves!(test_dh_key_exchange_fixed_shares); - fn test_dh_key_exchange_fixed_shares

() - where - P: ECPoint + Clone + Debug, - P::Scalar: Clone, - { - let secret_party_1: P::Scalar = ECScalar::from(&BigInt::one()); + fn test_dh_key_exchange_fixed_shares() { + let secret_party_1 = Scalar::try_from(&BigInt::from(1)).unwrap(); let (kg_party_one_first_message, kg_ec_key_pair_party1) = - Party1FirstMessage::

::first_with_fixed_secret_share(secret_party_1); - let secret_party_2: P::Scalar = ECScalar::from(&BigInt::from(2)); + Party1FirstMessage::::first_with_fixed_secret_share(secret_party_1); + let secret_party_2 = Scalar::try_from(&BigInt::from(2)).unwrap(); let (kg_party_two_first_message, kg_ec_key_pair_party2) = Party2FirstMessage::first_with_fixed_secret_share(secret_party_2.clone()); @@ -157,13 +139,12 @@ mod tests { &kg_party_two_first_message.public_share ) ); - let g: P = ECPoint::generator(); assert_eq!( compute_pubkey( &kg_ec_key_pair_party2, &kg_party_one_first_message.public_share ), - g * secret_party_2 + Point::generator() * secret_party_2 ); } } diff --git a/src/cryptographic_primitives/twoparty/dh_key_exchange_variant_with_pok_comm.rs b/src/cryptographic_primitives/twoparty/dh_key_exchange_variant_with_pok_comm.rs index 5e9faeb7..b9f2165c 100644 --- a/src/cryptographic_primitives/twoparty/dh_key_exchange_variant_with_pok_comm.rs +++ b/src/cryptographic_primitives/twoparty/dh_key_exchange_variant_with_pok_comm.rs @@ -15,7 +15,6 @@ /// https://eprint.iacr.org/2017/552.pdf protocol 3.1 first 3 steps. use std::fmt::Debug; -use derivative::Derivative; use serde::{Deserialize, Serialize}; use crate::arithmetic::traits::*; @@ -23,28 +22,25 @@ use crate::cryptographic_primitives::commitments::hash_commitment::HashCommitmen use crate::cryptographic_primitives::commitments::traits::Commitment; use crate::cryptographic_primitives::proofs::sigma_dlog::*; use crate::cryptographic_primitives::proofs::ProofError; -use crate::elliptic::curves::traits::*; +use crate::elliptic::curves::{Curve, Point, Scalar}; use crate::BigInt; -use zeroize::Zeroize; const SECURITY_BITS: usize = 256; #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcKeyPair { - pub public_share: P, - secret_share: P::Scalar, +#[serde(bound = "")] +pub struct EcKeyPair { + pub public_share: Point, + secret_share: Scalar, } -#[derive(Serialize, Deserialize, Derivative)] -#[derivative(Clone(bound = "P: Clone, P::Scalar: Clone"))] -#[derivative(Debug(bound = "P: Debug, P::Scalar: Debug"))] -#[serde(bound(serialize = "P: Serialize, P::Scalar: Serialize"))] -#[serde(bound(deserialize = "P: Deserialize<'de>, P::Scalar: Deserialize<'de>"))] -pub struct CommWitness { +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct CommWitness { pub pk_commitment_blind_factor: BigInt, pub zk_pok_blind_factor: BigInt, - pub public_share: P, - pub d_log_proof: DLogProof

, + pub public_share: Point, + pub d_log_proof: DLogProof, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -52,52 +48,42 @@ pub struct Party1FirstMessage { pub pk_commitment: BigInt, pub zk_pok_commitment: BigInt, } -#[derive(Serialize, Deserialize, Derivative)] -#[derivative(Clone(bound = "P: Clone, P::Scalar: Clone"))] -#[derivative(Debug(bound = "P: Debug, P::Scalar: Debug"))] -#[serde(bound(serialize = "P: Serialize, P::Scalar: Serialize"))] -#[serde(bound(deserialize = "P: Deserialize<'de>, P::Scalar: Deserialize<'de>"))] -pub struct Party2FirstMessage { - pub d_log_proof: DLogProof

, - pub public_share: P, + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct Party2FirstMessage { + pub d_log_proof: DLogProof, + pub public_share: Point, } -#[derive(Serialize, Deserialize, Derivative)] -#[derivative(Debug(bound = "P: Debug, P::Scalar: Debug"))] -#[serde(bound(serialize = "P: Serialize, P::Scalar: Serialize"))] -#[serde(bound(deserialize = "P: Deserialize<'de>, P::Scalar: Deserialize<'de>"))] -pub struct Party1SecondMessage { - pub comm_witness: CommWitness

, +#[derive(Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct Party1SecondMessage { + pub comm_witness: CommWitness, } #[derive(Debug, Serialize, Deserialize)] pub struct Party2SecondMessage {} impl Party1FirstMessage { - pub fn create_commitments

() -> (Party1FirstMessage, CommWitness

, EcKeyPair

) - where - P: ECPoint + Clone, - P::Scalar: Zeroize, - { - let base: P = ECPoint::generator(); + pub fn create_commitments() -> (Party1FirstMessage, CommWitness, EcKeyPair) { + let base = Point::::generator(); - let secret_share: P::Scalar = ECScalar::new_random(); + let secret_share = Scalar::random(); - let public_share = base.scalar_mul(&secret_share.get_element()); + let public_share = base * &secret_share; - let d_log_proof = DLogProof::

::prove(&secret_share); + let d_log_proof = DLogProof::::prove(&secret_share); // we use hash based commitment let pk_commitment_blind_factor = BigInt::sample(SECURITY_BITS); let pk_commitment = HashCommitment::create_commitment_with_user_defined_randomness( - &public_share.bytes_compressed_to_big_int(), + &BigInt::from_bytes(&public_share.to_bytes(true)), &pk_commitment_blind_factor, ); let zk_pok_blind_factor = BigInt::sample(SECURITY_BITS); let zk_pok_commitment = HashCommitment::create_commitment_with_user_defined_randomness( - &d_log_proof - .pk_t_rand_commitment - .bytes_compressed_to_big_int(), + &BigInt::from_bytes(&d_log_proof.pk_t_rand_commitment.to_bytes(true)), &zk_pok_blind_factor, ); let ec_key_pair = EcKeyPair { @@ -119,29 +105,23 @@ impl Party1FirstMessage { ) } - pub fn create_commitments_with_fixed_secret_share

( - secret_share: P::Scalar, - ) -> (Party1FirstMessage, CommWitness

, EcKeyPair

) - where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, - { - let base: P = ECPoint::generator(); - let public_share = base * secret_share.clone(); + pub fn create_commitments_with_fixed_secret_share( + secret_share: Scalar, + ) -> (Party1FirstMessage, CommWitness, EcKeyPair) { + let base = Point::::generator(); + let public_share = base * &secret_share; - let d_log_proof = DLogProof::

::prove(&secret_share); + let d_log_proof = DLogProof::::prove(&secret_share); let pk_commitment_blind_factor = BigInt::sample(SECURITY_BITS); let pk_commitment = HashCommitment::create_commitment_with_user_defined_randomness( - &public_share.bytes_compressed_to_big_int(), + &BigInt::from_bytes(&public_share.to_bytes(true)), &pk_commitment_blind_factor, ); let zk_pok_blind_factor = BigInt::sample(SECURITY_BITS); let zk_pok_commitment = HashCommitment::create_commitment_with_user_defined_randomness( - &d_log_proof - .pk_t_rand_commitment - .bytes_compressed_to_big_int(), + &BigInt::from_bytes(&d_log_proof.pk_t_rand_commitment.to_bytes(true)), &zk_pok_blind_factor, ); @@ -165,28 +145,20 @@ impl Party1FirstMessage { } } -impl

Party1SecondMessage

-where - P: ECPoint + Clone, - P::Scalar: Zeroize, -{ +impl Party1SecondMessage { pub fn verify_and_decommit( - comm_witness: CommWitness

, - proof: &DLogProof

, - ) -> Result, ProofError> { + comm_witness: CommWitness, + proof: &DLogProof, + ) -> Result, ProofError> { DLogProof::verify(proof)?; Ok(Party1SecondMessage { comm_witness }) } } -impl

Party2FirstMessage

-where - P: ECPoint + Clone, - P::Scalar: Zeroize + Clone, -{ - pub fn create() -> (Party2FirstMessage

, EcKeyPair

) { - let base: P = ECPoint::generator(); - let secret_share: P::Scalar = ECScalar::new_random(); - let public_share = base * secret_share.clone(); +impl Party2FirstMessage { + pub fn create() -> (Party2FirstMessage, EcKeyPair) { + let base = Point::::generator(); + let secret_share = Scalar::random(); + let public_share = base * &secret_share; let d_log_proof = DLogProof::prove(&secret_share); let ec_key_pair = EcKeyPair { public_share: public_share.clone(), @@ -202,10 +174,10 @@ where } pub fn create_with_fixed_secret_share( - secret_share: P::Scalar, - ) -> (Party2FirstMessage

, EcKeyPair

) { - let base: P = ECPoint::generator(); - let public_share = base * secret_share.clone(); + secret_share: Scalar, + ) -> (Party2FirstMessage, EcKeyPair) { + let base = Point::generator(); + let public_share = base * &secret_share; let d_log_proof = DLogProof::prove(&secret_share); let ec_key_pair = EcKeyPair { public_share: public_share.clone(), @@ -222,19 +194,18 @@ where } impl Party2SecondMessage { - pub fn verify_commitments_and_dlog_proof

( + pub fn verify_commitments_and_dlog_proof( party_one_first_message: &Party1FirstMessage, - party_one_second_message: &Party1SecondMessage

, - ) -> Result - where - P: ECPoint + Clone, - P::Scalar: Zeroize, - { + party_one_second_message: &Party1SecondMessage, + ) -> Result { let party_one_pk_commitment = &party_one_first_message.pk_commitment; let party_one_zk_pok_commitment = &party_one_first_message.zk_pok_commitment; let party_one_zk_pok_blind_factor = &party_one_second_message.comm_witness.zk_pok_blind_factor; let party_one_public_share = &party_one_second_message.comm_witness.public_share; + if party_one_public_share.is_zero() { + return Err(ProofError); + } let party_one_pk_commitment_blind_factor = &party_one_second_message .comm_witness .pk_commitment_blind_factor; @@ -243,7 +214,7 @@ impl Party2SecondMessage { let mut flag = true; if party_one_pk_commitment != &HashCommitment::create_commitment_with_user_defined_randomness( - &party_one_public_share.bytes_compressed_to_big_int(), + &BigInt::from_bytes(&party_one_public_share.to_bytes(true)), &party_one_pk_commitment_blind_factor, ) { @@ -252,9 +223,7 @@ impl Party2SecondMessage { if party_one_zk_pok_commitment != &HashCommitment::create_commitment_with_user_defined_randomness( - &party_one_d_log_proof - .pk_t_rand_commitment - .bytes_compressed_to_big_int(), + &BigInt::from_bytes(&party_one_d_log_proof.pk_t_rand_commitment.to_bytes(true)), &party_one_zk_pok_blind_factor, ) { @@ -266,12 +235,11 @@ impl Party2SecondMessage { Ok(Party2SecondMessage {}) } } -pub fn compute_pubkey

(local_share: &EcKeyPair

, other_share_public_share: &P) -> P -where - P: ECPoint + Clone, - P::Scalar: Clone, -{ - other_share_public_share.clone() * local_share.secret_share.clone() +pub fn compute_pubkey( + local_share: &EcKeyPair, + other_share_public_share: &Point, +) -> Point { + other_share_public_share * &local_share.secret_share } #[cfg(test)] @@ -279,14 +247,10 @@ mod tests { use crate::cryptographic_primitives::twoparty::dh_key_exchange_variant_with_pok_comm::*; crate::test_for_all_curves!(test_dh_key_exchange); - fn test_dh_key_exchange

() - where - P: ECPoint + Clone + Debug, - P::Scalar: Zeroize + Clone, - { + fn test_dh_key_exchange() { let (kg_party_one_first_message, kg_comm_witness, kg_ec_key_pair_party1) = - Party1FirstMessage::create_commitments::

(); - let (kg_party_two_first_message, kg_ec_key_pair_party2) = Party2FirstMessage::

::create(); + Party1FirstMessage::create_commitments::(); + let (kg_party_two_first_message, kg_ec_key_pair_party2) = Party2FirstMessage::::create(); let kg_party_one_second_message = Party1SecondMessage::verify_and_decommit( kg_comm_witness, &kg_party_two_first_message.d_log_proof, diff --git a/src/cryptographic_primitives/twoparty/mod.rs b/src/cryptographic_primitives/twoparty/mod.rs index d4cb0d8b..8acb0baf 100644 --- a/src/cryptographic_primitives/twoparty/mod.rs +++ b/src/cryptographic_primitives/twoparty/mod.rs @@ -8,7 +8,7 @@ /// This is an implementation of string coin tossing of a string, to generate a random string between ///two non-trusting parties. Based on the /// the protocol and proof analysis in "How To Simulate It – A Tutorial on the Simulation -/// Proof Technique∗" (https://eprint.iacr.org/2016/046.pdf) +/// Proof Technique∗" () pub mod coin_flip_optimal_rounds; ///This is an implementation of a Diffie Hellman Key Exchange. @@ -26,5 +26,5 @@ pub mod dh_key_exchange; /// party1 verifies party2 proof decommit to P1 and to the PoK /// party2 verifies party1 proof /// the shared secret is Q = xyG -/// reference can be found in protocol 3.1 step 1 - 3(b) in the paper https://eprint.iacr.org/2017/552.pdf +/// reference can be found in protocol 3.1 step 1 - 3(b) in the paper pub mod dh_key_exchange_variant_with_pok_comm; diff --git a/src/elliptic/curves/bls12_381/g1.rs b/src/elliptic/curves/bls12_381/g1.rs index 367680a4..79a74b0e 100644 --- a/src/elliptic/curves/bls12_381/g1.rs +++ b/src/elliptic/curves/bls12_381/g1.rs @@ -5,531 +5,236 @@ License MIT: */ -pub const SECRET_KEY_SIZE: usize = 32; -pub const COMPRESSED_SIZE: usize = 48; - use std::fmt; -use std::fmt::Debug; -use std::ops::{Add, Mul, Neg}; -use std::str; -use ff_zeroize::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; -use pairing_plus::bls12_381::{Fr, G1Compressed, G1Uncompressed, G1}; +use ff_zeroize::PrimeField; +use pairing_plus::bls12_381::{G1Compressed, G1Uncompressed, G1}; use pairing_plus::hash_to_curve::HashToCurve; use pairing_plus::hash_to_field::ExpandMsgXmd; -use pairing_plus::serdes::SerDes; -use pairing_plus::EncodedPoint; use pairing_plus::{CurveAffine, CurveProjective, Engine}; +use pairing_plus::{EncodedPoint, SubgroupCheck}; use sha2::Sha256; - -use serde::de::{self, Error, MapAccess, SeqAccess, Visitor}; -use serde::ser::SerializeStruct; -use serde::ser::{Serialize, Serializer}; -use serde::{Deserialize, Deserializer}; - -pub type SK = ::Fr; -pub type PK = ::G1Affine; +use zeroize::Zeroize; use crate::arithmetic::traits::*; +use crate::elliptic::curves::traits::*; use crate::BigInt; -use crate::ErrorKey::{self}; - -use std::ptr; -use std::sync::atomic; -use zeroize::Zeroize; - -use crate::elliptic::curves::traits::ECPoint; -use crate::elliptic::curves::traits::ECScalar; -#[cfg(feature = "merkle")] -use crypto::digest::Digest; -#[cfg(feature = "merkle")] -use crypto::sha3::Sha3; -#[cfg(feature = "merkle")] -use merkle::Hashable; -use std::io::Cursor; - -#[derive(Clone, Copy)] -pub struct FieldScalar { - purpose: &'static str, - fe: SK, -} -#[derive(Clone, Copy)] -pub struct G1Point { - purpose: &'static str, - ge: PK, -} -pub type GE = G1Point; -pub type FE = FieldScalar; -impl Zeroize for FieldScalar { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, FE::zero()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } -} +use super::scalar::FieldScalar; -impl ECScalar for FieldScalar { - type SecretKey = SK; +lazy_static::lazy_static! { + static ref GENERATOR: G1Point = G1Point { + purpose: "generator", + ge: PK::one(), + }; - fn new_random() -> FieldScalar { - let rnd_bn = BigInt::sample_below(&FE::q()); - ECScalar::from(&rnd_bn) - } - - fn zero() -> FieldScalar { - FieldScalar { - purpose: "zero", - fe: SK::default(), - } - } - - fn get_element(&self) -> SK { - self.fe - } - fn set_element(&mut self, element: SK) { - self.fe = element - } - - fn from(n: &BigInt) -> FieldScalar { - let n_mod = BigInt::modulus(n, &FE::q()); - let mut v = BigInt::to_bytes(&n_mod); - let mut bytes_array: [u8; SECRET_KEY_SIZE]; - if v.len() < SECRET_KEY_SIZE { - let mut template = vec![0; SECRET_KEY_SIZE - v.len()]; - template.extend_from_slice(&v); - v = template; - } - bytes_array = [0; SECRET_KEY_SIZE]; - let bytes = &v[..SECRET_KEY_SIZE]; - bytes_array.copy_from_slice(&bytes); - - // bytes_array.reverse(); - - let mut repr = SK::default().into_repr(); - repr.read_be(Cursor::new(&bytes_array[..])).unwrap(); - FieldScalar { - purpose: "from_big_int", - fe: Fr::from_repr(repr).unwrap(), - } - } - - fn to_big_int(&self) -> BigInt { - let tmp = self.fe.into_repr(); - let scalar_u64 = tmp.as_ref(); - - let to_bn = scalar_u64.iter().rev().fold(BigInt::zero(), |acc, x| { - let element_bn = BigInt::from(*x); - element_bn + (acc << 64) - }); - to_bn - } - - fn q() -> BigInt { - let q_u64: [u64; 4] = [ - 0xffffffff00000001, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, + static ref BASE_POINT2: G1Point = { + const BASE_POINT2: [u8; 96] = [ + 10, 18, 122, 36, 178, 251, 236, 31, 139, 88, 242, 163, 21, 198, 168, 208, 122, 195, + 135, 122, 7, 153, 197, 255, 160, 0, 89, 138, 39, 245, 105, 108, 99, 113, 78, 70, 130, + 172, 183, 57, 170, 180, 39, 32, 173, 29, 238, 62, 13, 166, 109, 90, 181, 17, 76, 247, + 26, 155, 130, 211, 18, 42, 235, 137, 225, 184, 210, 140, 54, 83, 233, 228, 226, 70, + 194, 50, 55, 116, 229, 2, 115, 227, 223, 31, 165, 39, 191, 209, 49, 127, 106, 196, 123, + 71, 70, 243, ]; - let to_bn = q_u64.iter().rev().fold(BigInt::zero(), |acc, x| { - let element_bn = BigInt::from(*x); - element_bn + (acc << 64) - }); - to_bn - } - - fn add(&self, other: &SK) -> FieldScalar { - let mut add_fe = FieldScalar { - purpose: "other add", - fe: *other, - }; - add_fe.fe.add_assign(&self.fe); - FieldScalar { - purpose: "add", - fe: add_fe.fe, - } - } - - fn mul(&self, other: &SK) -> FieldScalar { - let mut mul_fe = FieldScalar { - purpose: "other mul", - fe: *other, - }; - mul_fe.fe.mul_assign(&self.fe); - FieldScalar { - purpose: "mul", - fe: mul_fe.fe, - } - } - - fn sub(&self, other: &SK) -> FieldScalar { - let mut other_neg = *other; - other_neg.negate(); - let sub_fe = FieldScalar { - purpose: "other sub", - fe: other_neg, - }; - self.add(&sub_fe.get_element()) - } - - fn invert(&self) -> FieldScalar { - let sc = self.fe; - let inv_sc = sc.inverse().unwrap(); //TODO - - FieldScalar { - purpose: "inverse", - fe: inv_sc, + let mut point = G1Uncompressed::empty(); + point.as_mut().copy_from_slice(&BASE_POINT2); + G1Point { + purpose: "base_point2", + ge: point.into_affine().expect("invalid base_point"), } - } + }; } -impl Debug for FieldScalar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Point {{ purpose: {:?}, bytes: {:?} }}", - self.purpose, self.fe, - ) - } -} - -impl PartialEq for FieldScalar { - fn eq(&self, other: &FieldScalar) -> bool { - self.get_element() == other.get_element() - } -} - -impl Mul for FieldScalar { - type Output = FieldScalar; - fn mul(self, other: FieldScalar) -> FieldScalar { - (&self).mul(&other.get_element()) - } -} +pub const SECRET_KEY_SIZE: usize = 32; +pub const COMPRESSED_SIZE: usize = 48; -impl<'o> Mul<&'o FieldScalar> for FieldScalar { - type Output = FieldScalar; - fn mul(self, other: &'o FieldScalar) -> FieldScalar { - (&self).mul(&other.get_element()) - } -} +pub type PK = ::G1Affine; -impl Add for FieldScalar { - type Output = FieldScalar; - fn add(self, other: FieldScalar) -> FieldScalar { - (&self).add(&other.get_element()) - } -} +/// Bls12-381-1 (G1) curve implementation based on [pairing_plus] library +#[derive(Debug, PartialEq, Clone)] +pub enum Bls12_381_1 {} -impl<'o> Add<&'o FieldScalar> for FieldScalar { - type Output = FieldScalar; - fn add(self, other: &'o FieldScalar) -> FieldScalar { - (&self).add(&other.get_element()) - } +#[derive(Clone, Copy)] +pub struct G1Point { + purpose: &'static str, + ge: PK, } +pub type GE1 = G1Point; -impl Serialize for FieldScalar { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_big_int().to_hex()) - } -} +impl Curve for Bls12_381_1 { + type Point = GE1; + type Scalar = FieldScalar; -impl<'de> Deserialize<'de> for FieldScalar { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(BLS12_381ScalarVisitor) - } + const CURVE_NAME: &'static str = "bls12_381_1"; } -struct BLS12_381ScalarVisitor; +impl ECPoint for G1Point { + type Scalar = FieldScalar; + type Underlying = PK; -impl<'de> Visitor<'de> for BLS12_381ScalarVisitor { - type Value = FieldScalar; + type CompressedPoint = G1Compressed; + type UncompressedPoint = G1Uncompressed; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("bls12_381") + fn zero() -> G1Point { + G1Point { + purpose: "zero", + ge: PK::zero(), + } } - fn visit_str(self, s: &str) -> Result { - let v = BigInt::from_hex(s).map_err(E::custom)?; - Ok(ECScalar::from(&v)) + fn is_zero(&self) -> bool { + self.ge.is_zero() } -} -impl Debug for G1Point { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Point {{ purpose: {:?}, bytes: {:?} }}", - self.purpose, - self.bytes_compressed_to_big_int().to_hex() - ) + fn generator() -> &'static G1Point { + &GENERATOR } -} -impl PartialEq for G1Point { - fn eq(&self, other: &G1Point) -> bool { - self.get_element() == other.get_element() - } -} - -impl Zeroize for G1Point { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, GE::generator()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); + fn base_point2() -> &'static G1Point { + &BASE_POINT2 } -} -impl ECPoint for G1Point { - type SecretKey = SK; - type PublicKey = PK; - type Scalar = FieldScalar; + fn from_coords(x: &BigInt, y: &BigInt) -> Result { + let vec_x = x.to_bytes(); + let vec_y = y.to_bytes(); + if vec_x.len() > COMPRESSED_SIZE || vec_y.len() > COMPRESSED_SIZE { + return Err(NotOnCurve); + } + let mut uncompressed = [0u8; 2 * COMPRESSED_SIZE]; + uncompressed[COMPRESSED_SIZE - vec_x.len()..COMPRESSED_SIZE].copy_from_slice(&vec_x); + uncompressed[(2 * COMPRESSED_SIZE) - vec_y.len()..].copy_from_slice(&vec_y); + debug_assert_eq!(x, &BigInt::from_bytes(&uncompressed[..COMPRESSED_SIZE])); + debug_assert_eq!(y, &BigInt::from_bytes(&uncompressed[COMPRESSED_SIZE..])); - fn base_point2() -> G1Point { - const BASE_POINT2: [u8; 96] = [ - 10, 18, 122, 36, 178, 251, 236, 31, 139, 88, 242, 163, 21, 198, 168, 208, 122, 195, - 135, 122, 7, 153, 197, 255, 160, 0, 89, 138, 39, 245, 105, 108, 99, 113, 78, 70, 130, - 172, 183, 57, 170, 180, 39, 32, 173, 29, 238, 62, 13, 166, 109, 90, 181, 17, 76, 247, - 26, 155, 130, 211, 18, 42, 235, 137, 225, 184, 210, 140, 54, 83, 233, 228, 226, 70, - 194, 50, 55, 116, 229, 2, 115, 227, 223, 31, 165, 39, 191, 209, 49, 127, 106, 196, 123, - 71, 70, 243, - ]; let mut point = G1Uncompressed::empty(); - point.as_mut().copy_from_slice(&BASE_POINT2); - G1Point { - purpose: "base_ge2", - ge: point.into_affine().expect("invalid base_point"), + point.as_mut().copy_from_slice(&uncompressed); + + Ok(G1Point { + purpose: "from_coords", + ge: point.into_affine().map_err(|_| NotOnCurve)?, + }) + } + + fn x_coord(&self) -> Option { + if self.is_zero() { + None + } else { + let uncompressed = G1Uncompressed::from_affine(self.ge); + let x_coord = &uncompressed.as_ref()[0..COMPRESSED_SIZE]; + let bn = BigInt::from_bytes(x_coord); + Some(bn) } } - fn generator() -> G1Point { - G1Point { - purpose: "base_fe", - ge: PK::one(), + fn y_coord(&self) -> Option { + if self.is_zero() { + None + } else { + let uncompressed = G1Uncompressed::from_affine(self.ge); + let y_coord = &uncompressed.as_ref()[COMPRESSED_SIZE..COMPRESSED_SIZE * 2]; + let bn = BigInt::from_bytes(y_coord); + Some(bn) } } - fn get_element(&self) -> PK { - self.ge - } - - fn x_coor(&self) -> Option { - let tmp = G1Uncompressed::from_affine(self.ge); - let bytes = tmp.as_ref(); - let x_coor = &bytes[0..COMPRESSED_SIZE]; - let bn = BigInt::from_bytes(x_coor); - Some(bn) - } - - fn y_coor(&self) -> Option { - let tmp = G1Uncompressed::from_affine(self.ge); - let bytes = tmp.as_ref(); - let y_coor = &bytes[COMPRESSED_SIZE..COMPRESSED_SIZE * 2]; - let bn = BigInt::from_bytes(y_coor); - Some(bn) - } - - fn bytes_compressed_to_big_int(&self) -> BigInt { - let tmp = G1Compressed::from_affine(self.ge); - let bytes = tmp.as_ref(); - BigInt::from_bytes(bytes) + fn coords(&self) -> Option { + if self.is_zero() { + None + } else { + let uncompressed = G1Uncompressed::from_affine(self.ge); + let x = &uncompressed.as_ref()[0..COMPRESSED_SIZE]; + let y = &uncompressed.as_ref()[COMPRESSED_SIZE..COMPRESSED_SIZE * 2]; + let x = BigInt::from_bytes(x); + let y = BigInt::from_bytes(y); + Some(PointCoords { x, y }) + } } - fn from_bytes(bytes: &[u8]) -> Result { - let mut bytes_array_comp = [0u8; COMPRESSED_SIZE]; - match bytes.len() { - 0..=COMPRESSED_SIZE => { - (&mut bytes_array_comp[COMPRESSED_SIZE - bytes.len()..]).copy_from_slice(bytes); - } - _ => { - bytes_array_comp.copy_from_slice(&bytes[..COMPRESSED_SIZE]); - } + fn serialize_compressed(&self) -> Self::CompressedPoint { + G1Compressed::from_affine(self.ge) + } + + fn serialize_uncompressed(&self) -> Self::UncompressedPoint { + G1Uncompressed::from_affine(self.ge) + } + + fn deserialize(bytes: &[u8]) -> Result { + if bytes.len() == COMPRESSED_SIZE { + let mut compressed = G1Compressed::empty(); + compressed.as_mut().copy_from_slice(bytes); + Ok(G1Point { + purpose: "deserialize", + ge: compressed.into_affine().map_err(|_| DeserializationError)?, + }) + } else if bytes.len() == 2 * COMPRESSED_SIZE { + let mut uncompressed = G1Uncompressed::empty(); + uncompressed.as_mut().copy_from_slice(bytes); + Ok(G1Point { + purpose: "deserialize", + ge: uncompressed + .into_affine() + .map_err(|_| DeserializationError)?, + }) + } else { + Err(DeserializationError) } - - let g1_comp = G1::deserialize(&mut bytes_array_comp[..].as_ref(), true).unwrap(); - let pk = G1Point { - purpose: "from_bytes", - ge: g1_comp.into_affine(), //TODO: handle error - }; - - Ok(pk) } - // in this case the opposite of from_bytes: takes compressed pk to COMPRESSED_SIZE bytes. - fn pk_to_key_slice(&self) -> Vec { - let mut compressed_vec = vec![]; - PK::serialize(&self.ge, &mut compressed_vec, true) - .expect("serializing into vec should always succeed"); - compressed_vec + fn check_point_order_equals_group_order(&self) -> bool { + !self.is_zero() && self.ge.in_subgroup() } - fn scalar_mul(&self, fe: &SK) -> G1Point { - let mut ge_proj: G1 = self.ge.into(); - ge_proj.mul_assign(fe.into_repr()); + fn scalar_mul(&self, scalar: &Self::Scalar) -> G1Point { + let result = self.ge.mul(scalar.underlying_ref().into_repr()); G1Point { - purpose: "scalar_point_mul", - ge: ge_proj.into_affine(), + purpose: "scalar_mul", + ge: result.into_affine(), } } - fn add_point(&self, other: &PK) -> G1Point { - let mut ge_proj: G1 = self.ge.into(); - ge_proj.add_assign_mixed(other); + fn add_point(&self, other: &Self) -> G1Point { + let mut result = G1::from(self.ge); + result.add_assign_mixed(&other.ge); G1Point { - purpose: "combine", - ge: ge_proj.into_affine(), + purpose: "add_point", + ge: result.into_affine(), } } - fn sub_point(&self, other: &PK) -> G1Point { - let mut ge_proj: G1 = self.ge.into(); - ge_proj.sub_assign_mixed(other); + fn sub_point(&self, other: &Self) -> G1Point { + let mut result = G1::from(self.ge); + result.sub_assign_mixed(&other.ge); G1Point { - purpose: "sub", - ge: ge_proj.into_affine(), + purpose: "sub_point", + ge: result.into_affine(), } } - fn from_coor(_x: &BigInt, _y: &BigInt) -> G1Point { - // TODO - unimplemented!(); - } -} - -impl From for G1Point { - fn from(point: PK) -> Self { + fn neg_point(&self) -> Self { + let mut result = self.ge; + result.negate(); G1Point { - purpose: "from_point", - ge: point, + purpose: "neg", + ge: result, } } -} - -impl Mul for G1Point { - type Output = G1Point; - fn mul(self, other: FieldScalar) -> G1Point { - self.scalar_mul(&other.get_element()) - } -} - -impl<'o> Mul<&'o FieldScalar> for G1Point { - type Output = G1Point; - fn mul(self, other: &'o FieldScalar) -> G1Point { - self.scalar_mul(&other.get_element()) - } -} - -impl<'o> Mul<&'o FieldScalar> for &'o G1Point { - type Output = G1Point; - fn mul(self, other: &'o FieldScalar) -> G1Point { - self.scalar_mul(&other.get_element()) - } -} - -impl Add for G1Point { - type Output = G1Point; - fn add(self, other: G1Point) -> G1Point { - self.add_point(&other.get_element()) - } -} - -impl<'o> Add<&'o G1Point> for G1Point { - type Output = G1Point; - fn add(self, other: &'o G1Point) -> G1Point { - self.add_point(&other.get_element()) - } -} -impl<'o> Add<&'o G1Point> for &'o G1Point { - type Output = G1Point; - fn add(self, other: &'o G1Point) -> G1Point { - self.add_point(&other.get_element()) - } -} - -impl Neg for G1Point { - type Output = Self; - fn neg(mut self) -> Self { + fn neg_point_assign(&mut self) { self.ge.negate(); - self.purpose = "negated"; - self } -} -#[cfg(feature = "merkle")] -impl Hashable for G1Point { - fn update_context(&self, context: &mut Sha3) { - let bytes: Vec = self.pk_to_key_slice(); - context.input(&bytes[..]); + fn underlying_ref(&self) -> &Self::Underlying { + &self.ge } -} - -impl Serialize for G1Point { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = self.pk_to_key_slice(); - let bytes_as_bn = BigInt::from_bytes(&bytes[..]); - let mut state = serializer.serialize_struct("Bls12381G1Point", 1)?; - state.serialize_field("bytes_str", &bytes_as_bn.to_hex())?; - state.end() + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.ge } -} - -impl<'de> Deserialize<'de> for G1Point { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - const FIELDS: &[&str] = &["bytes_str"]; - deserializer.deserialize_struct("Bls12381G1Point", FIELDS, Bls12381G1PointVisitor) - } -} - -struct Bls12381G1PointVisitor; - -impl<'de> Visitor<'de> for Bls12381G1PointVisitor { - type Value = G1Point; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("Bls12381G1Point") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: SeqAccess<'de>, - { - let bytes_str = seq - .next_element()? - .ok_or_else(|| V::Error::invalid_length(0, &"a single element"))?; - let bytes_bn = BigInt::from_hex(bytes_str).map_err(V::Error::custom)?; - let bytes = BigInt::to_bytes(&bytes_bn); - G1Point::from_bytes(&bytes[..]).map_err(|_| V::Error::custom("failed to parse g1 point")) - } - - fn visit_map>(self, mut map: E) -> Result { - let mut bytes_str: String = "".to_string(); - - while let Some(key) = map.next_key::<&'de str>()? { - let v = map.next_value::<&'de str>()?; - match key { - "bytes_str" => { - bytes_str = String::from(v); - } - _ => return Err(E::Error::unknown_field(key, &["bytes_str"])), - } + fn from_underlying(ge: Self::Underlying) -> G1Point { + G1Point { + purpose: "from_underlying", + ge, } - let bytes_bn = BigInt::from_hex(&bytes_str).map_err(E::Error::custom)?; - let bytes = BigInt::to_bytes(&bytes_bn); - - G1Point::from_bytes(&bytes[..]).map_err(|_| E::Error::custom("failed to parse g1 point")) } } @@ -549,6 +254,29 @@ impl G1Point { } } +impl fmt::Debug for G1Point { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Point {{ purpose: {:?}, uncompressed: {:?} }}", + self.purpose, + hex::encode(self.serialize_uncompressed()), + ) + } +} + +impl PartialEq for G1Point { + fn eq(&self, other: &G1Point) -> bool { + self.ge == other.ge + } +} + +impl Zeroize for G1Point { + fn zeroize(&mut self) { + self.ge.zeroize() + } +} + #[cfg(test)] mod tests { use pairing_plus::bls12_381::{G1Uncompressed, G1}; @@ -557,155 +285,7 @@ mod tests { use pairing_plus::{CurveProjective, SubgroupCheck}; use sha2::Sha256; - use super::G1Point; - use crate::arithmetic::traits::*; - use crate::elliptic::curves::bls12_381::g1::{FE, GE}; - use crate::elliptic::curves::traits::ECPoint; - use crate::elliptic::curves::traits::ECScalar; - use crate::BigInt; - - #[test] - fn test_serdes_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - - let pk = GE::base_point2(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - } - - #[test] - fn bincode_pk() { - let pk = GE::generator(); - let bin = bincode::serialize(&pk).unwrap(); - let decoded: G1Point = bincode::deserialize(bin.as_slice()).unwrap(); - assert_eq!(decoded, pk); - } - - #[test] - #[should_panic] - #[allow(clippy::op_ref)] // Enables type inference. - fn test_serdes_bad_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - // we make sure that the string encodes invalid point: - let s: String = s.replace("30", "20"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - let eight = ECScalar::from(&BigInt::from(8)); - assert_eq!(des_pk, pk * &eight); - } - - #[test] - fn test_from_mpz() { - let rand_scalar: FE = ECScalar::new_random(); - let rand_bn = rand_scalar.to_big_int(); - let rand_scalar2: FE = ECScalar::from(&rand_bn); - assert_eq!(rand_scalar, rand_scalar2); - } - - #[test] - fn test_minus_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_minus_b_fe: FE = a.sub(&b.get_element()); - let base: GE = ECPoint::generator(); - - let point_ab1 = base * a_minus_b_fe; - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.sub_point(&point_b.get_element()); - println!( - "point ab1: {:?}", - point_ab1.bytes_compressed_to_big_int().to_hex() - ); - println!( - "point ab2: {:?}", - point_ab2.bytes_compressed_to_big_int().to_hex() - ); - - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_add_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_plus_b_fe = a + b; - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_plus_b_fe; - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.add_point(&point_b.get_element()); - - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_add_scalar() { - let a: FE = ECScalar::new_random(); - let zero: FE = FE::zero(); - let a_plus_zero: FE = a + zero; - - assert_eq!(a_plus_zero, a); - } - - #[test] - fn test_mul_scalar() { - let a = [ - 10, 10, 10, 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, 10, 10, 10, - ]; - - let a_bn = BigInt::from_bytes(&a[..]); - let a_fe: FE = ECScalar::from(&a_bn); - - let five = BigInt::from(5); - let five_fe: FE = ECScalar::from(&five); - println!("five_fe: {:?}", five_fe.clone()); - let five_a_bn = BigInt::mod_mul(&a_bn, &five, &FE::q()); - let five_a_fe = five_fe * a_fe; - let five_a_fe_2: FE = ECScalar::from(&five_a_bn); - - assert_eq!(five_a_fe, five_a_fe_2); - } - - #[test] - fn test_mul_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_mul_b_fe = a * b; - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_mul_b_fe; - let point_a = base * a; - let point_ab2 = point_a.scalar_mul(&b.get_element()); - - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_invert() { - let a: FE = ECScalar::new_random(); - - let a_bn = a.to_big_int(); - - let a_inv = a.invert(); - let a_inv_bn_1 = BigInt::mod_inv(&a_bn, &FE::q()).unwrap(); - let a_inv_bn_2 = a_inv.to_big_int(); - - assert_eq!(a_inv_bn_1, a_inv_bn_2); - } - - #[test] - fn test_scalar_mul_multiply_by_1() { - let g: GE = ECPoint::generator(); - - let fe: FE = ECScalar::from(&BigInt::from(1)); - let b_tag = g * fe; - assert_eq!(b_tag, g); - } + use super::{ECPoint, GE1}; #[test] fn base_point2_nothing_up_my_sleeve() { @@ -721,7 +301,7 @@ mod tests { println!("Uncompressed base_point2: {:?}", point_uncompressed); // Check that ECPoint::base_point2() returns generated point - let base_point2: GE = ECPoint::base_point2(); + let base_point2: &GE1 = ECPoint::base_point2(); assert_eq!(point, base_point2.ge); } } diff --git a/src/elliptic/curves/bls12_381/g2.rs b/src/elliptic/curves/bls12_381/g2.rs index 0aa7b8ea..6bddcc7c 100644 --- a/src/elliptic/curves/bls12_381/g2.rs +++ b/src/elliptic/curves/bls12_381/g2.rs @@ -5,295 +5,29 @@ License MIT: */ -pub const SECRET_KEY_SIZE: usize = 32; -pub const COMPRESSED_SIZE: usize = 96; - use std::fmt; -use std::fmt::Debug; -use std::ops::{Add, Mul, Neg}; -use std::str; - -use ff_zeroize::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; -use pairing_plus::bls12_381::Fr; -use pairing_plus::bls12_381::G2Compressed; -use pairing_plus::bls12_381::G2Uncompressed; -use pairing_plus::bls12_381::G2; + +use ff_zeroize::{PrimeField, ScalarEngine}; +use pairing_plus::bls12_381::{G2Compressed, G2Uncompressed, G2}; use pairing_plus::hash_to_curve::HashToCurve; use pairing_plus::hash_to_field::ExpandMsgXmd; -use pairing_plus::serdes::SerDes; -use pairing_plus::EncodedPoint; use pairing_plus::{CurveAffine, CurveProjective, Engine}; +use pairing_plus::{EncodedPoint, SubgroupCheck}; use sha2::Sha256; - -use serde::de::{self, Error, MapAccess, SeqAccess, Visitor}; -use serde::ser::SerializeStruct; -use serde::ser::{Serialize, Serializer}; -use serde::{Deserialize, Deserializer}; - -pub type SK = ::Fr; -pub type PK = ::G2Affine; - -use crate::arithmetic::traits::*; -use crate::BigInt; -use crate::ErrorKey::{self}; - -use std::ptr; -use std::sync::atomic; use zeroize::Zeroize; -use crate::elliptic::curves::traits::ECPoint; -use crate::elliptic::curves::traits::ECScalar; -#[cfg(feature = "merkle")] -use crypto::digest::Digest; -#[cfg(feature = "merkle")] -use crypto::sha3::Sha3; -#[cfg(feature = "merkle")] -use merkle::Hashable; -use std::io::Cursor; - -#[derive(Clone, Copy)] -pub struct FieldScalar { - purpose: &'static str, - fe: SK, -} -#[derive(Clone, Copy)] -pub struct G2Point { - purpose: &'static str, - ge: PK, -} -pub type GE = G2Point; -pub type FE = FieldScalar; - -impl Zeroize for FieldScalar { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, FE::zero()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } -} - -impl ECScalar for FieldScalar { - type SecretKey = SK; - - fn new_random() -> FieldScalar { - let rnd_bn = BigInt::sample_below(&FE::q()); - ECScalar::from(&rnd_bn) - } - - fn zero() -> FieldScalar { - FieldScalar { - purpose: "zero", - fe: SK::default(), - } - } - - fn get_element(&self) -> SK { - self.fe - } - fn set_element(&mut self, element: SK) { - self.fe = element - } - - fn from(n: &BigInt) -> FieldScalar { - let n_mod = BigInt::modulus(n, &FE::q()); - let mut v = BigInt::to_bytes(&n_mod); - let mut bytes_array: [u8; SECRET_KEY_SIZE]; - if v.len() < SECRET_KEY_SIZE { - let mut template = vec![0; SECRET_KEY_SIZE - v.len()]; - template.extend_from_slice(&v); - v = template; - } - bytes_array = [0; SECRET_KEY_SIZE]; - let bytes = &v[..SECRET_KEY_SIZE]; - bytes_array.copy_from_slice(&bytes); - - // bytes_array.reverse(); - - let mut repr = SK::default().into_repr(); - repr.read_be(Cursor::new(&bytes_array[..])).unwrap(); - FieldScalar { - purpose: "from_big_int", - fe: Fr::from_repr(repr).unwrap(), - } - } - - fn to_big_int(&self) -> BigInt { - let tmp = self.fe.into_repr(); - let scalar_u64 = tmp.as_ref(); - - let to_bn = scalar_u64.iter().rev().fold(BigInt::zero(), |acc, x| { - let element_bn = BigInt::from(*x); - element_bn + (acc << 64) - }); - to_bn - } - - fn q() -> BigInt { - let q_u64: [u64; 4] = [ - 0xffffffff00000001, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, - ]; - let to_bn = q_u64.iter().rev().fold(BigInt::zero(), |acc, x| { - let element_bn = BigInt::from(*x); - element_bn + (acc << 64) - }); - to_bn - } - - fn add(&self, other: &SK) -> FieldScalar { - let mut add_fe = FieldScalar { - purpose: "other add", - fe: *other, - }; - add_fe.fe.add_assign(&self.fe); - FieldScalar { - purpose: "add", - fe: add_fe.fe, - } - } - - fn mul(&self, other: &SK) -> FieldScalar { - let mut mul_fe = FieldScalar { - purpose: "other mul", - fe: *other, - }; - mul_fe.fe.mul_assign(&self.fe); - FieldScalar { - purpose: "mul", - fe: mul_fe.fe, - } - } - - fn sub(&self, other: &SK) -> FieldScalar { - let mut other_neg = *other; - other_neg.negate(); - let sub_fe = FieldScalar { - purpose: "other sub", - fe: other_neg, - }; - self.add(&sub_fe.get_element()) - } - - fn invert(&self) -> FieldScalar { - let sc = self.fe; - let inv_sc = sc.inverse().unwrap(); //TODO - FieldScalar { - purpose: "inverse", - fe: inv_sc, - } - } -} - -impl Debug for FieldScalar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Point {{ purpose: {:?}, bytes: {:?} }}", - self.purpose, self.fe, - ) - } -} +use crate::arithmetic::*; +use crate::elliptic::curves::traits::*; -impl PartialEq for FieldScalar { - fn eq(&self, other: &FieldScalar) -> bool { - self.get_element() == other.get_element() - } -} +use super::scalar::FieldScalar; -impl Mul for FieldScalar { - type Output = FieldScalar; - fn mul(self, other: FieldScalar) -> FieldScalar { - (&self).mul(&other.get_element()) - } -} +lazy_static::lazy_static! { + static ref GENERATOR: G2Point = G2Point { + purpose: "generator", + ge: PK::one(), + }; -impl<'o> Mul<&'o FieldScalar> for FieldScalar { - type Output = FieldScalar; - fn mul(self, other: &'o FieldScalar) -> FieldScalar { - (&self).mul(&other.get_element()) - } -} - -impl Add for FieldScalar { - type Output = FieldScalar; - fn add(self, other: FieldScalar) -> FieldScalar { - (&self).add(&other.get_element()) - } -} - -impl<'o> Add<&'o FieldScalar> for FieldScalar { - type Output = FieldScalar; - fn add(self, other: &'o FieldScalar) -> FieldScalar { - (&self).add(&other.get_element()) - } -} - -impl Serialize for FieldScalar { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_big_int().to_hex()) - } -} - -impl<'de> Deserialize<'de> for FieldScalar { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(BLS12_381ScalarVisitor) - } -} - -struct BLS12_381ScalarVisitor; - -impl<'de> Visitor<'de> for BLS12_381ScalarVisitor { - type Value = FieldScalar; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("bls12_381") - } - - fn visit_str(self, s: &str) -> Result { - let v = BigInt::from_hex(s).map_err(E::custom)?; - Ok(ECScalar::from(&v)) - } -} - -impl Debug for G2Point { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Point {{ purpose: {:?}, bytes: {:?} }}", - self.purpose, - self.bytes_compressed_to_big_int().to_hex() - ) - } -} - -impl PartialEq for G2Point { - fn eq(&self, other: &G2Point) -> bool { - self.get_element() == other.get_element() - } -} - -impl Zeroize for G2Point { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, GE::generator()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } -} - -impl ECPoint for G2Point { - type SecretKey = SK; - type PublicKey = PK; - type Scalar = FieldScalar; - - fn base_point2() -> G2Point { + static ref BASE_POINT2: G2Point = { const BASE_POINT2: [u8; 192] = [ 0, 204, 165, 72, 21, 96, 36, 119, 117, 242, 58, 55, 105, 140, 136, 76, 180, 140, 92, 212, 55, 3, 146, 72, 120, 181, 37, 205, 165, 221, 144, 86, 57, 124, 16, 19, 160, 215, @@ -311,234 +45,203 @@ impl ECPoint for G2Point { let mut point = G2Uncompressed::empty(); point.as_mut().copy_from_slice(&BASE_POINT2); G2Point { - purpose: "base_ge2", + purpose: "base_point2", ge: point.into_affine().expect("invalid base_point"), } - } - - fn generator() -> G2Point { - G2Point { - purpose: "base_fe", - ge: PK::one(), - } - } + }; +} - fn get_element(&self) -> PK { - self.ge - } +pub const SECRET_KEY_SIZE: usize = 32; +pub const COMPRESSED_SIZE: usize = 96; - fn x_coor(&self) -> Option { - let tmp = G2Uncompressed::from_affine(self.ge); - let bytes = tmp.as_ref(); - let x_coor = &bytes[0..COMPRESSED_SIZE]; - let bn = BigInt::from_bytes(x_coor); - Some(bn) - } +pub type SK = ::Fr; +pub type PK = ::G2Affine; - fn y_coor(&self) -> Option { - let tmp = G2Uncompressed::from_affine(self.ge); - let bytes = tmp.as_ref(); - let y_coor = &bytes[COMPRESSED_SIZE..2 * COMPRESSED_SIZE]; - let bn = BigInt::from_bytes(y_coor); - Some(bn) - } +/// Bls12-381-2 (G2) curve implementation based on [pairing_plus] library +#[derive(PartialEq, Debug, Clone)] +pub enum Bls12_381_2 {} - fn bytes_compressed_to_big_int(&self) -> BigInt { - let tmp = G2Compressed::from_affine(self.ge); - let bytes = tmp.as_ref(); - BigInt::from_bytes(bytes) - } +#[derive(Clone, Copy)] +pub struct G2Point { + purpose: &'static str, + ge: PK, +} - fn from_bytes(bytes: &[u8]) -> Result { - let mut bytes_array_comp = [0u8; COMPRESSED_SIZE]; - match bytes.len() { - 0..=COMPRESSED_SIZE => { - (&mut bytes_array_comp[COMPRESSED_SIZE - bytes.len()..]).copy_from_slice(bytes); - } - _ => { - bytes_array_comp.copy_from_slice(&bytes[..COMPRESSED_SIZE]); - } - } +pub type GE2 = G2Point; - let g2_comp = G2::deserialize(&mut bytes_array_comp[..].as_ref(), true).unwrap(); +impl Curve for Bls12_381_2 { + type Point = GE2; + type Scalar = FieldScalar; - let pk = G2Point { - purpose: "from_bytes", - ge: g2_comp.into_affine(), //TODO: handle error - }; + const CURVE_NAME: &'static str = "bls12_381_1"; +} - Ok(pk) - } +impl ECPoint for G2Point { + type Scalar = FieldScalar; + type Underlying = PK; - // in this case the opposite of from_bytes: takes compressed pk to COMPRESSED_SIZE bytes. - fn pk_to_key_slice(&self) -> Vec { - let mut compressed_vec = vec![]; - PK::serialize(&self.ge, &mut compressed_vec, true) - .expect("serializing into vec should always succeed"); - compressed_vec - } + type CompressedPoint = G2Compressed; + type UncompressedPoint = G2Uncompressed; - fn scalar_mul(&self, fe: &SK) -> G2Point { - let mut ge_proj: G2 = self.ge.into(); - ge_proj.mul_assign(fe.into_repr()); + fn zero() -> G2Point { G2Point { - purpose: "scalar_point_mul", - ge: ge_proj.into_affine(), + purpose: "zero", + ge: PK::zero(), } } - fn add_point(&self, other: &PK) -> G2Point { - let mut ge_proj: G2 = self.ge.into(); - ge_proj.add_assign_mixed(other); - G2Point { - purpose: "combine", - ge: ge_proj.into_affine(), - } + fn is_zero(&self) -> bool { + self.ge.is_zero() } - fn sub_point(&self, other: &PK) -> G2Point { - let mut ge_proj: G2 = self.ge.into(); - ge_proj.sub_assign_mixed(other); - G2Point { - purpose: "sub", - ge: ge_proj.into_affine(), - } + fn generator() -> &'static G2Point { + &GENERATOR } - fn from_coor(_x: &BigInt, _y: &BigInt) -> G2Point { - // TODO - unimplemented!(); + fn base_point2() -> &'static G2Point { + &BASE_POINT2 } -} -impl From for G2Point { - fn from(point: PK) -> Self { - G2Point { - purpose: "from_point", - ge: point, + fn from_coords(x: &BigInt, y: &BigInt) -> Result { + let vec_x = x.to_bytes(); + let vec_y = y.to_bytes(); + if vec_x.len() > COMPRESSED_SIZE || vec_y.len() > COMPRESSED_SIZE { + return Err(NotOnCurve); } - } -} + let mut uncompressed = [0u8; 2 * COMPRESSED_SIZE]; + uncompressed[COMPRESSED_SIZE - vec_x.len()..COMPRESSED_SIZE].copy_from_slice(&vec_x); + uncompressed[(2 * COMPRESSED_SIZE) - vec_y.len()..].copy_from_slice(&vec_y); + debug_assert_eq!(x, &BigInt::from_bytes(&uncompressed[..COMPRESSED_SIZE])); + debug_assert_eq!(y, &BigInt::from_bytes(&uncompressed[COMPRESSED_SIZE..])); -impl Mul for G2Point { - type Output = G2Point; - fn mul(self, other: FieldScalar) -> G2Point { - self.scalar_mul(&other.get_element()) + let mut point = G2Uncompressed::empty(); + point.as_mut().copy_from_slice(&uncompressed); + + Ok(G2Point { + purpose: "from_coords", + ge: point.into_affine().map_err(|_| NotOnCurve)?, + }) + } + + fn x_coord(&self) -> Option { + if self.is_zero() { + None + } else { + let uncompressed = G2Uncompressed::from_affine(self.ge); + let x_coord = &uncompressed.as_ref()[0..COMPRESSED_SIZE]; + let bn = BigInt::from_bytes(x_coord); + Some(bn) + } } -} -impl<'o> Mul<&'o FieldScalar> for G2Point { - type Output = G2Point; - fn mul(self, other: &'o FieldScalar) -> G2Point { - self.scalar_mul(&other.get_element()) + fn y_coord(&self) -> Option { + if self.is_zero() { + None + } else { + let uncompressed = G2Uncompressed::from_affine(self.ge); + let y_coord = &uncompressed.as_ref()[COMPRESSED_SIZE..COMPRESSED_SIZE * 2]; + let bn = BigInt::from_bytes(y_coord); + Some(bn) + } } -} -impl<'o> Mul<&'o FieldScalar> for &'o G2Point { - type Output = G2Point; - fn mul(self, other: &'o FieldScalar) -> G2Point { - self.scalar_mul(&other.get_element()) + fn coords(&self) -> Option { + if self.is_zero() { + None + } else { + let uncompressed = G2Uncompressed::from_affine(self.ge); + let x = &uncompressed.as_ref()[0..COMPRESSED_SIZE]; + let y = &uncompressed.as_ref()[COMPRESSED_SIZE..COMPRESSED_SIZE * 2]; + let x = BigInt::from_bytes(x); + let y = BigInt::from_bytes(y); + Some(PointCoords { x, y }) + } } -} -impl Add for G2Point { - type Output = G2Point; - fn add(self, other: G2Point) -> G2Point { - self.add_point(&other.get_element()) + fn serialize_compressed(&self) -> Self::CompressedPoint { + G2Compressed::from_affine(self.ge) + } + + fn serialize_uncompressed(&self) -> Self::UncompressedPoint { + G2Uncompressed::from_affine(self.ge) + } + + fn deserialize(bytes: &[u8]) -> Result { + if bytes.len() == COMPRESSED_SIZE { + let mut compressed = G2Compressed::empty(); + compressed.as_mut().copy_from_slice(bytes); + Ok(G2Point { + purpose: "deserialize", + ge: compressed.into_affine().map_err(|_| DeserializationError)?, + }) + } else if bytes.len() == 2 * COMPRESSED_SIZE { + let mut uncompressed = G2Uncompressed::empty(); + uncompressed.as_mut().copy_from_slice(bytes); + Ok(G2Point { + purpose: "deserialize", + ge: uncompressed + .into_affine() + .map_err(|_| DeserializationError)?, + }) + } else { + Err(DeserializationError) + } } -} -impl<'o> Add<&'o G2Point> for G2Point { - type Output = G2Point; - fn add(self, other: &'o G2Point) -> G2Point { - self.add_point(&other.get_element()) + fn check_point_order_equals_group_order(&self) -> bool { + !self.is_zero() && self.ge.in_subgroup() } -} -impl<'o> Add<&'o G2Point> for &'o G2Point { - type Output = G2Point; - fn add(self, other: &'o G2Point) -> G2Point { - self.add_point(&other.get_element()) + fn scalar_mul(&self, scalar: &Self::Scalar) -> G2Point { + let result = self.ge.mul(scalar.underlying_ref().into_repr()); + G2Point { + purpose: "scalar_mul", + ge: result.into_affine(), + } } -} -impl Neg for G2Point { - type Output = Self; - fn neg(mut self) -> Self { - self.ge.negate(); - self.purpose = "negated"; - self + fn add_point(&self, other: &Self) -> G2Point { + let mut result = G2::from(self.ge); + result.add_assign_mixed(&other.ge); + G2Point { + purpose: "add_point", + ge: result.into_affine(), + } } -} -#[cfg(feature = "merkle")] -impl Hashable for G2Point { - fn update_context(&self, context: &mut Sha3) { - let bytes: Vec = self.pk_to_key_slice(); - context.input(&bytes[..]); + fn sub_point(&self, other: &Self) -> G2Point { + let mut result = G2::from(self.ge); + result.sub_assign_mixed(&other.ge); + G2Point { + purpose: "sub_point", + ge: result.into_affine(), + } } -} -impl Serialize for G2Point { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = self.pk_to_key_slice(); - let bytes_as_bn = BigInt::from_bytes(&bytes[..]); - let mut state = serializer.serialize_struct("Bls12381G2Point", 1)?; - state.serialize_field("bytes_str", &bytes_as_bn.to_hex())?; - state.end() + fn neg_point(&self) -> Self { + let mut result = self.ge; + result.negate(); + G2Point { + purpose: "neg", + ge: result, + } } -} -impl<'de> Deserialize<'de> for G2Point { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - const FIELDS: &[&str] = &["bytes_str"]; - deserializer.deserialize_struct("Bls12381G2Point", FIELDS, Bls12381G2PointVisitor) + fn neg_point_assign(&mut self) { + self.ge.negate(); } -} -struct Bls12381G2PointVisitor; - -impl<'de> Visitor<'de> for Bls12381G2PointVisitor { - type Value = G2Point; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("Bls12381G2Point") + fn underlying_ref(&self) -> &Self::Underlying { + &self.ge } - - fn visit_seq(self, mut seq: V) -> Result - where - V: SeqAccess<'de>, - { - let bytes_str = seq - .next_element()? - .ok_or_else(|| V::Error::invalid_length(0, &"a single element"))?; - let bytes_bn = BigInt::from_hex(bytes_str).map_err(V::Error::custom)?; - let bytes = BigInt::to_bytes(&bytes_bn); - G2Point::from_bytes(&bytes[..]).map_err(|_| V::Error::custom("failed to parse g2 point")) + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.ge } - - fn visit_map>(self, mut map: E) -> Result { - let mut bytes_str: String = "".to_string(); - - while let Some(key) = map.next_key::<&'de str>()? { - let v = map.next_value::<&'de str>()?; - match key { - "bytes_str" => { - bytes_str = String::from(v); - } - _ => return Err(E::Error::unknown_field(key, &["bytes_str"])), - } + fn from_underlying(ge: Self::Underlying) -> G2Point { + G2Point { + purpose: "from_underlying", + ge, } - let bytes_bn = BigInt::from_hex(&bytes_str).map_err(E::Error::custom)?; - let bytes = BigInt::to_bytes(&bytes_bn); - - G2Point::from_bytes(&bytes[..]).map_err(|_| E::Error::custom("failed to parse g2 point")) } } @@ -558,162 +261,32 @@ impl G2Point { } } -#[cfg(test)] -mod tests { - use pairing_plus::bls12_381::{G2Uncompressed, G2}; - use pairing_plus::hash_to_curve::HashToCurve; - use pairing_plus::hash_to_field::ExpandMsgXmd; - use pairing_plus::{CurveProjective, SubgroupCheck}; - use sha2::Sha256; - - use super::G2Point; - use crate::arithmetic::traits::*; - use crate::elliptic::curves::bls12_381::g2::{FE, GE}; - use crate::elliptic::curves::traits::ECPoint; - use crate::elliptic::curves::traits::ECScalar; - use crate::BigInt; - - #[test] - fn test_serdes_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - - let pk = GE::base_point2(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - } - - #[test] - fn bincode_pk() { - let pk = GE::generator(); - let bin = bincode::serialize(&pk).unwrap(); - let decoded: G2Point = bincode::deserialize(bin.as_slice()).unwrap(); - assert_eq!(decoded, pk); - } - - #[test] - #[should_panic] - #[allow(clippy::op_ref)] // Enables type inference. - fn test_serdes_bad_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - // we make sure that the string encodes invalid point: - let s: String = s.replace("30", "20"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - let eight = ECScalar::from(&BigInt::from(8)); - assert_eq!(des_pk, pk * &eight); - } - - #[test] - fn test_from_mpz() { - let rand_scalar: FE = ECScalar::new_random(); - let rand_bn = rand_scalar.to_big_int(); - let rand_scalar2: FE = ECScalar::from(&rand_bn); - assert_eq!(rand_scalar, rand_scalar2); - } - - #[test] - fn test_minus_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_minus_b_fe: FE = a.sub(&b.get_element()); - let base: GE = ECPoint::generator(); - - let point_ab1 = base * a_minus_b_fe; - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.sub_point(&point_b.get_element()); - println!( - "point ab1: {:?}", - point_ab1.bytes_compressed_to_big_int().to_hex() - ); - println!( - "point ab2: {:?}", - point_ab2.bytes_compressed_to_big_int().to_hex() - ); - - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_add_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_plus_b_fe = a + b; - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_plus_b_fe; - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.add_point(&point_b.get_element()); - - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_add_scalar() { - let a: FE = ECScalar::new_random(); - let zero: FE = FE::zero(); - let a_plus_zero: FE = a + zero; - - assert_eq!(a_plus_zero, a); - } - - #[test] - fn test_mul_scalar() { - let a = [ - 10, 10, 10, 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, 10, 10, 10, - ]; - - let a_bn = BigInt::from_bytes(&a[..]); - let a_fe: FE = ECScalar::from(&a_bn); - - let five = BigInt::from(5); - let five_fe: FE = ECScalar::from(&five); - println!("five_fe: {:?}", five_fe.clone()); - let five_a_bn = BigInt::mod_mul(&a_bn, &five, &FE::q()); - let five_a_fe = five_fe * a_fe; - let five_a_fe_2: FE = ECScalar::from(&five_a_bn); - - assert_eq!(five_a_fe, five_a_fe_2); +impl fmt::Debug for G2Point { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Point {{ purpose: {:?}, uncompressed: {:?} }}", + self.purpose, + hex::encode(self.serialize_uncompressed()), + ) } +} - #[test] - fn test_mul_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_mul_b_fe = a * b; - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_mul_b_fe; - let point_a = base * a; - let point_ab2 = point_a.scalar_mul(&b.get_element()); - - assert_eq!(point_ab1, point_ab2); +impl PartialEq for G2Point { + fn eq(&self, other: &G2Point) -> bool { + self.ge == other.ge } +} - #[test] - fn test_invert() { - let a: FE = ECScalar::new_random(); - - let a_bn = a.to_big_int(); - - let a_inv = a.invert(); - let a_inv_bn_1 = BigInt::mod_inv(&a_bn, &FE::q()).unwrap(); - let a_inv_bn_2 = a_inv.to_big_int(); - - assert_eq!(a_inv_bn_1, a_inv_bn_2); +impl Zeroize for G2Point { + fn zeroize(&mut self) { + self.ge.zeroize() } - #[test] - fn test_scalar_mul_multiply_by_1() { - let g: GE = ECPoint::generator(); +} - let fe: FE = ECScalar::from(&BigInt::from(1)); - let b_tag = g * fe; - assert_eq!(b_tag, g); - } +#[cfg(test)] +mod tests { + use super::*; #[test] fn base_point2_nothing_up_my_sleeve() { @@ -729,7 +302,7 @@ mod tests { println!("Uncompressed base_point2: {:?}", point_uncompressed); // Check that ECPoint::base_point2() returns generated point - let base_point2: GE = ECPoint::base_point2(); + let base_point2: &G2Point = ECPoint::base_point2(); assert_eq!(point, base_point2.ge); } } diff --git a/src/elliptic/curves/bls12_381/mod.rs b/src/elliptic/curves/bls12_381/mod.rs index b410c7f3..ecb3164a 100644 --- a/src/elliptic/curves/bls12_381/mod.rs +++ b/src/elliptic/curves/bls12_381/mod.rs @@ -1,84 +1,6 @@ pub mod g1; pub mod g2; +mod pairing; +pub mod scalar; -use crate::elliptic::curves::bls12_381::g1::GE as GE1; -use crate::elliptic::curves::bls12_381::g2::GE as GE2; -use crate::elliptic::curves::traits::ECPoint; - -use ff_zeroize::Field; -use pairing_plus::bls12_381::{Bls12, Fq12}; -use pairing_plus::{CurveAffine, Engine}; - -#[derive(Clone, Copy, PartialEq, Debug)] -pub struct Pair { - pub e: Fq12, -} - -impl Pair { - pub fn compute_pairing(g1_ge: &GE1, g2_ge: &GE2) -> Self { - Pair { - e: g1_ge.get_element().pairing_with(&g2_ge.get_element()), - } - } - - /// Efficiently computes product of pairings. - /// - /// Computes `e(g1,g2) * e(g3,g4)` with a single final exponentiation. - /// - /// ## Panic - /// Method panics if miller_loop of product is equal to zero. - pub fn efficient_pairing_mul(g1: &GE1, g2: &GE2, g3: &GE1, g4: &GE2) -> Self { - Pair { - e: Bls12::final_exponentiation(&Bls12::miller_loop( - [ - (&(g1.get_element().prepare()), &(g2.get_element().prepare())), - (&(g3.get_element().prepare()), &(g4.get_element().prepare())), - ] - .iter(), - )) - .unwrap(), - } - } - - pub fn add_pair(&self, other: &Pair) -> Self { - let mut res = *self; - res.e.mul_assign(&other.e); - Pair { e: res.e } - } -} - -#[cfg(test)] -mod tests { - use super::Pair; - use crate::elliptic::curves::bls12_381::g1::FE; - use crate::elliptic::curves::bls12_381::g1::GE as GE1; - use crate::elliptic::curves::bls12_381::g2::GE as GE2; - use crate::elliptic::curves::traits::ECPoint; - use crate::elliptic::curves::traits::ECScalar; - - #[test] - fn powers_of_g1_and_g2() { - let a: GE1 = ECPoint::generator(); - let b: GE2 = ECPoint::generator(); - let scalar_factor: FE = ECScalar::new_random(); - let res_mul_a = a.scalar_mul(&scalar_factor.get_element()); - let res_mul_b = b.scalar_mul(&scalar_factor.get_element()); - let res_a_power = Pair::compute_pairing(&res_mul_a, &b); - let res_b_power = Pair::compute_pairing(&a, &res_mul_b); - assert_eq!(res_a_power, res_b_power); - } - - // e(P,Q)e(P,R) = e(P, Q+ R) - #[test] - fn pairing() { - let p: GE1 = ECPoint::generator(); - let q: GE2 = ECPoint::generator(); - let r: GE2 = ECPoint::base_point2(); - let q_plus_r = q + r; - let e_p_q = Pair::compute_pairing(&p, &q); - let e_p_r = Pair::compute_pairing(&p, &r); - let e_p_q_r = Pair::compute_pairing(&p, &q_plus_r); - let e_p_q_add_e_p_r = e_p_q.add_pair(&e_p_r); - assert_eq!(e_p_q_add_e_p_r, e_p_q_r); - } -} +pub use self::{g1::Bls12_381_1, g2::Bls12_381_2, pairing::Pair}; diff --git a/src/elliptic/curves/bls12_381/pairing.rs b/src/elliptic/curves/bls12_381/pairing.rs new file mode 100644 index 00000000..4c3c4399 --- /dev/null +++ b/src/elliptic/curves/bls12_381/pairing.rs @@ -0,0 +1,96 @@ +use ff_zeroize::Field; +use pairing_plus::bls12_381::{Bls12, Fq12}; +use pairing_plus::{CurveAffine, Engine}; + +use crate::elliptic::curves::bls12_381::{Bls12_381_1, Bls12_381_2}; +use crate::elliptic::curves::traits::*; +use crate::elliptic::curves::Point; + +/// Bilinear pairing function +/// +/// _Note_: pairing function support is experimental and subject to change +#[derive(Clone, Copy, PartialEq, Debug)] +pub struct Pair { + pub e: Fq12, +} + +impl Pair { + /// Computes pairing `e(p1, p2)` + pub fn compute_pairing(p1: &Point, p2: &Point) -> Self { + Pair { + e: p1 + .as_raw() + .underlying_ref() + .pairing_with(p2.as_raw().underlying_ref()), + } + } + + /// Efficiently computes product of pairings + /// + /// Computes `e(g1,g2) * e(g3,g4)` with a single final exponentiation. + /// + /// ## Panic + /// Method panics if miller_loop of product is equal to zero. + pub fn efficient_pairing_mul( + p1: &Point, + p2: &Point, + p3: &Point, + p4: &Point, + ) -> Self { + Pair { + e: Bls12::final_exponentiation(&Bls12::miller_loop( + [ + ( + &(p1.as_raw().underlying_ref().prepare()), + &(p2.as_raw().underlying_ref().prepare()), + ), + ( + &(p3.as_raw().underlying_ref().prepare()), + &(p4.as_raw().underlying_ref().prepare()), + ), + ] + .iter(), + )) + .unwrap(), + } + } + + pub fn add_pair(&self, other: &Pair) -> Self { + let mut res = *self; + res.e.mul_assign(&other.e); + Pair { e: res.e } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::elliptic::curves::Scalar; + + #[test] + fn powers_of_g1_and_g2() { + let a = Point::::generator().to_point(); + let b = Point::::generator().to_point(); + let scalar_factor_1 = Scalar::::random(); + let scalar_factor_2 = Scalar::::from_raw(scalar_factor_1.as_raw().clone()); + let res_mul_a = &a * &scalar_factor_1; + let res_mul_b = &b * &scalar_factor_2; + let res_a_power = Pair::compute_pairing(&res_mul_a, &b); + let res_b_power = Pair::compute_pairing(&a, &res_mul_b); + assert_eq!(res_a_power, res_b_power); + } + + // e(P,Q)e(P,R) = e(P, Q+ R) + #[test] + fn pairing() { + let p = Point::::generator().to_point(); + let q = Point::::generator().to_point(); + let r = Point::::base_point2(); + let q_plus_r = &q + r; + let e_p_q = Pair::compute_pairing(&p, &q); + let e_p_r = Pair::compute_pairing(&p, r); + let e_p_q_r = Pair::compute_pairing(&p, &q_plus_r); + let e_p_q_add_e_p_r = e_p_q.add_pair(&e_p_r); + assert_eq!(e_p_q_add_e_p_r, e_p_q_r); + } +} diff --git a/src/elliptic/curves/bls12_381/scalar.rs b/src/elliptic/curves/bls12_381/scalar.rs new file mode 100644 index 00000000..66b7d40a --- /dev/null +++ b/src/elliptic/curves/bls12_381/scalar.rs @@ -0,0 +1,185 @@ +use std::fmt; + +use ff_zeroize::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use pairing_plus::bls12_381::{Fr, FrRepr}; +use rand::rngs::OsRng; +use zeroize::Zeroizing; + +use crate::arithmetic::*; +use crate::elliptic::curves::traits::*; + +lazy_static::lazy_static! { + static ref GROUP_ORDER: BigInt = { + let q_u64: [u64; 4] = [ + 0xffffffff00000001, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, + ]; + let to_bn = q_u64.iter().rev().fold(BigInt::zero(), |acc, x| { + let element_bn = BigInt::from(*x); + element_bn + (acc << 64) + }); + to_bn + }; +} + +const SECRET_KEY_SIZE: usize = 32; + +pub type FE = FieldScalar; +pub type SK = ::Fr; + +#[derive(Clone)] +pub struct FieldScalar { + purpose: &'static str, + fe: Zeroizing, +} + +impl ECScalar for FieldScalar { + type Underlying = SK; + + type ScalarBytes = [u8; 32]; + + fn random() -> FieldScalar { + FieldScalar { + purpose: "random", + fe: Zeroizing::new(Field::random(&mut OsRng)), + } + } + + fn zero() -> FieldScalar { + FieldScalar { + purpose: "zero", + fe: Zeroizing::new(Field::zero()), + } + } + + fn from_bigint(n: &BigInt) -> FieldScalar { + let bytes = n + .modulus(Self::group_order()) + .to_bytes_array::() + .expect("n mod curve_order must be equal or less than 32 bytes"); + + let mut repr = FrRepr::default(); + repr.read_be(bytes.as_ref()).unwrap(); + FieldScalar { + purpose: "from_bigint", + fe: Fr::from_repr(repr).unwrap().into(), + } + } + + fn to_bigint(&self) -> BigInt { + let repr = self.fe.into_repr(); + let mut bytes = [0u8; SECRET_KEY_SIZE]; + repr.write_be(&mut bytes[..]).unwrap(); + BigInt::from_bytes(&bytes) + } + + fn serialize(&self) -> Self::ScalarBytes { + let repr = self.fe.into_repr(); + let mut bytes = [0u8; SECRET_KEY_SIZE]; + repr.write_be(&mut bytes[..]).unwrap(); + bytes + } + + fn deserialize(bytes: &[u8]) -> Result { + if bytes.len() != SECRET_KEY_SIZE { + return Err(DeserializationError); + } + let mut repr = FrRepr::default(); + repr.read_be(bytes.as_ref()).unwrap(); + Ok(FieldScalar { + purpose: "deserialize", + fe: Fr::from_repr(repr).unwrap().into(), + }) + } + + fn add(&self, other: &Self) -> FieldScalar { + let mut result = self.fe.clone(); + result.add_assign(&other.fe); + FieldScalar { + purpose: "add", + fe: result, + } + } + + fn mul(&self, other: &Self) -> FieldScalar { + let mut result = self.fe.clone(); + result.mul_assign(&other.fe); + FieldScalar { + purpose: "mul", + fe: result, + } + } + + fn sub(&self, other: &Self) -> FieldScalar { + let mut result = self.fe.clone(); + result.sub_assign(&other.fe); + FieldScalar { + purpose: "sub", + fe: result, + } + } + + fn neg(&self) -> FieldScalar { + let mut result = self.fe.clone(); + result.negate(); + FieldScalar { + purpose: "neg", + fe: result, + } + } + + fn invert(&self) -> Option { + Some(FieldScalar { + purpose: "invert", + fe: Zeroizing::new(self.fe.inverse()?), + }) + } + + fn add_assign(&mut self, other: &Self) { + self.fe.add_assign(&other.fe); + } + fn mul_assign(&mut self, other: &Self) { + self.fe.mul_assign(&other.fe); + } + fn sub_assign(&mut self, other: &Self) { + self.fe.sub_assign(&other.fe); + } + fn neg_assign(&mut self) { + self.fe.negate(); + } + + fn group_order() -> &'static BigInt { + &GROUP_ORDER + } + + fn underlying_ref(&self) -> &Self::Underlying { + &self.fe + } + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.fe + } + fn from_underlying(fe: Self::Underlying) -> FieldScalar { + FieldScalar { + purpose: "from_underlying", + fe: fe.into(), + } + } +} + +impl fmt::Debug for FieldScalar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Point {{ purpose: {:?}, bytes: {:?} }}", + self.purpose, self.fe, + ) + } +} + +impl PartialEq for FieldScalar { + fn eq(&self, other: &FieldScalar) -> bool { + self.fe == other.fe + } +} diff --git a/src/elliptic/curves/curve_ristretto.rs b/src/elliptic/curves/curve_ristretto.rs index a123c3bf..24cf2bd8 100644 --- a/src/elliptic/curves/curve_ristretto.rs +++ b/src/elliptic/curves/curve_ristretto.rs @@ -6,593 +6,343 @@ License MIT: */ -use super::traits::{ECPoint, ECScalar}; -use crate::arithmetic::traits::*; -use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; -use crate::cryptographic_primitives::hashing::traits::Hash; -use crate::BigInt; -use crate::ErrorKey::{self, InvalidPublicKey}; -use curve25519_dalek::constants::BASEPOINT_ORDER; -use curve25519_dalek::constants::RISTRETTO_BASEPOINT_COMPRESSED; +use std::convert::TryInto; +use std::ptr; +use std::sync::atomic; + +use curve25519_dalek::constants::{BASEPOINT_ORDER, RISTRETTO_BASEPOINT_POINT}; use curve25519_dalek::ristretto::CompressedRistretto; -use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::traits::{Identity, IsIdentity}; use rand::thread_rng; -use serde::de::{self, Error, MapAccess, SeqAccess, Visitor}; -use serde::ser::SerializeStruct; -use serde::ser::{Serialize, Serializer}; -use serde::{Deserialize, Deserializer}; -use std::fmt; -use std::ops::{Add, Mul}; -use std::str; -pub const SECRET_KEY_SIZE: usize = 32; -pub const COOR_BYTE_SIZE: usize = 32; -pub const NUM_OF_COORDINATES: usize = 4; +use sha2::{Digest, Sha256}; +use zeroize::{Zeroize, Zeroizing}; -use std::ptr; -use std::sync::atomic; -use zeroize::Zeroize; +use crate::arithmetic::*; +use crate::elliptic::curves::traits::*; -#[cfg(feature = "merkle")] -use crypto::digest::Digest; -#[cfg(feature = "merkle")] -use crypto::sha3::Sha3; -#[cfg(feature = "merkle")] -use merkle::Hashable; +use super::traits::{ECPoint, ECScalar}; -pub type SK = Scalar; -pub type PK = CompressedRistretto; +lazy_static::lazy_static! { + static ref GROUP_ORDER: BigInt = RistrettoScalar { + purpose: "intermediate GROUP_ORDER", + fe: BASEPOINT_ORDER.into(), + }.to_bigint(); + + static ref GENERATOR: RistrettoPoint = RistrettoPoint { + purpose: "generator", + ge: RISTRETTO_BASEPOINT_POINT, + }; + + static ref BASE_POINT2: RistrettoPoint = { + let g = RistrettoPoint::generator(); + let hash = Sha256::digest(g.serialize_compressed().as_ref()); + RistrettoPoint { + purpose: "base_point2", + ge: RistrettoPoint::deserialize(&hash).unwrap().ge, + } + }; +} -#[derive(Clone, Debug, Copy)] +pub const SECRET_KEY_SIZE: usize = 32; +pub const COOR_BYTE_SIZE: usize = 32; +pub const NUM_OF_COORDINATES: usize = 4; + +pub type SK = curve25519_dalek::scalar::Scalar; +pub type PK = curve25519_dalek::ristretto::RistrettoPoint; + +/// Ristretto curve implementation based on [curve25519_dalek] library +/// +/// ## Implementation notes +/// * x coordinate +/// +/// Underlying library intentionally doesn't expose x coordinate of curve point, therefore +/// `.x_coord()`, `.coords()` methods always return `None`, `from_coords()` constructor always +/// returns `Err(NotOnCurve)` +#[derive(Debug, PartialEq, Clone)] +pub enum Ristretto {} +#[derive(Clone, Debug)] pub struct RistrettoScalar { purpose: &'static str, - fe: SK, + fe: Zeroizing, } #[derive(Clone, Debug, Copy)] -pub struct RistrettoCurvPoint { +pub struct RistrettoPoint { purpose: &'static str, ge: PK, } -pub type GE = RistrettoCurvPoint; +pub type GE = RistrettoPoint; pub type FE = RistrettoScalar; -impl Zeroize for RistrettoScalar { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, FE::zero()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } +impl Curve for Ristretto { + type Point = GE; + type Scalar = FE; + + const CURVE_NAME: &'static str = "ristretto"; } impl ECScalar for RistrettoScalar { - type SecretKey = SK; + type Underlying = SK; - fn new_random() -> RistrettoScalar { + type ScalarBytes = [u8; 32]; + + fn random() -> RistrettoScalar { RistrettoScalar { purpose: "random", - fe: SK::random(&mut thread_rng()), + fe: SK::random(&mut thread_rng()).into(), } } fn zero() -> RistrettoScalar { - let q_fe: FE = ECScalar::from(&FE::q()); RistrettoScalar { purpose: "zero", - fe: q_fe.get_element(), + fe: SK::zero().into(), } } - fn get_element(&self) -> SK { - self.fe - } - fn set_element(&mut self, element: SK) { - self.fe = element + fn from_bigint(n: &BigInt) -> RistrettoScalar { + let curve_order = RistrettoScalar::group_order(); + let mut bytes = n + .modulus(&curve_order) + .to_bytes_array::<32>() + .expect("n mod curve_order must be equal or less than 32 bytes"); + bytes.reverse(); + RistrettoScalar { + purpose: "from_bigint", + fe: SK::from_bytes_mod_order(bytes).into(), + } } - fn from(n: &BigInt) -> RistrettoScalar { - let mut v = BigInt::to_bytes(n); - //TODO: add consistency check for sizes max 32/ max 64 - let mut bytes_array_32: [u8; 32]; - let mut bytes_array_64: [u8; 64]; - if v.len() < SECRET_KEY_SIZE { - let mut template = vec![0; SECRET_KEY_SIZE - v.len()]; - template.extend_from_slice(&v); - v = template; - } - if v.len() > SECRET_KEY_SIZE && v.len() < 2 * SECRET_KEY_SIZE { - let mut template = vec![0; 2 * SECRET_KEY_SIZE - v.len()]; - template.extend_from_slice(&v); - v = template; - } - if v.len() == SECRET_KEY_SIZE { - bytes_array_32 = [0; SECRET_KEY_SIZE]; - let bytes = &v[..]; - bytes_array_32.copy_from_slice(&bytes); - bytes_array_32.reverse(); - RistrettoScalar { - purpose: "from_big_int", - fe: SK::from_bytes_mod_order(bytes_array_32), - } - } else { - bytes_array_64 = [0; 2 * SECRET_KEY_SIZE]; - let bytes = &v[..]; - bytes_array_64.copy_from_slice(&bytes); - bytes_array_64.reverse(); - RistrettoScalar { - purpose: "from_big_int", - fe: SK::from_bytes_mod_order_wide(&bytes_array_64), - } - } + fn to_bigint(&self) -> BigInt { + let mut t = self.fe.to_bytes(); + t.reverse(); + BigInt::from_bytes(&t) } - fn to_big_int(&self) -> BigInt { - let t1 = &self.fe.to_bytes()[0..self.fe.to_bytes().len()]; - let mut t2 = t1.to_vec(); - t2.reverse(); - BigInt::from_bytes(&t2[0..self.fe.to_bytes().len()]) + fn serialize(&self) -> Self::ScalarBytes { + self.fe.to_bytes() } - fn q() -> BigInt { - let l = BASEPOINT_ORDER; - let l_fe = RistrettoScalar { - purpose: "q", - fe: l, - }; - l_fe.to_big_int() + fn deserialize(bytes: &[u8]) -> Result { + let bytes: [u8; 32] = bytes.try_into().or(Err(DeserializationError))?; + Ok(RistrettoScalar { + purpose: "from_bigint", + fe: SK::from_canonical_bytes(bytes) + .ok_or(DeserializationError)? + .into(), + }) } - fn add(&self, other: &SK) -> RistrettoScalar { + fn add(&self, other: &Self) -> RistrettoScalar { RistrettoScalar { purpose: "add", - fe: self.get_element() + other, + fe: (*self.fe + *other.fe).into(), } } - fn mul(&self, other: &SK) -> RistrettoScalar { + fn mul(&self, other: &Self) -> RistrettoScalar { RistrettoScalar { purpose: "mul", - fe: self.get_element() * other, + fe: (*self.fe * *other.fe).into(), } } - fn sub(&self, other: &SK) -> RistrettoScalar { + fn sub(&self, other: &Self) -> RistrettoScalar { RistrettoScalar { purpose: "sub", - fe: self.get_element() - other, + fe: (*self.fe - *other.fe).into(), } } - fn invert(&self) -> RistrettoScalar { - let inv: SK = self.get_element().invert(); + fn neg(&self) -> Self { RistrettoScalar { - purpose: "invert", - fe: inv, + purpose: "neg", + fe: (-&*self.fe).into(), } } -} -impl Mul for RistrettoScalar { - type Output = RistrettoScalar; - fn mul(self, other: RistrettoScalar) -> RistrettoScalar { - (&self).mul(&other.get_element()) + fn invert(&self) -> Option { + if self.is_zero() { + None + } else { + Some(RistrettoScalar { + purpose: "invert", + fe: self.fe.invert().into(), + }) + } } -} -impl<'o> Mul<&'o RistrettoScalar> for RistrettoScalar { - type Output = RistrettoScalar; - fn mul(self, other: &'o RistrettoScalar) -> RistrettoScalar { - (&self).mul(&other.get_element()) + fn add_assign(&mut self, other: &Self) { + *self.fe += &*other.fe; } -} - -impl Add for RistrettoScalar { - type Output = RistrettoScalar; - fn add(self, other: RistrettoScalar) -> RistrettoScalar { - (&self).add(&other.get_element()) + fn mul_assign(&mut self, other: &Self) { + *self.fe *= &*other.fe; } -} - -impl<'o> Add<&'o RistrettoScalar> for RistrettoScalar { - type Output = RistrettoScalar; - fn add(self, other: &'o RistrettoScalar) -> RistrettoScalar { - (&self).add(&other.get_element()) + fn sub_assign(&mut self, other: &Self) { + *self.fe -= &*other.fe; } -} -impl Serialize for RistrettoScalar { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_big_int().to_hex()) + fn group_order() -> &'static BigInt { + &GROUP_ORDER } -} -impl<'de> Deserialize<'de> for RistrettoScalar { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(Secp256k1ScalarVisitor) + fn underlying_ref(&self) -> &Self::Underlying { + &self.fe } -} - -struct Secp256k1ScalarVisitor; - -impl<'de> Visitor<'de> for Secp256k1ScalarVisitor { - type Value = RistrettoScalar; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("ristretto") + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.fe } - - fn visit_str(self, s: &str) -> Result { - let v = BigInt::from_hex(s).map_err(E::custom)?; - Ok(ECScalar::from(&v)) + fn from_underlying(fe: Self::Underlying) -> RistrettoScalar { + RistrettoScalar { + purpose: "from_underlying", + fe: fe.into(), + } } } impl PartialEq for RistrettoScalar { fn eq(&self, other: &RistrettoScalar) -> bool { - self.get_element() == other.get_element() + self.fe == other.fe } } -impl PartialEq for RistrettoCurvPoint { - fn eq(&self, other: &RistrettoCurvPoint) -> bool { - self.get_element() == other.get_element() - } -} - -impl Zeroize for RistrettoCurvPoint { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, GE::generator()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } -} - -impl ECPoint for RistrettoCurvPoint { - type SecretKey = SK; - type PublicKey = PK; +impl ECPoint for RistrettoPoint { type Scalar = RistrettoScalar; + type Underlying = PK; - fn base_point2() -> RistrettoCurvPoint { - let g: GE = ECPoint::generator(); - let hash = HSha256::create_hash(&[&g.bytes_compressed_to_big_int()]); - let bytes = BigInt::to_bytes(&hash); - let h: GE = ECPoint::from_bytes(&bytes[..]).unwrap(); - RistrettoCurvPoint { - purpose: "random", - ge: h.get_element(), - } - } + type CompressedPoint = [u8; 32]; + type UncompressedPoint = [u8; 32]; - fn generator() -> RistrettoCurvPoint { - RistrettoCurvPoint { - purpose: "base_fe", - ge: RISTRETTO_BASEPOINT_COMPRESSED, + fn zero() -> RistrettoPoint { + RistrettoPoint { + purpose: "zero", + ge: PK::identity(), } } - fn get_element(&self) -> PK { - self.ge - } - - fn x_coor(&self) -> Option { - unimplemented!(); - } - - fn y_coor(&self) -> Option { - let y_fe = SK::from_bytes_mod_order(self.ge.to_bytes()); - let y_fe = RistrettoScalar { - purpose: "y_coor", - fe: y_fe, - }; - Some(y_fe.to_big_int()) - } - - fn bytes_compressed_to_big_int(&self) -> BigInt { - BigInt::from_bytes(self.ge.to_bytes()[0..self.ge.to_bytes().len()].as_ref()) - } - fn from_bytes(bytes: &[u8]) -> Result { - let bytes_vec = bytes.to_vec(); - let mut bytes_array_32 = [0u8; 32]; - let byte_len = bytes_vec.len(); - match byte_len { - 0..=32 => { - let mut template = vec![0; 32 - bytes_vec.len()]; - template.extend_from_slice(&bytes); - let bytes_vec = template; - let bytes_slice = &bytes_vec[0..32]; - bytes_array_32.copy_from_slice(&bytes_slice); - let r_point: PK = CompressedRistretto::from_slice(&bytes_array_32); - let r_point_compress = r_point.decompress(); - match r_point_compress { - Some(x) => { - let new_point = RistrettoCurvPoint { - purpose: "random", - ge: x.compress(), - }; - Ok(new_point) - } - None => Err(InvalidPublicKey), - } - } - - _ => { - let bytes_slice = &bytes_vec[0..32]; - bytes_array_32.copy_from_slice(&bytes_slice); - let r_point: PK = CompressedRistretto::from_slice(&bytes_array_32); - let r_point_compress = r_point.decompress(); - match r_point_compress { - Some(x) => { - let new_point = RistrettoCurvPoint { - purpose: "random", - ge: x.compress(), - }; - Ok(new_point) - } - None => Err(InvalidPublicKey), - } - } - } + fn is_zero(&self) -> bool { + self.ge.is_identity() } - fn pk_to_key_slice(&self) -> Vec { - let result = self.ge.to_bytes(); - result.to_vec() + fn generator() -> &'static RistrettoPoint { + &GENERATOR } - fn scalar_mul(&self, fe: &SK) -> RistrettoCurvPoint { - let skpk = fe * (self.ge.decompress().unwrap()); - RistrettoCurvPoint { - purpose: "scalar_point_mul", - ge: skpk.compress(), - } + fn base_point2() -> &'static RistrettoPoint { + &BASE_POINT2 } - fn add_point(&self, other: &PK) -> RistrettoCurvPoint { - let pkpk = self.ge.decompress().unwrap() + other.decompress().unwrap(); - RistrettoCurvPoint { - purpose: "combine", - ge: pkpk.compress(), - } + fn from_coords(_x: &BigInt, _y: &BigInt) -> Result { + // Underlying library intentionally hides x coordinate. There's no way to match if `x` + // correspond to given `y`. + Err(NotOnCurve) } - fn sub_point(&self, other: &PK) -> RistrettoCurvPoint { - let pkpk = self.ge.decompress().unwrap() - other.decompress().unwrap(); - RistrettoCurvPoint { - purpose: "sub", - ge: pkpk.compress(), - } + fn x_coord(&self) -> Option { + // Underlying library intentionally hides x coordinate. There's no way we can know x + // coordinate + None } - fn from_coor(_x: &BigInt, _y: &BigInt) -> RistrettoCurvPoint { - unimplemented!(); + fn y_coord(&self) -> Option { + let mut y = self.ge.compress().to_bytes(); + y.reverse(); + Some(BigInt::from_bytes(&y[..])) } -} -impl Mul for RistrettoCurvPoint { - type Output = RistrettoCurvPoint; - fn mul(self, other: RistrettoScalar) -> RistrettoCurvPoint { - self.scalar_mul(&other.get_element()) + fn coords(&self) -> Option { + None } -} -impl<'o> Mul<&'o RistrettoScalar> for RistrettoCurvPoint { - type Output = RistrettoCurvPoint; - fn mul(self, other: &'o RistrettoScalar) -> RistrettoCurvPoint { - self.scalar_mul(&other.get_element()) + fn serialize_compressed(&self) -> Self::CompressedPoint { + self.ge.compress().to_bytes() } -} -impl<'o> Mul<&'o RistrettoScalar> for &'o RistrettoCurvPoint { - type Output = RistrettoCurvPoint; - fn mul(self, other: &'o RistrettoScalar) -> RistrettoCurvPoint { - self.scalar_mul(&other.get_element()) + fn serialize_uncompressed(&self) -> Self::UncompressedPoint { + self.ge.compress().to_bytes() } -} -impl Add for RistrettoCurvPoint { - type Output = RistrettoCurvPoint; - fn add(self, other: RistrettoCurvPoint) -> RistrettoCurvPoint { - self.add_point(&other.get_element()) - } -} + fn deserialize(bytes: &[u8]) -> Result { + let mut buffer = [0u8; 32]; + let n = bytes.len(); -impl<'o> Add<&'o RistrettoCurvPoint> for RistrettoCurvPoint { - type Output = RistrettoCurvPoint; - fn add(self, other: &'o RistrettoCurvPoint) -> RistrettoCurvPoint { - self.add_point(&other.get_element()) - } -} + if n == 0 || n > 32 { + return Err(DeserializationError); + } + buffer[32 - n..].copy_from_slice(bytes); -impl<'o> Add<&'o RistrettoCurvPoint> for &'o RistrettoCurvPoint { - type Output = RistrettoCurvPoint; - fn add(self, other: &'o RistrettoCurvPoint) -> RistrettoCurvPoint { - self.add_point(&other.get_element()) + CompressedRistretto::from_slice(&buffer) + .decompress() + .ok_or(DeserializationError) + .map(|ge| RistrettoPoint { + purpose: "deserialize", + ge, + }) } -} -#[cfg(feature = "merkle")] -impl Hashable for RistrettoCurvPoint { - fn update_context(&self, context: &mut Sha3) { - let bytes: Vec = self.pk_to_key_slice(); - context.input(&bytes[..]); + fn check_point_order_equals_group_order(&self) -> bool { + !self.is_zero() } -} -impl Serialize for RistrettoCurvPoint { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = self.pk_to_key_slice(); - let bytes_as_bn = BigInt::from_bytes(&bytes[..]); - let mut state = serializer.serialize_struct("RistrettoCurvPoint", 1)?; - state.serialize_field("bytes_str", &bytes_as_bn.to_hex())?; - state.end() + fn scalar_mul(&self, fe: &Self::Scalar) -> RistrettoPoint { + RistrettoPoint { + purpose: "scalar_mul", + ge: self.ge * *fe.fe, + } } -} -impl<'de> Deserialize<'de> for RistrettoCurvPoint { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - const FIELDS: &[&str] = &["bytes_str"]; - deserializer.deserialize_struct("RistrettoCurvPoint", FIELDS, RistrettoCurvPointVisitor) + fn add_point(&self, other: &Self) -> RistrettoPoint { + RistrettoPoint { + purpose: "add_point", + ge: self.ge + other.ge, + } } -} -struct RistrettoCurvPointVisitor; - -impl<'de> Visitor<'de> for RistrettoCurvPointVisitor { - type Value = RistrettoCurvPoint; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("RistrettoCurvPoint") + fn sub_point(&self, other: &Self) -> RistrettoPoint { + RistrettoPoint { + purpose: "sub_point", + ge: self.ge - other.ge, + } } - fn visit_seq(self, mut seq: V) -> Result - where - V: SeqAccess<'de>, - { - let bytes_str = seq - .next_element()? - .ok_or_else(|| V::Error::invalid_length(0, &"a single element"))?; - let bytes_bn = BigInt::from_hex(bytes_str).map_err(V::Error::custom)?; - let bytes = BigInt::to_bytes(&bytes_bn); - RistrettoCurvPoint::from_bytes(&bytes[..]) - .map_err(|_| V::Error::custom("failed to parse ristretto point")) + fn neg_point(&self) -> RistrettoPoint { + RistrettoPoint { + purpose: "sub_point", + ge: -self.ge, + } } - fn visit_map>(self, mut map: E) -> Result { - let mut bytes_str: String = "".to_string(); - - while let Some(key) = map.next_key::<&'de str>()? { - let v = map.next_value::<&'de str>()?; - match key { - "bytes_str" => { - bytes_str = String::from(v); - } - _ => return Err(E::Error::unknown_field(key, &["bytes_str"])), - } + fn scalar_mul_assign(&mut self, scalar: &Self::Scalar) { + self.ge *= &*scalar.fe + } + fn add_point_assign(&mut self, other: &Self) { + self.ge += &other.ge + } + fn sub_point_assign(&mut self, other: &Self) { + self.ge -= &other.ge + } + fn underlying_ref(&self) -> &Self::Underlying { + &self.ge + } + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.ge + } + fn from_underlying(ge: Self::Underlying) -> RistrettoPoint { + RistrettoPoint { + purpose: "from_underlying", + ge, } - let bytes_bn = BigInt::from_hex(&bytes_str).map_err(E::Error::custom)?; - let bytes = BigInt::to_bytes(&bytes_bn); + } +} - RistrettoCurvPoint::from_bytes(&bytes[..]) - .map_err(|_| E::Error::custom("failed to parse ristretto point")) +impl PartialEq for RistrettoPoint { + fn eq(&self, other: &RistrettoPoint) -> bool { + self.ge == other.ge } } -#[cfg(test)] -mod tests { - use super::{RistrettoCurvPoint, RistrettoScalar}; - use crate::arithmetic::traits::*; - use crate::elliptic::curves::traits::ECPoint; - use crate::elliptic::curves::traits::ECScalar; - use crate::BigInt; - - type GE = RistrettoCurvPoint; - type FE = RistrettoScalar; - - #[test] - fn test_serdes_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - - let pk = GE::base_point2(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - } - - #[test] - fn bincode_pk() { - let pk = GE::generator(); - let encoded = bincode::serialize(&pk).unwrap(); - let decoded: RistrettoCurvPoint = bincode::deserialize(encoded.as_slice()).unwrap(); - assert_eq!(decoded, pk); - } - - #[test] - #[should_panic] - fn test_serdes_bad_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - // we make sure that the string encodes invalid point: - let s: String = s.replace("e2f2", "e2f5"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - } - - #[test] - fn test_from_mpz() { - let rand_scalar: FE = ECScalar::new_random(); - let rand_bn = rand_scalar.to_big_int(); - let rand_scalar2: FE = ECScalar::from(&rand_bn); - assert_eq!(rand_scalar, rand_scalar2); - } - - #[test] - fn test_from_slice() { - let point: GE = GE::base_point2(); - let point_bn = point.bytes_compressed_to_big_int(); - let point_bytes = BigInt::to_bytes(&point_bn); - let point_reconstruct = GE::from_bytes(&point_bytes[..]).expect("bad encoding of point"); - assert_eq!(point_reconstruct, point); - } - - #[test] - #[should_panic] - fn test_from_slice_bad_point() { - // let rng = &mut thread_rng(); - // rng.fill(&mut scalar_bytes); - let scalar_bytes = [ - 47, 99, 244, 119, 185, 184, 77, 196, 233, 191, 206, 168, 191, 24, 226, 7, 254, 11, 131, - 172, 57, 35, 110, 9, 103, 25, 98, 249, 219, 248, 33, 115, - ]; - GE::from_bytes(&scalar_bytes[..]).expect("bad encoding of point"); - } - // this test fails once in a while. - #[test] - fn test_minus_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let b_bn = b.to_big_int(); - let order = FE::q(); - let minus_b = BigInt::mod_sub(&order, &b_bn, &order); - let a_minus_b = BigInt::mod_add(&a.to_big_int(), &minus_b, &order); - let a_minus_b_fe: FE = ECScalar::from(&a_minus_b); - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_minus_b_fe; - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.sub_point(&point_b.get_element()); - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_invert() { - let a: FE = ECScalar::new_random(); - let a_bn = a.to_big_int(); - let a_inv = a.invert(); - let a_inv_bn_1 = BigInt::mod_inv(&a_bn, &FE::q()).unwrap(); - let a_inv_bn_2 = a_inv.to_big_int(); - assert_eq!(a_inv_bn_1, a_inv_bn_2); - } - - #[test] - fn test_from_bytes_3() { - let test_vec = [ - 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, 0, 1, 2, 3, 4, 5, 6, - ]; - let result = RistrettoCurvPoint::from_bytes(&test_vec); - assert!(result.is_ok()) +impl Zeroize for RistrettoPoint { + fn zeroize(&mut self) { + unsafe { ptr::write_volatile(&mut self.ge, PK::default()) }; + atomic::compiler_fence(atomic::Ordering::SeqCst); } } diff --git a/src/elliptic/curves/ed25519.rs b/src/elliptic/curves/ed25519.rs index 2c65a7c7..d3140a30 100644 --- a/src/elliptic/curves/ed25519.rs +++ b/src/elliptic/curves/ed25519.rs @@ -8,38 +8,105 @@ // paper: https://ed25519.cr.yp.to/ed25519-20110926.pdf // based on https://docs.rs/cryptoxide/0.1.0/cryptoxide/curve25519/index.html // https://cr.yp.to/ecdh/curve25519-20060209.pdf + +use std::fmt; use std::fmt::Debug; +use std::ops; +use std::ptr; use std::str; -pub const TWO_TIMES_SECRET_KEY_SIZE: usize = 64; -use super::traits::{ECPoint, ECScalar}; -use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; -use crate::cryptographic_primitives::hashing::traits::Hash; -use serde::de::{self, Error, MapAccess, SeqAccess, Visitor}; -use serde::ser::SerializeStruct; -use serde::ser::{Serialize, Serializer}; -use serde::{Deserialize, Deserializer}; -use std::fmt; -use std::ops::{Add, Mul}; -pub type SK = Fe; -pub type PK = GeP3; +use std::sync::atomic; + +use cryptoxide::curve25519::*; +use zeroize::{Zeroize, Zeroizing}; + use crate::arithmetic::traits::*; +use crate::cryptographic_primitives::hashing::Digest; use crate::BigInt; -use crate::ErrorKey::{self, InvalidPublicKey}; -#[cfg(feature = "merkle")] -use crypto::digest::Digest; -#[cfg(feature = "merkle")] -use crypto::sha3::Sha3; -use cryptoxide::curve25519::*; -#[cfg(feature = "merkle")] -use merkle::Hashable; -use std::ptr; -use std::sync::atomic; -use zeroize::Zeroize; -#[derive(Clone, Copy)] +use super::traits::{ECPoint, ECScalar}; +use crate::elliptic::curves::{Curve, DeserializationError, NotOnCurve, PointCoords}; + +lazy_static::lazy_static! { + static ref GROUP_ORDER: BigInt = Ed25519Scalar { + purpose: "intermediate group_order", + fe: SK(Fe::from_bytes(&[ + 237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, + ])).into() + }.to_bigint(); + + static ref ZERO: Ed25519Point = Ed25519Point { + purpose: "zero", + ge: ge_scalarmult_base(&[ + 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, 0, + ]), + }; + + static ref GENERATOR: Ed25519Point = Ed25519Point { + purpose: "generator", + ge: ge_scalarmult_base(&[ + 1, 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, + ]), + }; + + static ref BASE_POINT2: Ed25519Point = { + let bytes = GENERATOR.serialize_compressed(); + let hashed = sha2::Sha256::digest(bytes.as_ref()); + let hashed_twice = sha2::Sha256::digest(&hashed); + let p = Ed25519Point::deserialize(&hashed_twice).unwrap(); + let eight = Ed25519Scalar::from_bigint(&BigInt::from(8)); + Ed25519Point { + purpose: "base_point2", + ge: p.scalar_mul(&eight).ge, + } + }; +} + +const FE_ZERO: Fe = Fe([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); +const TWO_TIMES_SECRET_KEY_SIZE: usize = 64; + +/// Alias to [Edwards point](GeP3) +pub type PK = GeP3; +/// Wraps [Fe] and implements Zeroize to it +#[derive(Clone)] +pub struct SK(pub Fe); + +impl Zeroize for SK { + fn zeroize(&mut self) { + self.0 .0.zeroize() + } +} +impl ops::Deref for SK { + type Target = Fe; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl ops::DerefMut for SK { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Ed25519 curve implementation based on [cryptoxide] library +/// +/// ## Implementation notes +/// * x coordinate +/// +/// Underlying library doesn't expose x coordinate of curve point, but there's an algorithm +/// recovering x coordinate of ed25519 point from its y coordinate. Every time you call +/// `.x_coord()` or `from_coords()`, it takes y coordinate and runs `xrecover(y)` underhood. Keep +/// in mind that `xrecover` is quite expensive operation. +#[derive(Debug, PartialEq, Clone)] +pub enum Ed25519 {} + +#[derive(Clone)] pub struct Ed25519Scalar { purpose: &'static str, - fe: SK, + fe: Zeroizing, } #[derive(Clone, Copy)] pub struct Ed25519Point { @@ -49,40 +116,41 @@ pub struct Ed25519Point { pub type GE = Ed25519Point; pub type FE = Ed25519Scalar; -impl Zeroize for Ed25519Scalar { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, FE::zero()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } +impl Curve for Ed25519 { + type Point = GE; + type Scalar = FE; + + const CURVE_NAME: &'static str = "ed25519"; } + impl ECScalar for Ed25519Scalar { - type SecretKey = SK; + type Underlying = SK; + + type ScalarBytes = [u8; 32]; // we chose to multiply by 8 (co-factor) all group elements to work in the prime order sub group. // each random fe is having its 3 first bits zeroed - fn new_random() -> Ed25519Scalar { - let rnd_bn = BigInt::sample_below(&FE::q()); - let rnd_bn_mul_8 = BigInt::mod_mul(&rnd_bn, &BigInt::from(8), &FE::q()); - ECScalar::from(&rnd_bn_mul_8) + fn random() -> Ed25519Scalar { + let rnd_bn = BigInt::sample_below(Self::group_order()); + let rnd_bn_mul_8 = BigInt::mod_mul(&rnd_bn, &BigInt::from(8), Self::group_order()); + Ed25519Scalar { + purpose: "random", + fe: Self::from_bigint(&rnd_bn_mul_8).fe, + } } fn zero() -> Ed25519Scalar { - let q_fe: FE = ECScalar::from(&FE::q()); Ed25519Scalar { purpose: "zero", - fe: q_fe.get_element(), + fe: SK(FE_ZERO).into(), } } - fn get_element(&self) -> SK { - self.fe - } - fn set_element(&mut self, element: SK) { - self.fe = element + fn is_zero(&self) -> bool { + self.fe.0 == FE_ZERO } - fn from(n: &BigInt) -> Ed25519Scalar { + fn from_bigint(n: &BigInt) -> Ed25519Scalar { let mut v = BigInt::to_bytes(&n); if v.len() > TWO_TIMES_SECRET_KEY_SIZE { v = v[0..TWO_TIMES_SECRET_KEY_SIZE].to_vec(); @@ -94,74 +162,105 @@ impl ECScalar for Ed25519Scalar { v.reverse(); sc_reduce(&mut v[..]); Ed25519Scalar { - purpose: "from_big_int", - fe: SK::from_bytes(&v[..]), + purpose: "from_bigint", + fe: SK(Fe::from_bytes(&v[..])).into(), } } - fn to_big_int(&self) -> BigInt { - let t1 = &self.fe.to_bytes()[0..self.fe.to_bytes().len()]; - let mut t2 = t1.to_vec(); - t2.reverse(); - BigInt::from_bytes(&t2[0..self.fe.to_bytes().len()]) + fn to_bigint(&self) -> BigInt { + let mut t = self.fe.to_bytes().to_vec(); + t.reverse(); + BigInt::from_bytes(&t) } - fn q() -> BigInt { - let q_bytes_array: [u8; 32]; - q_bytes_array = [ - 237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, - ]; - let l_fe = SK::from_bytes(&q_bytes_array); - let l_fe = Ed25519Scalar { - purpose: "q", - fe: l_fe, - }; - l_fe.to_big_int() + fn serialize(&self) -> Self::ScalarBytes { + self.fe.to_bytes() } - fn add(&self, other: &SK) -> Ed25519Scalar { - let other_point = Ed25519Scalar { - purpose: "other add", - fe: *other, - }; - let lhs_bn = self.to_big_int(); - let rhs_bn = other_point.to_big_int(); - let sum = BigInt::mod_add(&lhs_bn, &rhs_bn, &FE::q()); - let sum_fe: FE = ECScalar::from(&sum); + fn deserialize(bytes: &[u8]) -> Result { + if bytes.len() != 32 { + return Err(DeserializationError); + } + Ok(Ed25519Scalar { + purpose: "deserialize", + fe: SK(Fe::from_bytes(bytes)).into(), + }) + } - sum_fe + fn add(&self, other: &Self) -> Ed25519Scalar { + Ed25519Scalar { + purpose: "add", + fe: Self::from_bigint(&BigInt::mod_add( + &self.to_bigint(), + &other.to_bigint(), + Self::group_order(), + )) + .fe, + } } - fn mul(&self, other: &SK) -> Ed25519Scalar { - let other_point = Ed25519Scalar { - purpose: "other mul", - fe: *other, - }; - let lhs_bn = self.to_big_int(); - let rhs_bn = other_point.to_big_int(); - let mul = BigInt::mod_mul(&lhs_bn, &rhs_bn, &FE::q()); - let mul_fe: FE = ECScalar::from(&mul); - mul_fe + fn mul(&self, other: &Self) -> Ed25519Scalar { + Ed25519Scalar { + purpose: "mul", + fe: Self::from_bigint(&BigInt::mod_mul( + &self.to_bigint(), + &other.to_bigint(), + Self::group_order(), + )) + .fe, + } } - fn sub(&self, other: &SK) -> Ed25519Scalar { - let other_point = Ed25519Scalar { - purpose: "other sub", - fe: *other, - }; - let lhs_bn = self.to_big_int(); - let rhs_bn = other_point.to_big_int(); - let sub = BigInt::mod_sub(&lhs_bn, &rhs_bn, &FE::q()); - let sub_fe: FE = ECScalar::from(&sub); - sub_fe + fn sub(&self, other: &Self) -> Ed25519Scalar { + Ed25519Scalar { + purpose: "sub", + fe: Self::from_bigint(&BigInt::mod_sub( + &self.to_bigint(), + &other.to_bigint(), + Self::group_order(), + )) + .fe, + } + } + + fn neg(&self) -> Self { + Ed25519Scalar { + purpose: "neg", + fe: Self::from_bigint(&BigInt::mod_sub( + &Self::zero().to_bigint(), + &self.to_bigint(), + Self::group_order(), + )) + .fe, + } + } + + fn invert(&self) -> Option { + if self.is_zero() { + None + } else { + Some(Ed25519Scalar { + purpose: "invert", + fe: Self::from_bigint(&BigInt::mod_inv(&self.to_bigint(), Self::group_order())?).fe, + }) + } } - fn invert(&self) -> Ed25519Scalar { - let self_bn = self.to_big_int(); - let inv = BigInt::mod_inv(&self_bn, &FE::q()).unwrap(); - let inv_fe: FE = ECScalar::from(&inv); - inv_fe + fn group_order() -> &'static BigInt { + &GROUP_ORDER + } + + fn underlying_ref(&self) -> &Self::Underlying { + &self.fe + } + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.fe + } + fn from_underlying(fe: Self::Underlying) -> Ed25519Scalar { + Ed25519Scalar { + purpose: "from_underlying", + fe: fe.into(), + } } } @@ -178,68 +277,7 @@ impl Debug for Ed25519Scalar { impl PartialEq for Ed25519Scalar { fn eq(&self, other: &Ed25519Scalar) -> bool { - self.get_element().to_bytes() == other.get_element().to_bytes() - } -} - -impl Mul for Ed25519Scalar { - type Output = Ed25519Scalar; - fn mul(self, other: Ed25519Scalar) -> Ed25519Scalar { - (&self).mul(&other.get_element()) - } -} - -impl<'o> Mul<&'o Ed25519Scalar> for Ed25519Scalar { - type Output = Ed25519Scalar; - fn mul(self, other: &'o Ed25519Scalar) -> Ed25519Scalar { - (&self).mul(&other.get_element()) - } -} - -impl Add for Ed25519Scalar { - type Output = Ed25519Scalar; - fn add(self, other: Ed25519Scalar) -> Ed25519Scalar { - (&self).add(&other.get_element()) - } -} - -impl<'o> Add<&'o Ed25519Scalar> for Ed25519Scalar { - type Output = Ed25519Scalar; - fn add(self, other: &'o Ed25519Scalar) -> Ed25519Scalar { - (&self).add(&other.get_element()) - } -} - -impl Serialize for Ed25519Scalar { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_big_int().to_hex()) - } -} - -impl<'de> Deserialize<'de> for Ed25519Scalar { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(Ed25519ScalarVisitor) - } -} - -struct Ed25519ScalarVisitor; - -impl<'de> Visitor<'de> for Ed25519ScalarVisitor { - type Value = Ed25519Scalar; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("ed25519") - } - - fn visit_str(self, s: &str) -> Result { - let v = BigInt::from_hex(s).map_err(E::custom)?; - Ok(ECScalar::from(&v)) + self.fe.0 == other.fe.0 } } @@ -256,76 +294,86 @@ impl Debug for Ed25519Point { impl PartialEq for Ed25519Point { fn eq(&self, other: &Ed25519Point) -> bool { - self.get_element().to_bytes() == other.get_element().to_bytes() + self.ge.to_bytes() == other.ge.to_bytes() } } impl Zeroize for Ed25519Point { fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, GE::generator()) }; - atomic::fence(atomic::Ordering::SeqCst); + unsafe { ptr::write_volatile(&mut self.ge, GENERATOR.ge) }; atomic::compiler_fence(atomic::Ordering::SeqCst); } } impl ECPoint for Ed25519Point { - type SecretKey = SK; - type PublicKey = PK; + type Underlying = PK; type Scalar = Ed25519Scalar; - fn base_point2() -> Ed25519Point { - let g: GE = ECPoint::generator(); - let hash = HSha256::create_hash(&[&g.bytes_compressed_to_big_int()]); - let hash = HSha256::create_hash(&[&hash]); - let bytes = BigInt::to_bytes(&hash); - let h: GE = ECPoint::from_bytes(&bytes[..]).unwrap(); - Ed25519Point { - purpose: "random", - ge: h.get_element(), - } + type CompressedPoint = [u8; 32]; + type UncompressedPoint = [u8; 32]; + + fn zero() -> Ed25519Point { + *ZERO } - fn generator() -> Ed25519Point { - let vec_1: [u8; 32]; - vec_1 = [ - 1, 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, - ]; - Ed25519Point { - purpose: "base_fe", - ge: ge_scalarmult_base(&vec_1[..]), - } + fn is_zero(&self) -> bool { + self == &*ZERO } - fn get_element(&self) -> PK { - self.ge + fn generator() -> &'static Ed25519Point { + &GENERATOR } - fn x_coor(&self) -> Option { - let y = self.y_coor().unwrap(); - Some(xrecover(y)) + fn base_point2() -> &'static Ed25519Point { + &BASE_POINT2 } - fn y_coor(&self) -> Option { - let y_fe = SK::from_bytes(self.ge.to_bytes()[0..self.ge.to_bytes().len()].as_ref()); - let y = Ed25519Scalar { - purpose: "base_fe", - fe: y_fe, + fn from_coords(x: &BigInt, y: &BigInt) -> Result { + let expected_x = xrecover(y); + if &expected_x != x { + return Err(NotOnCurve); + } + let y_bytes = y.to_bytes(); + let mut padded = match y_bytes.len() { + n if n > 32 => return Err(NotOnCurve), + 32 => y_bytes, + _ => { + let mut padding = vec![0; 32 - y_bytes.len()]; + padding.extend_from_slice(&y_bytes); + padding + } }; - Some(y.to_big_int()) + padded.reverse(); + Self::deserialize(&padded).map_err(|_e| NotOnCurve) + } + + fn x_coord(&self) -> Option { + let y = self.y_coord().unwrap(); + Some(xrecover(&y)) } - fn bytes_compressed_to_big_int(&self) -> BigInt { - BigInt::from_bytes(self.ge.to_bytes()[0..self.ge.to_bytes().len()].as_ref()) + fn y_coord(&self) -> Option { + let mut bytes = self.ge.to_bytes().to_vec(); + bytes.reverse(); + Some(BigInt::from_bytes(&bytes)) } - // from_bytes will return Ok only if the bytes encode a valid point. - // since valid point are not necessarily in the sub group of prime order this needs to be checked - // as well such that Ok will be returned only for valid point of the sub group prime order. - // currently we change the encoded point by multiply by 8 to make sure it is in the sub group of prime order. - // This is because for our use cases so far it doesn't matter and multiply by 8 is faster than testing for a point - // in the sub group prime order - fn from_bytes(bytes: &[u8]) -> Result { + fn coords(&self) -> Option { + let y = self + .y_coord() + .expect("coordinates are always defined for edwards curves"); + Some(PointCoords { x: xrecover(&y), y }) + } + + fn serialize_compressed(&self) -> Self::CompressedPoint { + self.ge.to_bytes() + } + + fn serialize_uncompressed(&self) -> Self::UncompressedPoint { + self.ge.to_bytes() + } + + fn deserialize(bytes: &[u8]) -> Result { let bytes_vec = bytes.to_vec(); let mut bytes_array_32 = [0u8; 32]; let byte_len = bytes_vec.len(); @@ -342,18 +390,14 @@ impl ECPoint for Ed25519Point { let ge_bytes = ge_from_bytes.unwrap().to_bytes(); let ge_from_bytes = PK::from_bytes_negate_vartime(&ge_bytes[..]); match ge_from_bytes { - Some(y) => { - let eight: FE = ECScalar::from(&BigInt::from(8)); - let new_point = Ed25519Point { - purpose: "random", - ge: y, - }; - Ok(new_point * eight) - } - None => Err(InvalidPublicKey), + Some(y) => Ok(Ed25519Point { + purpose: "deserialize", + ge: y, + }), + None => Err(DeserializationError), } } - None => Err(InvalidPublicKey), + None => Err(DeserializationError), } } _ => { @@ -365,36 +409,26 @@ impl ECPoint for Ed25519Point { let ge_bytes = ge_from_bytes.unwrap().to_bytes(); let ge_from_bytes = PK::from_bytes_negate_vartime(&ge_bytes[..]); match ge_from_bytes { - Some(y) => { - let eight: FE = ECScalar::from(&BigInt::from(8)); - let new_point = Ed25519Point { - purpose: "random", - ge: y, - }; - Ok(new_point * eight) - } - None => Err(InvalidPublicKey), + Some(y) => Ok(Ed25519Point { + purpose: "random", + ge: y, + }), + None => Err(DeserializationError), } } - None => Err(InvalidPublicKey), + None => Err(DeserializationError), } } } } - fn pk_to_key_slice(&self) -> Vec { - let result = self.ge.to_bytes(); - result.to_vec() - } - - fn scalar_mul(&self, fe: &SK) -> Ed25519Point { + fn scalar_mul(&self, fe: &Self::Scalar) -> Ed25519Point { let vec_0: [u8; 32]; vec_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, 0, 0, ]; - let p2_point = - GeP2::double_scalarmult_vartime(&fe.to_bytes()[..], self.get_element(), &vec_0[..]); + let p2_point = GeP2::double_scalarmult_vartime(&fe.fe.to_bytes()[..], self.ge, &vec_0[..]); let mut p2_bytes = p2_point.to_bytes(); p2_bytes[31] ^= 1 << 7; @@ -402,23 +436,23 @@ impl ECPoint for Ed25519Point { let ge = GeP3::from_bytes_negate_vartime(&p2_bytes[..]).unwrap(); Ed25519Point { - purpose: "scalar_point_mul", + purpose: "scalar_mul", ge, } } - fn add_point(&self, other: &PK) -> Ed25519Point { - let pkpk = self.ge + other.to_cached(); + fn add_point(&self, other: &Self) -> Ed25519Point { + let pkpk = self.ge + other.ge.to_cached(); let mut pk_p2_bytes = pkpk.to_p2().to_bytes(); pk_p2_bytes[31] ^= 1 << 7; Ed25519Point { - purpose: "combine", + purpose: "add", ge: PK::from_bytes_negate_vartime(&pk_p2_bytes).unwrap(), } } - fn sub_point(&self, other: &PK) -> Ed25519Point { - let pkpk = self.ge - other.to_cached(); + fn sub_point(&self, other: &Self) -> Ed25519Point { + let pkpk = self.ge - other.ge.to_cached(); let mut pk_p2_bytes = pkpk.to_p2().to_bytes(); pk_p2_bytes[31] ^= 1 << 7; @@ -428,130 +462,27 @@ impl ECPoint for Ed25519Point { } } - fn from_coor(_x: &BigInt, _y: &BigInt) -> Ed25519Point { - unimplemented!(); - } -} - -impl Mul for Ed25519Point { - type Output = Ed25519Point; - fn mul(self, other: Ed25519Scalar) -> Ed25519Point { - self.scalar_mul(&other.get_element()) - } -} - -impl<'o> Mul<&'o Ed25519Scalar> for Ed25519Point { - type Output = Ed25519Point; - fn mul(self, other: &'o Ed25519Scalar) -> Ed25519Point { - self.scalar_mul(&other.get_element()) - } -} - -impl<'o> Mul<&'o Ed25519Scalar> for &'o Ed25519Point { - type Output = Ed25519Point; - fn mul(self, other: &'o Ed25519Scalar) -> Ed25519Point { - self.scalar_mul(&other.get_element()) - } -} - -impl Add for Ed25519Point { - type Output = Ed25519Point; - fn add(self, other: Ed25519Point) -> Ed25519Point { - self.add_point(&other.get_element()) - } -} - -impl<'o> Add<&'o Ed25519Point> for Ed25519Point { - type Output = Ed25519Point; - fn add(self, other: &'o Ed25519Point) -> Ed25519Point { - self.add_point(&other.get_element()) - } -} - -impl<'o> Add<&'o Ed25519Point> for &'o Ed25519Point { - type Output = Ed25519Point; - fn add(self, other: &'o Ed25519Point) -> Ed25519Point { - self.add_point(&other.get_element()) - } -} - -#[cfg(feature = "merkle")] -impl Hashable for Ed25519Point { - fn update_context(&self, context: &mut Sha3) { - let bytes: Vec = self.pk_to_key_slice(); - context.input(&bytes[..]); - } -} - -impl Serialize for Ed25519Point { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = self.pk_to_key_slice(); - let bytes_as_bn = BigInt::from_bytes(&bytes[..]); - let padded_bytes_hex = format!("{:0>64}", bytes_as_bn.to_hex()); - let mut state = serializer.serialize_struct("ed25519CurvePoint", 1)?; - state.serialize_field("bytes_str", &padded_bytes_hex)?; - state.end() - } -} - -impl<'de> Deserialize<'de> for Ed25519Point { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let fields = &["bytes_str"]; - deserializer.deserialize_struct("Ed25519Point", fields, Ed25519PointVisitor) + fn neg_point(&self) -> Self { + ZERO.sub_point(self) } -} - -struct Ed25519PointVisitor; - -impl<'de> Visitor<'de> for Ed25519PointVisitor { - type Value = Ed25519Point; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("Ed25519Point") + fn underlying_ref(&self) -> &Self::Underlying { + &self.ge } - - fn visit_seq(self, mut seq: V) -> Result - where - V: SeqAccess<'de>, - { - let bytes_str = seq - .next_element()? - .ok_or_else(|| V::Error::invalid_length(0, &"a single element"))?; - let bytes_bn = BigInt::from_hex(bytes_str).map_err(V::Error::custom)?; - let bytes = BigInt::to_bytes(&bytes_bn); - Ed25519Point::from_bytes(&bytes[..]) - .map_err(|_| V::Error::custom("failed to parse ed25519 point")) + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.ge } - - fn visit_map>(self, mut map: E) -> Result { - let mut bytes_str: String = "".to_string(); - - while let Some(key) = map.next_key::<&'de str>()? { - let v = map.next_value::<&'de str>()?; - match key { - "bytes_str" => { - bytes_str = String::from(v); - } - _ => return Err(E::Error::unknown_field(key, &["bytes_str"])), - } + fn from_underlying(ge: Self::Underlying) -> Ed25519Point { + Ed25519Point { + purpose: "from_underlying", + ge, } - - let bytes_bn = BigInt::from_hex(&bytes_str).map_err(E::Error::custom)?; - let bytes = BigInt::to_bytes(&bytes_bn); - - Ed25519Point::from_bytes(&bytes[..]).map_err(|_| E::Error::custom("invalid ed25519 point")) } } #[allow(clippy::many_single_char_names)] //helper function, based on https://ed25519.cr.yp.to/python/ed25519.py -pub fn xrecover(y_coor: BigInt) -> BigInt { +fn xrecover(y_coor: &BigInt) -> BigInt { // let d = "37095705934669439343138083508754565189542113879843219016388785533085940283555"; // let d_bn = BigInt::from(d.as_bytes()); let q = BigInt::from(2u32).pow(255u32) - BigInt::from(19u32); @@ -560,7 +491,7 @@ pub fn xrecover(y_coor: BigInt) -> BigInt { let d_d = expmod(&BigInt::from(121_666), &(q.clone() - BigInt::from(2)), &q); let d_bn = d_n * d_d; - let y_sqr = y_coor.clone() * y_coor; + let y_sqr = y_coor * y_coor; let u = y_sqr.clone() - one.clone(); let v = y_sqr * d_bn + one; let v_inv = expmod(&v, &(q.clone() - BigInt::from(2)), &q); @@ -582,7 +513,7 @@ pub fn xrecover(y_coor: BigInt) -> BigInt { } //helper function, based on https://ed25519.cr.yp.to/python/ed25519.py -pub fn expmod(b: &BigInt, e: &BigInt, m: &BigInt) -> BigInt { +fn expmod(b: &BigInt, e: &BigInt, m: &BigInt) -> BigInt { let one = BigInt::one(); if e.clone() == BigInt::zero() { return one; @@ -595,224 +526,3 @@ pub fn expmod(b: &BigInt, e: &BigInt, m: &BigInt) -> BigInt { } t } - -#[cfg(test)] -mod tests { - use super::{Ed25519Point, Ed25519Scalar}; - use crate::arithmetic::traits::*; - use crate::elliptic::curves::traits::ECPoint; - use crate::elliptic::curves::traits::ECScalar; - use crate::BigInt; - - type GE = Ed25519Point; - type FE = Ed25519Scalar; - - #[test] - #[allow(clippy::op_ref)] // Enables type inference. - fn test_serdes_pk() { - let mut pk = GE::generator(); - let mut s = serde_json::to_string(&pk).expect("Failed in serialization"); - let mut des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - let eight = ECScalar::from(&BigInt::from(8)); - assert_eq!(des_pk, pk * &eight); - - pk = GE::base_point2(); - s = serde_json::to_string(&pk).expect("Failed in serialization"); - des_pk = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk * &eight); - - // deserialize serialization of bytes_str < 64 hex - s = "{\"bytes_str\":\"2c42d43e1a277e8f3d7d5aacde519c80b913341e425b624d867f790d1578e0\"}" - .to_string(); - des_pk = serde_json::from_str(&s).expect("Failed in deserialization"); - let eight_inverse: FE = eight.invert(); - let des_pk_mul = des_pk * &eight_inverse; - assert_eq!( - des_pk_mul.bytes_compressed_to_big_int().to_hex(), - "2c42d43e1a277e8f3d7d5aacde519c80b913341e425b624d867f790d1578e0" - ); - - // serialize with padding - let ser_pk = serde_json::to_string(&des_pk_mul).expect("Failed in serialization"); - assert_eq!( - &ser_pk, - "{\"bytes_str\":\"002c42d43e1a277e8f3d7d5aacde519c80b913341e425b624d867f790d1578e0\"}" - ); - - // deserialize a padded serialization - let des_pk2: GE = serde_json::from_str(&ser_pk).expect("Failed in deserialization"); - assert_eq!(des_pk_mul, des_pk2 * &eight_inverse); - } - - #[test] - #[allow(clippy::op_ref)] // Enables type inference. - fn bincode_pk() { - let pk = GE::generator(); - let encoded = bincode::serialize(&pk).unwrap(); - let decoded: Ed25519Point = bincode::deserialize(encoded.as_slice()).unwrap(); - let eight = ECScalar::from(&BigInt::from(8)); - assert_eq!(pk * &eight, decoded); - } - - #[test] - #[should_panic] - #[allow(clippy::op_ref)] // Enables type inference. - fn test_serdes_bad_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - // we make sure that the string encodes invalid point: - let s: String = s.replace("5866", "5867"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - let eight = ECScalar::from(&BigInt::from(8)); - assert_eq!(des_pk, pk * &eight); - } - - #[test] - fn test_from_mpz() { - let rand_scalar: FE = ECScalar::new_random(); - let rand_bn = rand_scalar.to_big_int(); - let rand_scalar2: FE = ECScalar::from(&rand_bn); - assert_eq!(rand_scalar, rand_scalar2); - } - - #[test] - fn test_minus_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_minus_b_fe: FE = a.sub(&b.get_element()); - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_minus_b_fe; - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.sub_point(&point_b.get_element()); - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_add_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_plus_b_fe = a + b; - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_plus_b_fe; - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.add_point(&point_b.get_element()); - - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_add_scalar() { - let a: FE = ECScalar::new_random(); - let zero: FE = FE::zero(); - let a_plus_zero: FE = a + zero; - - assert_eq!(a_plus_zero, a); - } - - #[test] - fn test_mul_scalar() { - let a = [ - 10, 10, 10, 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, 10, 10, 10, - ]; - let a_bn = BigInt::from_bytes(&a[..]); - let a_fe: FE = ECScalar::from(&a_bn); - - let five = BigInt::from(5); - let five_fe: FE = ECScalar::from(&five); - let five_a_bn = BigInt::mod_mul(&a_bn, &five, &FE::q()); - let five_a_fe = five_fe * a_fe; - let five_a_fe_2: FE = ECScalar::from(&five_a_bn); - - assert_eq!(five_a_fe, five_a_fe_2); - } - - #[test] - fn test_mul_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let a_mul_b_fe = a * b; - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_mul_b_fe; - let point_a = base * a; - let point_ab2 = point_a.scalar_mul(&b.get_element()); - - assert_eq!(point_ab1, point_ab2); - } - - #[test] - fn test_invert() { - let a: FE = ECScalar::new_random(); - - let a_bn = a.to_big_int(); - - let a_inv = a.invert(); - let a_inv_bn_1 = BigInt::mod_inv(&a_bn, &FE::q()).unwrap(); - let a_inv_bn_2 = a_inv.to_big_int(); - - assert_eq!(a_inv_bn_1, a_inv_bn_2); - } - #[test] - fn test_from_bytes_2() { - let test_vec = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, - 6, - ]; - let result = Ed25519Point::from_bytes(&test_vec); - assert!(result.is_ok()) - } - #[test] - fn test_from_bytes_3() { - let test_vec = [ - 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, 0, 1, 2, 3, 4, 5, 6, - ]; - let result = Ed25519Point::from_bytes(&test_vec); - assert!(result.is_ok()) - } - - #[test] - fn test_scalar_mul_multiply_by_1() { - let g: GE = ECPoint::generator(); - - let fe: FE = ECScalar::from(&BigInt::from(1)); - let b_tag = g * fe; - assert_eq!(b_tag, g); - } - - #[test] - fn test_gep3_to_bytes_from_bytes() { - let g: GE = ECPoint::generator(); - let test_vec: [u8; 32]; - test_vec = [ - 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, 1, - ]; - let tv_bn = BigInt::from_bytes(&test_vec[..]); - let test_fe: FE = ECScalar::from(&tv_bn); - let test_ge = g * test_fe; - let test_ge_bytes = test_ge.get_element().to_bytes(); - let test_ge2: GE = ECPoint::from_bytes(&test_ge_bytes[..]).unwrap(); - let eight: FE = ECScalar::from(&BigInt::from(8)); - - assert_eq!(test_ge2, test_ge * eight); - } - - #[test] - fn test_scalar_to_bn_and_back() { - let s_a: FE = ECScalar::new_random(); - let s_bn = s_a.to_big_int(); - let s_b: FE = ECScalar::from(&s_bn); - assert_eq!(s_a, s_b); - } - #[test] - fn test_xy_coor() { - let g: GE = GE::generator(); - assert_eq!( - g.x_coor().unwrap().to_hex(), - "216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a" - ); - } -} diff --git a/src/elliptic/curves/mod.rs b/src/elliptic/curves/mod.rs index cc22a67f..3103c2eb 100644 --- a/src/elliptic/curves/mod.rs +++ b/src/elliptic/curves/mod.rs @@ -1,6 +1,111 @@ +//! # General purpose elliptic curve cryptography +//! +//! Here we define generic elliptic curve cryptography and provide implementation of several curves. +//! +//! ## Usage +//! +//! Elliptic curve cryptography involves points and scalars. We provide respective structures +//! [Point\](Point), [Scalar\](Scalar), where generic `E` stands for choice of elliptic +//! curve, e.g. [Secp256k1] (`Point`, `Scalar`). +//! +//! Various methods and traits are defined for points and scalars which basically empowers you to do +//! anything you can do in elliptic curve cryptography. +//! +//! ## Examples +//! +//! ### Public point/private scalar generation +//! ```rust +//! use curv::elliptic::curves::{Point, Scalar, Secp256k1}; +//! +//! // Samples a random nonzero scalar (mod group order) +//! let secret = Scalar::::random(); +//! // Multiplies generator at secret, retrieving a public point +//! let public = Point::generator() * secret; +//! ``` +//! +//! ### Diffie-Hellman +//! +//! Function below is a final step of the Diffie-Hellman key exchange protocol when both parties +//! have exchanged their ephemeral public keys. Giving it here just for an example, the `curv` library +//! is equipped with an implementation of this protocol (see [dh_key_exchange], or its more involved +//! version: [dh_key_exchange_variant_with_pok_comm]). +//! +//! [dh_key_exchange]: crate::cryptographic_primitives::twoparty::dh_key_exchange +//! [dh_key_exchange_variant_with_pok_comm]: crate::cryptographic_primitives::twoparty::dh_key_exchange_variant_with_pok_comm +//! +//! ```rust +//! use curv::elliptic::curves::{Point, Scalar, Ed25519}; +//! +//! fn diffie_hellman( +//! my_secret: &Scalar, +//! counterparty_point: &Point +//! ) -> Point { +//! my_secret * counterparty_point +//! } +//! ``` +//! +//! You may have noticed that this function lacks of subgroup check (whether counterparty +//! point has order=group_order), and is vulnerable to [small subgroup attack][subgroup-attack]. +//! Actually, **it isn't**! Any `Point` instance is guaranteed to have large prime order (unless +//! it's zero point), so you can be sure that subgroup check was performed. See [guarantees section] +//! to learn more. +//! +//! [subgroup-attack]: http://safecurves.cr.yp.to/twist.html +//! [Guarantees section]: Point#guarantees +//! +//! Function above can be slightly modified to be generic over choice of curve: +//! +//! ```rust +//! use curv::elliptic::curves::{Curve, Point, Scalar}; +//! +//! fn diffie_hellman( +//! my_secret: &Scalar, +//! counterparty_point: &Point +//! ) -> Point { +//! my_secret * counterparty_point +//! } +//! ``` +//! +//! `Point` (for generic `E: Curve`) implements many traits you might need (e.g. Serialize, PartialEq, +//! Debug, etc.) without specifying additional bounds. The same applies to `Scalar`. +//! +//! ## Implementing your own curve +//! +//! Downstream crates can define their own curves just by implementing [Curve], [ECPoint], [ECScalar] +//! traits, no additional work is required. Note that these traits are intended not to be used directly. +//! Point, Scalar structures wrap ECPoint / ECScalar implementation, and provide a lot of convenient +//! methods, implement arithmetic traits, etc. + pub mod bls12_381; pub mod curve_ristretto; pub mod ed25519; pub mod p256; pub mod secp256_k1; -pub mod traits; + +#[cfg(test)] +mod test; +mod traits; +mod wrappers; + +#[doc(inline)] +pub use self::{ + bls12_381::{Bls12_381_1, Bls12_381_2}, + curve_ristretto::Ristretto, + ed25519::Ed25519, + p256::Secp256r1, + secp256_k1::Secp256k1, +}; +pub use self::{ + traits::{Curve, ECPoint, ECScalar, PointCoords}, + wrappers::{EncodedPoint, EncodedScalar, Generator, Point, Scalar}, +}; + +pub mod error { + pub use super::{ + traits::{DeserializationError, NotOnCurve}, + wrappers::error::*, + }; +} + +#[doc(no_inline)] +pub use self::error::*; diff --git a/src/elliptic/curves/p256.rs b/src/elliptic/curves/p256.rs index 048ab7fc..2d4e6dfd 100644 --- a/src/elliptic/curves/p256.rs +++ b/src/elliptic/curves/p256.rs @@ -1,41 +1,41 @@ // NIST P-256 elliptic curve utility functions. -use super::traits::{ECPoint, ECScalar}; -use crate::arithmetic::traits::*; -use crate::BigInt; -use crate::ErrorKey; -use generic_array::typenum::U32; -use generic_array::GenericArray; -use p256::ecdsa::VerifyKey; +use p256::elliptic_curve::group::prime::PrimeCurveAffine; use p256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; -use p256::{AffinePoint, EncodedPoint, ProjectivePoint, Scalar}; +use p256::{AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint, Scalar}; + use rand::{thread_rng, Rng}; -use serde::de; -use serde::de::Visitor; -use serde::ser::{Serialize, Serializer}; -use serde::{Deserialize, Deserializer}; -use std::ops::{Add, Mul, Sub}; -use std::sync::atomic; -use std::{fmt, ptr}; +use serde::{Deserialize, Serialize}; use zeroize::Zeroize; -pub type SK = Scalar; -pub type PK = VerifyKey; - -#[derive(Clone, Copy, Debug)] -pub struct Secp256r1Scalar { - purpose: &'static str, - fe: SK, +use super::traits::{ECPoint, ECScalar}; +use crate::arithmetic::traits::*; +use crate::elliptic::curves::{Curve, DeserializationError, NotOnCurve, PointCoords}; +use crate::BigInt; +use p256::elliptic_curve::group::ff::PrimeField; + +lazy_static::lazy_static! { + static ref GROUP_ORDER: BigInt = BigInt::from_bytes(&GROUP_ORDER_BYTES); + + static ref BASE_POINT2_ENCODED: EncodedPoint = { + let mut g = [0u8; 65]; + g[0] = 0x04; + g[1..33].copy_from_slice(&BASE_POINT2_X); + g[33..].copy_from_slice(&BASE_POINT2_Y); + EncodedPoint::from_bytes(&g).unwrap() + }; + + static ref BASE_POINT2: Secp256r1Point = Secp256r1Point { + purpose: "base_point2", + ge: PK::from_encoded_point(&BASE_POINT2_ENCODED).unwrap(), + }; + + static ref GENERATOR: Secp256r1Point = Secp256r1Point { + purpose: "generator", + ge: AffinePoint::generator() + }; } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Secp256r1Point { - purpose: &'static str, - ge: PK, -} -pub type GE = Secp256r1Point; -pub type FE = Secp256r1Scalar; - /* X coordinate of a point of unknown discrete logarithm. Computed using a deterministic algorithm with the generator as input. See test_base_point2 */ @@ -47,727 +47,320 @@ const BASE_POINT2_Y: [u8; 32] = [ 0x30, 0xe2, 0xfe, 0xb3, 0x8d, 0x82, 0x4e, 0x0e, 0xa2, 0x95, 0x2f, 0x2a, 0x48, 0x5b, 0xbc, 0xdd, 0x4c, 0x72, 0x8a, 0x74, 0xf4, 0xfa, 0xc7, 0xdc, 0x0d, 0xc9, 0x90, 0x8d, 0x9a, 0x8d, 0xc1, 0xa4, ]; +const GROUP_ORDER_BYTES: [u8; 32] = [ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51, +]; -impl Zeroize for Secp256r1Scalar { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, Secp256r1Scalar::zero()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } +/// P-256 curve implementation based on [p256] library +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Secp256r1 {} + +pub type SK = Scalar; +pub type PK = AffinePoint; + +#[derive(Clone, Debug)] +pub struct Secp256r1Scalar { + purpose: &'static str, + fe: zeroize::Zeroizing, } -impl ECScalar for Secp256r1Scalar { - type SecretKey = SK; +#[derive(Clone, Copy, Debug)] +pub struct Secp256r1Point { + purpose: &'static str, + ge: PK, +} - fn new_random() -> Secp256r1Scalar { - let mut arr = [0u8; 32]; - thread_rng().fill(&mut arr[..]); - let gen_arr: GenericArray = *GenericArray::from_slice(&arr); +pub type GE = Secp256r1Point; +pub type FE = Secp256r1Scalar; + +impl Curve for Secp256r1 { + type Point = GE; + type Scalar = FE; + + const CURVE_NAME: &'static str = "secp256r1"; +} + +impl ECScalar for Secp256r1Scalar { + type Underlying = SK; + + type ScalarBytes = FieldBytes; + + fn random() -> Secp256r1Scalar { + let mut rng = thread_rng(); + let scalar = loop { + let mut bytes = FieldBytes::default(); + rng.fill(&mut bytes[..]); + if let Some(scalar) = Scalar::from_repr(bytes) { + break scalar; + } + }; Secp256r1Scalar { purpose: "random", - fe: Scalar::from_bytes_reduced(&gen_arr), + fe: scalar.into(), } } fn zero() -> Secp256r1Scalar { - let zero_arr = [0u8; 32]; - let zero = unsafe { std::mem::transmute::<[u8; 32], Scalar>(zero_arr) }; Secp256r1Scalar { purpose: "zero", - fe: zero, + fe: Scalar::zero().into(), } } - fn get_element(&self) -> SK { - self.fe + fn is_zero(&self) -> bool { + bool::from(self.fe.is_zero()) } - fn set_element(&mut self, element: SK) { - self.fe = element - } - - fn from(n: &BigInt) -> Secp256r1Scalar { - let curve_order = Secp256r1Scalar::q(); - let n_reduced = BigInt::mod_add(n, &BigInt::from(0), &curve_order); - let mut v = BigInt::to_bytes(&n_reduced); - const SECRET_KEY_SIZE: usize = 32; - - if v.len() < SECRET_KEY_SIZE { - let mut template = vec![0; SECRET_KEY_SIZE - v.len()]; - template.extend_from_slice(&v); - v = template; - } - let arr: GenericArray = *GenericArray::from_slice(&v); + fn from_bigint(n: &BigInt) -> Secp256r1Scalar { + let curve_order = Secp256r1Scalar::group_order(); + let n_reduced = n + .modulus(&curve_order) + .to_bytes_array::<32>() + .expect("n mod curve_order must be equal or less than 32 bytes"); Secp256r1Scalar { - purpose: "from_big_int", - fe: Scalar::from_bytes_reduced(&arr), + purpose: "from_bigint", + fe: Scalar::from_bytes_reduced(&n_reduced.into()).into(), } } - fn to_big_int(&self) -> BigInt { + fn to_bigint(&self) -> BigInt { BigInt::from_bytes(self.fe.to_bytes().as_slice()) } - fn q() -> BigInt { - const CURVE_ORDER: [u8; 32] = [ - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, - 0xfc, 0x63, 0x25, 0x51, - ]; - BigInt::from_bytes(CURVE_ORDER.as_ref()) + fn serialize(&self) -> Self::ScalarBytes { + self.fe.to_bytes() + } + + fn deserialize(bytes: &[u8]) -> Result { + if bytes.len() != 32 { + return Err(DeserializationError); + } + let bytes = *FieldBytes::from_slice(bytes); + Ok(Secp256r1Scalar { + purpose: "deserialize", + fe: Scalar::from_repr(bytes).ok_or(DeserializationError)?.into(), + }) } - fn add(&self, other: &SK) -> Secp256r1Scalar { + fn add(&self, other: &Self) -> Secp256r1Scalar { Secp256r1Scalar { purpose: "add", - fe: self.get_element() + other, + fe: (*self.fe + *other.fe).into(), } } - fn mul(&self, other: &SK) -> Secp256r1Scalar { + fn mul(&self, other: &Self) -> Secp256r1Scalar { Secp256r1Scalar { purpose: "mul", - fe: self.get_element() * other, + fe: (*self.fe * *other.fe).into(), } } - fn sub(&self, other: &SK) -> Secp256r1Scalar { + fn sub(&self, other: &Self) -> Secp256r1Scalar { Secp256r1Scalar { purpose: "sub", - fe: self.get_element() - other, + fe: (*self.fe - *other.fe).into(), } } - fn invert(&self) -> Secp256r1Scalar { + fn neg(&self) -> Self { Secp256r1Scalar { - purpose: "invert", - fe: self.fe.invert().unwrap(), + purpose: "sub", + fe: (-&*self.fe).into(), } } -} - -impl Mul for Secp256r1Scalar { - type Output = Secp256r1Scalar; - fn mul(self, other: Secp256r1Scalar) -> Secp256r1Scalar { - (&self).mul(&other.get_element()) - } -} -impl<'o> Mul<&'o Secp256r1Scalar> for Secp256r1Scalar { - type Output = Secp256r1Scalar; - fn mul(self, other: &'o Secp256r1Scalar) -> Secp256r1Scalar { - (&self).mul(&other.get_element()) + fn invert(&self) -> Option { + Some(Secp256r1Scalar { + purpose: "invert", + fe: Option::::from(self.fe.invert())?.into(), + }) } -} -impl Add for Secp256r1Scalar { - type Output = Secp256r1Scalar; - fn add(self, other: Secp256r1Scalar) -> Secp256r1Scalar { - (&self).add(&other.get_element()) + fn add_assign(&mut self, other: &Self) { + *self.fe += &*other.fe } -} - -impl<'o> Add<&'o Secp256r1Scalar> for Secp256r1Scalar { - type Output = Secp256r1Scalar; - fn add(self, other: &'o Secp256r1Scalar) -> Secp256r1Scalar { - (&self).add(&other.get_element()) + fn mul_assign(&mut self, other: &Self) { + *self.fe *= &*other.fe } -} - -impl Sub for Secp256r1Scalar { - type Output = Secp256r1Scalar; - fn sub(self, other: Secp256r1Scalar) -> Secp256r1Scalar { - (&self).sub(&other.get_element()) + fn sub_assign(&mut self, other: &Self) { + *self.fe -= &*other.fe } -} -impl<'o> Sub<&'o Secp256r1Scalar> for Secp256r1Scalar { - type Output = Secp256r1Scalar; - fn sub(self, other: &'o Secp256r1Scalar) -> Secp256r1Scalar { - (&self).sub(&other.get_element()) + fn group_order() -> &'static BigInt { + &GROUP_ORDER } -} -impl Serialize for Secp256r1Scalar { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{:0>64}", self.to_big_int().to_hex())) + fn underlying_ref(&self) -> &SK { + &self.fe } -} -impl<'de> Deserialize<'de> for Secp256r1Scalar { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(Secp256r1ScalarVisitor) + fn underlying_mut(&mut self) -> &mut SK { + &mut self.fe } -} - -struct Secp256r1ScalarVisitor; - -impl<'de> Visitor<'de> for Secp256r1ScalarVisitor { - type Value = Secp256r1Scalar; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("Secp256r1Scalar") - } - - fn visit_str(self, s: &str) -> Result { - let v = BigInt::from_hex(s).map_err(E::custom)?; - Ok(ECScalar::from(&v)) + fn from_underlying(fe: SK) -> Self { + Secp256r1Scalar { + purpose: "from_underlying", + fe: fe.into(), + } } } impl PartialEq for Secp256r1Scalar { fn eq(&self, other: &Secp256r1Scalar) -> bool { - self.get_element().to_bytes() == other.get_element().to_bytes() - } -} - -impl Zeroize for Secp256r1Point { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, GE::generator()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); + self.fe == other.fe } } impl ECPoint for Secp256r1Point { - type SecretKey = SK; - type PublicKey = PK; type Scalar = Secp256r1Scalar; + type Underlying = PK; - fn base_point2() -> Secp256r1Point { - let mut v = vec![4_u8]; - v.extend(BASE_POINT2_X.as_ref()); - v.extend(BASE_POINT2_Y.as_ref()); - Secp256r1Point::from_bytes(&v).unwrap() - } + type CompressedPoint = EncodedPoint; + type UncompressedPoint = EncodedPoint; - fn generator() -> Secp256r1Point { + fn zero() -> Secp256r1Point { Secp256r1Point { - purpose: "base_fe", - ge: VerifyKey::from_encoded_point(&AffinePoint::generator().to_encoded_point(true)) - .unwrap(), + purpose: "zero", + ge: AffinePoint::identity(), } } - fn get_element(&self) -> PK { - self.ge + fn is_zero(&self) -> bool { + bool::from(self.ge.is_identity()) } - fn bytes_compressed_to_big_int(&self) -> BigInt { - BigInt::from_bytes(self.get_element().to_encoded_point(true).as_bytes()) + fn generator() -> &'static Secp256r1Point { + &GENERATOR } - fn x_coor(&self) -> Option { - Some(BigInt::from_bytes( - EncodedPoint::from(&self.ge).x().as_slice(), - )) + fn base_point2() -> &'static Secp256r1Point { + &BASE_POINT2 } - fn y_coor(&self) -> Option { - // need this back and forth conversion to get an uncompressed point - let tmp = AffinePoint::from_encoded_point(&EncodedPoint::from(&self.ge)).unwrap(); - Some(BigInt::from_bytes( - tmp.to_encoded_point(false).y().unwrap().as_slice(), + fn from_coords(x: &BigInt, y: &BigInt) -> Result { + let x_arr = x.to_bytes_array::<32>().ok_or(NotOnCurve)?; + let y_arr = y.to_bytes_array::<32>().ok_or(NotOnCurve)?; + let ge = PK::from_encoded_point(&EncodedPoint::from_affine_coordinates( + &x_arr.into(), + &y_arr.into(), + false, )) - } + .ok_or(NotOnCurve)?; - fn from_bytes(bytes: &[u8]) -> Result { - let result = PK::new(&bytes); - let test = result.map(|pk| Secp256r1Point { - purpose: "random", - ge: pk, - }); - test.map_err(|_err| ErrorKey::InvalidPublicKey) + Ok(Secp256r1Point { + purpose: "from_coords", + ge, + }) } - fn pk_to_key_slice(&self) -> Vec { - let tmp = AffinePoint::from_encoded_point(&EncodedPoint::from(&self.ge)).unwrap(); - tmp.to_encoded_point(false).as_ref().to_vec() + fn x_coord(&self) -> Option { + let encoded = self.ge.to_encoded_point(false); + let x = BigInt::from_bytes(encoded.x()?.as_slice()); + Some(x) } - fn scalar_mul(&self, fe: &SK) -> Secp256r1Point { - let point = ProjectivePoint::from( - AffinePoint::from_encoded_point(&EncodedPoint::from(&self.ge)).unwrap(), - ); - let scalar = Scalar::from_bytes_reduced(&fe.to_bytes()); - Secp256r1Point { - purpose: "mul", - ge: VerifyKey::from_encoded_point(&(point * scalar).to_affine().to_encoded_point(true)) - .unwrap(), - } + fn y_coord(&self) -> Option { + let encoded = self.ge.to_encoded_point(false); + let y = BigInt::from_bytes(encoded.y()?.as_slice()); + Some(y) } - fn add_point(&self, other: &PK) -> Secp256r1Point { - let point1 = ProjectivePoint::from( - AffinePoint::from_encoded_point(&EncodedPoint::from(&self.ge)).unwrap(), - ); - let point2 = ProjectivePoint::from( - AffinePoint::from_encoded_point(&EncodedPoint::from(other)).unwrap(), - ); - Secp256r1Point { - purpose: "mul", - ge: VerifyKey::from_encoded_point( - &(point1 + point2).to_affine().to_encoded_point(true), - ) - .unwrap(), - } + fn coords(&self) -> Option { + let encoded = self.ge.to_encoded_point(false); + let x = BigInt::from_bytes(encoded.x()?.as_slice()); + let y = BigInt::from_bytes(encoded.y()?.as_slice()); + Some(PointCoords { x, y }) } - fn sub_point(&self, other: &PK) -> Secp256r1Point { - let point1 = ProjectivePoint::from( - AffinePoint::from_encoded_point(&EncodedPoint::from(&self.ge)).unwrap(), - ); - let point2 = ProjectivePoint::from( - AffinePoint::from_encoded_point(&EncodedPoint::from(other)).unwrap(), - ); - Secp256r1Point { - purpose: "sub", - ge: VerifyKey::from_encoded_point( - &(point1 - point2).to_affine().to_encoded_point(true), - ) - .unwrap(), - } + fn serialize_compressed(&self) -> Self::CompressedPoint { + self.ge.to_encoded_point(true) } - fn from_coor(x: &BigInt, y: &BigInt) -> Secp256r1Point { - let mut vec_x = BigInt::to_bytes(x); - let mut vec_y = BigInt::to_bytes(y); - const COORDINATE_SIZE: usize = 32; - assert!(vec_x.len() <= COORDINATE_SIZE, "x coordinate is too big."); - assert!(vec_x.len() <= COORDINATE_SIZE, "y coordinate is too big."); - if vec_x.len() < COORDINATE_SIZE { - // pad - let mut x_buffer = vec![0; COORDINATE_SIZE - vec_x.len()]; - x_buffer.extend_from_slice(&vec_x); - vec_x = x_buffer - } - if vec_y.len() < COORDINATE_SIZE { - // pad - let mut y_buffer = vec![0; COORDINATE_SIZE - vec_y.len()]; - y_buffer.extend_from_slice(&vec_y); - vec_y = y_buffer - } - - let x_arr: GenericArray = *GenericArray::from_slice(&vec_x); - let y_arr: GenericArray = *GenericArray::from_slice(&vec_y); - Secp256r1Point { - purpose: "base_fe", - ge: VerifyKey::from_encoded_point(&EncodedPoint::from_affine_coordinates( - &x_arr, &y_arr, false, - )) - .unwrap(), - } - } -} - -impl Secp256r1Point { - // derive point from BigInt - fn from_bigint(i: &BigInt) -> Result { - let vec = BigInt::to_bytes(i); - let point = match Secp256r1Point::from_bytes(&vec) { - Ok(v) => v, - Err(_) => return Err(()), - }; - Ok(point) - } -} - -impl Mul for Secp256r1Point { - type Output = Secp256r1Point; - fn mul(self, other: Secp256r1Scalar) -> Self::Output { - self.scalar_mul(&other.get_element()) + fn serialize_uncompressed(&self) -> Self::UncompressedPoint { + self.ge.to_encoded_point(false) } -} -impl<'o> Mul<&'o Secp256r1Scalar> for Secp256r1Point { - type Output = Secp256r1Point; - fn mul(self, other: &'o Secp256r1Scalar) -> Self::Output { - self.scalar_mul(&other.get_element()) + fn deserialize(bytes: &[u8]) -> Result { + let encoded = EncodedPoint::from_bytes(bytes).map_err(|_| DeserializationError)?; + Ok(Secp256r1Point { + purpose: "deserialize", + ge: AffinePoint::from_encoded_point(&encoded).ok_or(DeserializationError)?, + }) } -} -impl<'o> Mul<&'o Secp256r1Scalar> for &'o Secp256r1Point { - type Output = Secp256r1Point; - fn mul(self, other: &'o Secp256r1Scalar) -> Self::Output { - self.scalar_mul(&other.get_element()) + fn check_point_order_equals_group_order(&self) -> bool { + // This curve has cofactor=1 => any nonzero point has order GROUP_ORDER + !self.is_zero() } -} -impl Add for Secp256r1Point { - type Output = Secp256r1Point; - fn add(self, other: Secp256r1Point) -> Self::Output { - self.add_point(&other.get_element()) + fn scalar_mul(&self, fe: &Self::Scalar) -> Secp256r1Point { + Secp256r1Point { + purpose: "scalar_mul", + ge: (self.ge * *fe.fe).to_affine(), + } } -} -impl<'o> Add<&'o Secp256r1Point> for Secp256r1Point { - type Output = Secp256r1Point; - fn add(self, other: &'o Secp256r1Point) -> Self::Output { - self.add_point(&other.get_element()) + fn add_point(&self, other: &Self) -> Self { + Secp256r1Point { + purpose: "add_point", + ge: (ProjectivePoint::from(self.ge) + other.ge).to_affine(), + } } -} -impl<'o> Add<&'o Secp256r1Point> for &'o Secp256r1Point { - type Output = Secp256r1Point; - fn add(self, other: &'o Secp256r1Point) -> Self::Output { - self.add_point(&other.get_element()) + fn sub_point(&self, other: &Self) -> Self { + Secp256r1Point { + purpose: "sub_point", + ge: (ProjectivePoint::from(self.ge) - other.ge).to_affine(), + } } -} -impl Sub for Secp256r1Point { - type Output = Secp256r1Point; - fn sub(self, other: Secp256r1Point) -> Self::Output { - self.sub_point(&other.get_element()) + fn neg_point(&self) -> Self { + Secp256r1Point { + purpose: "neg_point", + ge: -self.ge, + } } -} -impl<'o> Sub<&'o Secp256r1Point> for Secp256r1Point { - type Output = Secp256r1Point; - fn sub(self, other: &'o Secp256r1Point) -> Self::Output { - self.sub_point(&other.get_element()) + /// Reference to underlying curve implementation + fn underlying_ref(&self) -> &Self::Underlying { + &self.ge } -} - -impl<'o> Sub<&'o Secp256r1Point> for &'o Secp256r1Point { - type Output = Secp256r1Point; - fn sub(self, other: &'o Secp256r1Point) -> Self::Output { - self.sub_point(&other.get_element()) + /// Mutual reference to underlying curve implementation + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.ge } -} - -impl Serialize for Secp256r1Point { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!( - "{:0>66}", - self.bytes_compressed_to_big_int().to_hex() - )) + /// Construct a point from its underlying representation + fn from_underlying(ge: Self::Underlying) -> Self { + Secp256r1Point { + purpose: "from_underlying", + ge, + } } } -impl<'de> Deserialize<'de> for Secp256r1Point { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(Secp256r1PointVisitor) +impl Zeroize for Secp256r1Point { + fn zeroize(&mut self) { + self.ge.zeroize() } } -struct Secp256r1PointVisitor; - -impl<'de> Visitor<'de> for Secp256r1PointVisitor { - type Value = Secp256r1Point; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("Secp256r1Point") - } - - fn visit_str(self, s: &str) -> Result - where - E: de::Error, - { - let bn = BigInt::from_hex(s).map_err(E::custom)?; - match Secp256r1Point::from_bigint(&bn) { - Ok(v) => Ok(v), - Err(_) => Err(E::custom(format!( - "Error deriving Secp256r1Point from string: {}", - s - ))), - } +impl PartialEq for Secp256r1Point { + fn eq(&self, other: &Self) -> bool { + self.ge == other.ge } } #[cfg(test)] mod tests { - use super::{BigInt, ErrorKey}; - use super::{Secp256r1Point, Secp256r1Scalar}; - use crate::arithmetic::traits::*; - use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; - use crate::cryptographic_primitives::hashing::traits::Hash; - use crate::elliptic::curves::traits::{ECPoint, ECScalar}; - - fn random_point() -> Secp256r1Point { - let random_scalar: Secp256r1Scalar = Secp256r1Scalar::new_random(); - let base_point = Secp256r1Point::generator(); - let pk = base_point.scalar_mul(&random_scalar.get_element()); - Secp256r1Point { - purpose: "random_point", - ge: pk.get_element(), - } - } - - #[test] - fn serialize_sk() { - let scalar: Secp256r1Scalar = ECScalar::from(&BigInt::from(123456)); - let s = serde_json::to_string(&scalar).expect("Failed in serialization"); - assert_eq!( - s, - "\"000000000000000000000000000000000000000000000000000000000001e240\"" - ); - } - - #[test] - fn serialize_rand_pk_verify_pad() { - let vx = BigInt::from_hex( - &"9e6b4c9775d5af0aff94a55035a2b039f7cfc19b9e67004f190ddfaada82b405".to_string(), - ) - .unwrap(); - - let vy = BigInt::from_hex( - &"d3fa4d180ea04d8da373bb61782bc6b509f7b6e374d6a47b253e4853ad1cd5fc".to_string(), - ) - .unwrap(); - Secp256r1Point::from_coor(&vx, &vy); // x and y of size 32 - - let x = BigInt::from_hex( - &"2d054d254d1d112b1e7a134780ae7975a2a57b35089b2afa45dc42ed9afe1b".to_string(), - ) - .unwrap(); - - let y = BigInt::from_hex( - &"16f436c897a9733a4d83eed96147b273348c98fb680d7361d915ec6b5ce761ca".to_string(), - ) - .unwrap(); - Secp256r1Point::from_coor(&x, &y); // x and y not of size 32 each - - let r = random_point(); - let r_expected = Secp256r1Point::from_coor(&r.x_coor().unwrap(), &r.y_coor().unwrap()); - assert_eq!(r.x_coor().unwrap(), r_expected.x_coor().unwrap()); - assert_eq!(r.y_coor().unwrap(), r_expected.y_coor().unwrap()); - } - - #[test] - fn deserialize_sk() { - let s = "\"1e240\""; - let dummy: Secp256r1Scalar = serde_json::from_str(s).expect("Failed in serialization"); - - let sk: Secp256r1Scalar = ECScalar::from(&BigInt::from(123456)); - - assert_eq!(dummy.to_big_int(), sk.to_big_int()); - } - - #[test] - fn serialize_pk() { - let pk = Secp256r1Point::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let expected = pk.bytes_compressed_to_big_int().to_hex(); - assert_eq!( - s, - serde_json::to_string(&("0".to_string() + &expected)).unwrap() - ); - let des_pk: Secp256r1Point = serde_json::from_str(&s).expect("Failed in serialization"); - assert_eq!(des_pk.ge, pk.ge); - } - - #[test] - fn bincode_pk() { - let pk = Secp256r1Point::generator(); - let bin = bincode::serialize(&pk).unwrap(); - let decoded: Secp256r1Point = bincode::deserialize(bin.as_slice()).unwrap(); - assert_eq!(decoded.get_element(), pk.get_element()); - } + use sha2::{Digest, Sha256}; - #[test] - fn test_serdes_pk() { - let pk = Secp256r1Point::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: Secp256r1Point = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk.get_element(), pk.get_element()); - - let pk = Secp256r1Point::base_point2(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: Secp256r1Point = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk.get_element(), pk.get_element()); - } - - #[test] - #[should_panic] - fn test_serdes_bad_pk() { - let pk = Secp256r1Point::generator(); - let mut s = serde_json::to_string(&pk).expect("Failed in serialization"); - // we make sure that the string encodes invalid point: - s = s.replace("2770", "2780"); - let des_pk: Secp256r1Point = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - } - - #[test] - fn test_from_bytes() { - let vec = BigInt::to_bytes(&BigInt::from(1337)); - let result = Secp256r1Point::from_bytes(&vec); - assert_eq!(result.unwrap_err(), ErrorKey::InvalidPublicKey) - } + use crate::arithmetic::*; - #[test] - fn test_from_bytes_3() { - let test_vec = [ - 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, 0, 1, 2, 3, 4, 5, 6, - ]; - let result = Secp256r1Point::from_bytes(&test_vec); - assert!(result.is_ok() | result.is_err()) - } - - #[test] - fn test_from_bytes_4() { - let test_vec = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, - ]; - let result = Secp256r1Point::from_bytes(&test_vec); - assert!(result.is_ok() | result.is_err()) - } - - #[test] - fn test_from_bytes_5() { - let test_vec = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, - 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, - 4, 5, 6, - ]; - let result = Secp256r1Point::from_bytes(&test_vec); - assert!(result.is_ok() | result.is_err()) - } - - #[test] - fn test_add_sub() { - let q = Secp256r1Scalar::q(); - let start: Secp256r1Scalar = ECScalar::new_random(); - let b: Secp256r1Scalar = ECScalar::new_random(); - let tmp = BigInt::mod_add(&start.to_big_int(), &b.to_big_int(), &q); - let end = BigInt::mod_sub(&tmp, &b.to_big_int(), &q); - assert_eq!(start.to_big_int(), end); - } - - #[test] - fn test_minus_point() { - let a: Secp256r1Scalar = ECScalar::new_random(); - let b: Secp256r1Scalar = ECScalar::new_random(); - let b_bn = b.to_big_int(); - let q = Secp256r1Scalar::q(); - let minus_b = BigInt::mod_sub(&q, &b_bn, &q); - let a_minus_b = BigInt::mod_add(&a.to_big_int(), &minus_b, &q); - let a_minus_b_fe: Secp256r1Scalar = ECScalar::from(&a_minus_b); - let base: Secp256r1Point = ECPoint::generator(); - let point_ab1 = base * a_minus_b_fe; - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.sub_point(&point_b.get_element()); - assert_eq!(point_ab1.get_element(), point_ab2.get_element()); - } - - #[test] - fn test_simple_inversion2() { - let a: Secp256r1Scalar = ECScalar::from(&BigInt::from(2)); - let a_inv = a.invert(); - let a_inv_int = a_inv.to_big_int(); - assert_eq!( - a_inv_int, - BigInt::from_hex("7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a9") - .unwrap(), - ); - } - - #[test] - fn test_simple_inversion3() { - let a: Secp256r1Scalar = ECScalar::from(&BigInt::from(1234567890)); - let a_inv = a.invert().to_big_int(); - assert_eq!( - a_inv, - BigInt::from_hex("93a24a3b7e3b3a49a5acf862e8360bdd456e4c095dec9b97772bb758f725715a") - .unwrap(), - ); - } - - #[test] - fn test_invert() { - let a_bn = BigInt::sample(256); - let a: Secp256r1Scalar = ECScalar::from(&a_bn); - let a_inv = a.invert(); - let a_inv_bn_1 = BigInt::mod_inv(&a_bn, &Secp256r1Scalar::q()).unwrap(); - let a_inv_bn_2 = a_inv.to_big_int(); - assert_eq!(a_inv_bn_1, a_inv_bn_2); - } - - #[test] - fn test_scalar_mul_scalar() { - let a: Secp256r1Scalar = ECScalar::new_random(); - let b: Secp256r1Scalar = ECScalar::new_random(); - let c1 = a.mul(&b.get_element()); - let c2 = a * b; - assert_eq!(c1.get_element().to_bytes(), c2.get_element().to_bytes()); - } - - #[test] - fn test_scalar_mul1() { - let base_point = Secp256r1Point::generator(); - let int: Secp256r1Scalar = ECScalar::from(&BigInt::from(1)); - let test = base_point * int; - assert_eq!( - test.x_coor().unwrap().to_hex(), - "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296".to_lowercase() - ); - assert_eq!( - test.y_coor().unwrap().to_hex(), - "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5".to_lowercase() - ); - } - - #[test] - fn test_scalar_mul2() { - let base_point = Secp256r1Point::generator(); - let int: Secp256r1Scalar = ECScalar::from(&BigInt::from(2)); - let test = base_point * int; - assert_eq!( - test.x_coor().unwrap().to_hex(), - "7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978".to_lowercase() - ); - assert_eq!( - format!("{:0>64}", test.y_coor().unwrap().to_hex()), - "07775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1".to_lowercase() - ); - } - - #[test] - fn test_scalar_mul3() { - let base_point = Secp256r1Point::generator(); - let int: Secp256r1Scalar = ECScalar::from( - &BigInt::from_hex("7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978") - .unwrap(), - ); - let test = base_point * int; - assert_eq!( - test.x_coor().unwrap().to_hex(), - "4F6DD42033C0666A04DFC107F4CB4D5D22E33AE178006803D967CB25D95B7DB4".to_lowercase() - ); - assert_eq!( - format!("{:0>64}", test.y_coor().unwrap().to_hex()), - "085DB1B0952D8E081A3E13398A89911A038AAB054AE3E26718A5E582ED9FDD38".to_lowercase() - ); - } - - #[test] - fn test_pk_to_key_slice() { - for _ in 1..200 { - let r = Secp256r1Scalar::new_random(); - let rg = Secp256r1Point::generator() * r; - let key_slice = rg.pk_to_key_slice(); - assert!(key_slice.len() == 65); - assert!(key_slice[0] == 4); - let rg_prime: Secp256r1Point = ECPoint::from_bytes(&key_slice).unwrap(); - assert_eq!(rg_prime.get_element(), rg.get_element()); - } - } + use super::{ECPoint, GE}; #[test] fn test_base_point2() { @@ -775,110 +368,22 @@ mod tests { It is done by using SHA256 repeatedly as a pseudo-random function, with the generator as the initial input, until receiving a valid Secp256r1 point. */ - let base_point2 = Secp256r1Point::base_point2(); + let base_point2 = GE::base_point2(); - let g = Secp256r1Point::generator(); - let mut hash = HSha256::create_hash(&[&g.bytes_compressed_to_big_int()]); - hash = HSha256::create_hash(&[&hash]); + let g = GE::generator(); + let hash = Sha256::digest(g.serialize_compressed().as_ref()); + let hash = Sha256::digest(&hash); - assert_eq!(hash, base_point2.x_coor().unwrap(),); + assert_eq!(BigInt::from_bytes(&hash), base_point2.x_coord().unwrap()); - // check that base_point2 is indeed on the curve (from_coor() will fail otherwise) + // check that base_point2 is indeed on the curve (from_coords() will fail otherwise) assert_eq!( - Secp256r1Point::from_coor( - &base_point2.x_coor().unwrap(), - &base_point2.y_coor().unwrap() + &GE::from_coords( + &base_point2.x_coord().unwrap(), + &base_point2.y_coord().unwrap() ) - .get_element(), - base_point2.get_element() - ); - } - - #[test] - fn scalar_bigint_conversion1() { - let int = BigInt::sample(256); - let scalar: Secp256r1Scalar = ECScalar::from(&int); - assert_eq!(scalar.to_big_int(), int); - } - - #[test] - fn point_bigint_conversion1() { - let g = Secp256r1Point::generator(); - let h = g.bytes_compressed_to_big_int(); - let i = Secp256r1Point::from_bigint(&h).unwrap(); - assert_eq!(i.get_element(), g.get_element()); - } - - #[test] - fn point_bigint_conversion2() { - let g = Secp256r1Point::generator(); - let r: Secp256r1Scalar = ECScalar::from(&BigInt::sample(256)); - let point = g * r; - let point_int = point.bytes_compressed_to_big_int(); - let point_test = Secp256r1Point::from_bigint(&point_int).unwrap(); - assert_eq!(point.get_element(), point_test.get_element()); - } - - #[test] - fn scalar_bigint_conversion2() { - let i = Secp256r1Scalar::new_random(); - let int = i.to_big_int(); - let j: Secp256r1Scalar = ECScalar::from(&int); - assert_eq!(i.to_big_int(), j.to_big_int()); - } - - #[test] - fn pk_to_hex() { - let secret = - BigInt::from_hex("79196b247effbe3192763a5c37b18f5d89e7d0a8c83d246917add0a842d5af8b") - .unwrap(); - let sk: Secp256r1Scalar = ECScalar::from(&secret); - let g = Secp256r1Point::generator(); - let h = g * sk; - assert_eq!( - format!("{:0>66}", h.bytes_compressed_to_big_int().to_hex()), - "025c31225f77535b1ceb7f603ef73627bf096a1efb65c1fdf0f7c1c9d64cf167ca" + .unwrap(), + base_point2 ); } - - #[test] - fn scalar_from_bigint() { - let r = Secp256r1Scalar::new_random(); - let int = r.to_big_int(); - let s: Secp256r1Scalar = ECScalar::from(&int); - assert_eq!(r.to_big_int(), s.to_big_int()); - } - - #[test] - fn add_sub_point() { - let g = Secp256r1Point::generator(); - let i: Secp256r1Scalar = ECScalar::from(&BigInt::from(3)); - assert_eq!((g + g + g).get_element(), (g * i).get_element()); - assert_eq!((g + g).get_element(), (g + g - g + g).get_element()); - } - - #[test] - fn add_scalar() { - let i: Secp256r1Scalar = ECScalar::from(&BigInt::from(1)); - let j: Secp256r1Scalar = ECScalar::from(&BigInt::from(2)); - assert_eq!((i + i).to_big_int(), j.to_big_int()); - assert_eq!((i + i + i + i).to_big_int(), (j + j).to_big_int()); - } - - #[test] - fn sub_scalar() { - let i: Secp256r1Scalar = ECScalar::from(&BigInt::from(1)); - assert_eq!((i + i - i).to_big_int(), i.to_big_int()); - let j: Secp256r1Scalar = ECScalar::from(&BigInt::from(2)); - assert_eq!((j + j - j).to_big_int(), j.to_big_int()); - let k = Secp256r1Scalar::new_random(); - assert_eq!((k + k - k).to_big_int(), k.to_big_int()); - } - - #[test] - fn mul_scalar() { - let i: Secp256r1Scalar = ECScalar::from(&BigInt::from(1)); - let j: Secp256r1Scalar = ECScalar::from(&BigInt::from(2)); - assert_eq!((j * i).to_big_int(), j.to_big_int()); - } } diff --git a/src/elliptic/curves/secp256_k1.rs b/src/elliptic/curves/secp256_k1.rs index 41f79c25..9724a92c 100644 --- a/src/elliptic/curves/secp256_k1.rs +++ b/src/elliptic/curves/secp256_k1.rs @@ -16,31 +16,52 @@ // The Public Key codec: Point <> SecretKey // -use super::traits::{ECPoint, ECScalar}; -use crate::arithmetic::traits::*; -use crate::BigInt; -use crate::ErrorKey; - -#[cfg(feature = "merkle")] -use crypto::digest::Digest; -#[cfg(feature = "merkle")] -use crypto::sha3::Sha3; -#[cfg(feature = "merkle")] -use merkle::Hashable; -use rand::thread_rng; +use std::ops; +use std::ops::Deref; +use std::ptr; +use std::sync::atomic; + use secp256k1::constants::{ - CURVE_ORDER, GENERATOR_X, GENERATOR_Y, SECRET_KEY_SIZE, UNCOMPRESSED_PUBLIC_KEY_SIZE, + self, GENERATOR_X, GENERATOR_Y, SECRET_KEY_SIZE, UNCOMPRESSED_PUBLIC_KEY_SIZE, }; -use secp256k1::{PublicKey, Secp256k1, SecretKey, VerifyOnly}; -use serde::de::{self, Error, MapAccess, SeqAccess, Visitor}; -use serde::ser::SerializeStruct; -use serde::ser::{Serialize, Serializer}; -use serde::{Deserialize, Deserializer}; -use std::fmt; -use std::ops::{Add, Mul}; -use std::ptr; -use std::sync::{atomic, Once}; -use zeroize::Zeroize; +use secp256k1::{PublicKey, SecretKey, SECP256K1}; +use serde::{Deserialize, Serialize}; +use zeroize::{Zeroize, Zeroizing}; + +use crate::arithmetic::*; + +use super::traits::*; + +lazy_static::lazy_static! { + static ref CURVE_ORDER: BigInt = BigInt::from_bytes(&constants::CURVE_ORDER); + + static ref GENERATOR_UNCOMRESSED: [u8; 65] = { + let mut g = [0u8; 65]; + g[0] = 0x04; + g[1..33].copy_from_slice(&GENERATOR_X); + g[33..].copy_from_slice(&GENERATOR_Y); + g + }; + + static ref BASE_POINT2_UNCOMPRESSED: [u8; 65] = { + let mut g = [0u8; 65]; + g[0] = 0x04; + g[1..33].copy_from_slice(&BASE_POINT2_X); + g[33..].copy_from_slice(&BASE_POINT2_Y); + g + }; + + static ref GENERATOR: Secp256k1Point = Secp256k1Point { + purpose: "generator", + ge: Some(PK(PublicKey::from_slice(&GENERATOR_UNCOMRESSED[..]).unwrap())), + }; + + static ref BASE_POINT2: Secp256k1Point = Secp256k1Point { + purpose: "base_point2", + ge: Some(PK(PublicKey::from_slice(&BASE_POINT2_UNCOMPRESSED[..]).unwrap())), + }; +} + /* X coordinate of a point of unknown discrete logarithm. Computed using a deterministic algorithm with the generator as input. See test_base_point2 */ @@ -54,761 +75,451 @@ const BASE_POINT2_Y: [u8; 32] = [ 0x80, 0x7b, 0xcb, 0xa1, 0xdf, 0x0d, 0xf0, 0x7a, 0x82, 0x17, 0xe9, 0xf7, 0xf7, 0xc2, 0xbe, 0x88, ]; -pub type SK = SecretKey; -pub type PK = PublicKey; +/// SK wraps secp256k1::SecretKey and implements Zeroize to it +#[derive(Clone, PartialEq, Debug)] +pub struct SK(pub SecretKey); +/// PK wraps secp256k1::PublicKey and implements Zeroize to it +#[derive(Copy, Clone, PartialEq, Debug)] +pub struct PK(pub PublicKey); -#[derive(Clone, Debug, Copy)] -pub struct Secp256k1Scalar { - purpose: &'static str, - fe: SK, +impl ops::Deref for SK { + type Target = SecretKey; + + fn deref(&self) -> &Self::Target { + &self.0 + } } -#[derive(Clone, Debug, Copy)] -pub struct Secp256k1Point { - purpose: &'static str, - ge: PK, +impl ops::DerefMut for SK { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } } -pub type GE = Secp256k1Point; -pub type FE = Secp256k1Scalar; - -impl Secp256k1Point { - pub fn random_point() -> Secp256k1Point { - let random_scalar: Secp256k1Scalar = Secp256k1Scalar::new_random(); - let base_point = Secp256k1Point::generator(); - let pk = base_point.scalar_mul(&random_scalar.get_element()); - Secp256k1Point { - purpose: "random_point", - ge: pk.get_element(), - } + +impl ops::Deref for PK { + type Target = PublicKey; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl ops::DerefMut for PK { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Zeroize for SK { + fn zeroize(&mut self) { + let sk = self.0.as_mut_ptr(); + let sk_bytes = unsafe { std::slice::from_raw_parts_mut(sk, 32) }; + sk_bytes.zeroize() } } -impl Zeroize for Secp256k1Scalar { +impl Zeroize for PK { fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, FE::zero()) }; - atomic::fence(atomic::Ordering::SeqCst); + let zeroed = unsafe { secp256k1::ffi::PublicKey::new() }; + unsafe { ptr::write_volatile(self.0.as_mut_ptr(), zeroed) }; atomic::compiler_fence(atomic::Ordering::SeqCst); } } +/// K-256 curve implementation based on [secp256k1] library +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Secp256k1 {} + +impl Curve for Secp256k1 { + type Point = GE; + type Scalar = FE; + + const CURVE_NAME: &'static str = "secp256k1"; +} + +#[derive(Clone, Debug)] +pub struct Secp256k1Scalar { + purpose: &'static str, + /// Zeroizing wraps SK and zeroize it on drop + /// + /// `fe` might be None — special case for scalar being zero + fe: zeroize::Zeroizing>, +} +#[derive(Clone, Debug, Copy)] +pub struct Secp256k1Point { + purpose: &'static str, + ge: Option, +} + +type GE = Secp256k1Point; +type FE = Secp256k1Scalar; + impl ECScalar for Secp256k1Scalar { - type SecretKey = SK; + type Underlying = Option; + + type ScalarBytes = [u8; 32]; - fn new_random() -> Secp256k1Scalar { + fn random() -> Secp256k1Scalar { + let sk = SK(SecretKey::new(&mut rand_legacy::thread_rng())); Secp256k1Scalar { purpose: "random", - fe: SecretKey::new(&mut thread_rng()), + fe: Zeroizing::new(Some(sk)), } } fn zero() -> Secp256k1Scalar { - let zero_arr = [0u8; 32]; - let zero = unsafe { std::mem::transmute::<[u8; 32], SecretKey>(zero_arr) }; Secp256k1Scalar { purpose: "zero", - fe: zero, + fe: Zeroizing::new(None), } } - fn get_element(&self) -> SK { - self.fe + fn is_zero(&self) -> bool { + self.fe.is_none() } - fn set_element(&mut self, element: SK) { - self.fe = element - } - - fn from(n: &BigInt) -> Secp256k1Scalar { - let curve_order = FE::q(); - let n_reduced = BigInt::mod_add(n, &BigInt::from(0), &curve_order); - let mut v = BigInt::to_bytes(&n_reduced); - - if v.len() < SECRET_KEY_SIZE { - let mut template = vec![0; SECRET_KEY_SIZE - v.len()]; - template.extend_from_slice(&v); - v = template; + fn from_bigint(n: &BigInt) -> Secp256k1Scalar { + let n = n.modulus(Self::group_order()); + if n.is_zero() { + return Secp256k1Scalar { + purpose: "from_bigint", + fe: Self::zero().fe, + }; } + let bytes = n + .to_bytes_array::() + .expect("n mod curve_order must be equal or less than 32 bytes"); Secp256k1Scalar { - purpose: "from_big_int", - fe: SK::from_slice(&v).unwrap(), + purpose: "from_bigint", + fe: Zeroizing::new(Some(SK( + SecretKey::from_slice(&bytes).expect("fe is in (0, order) and exactly 32 bytes") + ))), } } - fn to_big_int(&self) -> BigInt { - BigInt::from_bytes(&(self.fe[0..self.fe.len()])) + fn to_bigint(&self) -> BigInt { + match &*self.fe { + Some(sk) => BigInt::from_bytes(&sk[..]), + None => BigInt::zero(), + } } - fn q() -> BigInt { - BigInt::from_bytes(CURVE_ORDER.as_ref()) + fn serialize(&self) -> Self::ScalarBytes { + match &*self.fe { + Some(s) => *s.as_ref(), + None => [0u8; 32], + } } - fn add(&self, other: &SK) -> Secp256k1Scalar { - let mut other_scalar: FE = ECScalar::new_random(); - other_scalar.set_element(*other); - let res: FE = ECScalar::from(&BigInt::mod_add( - &self.to_big_int(), - &other_scalar.to_big_int(), - &FE::q(), - )); + fn deserialize(bytes: &[u8]) -> Result { + let sk = if bytes == [0; 32] { + None + } else { + Some(SK( + SecretKey::from_slice(bytes).or(Err(DeserializationError))? + )) + }; + Ok(Secp256k1Scalar { + purpose: "deserialize", + fe: sk.into(), + }) + } + + fn add(&self, other: &Self) -> Secp256k1Scalar { + let fe = match (&*self.fe, &*other.fe) { + (None, right) => right.clone(), + (left, None) => left.clone(), + (Some(left), Some(right)) => { + let mut res = left.clone(); + res.add_assign(&right.0[..]).ok().map(|_| res) // right might be the negation of left. + } + }; + Secp256k1Scalar { purpose: "add", - fe: res.get_element(), + fe: Zeroizing::new(fe), } } - fn mul(&self, other: &SK) -> Secp256k1Scalar { - let mut other_scalar: FE = ECScalar::new_random(); - other_scalar.set_element(*other); - let res: FE = ECScalar::from(&BigInt::mod_mul( - &self.to_big_int(), - &other_scalar.to_big_int(), - &FE::q(), - )); + fn mul(&self, other: &Self) -> Secp256k1Scalar { + let fe = match (&*self.fe, &*other.fe) { + (None, _) | (_, None) => None, + (Some(left), Some(right)) => { + let mut res = left.clone(); + res.0 + .mul_assign(&right.0[..]) + .expect("Can't fail as it's a valid secret"); + Some(res) + } + }; + Secp256k1Scalar { purpose: "mul", - fe: res.get_element(), + fe: Zeroizing::new(fe), } } - fn sub(&self, other: &SK) -> Secp256k1Scalar { - let mut other_scalar: FE = ECScalar::new_random(); - other_scalar.set_element(*other); - let res: FE = ECScalar::from(&BigInt::mod_sub( - &self.to_big_int(), - &other_scalar.to_big_int(), - &FE::q(), - )); + fn sub(&self, other: &Self) -> Secp256k1Scalar { + let right = other.neg(); + let res = self.clone().add(&right); Secp256k1Scalar { purpose: "sub", - fe: res.get_element(), + fe: res.fe, } } - fn invert(&self) -> Secp256k1Scalar { - let bignum = self.to_big_int(); - let bn_inv = BigInt::mod_inv(&bignum, &FE::q()).unwrap(); - ECScalar::from(&bn_inv) - } -} -impl Mul for Secp256k1Scalar { - type Output = Secp256k1Scalar; - fn mul(self, other: Secp256k1Scalar) -> Secp256k1Scalar { - (&self).mul(&other.get_element()) + fn neg(&self) -> Self { + let fe = self.fe.deref().clone().map(|mut fe| { + fe.negate_assign(); + fe + }); + Secp256k1Scalar { + purpose: "neg", + fe: Zeroizing::new(fe), + } } -} -impl<'o> Mul<&'o Secp256k1Scalar> for Secp256k1Scalar { - type Output = Secp256k1Scalar; - fn mul(self, other: &'o Secp256k1Scalar) -> Secp256k1Scalar { - (&self).mul(&other.get_element()) + fn invert(&self) -> Option { + let n = self.to_bigint(); + let n_inv = BigInt::mod_inv(&n, Self::group_order()); + n_inv.map(|i| Secp256k1Scalar { + purpose: "invert", + fe: Self::from_bigint(&i).fe, + }) } -} -impl Add for Secp256k1Scalar { - type Output = Secp256k1Scalar; - fn add(self, other: Secp256k1Scalar) -> Secp256k1Scalar { - (&self).add(&other.get_element()) + fn group_order() -> &'static BigInt { + &CURVE_ORDER } -} -impl<'o> Add<&'o Secp256k1Scalar> for Secp256k1Scalar { - type Output = Secp256k1Scalar; - fn add(self, other: &'o Secp256k1Scalar) -> Secp256k1Scalar { - (&self).add(&other.get_element()) + fn underlying_ref(&self) -> &Self::Underlying { + &self.fe } -} - -impl Serialize for Secp256k1Scalar { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_big_int().to_hex()) - } -} -impl<'de> Deserialize<'de> for Secp256k1Scalar { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(Secp256k1ScalarVisitor) + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.fe } -} - -struct Secp256k1ScalarVisitor; -impl<'de> Visitor<'de> for Secp256k1ScalarVisitor { - type Value = Secp256k1Scalar; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("Secp256k1Scalar") - } - - fn visit_str(self, s: &str) -> Result { - let v = BigInt::from_hex(s).map_err(E::custom)?; - Ok(ECScalar::from(&v)) + fn from_underlying(u: Self::Underlying) -> Secp256k1Scalar { + Secp256k1Scalar { + purpose: "from_underlying", + fe: Zeroizing::new(u), + } } } impl PartialEq for Secp256k1Scalar { fn eq(&self, other: &Secp256k1Scalar) -> bool { - self.get_element() == other.get_element() - } -} - -impl PartialEq for Secp256k1Point { - fn eq(&self, other: &Secp256k1Point) -> bool { - self.get_element() == other.get_element() - } -} - -impl Zeroize for Secp256k1Point { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(self, GE::generator()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); + self.underlying_ref() == other.underlying_ref() } } impl ECPoint for Secp256k1Point { - type SecretKey = SK; - type PublicKey = PK; type Scalar = Secp256k1Scalar; + type Underlying = Option; - fn base_point2() -> Secp256k1Point { - let mut v = vec![4_u8]; - v.extend(BASE_POINT2_X.as_ref()); - v.extend(BASE_POINT2_Y.as_ref()); - Secp256k1Point { - purpose: "random", - ge: PK::from_slice(&v).unwrap(), - } - } + type CompressedPoint = [u8; 33]; + type UncompressedPoint = [u8; 65]; - fn generator() -> Secp256k1Point { - let mut v = vec![4_u8]; - v.extend(GENERATOR_X.as_ref()); - v.extend(GENERATOR_Y.as_ref()); + fn zero() -> Secp256k1Point { Secp256k1Point { - purpose: "base_fe", - ge: PK::from_slice(&v).unwrap(), + purpose: "zero", + ge: None, } } - fn get_element(&self) -> PK { - self.ge - } - - /// to return from BigInt to PK use from_bytes: - /// 1) convert BigInt::to_vec - /// 2) remove first byte [1..33] - /// 3) call from_bytes - fn bytes_compressed_to_big_int(&self) -> BigInt { - let serial = self.ge.serialize(); - BigInt::from_bytes(&serial[0..33]) + fn is_zero(&self) -> bool { + self.ge.is_none() } - fn x_coor(&self) -> Option { - let serialized_pk = PK::serialize_uncompressed(&self.ge); - let x = &serialized_pk[1..serialized_pk.len() / 2 + 1]; - let x_vec = x.to_vec(); - Some(BigInt::from_bytes(&x_vec[..])) + fn generator() -> &'static Secp256k1Point { + &GENERATOR } - fn y_coor(&self) -> Option { - let serialized_pk = PK::serialize_uncompressed(&self.ge); - let y = &serialized_pk[(serialized_pk.len() - 1) / 2 + 1..serialized_pk.len()]; - let y_vec = y.to_vec(); - Some(BigInt::from_bytes(&y_vec[..])) + fn base_point2() -> &'static Secp256k1Point { + &BASE_POINT2 } - fn from_bytes(bytes: &[u8]) -> Result { - let bytes_vec = bytes.to_vec(); - let mut bytes_array_65 = [0u8; 65]; - let mut bytes_array_33 = [0u8; 33]; + fn from_coords(x: &BigInt, y: &BigInt) -> Result { + let vec_x = x.to_bytes(); + let vec_y = y.to_bytes(); + const COOR_SIZE: usize = (UNCOMPRESSED_PUBLIC_KEY_SIZE - 1) / 2; + let mut point = [0u8; UNCOMPRESSED_PUBLIC_KEY_SIZE]; + point[0] = 0x04; + point[1 + COOR_SIZE - vec_x.len()..1 + COOR_SIZE].copy_from_slice(&vec_x); + point[1 + (2 * COOR_SIZE) - vec_y.len()..].copy_from_slice(&vec_y); - let byte_len = bytes_vec.len(); - match byte_len { - 33..=63 => { - let mut template = vec![0; 64 - bytes_vec.len()]; - template.extend_from_slice(&bytes); - let mut bytes_vec = template; - let mut template: Vec = vec![4]; - template.append(&mut bytes_vec); - let bytes_slice = &template[..]; + debug_assert_eq!(x, &BigInt::from_bytes(&point[1..1 + COOR_SIZE])); + debug_assert_eq!(y, &BigInt::from_bytes(&point[1 + COOR_SIZE..])); - bytes_array_65.copy_from_slice(&bytes_slice[0..65]); - let result = PK::from_slice(&bytes_array_65); - let test = result.map(|pk| Secp256k1Point { - purpose: "random", - ge: pk, - }); - test.map_err(|_err| ErrorKey::InvalidPublicKey) - } + PublicKey::from_slice(&point) + .map(|ge| Secp256k1Point { + purpose: "from_coords", + ge: Some(PK(ge)), + }) + .map_err(|_| NotOnCurve) + } - 0..=32 => { - let mut template = vec![0; 32 - bytes_vec.len()]; - template.extend_from_slice(&bytes); - let mut bytes_vec = template; - let mut template: Vec = vec![2]; - template.append(&mut bytes_vec); - let bytes_slice = &template[..]; - - bytes_array_33.copy_from_slice(&bytes_slice[0..33]); - let result = PK::from_slice(&bytes_array_33); - let test = result.map(|pk| Secp256k1Point { - purpose: "random", - ge: pk, - }); - test.map_err(|_err| ErrorKey::InvalidPublicKey) - } - _ => { - let bytes_slice = &bytes_vec[0..64]; - let mut bytes_vec = bytes_slice.to_vec(); - let mut template: Vec = vec![4]; - template.append(&mut bytes_vec); - let bytes_slice = &template[..]; - - bytes_array_65.copy_from_slice(&bytes_slice[0..65]); - let result = PK::from_slice(&bytes_array_65); - let test = result.map(|pk| Secp256k1Point { - purpose: "random", - ge: pk, - }); - test.map_err(|_err| ErrorKey::InvalidPublicKey) + fn x_coord(&self) -> Option { + match &self.ge { + Some(ge) => { + let serialized_pk = ge.serialize_uncompressed(); + let x = &serialized_pk[1..serialized_pk.len() / 2 + 1]; + Some(BigInt::from_bytes(x)) } + None => None, } } - fn pk_to_key_slice(&self) -> Vec { - let mut v = vec![4_u8]; - let x_vec = BigInt::to_bytes(&self.x_coor().unwrap()); - let y_vec = BigInt::to_bytes(&self.y_coor().unwrap()); - - let mut raw_x: Vec = Vec::new(); - let mut raw_y: Vec = Vec::new(); - raw_x.extend(vec![0u8; 32 - x_vec.len()]); - raw_x.extend(x_vec); - - raw_y.extend(vec![0u8; 32 - y_vec.len()]); - raw_y.extend(y_vec); - v.extend(raw_x); - v.extend(raw_y); - v + fn y_coord(&self) -> Option { + match &self.ge { + Some(ge) => { + let serialized_pk = ge.serialize_uncompressed(); + let y = &serialized_pk[(serialized_pk.len() - 1) / 2 + 1..serialized_pk.len()]; + Some(BigInt::from_bytes(y)) + } + None => None, + } } - fn scalar_mul(&self, fe: &SK) -> Secp256k1Point { - let mut new_point = *self; - new_point - .ge - .mul_assign(get_context(), &fe[..]) - .expect("Assignment expected"); - new_point + fn coords(&self) -> Option { + match &self.ge { + Some(ge) => { + let serialized_pk = ge.serialize_uncompressed(); + let x = &serialized_pk[1..serialized_pk.len() / 2 + 1]; + let y = &serialized_pk[(serialized_pk.len() - 1) / 2 + 1..serialized_pk.len()]; + Some(PointCoords { + x: BigInt::from_bytes(x), + y: BigInt::from_bytes(y), + }) + } + None => None, + } } - fn add_point(&self, other: &PK) -> Secp256k1Point { - Secp256k1Point { - purpose: "combine", - ge: self.ge.combine(other).unwrap(), + fn serialize_compressed(&self) -> Self::CompressedPoint { + match self.ge { + None => [0u8; 33], + Some(ge) => ge.serialize(), } } - fn sub_point(&self, other: &PK) -> Secp256k1Point { - let point = Secp256k1Point { - purpose: "sub_point", - ge: *other, - }; - let p: Vec = vec![ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 252, 47, - ]; - let order = BigInt::from_bytes(&p[..]); - let x = point.x_coor().unwrap(); - let y = point.y_coor().unwrap(); - let minus_y = BigInt::mod_sub(&order, &y, &order); - - let x_vec = BigInt::to_bytes(&x); - let y_vec = BigInt::to_bytes(&minus_y); - - let mut template_x = vec![0; 32 - x_vec.len()]; - template_x.extend_from_slice(&x_vec); - let mut x_vec = template_x; - - let mut template_y = vec![0; 32 - y_vec.len()]; - template_y.extend_from_slice(&y_vec); - let y_vec = template_y; - - x_vec.extend_from_slice(&y_vec); - - let minus_point: GE = ECPoint::from_bytes(&x_vec).unwrap(); - //let minus_point: GE = ECPoint::from_coor(&x, &y_inv); - ECPoint::add_point(self, &minus_point.get_element()) - } - - fn from_coor(x: &BigInt, y: &BigInt) -> Secp256k1Point { - let mut vec_x = BigInt::to_bytes(x); - let mut vec_y = BigInt::to_bytes(y); - let coor_size = (UNCOMPRESSED_PUBLIC_KEY_SIZE - 1) / 2; - - if vec_x.len() < coor_size { - // pad - let mut x_buffer = vec![0; coor_size - vec_x.len()]; - x_buffer.extend_from_slice(&vec_x); - vec_x = x_buffer + fn serialize_uncompressed(&self) -> Self::UncompressedPoint { + match self.ge { + None => [0u8; 65], + Some(ge) => ge.serialize_uncompressed(), } + } - if vec_y.len() < coor_size { - // pad - let mut y_buffer = vec![0; coor_size - vec_y.len()]; - y_buffer.extend_from_slice(&vec_y); - vec_y = y_buffer + fn deserialize(bytes: &[u8]) -> Result { + if bytes == [0; 33] || bytes == [0; 65] { + Ok(Secp256k1Point { + purpose: "from_bytes", + ge: None, + }) + } else { + let pk = PublicKey::from_slice(bytes).map_err(|_| DeserializationError)?; + Ok(Secp256k1Point { + purpose: "from_bytes", + ge: Some(PK(pk)), + }) } + } - assert_eq!(x, &BigInt::from_bytes(vec_x.as_ref())); - assert_eq!(y, &BigInt::from_bytes(vec_y.as_ref())); - - let mut v = vec![4_u8]; - v.extend(vec_x); - v.extend(vec_y); + fn check_point_order_equals_group_order(&self) -> bool { + // This curve has cofactor=1 => any nonzero point has order GROUP_ORDER + !self.is_zero() + } + fn scalar_mul(&self, scalar: &Self::Scalar) -> Secp256k1Point { + let mut res = *self; + res.scalar_mul_assign(scalar); Secp256k1Point { - purpose: "base_fe", - ge: PK::from_slice(&v).unwrap(), + purpose: "mul", + ge: res.ge, } } -} -static mut CONTEXT: Option> = None; -pub fn get_context() -> &'static Secp256k1 { - static INIT_CONTEXT: Once = Once::new(); - INIT_CONTEXT.call_once(|| unsafe { - CONTEXT = Some(Secp256k1::verification_only()); - }); - unsafe { CONTEXT.as_ref().unwrap() } -} - -#[cfg(feature = "merkle")] -impl Hashable for Secp256k1Point { - fn update_context(&self, context: &mut Sha3) { - let bytes: Vec = self.pk_to_key_slice(); - context.input(&bytes[..]); + fn generator_mul(scalar: &Self::Scalar) -> Self { + let ge = scalar + .fe + .as_ref() + .map(|sk| PK(PublicKey::from_secret_key(&SECP256K1, sk))); + Secp256k1Point { + purpose: "generator_mul", + ge, + } } -} -impl Mul for Secp256k1Point { - type Output = Secp256k1Point; - fn mul(self, other: Secp256k1Scalar) -> Self::Output { - self.scalar_mul(&other.get_element()) - } -} + fn add_point(&self, other: &Self) -> Secp256k1Point { + let ge = match (&self.ge, &other.ge) { + (None, right) => *right, + (left, None) => *left, + (Some(left), Some(right)) => left.combine(right).ok().map(PK), // right might be the negation of left + }; -impl<'o> Mul<&'o Secp256k1Scalar> for Secp256k1Point { - type Output = Secp256k1Point; - fn mul(self, other: &'o Secp256k1Scalar) -> Self::Output { - self.scalar_mul(&other.get_element()) + Secp256k1Point { purpose: "add", ge } } -} -impl<'o> Mul<&'o Secp256k1Scalar> for &'o Secp256k1Point { - type Output = Secp256k1Point; - fn mul(self, other: &'o Secp256k1Scalar) -> Self::Output { - self.scalar_mul(&other.get_element()) + fn sub_point(&self, other: &Self) -> Secp256k1Point { + let other_negated = other.neg_point(); + let ge = self.add_point(&other_negated).ge; + Secp256k1Point { purpose: "sub", ge } } -} -impl Add for Secp256k1Point { - type Output = Secp256k1Point; - fn add(self, other: Secp256k1Point) -> Self::Output { - self.add_point(&other.get_element()) + fn neg_point(&self) -> Secp256k1Point { + let ge = self.ge.map(|mut ge| { + ge.0.negate_assign(&SECP256K1); + ge + }); + Secp256k1Point { purpose: "neg", ge } } -} -impl<'o> Add<&'o Secp256k1Point> for Secp256k1Point { - type Output = Secp256k1Point; - fn add(self, other: &'o Secp256k1Point) -> Self::Output { - self.add_point(&other.get_element()) + fn scalar_mul_assign(&mut self, scalar: &Self::Scalar) { + match (&mut self.ge, &*scalar.fe) { + (None, _) | (_, None) => { + self.ge = None; + } + (Some(ge), Some(fe)) => { + ge.0.mul_assign(&SECP256K1, &fe.0[..]) + .expect("Can't fail as it's a valid secret"); + } + }; + self.purpose = "mul_assign"; } -} -impl<'o> Add<&'o Secp256k1Point> for &'o Secp256k1Point { - type Output = Secp256k1Point; - fn add(self, other: &'o Secp256k1Point) -> Self::Output { - self.add_point(&other.get_element()) + fn underlying_ref(&self) -> &Self::Underlying { + &self.ge } -} - -impl Serialize for Secp256k1Point { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Secp256k1Point", 2)?; - state.serialize_field("x", &self.x_coor().unwrap().to_hex())?; - state.serialize_field("y", &self.y_coor().unwrap().to_hex())?; - state.end() + fn underlying_mut(&mut self) -> &mut Self::Underlying { + &mut self.ge } -} - -impl<'de> Deserialize<'de> for Secp256k1Point { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let fields = &["x", "y"]; - deserializer.deserialize_struct("Secp256k1Point", fields, Secp256k1PointVisitor) + fn from_underlying(ge: Self::Underlying) -> Secp256k1Point { + Secp256k1Point { + purpose: "from_underlying", + ge, + } } } -struct Secp256k1PointVisitor; - -impl<'de> Visitor<'de> for Secp256k1PointVisitor { - type Value = Secp256k1Point; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("Secp256k1Point") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: SeqAccess<'de>, - { - let x = seq - .next_element()? - .ok_or_else(|| V::Error::invalid_length(0, &"a single element"))?; - let y = seq - .next_element()? - .ok_or_else(|| V::Error::invalid_length(0, &"a single element"))?; - - let bx = BigInt::from_hex(x).map_err(V::Error::custom)?; - let by = BigInt::from_hex(y).map_err(V::Error::custom)?; - - Ok(Secp256k1Point::from_coor(&bx, &by)) +impl PartialEq for Secp256k1Point { + fn eq(&self, other: &Secp256k1Point) -> bool { + self.underlying_ref() == other.underlying_ref() } +} - fn visit_map>(self, mut map: E) -> Result { - let mut x = String::new(); - let mut y = String::new(); - - while let Some(ref key) = map.next_key::()? { - let v = map.next_value::()?; - if key == "x" { - x = v - } else if key == "y" { - y = v - } else { - return Err(E::Error::unknown_field(key, &["x", "y"])); - } - } - - let bx = BigInt::from_hex(&x).map_err(E::Error::custom)?; - let by = BigInt::from_hex(&y).map_err(E::Error::custom)?; - - Ok(Secp256k1Point::from_coor(&bx, &by)) +impl Zeroize for Secp256k1Point { + fn zeroize(&mut self) { + self.ge.zeroize() } } #[cfg(test)] -mod tests { - use super::BigInt; - use super::Secp256k1Point; - use super::Secp256k1Scalar; - use crate::arithmetic::traits::*; - use crate::cryptographic_primitives::hashing::hash_sha256::HSha256; - use crate::cryptographic_primitives::hashing::traits::Hash; - use crate::elliptic::curves::traits::ECPoint; - use crate::elliptic::curves::traits::ECScalar; - - #[test] - fn serialize_sk() { - let scalar: Secp256k1Scalar = ECScalar::from(&BigInt::from(123456)); - let s = serde_json::to_string(&scalar).expect("Failed in serialization"); - assert_eq!(s, "\"1e240\""); - } - - #[test] - fn serialize_rand_pk_verify_pad() { - let vx = BigInt::from_hex( - &"ccaf75ab7960a01eb421c0e2705f6e84585bd0a094eb6af928c892a4a2912508".to_string(), - ) - .unwrap(); - - let vy = BigInt::from_hex( - &"e788e294bd64eee6a73d2fc966897a31eb370b7e8e9393b0d8f4f820b48048df".to_string(), - ) - .unwrap(); - - Secp256k1Point::from_coor(&vx, &vy); // x and y of size 32 - - let x = BigInt::from_hex( - &"5f6853305467a385b56a5d87f382abb52d10835a365ec265ce510e04b3c3366f".to_string(), - ) - .unwrap(); - - let y = BigInt::from_hex( - &"b868891567ca1ee8c44706c0dc190dd7779fe6f9b92ced909ad870800451e3".to_string(), - ) - .unwrap(); - - Secp256k1Point::from_coor(&x, &y); // x and y not of size 32 each +mod test { + use sha2::{Digest, Sha256}; - let r = Secp256k1Point::random_point(); - let r_expected = Secp256k1Point::from_coor(&r.x_coor().unwrap(), &r.y_coor().unwrap()); + use crate::arithmetic::*; - assert_eq!(r.x_coor().unwrap(), r_expected.x_coor().unwrap()); - assert_eq!(r.y_coor().unwrap(), r_expected.y_coor().unwrap()); - } - - #[test] - fn deserialize_sk() { - let s = "\"1e240\""; - let dummy: Secp256k1Scalar = serde_json::from_str(s).expect("Failed in serialization"); - - let sk: Secp256k1Scalar = ECScalar::from(&BigInt::from(123456)); - - assert_eq!(dummy, sk); - } - - #[test] - fn serialize_pk() { - let pk = Secp256k1Point::generator(); - let x = pk.x_coor().unwrap(); - let y = pk.y_coor().unwrap(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - - let expected = format!("{{\"x\":\"{}\",\"y\":\"{}\"}}", x.to_hex(), y.to_hex()); - assert_eq!(s, expected); - - let des_pk: Secp256k1Point = serde_json::from_str(&s).expect("Failed in serialization"); - assert_eq!(des_pk.ge, pk.ge); - } - - #[test] - fn bincode_pk() { - let pk = Secp256k1Point::generator(); - let bin = bincode::serialize(&pk).unwrap(); - let decoded: Secp256k1Point = bincode::deserialize(bin.as_slice()).unwrap(); - assert_eq!(decoded, pk); - } - - use crate::elliptic::curves::secp256_k1::{FE, GE}; - use crate::ErrorKey; - - #[test] - fn test_serdes_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - - let pk = GE::base_point2(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - } - - #[test] - #[should_panic] - fn test_serdes_bad_pk() { - let pk = GE::generator(); - let s = serde_json::to_string(&pk).expect("Failed in serialization"); - // we make sure that the string encodes invalid point: - let s: String = s.replace("79be", "79bf"); - let des_pk: GE = serde_json::from_str(&s).expect("Failed in deserialization"); - assert_eq!(des_pk, pk); - } - - #[test] - fn test_from_bytes() { - let g = Secp256k1Point::generator(); - let hash = HSha256::create_hash(&[&g.bytes_compressed_to_big_int()]); - let hash_vec = BigInt::to_bytes(&hash); - let result = Secp256k1Point::from_bytes(&hash_vec); - assert_eq!(result.unwrap_err(), ErrorKey::InvalidPublicKey) - } - - #[test] - fn test_from_bytes_3() { - let test_vec = [ - 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, 0, 1, 2, 3, 4, 5, 6, - ]; - let result = Secp256k1Point::from_bytes(&test_vec); - assert!(result.is_ok() | result.is_err()) - } - - #[test] - fn test_from_bytes_4() { - let test_vec = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, - ]; - let result = Secp256k1Point::from_bytes(&test_vec); - assert!(result.is_ok() | result.is_err()) - } - - #[test] - fn test_from_bytes_5() { - let test_vec = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, - 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, - 4, 5, 6, - ]; - let result = Secp256k1Point::from_bytes(&test_vec); - assert!(result.is_ok() | result.is_err()) - } - - #[test] - fn test_minus_point() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let b_bn = b.to_big_int(); - let order = FE::q(); - let minus_b = BigInt::mod_sub(&order, &b_bn, &order); - let a_minus_b = BigInt::mod_add(&a.to_big_int(), &minus_b, &order); - let a_minus_b_fe: FE = ECScalar::from(&a_minus_b); - let base: GE = ECPoint::generator(); - let point_ab1 = base * a_minus_b_fe; - - let point_a = base * a; - let point_b = base * b; - let point_ab2 = point_a.sub_point(&point_b.get_element()); - assert_eq!(point_ab1.get_element(), point_ab2.get_element()); - } - - #[test] - fn test_invert() { - let a: FE = ECScalar::new_random(); - let a_bn = a.to_big_int(); - let a_inv = a.invert(); - let a_inv_bn_1 = BigInt::mod_inv(&a_bn, &FE::q()).unwrap(); - let a_inv_bn_2 = a_inv.to_big_int(); - assert_eq!(a_inv_bn_1, a_inv_bn_2); - } - - #[test] - fn test_scalar_mul_scalar() { - let a: FE = ECScalar::new_random(); - let b: FE = ECScalar::new_random(); - let c1 = a.mul(&b.get_element()); - let c2 = a * b; - assert_eq!(c1.get_element(), c2.get_element()); - } - - #[test] - fn test_pk_to_key_slice() { - for _ in 1..200 { - let r = FE::new_random(); - let rg = GE::generator() * r; - let key_slice = rg.pk_to_key_slice(); - - assert!(key_slice.len() == 65); - assert!(key_slice[0] == 4); - - let rg_prime: GE = ECPoint::from_bytes(&key_slice[1..65]).unwrap(); - assert_eq!(rg_prime.get_element(), rg.get_element()); - } - } + use super::{ECPoint, GE}; #[test] fn test_base_point2() { @@ -816,21 +527,22 @@ mod tests { It is done by using SHA256 repeatedly as a pseudo-random function, with the generator as the initial input, until receiving a valid Secp256k1 point. */ - let base_point2 = Secp256k1Point::base_point2(); + let base_point2 = GE::base_point2(); - let g = Secp256k1Point::generator(); - let mut hash = HSha256::create_hash(&[&g.bytes_compressed_to_big_int()]); - hash = HSha256::create_hash(&[&hash]); - hash = HSha256::create_hash(&[&hash]); + let g = GE::generator(); + let hash = Sha256::digest(&g.serialize_compressed()); + let hash = Sha256::digest(&hash); + let hash = Sha256::digest(&hash); - assert_eq!(hash, base_point2.x_coor().unwrap(),); + assert_eq!(BigInt::from_bytes(&hash), base_point2.x_coord().unwrap()); // check that base_point2 is indeed on the curve (from_coor() will fail otherwise) assert_eq!( - Secp256k1Point::from_coor( - &base_point2.x_coor().unwrap(), - &base_point2.y_coor().unwrap() - ), + &GE::from_coords( + &base_point2.x_coord().unwrap(), + &base_point2.y_coord().unwrap() + ) + .unwrap(), base_point2 ); } diff --git a/src/elliptic/curves/test.rs b/src/elliptic/curves/test.rs new file mode 100644 index 00000000..2dc5dccd --- /dev/null +++ b/src/elliptic/curves/test.rs @@ -0,0 +1,360 @@ +#![allow(non_snake_case)] + +use std::iter; + +use rand::{rngs::OsRng, Rng}; + +use crate::arithmetic::*; +use crate::test_for_all_curves; + +use super::traits::*; + +fn random_nonzero_scalar() -> S { + loop { + let s = S::random(); + if !s.is_zero() { + break s; + } + } +} + +test_for_all_curves!(valid_zero_point); +fn valid_zero_point() { + let zero = E::Scalar::zero(); + assert!(zero.is_zero()); + assert_eq!(zero, E::Scalar::zero()); +} + +test_for_all_curves!(zero_point_arithmetic); +fn zero_point_arithmetic() { + let zero_point = E::Point::zero(); + let point = E::Point::generator().scalar_mul(&random_nonzero_scalar()); + + assert_eq!(zero_point.add_point(&point), point, "O + P = P"); + assert_eq!(point.add_point(&zero_point), point, "P + O = P"); + + let point_neg = point.neg_point(); + assert!(point.add_point(&point_neg).is_zero(), "P + (-P) = O"); + assert!(point.sub_point(&point).is_zero(), "P - P = O"); + + let zero_scalar = E::Scalar::zero(); + assert!(point.scalar_mul(&zero_scalar).is_zero(), "P * 0 = O"); + let scalar = random_nonzero_scalar(); + assert!(zero_point.scalar_mul(&scalar).is_zero(), "O * s = O") +} + +test_for_all_curves!(scalar_modulo_curve_order); +fn scalar_modulo_curve_order() { + let n = E::Scalar::group_order(); + let s = E::Scalar::from_bigint(n); + assert!(s.is_zero()); + + let s = E::Scalar::from_bigint(&(n + 1)); + assert_eq!(s, E::Scalar::from_bigint(&BigInt::from(1))); +} + +test_for_all_curves!(zero_scalar_arithmetic); +fn zero_scalar_arithmetic() { + let s: E::Scalar = random_nonzero_scalar(); + let z = E::Scalar::zero(); + assert!(s.mul(&z).is_zero()); + assert!(z.mul(&s).is_zero()); + assert_eq!(s.add(&z), s); + assert_eq!(z.add(&s), s); +} + +test_for_all_curves!(point_addition_multiplication); +fn point_addition_multiplication() { + let point = E::Point::generator().scalar_mul(&random_nonzero_scalar()); + assert!(!point.is_zero(), "G * s != O"); + + let addition = iter::successors(Some(point.clone()), |p| Some(p.add_point(&point))) + .take(10) + .collect::>(); + let multiplication = (1..=10) + .map(|i| E::Scalar::from_bigint(&BigInt::from(i))) + .map(|s| point.scalar_mul(&s)) + .collect::>(); + assert_eq!(addition, multiplication); +} + +test_for_all_curves!(serialize_deserialize_point); +fn serialize_deserialize_point() { + let rand_point = ::generator().scalar_mul(&random_nonzero_scalar()); + let zero = E::Point::zero(); + for point in [rand_point, zero] { + let bytes = point.serialize_compressed(); + let deserialized = ::deserialize(bytes.as_ref()).unwrap(); + assert_eq!(point, deserialized); + let bytes = point.serialize_uncompressed(); + let deserialized = ::deserialize(bytes.as_ref()).unwrap(); + assert_eq!(point, deserialized); + } +} + +test_for_all_curves!(zero_point_serialization); +fn zero_point_serialization() { + let point: E::Point = ECPoint::zero(); + let bytes = point.serialize_compressed(); + let point_from_compressed: E::Point = ECPoint::deserialize(bytes.as_ref()).unwrap(); + assert_eq!(point, point_from_compressed); + + let bytes = point.serialize_uncompressed(); + let point_from_uncompressed: E::Point = ECPoint::deserialize(bytes.as_ref()).unwrap(); + assert_eq!(point, point_from_uncompressed); +} + +test_for_all_curves!(generator_mul_curve_order_is_zero); +fn generator_mul_curve_order_is_zero() { + let g: &E::Point = ECPoint::generator(); + let n = E::Scalar::group_order() - 1; + let s = E::Scalar::from_bigint(&n); + assert!(g.scalar_mul(&s).add_point(&g).is_zero()); +} + +test_for_all_curves!(scalar_behaves_the_same_as_bigint); +fn scalar_behaves_the_same_as_bigint() { + let mut rng = OsRng; + let q = E::Scalar::group_order(); + + let mut n = BigInt::zero(); + let mut s: E::Scalar = ECScalar::zero(); + + for _ in 0..100 { + let operation = rng.gen_range(0, 4); + if operation == 0 { + let n_inv = BigInt::mod_inv(&n, q); + let s_inv = s.invert().map(|s| s.to_bigint()); + + assert_eq!( + s_inv, + n_inv, + "{}^-1 = {} (got {})", + n, + n_inv + .as_ref() + .map(|i| i.to_string()) + .unwrap_or("None".to_string()), + s_inv + .as_ref() + .map(|i| i.to_string()) + .unwrap_or("None".to_string()), + ); + } else { + let n_was = n.clone(); + let k = BigInt::sample_below(&(q * 2)); + let k_s: E::Scalar = ECScalar::from_bigint(&k); + let op; + + match operation { + 1 => { + op = "+"; + n = BigInt::mod_add(&n, &k, q); + + let s_no_assign = s.add(&k_s); + s.add_assign(&k_s); + assert_eq!(s, s_no_assign); + } + 2 => { + op = "*"; + n = BigInt::mod_mul(&n, &k, q); + + let s_no_assign = s.mul(&k_s); + s.mul_assign(&k_s); + assert_eq!(s, s_no_assign); + } + 3 => { + op = "-"; + n = BigInt::mod_sub(&n, &k, q); + + let s_no_assign = s.sub(&k_s); + s.sub_assign(&k_s); + assert_eq!(s, s_no_assign); + } + _ => unreachable!(), + } + + assert_eq!( + s.to_bigint(), + n.modulus(q), + "{} {} {} = {} (got {})", + n_was, + op, + k, + n, + s.to_bigint() + ); + } + } +} + +test_for_all_curves!(from_coords_produces_the_same_point); +fn from_coords_produces_the_same_point() { + if E::CURVE_NAME == "ristretto" { + // This curve is exception. + return; + } + let s: E::Scalar = random_nonzero_scalar(); + println!("s={}", s.to_bigint()); + + let p: E::Point = ::generator().scalar_mul(&s); + let coords = p.coords().unwrap(); + let p2: E::Point = ECPoint::from_coords(&coords.x, &coords.y).unwrap(); + assert_eq!(p, p2); +} + +test_for_all_curves!(test_point_addition); +fn test_point_addition() { + let a: E::Scalar = random_nonzero_scalar(); + let b: E::Scalar = random_nonzero_scalar(); + + let aG: E::Point = ECPoint::generator_mul(&a); + let bG: E::Point = ECPoint::generator_mul(&b); + let a_plus_b = a.add(&b); + let a_plus_b_G: E::Point = ECPoint::generator_mul(&a_plus_b); + + assert_eq!(aG.add_point(&bG), a_plus_b_G); +} + +test_for_all_curves!(test_point_assign_addition); +fn test_point_assign_addition() { + let a: E::Scalar = random_nonzero_scalar(); + let b: E::Scalar = random_nonzero_scalar(); + + let aG: E::Point = ECPoint::generator_mul(&a); + let bG: E::Point = ECPoint::generator_mul(&b); + + let a_plus_b_G_1 = aG.add_point(&bG); + let a_plus_b_G_2 = { + let mut aG = aG; + aG.add_point_assign(&bG); + aG + }; + + assert_eq!(a_plus_b_G_1, a_plus_b_G_2); +} + +test_for_all_curves!(test_point_subtraction); +fn test_point_subtraction() { + let a: E::Scalar = random_nonzero_scalar(); + let b: E::Scalar = random_nonzero_scalar(); + + let aG: E::Point = ECPoint::generator_mul(&a); + let bG: E::Point = ECPoint::generator_mul(&b); + let a_minus_b = a.sub(&b); + let a_minus_b_G: E::Point = ECPoint::generator_mul(&a_minus_b); + + assert_eq!(aG.sub_point(&bG), a_minus_b_G); +} + +test_for_all_curves!(test_point_assign_subtraction); +fn test_point_assign_subtraction() { + let a: E::Scalar = random_nonzero_scalar(); + let b: E::Scalar = random_nonzero_scalar(); + + let aG: E::Point = ECPoint::generator_mul(&a); + let bG: E::Point = ECPoint::generator_mul(&b); + + let a_minus_b_G_1: E::Point = aG.sub_point(&bG); + let a_minus_b_G_2 = { + let mut aG = aG; + aG.sub_point_assign(&bG); + aG + }; + + assert_eq!(a_minus_b_G_1, a_minus_b_G_2); +} + +test_for_all_curves!(test_multiplication_point_at_scalar); +fn test_multiplication_point_at_scalar() { + let a: E::Scalar = random_nonzero_scalar(); + let b: E::Scalar = random_nonzero_scalar(); + + let aG: E::Point = ECPoint::generator_mul(&a); + let abG: E::Point = aG.scalar_mul(&b); + let a_mul_b = a.mul(&b); + let a_mul_b_G: E::Point = ECPoint::generator_mul(&a_mul_b); + + assert_eq!(abG, a_mul_b_G); +} + +test_for_all_curves!(test_assign_multiplication_point_at_scalar); +fn test_assign_multiplication_point_at_scalar() { + let a: E::Scalar = random_nonzero_scalar(); + let b: E::Scalar = random_nonzero_scalar(); + + let aG: E::Point = ECPoint::generator_mul(&a); + + let abG_1: E::Point = aG.scalar_mul(&b); + let abG_2 = { + let mut aG = aG; + aG.scalar_mul_assign(&b); + aG + }; + + assert_eq!(abG_1, abG_2); +} + +test_for_all_curves!(serialize_deserialize_scalar); +fn serialize_deserialize_scalar() { + let rand_point: E::Scalar = random_nonzero_scalar(); + let zero = E::Scalar::zero(); + for scalar in [rand_point, zero] { + let bytes = scalar.serialize(); + let deserialized = ::deserialize(bytes.as_ref()).unwrap(); + assert_eq!(scalar, deserialized); + } +} + +test_for_all_curves!(scalar_invert); +fn scalar_invert() { + let n: E::Scalar = random_nonzero_scalar(); + + let n_inv = n.invert().unwrap(); + assert_eq!(n.mul(&n_inv), ECScalar::from_bigint(&BigInt::one())) +} + +test_for_all_curves!(zero_scalar_invert); +fn zero_scalar_invert() { + let n: E::Scalar = ECScalar::zero(); + let n_inv = n.invert(); + assert!(n_inv.is_none()) +} + +test_for_all_curves!(point_negation); +fn point_negation() { + let p1 = ::generator_mul(&random_nonzero_scalar()); + let p2 = p1.neg_point(); + assert_eq!(p1.add_point(&p2), ECPoint::zero()); +} + +test_for_all_curves!(point_assign_negation); +fn point_assign_negation() { + let p = ::generator_mul(&random_nonzero_scalar()); + let p_neg_1 = p.neg_point(); + let p_neg_2 = { + let mut p = p; + p.neg_point_assign(); + p + }; + assert_eq!(p_neg_1, p_neg_2); +} + +test_for_all_curves!(scalar_negation); +fn scalar_negation() { + let s1: E::Scalar = random_nonzero_scalar(); + let s2 = s1.neg(); + assert_eq!(s1.add(&s2), E::Scalar::zero()); +} + +test_for_all_curves!(scalar_assign_negation); +fn scalar_assign_negation() { + let s: E::Scalar = random_nonzero_scalar(); + let s_neg_1 = s.neg(); + let s_neg_2 = { + let mut s = s; + s.neg_assign(); + s + }; + assert_eq!(s_neg_1, s_neg_2); +} diff --git a/src/elliptic/curves/traits.rs b/src/elliptic/curves/traits.rs index ff029c6c..e7f0dc0f 100644 --- a/src/elliptic/curves/traits.rs +++ b/src/elliptic/curves/traits.rs @@ -5,48 +5,262 @@ License MIT: */ -use std::ops::{Add, Mul}; +use std::fmt; + +use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; use crate::BigInt; -use crate::ErrorKey; -pub trait ECScalar: Mul + Add + Sized { - type SecretKey; +/// Elliptic curve implementation +/// +/// Refers to according implementation of [ECPoint] and [ECScalar]. +pub trait Curve: PartialEq + Clone + fmt::Debug + 'static { + type Point: ECPoint; + type Scalar: ECScalar; + + /// Canonical name for this curve + const CURVE_NAME: &'static str; +} + +/// Scalar value modulus [group order](Self::group_order) +/// +/// ## Note +/// This is a low-level trait, you should not use it directly. See wrappers [Point], [Scalar]. +/// +/// [Point]: super::wrappers::Point +/// [Scalar]: super::wrappers::Scalar +/// +/// Trait exposes various methods to manipulate scalars. Scalar can be zero. Scalar must zeroize its +/// value on drop. +pub trait ECScalar: Clone + PartialEq + fmt::Debug + 'static { + /// Underlying scalar type that can be retrieved in case of missing methods in this trait + type Underlying; - fn new_random() -> Self; + /// Serialized scalar + type ScalarBytes: AsRef<[u8]>; + + /// Samples a random scalar + fn random() -> Self; + + /// Constructs a zero scalar fn zero() -> Self; - fn get_element(&self) -> Self::SecretKey; - fn set_element(&mut self, element: Self::SecretKey); - fn from(n: &BigInt) -> Self; - fn to_big_int(&self) -> BigInt; - fn q() -> BigInt; - fn add(&self, other: &Self::SecretKey) -> Self; - fn mul(&self, other: &Self::SecretKey) -> Self; - fn sub(&self, other: &Self::SecretKey) -> Self; - fn invert(&self) -> Self; + /// Checks if the scalar equals to zero + fn is_zero(&self) -> bool { + self == &Self::zero() + } + + /// Constructs a scalar `n % group_order` + fn from_bigint(n: &BigInt) -> Self; + /// Converts a scalar to BigInt + fn to_bigint(&self) -> BigInt; + /// Serializes scalar into bytes + fn serialize(&self) -> Self::ScalarBytes; + /// Deserializes scalar from bytes + fn deserialize(bytes: &[u8]) -> Result; + + /// Calculates `(self + other) mod group_order` + fn add(&self, other: &Self) -> Self; + /// Calculates `(self * other) mod group_order` + fn mul(&self, other: &Self) -> Self; + /// Calculates `(self - other) mod group_order` + fn sub(&self, other: &Self) -> Self; + /// Calculates `-self mod group_order` + fn neg(&self) -> Self; + /// Calculates `self^-1 (mod group_order)`, returns None if self equals to zero + fn invert(&self) -> Option; + /// Calculates `(self + other) mod group_order`, and assigns result to `self` + fn add_assign(&mut self, other: &Self) { + *self = self.add(other) + } + /// Calculates `(self * other) mod group_order`, and assigns result to `self` + fn mul_assign(&mut self, other: &Self) { + *self = self.mul(other) + } + /// Calculates `(self - other) mod group_order`, and assigns result to `self` + fn sub_assign(&mut self, other: &Self) { + *self = self.sub(other) + } + /// Calculates `-self mod group_order`, and assigns result to `self` + fn neg_assign(&mut self) { + *self = self.neg() + } + + /// Returns an order of generator point + fn group_order() -> &'static BigInt; + + /// Returns a reference to underlying scalar value + fn underlying_ref(&self) -> &Self::Underlying; + /// Returns a mutable reference to underlying scalar value + fn underlying_mut(&mut self) -> &mut Self::Underlying; + /// Constructs a scalar from underlying value + fn from_underlying(u: Self::Underlying) -> Self; } -// TODO: add a fn is_point -pub trait ECPoint: - Mul<::Scalar, Output = Self> + Add + PartialEq -where - Self: Sized, -{ - type SecretKey; - type PublicKey; - - type Scalar: ECScalar; - - fn base_point2() -> Self; - fn generator() -> Self; - fn get_element(&self) -> Self::PublicKey; - fn x_coor(&self) -> Option; - fn y_coor(&self) -> Option; - fn bytes_compressed_to_big_int(&self) -> BigInt; - fn from_bytes(bytes: &[u8]) -> Result; - fn pk_to_key_slice(&self) -> Vec; - fn scalar_mul(&self, fe: &Self::SecretKey) -> Self; - fn add_point(&self, other: &Self::PublicKey) -> Self; - fn sub_point(&self, other: &Self::PublicKey) -> Self; - fn from_coor(x: &BigInt, y: &BigInt) -> Self; +/// Point on elliptic curve +/// +/// ## Note +/// This is a low-level trait, you should not use it directly. See [Point], [Scalar]. +/// +/// [Point]: super::wrappers::Point +/// [Scalar]: super::wrappers::Scalar +/// +/// Trait exposes various methods that make elliptic curve arithmetic. The point can +/// be [zero](ECPoint::zero). Unlike [ECScalar], ECPoint isn't required to zeroize its value on drop, +/// but it implements [Zeroize] trait so you can force zeroizing policy on your own. +pub trait ECPoint: Zeroize + Clone + PartialEq + fmt::Debug + 'static { + /// Scalar value the point can be multiplied at + type Scalar: ECScalar; + /// Underlying curve implementation that can be retrieved in case of missing methods in this trait + type Underlying; + + /// Point serialized in compressed form + /// + /// Usually represented as byte array `[u8; COMPRESSED_LEN]` + type CompressedPoint: AsRef<[u8]>; + /// Point serialized in uncompressed form + /// + /// Usually represented as byte array `[u8; UNCOMPRESSED_LEN]` + type UncompressedPoint: AsRef<[u8]>; + + /// Zero point + /// + /// Zero point is usually denoted as O. It's curve neutral element, i.e. `forall A. A + O = A`. + /// Weierstrass and Montgomery curves employ special "point at infinity" to add neutral elements, + /// such points don't have coordinates (i.e. [from_coords], [x_coord], [y_coord] return `None`). + /// Edwards curves' neutral element has coordinates. + /// + /// [from_coords]: Self::from_coords + /// [x_coord]: Self::x_coord + /// [y_coord]: Self::y_coord + fn zero() -> Self; + + /// Returns `true` if point is a neutral element + fn is_zero(&self) -> bool { + self == &Self::zero() + } + + /// Curve generator + /// + /// Returns a static reference at actual value because in most cases reference value is fine. + /// Use `.clone()` if you need to take it by value, i.e. `ECPoint::generator().clone()` + fn generator() -> &'static Self; + /// Curve second generator + /// + /// We provide an alternative generator value and prove that it was picked randomly + fn base_point2() -> &'static Self; + + /// Constructs a curve point from its coordinates + /// + /// Returns error if x, y are not on curve + fn from_coords(x: &BigInt, y: &BigInt) -> Result; + /// Returns `x` coordinate of the point, or `None` if point is at infinity + fn x_coord(&self) -> Option; + /// Returns `y` coordinate of the point, or `None` if point is at infinity + fn y_coord(&self) -> Option; + /// Returns point coordinates (`x` and `y`), or `None` if point is at infinity + fn coords(&self) -> Option; + + /// Serializes point into bytes in compressed + /// + /// Serialization must always succeed even if it's point at infinity. + fn serialize_compressed(&self) -> Self::CompressedPoint; + /// Serializes point into bytes in uncompressed + /// + /// Serialization must always succeed even if it's point at infinity. + fn serialize_uncompressed(&self) -> Self::UncompressedPoint; + /// Deserializes point from bytes + /// + /// Whether point in compressed or uncompressed form will be deducted from its size + fn deserialize(bytes: &[u8]) -> Result; + + /// Checks that order of this point equals to [group order](ECScalar::group_order) + /// + /// Generally, point might be composition of different subgroups points: `P = sG + kT` (`G` — + /// curve generator of order `q`=[group_order](ECScalar::group_order), `T` — generator of smaller + /// order). This function ensures that the point is of order `q`, ie. of form: `P = sG`. + /// + /// For curves with co-factor ≠ 1, following check must be carried out: + /// + /// ```text + /// P ≠ 0 ∧ qP ≠ 0 + /// ``` + /// + /// For curves with co-factor = 1, the check above can be reduced to: `P ≠ 0`. + fn check_point_order_equals_group_order(&self) -> bool { + let mut self_at_q = self.scalar_mul(&Self::Scalar::from_bigint( + &(Self::Scalar::group_order() - 1), + )); + self_at_q.add_point_assign(self); + !self.is_zero() && self_at_q.is_zero() + } + + /// Multiplies the point at scalar value + fn scalar_mul(&self, scalar: &Self::Scalar) -> Self; + /// Multiplies curve generator at given scalar + /// + /// Basically, it's the same as `ECPoint::generator().scalar_mul(&s)`, but can be more efficient + /// because most curve libs have constant time high performance generator multiplication. + fn generator_mul(scalar: &Self::Scalar) -> Self { + Self::generator().scalar_mul(scalar) + } + /// Adds two points + fn add_point(&self, other: &Self) -> Self; + /// Substrates `other` from `self` + fn sub_point(&self, other: &Self) -> Self; + /// Negates point + fn neg_point(&self) -> Self; + + /// Multiplies the point at scalar value, assigns result to `self` + fn scalar_mul_assign(&mut self, scalar: &Self::Scalar) { + *self = self.scalar_mul(scalar) + } + /// Adds two points, assigns result to `self` + fn add_point_assign(&mut self, other: &Self) { + *self = self.add_point(other) + } + /// Substrates `other` from `self`, assigns result to `self` + fn sub_point_assign(&mut self, other: &Self) { + *self = self.sub_point(other) + } + /// Negates point, assigns result to `self` + fn neg_point_assign(&mut self) { + *self = self.neg_point() + } + + /// Reference to underlying curve implementation + fn underlying_ref(&self) -> &Self::Underlying; + /// Mutual reference to underlying curve implementation + fn underlying_mut(&mut self) -> &mut Self::Underlying; + /// Construct a point from its underlying representation + fn from_underlying(u: Self::Underlying) -> Self; +} + +/// Affine coordinates of a point +#[derive(Serialize, Deserialize)] +pub struct PointCoords { + pub x: BigInt, + pub y: BigInt, } + +#[derive(Debug)] +pub struct DeserializationError; + +impl fmt::Display for DeserializationError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "failed to deserialize the point/scalar") + } +} + +impl std::error::Error for DeserializationError {} + +#[derive(Debug)] +pub struct NotOnCurve; + +impl fmt::Display for NotOnCurve { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "point not on the curve") + } +} + +impl std::error::Error for NotOnCurve {} diff --git a/src/elliptic/curves/wrappers/arithmetic.rs b/src/elliptic/curves/wrappers/arithmetic.rs new file mode 100644 index 00000000..d25b7003 --- /dev/null +++ b/src/elliptic/curves/wrappers/arithmetic.rs @@ -0,0 +1,491 @@ +use std::ops; + +use crate::elliptic::curves::traits::*; + +use super::*; + +macro_rules! matrix { + ( + trait = $trait:ident, + trait_fn = $trait_fn:ident, + output = $output:ty, + output_new = $output_new:expr, + point_fn = $point_fn:ident, + point_assign_fn = $point_assign_fn:ident, + pairs = {(r_<$($l:lifetime),*> $lhs_ref:ty, $rhs:ty), $($rest:tt)*} + ) => { + impl<$($l,)* E: Curve> ops::$trait<$rhs> for $lhs_ref { + type Output = $output; + fn $trait_fn(self, rhs: $rhs) -> Self::Output { + let p = self.as_raw().$point_fn(rhs.as_raw()); + $output_new(p) + } + } + matrix!{ + trait = $trait, + trait_fn = $trait_fn, + output = $output, + output_new = $output_new, + point_fn = $point_fn, + point_assign_fn = $point_assign_fn, + pairs = {$($rest)*} + } + }; + + ( + trait = $trait:ident, + trait_fn = $trait_fn:ident, + output = $output:ty, + output_new = $output_new:expr, + point_fn = $point_fn:ident, + point_assign_fn = $point_assign_fn:ident, + pairs = {(_r<$($l:lifetime),*> $lhs:ty, $rhs_ref:ty), $($rest:tt)*} + ) => { + impl<$($l,)* E: Curve> ops::$trait<$rhs_ref> for $lhs { + type Output = $output; + fn $trait_fn(self, rhs: $rhs_ref) -> Self::Output { + let p = rhs.as_raw().$point_fn(self.as_raw()); + $output_new(p) + } + } + matrix!{ + trait = $trait, + trait_fn = $trait_fn, + output = $output, + output_new = $output_new, + point_fn = $point_fn, + point_assign_fn = $point_assign_fn, + pairs = {$($rest)*} + } + }; + + ( + trait = $trait:ident, + trait_fn = $trait_fn:ident, + output = $output:ty, + output_new = $output_new:expr, + point_fn = $point_fn:ident, + point_assign_fn = $point_assign_fn:ident, + pairs = {(o_<$($l:lifetime),*> $lhs_owned:ty, $rhs:ty), $($rest:tt)*} + ) => { + impl<$($l,)* E: Curve> ops::$trait<$rhs> for $lhs_owned { + type Output = $output; + fn $trait_fn(self, rhs: $rhs) -> Self::Output { + let mut raw = self.into_raw(); + raw.$point_assign_fn(rhs.as_raw()); + $output_new(raw) + } + } + matrix!{ + trait = $trait, + trait_fn = $trait_fn, + output = $output, + output_new = $output_new, + point_fn = $point_fn, + point_assign_fn = $point_assign_fn, + pairs = {$($rest)*} + } + }; + + ( + trait = $trait:ident, + trait_fn = $trait_fn:ident, + output = $output:ty, + output_new = $output_new:expr, + point_fn = $point_fn:ident, + point_assign_fn = $point_assign_fn:ident, + pairs = {(_o<$($l:lifetime),*> $lhs:ty, $rhs_owned:ty), $($rest:tt)*} + ) => { + impl<$($l,)* E: Curve> ops::$trait<$rhs_owned> for $lhs { + type Output = $output; + fn $trait_fn(self, rhs: $rhs_owned) -> Self::Output { + let mut raw = rhs.into_raw(); + raw.$point_assign_fn(self.as_raw()); + $output_new(raw) + } + } + matrix!{ + trait = $trait, + trait_fn = $trait_fn, + output = $output, + output_new = $output_new, + point_fn = $point_fn, + point_assign_fn = $point_assign_fn, + pairs = {$($rest)*} + } + }; + + ( + trait = $trait:ident, + trait_fn = $trait_fn:ident, + output = $output:ty, + output_new = $output_new:expr, + point_fn = $point_fn:ident, + point_assign_fn = $point_assign_fn:ident, + pairs = {} + ) => { + // happy termination + }; +} + +fn addition_of_two_points(result: E::Point) -> Point { + // Safety: addition of two points of group order is always either a zero point or point of group + // order: `A + B = aG + bG = (a + b)G` + unsafe { Point::from_raw_unchecked(result) } +} + +matrix! { + trait = Add, + trait_fn = add, + output = Point, + output_new = addition_of_two_points, + point_fn = add_point, + point_assign_fn = add_point_assign, + pairs = { + (o_<> Point, Point), (o_<> Point, &Point), + (o_<> Point, Generator), + + (_o<> &Point, Point), (r_<> &Point, &Point), + (r_<> &Point, Generator), + + (_o<> Generator, Point), (r_<> Generator, &Point), + (r_<> Generator, Generator), + } +} + +fn subtraction_of_two_point(result: E::Point) -> Point { + // Safety: subtraction of two points of group order is always either a zero point or point of group + // order: `A - B = aG - bG = (a - b)G` + unsafe { Point::from_raw_unchecked(result) } +} + +matrix! { + trait = Sub, + trait_fn = sub, + output = Point, + output_new = subtraction_of_two_point, + point_fn = sub_point, + point_assign_fn = sub_point_assign, + pairs = { + (o_<> Point, Point), (o_<> Point, &Point), + (o_<> Point, Generator), + + (_o<> &Point, Point), (r_<> &Point, &Point), + (r_<> &Point, Generator), + + (_o<> Generator, Point), (r_<> Generator, &Point), + (r_<> Generator, Generator), + } +} + +fn multiplication_of_point_at_scalar(result: E::Point) -> Point { + // Safety: multiplication of point of group order at a scalar is always either a zero point or + // point of group order: `kA = kaG` + unsafe { Point::from_raw_unchecked(result) } +} + +matrix! { + trait = Mul, + trait_fn = mul, + output = Point, + output_new = multiplication_of_point_at_scalar, + point_fn = scalar_mul, + point_assign_fn = scalar_mul_assign, + pairs = { + (o_<> Point, Scalar), (o_<> Point, &Scalar), + (r_<> &Point, Scalar), (r_<> &Point, &Scalar), + + (_o<> Scalar, Point), (_o<> &Scalar, Point), + (_r<> Scalar, &Point), (_r<> &Scalar, &Point), + } +} + +matrix! { + trait = Add, + trait_fn = add, + output = Scalar, + output_new = Scalar::from_raw, + point_fn = add, + point_assign_fn = add_assign, + pairs = { + (o_<> Scalar, Scalar), (o_<> Scalar, &Scalar), + (_o<> &Scalar, Scalar), (r_<> &Scalar, &Scalar), + } +} + +matrix! { + trait = Sub, + trait_fn = sub, + output = Scalar, + output_new = Scalar::from_raw, + point_fn = sub, + point_assign_fn = sub_assign, + pairs = { + (o_<> Scalar, Scalar), (o_<> Scalar, &Scalar), + (_o<> &Scalar, Scalar), (r_<> &Scalar, &Scalar), + } +} + +matrix! { + trait = Mul, + trait_fn = mul, + output = Scalar, + output_new = Scalar::from_raw, + point_fn = mul, + point_assign_fn = mul_assign, + pairs = { + (o_<> Scalar, Scalar), (o_<> Scalar, &Scalar), + (_o<> &Scalar, Scalar), (r_<> &Scalar, &Scalar), + } +} + +impl ops::Mul<&Scalar> for Generator { + type Output = Point; + fn mul(self, rhs: &Scalar) -> Self::Output { + Point::from_raw(E::Point::generator_mul(rhs.as_raw())).expect( + "generator multiplied by scalar is always a point of group order or a zero point", + ) + } +} + +impl ops::Mul> for Generator { + type Output = Point; + fn mul(self, rhs: Scalar) -> Self::Output { + self.mul(&rhs) + } +} + +impl ops::Mul> for &Scalar { + type Output = Point; + fn mul(self, rhs: Generator) -> Self::Output { + rhs.mul(self) + } +} + +impl ops::Mul> for Scalar { + type Output = Point; + fn mul(self, rhs: Generator) -> Self::Output { + rhs.mul(self) + } +} + +impl ops::Neg for Scalar { + type Output = Scalar; + + fn neg(self) -> Self::Output { + Scalar::from_raw(self.as_raw().neg()) + } +} + +impl ops::Neg for &Scalar { + type Output = Scalar; + + fn neg(self) -> Self::Output { + Scalar::from_raw(self.as_raw().neg()) + } +} + +impl ops::Neg for Point { + type Output = Point; + + fn neg(self) -> Self::Output { + Point::from_raw(self.as_raw().neg_point()) + .expect("neg must not produce point of different order") + } +} + +impl ops::Neg for &Point { + type Output = Point; + + fn neg(self) -> Self::Output { + Point::from_raw(self.as_raw().neg_point()) + .expect("neg must not produce point of different order") + } +} + +impl ops::Neg for Generator { + type Output = Point; + + fn neg(self) -> Self::Output { + Point::from_raw(self.as_raw().neg_point()) + .expect("neg must not produce point of different order") + } +} + +#[cfg(test)] +mod test { + use super::*; + + macro_rules! assert_operator_defined_for { + ( + assert_fn = $assert_fn:ident, + lhs = {}, + rhs = {$($rhs:ty),*}, + ) => { + // Corner case + }; + ( + assert_fn = $assert_fn:ident, + lhs = {$lhs:ty $(, $lhs_tail:ty)*}, + rhs = {$($rhs:ty),*}, + ) => { + assert_operator_defined_for! { + assert_fn = $assert_fn, + lhs = $lhs, + rhs = {$($rhs),*}, + } + assert_operator_defined_for! { + assert_fn = $assert_fn, + lhs = {$($lhs_tail),*}, + rhs = {$($rhs),*}, + } + }; + ( + assert_fn = $assert_fn:ident, + lhs = $lhs:ty, + rhs = {$($rhs:ty),*}, + ) => { + $($assert_fn::());* + }; + } + + /// Function asserts that P2 can be added to P1 (ie. P1 + P2) and result is Point. + /// If any condition doesn't meet, function won't compile. + #[allow(dead_code)] + fn assert_point_addition_defined() + where + P1: ops::Add>, + E: Curve, + { + // no-op + } + + #[test] + fn test_point_addition_defined() { + fn _curve() { + assert_operator_defined_for! { + assert_fn = assert_point_addition_defined, + lhs = {Point, &Point, Generator}, + rhs = {Point, &Point, Generator}, + } + } + } + + /// Function asserts that P2 can be subtracted from P1 (ie. P1 - P2) and result is Point. + /// If any condition doesn't meet, function won't compile. + #[allow(dead_code)] + fn assert_point_subtraction_defined() + where + P1: ops::Sub>, + E: Curve, + { + // no-op + } + + #[test] + fn test_point_subtraction_defined() { + fn _curve() { + assert_operator_defined_for! { + assert_fn = assert_point_subtraction_defined, + lhs = {Point, &Point, Generator}, + rhs = {Point, &Point, Generator}, + } + } + } + + /// Function asserts that M can be multiplied by N (ie. M * N) and result is Point. + /// If any condition doesn't meet, function won't compile. + #[allow(dead_code)] + fn assert_point_multiplication_defined() + where + M: ops::Mul>, + E: Curve, + { + // no-op + } + + #[test] + fn test_point_multiplication_defined() { + fn _curve() { + assert_operator_defined_for! { + assert_fn = assert_point_multiplication_defined, + lhs = {Point, &Point, Generator}, + rhs = {Scalar, &Scalar}, + } + + // and vice-versa + + assert_operator_defined_for! { + assert_fn = assert_point_multiplication_defined, + lhs = {Scalar, &Scalar}, + rhs = {Point, &Point, Generator}, + } + } + } + + /// Function asserts that S2 can be added to S1 (ie. S1 + S2) and result is Scalar. + /// If any condition doesn't meet, function won't compile. + #[allow(dead_code)] + fn assert_scalars_addition_defined() + where + S1: ops::Add>, + E: Curve, + { + // no-op + } + + #[test] + fn test_scalars_addition_defined() { + fn _curve() { + assert_operator_defined_for! { + assert_fn = assert_scalars_addition_defined, + lhs = {Scalar, Scalar}, + rhs = {Scalar, Scalar}, + } + } + } + + /// Function asserts that S2 can be subtracted from S1 (ie. S1 - S2) and result is Scalar. + /// If any condition doesn't meet, function won't compile. + #[allow(dead_code)] + fn assert_scalars_subtraction_defined() + where + S1: ops::Sub>, + E: Curve, + { + // no-op + } + + #[test] + fn test_scalars_subtraction_defined() { + fn _curve() { + assert_operator_defined_for! { + assert_fn = assert_scalars_subtraction_defined, + lhs = {Scalar, Scalar}, + rhs = {Scalar, Scalar}, + } + } + } + + /// Function asserts that S1 can be multiplied by S2 (ie. S1 * S2) and result is Scalar. + /// If any condition doesn't meet, function won't compile. + #[allow(dead_code)] + fn assert_scalars_multiplication_defined() + where + S1: ops::Mul>, + E: Curve, + { + // no-op + } + + #[test] + fn test_scalars_multiplication_defined() { + fn _curve() { + assert_operator_defined_for! { + assert_fn = assert_scalars_multiplication_defined, + lhs = {Scalar, Scalar}, + rhs = {Scalar, Scalar}, + } + } + } +} diff --git a/src/elliptic/curves/wrappers/encoded_point.rs b/src/elliptic/curves/wrappers/encoded_point.rs new file mode 100644 index 00000000..fc43b7b7 --- /dev/null +++ b/src/elliptic/curves/wrappers/encoded_point.rs @@ -0,0 +1,21 @@ +use std::ops::Deref; + +use crate::elliptic::curves::{Curve, ECPoint}; + +/// Point encoded in (un)compressed form +pub struct EncodedPoint(pub(super) EncodedPointChoice); + +pub(super) enum EncodedPointChoice { + Compressed(::CompressedPoint), + Uncompressed(::UncompressedPoint), +} + +impl Deref for EncodedPoint { + type Target = [u8]; + fn deref(&self) -> &[u8] { + match &self.0 { + EncodedPointChoice::Compressed(bytes) => bytes.as_ref(), + EncodedPointChoice::Uncompressed(bytes) => bytes.as_ref(), + } + } +} diff --git a/src/elliptic/curves/wrappers/encoded_scalar.rs b/src/elliptic/curves/wrappers/encoded_scalar.rs new file mode 100644 index 00000000..a0319fc3 --- /dev/null +++ b/src/elliptic/curves/wrappers/encoded_scalar.rs @@ -0,0 +1,29 @@ +use std::ops::Deref; + +use crate::elliptic::curves::{Curve, ECScalar, Scalar}; + +/// Encoded scalar +pub struct EncodedScalar { + bytes: ::ScalarBytes, +} + +impl Deref for EncodedScalar { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.bytes.as_ref() + } +} + +impl<'s, E: Curve> From<&'s Scalar> for EncodedScalar { + fn from(s: &'s Scalar) -> Self { + Self { + bytes: s.as_raw().serialize(), + } + } +} + +impl From> for EncodedScalar { + fn from(s: Scalar) -> Self { + Self::from(&s) + } +} diff --git a/src/elliptic/curves/wrappers/error.rs b/src/elliptic/curves/wrappers/error.rs new file mode 100644 index 00000000..015d660c --- /dev/null +++ b/src/elliptic/curves/wrappers/error.rs @@ -0,0 +1,75 @@ +use std::fmt; + +use thiserror::Error; + +use crate::elliptic::curves::traits::*; + +#[derive(Debug, Error, Clone, PartialEq)] +#[error("invalid point (point order ≠ group order)")] +pub struct MismatchedPointOrder(()); + +impl MismatchedPointOrder { + pub(super) fn new() -> Self { + MismatchedPointOrder(()) + } +} + +#[derive(Debug, Error)] +pub enum PointFromBytesError { + #[error("failed to deserialize the point")] + DeserializationError, + #[error("invalid point ({0})")] + InvalidPoint(MismatchedPointOrder), +} + +#[derive(Debug, Error)] +pub enum PointFromCoordsError { + #[error("{}", NotOnCurve)] + NotOnCurve, + #[error("invalid point ({0})")] + InvalidPoint(MismatchedPointOrder), +} + +/// Indicates that conversion or computation failed due to occurred zero point +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct ZeroPointError(()); + +impl ZeroPointError { + pub(super) fn new() -> Self { + ZeroPointError(()) + } +} + +impl fmt::Display for ZeroPointError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "nonzero check failed: point is zero") + } +} + +impl std::error::Error for ZeroPointError {} + +/// Indicates that conversion or computation failed due to occurred zero scalar +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct ZeroScalarError(()); + +impl ZeroScalarError { + pub(super) fn new() -> Self { + ZeroScalarError(()) + } +} + +impl fmt::Display for ZeroScalarError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "nonzero check failed: scalar is zero") + } +} + +impl std::error::Error for ZeroScalarError {} + +#[derive(Error, Debug)] +pub enum InvalidPoint { + #[error("x,y correspond to zero point")] + ZeroPoint, + #[error("{}", MismatchedPointOrder(()))] + MismatchedPointOrder, +} diff --git a/src/elliptic/curves/wrappers/generator.rs b/src/elliptic/curves/wrappers/generator.rs new file mode 100644 index 00000000..356611dc --- /dev/null +++ b/src/elliptic/curves/wrappers/generator.rs @@ -0,0 +1,84 @@ +use std::marker::PhantomData; +use std::ops::Deref; + +use crate::elliptic::curves::traits::*; + +use super::Point; + +/// Elliptic curve generator +/// +/// Holds internally a static reference on curve generator. Can be used in arithmetic interchangeably +/// as [&Point\](Point). +/// +/// You can convert the generator into `Point` and `&'static Point` using +/// [`to_point`](Self::to_point) and [`as_point`](Self::as_point) +/// methods respectively. +/// +/// ## Example +/// +/// ```rust +/// # use curv::elliptic::curves::{Point, Scalar, Secp256k1}; +/// let s = Scalar::::random(); +/// let g = Point::::generator(); +/// let result: Point = s * g; +/// ``` +/// +/// ## Performance +/// +/// Generator multiplication is often more efficient than regular point multiplication, so avoid +/// converting generator into the `Point` as long as it's possible: +/// +/// ```rust +/// # use curv::elliptic::curves::{Point, Scalar, Secp256k1, Generator}; +/// let s: Scalar = Scalar::random(); +/// // Generator multiplication: +/// let g: Generator = Point::generator(); +/// let p1: Point = g * &s; +/// // Point multiplication: +/// let g: Point = g.to_point(); +/// let p2: Point = g * &s; +/// // Result will be the same, but generator multiplication is usually faster +/// assert_eq!(p1, p2); +/// ``` +pub struct Generator { + _ph: PhantomData<&'static E::Point>, +} + +impl Default for Generator { + fn default() -> Self { + Self { _ph: PhantomData } + } +} + +impl Generator { + /// Clones generator point, returns `Point` + pub fn to_point(self) -> Point { + Point::from(self) + } + + /// Converts generator into `&'static Point` + pub fn as_point(self) -> &'static Point { + // Safety: generator point expected to have correct order + unsafe { Point::from_raw_ref_unchecked(self.as_raw()) } + } + + /// Returns a reference to low-level point implementation + pub fn as_raw(self) -> &'static E::Point { + E::Point::generator() + } +} + +impl Clone for Generator { + fn clone(&self) -> Self { + Self { _ph: PhantomData } + } +} + +impl Copy for Generator {} + +impl Deref for Generator { + type Target = Point; + fn deref(&self) -> &Point { + self.as_point() + } +} diff --git a/src/elliptic/curves/wrappers/mod.rs b/src/elliptic/curves/wrappers/mod.rs new file mode 100644 index 00000000..9f63f382 --- /dev/null +++ b/src/elliptic/curves/wrappers/mod.rs @@ -0,0 +1,13 @@ +mod arithmetic; +mod encoded_point; +mod encoded_scalar; +pub mod error; +mod generator; +mod point; +mod scalar; +mod serde_support; + +pub use self::{ + encoded_point::EncodedPoint, encoded_scalar::EncodedScalar, generator::Generator, point::Point, + scalar::Scalar, +}; diff --git a/src/elliptic/curves/wrappers/point.rs b/src/elliptic/curves/wrappers/point.rs new file mode 100644 index 00000000..a5eb69be --- /dev/null +++ b/src/elliptic/curves/wrappers/point.rs @@ -0,0 +1,309 @@ +use std::{fmt, iter}; + +use crate::elliptic::curves::traits::*; +use crate::BigInt; + +use super::{ + error::{MismatchedPointOrder, PointFromBytesError, PointFromCoordsError, ZeroPointError}, + EncodedPoint, Generator, +}; +use crate::elliptic::curves::wrappers::encoded_point::EncodedPointChoice; + +/// Elliptic point of a [group order](super::Scalar::group_order), or a zero point +/// +/// ## Guarantees +/// +/// * On curve +/// +/// Any instance of `Point` is guaranteed to belong to curve `E`, i.e. its coordinates must +/// satisfy curve equations +/// * Point order equals to [group order](super::Scalar::group_order) (unless it's zero point) +/// +/// I.e. denoting `q = group_order`, following predicate is always true: +/// `P = O ∨ qP = O ∧ forall 0 < s < q. sP ≠ O` +/// +/// ## Security +/// +/// Validate points if they come from untrusted source. Mistakenly used zero point might break security +/// of cryptoalgorithm. Use [ensure_nonzero](Point::ensure_nonzero) to validate them. +/// +/// ```rust +/// # use curv::elliptic::curves::{Point, Curve, ZeroPointError}; +/// # struct T; +/// fn process_input(point: &Point) -> Result { +/// point.ensure_nonzero()?; +/// // ... process the point +/// # Ok(T) +/// } +/// ``` +/// +/// ## Arithmetics +/// +/// You can add, subtract two points, or multiply point at scalar: +/// +/// ```rust +/// # use curv::elliptic::curves::{Point, Scalar, Secp256k1}; +/// fn expression( +/// a: Point, +/// b: Point, +/// c: Scalar, +/// ) -> Point { +/// a + b * c +/// } +/// ``` +#[repr(transparent)] +pub struct Point { + raw_point: E::Point, +} + +impl Point { + /// Ensures that `self` is not zero, returns `Err(_)` otherwise + pub fn ensure_nonzero(&self) -> Result<(), ZeroPointError> { + if self.is_zero() { + Err(ZeroPointError::new()) + } else { + Ok(()) + } + } + /// Curve generator + /// + /// Returns a structure holding a static reference on actual value (in most cases referenced + /// value is fine). Use [`.to_point()`](Generator::to_point) if you need to take it by value. + pub fn generator() -> Generator { + Generator::default() + } + + /// Curve second generator + /// + /// We provide an alternative generator value and prove that it was picked randomly. + /// + /// Returns a static reference to actual point — in most cases referenced value is fine. Use + /// `.clone()` to take it by value. + pub fn base_point2() -> &'static Self { + let p = E::Point::base_point2(); + // Safety: we proof that base_point2 has correct order + unsafe { Self::from_raw_ref_unchecked(p) } + } + + /// Constructs zero point + /// + /// Zero point (or curve neutral element) is usually denoted as `O`. Its property: `forall A. A + O = A`. + /// + /// Weierstrass and Montgomery curves employ special "point at infinity" that represent a neutral + /// element, such points don't have coordinates (i.e. [from_coords], [x_coord], [y_coord] return + /// `None`). Edwards curves' neutral element has coordinates. + /// + /// [from_coords]: Self::from_coords + /// [x_coord]: Self::x_coord + /// [y_coord]: Self::y_coord + pub fn zero() -> Self { + // Safety: `self` can be constructed to hold a zero point + unsafe { Self::from_raw_unchecked(E::Point::zero()) } + } + + /// Checks whether point is zero + pub fn is_zero(&self) -> bool { + self.as_raw().is_zero() + } + + /// Returns point coordinates + /// + /// Point might not have coordinates (specifically, "point at infinity" doesn't), in this case + /// `None` is returned. Also, some curve libraries do not expose point coordinates (eg. see + /// [Ristretto] curve implementation notes). + /// + /// [Ristretto]: crate::elliptic::curves::Ristretto + pub fn coords(&self) -> Option { + self.as_raw().coords() + } + + /// Returns point x coordinate + /// + /// See [coords](Self::coords) method that retrieves both x and y at once. + pub fn x_coord(&self) -> Option { + self.as_raw().x_coord() + } + + /// Returns point y coordinate + /// + /// See [coords](Self::coords) method that retrieves both x and y at once. + pub fn y_coord(&self) -> Option { + self.as_raw().y_coord() + } + + /// Constructs a point from its coordinates, returns error if coordinates don't satisfy + /// curve equation or if point has invalid order + pub fn from_coords(x: &BigInt, y: &BigInt) -> Result { + let raw_point = E::Point::from_coords(x, y) + .map_err(|_: NotOnCurve| PointFromCoordsError::NotOnCurve)?; + Self::from_raw(raw_point).map_err(PointFromCoordsError::InvalidPoint) + } + + /// Tries to parse a point in (un)compressed form + /// + /// Whether it's in compressed or uncompressed form will be deduced from its length + pub fn from_bytes(bytes: &[u8]) -> Result { + let p = E::Point::deserialize(bytes) + .map_err(|_: DeserializationError| PointFromBytesError::DeserializationError)?; + Self::from_raw(p).map_err(PointFromBytesError::InvalidPoint) + } + + /// Serializes a point in (un)compressed form + pub fn to_bytes(&self, compressed: bool) -> EncodedPoint { + if compressed { + EncodedPoint(EncodedPointChoice::Compressed( + self.as_raw().serialize_compressed(), + )) + } else { + EncodedPoint(EncodedPointChoice::Uncompressed( + self.as_raw().serialize_uncompressed(), + )) + } + } + + /// Constructs a `Point` from low-level [ECPoint] implementor + /// + /// Returns error if point is not valid. Valid point is either a zero point, or a point of + /// [group order]. + /// + /// Typically, you don't need to use this constructor. See [generator](Point::generator), + /// [base_point2](Point::base_point2), [from_coords](Self::from_coords), [from_bytes](Self::from_bytes) + /// constructors, and `From` and `TryFrom` traits implemented for `Point`. + /// + /// [ECPoint]: crate::elliptic::curves::ECPoint + /// [group order]: crate::elliptic::curves::ECScalar::group_order + pub fn from_raw(raw_point: E::Point) -> Result { + if raw_point.is_zero() || raw_point.check_point_order_equals_group_order() { + Ok(Self { raw_point }) + } else { + Err(MismatchedPointOrder::new()) + } + } + + /// Constructs a `&Point` from reference to low-level [ECPoint] implementation + /// + /// Returns error if point is not valid. Valid point is either a zero point, or a point of + /// [group order]. + /// + /// Typically, you don't need to use this constructor. See [generator](Point::generator), + /// [base_point2](Point::base_point2) constructors. + /// + /// [ECPoint]: crate::elliptic::curves::ECPoint + /// [group order]: crate::elliptic::curves::ECScalar::group_order + pub fn from_raw_ref(raw_point: &E::Point) -> Result<&Self, MismatchedPointOrder> { + if raw_point.is_zero() || raw_point.check_point_order_equals_group_order() { + // Safety: we checked that point is either zero or has correct order + let reference = unsafe { Self::from_raw_ref_unchecked(raw_point) }; + Ok(reference) + } else { + Err(MismatchedPointOrder::new()) + } + } + + /// Constructs a `Point` from low-level [ECPoint] implementor + /// + /// Unsafe equivalent of [from_raw](Self::from_raw). It debug asserts that given `raw_point` is + /// valid (the assertion is optimized out in release builds by default). + /// + /// # Safety + /// + /// You must guarantee that either point order is equal to curve [group order] or it's a zero point. + /// To perform this check, you may use + /// [ECPoint::check_point_order_equals_group_order][check_point_order_equals_group_order] + /// and [ECPoint::is_zero][is_zero] methods. + /// + /// [ECPoint]: crate::elliptic::curves::ECPoint + /// [group order]: crate::elliptic::curves::ECScalar::group_order + /// [check_point_order_equals_group_order]: crate::elliptic::curves::ECPoint::check_point_order_equals_group_order + /// [is_zero]: crate::elliptic::curves::ECPoint::is_zero + pub unsafe fn from_raw_unchecked(raw_point: E::Point) -> Self { + debug_assert!(raw_point.is_zero() || raw_point.check_point_order_equals_group_order()); + Self { raw_point } + } + + /// Constructs a `Point` from reference to low-level [ECPoint] implementor + /// + /// Unsafe equivalent of [from_raw_ref](Self::from_raw_ref). It debug asserts that given `raw_point` is + /// valid (the assertion is optimized out in release builds by default). + /// + /// # Safety + /// + /// You must guarantee that either point order is equal to curve [group order] or it's a zero point. + /// To perform this check, you may use + /// [ECPoint::check_point_order_equals_group_order][check_point_order_equals_group_order] + /// and [ECPoint::is_zero][is_zero] methods. + /// + /// [ECPoint]: crate::elliptic::curves::ECPoint + /// [group order]: crate::elliptic::curves::ECScalar::group_order + /// [check_point_order_equals_group_order]: crate::elliptic::curves::ECPoint::check_point_order_equals_group_order + /// [is_zero]: crate::elliptic::curves::ECPoint::is_zero + pub unsafe fn from_raw_ref_unchecked(raw_point: &E::Point) -> &Self { + debug_assert!(raw_point.is_zero() || raw_point.check_point_order_equals_group_order()); + // Safety: Self is repr(transparent) wrapper over E::Point => cast is sound + &*(raw_point as *const E::Point as *const Self) + } + + /// Returns a reference to low-level point implementation + /// + /// Typically, you don't need to work with `ECPoint` trait directly. `Point` wrapper + /// provides convenient utilities around it: it implements arithmetic operators, (de)serialization + /// traits, various getters (like [`.coords()`](Self::coords). If you believe that some functionality + /// is missing, please [open an issue](https://github.com/ZenGo-X/curv). + pub fn as_raw(&self) -> &E::Point { + &self.raw_point + } + + /// Converts a point into inner low-level point implementation + /// + /// Typically, you don't need to work with `ECPoint` trait directly. `Point` wraps `ECPoint` + /// and provides convenient utilities around it: it implements arithmetic operators, (de)serialization + /// traits, various getters (like [`.coords()`](Self::coords)). If you believe that some functionality + /// is missing, please [open an issue](https://github.com/ZenGo-X/curv). + pub fn into_raw(self) -> E::Point { + self.raw_point + } +} + +impl PartialEq for Point { + fn eq(&self, other: &Self) -> bool { + self.raw_point.eq(&other.raw_point) + } +} + +impl PartialEq> for Point { + fn eq(&self, other: &Generator) -> bool { + self.as_raw().eq(other.as_raw()) + } +} + +impl Clone for Point { + fn clone(&self) -> Self { + // Safety: self is guaranteed to have correct order + unsafe { Point::from_raw_unchecked(self.as_raw().clone()) } + } +} + +impl fmt::Debug for Point { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.raw_point.fmt(f) + } +} + +impl From> for Point { + fn from(g: Generator) -> Self { + // Safety: curve generator order must be equal to group_order + unsafe { Point::from_raw_unchecked(g.as_raw().clone()) } + } +} + +impl iter::Sum for Point { + fn sum>(iter: I) -> Self { + iter.fold(Point::zero(), |acc, p| acc + p) + } +} + +impl<'p, E: Curve> iter::Sum<&'p Point> for Point { + fn sum>>(iter: I) -> Self { + iter.fold(Point::zero(), |acc, p| acc + p) + } +} diff --git a/src/elliptic/curves/wrappers/scalar.rs b/src/elliptic/curves/wrappers/scalar.rs new file mode 100644 index 00000000..3b782603 --- /dev/null +++ b/src/elliptic/curves/wrappers/scalar.rs @@ -0,0 +1,208 @@ +use std::{fmt, iter}; + +use crate::elliptic::curves::traits::{Curve, ECScalar}; +use crate::elliptic::curves::wrappers::encoded_scalar::EncodedScalar; +use crate::elliptic::curves::{DeserializationError, ZeroScalarError}; +use crate::BigInt; + +/// Scalar value in a prime field +/// +/// ## Guarantees +/// +/// * Modulus group order +/// +/// Denoting [group order](Self::group_order) as `n`, any instance `s` of `Scalar` is guaranteed +/// to be non-negative integer modulo `n`: `0 <= s < n` +/// +/// ## Arithmetics +/// +/// Supported operations: +/// * Unary: you can [invert](Self::invert) and negate a scalar +/// * Binary: you can add, subtract, and multiply two scalars +/// +/// ### Example +/// +/// ```rust +/// # use curv::elliptic::curves::{Scalar, Secp256k1}; +/// fn expression( +/// a: &Scalar, +/// b: &Scalar, +/// c: &Scalar +/// ) -> Scalar { +/// a + b * c +/// } +/// ``` +#[repr(transparent)] +pub struct Scalar { + raw_scalar: E::Scalar, +} + +impl Scalar { + /// Ensures that `self` is not zero, returns `Err(_)` otherwise + pub fn ensure_nonzero(&self) -> Result<(), ZeroScalarError> { + if self.is_zero() { + Err(ZeroScalarError::new()) + } else { + Ok(()) + } + } + + /// Samples a random nonzero scalar + pub fn random() -> Self { + loop { + let s = E::Scalar::random(); + if !s.is_zero() { + break Scalar::from_raw(s); + } + } + } + + /// Constructs zero scalar + pub fn zero() -> Self { + Self::from_raw(E::Scalar::zero()) + } + + /// Checks if a scalar is zero + pub fn is_zero(&self) -> bool { + self.as_raw().is_zero() + } + + /// Converts a scalar to [BigInt] + pub fn to_bigint(&self) -> BigInt { + self.as_raw().to_bigint() + } + + /// Constructs a scalar `n % curve_order` from given `n` + pub fn from_bigint(n: &BigInt) -> Self { + Self::from_raw(E::Scalar::from_bigint(n)) + } + + /// Serializes a scalar to bytes + pub fn to_bytes(&self) -> EncodedScalar { + EncodedScalar::from(self) + } + + /// Constructs a scalar from bytes + pub fn from_bytes(bytes: &[u8]) -> Result { + ECScalar::deserialize(bytes).map(Self::from_raw) + } + + /// Returns an order of generator point + pub fn group_order() -> &'static BigInt { + E::Scalar::group_order() + } + + /// Returns inversion `self^-1 mod group_order`, or None if `self` is zero + pub fn invert(&self) -> Option { + self.as_raw().invert().map(Self::from_raw) + } + + /// Constructs a `Scalar` from low-level [ECScalar] implementor + /// + /// Typically, you don't need to use this constructor. See [random](Self::random), + /// [from_bigint](Self::from_bigint) constructors, and `From`, `TryFrom` traits implemented + /// for `Scalar`. + /// + /// [ECScalar]: crate::elliptic::curves::ECScalar + pub fn from_raw(raw_scalar: E::Scalar) -> Self { + Self { raw_scalar } + } + + /// Returns a reference to low-level scalar implementation + /// + /// Typically, you don't need to work with `ECScalar` trait directly. `Scalar` wraps `ECScalar` + /// and provides convenient utilities around it: it implements arithmetic operators, (de)serialization + /// traits, etc. If you believe that some functionality is missing, please + /// [open an issue](https://github.com/ZenGo-X/curv). + pub fn as_raw(&self) -> &E::Scalar { + &self.raw_scalar + } + + /// Converts a scalar into inner low-level scalar implementation + /// + /// Typically, you don't need to work with `ECScalar` trait directly. `Scalar` wraps `ECScalar` + /// and provides convenient utilities around it: it implements arithmetic operators, (de)serialization + /// traits, etc. If you believe that some functionality is missing, please + /// [open an issue](https://github.com/ZenGo-X/curv). + pub fn into_raw(self) -> E::Scalar { + self.raw_scalar + } +} + +impl Clone for Scalar { + fn clone(&self) -> Self { + Self::from_raw(self.as_raw().clone()) + } +} + +impl fmt::Debug for Scalar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.as_raw().fmt(f) + } +} + +impl PartialEq for Scalar { + fn eq(&self, other: &Self) -> bool { + self.as_raw().eq(other.as_raw()) + } +} + +impl From for Scalar { + fn from(n: u16) -> Self { + Self::from(&BigInt::from(n)) + } +} + +impl From for Scalar { + fn from(n: u32) -> Self { + Self::from(&BigInt::from(n)) + } +} + +impl From for Scalar { + fn from(n: u64) -> Self { + Self::from(&BigInt::from(n)) + } +} + +impl From for Scalar { + fn from(n: i32) -> Self { + Self::from(&BigInt::from(n)) + } +} + +impl From<&BigInt> for Scalar { + fn from(n: &BigInt) -> Self { + Scalar::from_raw(E::Scalar::from_bigint(n)) + } +} + +impl From for Scalar { + fn from(n: BigInt) -> Self { + Self::from(&n) + } +} + +impl iter::Sum for Scalar { + fn sum>(iter: I) -> Self { + iter.fold(Scalar::zero(), |acc, s| acc + s) + } +} + +impl<'s, E: Curve> iter::Sum<&'s Scalar> for Scalar { + fn sum>>(iter: I) -> Self { + iter.fold(Scalar::zero(), |acc, s| acc + s) + } +} + +impl iter::Product for Scalar { + fn product>(iter: I) -> Self { + iter.fold(Scalar::from(1), |acc, s| acc * s) + } +} + +impl<'s, E: Curve> iter::Product<&'s Scalar> for Scalar { + fn product>>(iter: I) -> Self { + iter.fold(Scalar::from(1), |acc, s| acc * s) + } +} diff --git a/src/elliptic/curves/wrappers/serde_support.rs b/src/elliptic/curves/wrappers/serde_support.rs new file mode 100644 index 00000000..45d19d7c --- /dev/null +++ b/src/elliptic/curves/wrappers/serde_support.rs @@ -0,0 +1,345 @@ +use std::fmt; +use std::marker::PhantomData; + +use serde::de::{Error, MapAccess, Visitor}; +use serde::ser::SerializeStruct; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_bytes::Bytes; + +use crate::elliptic::curves::{Curve, Point, Scalar}; + +// --- +// --- Point (de)serialization +// --- + +impl<'p, E: Curve> Serialize for Point { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_struct("Point", 2)?; + s.serialize_field("curve", E::CURVE_NAME)?; + s.serialize_field( + "point", + // Serializes bytes efficiently + Bytes::new(&self.to_bytes(true)), + )?; + s.end() + } +} + +impl<'de, E: Curve> Deserialize<'de> for Point { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PointVisitor(PhantomData); + + impl<'de, E: Curve> Visitor<'de> for PointVisitor { + type Value = Point; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "point of {} curve", E::CURVE_NAME) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut curve_name: Option> = None; + let mut point: Option> = None; + + while let Some(key) = map.next_key()? { + match key { + PointField::Curve => { + if curve_name.is_some() { + return Err(A::Error::duplicate_field("curve_name")); + } + curve_name = Some(map.next_value()?) + } + PointField::Point => { + if point.is_some() { + return Err(A::Error::duplicate_field("point")); + } + point = Some(map.next_value()?) + } + } + } + let _curve_name = + curve_name.ok_or_else(|| A::Error::missing_field("curve_name"))?; + let point = point.ok_or_else(|| A::Error::missing_field("point"))?; + Ok(point.0) + } + } + + deserializer.deserialize_struct("Point", &["curve", "point"], PointVisitor(PhantomData)) + } +} + +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "snake_case")] +enum PointField { + Curve, + Point, +} + +/// Efficient guard for asserting that deserialized `&str`/`String` is `E::CURVE_NAME` +struct CurveNameGuard(PhantomData); + +impl<'de, E: Curve> Deserialize<'de> for CurveNameGuard { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CurveNameVisitor(PhantomData); + + impl<'de, E: Curve> Visitor<'de> for CurveNameVisitor { + type Value = (); + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "curve name (constrained to be '{}')", E::CURVE_NAME) + } + + fn visit_str(self, v: &str) -> Result + where + Err: Error, + { + if v == E::CURVE_NAME { + Ok(()) + } else { + Err(Err::custom(format!( + "belongs to {} curve, expected {} curve", + v, + E::CURVE_NAME + ))) + } + } + } + + deserializer + .deserialize_str(CurveNameVisitor(PhantomData::)) + .map(|_| CurveNameGuard(PhantomData)) + } +} + +struct PointFromBytes(Point); + +impl<'de, E: Curve> Deserialize<'de> for PointFromBytes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PointBytesVisitor(PhantomData); + + impl<'de, E: Curve> Visitor<'de> for PointBytesVisitor { + type Value = Point; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "point of {} curve", E::CURVE_NAME) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + Err: Error, + { + Point::from_bytes(v).map_err(|e| Err::custom(format!("invalid point: {}", e))) + } + } + + deserializer + .deserialize_bytes(PointBytesVisitor(PhantomData)) + .map(PointFromBytes) + } +} + +// --- +// --- Scalar (de)serialization +// --- + +impl Serialize for Scalar { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_struct("Scalar", 2)?; + s.serialize_field("curve", E::CURVE_NAME)?; + s.serialize_field( + "scalar", + // Serializes bytes efficiently + Bytes::new(&self.to_bytes()), + )?; + s.end() + } +} + +impl<'de, E: Curve> Deserialize<'de> for Scalar { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ScalarVisitor(PhantomData); + + impl<'de, E: Curve> Visitor<'de> for ScalarVisitor { + type Value = Scalar; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "scalar of {} curve", E::CURVE_NAME) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut curve_name: Option> = None; + let mut scalar: Option> = None; + + while let Some(key) = map.next_key()? { + match key { + ScalarField::Curve => { + if curve_name.is_some() { + return Err(A::Error::duplicate_field("curve_name")); + } + curve_name = Some(map.next_value()?) + } + ScalarField::Scalar => { + if scalar.is_some() { + return Err(A::Error::duplicate_field("scalar")); + } + scalar = Some(map.next_value()?) + } + } + } + let _curve_name = + curve_name.ok_or_else(|| A::Error::missing_field("curve_name"))?; + let scalar = scalar.ok_or_else(|| A::Error::missing_field("scalar"))?; + Ok(scalar.0) + } + } + + deserializer.deserialize_struct("Scalar", &["curve", "scalar"], ScalarVisitor(PhantomData)) + } +} + +struct ScalarFromBytes(Scalar); + +impl<'de, E: Curve> Deserialize<'de> for ScalarFromBytes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ScalarBytesVisitor(PhantomData); + + impl<'de, E: Curve> Visitor<'de> for ScalarBytesVisitor { + type Value = Scalar; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "scalar value of {} curve", E::CURVE_NAME) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + Err: Error, + { + Scalar::from_bytes(v).map_err(|_| Err::custom("invalid scalar")) + } + } + + deserializer + .deserialize_bytes(ScalarBytesVisitor(PhantomData)) + .map(ScalarFromBytes) + } +} + +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "snake_case")] +enum ScalarField { + Curve, + Scalar, +} + +#[cfg(test)] +mod serde_tests { + use serde_test::{assert_de_tokens_error, assert_tokens, Token::*}; + + use crate::elliptic::curves::*; + use crate::test_for_all_curves; + + test_for_all_curves!(test_serde_point); + fn test_serde_point() { + let random_point = Point::::generator() * Scalar::random(); + for point in [Point::zero(), random_point] { + println!("Point: {:?}", point); + let bytes = point.to_bytes(true).to_vec(); + let tokens = vec![ + Struct { + name: "Point", + len: 2, + }, + Str("curve"), + Str(E::CURVE_NAME), + Str("point"), + Bytes(bytes.leak()), + StructEnd, + ]; + assert_tokens(&point, &tokens); + } + } + + test_for_all_curves!(test_serde_scalar); + fn test_serde_scalar() { + for scalar in [Scalar::::zero(), Scalar::random()] { + println!("Scalar: {:?}", scalar); + let bytes = scalar.to_bytes().to_vec(); + let tokens = vec![ + Struct { + name: "Scalar", + len: 2, + }, + Str("curve"), + Str(E::CURVE_NAME), + Str("scalar"), + Bytes(bytes.leak()), + StructEnd, + ]; + assert_tokens(&scalar, &tokens); + } + } + + test_for_all_curves!(doesnt_deserialize_point_from_different_curve); + fn doesnt_deserialize_point_from_different_curve() { + let tokens = vec![ + Struct { + name: "Point", + len: 2, + }, + Str("curve"), + Str("%not_existing%"), + ]; + assert_de_tokens_error::>( + &tokens, + &format!( + "belongs to %not_existing% curve, expected {} curve", + E::CURVE_NAME + ), + ) + } + + test_for_all_curves!(doesnt_deserialize_scalar_from_different_curve); + fn doesnt_deserialize_scalar_from_different_curve() { + let tokens = vec![ + Struct { + name: "Scalar", + len: 2, + }, + Str("curve"), + Str("%not_existing%"), + ]; + assert_de_tokens_error::>( + &tokens, + &format!( + "belongs to %not_existing% curve, expected {} curve", + E::CURVE_NAME + ), + ) + } +} diff --git a/src/lib.rs b/src/lib.rs index 5356d21d..ef59c4f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,27 +37,32 @@ macro_rules! test_for_all_curves { #[test] $($attrs)* fn [<$fn _secp256k1>]() { - $fn::() + $fn::() } #[test] $($attrs)* - fn [<$fn _ristretto>]() { - $fn::() + fn [<$fn _p256>]() { + $fn::() } #[test] $($attrs)* fn [<$fn _ed25519>]() { - $fn::() + $fn::() } #[test] $($attrs)* - fn [<$fn _bls12_381>]() { - $fn::() + fn [<$fn _ristretto>]() { + $fn::() } #[test] $($attrs)* - fn [<$fn _p256>]() { - $fn::() + fn [<$fn _bls12_381_1>]() { + $fn::() + } + #[test] + $($attrs)* + fn [<$fn _bls12_381_2>]() { + $fn::() } } };