From 83f42fb5d90758fade9ea721b0719d9c4f6aa581 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Fri, 26 May 2023 18:08:58 -0600 Subject: [PATCH 01/11] clean up header files in blst_include.h --- crypto/blst_include.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crypto/blst_include.h b/crypto/blst_include.h index e408c9c0c70..89966463c61 100644 --- a/crypto/blst_include.h +++ b/crypto/blst_include.h @@ -4,12 +4,10 @@ // extra tools to use BLST low level that are needed by the Flow crypto library // eventually this file would replace blst.h -#include "bls12381_utils.h" #include "point.h" #include "fields.h" #include "consts.h" #include "errors.h" -#include "sha256.h" // types used by the Flow crypto library that are imported from BLST // these type definitions are used as an abstraction from BLST internal types @@ -66,8 +64,8 @@ typedef _Bool bool; /* it's assumed that cgo calls modern enough compiler */ // are represented as a little endian vector of limbs. // `Fr` is equivalent to type `vec256` (used internally by BLST for F_r elements). // `Fr` is defined as a struct to be exportable through cgo to the Go layer. -#define R_BITS 255 -typedef struct {limb_t limbs[(R_BITS+63)/64];} Fr; // TODO: use Fr_LIMBS +#define R_BITS 255 // equal to Fr_bits in bls12381_utils.h +typedef struct {limb_t limbs[(R_BITS+63)/64];} Fr; // field elements F_p // F_p elements are represented as big numbers reduced modulo `p`. Big numbers From ec2ceb400891ae67bbc9deb38835901bda359513 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Fri, 26 May 2023 19:18:38 -0600 Subject: [PATCH 02/11] update boolean usage from bool_t to C type bool --- crypto/bls.go | 2 +- crypto/bls12381_utils.c | 28 ++++++++-------- crypto/bls12381_utils.go | 16 ++++----- crypto/bls12381_utils.h | 27 ++++++++------- crypto/bls_thresholdsign_core.c | 6 ++-- crypto/bls_thresholdsign_include.h | 2 +- crypto/blst_include.h | 54 +++--------------------------- crypto/dkg_core.c | 2 +- crypto/dkg_feldmanvss.go | 4 +-- crypto/dkg_feldmanvssq.go | 5 +-- crypto/dkg_include.h | 2 +- 11 files changed, 54 insertions(+), 94 deletions(-) diff --git a/crypto/bls.go b/crypto/bls.go index c8650c9dc60..3206a29cdf9 100644 --- a/crypto/bls.go +++ b/crypto/bls.go @@ -347,7 +347,7 @@ func (a *blsBLS12381Algo) decodePublicKey(publicKeyBytes []byte) (PublicKey, err } // membership check in G2 - if C.E2_in_G2((*C.E2)(&pk.point)) == (C.ulonglong)(0) { + if !bool(C.E2_in_G2((*C.E2)(&pk.point))) { return nil, invalidInputsErrorf("input key is infinity or does not encode a BLS12-381 point in the valid group") } diff --git a/crypto/bls12381_utils.c b/crypto/bls12381_utils.c index a6b1e5c5e44..07224cd4242 100644 --- a/crypto/bls12381_utils.c +++ b/crypto/bls12381_utils.c @@ -38,12 +38,12 @@ const Fr BLS12_381_rR = {{ \ }}; // returns true if a == 0 and false otherwise -bool_t Fr_is_zero(const Fr* a) { +bool Fr_is_zero(const Fr* a) { return bytes_are_zero((const byte*)a, sizeof(Fr)); } // returns true if a == b and false otherwise -bool_t Fr_is_equal(const Fr* a, const Fr* b) { +bool Fr_is_equal(const Fr* a, const Fr* b) { return vec_is_equal(a, b, sizeof(Fr)); } @@ -265,7 +265,7 @@ static void Fr_from_be_bytes(Fr* out, const byte *bytes, size_t n) // Reads a scalar from an array and maps it to Fr using modular reduction. // Input is byte-big-endian as used by the external APIs. // It returns true if scalar is zero and false otherwise. -bool_t map_bytes_to_Fr(Fr* a, const byte* bin, int len) { +bool map_bytes_to_Fr(Fr* a, const byte* bin, int len) { Fr_from_be_bytes(a, bin, len); return Fr_is_zero(a); } @@ -311,7 +311,7 @@ static void Fp_neg(Fp *res, const Fp *a) { // The boolean output is valid whether `a` is in Montgomery form or not, // since montgomery constant `R` is a quadratic residue. // However, the square root is valid only if `a` is in montgomery form. -static bool_t Fp_sqrt_montg(Fp *res, const Fp* a) { +static bool Fp_sqrt_montg(Fp *res, const Fp* a) { return sqrt_fp((limb_t*)res, (limb_t*)a); } @@ -415,7 +415,7 @@ static void Fp2_squ_montg(Fp2 *res, const Fp2 *a) { // The boolean output is valid whether `a` is in Montgomery form or not, // since montgomery constant `R` is a quadratic residue. // However, the square root is valid only if `a` is in montgomery form. -static bool_t Fp2_sqrt_montg(Fp2 *res, const Fp2* a) { +static bool Fp2_sqrt_montg(Fp2 *res, const Fp2* a) { return sqrt_fp2((vec384*)res, (vec384*)a); } @@ -466,13 +466,13 @@ void E1_copy(E1* res, const E1* p) { } // checks p1 == p2 -bool_t E1_is_equal(const E1* p1, const E1* p2) { +bool E1_is_equal(const E1* p1, const E1* p2) { // `POINTonE1_is_equal` includes the infinity case return POINTonE1_is_equal((const POINTonE1*)p1, (const POINTonE1*)p2); } // compare p to infinity -bool_t E1_is_infty(const E1* p) { +bool E1_is_infty(const E1* p) { // BLST infinity points are defined by Z=0 return vec_is_zero(p->z, sizeof(p->z)); } @@ -495,14 +495,14 @@ void E1_to_affine(E1* res, const E1* p) { } // checks affine point `p` is in E1 -bool_t E1_affine_on_curve(const E1* p) { +bool E1_affine_on_curve(const E1* p) { // BLST's `POINTonE1_affine_on_curve` does not include the inifity case! return POINTonE1_affine_on_curve((POINTonE1_affine*)p) | E1_is_infty(p); } // checks if input E1 point is on the subgroup G1. // It assumes input `p` is on E1. -bool_t E1_in_G1(const E1* p){ +bool E1_in_G1(const E1* p){ // currently uses Scott method return POINTonE1_in_G1((const POINTonE1*)p); } @@ -859,19 +859,19 @@ void E2_set_infty(E2* p) { } // check if `p` is infinity -bool_t E2_is_infty(const E2* p) { +bool E2_is_infty(const E2* p) { // BLST infinity points are defined by Z=0 return vec_is_zero(p->z, sizeof(p->z)); } // checks affine point `p` is in E2 -bool_t E2_affine_on_curve(const E2* p) { +bool E2_affine_on_curve(const E2* p) { // BLST's `POINTonE2_affine_on_curve` does not include the infinity case! return POINTonE2_affine_on_curve((POINTonE2_affine*)p) | E2_is_infty(p); } // checks p1 == p2 -bool_t E2_is_equal(const E2* p1, const E2* p2) { +bool E2_is_equal(const E2* p1, const E2* p2) { // `POINTonE2_is_equal` includes the infinity case return POINTonE2_is_equal((const POINTonE2*)p1, (const POINTonE2*)p2); } @@ -935,7 +935,7 @@ void G2_mult_gen(E2* res, const Fr* expo) { // checks if input E2 point is on the subgroup G2. // It assumes input `p` is on E2. -bool_t E2_in_G2(const E2* p){ +bool E2_in_G2(const E2* p){ // currently uses Scott method return POINTonE2_in_G2((const POINTonE2*)p); } @@ -1084,7 +1084,7 @@ BLST_ERROR unsafe_map_bytes_to_G2complement(E2* p, const byte* bytes, int len) { // ------------------- Pairing utilities -bool_t Fp12_is_one(Fp12 *a) { +bool Fp12_is_one(Fp12 *a) { return vec_is_equal(a, BLS12_381_Rx.p12, sizeof(Fp12)); } diff --git a/crypto/bls12381_utils.go b/crypto/bls12381_utils.go index 9695d45aba2..94083cf9abe 100644 --- a/crypto/bls12381_utils.go +++ b/crypto/bls12381_utils.go @@ -89,28 +89,28 @@ func generatorScalarMultG2(res *pointE2, expo *scalar) { // comparison in Fr where r is the group order of G1/G2 // (both scalars should be reduced mod r) func (x *scalar) equals(other *scalar) bool { - return C.Fr_is_equal((*C.Fr)(x), (*C.Fr)(other)) != 0 + return bool(C.Fr_is_equal((*C.Fr)(x), (*C.Fr)(other))) } // comparison in E1 func (p *pointE1) equals(other *pointE1) bool { - return C.E1_is_equal((*C.E1)(p), (*C.E1)(other)) != 0 + return bool(C.E1_is_equal((*C.E1)(p), (*C.E1)(other))) } // comparison in E2 func (p *pointE2) equals(other *pointE2) bool { - return C.E2_is_equal((*C.E2)(p), (*C.E2)(other)) != 0 + return bool(C.E2_is_equal((*C.E2)(p), (*C.E2)(other))) } // Comparison to zero in Fr. // Scalar must be already reduced modulo r func (x *scalar) isZero() bool { - return C.Fr_is_zero((*C.Fr)(x)) != 0 + return bool(C.Fr_is_zero((*C.Fr)(x))) } // Comparison to point at infinity in G2. func (p *pointE2) isInfinity() bool { - return C.E2_is_infty((*C.E2)(p)) != 0 + return bool(C.E2_is_infty((*C.E2)(p))) } // generates a random element in F_r using input random source, @@ -142,7 +142,7 @@ func mapToFr(x *scalar, src []byte) bool { isZero := C.map_bytes_to_Fr((*C.Fr)(x), (*C.uchar)(&src[0]), (C.int)(len(src))) - return isZero != (C.ulonglong)(0) + return bool(isZero) } // writeScalar writes a scalar in a slice of bytes @@ -231,13 +231,13 @@ func readPointE1(a *pointE1, src []byte) error { // checkMembershipG1 wraps a call to a subgroup check in G1 since cgo can't be used // in go test files. func checkMembershipG1(pt *pointE1) bool { - return C.E1_in_G1((*C.E1)(pt)) != (C.ulonglong)(0) + return bool(C.E1_in_G1((*C.E1)(pt))) } // checkMembershipG2 wraps a call to a subgroup check in G2 since cgo can't be used // in go test files. func checkMembershipG2(pt *pointE2) bool { - return C.E2_in_G2((*C.E2)(pt)) != (C.ulonglong)(0) + return bool(C.E2_in_G2((*C.E2)(pt))) } // This is only a TEST/DEBUG/BENCH function. diff --git a/crypto/bls12381_utils.h b/crypto/bls12381_utils.h index d2f2d8b489f..3e8ca1b06ea 100644 --- a/crypto/bls12381_utils.h +++ b/crypto/bls12381_utils.h @@ -8,6 +8,9 @@ #include #include "blst_include.h" +typedef uint8_t byte; +typedef _Bool bool; // assuming cgo is using a modern enough compiler + #define SEC_BITS 128 #define VALID 0 #define INVALID 1 @@ -50,8 +53,8 @@ int bls_spock_verify(const E2*, const byte*, const E2*, const byte*); // Fr utilities extern const Fr BLS12_381_rR; -bool_t Fr_is_zero(const Fr* a); -bool_t Fr_is_equal(const Fr* a, const Fr* b); +bool Fr_is_zero(const Fr* a); +bool Fr_is_equal(const Fr* a, const Fr* b); void Fr_set_limb(Fr*, const limb_t); void Fr_copy(Fr*, const Fr*); void Fr_set_zero(Fr*); @@ -69,7 +72,7 @@ void Fr_inv_exp_montg(Fr *res, const Fr *a); BLST_ERROR Fr_read_bytes(Fr* a, const byte *bin, int len); BLST_ERROR Fr_star_read_bytes(Fr* a, const byte *bin, int len); void Fr_write_bytes(byte *bin, const Fr* a); -bool_t map_bytes_to_Fr(Fr*, const byte*, int); +bool map_bytes_to_Fr(Fr*, const byte*, int); // Fp utilities void Fp_mul_montg(Fp *, const Fp *, const Fp *); @@ -77,12 +80,12 @@ void Fp_squ_montg(Fp *, const Fp *); // E1 and G1 utilities void E1_copy(E1*, const E1*); -bool_t E1_is_equal(const E1*, const E1*); +bool E1_is_equal(const E1*, const E1*); void E1_set_infty(E1*); -bool_t E1_is_infty(const E1*); +bool E1_is_infty(const E1*); void E1_to_affine(E1*, const E1*); -bool_t E1_affine_on_curve(const E1*); -bool_t E1_in_G1(const E1*); +bool E1_affine_on_curve(const E1*); +bool E1_in_G1(const E1*); void E1_mult(E1*, const E1*, const Fr*); void E1_add(E1*, const E1*, const E1*); void E1_neg(E1*, const E1*); @@ -99,9 +102,9 @@ int map_to_G1(E1*, const byte*, const int); // E2 and G2 utilities void E2_set_infty(E2* p); -bool_t E2_is_infty(const E2*); -bool_t E2_affine_on_curve(const E2*); -bool_t E2_is_equal(const E2*, const E2*); +bool E2_is_infty(const E2*); +bool E2_affine_on_curve(const E2*); +bool E2_is_equal(const E2*, const E2*); void E2_copy(E2*, const E2*); void E2_to_affine(E2*, const E2*); BLST_ERROR E2_read_bytes(E2*, const byte *, const int); @@ -113,12 +116,12 @@ void E2_add(E2* res, const E2* a, const E2* b); void E2_neg(E2*, const E2*); void E2_sum_vector(E2*, const E2*, const int); void E2_subtract_vector(E2* res, const E2* x, const E2* y, const int len); -bool_t E2_in_G2(const E2*); +bool E2_in_G2(const E2*); void unsafe_map_bytes_to_G2(E2*, const byte*, int); BLST_ERROR unsafe_map_bytes_to_G2complement(E2*, const byte*, int); // pairing and Fp12 -bool_t Fp12_is_one(Fp12*); +bool Fp12_is_one(Fp12*); void Fp12_set_one(Fp12*); void multi_pairing(Fp12*, const E1*, const E2*, const int); diff --git a/crypto/bls_thresholdsign_core.c b/crypto/bls_thresholdsign_core.c index e160a16e7c9..78a87823b4c 100644 --- a/crypto/bls_thresholdsign_core.c +++ b/crypto/bls_thresholdsign_core.c @@ -7,7 +7,7 @@ // Computes the Lagrange coefficient L_i(0) in Fr with regards to the range [indices(0)..indices(t)] // and stores it in `res`, where t is the degree of the polynomial P. // `len` is equal to `t+1` where `t` is the polynomial degree. -static void Fr_lagrange_coeff_at_zero(Fr* res, const int i, const uint8_t indices[], const int len){ +static void Fr_lagrange_coeff_at_zero(Fr* res, const int i, const byte indices[], const int len){ // coefficient is computed as N * D^(-1) Fr numerator; // eventually would represent N*R^k @@ -63,7 +63,7 @@ static void Fr_lagrange_coeff_at_zero(Fr* res, const int i, const uint8_t indice // Computes the Langrange interpolation at zero P(0) = LI(0) with regards to the indices [indices(0)..indices(t)] // and their G1 images [shares(0)..shares(t)], and stores the resulting G1 point in `dest`. // `len` is equal to `t+1` where `t` is the polynomial degree. -static void E1_lagrange_interpolate_at_zero(E1* out, const E1 shares[], const uint8_t indices[], const int len) { +static void E1_lagrange_interpolate_at_zero(E1* out, const E1 shares[], const byte indices[], const int len) { // Purpose is to compute Q(0) where Q(x) = A_0 + A_1*x + ... + A_t*x^t in G1 // where A_i = g1 ^ a_i @@ -83,7 +83,7 @@ static void E1_lagrange_interpolate_at_zero(E1* out, const E1 shares[], const ui // Computes the Langrange interpolation at zero LI(0) with regards to the indices [indices(0)..indices(t)] // and writes their E1 concatenated serializations [shares(1)..shares(t+1)] in `dest`. // `len` is equal to `t+1` where `t` is the polynomial degree. -int E1_lagrange_interpolate_at_zero_write(byte* dest, const byte* shares, const uint8_t indices[], const int len) { +int E1_lagrange_interpolate_at_zero_write(byte* dest, const byte* shares, const byte indices[], const int len) { int read_ret; E1* E1_shares = malloc(sizeof(E1) * len); for (int i=0; i < len; i++) { diff --git a/crypto/bls_thresholdsign_include.h b/crypto/bls_thresholdsign_include.h index 1275b10bab4..3937f8ce965 100644 --- a/crypto/bls_thresholdsign_include.h +++ b/crypto/bls_thresholdsign_include.h @@ -3,7 +3,7 @@ #include "bls_include.h" -int E1_lagrange_interpolate_at_zero_write(byte*, const byte* , const uint8_t[], const int); +int E1_lagrange_interpolate_at_zero_write(byte*, const byte* , const byte[], const int); extern void Fr_polynomial_image(Fr* out, E2* y, const Fr* a, const int a_size, const byte x); #endif diff --git a/crypto/blst_include.h b/crypto/blst_include.h index 89966463c61..20c2fcad5df 100644 --- a/crypto/blst_include.h +++ b/crypto/blst_include.h @@ -1,63 +1,19 @@ #ifndef __BLST_INCLUDE_H__ #define __BLST_INCLUDE_H__ -// extra tools to use BLST low level that are needed by the Flow crypto library -// eventually this file would replace blst.h - +// BLST src headers #include "point.h" #include "fields.h" #include "consts.h" -#include "errors.h" - -// types used by the Flow crypto library that are imported from BLST -// these type definitions are used as an abstraction from BLST internal types - -// Parts of this file have been copied from blst.h in the BLST repo -/* - * Copyright Supranational LLC - * Licensed under the Apache License, Version 2.0, see LICENSE for details. - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifdef __SIZE_TYPE__ -typedef __SIZE_TYPE__ size_t; -#else -#include -#endif - -#if defined(__UINT8_TYPE__) && defined(__UINT32_TYPE__) \ - && defined(__UINT64_TYPE__) -typedef __UINT8_TYPE__ uint8_t; -typedef __UINT32_TYPE__ uint32_t; -typedef __UINT64_TYPE__ uint64_t; -#else -#include -#endif - -typedef uint8_t byte; - -#ifdef __cplusplus -extern "C" { -#elif defined(__BLST_CGO__) -typedef _Bool bool; /* it's assumed that cgo calls modern enough compiler */ -#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901 -# define bool _Bool -#else -# define bool int -#endif - -#ifdef SWIG -# define DEFNULL =NULL -#elif defined __cplusplus -# define DEFNULL =0 -#else -# define DEFNULL -#endif +#include "errors.h" // TODO: add sanity checks that BLST_PK_IS_INFINITY is indeed the last // enum value (eventually submit a fix to BLST) #define BLST_BAD_SCALAR ((BLST_PK_IS_INFINITY)+1) +// types used by the Flow crypto library that are imported from BLST +// these type definitions are used as an abstraction from BLST internal types + // field elements F_r // where `r` is the order of G1/G2. // F_r elements are represented as big numbers reduced modulo `r`. Big numbers diff --git a/crypto/dkg_core.c b/crypto/dkg_core.c index 2b34572089c..9966fbcfc37 100644 --- a/crypto/dkg_core.c +++ b/crypto/dkg_core.c @@ -78,7 +78,7 @@ BLST_ERROR E2_vector_read_bytes(E2* A, const byte* src, const int len){ // checks the discrete log relationship in G2. // - returns 1 if g2^x = y, where g2 is the generator of G2 // - returns 0 otherwise. -bool_t G2_check_log(const Fr* x, const E2* y) { +bool G2_check_log(const Fr* x, const E2* y) { E2 tmp; G2_mult_gen(&tmp, x); return E2_is_equal(&tmp, y); diff --git a/crypto/dkg_feldmanvss.go b/crypto/dkg_feldmanvss.go index dd81bbcd79c..98420cc87cf 100644 --- a/crypto/dkg_feldmanvss.go +++ b/crypto/dkg_feldmanvss.go @@ -473,9 +473,9 @@ func readVerifVector(A []pointE2, src []byte) error { func (s *feldmanVSSstate) verifyShare() bool { // check y[current] == x.G2 - return C.G2_check_log( + return bool(C.G2_check_log( (*C.Fr)(&s.x), - (*C.E2)(&s.y[s.myIndex])) != 0 + (*C.E2)(&s.y[s.myIndex]))) } // computePublicKeys extracts the participants public keys from the verification vector diff --git a/crypto/dkg_feldmanvssq.go b/crypto/dkg_feldmanvssq.go index 620c962faaa..a7de2fe93d9 100644 --- a/crypto/dkg_feldmanvssq.go +++ b/crypto/dkg_feldmanvssq.go @@ -511,9 +511,10 @@ func (s *feldmanVSSQualState) buildAndBroadcastComplaintAnswer(complainee index) // - true if the complaint answer is not correct func (s *feldmanVSSQualState) checkComplaint(complainer index, c *complaint) bool { // check y[complainer] == share.G2 - return C.G2_check_log( + isLog := C.G2_check_log( (*C.Fr)(&c.answer), - (*C.E2)(&s.y[complainer])) == 0 + (*C.E2)(&s.y[complainer])) + return !bool(isLog) } // data = |complainee| diff --git a/crypto/dkg_include.h b/crypto/dkg_include.h index ca6619eb10f..8d3bdc7e1d7 100644 --- a/crypto/dkg_include.h +++ b/crypto/dkg_include.h @@ -8,6 +8,6 @@ void Fr_polynomial_image(Fr* out, E2* y, const Fr* a, const int deg, cons void E2_polynomial_images(E2* y, const int len_y, const E2* A, const int deg); void G2_vector_write_bytes(byte* out, const E2* A, const int len); BLST_ERROR E2_vector_read_bytes(E2* A, const byte* src, const int len); -bool_t G2_check_log(const Fr* x, const E2* y); +bool G2_check_log(const Fr* x, const E2* y); #endif From fb4ac123b967fdbe4d3f1b676d6f299367a81ecf Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Sat, 27 May 2023 22:11:45 -0600 Subject: [PATCH 03/11] add sanity check scalar mult in G1 and G2 --- crypto/bls12381_utils_test.go | 36 +++++++++++++++++++++++++++++++++++ crypto/ecdsa_test.go | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/crypto/bls12381_utils_test.go b/crypto/bls12381_utils_test.go index 69d7e687f9b..17a1526414a 100644 --- a/crypto/bls12381_utils_test.go +++ b/crypto/bls12381_utils_test.go @@ -9,6 +9,42 @@ import ( "github.com/stretchr/testify/require" ) +// Sanity check of G1 and G2 scalar multiplication +func TestScalarMultBLS12381(t *testing.T) { + expoBytes, err := hex.DecodeString("444465cb6cc2dba9474e6beeb6a9013fbf1260d073429fb14a31e63e89129390") + require.NoError(t, err) + + var expo scalar + isZero := mapToFr(&expo, expoBytes) + require.False(t, isZero) + + // G1 generator multiplication + // Note that generator and random point multiplications + // are implemented with the same algorithm + t.Run("G1", func(t *testing.T) { + var p pointE1 + generatorScalarMultG1(&p, &expo) + expected, err := hex.DecodeString("96484ca50719f5d2533047960878b6bae8289646c0f00a942a1e6992be9981a9e0c7a51e9918f9b19d178cf04a8018a4") + require.NoError(t, err) + pBytes := make([]byte, SignatureLenBLSBLS12381) + writePointE1(pBytes, &p) + assert.Equal(t, pBytes, expected) + }) + + // G2 generator multiplication + // Note that generator and random point multiplications + // are implemented with the same algorithm + t.Run("G2", func(t *testing.T) { + var p pointE2 + generatorScalarMultG2(&p, &expo) + expected, err := hex.DecodeString("b35f5043f166848805b98da62dcb9c5d2f25e497bd0d9c461d4a00d19e4e67cc1e813de3c99479d5a2c62fb754fd7df40c4fd60c46834c8ae665343a3ff7dc3cc929de34ad62b7b55974f4e3fd20990d3e564b96e4d33de87716052d58cf823e") + require.NoError(t, err) + pBytes := make([]byte, PubKeyLenBLSBLS12381) + writePointE2(pBytes, &p) + assert.Equal(t, pBytes, expected) + }) +} + // G1 and G2 scalar multiplication func BenchmarkScalarMult(b *testing.B) { seed := make([]byte, securityBits/8) diff --git a/crypto/ecdsa_test.go b/crypto/ecdsa_test.go index d5d38f8e947..6a69453816d 100644 --- a/crypto/ecdsa_test.go +++ b/crypto/ecdsa_test.go @@ -157,7 +157,7 @@ func TestECDSAUtils(t *testing.T) { // TestScalarMult is a unit test of the scalar multiplication // This is only a sanity check meant to make sure the curve implemented // is checked against an independant test vector -func TestScalarMult(t *testing.T) { +func TestScalarMultP256_secp256k1(t *testing.T) { secp256k1 := secp256k1Instance.curve p256 := p256Instance.curve genericMultTests := []struct { From 0232a953b2040dda260de7aa4c761f8404efb1e5 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Sun, 28 May 2023 00:27:37 -0600 Subject: [PATCH 04/11] use not enough shares error in BLSReconstructThresholdSignature --- crypto/bls_thresholdsign.go | 14 +++++++++----- crypto/bls_thresholdsign_test.go | 9 +++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/crypto/bls_thresholdsign.go b/crypto/bls_thresholdsign.go index 3cef4d4e605..a7eaad5a2a4 100644 --- a/crypto/bls_thresholdsign.go +++ b/crypto/bls_thresholdsign.go @@ -437,10 +437,14 @@ func (s *blsThresholdSignatureInspector) reconstructThresholdSignature() (Signat // // size is the number of participants, it must be in the range [ThresholdSignMinSize..ThresholdSignMaxSize]. // threshold is the threshold value, it must be in the range [MinimumThreshold..size-1]. -// The function does not check the validity of the shares, and does not check -// the validity of the resulting signature. +// The function does not accept any input public key. Therefore, it does not check the validity of the +// shares against individual public keys, and does not check the validity of the resulting signature +// against the group public key. // BLSReconstructThresholdSignature returns: -// - (nil, error) if the inputs are not in the correct range, if the threshold is not reached +// - (nil, invalidInputsError) if : +// -- numbers of shares does not match the number of signers +// -- the inputs are not in the correct range. +// - (nil, notEnoughSharesError) if the threshold is not reached. // - (nil, duplicatedSignerError) if input signers are not distinct. // - (nil, invalidSignatureError) if at least one of the first (threshold+1) signatures. // does not serialize to a valid E1 point. @@ -470,8 +474,8 @@ func BLSReconstructThresholdSignature(size int, threshold int, } if len(shares) < threshold+1 { - return nil, invalidInputsErrorf( - "the number of signatures does not reach the threshold") + return nil, notEnoughSharesErrorf( + "the number of signatures %d is less than the minimum %d", len(shares), threshold+1) } // map to check signers are distinct diff --git a/crypto/bls_thresholdsign_test.go b/crypto/bls_thresholdsign_test.go index 3e55f3d1806..20d578db264 100644 --- a/crypto/bls_thresholdsign_test.go +++ b/crypto/bls_thresholdsign_test.go @@ -594,9 +594,14 @@ func testCentralizedStatelessAPI(t *testing.T) { signers[randomDuplicate] = tmp } + // check with not enough signatures + thresholdSignature, err = BLSReconstructThresholdSignature(n, threshold, signShares[:threshold], signers[:threshold]) + assert.Error(t, err) + assert.True(t, IsNotEnoughSharesError(err)) + assert.Nil(t, thresholdSignature) + // check with an invalid signature (invalid serialization) - invalidSig := make([]byte, signatureLengthBLSBLS12381) - signShares[0] = invalidSig + signShares[0] = BLSInvalidSignature() thresholdSignature, err = BLSReconstructThresholdSignature(n, threshold, signShares, signers[:threshold+1]) assert.Error(t, err) assert.True(t, IsInvalidSignatureError(err)) From 5fa28df293e2490da9b77d9df86edb492ced274a Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Sun, 28 May 2023 01:47:16 -0600 Subject: [PATCH 05/11] refactor BLS constants to use internal BLS12_381 length constants --- crypto/bls.go | 35 ++++++++++++++--------------------- crypto/bls12381_utils.c | 12 ++++++++++++ crypto/bls12381_utils.go | 14 +++++++++----- crypto/bls12381_utils.h | 3 +++ crypto/bls12381_utils_test.go | 10 +++++----- crypto/bls_crossBLST_test.go | 2 +- crypto/dkg_feldmanvss.go | 8 ++++---- crypto/dkg_feldmanvssq.go | 4 ++-- 8 files changed, 50 insertions(+), 38 deletions(-) diff --git a/crypto/bls.go b/crypto/bls.go index 3206a29cdf9..65c7ce4d390 100644 --- a/crypto/bls.go +++ b/crypto/bls.go @@ -39,24 +39,16 @@ import ( "github.com/onflow/flow-go/crypto/hash" ) -const ( - // BLS12-381 - // p size in bytes, where G1 is defined over the field Zp - fieldSize = 48 - // - // 1 for compressed, 0 for uncompressed - values should not be changed - uncompressed = 0 //nolint - compressed = 1 - // Points compression when serialized - serializationG1 = compressed - serializationG2 = compressed - // - // SignatureLenBLSBLS12381 is the size of G1 elements - SignatureLenBLSBLS12381 = fieldSize * (2 - serializationG1) // the length is divided by 2 if compression is on - PrKeyLenBLSBLS12381 = 32 // equal to frBytesLen - // PubKeyLenBLSBLS12381 is the size of G2 elements - PubKeyLenBLSBLS12381 = 2 * fieldSize * (2 - serializationG2) // the length is divided by 2 if compression is on +var ( + // SignatureLenBLSBLS12381 is the size of a `G_1` element. + SignatureLenBLSBLS12381 = g1BytesLen + // PubKeyLenBLSBLS12381 is the size of a `G_2` element. + PubKeyLenBLSBLS12381 = g2BytesLen + // PrKeyLenBLSBLS12381 is the size of a `F_r` element, where `r` is the order of `G_1` and `G_2`. + PrKeyLenBLSBLS12381 = frBytesLen +) +const ( // Hash to curve params // hash to curve suite ID of the form : CurveID_ || HashID_ || MapID_ || encodingVariant_ h2cSuiteID = "BLS12381G1_XOF:KMAC128_SSWU_RO_" @@ -70,8 +62,7 @@ const ( ) // expandMsgOutput is the output length of the expand_message step as required by the -// hash_to_curve algorithm (and the map to G1 step) -// +// hash_to_curve algorithm (and the map to G1 step). // (Cgo does not export C macros) var expandMsgOutput = int(C.get_mapToG1_input_len()) @@ -360,7 +351,8 @@ func (a *blsBLS12381Algo) decodePublicKey(publicKeyBytes []byte) (PublicKey, err // decodePublicKeyCompressed decodes a slice of bytes into a public key. // since we use the compressed representation by default, this checks the default and delegates to decodePublicKeyCompressed func (a *blsBLS12381Algo) decodePublicKeyCompressed(publicKeyBytes []byte) (PublicKey, error) { - if serializationG2 != compressed { + // in compression mode, g2BytesLen is equal to 2 * Fp_bytes + if g2BytesLen != 2*fpBytesLen { panic("library is not configured to use compressed public key serialization") } return a.decodePublicKey(publicKeyBytes) @@ -490,7 +482,8 @@ func (pk *pubKeyBLSBLS12381) Size() int { // The encoding is a compressed encoding of the point // [zcash] https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-zcash-serialization-format- func (a *pubKeyBLSBLS12381) EncodeCompressed() []byte { - if serializationG2 != compressed { + // in compression mode, g2BytesLen is equal to 2 * Fp_bytes + if g2BytesLen != 2*fpBytesLen { panic("library is not configured to use compressed public key serialization") } dest := make([]byte, pubKeyLengthBLSBLS12381) diff --git a/crypto/bls12381_utils.c b/crypto/bls12381_utils.c index 07224cd4242..00d32ccdfed 100644 --- a/crypto/bls12381_utils.c +++ b/crypto/bls12381_utils.c @@ -24,6 +24,18 @@ int get_Fr_BYTES() { return Fr_BYTES; } +int get_Fp_BYTES() { + return Fp_BYTES; +} + +int get_G1_SER_BYTES() { + return G1_SER_BYTES; +} + +int get_G2_SER_BYTES() { + return G2_SER_BYTES; +} + int get_mapToG1_input_len() { return MAP_TO_G1_INPUT_LEN; } diff --git a/crypto/bls12381_utils.go b/crypto/bls12381_utils.go index 94083cf9abe..f0a6c77a12d 100644 --- a/crypto/bls12381_utils.go +++ b/crypto/bls12381_utils.go @@ -44,8 +44,12 @@ type scalar C.Fr // TODO: For now scalars are represented as field elements Fr since all scalars // are less than r - check if distinguishing two types in necessary -// BLS12-381 related lengths +// BLS12-381 related lengths, exported as functions +// because cgo does not recognize C macros. var frBytesLen = int(C.get_Fr_BYTES()) +var g1BytesLen = int(C.get_G1_SER_BYTES()) +var g2BytesLen = int(C.get_G2_SER_BYTES()) +var fpBytesLen = int(C.get_Fp_BYTES()) // get some constants from the C layer // (Cgo does not export C macros) @@ -151,14 +155,14 @@ func writeScalar(dest []byte, x *scalar) { } // writePointE2 writes a G2 point in a slice of bytes -// The slice should be of size PubKeyLenBLSBLS12381 and the serialization +// The slice should be of size g2BytesLen and the serialization // follows the Zcash format specified in draft-irtf-cfrg-pairing-friendly-curves func writePointE2(dest []byte, a *pointE2) { C.E2_write_bytes((*C.uchar)(&dest[0]), (*C.E2)(a)) } // writePointE1 writes a G1 point in a slice of bytes -// The slice should be of size SignatureLenBLSBLS12381 and the serialization +// The slice should be of size g1BytesLen and the serialization // follows the Zcash format specified in draft-irtf-cfrg-pairing-friendly-curves func writePointE1(dest []byte, a *pointE1) { C.E1_write_bytes((*C.uchar)(&dest[0]), (*C.E1)(a)) @@ -187,7 +191,7 @@ func readScalarFrStar(a *scalar, src []byte) error { } // readPointE2 reads a E2 point from a slice of bytes -// The slice is expected to be of size PubKeyLenBLSBLS12381 and the deserialization +// The slice is expected to be of size g2BytesLen and the deserialization // follows the Zcash format specified in draft-irtf-cfrg-pairing-friendly-curves. // No G2 membership check is performed. func readPointE2(a *pointE2, src []byte) error { @@ -208,7 +212,7 @@ func readPointE2(a *pointE2, src []byte) error { } // readPointE1 reads a E1 point from a slice of bytes -// The slice should be of size SignatureLenBLSBLS12381 and the deserialization +// The slice should be of size g1BytesLen and the deserialization // follows the Zcash format specified in draft-irtf-cfrg-pairing-friendly-curves. // No G1 membership check is performed. func readPointE1(a *pointE1, src []byte) error { diff --git a/crypto/bls12381_utils.h b/crypto/bls12381_utils.h index 3e8ca1b06ea..f3800e6ebef 100644 --- a/crypto/bls12381_utils.h +++ b/crypto/bls12381_utils.h @@ -46,6 +46,9 @@ typedef _Bool bool; // assuming cgo is using a modern enough compiler int get_valid(); int get_invalid(); int get_Fr_BYTES(); +int get_Fp_BYTES(); +int get_G1_SER_BYTES(); +int get_G2_SER_BYTES(); int get_mapToG1_input_len(); // BLS based SPoCK diff --git a/crypto/bls12381_utils_test.go b/crypto/bls12381_utils_test.go index 17a1526414a..2c9d76bbbe5 100644 --- a/crypto/bls12381_utils_test.go +++ b/crypto/bls12381_utils_test.go @@ -26,7 +26,7 @@ func TestScalarMultBLS12381(t *testing.T) { generatorScalarMultG1(&p, &expo) expected, err := hex.DecodeString("96484ca50719f5d2533047960878b6bae8289646c0f00a942a1e6992be9981a9e0c7a51e9918f9b19d178cf04a8018a4") require.NoError(t, err) - pBytes := make([]byte, SignatureLenBLSBLS12381) + pBytes := make([]byte, g1BytesLen) writePointE1(pBytes, &p) assert.Equal(t, pBytes, expected) }) @@ -39,7 +39,7 @@ func TestScalarMultBLS12381(t *testing.T) { generatorScalarMultG2(&p, &expo) expected, err := hex.DecodeString("b35f5043f166848805b98da62dcb9c5d2f25e497bd0d9c461d4a00d19e4e67cc1e813de3c99479d5a2c62fb754fd7df40c4fd60c46834c8ae665343a3ff7dc3cc929de34ad62b7b55974f4e3fd20990d3e564b96e4d33de87716052d58cf823e") require.NoError(t, err) - pBytes := make([]byte, PubKeyLenBLSBLS12381) + pBytes := make([]byte, g2BytesLen) writePointE2(pBytes, &p) assert.Equal(t, pBytes, expected) }) @@ -130,7 +130,7 @@ func BenchmarkMapToG1(b *testing.B) { // test subgroup membership check in G1 and G2 func TestSubgroupCheck(t *testing.T) { prg := getPRG(t) - seed := make([]byte, PubKeyLenBLSBLS12381) + seed := make([]byte, g2BytesLen) _, err := prg.Read(seed) require.NoError(t, err) @@ -165,7 +165,7 @@ func TestSubgroupCheck(t *testing.T) { // subgroup membership check bench func BenchmarkSubgroupCheck(b *testing.B) { - seed := make([]byte, PubKeyLenBLSBLS12381) + seed := make([]byte, g2BytesLen) _, err := mrand.Read(seed) require.NoError(b, err) @@ -195,7 +195,7 @@ func BenchmarkSubgroupCheck(b *testing.B) { func TestReadWriteG1(t *testing.T) { prg := getPRG(t) seed := make([]byte, frBytesLen) - bytes := make([]byte, SignatureLenBLSBLS12381) + bytes := make([]byte, g1BytesLen) // generate a random G1 point, encode it, decode it, // and compare it the original point iterations := 50 diff --git a/crypto/bls_crossBLST_test.go b/crypto/bls_crossBLST_test.go index 6d3f1765e25..7629289ba9e 100644 --- a/crypto/bls_crossBLST_test.go +++ b/crypto/bls_crossBLST_test.go @@ -134,7 +134,7 @@ func testEncodeDecodePublicKeyCrossBLST(t *rapid.T) { // testEncodeDecodeG1CrossBLST tests encoding and decoding of G1 points are consistent with BLST. // This test assumes signature serialization is identical to BLST. func testEncodeDecodeG1CrossBLST(t *rapid.T) { - randomSlice := rapid.SliceOfN(rapid.Byte(), SignatureLenBLSBLS12381, SignatureLenBLSBLS12381) + randomSlice := rapid.SliceOfN(rapid.Byte(), g1BytesLen, g1BytesLen) validSignatureFlow := rapid.Custom(validSignatureBytesFlow) validSignatureBLST := rapid.Custom(validSignatureBytesBLST) // sigBytes are bytes of either a valid serialization of a E1/G1 point, or random bytes diff --git a/crypto/dkg_feldmanvss.go b/crypto/dkg_feldmanvss.go index 98420cc87cf..ac76469f962 100644 --- a/crypto/dkg_feldmanvss.go +++ b/crypto/dkg_feldmanvss.go @@ -151,11 +151,11 @@ func (s *feldmanVSSstate) End() (PrivateKey, PublicKey, []PublicKey, error) { return x, Y, y, nil } -const ( - shareSize = PrKeyLenBLSBLS12381 +var ( + shareSize = frBytesLen // the actual verifVectorSize depends on the state and is: - // PubKeyLenBLSBLS12381*(t+1) - verifVectorSize = PubKeyLenBLSBLS12381 + // g2BytesLen*(t+1) + verifVectorSize = g2BytesLen ) // HandleBroadcastMsg processes a new broadcasted message received by the current participant. diff --git a/crypto/dkg_feldmanvssq.go b/crypto/dkg_feldmanvssq.go index a7de2fe93d9..b8056b990dc 100644 --- a/crypto/dkg_feldmanvssq.go +++ b/crypto/dkg_feldmanvssq.go @@ -201,9 +201,9 @@ func (s *feldmanVSSQualState) End() (PrivateKey, PublicKey, []PublicKey, error) return x, Y, y, nil } -const ( +var ( complaintSize = 1 - complaintAnswerSize = 1 + PrKeyLenBLSBLS12381 + complaintAnswerSize = 1 + frBytesLen ) // HandleBroadcastMsg processes a new broadcasted message received by the current participant. From d2c7cbf2eb3172f6871cefd1eb7a579d61ae2bc9 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Mon, 29 May 2023 13:52:29 -0600 Subject: [PATCH 06/11] more consolidation of length constants --- crypto/bls.go | 17 ++++++----------- crypto/bls12381_utils.go | 4 ++-- crypto/bls_core.c | 21 ++++----------------- crypto/bls_crossBLST_test.go | 4 ++-- crypto/bls_include.h | 11 ----------- crypto/bls_multisig.go | 12 ++++++------ crypto/bls_test.go | 4 ++-- crypto/bls_thresholdsign.go | 8 ++++---- crypto/spock.go | 2 +- 9 files changed, 27 insertions(+), 56 deletions(-) diff --git a/crypto/bls.go b/crypto/bls.go index 65c7ce4d390..f515a9445dc 100644 --- a/crypto/bls.go +++ b/crypto/bls.go @@ -184,7 +184,7 @@ func (pk *pubKeyBLSBLS12381) Verify(s Signature, data []byte, kmac hash.Hasher) return false, err } - if len(s) != signatureLengthBLSBLS12381 { + if len(s) != SignatureLenBLSBLS12381 { return false, nil } @@ -214,7 +214,7 @@ func (pk *pubKeyBLSBLS12381) Verify(s Signature, data []byte, kmac hash.Hasher) // 0xC0 is the header of the point at infinity serialization (either in G1 or G2) const infinityPointHeader = byte(0xC0) -var identityBLSSignature = append([]byte{infinityPointHeader}, make([]byte, signatureLengthBLSBLS12381-1)...) +var identityBLSSignature = append([]byte{infinityPointHeader}, make([]byte, SignatureLenBLSBLS12381-1)...) // IsBLSSignatureIdentity checks whether the input signature is // the identity signature (point at infinity in G1). @@ -327,9 +327,9 @@ func (a *blsBLS12381Algo) decodePrivateKey(privateKeyBytes []byte) (PrivateKey, // a faster check during signature verifications. Any verification against an identity // public key outputs `false`. func (a *blsBLS12381Algo) decodePublicKey(publicKeyBytes []byte) (PublicKey, error) { - if len(publicKeyBytes) != pubKeyLengthBLSBLS12381 { + if len(publicKeyBytes) != PubKeyLenBLSBLS12381 { return nil, invalidInputsErrorf("input length must be %d, got %d", - pubKeyLengthBLSBLS12381, len(publicKeyBytes)) + PubKeyLenBLSBLS12381, len(publicKeyBytes)) } var pk pubKeyBLSBLS12381 err := readPointE2(&pk.point, publicKeyBytes) @@ -415,7 +415,7 @@ func (sk *prKeyBLSBLS12381) PublicKey() PublicKey { // Encode returns a byte encoding of the private key. // The encoding is a raw encoding in big endian padded to the group order func (a *prKeyBLSBLS12381) Encode() []byte { - dest := make([]byte, prKeyLengthBLSBLS12381) + dest := make([]byte, frBytesLen) writeScalar(dest, &a.scalar) return dest } @@ -486,7 +486,7 @@ func (a *pubKeyBLSBLS12381) EncodeCompressed() []byte { if g2BytesLen != 2*fpBytesLen { panic("library is not configured to use compressed public key serialization") } - dest := make([]byte, pubKeyLengthBLSBLS12381) + dest := make([]byte, g2BytesLen) writePointE2(dest, &a.point) return dest } @@ -511,11 +511,6 @@ func (pk *pubKeyBLSBLS12381) String() string { return pk.point.String() } -// Get Macro definitions from the C layer as Cgo does not export macros -var signatureLengthBLSBLS12381 = int(C.get_signature_len()) -var pubKeyLengthBLSBLS12381 = int(C.get_pk_len()) -var prKeyLengthBLSBLS12381 = int(C.get_sk_len()) - // This is only a TEST function. // signWithXMDSHA256 signs a message using XMD_SHA256 as a hash to field. // diff --git a/crypto/bls12381_utils.go b/crypto/bls12381_utils.go index f0a6c77a12d..0cc7e75a509 100644 --- a/crypto/bls12381_utils.go +++ b/crypto/bls12381_utils.go @@ -70,7 +70,7 @@ func (a *scalar) String() string { } func (p *pointE2) String() string { - encoding := make([]byte, pubKeyLengthBLSBLS12381) + encoding := make([]byte, g2BytesLen) writePointE2(encoding, p) return fmt.Sprintf("%#x", encoding) } @@ -307,7 +307,7 @@ func hashToG1Bytes(data, dst []byte) []byte { } // serialize the point - pointBytes := make([]byte, signatureLengthBLSBLS12381) + pointBytes := make([]byte, g1BytesLen) writePointE1(pointBytes, &point) return pointBytes } diff --git a/crypto/bls_core.c b/crypto/bls_core.c index 6711320cf51..e1578a150fe 100644 --- a/crypto/bls_core.c +++ b/crypto/bls_core.c @@ -4,19 +4,6 @@ // The functions are tested for ALLOC=AUTO (not for ALLOC=DYNAMIC) -// functions to export macros to the Go layer (because cgo does not import macros) -int get_signature_len() { - return SIGNATURE_LEN; -} - -int get_pk_len() { - return PK_LEN; -} - -int get_sk_len() { - return SK_LEN; -} - // Computes a BLS signature from a G1 point and writes it in `out`. // `out` must be allocated properly with `G1_SER_BYTES` bytes. static void bls_sign_E1(byte* out, const Fr* sk, const E1* h) { @@ -93,7 +80,7 @@ int bls_verifyPerDistinctMessage(const byte* sig, if (!elemsG2) goto outG2; // elemsG1[0] = sig - if (E1_read_bytes(&elemsG1[0], sig, SIGNATURE_LEN) != BLST_SUCCESS) { + if (E1_read_bytes(&elemsG1[0], sig, G1_SER_BYTES) != BLST_SUCCESS) { ret = INVALID; goto out; } @@ -167,7 +154,7 @@ int bls_verifyPerDistinctKey(const byte* sig, if (!elemsG2) goto outG2; // elemsG1[0] = s - if (E1_read_bytes(&elemsG1[0], sig, SIGNATURE_LEN) != BLST_SUCCESS) { + if (E1_read_bytes(&elemsG1[0], sig, G1_SER_BYTES) != BLST_SUCCESS) { ret = INVALID; goto out; } @@ -243,7 +230,7 @@ int bls_verifyPerDistinctKey(const byte* sig, int bls_verify(const E2* pk, const byte* sig, const byte* hash, const int hash_len) { E1 s, h; // deserialize the signature into a curve point - if (E1_read_bytes(&s, sig, SIGNATURE_LEN) != BLST_SUCCESS) { + if (E1_read_bytes(&s, sig, G1_SER_BYTES) != BLST_SUCCESS) { return INVALID; } @@ -393,7 +380,7 @@ void bls_batch_verify(const int sigs_len, byte* results, const E2* pks_input, // the tree aggregations remain valid. // - valid points are multiplied by a random scalar (same for public keys at same index) // to make sure a signature at index (i) is verified against the public key at the same index. - int read_ret = E1_read_bytes(&sigs[i], &sigs_bytes[SIGNATURE_LEN*i], SIGNATURE_LEN); + int read_ret = E1_read_bytes(&sigs[i], &sigs_bytes[G1_SER_BYTES*i], G1_SER_BYTES); if (read_ret != BLST_SUCCESS || !E1_in_G1(&sigs[i])) { // set signature and key to infinity (no effect on the aggregation tree) // and set result to invalid (result won't be overwritten) diff --git a/crypto/bls_crossBLST_test.go b/crypto/bls_crossBLST_test.go index 7629289ba9e..3b3939eaf6c 100644 --- a/crypto/bls_crossBLST_test.go +++ b/crypto/bls_crossBLST_test.go @@ -80,7 +80,7 @@ func validSignatureBytesBLST(t *rapid.T) []byte { // testEncodeDecodePrivateKeyCrossBLST tests encoding and decoding of private keys are consistent with BLST. // This test assumes private key serialization is identical to the one in BLST. func testEncodeDecodePrivateKeyCrossBLST(t *rapid.T) { - randomSlice := rapid.SliceOfN(rapid.Byte(), prKeyLengthBLSBLS12381, prKeyLengthBLSBLS12381) + randomSlice := rapid.SliceOfN(rapid.Byte(), PrKeyLenBLSBLS12381, PrKeyLenBLSBLS12381) validSliceFlow := rapid.Custom(validPrivateKeyBytesFlow) validSliceBLST := rapid.Custom(validPrivateKeyBytesBLST) // skBytes are bytes of either a valid or a random private key @@ -154,7 +154,7 @@ func testEncodeDecodeG1CrossBLST(t *rapid.T) { // check both serializations of G1 points are equal if flowPass && blstPass { - sigFlowOutBytes := make([]byte, signatureLengthBLSBLS12381) + sigFlowOutBytes := make([]byte, g1BytesLen) writePointE1(sigFlowOutBytes, &pointFlow) sigBLSTOutBytes := pointBLST.Compress() assert.Equal(t, sigFlowOutBytes, sigBLSTOutBytes) diff --git a/crypto/bls_include.h b/crypto/bls_include.h index 4b8e1075501..1ca61b376c4 100644 --- a/crypto/bls_include.h +++ b/crypto/bls_include.h @@ -5,19 +5,8 @@ #include "bls12381_utils.h" -// Signature, Public key and Private key lengths -#define FULL_SIGNATURE_LEN G1_BYTES -#define FULL_PK_LEN G2_BYTES -#define SIGNATURE_LEN (FULL_SIGNATURE_LEN/(G1_SERIALIZATION+1)) -#define PK_LEN (FULL_PK_LEN/(G2_SERIALIZATION+1)) -#define SK_BITS (Fr_BITS) -#define SK_LEN BITS_TO_BYTES(SK_BITS) // bls core (functions in bls_core.c) -int get_signature_len(); -int get_pk_len(); -int get_sk_len(); - int bls_sign(byte*, const Fr*, const byte*, const int); int bls_verify(const E2*, const byte*, const byte*, const int); int bls_verifyPerDistinctMessage(const byte*, const int, const byte*, const uint32_t*, diff --git a/crypto/bls_multisig.go b/crypto/bls_multisig.go index 5714b7e2a34..6c99ae461e2 100644 --- a/crypto/bls_multisig.go +++ b/crypto/bls_multisig.go @@ -102,14 +102,14 @@ func AggregateBLSSignatures(sigs []Signature) (Signature, error) { } // flatten the shares (required by the C layer) - flatSigs := make([]byte, 0, signatureLengthBLSBLS12381*len(sigs)) + flatSigs := make([]byte, 0, SignatureLenBLSBLS12381*len(sigs)) for i, sig := range sigs { - if len(sig) != signatureLengthBLSBLS12381 { + if len(sig) != SignatureLenBLSBLS12381 { return nil, fmt.Errorf("signature at index %d has an invalid length: %w", i, invalidSignatureError) } flatSigs = append(flatSigs, sig...) } - aggregatedSig := make([]byte, signatureLengthBLSBLS12381) + aggregatedSig := make([]byte, SignatureLenBLSBLS12381) // add the points in the C layer result := C.E1_sum_vector_byte( @@ -325,7 +325,7 @@ func VerifyBLSSignatureManyMessages( ) (bool, error) { // check signature length - if len(s) != signatureLengthBLSBLS12381 { + if len(s) != SignatureLenBLSBLS12381 { return false, nil } // check the list lengths @@ -494,7 +494,7 @@ func BatchVerifyBLSSignaturesOneMessage( } // flatten the shares (required by the C layer) - flatSigs := make([]byte, 0, signatureLengthBLSBLS12381*len(sigs)) + flatSigs := make([]byte, 0, SignatureLenBLSBLS12381*len(sigs)) pkPoints := make([]pointE2, 0, len(pks)) getIdentityPoint := func() pointE2 { @@ -508,7 +508,7 @@ func BatchVerifyBLSSignaturesOneMessage( return falseSlice, fmt.Errorf("key at index %d is invalid: %w", i, notBLSKeyError) } - if len(sigs[i]) != signatureLengthBLSBLS12381 || pkBLS.isIdentity { + if len(sigs[i]) != SignatureLenBLSBLS12381 || pkBLS.isIdentity { // case of invalid signature: set the signature and public key at index `i` // to identities so that there is no effect on the aggregation tree computation. // However, the boolean return for index `i` is set to `false` and won't be overwritten. diff --git a/crypto/bls_test.go b/crypto/bls_test.go index 801af0a24a5..d8561ccc5f6 100644 --- a/crypto/bls_test.go +++ b/crypto/bls_test.go @@ -411,7 +411,7 @@ func TestBLSAggregateSignatures(t *testing.T) { assert.False(t, result) // test with a signature of a wrong length - shortSig := sigs[0][:signatureLengthBLSBLS12381-1] + shortSig := sigs[0][:SignatureLenBLSBLS12381-1] aggSig, err = AggregateBLSSignatures([]Signature{shortSig}) assert.Error(t, err) assert.True(t, IsInvalidSignatureError(err)) @@ -1199,7 +1199,7 @@ func TestBLSIdentity(t *testing.T) { sk := randomSK(t, rand) sig, err := sk.Sign(msg, hasher) require.NoError(t, err) - oppositeSig := make([]byte, signatureLengthBLSBLS12381) + oppositeSig := make([]byte, SignatureLenBLSBLS12381) copy(oppositeSig, sig) negatePoint(oppositeSig) aggSig, err := AggregateBLSSignatures([]Signature{sig, oppositeSig}) diff --git a/crypto/bls_thresholdsign.go b/crypto/bls_thresholdsign.go index a7eaad5a2a4..2f05ed72c42 100644 --- a/crypto/bls_thresholdsign.go +++ b/crypto/bls_thresholdsign.go @@ -399,10 +399,10 @@ func (s *blsThresholdSignatureInspector) reconstructThresholdSignature() (Signat return nil, notEnoughSharesErrorf("number of signature shares %d is not enough, %d are required", len(s.shares), s.threshold+1) } - thresholdSignature := make([]byte, signatureLengthBLSBLS12381) + thresholdSignature := make([]byte, SignatureLenBLSBLS12381) // prepare the C layer inputs - shares := make([]byte, 0, len(s.shares)*signatureLengthBLSBLS12381) + shares := make([]byte, 0, len(s.shares)*SignatureLenBLSBLS12381) signers := make([]index, 0, len(s.shares)) for index, share := range s.shares { shares = append(shares, share...) @@ -482,7 +482,7 @@ func BLSReconstructThresholdSignature(size int, threshold int, m := make(map[index]bool) // flatten the shares (required by the C layer) - flatShares := make([]byte, 0, signatureLengthBLSBLS12381*(threshold+1)) + flatShares := make([]byte, 0, SignatureLenBLSBLS12381*(threshold+1)) indexSigners := make([]index, 0, threshold+1) for i, share := range shares { flatShares = append(flatShares, share...) @@ -500,7 +500,7 @@ func BLSReconstructThresholdSignature(size int, threshold int, indexSigners = append(indexSigners, index(signers[i])+1) } - thresholdSignature := make([]byte, signatureLengthBLSBLS12381) + thresholdSignature := make([]byte, SignatureLenBLSBLS12381) // Lagrange Interpolate at point 0 if C.E1_lagrange_interpolate_at_zero_write( (*C.uchar)(&thresholdSignature[0]), diff --git a/crypto/spock.go b/crypto/spock.go index 8180b9b72bd..da269c23ac1 100644 --- a/crypto/spock.go +++ b/crypto/spock.go @@ -73,7 +73,7 @@ func SPOCKVerify(pk1 PublicKey, proof1 Signature, pk2 PublicKey, proof2 Signatur return false, notBLSKeyError } - if len(proof1) != signatureLengthBLSBLS12381 || len(proof2) != signatureLengthBLSBLS12381 { + if len(proof1) != g1BytesLen || len(proof2) != g1BytesLen { return false, nil } From a88897291207f75d455f82e8af45dd863e444198 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Mon, 29 May 2023 15:23:51 -0600 Subject: [PATCH 07/11] cgo supports macros! use C constant macros in go --- crypto/bls.go | 8 +++----- crypto/bls12381_utils.c | 33 +-------------------------------- crypto/bls12381_utils.go | 33 +++++++++++++++------------------ crypto/bls12381_utils.h | 9 --------- 4 files changed, 19 insertions(+), 64 deletions(-) diff --git a/crypto/bls.go b/crypto/bls.go index f515a9445dc..9930db2a3b4 100644 --- a/crypto/bls.go +++ b/crypto/bls.go @@ -59,13 +59,11 @@ const ( // Cipher suite used for BLS PoP of the form : BLS_POP_ || h2cSuiteID || SchemeTag_ // The PoP cipher suite is guaranteed to be different than all signature ciphersuites blsPOPCipherSuite = "BLS_POP_" + h2cSuiteID + schemeTag + // expandMsgOutput is the output length of the expand_message step as required by the + // hash_to_curve algorithm (and the map to G1 step). + expandMsgOutput = int(C.MAP_TO_G1_INPUT_LEN) ) -// expandMsgOutput is the output length of the expand_message step as required by the -// hash_to_curve algorithm (and the map to G1 step). -// (Cgo does not export C macros) -var expandMsgOutput = int(C.get_mapToG1_input_len()) - // blsBLS12381Algo, embeds SignAlgo type blsBLS12381Algo struct { // the signing algo and parameters diff --git a/crypto/bls12381_utils.c b/crypto/bls12381_utils.c index 00d32ccdfed..5edbb92b78e 100644 --- a/crypto/bls12381_utils.c +++ b/crypto/bls12381_utils.c @@ -9,37 +9,6 @@ // compile all blst C src along with this file #include "blst_src.c" -// The functions are tested for ALLOC=AUTO (not for ALLOC=DYNAMIC) - -// return macro values to the upper Go Layer -int get_valid() { - return VALID; -} - -int get_invalid() { - return INVALID; -} - -int get_Fr_BYTES() { - return Fr_BYTES; -} - -int get_Fp_BYTES() { - return Fp_BYTES; -} - -int get_G1_SER_BYTES() { - return G1_SER_BYTES; -} - -int get_G2_SER_BYTES() { - return G2_SER_BYTES; -} - -int get_mapToG1_input_len() { - return MAP_TO_G1_INPUT_LEN; -} - // ------------------- Fr utilities // Montgomery constant R related to the curve order r @@ -47,7 +16,7 @@ int get_mapToG1_input_len() { const Fr BLS12_381_rR = {{ \ TO_LIMB_T(0x1824b159acc5056f), TO_LIMB_T(0x998c4fefecbc4ff5), \ TO_LIMB_T(0x5884b7fa00034802), TO_LIMB_T(0x00000001fffffffe), \ - }}; +}}; // returns true if a == 0 and false otherwise bool Fr_is_zero(const Fr* a) { diff --git a/crypto/bls12381_utils.go b/crypto/bls12381_utils.go index 0cc7e75a509..75b9385d3ab 100644 --- a/crypto/bls12381_utils.go +++ b/crypto/bls12381_utils.go @@ -44,24 +44,21 @@ type scalar C.Fr // TODO: For now scalars are represented as field elements Fr since all scalars // are less than r - check if distinguishing two types in necessary -// BLS12-381 related lengths, exported as functions -// because cgo does not recognize C macros. -var frBytesLen = int(C.get_Fr_BYTES()) -var g1BytesLen = int(C.get_G1_SER_BYTES()) -var g2BytesLen = int(C.get_G2_SER_BYTES()) -var fpBytesLen = int(C.get_Fp_BYTES()) - -// get some constants from the C layer -// (Cgo does not export C macros) -var valid = C.get_valid() -var invalid = C.get_invalid() - -// get some constants from the C layer -// var blst_errors = C.blst_get_errors() -var blst_valid = (int)(C.BLST_SUCCESS) -var blst_bad_encoding = (int)(C.BLST_BAD_ENCODING) -var blst_bad_scalar = (int)(C.BLST_BAD_SCALAR) -var blst_point_not_on_curve = (int)(C.BLST_POINT_NOT_ON_CURVE) +const ( + // BLS12-381 related lengths imported from the C layer + frBytesLen = int(C.Fr_BYTES) + g1BytesLen = int(C.G1_SER_BYTES) + g2BytesLen = int(C.G2_SER_BYTES) + fpBytesLen = int(C.Fp_BYTES) + + // more internal constants from the C layer + valid = C.VALID + invalid = C.INVALID + blst_valid = int(C.BLST_SUCCESS) + blst_bad_encoding = int(C.BLST_BAD_ENCODING) + blst_bad_scalar = int(C.BLST_BAD_SCALAR) + blst_point_not_on_curve = int(C.BLST_POINT_NOT_ON_CURVE) +) func (a *scalar) String() string { encoding := make([]byte, frBytesLen) diff --git a/crypto/bls12381_utils.h b/crypto/bls12381_utils.h index f3800e6ebef..48a7b1476de 100644 --- a/crypto/bls12381_utils.h +++ b/crypto/bls12381_utils.h @@ -42,15 +42,6 @@ typedef _Bool bool; // assuming cgo is using a modern enough compiler #define G1_SER_BYTES (G1_BYTES/(G1_SERIALIZATION+1)) #define G2_SER_BYTES (G2_BYTES/(G2_SERIALIZATION+1)) - -int get_valid(); -int get_invalid(); -int get_Fr_BYTES(); -int get_Fp_BYTES(); -int get_G1_SER_BYTES(); -int get_G2_SER_BYTES(); -int get_mapToG1_input_len(); - // BLS based SPoCK int bls_spock_verify(const E2*, const byte*, const E2*, const byte*); From 945f2b9fb3feca4bb5aeeb5efcbfeffd6411647f Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Mon, 29 May 2023 19:16:32 -0600 Subject: [PATCH 08/11] define new internal ERROR type to abstract BLST_ERROR --- crypto/bls.go | 4 +- crypto/bls12381_utils.c | 270 +++++++++++++------------------- crypto/bls12381_utils.go | 49 +++--- crypto/bls12381_utils.h | 61 ++++---- crypto/bls_core.c | 58 ++++++- crypto/bls_include.h | 6 +- crypto/bls_multisig.go | 7 - crypto/bls_thresholdsign_core.c | 4 +- crypto/blst_include.h | 5 - crypto/dkg_core.c | 6 +- crypto/dkg_feldmanvss.go | 2 +- crypto/dkg_include.h | 2 +- 12 files changed, 231 insertions(+), 243 deletions(-) diff --git a/crypto/bls.go b/crypto/bls.go index 9930db2a3b4..93dd487a817 100644 --- a/crypto/bls.go +++ b/crypto/bls.go @@ -39,16 +39,14 @@ import ( "github.com/onflow/flow-go/crypto/hash" ) -var ( +const ( // SignatureLenBLSBLS12381 is the size of a `G_1` element. SignatureLenBLSBLS12381 = g1BytesLen // PubKeyLenBLSBLS12381 is the size of a `G_2` element. PubKeyLenBLSBLS12381 = g2BytesLen // PrKeyLenBLSBLS12381 is the size of a `F_r` element, where `r` is the order of `G_1` and `G_2`. PrKeyLenBLSBLS12381 = frBytesLen -) -const ( // Hash to curve params // hash to curve suite ID of the form : CurveID_ || HashID_ || MapID_ || encodingVariant_ h2cSuiteID = "BLS12381G1_XOF:KMAC128_SSWU_RO_" diff --git a/crypto/bls12381_utils.c b/crypto/bls12381_utils.c index 5edbb92b78e..35bf1ff4686 100644 --- a/crypto/bls12381_utils.c +++ b/crypto/bls12381_utils.c @@ -172,40 +172,40 @@ static void pow256_from_Fr(pow256 ret, const Fr* in) { // reads a scalar in `a` and checks it is a valid Fr element (a < r). // input is bytes-big-endian. // returns: -// - BLST_BAD_ENCODING if the length is invalid -// - BLST_BAD_SCALAR if the scalar isn't in Fr -// - BLST_SUCCESS if the scalar is valid -BLST_ERROR Fr_read_bytes(Fr* a, const byte *bin, int len) { +// - BAD_ENCODING if the length is invalid +// - BAD_VALUE if the scalar isn't in Fr +// - VALID if the scalar is valid +ERROR Fr_read_bytes(Fr* a, const byte *bin, int len) { if (len != Fr_BYTES) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } pow256 tmp; // compare to r using the provided tool from BLST pow256_from_be_bytes(tmp, bin); // TODO: check endianness!! if (!check_mod_256(tmp, BLS12_381_r)) { // check_mod_256 compares pow256 against a vec256! - return BLST_BAD_SCALAR; + return BAD_VALUE; } vec_zero(tmp, sizeof(tmp)); limbs_from_be_bytes((limb_t*)a, bin, Fr_BYTES); // TODO: check endianness!! - return BLST_SUCCESS; + return VALID; } // reads a scalar in `a` and checks it is a valid Fr_star element (0 < a < r). // input bytes are big endian. // returns: -// - BLST_BAD_ENCODING if the length is invalid -// - BLST_BAD_SCALAR if the scalar isn't in Fr_star -// - BLST_SUCCESS if the scalar is valid -BLST_ERROR Fr_star_read_bytes(Fr* a, const byte *bin, int len) { +// - BAD_ENCODING if the length is invalid +// - BAD_VALUE if the scalar isn't in Fr_star +// - VALID if the scalar is valid +ERROR Fr_star_read_bytes(Fr* a, const byte *bin, int len) { int ret = Fr_read_bytes(a, bin, len); - if (ret != BLST_SUCCESS) { + if (ret != VALID) { return ret; } // check if a=0 if (Fr_is_zero(a)) { - return BLST_BAD_SCALAR; + return BAD_VALUE; } - return BLST_SUCCESS; + return VALID; } // write Fr element `a` in big endian bytes. @@ -329,19 +329,19 @@ void Fp_from_montg(Fp *res, const Fp *a) { // reads a scalar in `a` and checks it is a valid Fp element (a < p). // input is bytes-big-endian. // returns: -// - BLST_BAD_ENCODING if the length is invalid -// - BLST_BAD_SCALAR if the scalar isn't in Fp -// - BLST_SUCCESS if the scalar is valid -BLST_ERROR Fp_read_bytes(Fp* a, const byte *bin, int len) { +// - BAD_ENCODING if the length is invalid +// - BAD_VALUE if the scalar isn't in Fp +// - VALID if the scalar is valid +ERROR Fp_read_bytes(Fp* a, const byte *bin, int len) { if (len != Fp_BYTES) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } limbs_from_be_bytes((limb_t*)a, bin, Fp_BYTES); // compare read scalar to p if (!Fp_check(a)) { - return BLST_BAD_ENCODING; + return BAD_VALUE; } - return BLST_SUCCESS; + return VALID; } @@ -413,22 +413,22 @@ static byte Fp2_get_sign(Fp2* y) { // input is a serialization of real(a) concatenated to serializetion of imag(a). // a[i] are both Fp elements. // returns: -// - BLST_BAD_ENCODING if the length is invalid -// - BLST_BAD_SCALAR if the scalar isn't in Fp -// - BLST_SUCCESS if the scalar is valid -static BLST_ERROR Fp2_read_bytes(Fp2* a, const byte *bin, int len) { +// - BAD_ENCODING if the length is invalid +// - BAD_VALUE if the scalar isn't in Fp +// - VALID if the scalar is valid +static ERROR Fp2_read_bytes(Fp2* a, const byte *bin, int len) { if (len != Fp2_BYTES) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } - BLST_ERROR ret = Fp_read_bytes(&real(a), bin, Fp_BYTES); - if (ret != BLST_SUCCESS) { + ERROR ret = Fp_read_bytes(&real(a), bin, Fp_BYTES); + if (ret != VALID) { return ret; } ret = Fp_read_bytes(&imag(a), bin + Fp_BYTES, Fp_BYTES); - if ( ret != BLST_SUCCESS) { + if ( ret != VALID) { return ret; } - return BLST_SUCCESS; + return VALID; } // write Fp2 element to bin and assume `bin` has `Fp2_BYTES` allocated bytes. @@ -494,23 +494,23 @@ bool E1_in_G1(const E1* p){ // https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-zcash-serialization-format-) // // returns: -// - BLST_BAD_ENCODING if the length is invalid or serialization header bits are invalid -// - BLST_BAD_SCALAR if Fp coordinates couldn't deserialize -// - BLST_POINT_NOT_ON_CURVE if deserialized point isn't on E1 -// - BLST_SUCCESS if deserialization is valid +// - BAD_ENCODING if the length is invalid or serialization header bits are invalid +// - BAD_VALUE if Fp coordinates couldn't deserialize +// - POINT_NOT_ON_CURVE if deserialized point isn't on E1 +// - VALID if deserialization is valid // TODO: replace with POINTonE1_Deserialize_BE and POINTonE1_Uncompress_Z, // and update logic with G2 subgroup check? -BLST_ERROR E1_read_bytes(E1* a, const byte *bin, const int len) { +ERROR E1_read_bytes(E1* a, const byte *bin, const int len) { // check the length if (len != G1_SER_BYTES) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } // check the compression bit int compressed = bin[0] >> 7; if ((compressed == 1) != (G1_SERIALIZATION == COMPRESSED)) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } // check if the point in infinity @@ -518,29 +518,29 @@ BLST_ERROR E1_read_bytes(E1* a, const byte *bin, const int len) { if (is_infinity) { // the remaining bits need to be cleared if (bin[0] & 0x3F) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } for (int i=1; i> 5) & 1; if (y_sign && (!compressed)) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } // use a temporary buffer to mask the header bits and read a.x byte temp[Fp_BYTES]; memcpy(temp, bin, Fp_BYTES); temp[0] &= 0x1F; // clear the header bits - BLST_ERROR ret = Fp_read_bytes(&a->x, temp, sizeof(temp)); - if (ret != BLST_SUCCESS) { + ERROR ret = Fp_read_bytes(&a->x, temp, sizeof(temp)); + if (ret != VALID) { return ret; } @@ -549,14 +549,14 @@ BLST_ERROR E1_read_bytes(E1* a, const byte *bin, const int len) { if (G1_SERIALIZATION == UNCOMPRESSED) { ret = Fp_read_bytes(&a->y, bin + Fp_BYTES, sizeof(a->y)); - if (ret != BLST_SUCCESS){ + if (ret != VALID){ return ret; } // check read point is on curve if (!E1_affine_on_curve(a)) { - return BLST_POINT_NOT_ON_CURVE; + return POINT_NOT_ON_CURVE; } - return BLST_SUCCESS; + return VALID; } // compute the possible square root @@ -565,13 +565,13 @@ BLST_ERROR E1_read_bytes(E1* a, const byte *bin, const int len) { Fp_mul_montg(&a->y, &a->y, &a->x); // x^3 Fp_add(&a->y, &a->y, &B_E1); // B_E1 is already in Montg form if (!Fp_sqrt_montg(&a->y, &a->y)) // check whether x^3+b is a quadratic residue - return BLST_POINT_NOT_ON_CURVE; + return POINT_NOT_ON_CURVE; // resulting (x,y) is guaranteed to be on curve (y is already in Montg form) if (Fp_get_sign(&a->y) != y_sign) { Fp_neg(&a->y, &a->y); // flip y sign if needed } - return BLST_SUCCESS; + return VALID; } // E1_write_bytes exports a point in E1(Fp) to a buffer in a compressed or uncompressed form. @@ -649,7 +649,7 @@ int E1_sum_vector_byte(byte* dest, const byte* sigs_bytes, const int sigs_len) { // import the points from the array for (int i=0; i < n; i++) { // deserialize each point from the input array - if (E1_read_bytes(&sigs[i], &sigs_bytes[G1_SER_BYTES*i], G1_SER_BYTES) != BLST_SUCCESS) { + if (E1_read_bytes(&sigs[i], &sigs_bytes[G1_SER_BYTES*i], G1_SER_BYTES) != VALID) { error = INVALID; goto out; } @@ -706,9 +706,46 @@ int map_to_G1(E1* h, const byte* hash, const int len) { return VALID; } +// maps the bytes to a point in G1. +// `len` should be at least Fr_BYTES. +// this is a testing file only, should not be used in any protocol! +void unsafe_map_bytes_to_G1(E1* p, const byte* bytes, int len) { + assert(len >= Fr_BYTES); + // map to Fr + Fr log; + map_bytes_to_Fr(&log, bytes, len); + // multiplies G1 generator by a random scalar + G1_mult_gen(p, &log); +} + +// generates a point in E1\G1 and stores it in p +// this is a testing file only, should not be used in any protocol! +ERROR unsafe_map_bytes_to_G1complement(E1* p, const byte* bytes, int len) { + assert(G1_SERIALIZATION == COMPRESSED); + assert(len >= G1_SER_BYTES); + + // attempt to deserilize a compressed E1 point from input bytes + // after fixing the header 2 bits + byte copy[G1_SER_BYTES]; + memcpy(copy, bytes, sizeof(copy)); + copy[0] |= 1<<7; // set compression bit + copy[0] &= ~(1<<6); // clear infinity bit - point is not infinity + + ERROR ser = E1_read_bytes(p, copy, G1_SER_BYTES); + if (ser != VALID) { + return ser; + } + + // map the point to E2\G2 by clearing G2 order + E1_mult(p, p, (const Fr*)BLS12_381_r); + E1_to_affine(p, p); + + assert(E1_affine_on_curve(p)); // sanity check to make sure p is in E2 + return VALID; +} + // ------------------- E2 utilities -const E1* BLS12_381_g1 = (const E1*)&BLS12_381_G1; /// TODO:delete const E2* BLS12_381_g2 = (const E2*)&BLS12_381_G2; const E2* BLS12_381_minus_g2 = (const E2*)&BLS12_381_NEG_G2; @@ -716,23 +753,23 @@ const E2* BLS12_381_minus_g2 = (const E2*)&BLS12_381_NEG_G2; // The resulting point is guaranteed to be on curve E2 (no G2 check is included). // // returns: -// - BLST_BAD_ENCODING if the length is invalid or serialization header bits are invalid -// - BLST_BAD_SCALAR if Fp^2 coordinates couldn't deserialize -// - BLST_POINT_NOT_ON_CURVE if deserialized point isn't on E2 -// - BLST_SUCCESS if deserialization is valid +// - BAD_ENCODING if the length is invalid or serialization header bits are invalid +// - BAD_VALUE if Fp^2 coordinates couldn't deserialize +// - POINT_NOT_ON_CURVE if deserialized point isn't on E2 +// - VALID if deserialization is valid // TODO: replace with POINTonE2_Deserialize_BE and POINTonE2_Uncompress_Z, // and update logic with G2 subgroup check? -BLST_ERROR E2_read_bytes(E2* a, const byte *bin, const int len) { +ERROR E2_read_bytes(E2* a, const byte *bin, const int len) { // check the length if (len != G2_SER_BYTES) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } // check the compression bit int compressed = bin[0] >> 7; if ((compressed == 1) != (G2_SERIALIZATION == COMPRESSED)) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } // check if the point in infinity @@ -740,29 +777,29 @@ BLST_ERROR E2_read_bytes(E2* a, const byte *bin, const int len) { if (is_infinity) { // the remaining bits need to be cleared if (bin[0] & 0x3F) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } for (int i=1; i> 5) & 1; if (y_sign && (!compressed)) { - return BLST_BAD_ENCODING; + return BAD_ENCODING; } // use a temporary buffer to mask the header bits and read a.x byte temp[Fp2_BYTES]; memcpy(temp, bin, Fp2_BYTES); temp[0] &= 0x1F; // clear the header bits - BLST_ERROR ret = Fp2_read_bytes(&a->x, temp, sizeof(temp)); - if (ret != BLST_SUCCESS) { + ERROR ret = Fp2_read_bytes(&a->x, temp, sizeof(temp)); + if (ret != VALID) { return ret; } @@ -773,14 +810,14 @@ BLST_ERROR E2_read_bytes(E2* a, const byte *bin, const int len) { if (G2_SERIALIZATION == UNCOMPRESSED) { ret = Fp2_read_bytes(&(a->y), bin + Fp2_BYTES, sizeof(a->y)); - if (ret != BLST_SUCCESS){ + if (ret != VALID){ return ret; } // check read point is on curve if (!E2_affine_on_curve(a)) { - return BLST_POINT_NOT_ON_CURVE; + return POINT_NOT_ON_CURVE; } - return BLST_SUCCESS; + return VALID; } // compute the possible square root @@ -793,13 +830,13 @@ BLST_ERROR E2_read_bytes(E2* a, const byte *bin, const int len) { Fp2_mul_montg(a_y, a_y, a_x); Fp2_add(a_y, a_y, &B_E2); // B_E2 is already in Montg form if (!Fp2_sqrt_montg(a_y, a_y)) // check whether x^3+b is a quadratic residue - return BLST_POINT_NOT_ON_CURVE; + return POINT_NOT_ON_CURVE; // resulting (x,y) is guaranteed to be on curve (y is already in Montg form) if (Fp2_get_sign(a_y) != y_sign) { Fp2_neg(a_y, a_y); // flip y sign if needed } - return BLST_SUCCESS; + return VALID; } // E2_write_bytes exports a point in E2(Fp^2) to a buffer in a compressed or uncompressed form. @@ -929,52 +966,6 @@ void E2_sum_vector(E2* sum, const E2* y, const int len){ } } -// ------------------- other - - -// Verifies the validity of 2 SPoCK proofs and 2 public keys. -// Membership check in G1 of both proofs is verified in this function. -// Membership check in G2 of both keys is not verified in this function. -// the membership check in G2 is separated to allow optimizing multiple verifications -// using the same public keys. -int bls_spock_verify(const E2* pk1, const byte* sig1, const E2* pk2, const byte* sig2) { - E1 elemsG1[2]; - E2 elemsG2[2]; - - // elemsG1[0] = s1 - if (E1_read_bytes(&elemsG1[0], sig1, G1_SER_BYTES) != BLST_SUCCESS) { - return INVALID; - }; - // check s1 is in G1 - if (!E1_in_G1(&elemsG1[0])) { - return INVALID; - } - - // elemsG1[1] = s2 - if (E1_read_bytes(&elemsG1[1], sig2, G1_SER_BYTES) != BLST_SUCCESS) { - return INVALID; - }; - // check s2 is in G1 - if (!E1_in_G1(&elemsG1[1])) { - return INVALID; - } - - // elemsG2[1] = pk1 - E2_copy(&elemsG2[1], pk1); - - // elemsG2[0] = -pk2 - E2_neg(&elemsG2[0], pk2); - - // double pairing - Fp12 e; - multi_pairing(&e, elemsG1 , elemsG2, 2); - - if (Fp12_is_one(&e)) { - return VALID; - } - return INVALID; -} - // Subtracts all G2 array elements `y` from an element `x` and writes the // result in res void E2_subtract_vector(E2* res, const E2* x, const E2* y, const int len){ @@ -983,45 +974,6 @@ void E2_subtract_vector(E2* res, const E2* x, const E2* y, const int len){ E2_add(res, x, res); } - -// maps the bytes to a point in G1. -// `len` should be at least Fr_BYTES. -// this is a testing file only, should not be used in any protocol! -void unsafe_map_bytes_to_G1(E1* p, const byte* bytes, int len) { - assert(len >= Fr_BYTES); - // map to Fr - Fr log; - map_bytes_to_Fr(&log, bytes, len); - // multiplies G1 generator by a random scalar - G1_mult_gen(p, &log); -} - -// generates a point in E1\G1 and stores it in p -// this is a testing file only, should not be used in any protocol! -BLST_ERROR unsafe_map_bytes_to_G1complement(E1* p, const byte* bytes, int len) { - assert(G1_SERIALIZATION == COMPRESSED); - assert(len >= G1_SER_BYTES); - - // attempt to deserilize a compressed E1 point from input bytes - // after fixing the header 2 bits - byte copy[G1_SER_BYTES]; - memcpy(copy, bytes, sizeof(copy)); - copy[0] |= 1<<7; // set compression bit - copy[0] &= ~(1<<6); // clear infinity bit - point is not infinity - - BLST_ERROR ser = E1_read_bytes(p, copy, G1_SER_BYTES); - if (ser != BLST_SUCCESS) { - return ser; - } - - // map the point to E2\G2 by clearing G2 order - E1_mult(p, p, (const Fr*)BLS12_381_r); - E1_to_affine(p, p); - - assert(E1_affine_on_curve(p)); // sanity check to make sure p is in E2 - return BLST_SUCCESS; -} - // maps the bytes to a point in G2. // `len` should be at least Fr_BYTES. // this is a testing tool only, it should not be used in any protocol! @@ -1035,11 +987,11 @@ void unsafe_map_bytes_to_G2(E2* p, const byte* bytes, int len) { } // attempts to map `bytes` to a point in E2\G2 and stores it in p. -// `len` should be at least G2_SER_BYTES. It returns BLST_SUCCESS only if mapping +// `len` should be at least G2_SER_BYTES. It returns VALID only if mapping // succeeds. // For now, function only works when E2 serialization is compressed. // this is a testing tool only, it should not be used in any protocol! -BLST_ERROR unsafe_map_bytes_to_G2complement(E2* p, const byte* bytes, int len) { +ERROR unsafe_map_bytes_to_G2complement(E2* p, const byte* bytes, int len) { assert(G2_SERIALIZATION == COMPRESSED); assert(len >= G2_SER_BYTES); @@ -1050,8 +1002,8 @@ BLST_ERROR unsafe_map_bytes_to_G2complement(E2* p, const byte* bytes, int len) { copy[0] |= 1<<7; // set compression bit copy[0] &= ~(1<<6); // clear infinity bit - point is not infinity - BLST_ERROR ser = E2_read_bytes(p, copy, G2_SER_BYTES); - if (ser != BLST_SUCCESS) { + ERROR ser = E2_read_bytes(p, copy, G2_SER_BYTES); + if (ser != VALID) { return ser; } @@ -1060,7 +1012,7 @@ BLST_ERROR unsafe_map_bytes_to_G2complement(E2* p, const byte* bytes, int len) { E2_to_affine(p, p); assert(E2_affine_on_curve(p)); // sanity check to make sure p is in E2 - return BLST_SUCCESS; + return VALID; } // ------------------- Pairing utilities @@ -1079,7 +1031,7 @@ void Fp12_set_one(Fp12 *a) { // It assumes `p` and `q` are correctly initialized and all // p[i] and q[i] are respectively on G1 and G2 (it does not // check their memberships). -void multi_pairing(Fp12* res, const E1 *p, const E2 *q, const int len) { +void Fp12_multi_pairing(Fp12* res, const E1 *p, const E2 *q, const int len) { // easier access pointer vec384fp6* res_vec = (vec384fp6*)res; // N_MAX is defined within BLST. It should represent a good tradeoff of the max number diff --git a/crypto/bls12381_utils.go b/crypto/bls12381_utils.go index 75b9385d3ab..812319ced63 100644 --- a/crypto/bls12381_utils.go +++ b/crypto/bls12381_utils.go @@ -41,23 +41,23 @@ type pointE1 C.E1 type pointE2 C.E2 type scalar C.Fr -// TODO: For now scalars are represented as field elements Fr since all scalars -// are less than r - check if distinguishing two types in necessary +// Note that scalars and field elements F_r are represented in Go by the same type +// called `scalar`, which is internally represented by C type `Fr`. Scalars used by the +// Go layer are all reduced modulo the curve order `r`. const ( // BLS12-381 related lengths imported from the C layer frBytesLen = int(C.Fr_BYTES) + fpBytesLen = int(C.Fp_BYTES) g1BytesLen = int(C.G1_SER_BYTES) g2BytesLen = int(C.G2_SER_BYTES) - fpBytesLen = int(C.Fp_BYTES) - // more internal constants from the C layer - valid = C.VALID - invalid = C.INVALID - blst_valid = int(C.BLST_SUCCESS) - blst_bad_encoding = int(C.BLST_BAD_ENCODING) - blst_bad_scalar = int(C.BLST_BAD_SCALAR) - blst_point_not_on_curve = int(C.BLST_POINT_NOT_ON_CURVE) + // error constants imported from the C layer + valid = C.VALID + invalid = C.INVALID + badEncoding = C.BAD_ENCODING + badValue = C.BAD_VALUE + pointNotOnCurve = C.POINT_NOT_ON_CURVE ) func (a *scalar) String() string { @@ -173,18 +173,17 @@ func readScalarFrStar(a *scalar, src []byte) error { (*C.uchar)(&src[0]), (C.int)(len(src))) - switch int(read) { - case blst_valid: + switch read { + case valid: return nil - case blst_bad_encoding: + case badEncoding: return invalidInputsErrorf("input length must be %d, got %d", frBytesLen, len(src)) - case blst_bad_scalar: + case badValue: return invalidInputsErrorf("scalar is not in the correct range w.r.t the BLS12-381 curve") default: return invalidInputsErrorf("reading the scalar failed") } - } // readPointE2 reads a E2 point from a slice of bytes @@ -196,12 +195,12 @@ func readPointE2(a *pointE2, src []byte) error { (*C.uchar)(&src[0]), (C.int)(len(src))) - switch int(read) { - case blst_valid: + switch read { + case valid: return nil - case blst_bad_encoding, blst_bad_scalar: + case badEncoding, badValue: return invalidInputsErrorf("input could not deserialize to a E2 point") - case blst_point_not_on_curve: + case pointNotOnCurve: return invalidInputsErrorf("input is not a point on curve E2") default: return errors.New("reading E2 point failed") @@ -217,12 +216,12 @@ func readPointE1(a *pointE1, src []byte) error { (*C.uchar)(&src[0]), (C.int)(len(src))) - switch int(read) { - case blst_valid: + switch read { + case valid: return nil - case blst_bad_encoding, blst_bad_scalar: + case badEncoding, badValue: return invalidInputsErrorf("input could not deserialize to a E1 point") - case blst_point_not_on_curve: + case pointNotOnCurve: return invalidInputsErrorf("input is not a point on curve E1") default: return errors.New("reading E1 point failed") @@ -263,7 +262,7 @@ func unsafeMapToG1(pt *pointE1, seed []byte) { // It generates a random point in E2\G2 and stores it in input point. func unsafeMapToG1Complement(pt *pointE1, seed []byte) bool { res := C.unsafe_map_bytes_to_G1complement((*C.E1)(pt), (*C.uchar)(&seed[0]), (C.int)(len(seed))) - return int(res) == blst_valid + return int(res) == valid } // unsafeMapToG2 is a test function, it wraps a call to C since cgo can't be used in go test files. @@ -277,7 +276,7 @@ func unsafeMapToG2(pt *pointE2, seed []byte) { // It generates a random point in E2\G2 and stores it in input point. func unsafeMapToG2Complement(pt *pointE2, seed []byte) bool { res := C.unsafe_map_bytes_to_G2complement((*C.E2)(pt), (*C.uchar)(&seed[0]), (C.int)(len(seed))) - return int(res) == blst_valid + return int(res) == valid } // This is only a TEST function. diff --git a/crypto/bls12381_utils.h b/crypto/bls12381_utils.h index 48a7b1476de..921df90624d 100644 --- a/crypto/bls12381_utils.h +++ b/crypto/bls12381_utils.h @@ -11,10 +11,18 @@ typedef uint8_t byte; typedef _Bool bool; // assuming cgo is using a modern enough compiler +// minimum targeted security level #define SEC_BITS 128 -#define VALID 0 -#define INVALID 1 -#define UNDEFINED (((VALID&1)^1) | ((INVALID&2)^2)) // different value than RLC_OK and RLC_ERR + +typedef enum { + VALID = 0, + INVALID, + BAD_ENCODING, + BAD_VALUE, + POINT_NOT_ON_CURVE, + POINT_NOT_IN_GROUP, + UNDEFINED, +} ERROR; #define BITS_TO_BYTES(x) ((x+7)>>3) #define BITS_TO_LIMBS(x) ((x+63)>>6) @@ -42,13 +50,10 @@ typedef _Bool bool; // assuming cgo is using a modern enough compiler #define G1_SER_BYTES (G1_BYTES/(G1_SERIALIZATION+1)) #define G2_SER_BYTES (G2_BYTES/(G2_SERIALIZATION+1)) -// BLS based SPoCK -int bls_spock_verify(const E2*, const byte*, const E2*, const byte*); - // Fr utilities extern const Fr BLS12_381_rR; -bool Fr_is_zero(const Fr* a); -bool Fr_is_equal(const Fr* a, const Fr* b); +bool Fr_is_zero(const Fr* a); +bool Fr_is_equal(const Fr* a, const Fr* b); void Fr_set_limb(Fr*, const limb_t); void Fr_copy(Fr*, const Fr*); void Fr_set_zero(Fr*); @@ -63,10 +68,10 @@ void Fr_from_montg(Fr *res, const Fr *a); void Fr_exp_montg(Fr *res, const Fr* base, const limb_t* expo, const int expo_len); void Fr_inv_montg_eucl(Fr *res, const Fr *a); void Fr_inv_exp_montg(Fr *res, const Fr *a); -BLST_ERROR Fr_read_bytes(Fr* a, const byte *bin, int len); -BLST_ERROR Fr_star_read_bytes(Fr* a, const byte *bin, int len); +ERROR Fr_read_bytes(Fr* a, const byte *bin, int len); +ERROR Fr_star_read_bytes(Fr* a, const byte *bin, int len); void Fr_write_bytes(byte *bin, const Fr* a); -bool map_bytes_to_Fr(Fr*, const byte*, int); +bool map_bytes_to_Fr(Fr*, const byte*, int); // Fp utilities void Fp_mul_montg(Fp *, const Fp *, const Fp *); @@ -74,34 +79,34 @@ void Fp_squ_montg(Fp *, const Fp *); // E1 and G1 utilities void E1_copy(E1*, const E1*); -bool E1_is_equal(const E1*, const E1*); +bool E1_is_equal(const E1*, const E1*); void E1_set_infty(E1*); -bool E1_is_infty(const E1*); +bool E1_is_infty(const E1*); void E1_to_affine(E1*, const E1*); -bool E1_affine_on_curve(const E1*); -bool E1_in_G1(const E1*); +bool E1_affine_on_curve(const E1*); +bool E1_in_G1(const E1*); void E1_mult(E1*, const E1*, const Fr*); void E1_add(E1*, const E1*, const E1*); void E1_neg(E1*, const E1*); void E1_sum_vector(E1*, const E1*, const int); int E1_sum_vector_byte(byte*, const byte*, const int); void G1_mult_gen(E1*, const Fr*); -BLST_ERROR E1_read_bytes(E1*, const byte *, const int); +ERROR E1_read_bytes(E1*, const byte *, const int); void E1_write_bytes(byte *, const E1*); void unsafe_map_bytes_to_G1(E1*, const byte*, int); -BLST_ERROR unsafe_map_bytes_to_G1complement(E1*, const byte*, int); -// hash to curve functions (functions in bls12381_hashtocurve.c) -#define MAP_TO_G1_INPUT_LEN (2*(Fp_BYTES + SEC_BITS/8)) -int map_to_G1(E1*, const byte*, const int); +ERROR unsafe_map_bytes_to_G1complement(E1*, const byte*, int); + +#define MAP_TO_G1_INPUT_LEN (2*(Fp_BYTES + SEC_BITS/8)) +int map_to_G1(E1*, const byte*, const int); // functions in bls12381_hashtocurve.c // E2 and G2 utilities void E2_set_infty(E2* p); -bool E2_is_infty(const E2*); -bool E2_affine_on_curve(const E2*); -bool E2_is_equal(const E2*, const E2*); +bool E2_is_infty(const E2*); +bool E2_affine_on_curve(const E2*); +bool E2_is_equal(const E2*, const E2*); void E2_copy(E2*, const E2*); void E2_to_affine(E2*, const E2*); -BLST_ERROR E2_read_bytes(E2*, const byte *, const int); +ERROR E2_read_bytes(E2*, const byte *, const int); void E2_write_bytes(byte *, const E2*); void G2_mult_gen(E2*, const Fr*); void E2_mult(E2*, const E2*, const Fr*); @@ -110,14 +115,14 @@ void E2_add(E2* res, const E2* a, const E2* b); void E2_neg(E2*, const E2*); void E2_sum_vector(E2*, const E2*, const int); void E2_subtract_vector(E2* res, const E2* x, const E2* y, const int len); -bool E2_in_G2(const E2*); +bool E2_in_G2(const E2*); void unsafe_map_bytes_to_G2(E2*, const byte*, int); -BLST_ERROR unsafe_map_bytes_to_G2complement(E2*, const byte*, int); +ERROR unsafe_map_bytes_to_G2complement(E2*, const byte*, int); // pairing and Fp12 -bool Fp12_is_one(Fp12*); +bool Fp12_is_one(Fp12*); void Fp12_set_one(Fp12*); -void multi_pairing(Fp12*, const E1*, const E2*, const int); +void Fp12_multi_pairing(Fp12*, const E1*, const E2*, const int); // utility testing function void xmd_sha256(byte *, int, byte *, int, byte *, int); diff --git a/crypto/bls_core.c b/crypto/bls_core.c index e1578a150fe..0771269ed86 100644 --- a/crypto/bls_core.c +++ b/crypto/bls_core.c @@ -47,7 +47,7 @@ static int bls_verify_E1(const E2* pk, const E1* s, const E1* h) { // double pairing Fp12 e; - multi_pairing(&e, elemsG1, elemsG2, 2); + Fp12_multi_pairing(&e, elemsG1, elemsG2, 2); if (Fp12_is_one(&e)) { return VALID; } @@ -80,7 +80,7 @@ int bls_verifyPerDistinctMessage(const byte* sig, if (!elemsG2) goto outG2; // elemsG1[0] = sig - if (E1_read_bytes(&elemsG1[0], sig, G1_SER_BYTES) != BLST_SUCCESS) { + if (E1_read_bytes(&elemsG1[0], sig, G1_SER_BYTES) != VALID) { ret = INVALID; goto out; } @@ -113,7 +113,7 @@ int bls_verifyPerDistinctMessage(const byte* sig, // multi pairing Fp12 e; - multi_pairing(&e, elemsG1 , elemsG2, nb_hashes+1); + Fp12_multi_pairing(&e, elemsG1 , elemsG2, nb_hashes+1); if (Fp12_is_one(&e)) { ret = VALID; } else { @@ -154,7 +154,7 @@ int bls_verifyPerDistinctKey(const byte* sig, if (!elemsG2) goto outG2; // elemsG1[0] = s - if (E1_read_bytes(&elemsG1[0], sig, G1_SER_BYTES) != BLST_SUCCESS) { + if (E1_read_bytes(&elemsG1[0], sig, G1_SER_BYTES) != VALID) { ret = INVALID; goto out; } @@ -206,7 +206,7 @@ int bls_verifyPerDistinctKey(const byte* sig, // multi pairing Fp12 e; - multi_pairing(&e, elemsG1, elemsG2, nb_pks+1); + Fp12_multi_pairing(&e, elemsG1, elemsG2, nb_pks+1); if (Fp12_is_one(&e)) { ret = VALID; @@ -230,7 +230,7 @@ int bls_verifyPerDistinctKey(const byte* sig, int bls_verify(const E2* pk, const byte* sig, const byte* hash, const int hash_len) { E1 s, h; // deserialize the signature into a curve point - if (E1_read_bytes(&s, sig, G1_SER_BYTES) != BLST_SUCCESS) { + if (E1_read_bytes(&s, sig, G1_SER_BYTES) != VALID) { return INVALID; } @@ -381,7 +381,7 @@ void bls_batch_verify(const int sigs_len, byte* results, const E2* pks_input, // - valid points are multiplied by a random scalar (same for public keys at same index) // to make sure a signature at index (i) is verified against the public key at the same index. int read_ret = E1_read_bytes(&sigs[i], &sigs_bytes[G1_SER_BYTES*i], G1_SER_BYTES); - if (read_ret != BLST_SUCCESS || !E1_in_G1(&sigs[i])) { + if (read_ret != VALID || !E1_in_G1(&sigs[i])) { // set signature and key to infinity (no effect on the aggregation tree) // and set result to invalid (result won't be overwritten) E2_set_infty(&pks[i]); @@ -420,3 +420,47 @@ void bls_batch_verify(const int sigs_len, byte* results, const E2* pks_input, out_sigs: free(pks); } + +// Verifies the validity of 2 SPoCK proofs and 2 public keys. +// Membership check in G1 of both proofs is verified in this function. +// Membership check in G2 of both keys is not verified in this function. +// the membership check in G2 is separated to allow optimizing multiple verifications +// using the same public keys. +int bls_spock_verify(const E2* pk1, const byte* sig1, const E2* pk2, const byte* sig2) { + E1 elemsG1[2]; + E2 elemsG2[2]; + + // elemsG1[0] = s1 + if (E1_read_bytes(&elemsG1[0], sig1, G1_SER_BYTES) != VALID) { + return INVALID; + }; + // check s1 is in G1 + if (!E1_in_G1(&elemsG1[0])) { + return INVALID; + } + + // elemsG1[1] = s2 + if (E1_read_bytes(&elemsG1[1], sig2, G1_SER_BYTES) != VALID) { + return INVALID; + }; + // check s2 is in G1 + if (!E1_in_G1(&elemsG1[1])) { + return INVALID; + } + + // elemsG2[1] = pk1 + E2_copy(&elemsG2[1], pk1); + + // elemsG2[0] = -pk2 + E2_neg(&elemsG2[0], pk2); + + // double pairing + Fp12 e; + Fp12_multi_pairing(&e, elemsG1 , elemsG2, 2); + + if (Fp12_is_one(&e)) { + return VALID; + } + return INVALID; +} + diff --git a/crypto/bls_include.h b/crypto/bls_include.h index 1ca61b376c4..2cbf91b2936 100644 --- a/crypto/bls_include.h +++ b/crypto/bls_include.h @@ -5,8 +5,7 @@ #include "bls12381_utils.h" - -// bls core (functions in bls_core.c) +// BLS signature core (functions in bls_core.c) int bls_sign(byte*, const Fr*, const byte*, const int); int bls_verify(const E2*, const byte*, const byte*, const int); int bls_verifyPerDistinctMessage(const byte*, const int, const byte*, const uint32_t*, @@ -17,4 +16,7 @@ int bls_verifyPerDistinctKey(const byte*, void bls_batch_verify(const int, byte*, const E2*, const byte*, const byte*, const int, const byte*); +// BLS based SPoCK +int bls_spock_verify(const E2*, const byte*, const E2*, const byte*); + #endif diff --git a/crypto/bls_multisig.go b/crypto/bls_multisig.go index 6c99ae461e2..fdb21a986f1 100644 --- a/crypto/bls_multisig.go +++ b/crypto/bls_multisig.go @@ -5,12 +5,7 @@ import ( "errors" "fmt" - _ "errors" - - _ "fmt" - "github.com/onflow/flow-go/crypto/hash" - _ "github.com/onflow/flow-go/crypto/hash" ) // BLS multi-signature using BLS12-381 curve @@ -95,7 +90,6 @@ func BLSVerifyPOP(pk PublicKey, s Signature) (bool, error) { // - (nil, error) if an unexpected error occurs // - (aggregated_signature, nil) otherwise func AggregateBLSSignatures(sigs []Signature) (Signature, error) { - // check for empty list if len(sigs) == 0 { return nil, blsAggregateEmptyListError @@ -140,7 +134,6 @@ func AggregateBLSSignatures(sigs []Signature) (Signature, error) { // - (nil, blsAggregateEmptyListError) if no keys are provided (input slice is empty) // - (aggregated_key, nil) otherwise func AggregateBLSPrivateKeys(keys []PrivateKey) (PrivateKey, error) { - // check for empty list if len(keys) == 0 { return nil, blsAggregateEmptyListError diff --git a/crypto/bls_thresholdsign_core.c b/crypto/bls_thresholdsign_core.c index 78a87823b4c..e951cc9c33f 100644 --- a/crypto/bls_thresholdsign_core.c +++ b/crypto/bls_thresholdsign_core.c @@ -22,7 +22,7 @@ static void Fr_lagrange_coeff_at_zero(Fr* res, const int i, const byte indices[] // the highest k such that fact(MAX_IND)/fact(MAX_IND-k) < 2^64 (approximately 64/MAX_IND_BITS) // this means we can multiply up to (k) indices in a limb (64 bits) without overflowing. - #define MAX_IND_LOOPS 64/MAX_IND_BITS + #define MAX_IND_LOOPS (64/MAX_IND_BITS) const int loops = MAX_IND_LOOPS; int k,j = 0; Fr tmp; @@ -88,7 +88,7 @@ int E1_lagrange_interpolate_at_zero_write(byte* dest, const byte* shares, const E1* E1_shares = malloc(sizeof(E1) * len); for (int i=0; i < len; i++) { read_ret = E1_read_bytes(&E1_shares[i], &shares[G1_SER_BYTES * i], G1_SER_BYTES); - if (read_ret != BLST_SUCCESS) { + if (read_ret != VALID) { goto out; } } diff --git a/crypto/blst_include.h b/crypto/blst_include.h index 20c2fcad5df..e3c0bb9701a 100644 --- a/crypto/blst_include.h +++ b/crypto/blst_include.h @@ -5,11 +5,6 @@ #include "point.h" #include "fields.h" #include "consts.h" -#include "errors.h" - -// TODO: add sanity checks that BLST_PK_IS_INFINITY is indeed the last -// enum value (eventually submit a fix to BLST) -#define BLST_BAD_SCALAR ((BLST_PK_IS_INFINITY)+1) // types used by the Flow crypto library that are imported from BLST // these type definitions are used as an abstraction from BLST internal types diff --git a/crypto/dkg_core.c b/crypto/dkg_core.c index 9966fbcfc37..15e8e0c48b3 100644 --- a/crypto/dkg_core.c +++ b/crypto/dkg_core.c @@ -63,16 +63,16 @@ void G2_vector_write_bytes(byte* out, const E2* A, const int len) { // The function imports an array of E2 points from a concatenated array of bytes. // The bytes array is supposed to be in (len * G2_SER_BYTES) -BLST_ERROR E2_vector_read_bytes(E2* A, const byte* src, const int len){ +ERROR E2_vector_read_bytes(E2* A, const byte* src, const int len){ byte* p = (byte*) src; for (int i=0; i Date: Tue, 30 May 2023 17:22:21 -0600 Subject: [PATCH 09/11] update code base to work for G1 serialization defined as uncompressed --- crypto/bls.go | 13 +++------- crypto/bls12381_utils.c | 40 +++++++++++------------------- crypto/bls12381_utils.go | 35 +++++++++++++++++++++++--- crypto/bls12381_utils.h | 15 +++++------ crypto/bls12381_utils_test.go | 27 ++++++++------------ crypto/bls_include.h | 4 +-- crypto/bls_multisig.go | 2 +- crypto/bls_test.go | 28 ++++++++++++++------- crypto/bls_thresholdsign_include.h | 4 +-- crypto/dkg_include.h | 4 +-- crypto/sign.go | 7 +++--- crypto/spock_test.go | 4 +-- 12 files changed, 99 insertions(+), 84 deletions(-) diff --git a/crypto/bls.go b/crypto/bls.go index 93dd487a817..7f884a73c49 100644 --- a/crypto/bls.go +++ b/crypto/bls.go @@ -207,11 +207,6 @@ func (pk *pubKeyBLSBLS12381) Verify(s Signature, data []byte, kmac hash.Hasher) } } -// 0xC0 is the header of the point at infinity serialization (either in G1 or G2) -const infinityPointHeader = byte(0xC0) - -var identityBLSSignature = append([]byte{infinityPointHeader}, make([]byte, SignatureLenBLSBLS12381-1)...) - // IsBLSSignatureIdentity checks whether the input signature is // the identity signature (point at infinity in G1). // @@ -221,7 +216,7 @@ var identityBLSSignature = append([]byte{infinityPointHeader}, make([]byte, Sign // suspected to be equal to identity, which avoids failing the aggregated // signature verification. func IsBLSSignatureIdentity(s Signature) bool { - return bytes.Equal(s, identityBLSSignature) + return bytes.Equal(s, g1Serialization) } // generatePrivateKey deterministically generates a private key for BLS on BLS12-381 curve. @@ -347,8 +342,7 @@ func (a *blsBLS12381Algo) decodePublicKey(publicKeyBytes []byte) (PublicKey, err // decodePublicKeyCompressed decodes a slice of bytes into a public key. // since we use the compressed representation by default, this checks the default and delegates to decodePublicKeyCompressed func (a *blsBLS12381Algo) decodePublicKeyCompressed(publicKeyBytes []byte) (PublicKey, error) { - // in compression mode, g2BytesLen is equal to 2 * Fp_bytes - if g2BytesLen != 2*fpBytesLen { + if !isG2Compressed() { panic("library is not configured to use compressed public key serialization") } return a.decodePublicKey(publicKeyBytes) @@ -478,8 +472,7 @@ func (pk *pubKeyBLSBLS12381) Size() int { // The encoding is a compressed encoding of the point // [zcash] https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-zcash-serialization-format- func (a *pubKeyBLSBLS12381) EncodeCompressed() []byte { - // in compression mode, g2BytesLen is equal to 2 * Fp_bytes - if g2BytesLen != 2*fpBytesLen { + if !isG2Compressed() { panic("library is not configured to use compressed public key serialization") } dest := make([]byte, g2BytesLen) diff --git a/crypto/bls12381_utils.c b/crypto/bls12381_utils.c index 35bf1ff4686..30f8b862aa0 100644 --- a/crypto/bls12381_utils.c +++ b/crypto/bls12381_utils.c @@ -543,6 +543,7 @@ ERROR E1_read_bytes(E1* a, const byte *bin, const int len) { if (ret != VALID) { return ret; } + Fp_to_montg(&a->x, &a->x); // set a.z to 1 Fp_copy(&a->z, &BLS12_381_pR); @@ -552,6 +553,7 @@ ERROR E1_read_bytes(E1* a, const byte *bin, const int len) { if (ret != VALID){ return ret; } + Fp_to_montg(&a->y, &a->y); // check read point is on curve if (!E1_affine_on_curve(a)) { return POINT_NOT_ON_CURVE; @@ -560,12 +562,12 @@ ERROR E1_read_bytes(E1* a, const byte *bin, const int len) { } // compute the possible square root - Fp_to_montg(&a->x, &a->x); Fp_squ_montg(&a->y, &a->x); Fp_mul_montg(&a->y, &a->y, &a->x); // x^3 Fp_add(&a->y, &a->y, &B_E1); // B_E1 is already in Montg form - if (!Fp_sqrt_montg(&a->y, &a->y)) // check whether x^3+b is a quadratic residue + if (!Fp_sqrt_montg(&a->y, &a->y)) { // check whether x^3+b is a quadratic residue return POINT_NOT_ON_CURVE; + } // resulting (x,y) is guaranteed to be on curve (y is already in Montg form) if (Fp_get_sign(&a->y) != y_sign) { @@ -718,30 +720,18 @@ void unsafe_map_bytes_to_G1(E1* p, const byte* bytes, int len) { G1_mult_gen(p, &log); } -// generates a point in E1\G1 and stores it in p +// maps bytes to a point in E1\G1. +// `len` must be at least 96 bytes. // this is a testing file only, should not be used in any protocol! -ERROR unsafe_map_bytes_to_G1complement(E1* p, const byte* bytes, int len) { - assert(G1_SERIALIZATION == COMPRESSED); - assert(len >= G1_SER_BYTES); - - // attempt to deserilize a compressed E1 point from input bytes - // after fixing the header 2 bits - byte copy[G1_SER_BYTES]; - memcpy(copy, bytes, sizeof(copy)); - copy[0] |= 1<<7; // set compression bit - copy[0] &= ~(1<<6); // clear infinity bit - point is not infinity - - ERROR ser = E1_read_bytes(p, copy, G1_SER_BYTES); - if (ser != VALID) { - return ser; - } - - // map the point to E2\G2 by clearing G2 order - E1_mult(p, p, (const Fr*)BLS12_381_r); - E1_to_affine(p, p); - - assert(E1_affine_on_curve(p)); // sanity check to make sure p is in E2 - return VALID; +void unsafe_map_bytes_to_G1complement(E1* p, const byte* bytes, int len) { + assert(len >= 96); + Fp u; + map_96_bytes_to_Fp(&u, bytes, 96); + // map to E1's isogenous and then to E1 + map_to_isogenous_E1((POINTonE1 *)p, u); + isogeny_map_to_E1((POINTonE1 *)p, (POINTonE1 *)p); + // clear G1 order + E1_mult(p, p, (Fr*)&BLS12_381_r); } // ------------------- E2 utilities diff --git a/crypto/bls12381_utils.go b/crypto/bls12381_utils.go index 812319ced63..b9535d39955 100644 --- a/crypto/bls12381_utils.go +++ b/crypto/bls12381_utils.go @@ -60,6 +60,28 @@ const ( pointNotOnCurve = C.POINT_NOT_ON_CURVE ) +// header of the point at infinity serializations +var g1SerHeader byte // g1 +var g2SerHeader byte // g2 + +// `g1“ serialization +var g1Serialization []byte + +// initialization of BLS12-381 curve +func initBLS12381() { + if isG1Compressed() { + g1SerHeader = 0xC0 + } else { + g1SerHeader = 0x40 + } + g1Serialization = append([]byte{g1SerHeader}, make([]byte, g1BytesLen-1)...) + if isG2Compressed() { + g2SerHeader = 0xC0 + } else { + g2SerHeader = 0x40 + } +} + func (a *scalar) String() string { encoding := make([]byte, frBytesLen) writeScalar(encoding, a) @@ -260,9 +282,8 @@ func unsafeMapToG1(pt *pointE1, seed []byte) { // unsafeMapToG1Complement is a test function, it wraps a call to C since cgo can't be used in go test files. // It generates a random point in E2\G2 and stores it in input point. -func unsafeMapToG1Complement(pt *pointE1, seed []byte) bool { - res := C.unsafe_map_bytes_to_G1complement((*C.E1)(pt), (*C.uchar)(&seed[0]), (C.int)(len(seed))) - return int(res) == valid +func unsafeMapToG1Complement(pt *pointE1, seed []byte) { + C.unsafe_map_bytes_to_G1complement((*C.E1)(pt), (*C.uchar)(&seed[0]), (C.int)(len(seed))) } // unsafeMapToG2 is a test function, it wraps a call to C since cgo can't be used in go test files. @@ -307,3 +328,11 @@ func hashToG1Bytes(data, dst []byte) []byte { writePointE1(pointBytes, &point) return pointBytes } + +func isG1Compressed() bool { + return g1BytesLen == fpBytesLen +} + +func isG2Compressed() bool { + return g2BytesLen == 2*fpBytesLen +} diff --git a/crypto/bls12381_utils.h b/crypto/bls12381_utils.h index 921df90624d..ccbb4c9655c 100644 --- a/crypto/bls12381_utils.h +++ b/crypto/bls12381_utils.h @@ -2,8 +2,8 @@ // these tools are shared by the BLS signature scheme, the BLS based threshold signature // and the BLS distributed key generation protocols -#ifndef _REL_MISC_INCLUDE_H -#define _REL_MISC_INCLUDE_H +#ifndef _BLS12_381_UTILS_H +#define _BLS12_381_UTILS_H #include #include "blst_include.h" @@ -43,8 +43,8 @@ typedef enum { #define G2_BYTES (2*Fp2_BYTES) // Compressed and uncompressed points -#define COMPRESSED 1 -#define UNCOMPRESSED 0 +#define COMPRESSED 1 +#define UNCOMPRESSED 0 #define G1_SERIALIZATION (COMPRESSED) #define G2_SERIALIZATION (COMPRESSED) #define G1_SER_BYTES (G1_BYTES/(G1_SERIALIZATION+1)) @@ -94,7 +94,7 @@ void G1_mult_gen(E1*, const Fr*); ERROR E1_read_bytes(E1*, const byte *, const int); void E1_write_bytes(byte *, const E1*); void unsafe_map_bytes_to_G1(E1*, const byte*, int); -ERROR unsafe_map_bytes_to_G1complement(E1*, const byte*, int); +void unsafe_map_bytes_to_G1complement(E1*, const byte*, int); #define MAP_TO_G1_INPUT_LEN (2*(Fp_BYTES + SEC_BITS/8)) int map_to_G1(E1*, const byte*, const int); // functions in bls12381_hashtocurve.c @@ -130,6 +130,7 @@ void xmd_sha256(byte *, int, byte *, int, byte *, int); // Debugging related functions #define DEBUG 0 #if (DEBUG == 1) +#include void bytes_print_(char*, byte*, int); void Fr_print_(char*, Fr*); void Fp_print_(char*, const Fp*); @@ -137,6 +138,6 @@ void Fp2_print_(char*, const Fp2*); void Fp12_print_(char*, const Fp12*); void E1_print_(char*, const E1*, const int); void E2_print_(char*, const E2*, const int); -#endif // DEBUG +#endif /* DEBUG */ -#endif \ No newline at end of file +#endif /* BLS12_381_UTILS */ \ No newline at end of file diff --git a/crypto/bls12381_utils_test.go b/crypto/bls12381_utils_test.go index 2c9d76bbbe5..7741238278e 100644 --- a/crypto/bls12381_utils_test.go +++ b/crypto/bls12381_utils_test.go @@ -139,12 +139,7 @@ func TestSubgroupCheck(t *testing.T) { unsafeMapToG1(&p, seed) // point in G1 assert.True(t, checkMembershipG1(&p)) - inG1 := false - for !inG1 { - _, err := prg.Read(seed) - require.NoError(t, err) - inG1 = unsafeMapToG1Complement(&p, seed) // point in E2\G2 - } + unsafeMapToG1Complement(&p, seed) // point in E2\G2 assert.False(t, checkMembershipG1(&p)) }) @@ -198,8 +193,8 @@ func TestReadWriteG1(t *testing.T) { bytes := make([]byte, g1BytesLen) // generate a random G1 point, encode it, decode it, // and compare it the original point - iterations := 50 t.Run("random points", func(t *testing.T) { + iterations := 50 for i := 0; i < iterations; i++ { var p, q pointE1 _, err := prg.Read(seed) @@ -213,16 +208,14 @@ func TestReadWriteG1(t *testing.T) { }) t.Run("infinity", func(t *testing.T) { - for i := 0; i < iterations; i++ { - var p, q pointE1 - seed := make([]byte, frBytesLen) - unsafeMapToG1(&p, seed) // this results in the infinity point - writePointE1(bytes, &p) - require.True(t, IsBLSSignatureIdentity(bytes)) // sanity check - err := readPointE1(&q, bytes) - require.NoError(t, err) - assert.True(t, p.equals(&q)) - } + var p, q pointE1 + seed := make([]byte, frBytesLen) + unsafeMapToG1(&p, seed) // this results in the infinity point + writePointE1(bytes, &p) + require.True(t, IsBLSSignatureIdentity(bytes)) // sanity check + err := readPointE1(&q, bytes) + require.NoError(t, err) + assert.True(t, p.equals(&q)) }) } diff --git a/crypto/bls_include.h b/crypto/bls_include.h index 2cbf91b2936..c5dba4d45de 100644 --- a/crypto/bls_include.h +++ b/crypto/bls_include.h @@ -1,7 +1,7 @@ // this file is about the core functions required by the BLS signature scheme -#ifndef _REL_BLS_INCLUDE_H -#define _REL_BLS_INCLUDE_H +#ifndef _BLS_INCLUDE_H +#define _BLS_INCLUDE_H #include "bls12381_utils.h" diff --git a/crypto/bls_multisig.go b/crypto/bls_multisig.go index fdb21a986f1..7adbb0c1f45 100644 --- a/crypto/bls_multisig.go +++ b/crypto/bls_multisig.go @@ -507,7 +507,7 @@ func BatchVerifyBLSSignaturesOneMessage( // However, the boolean return for index `i` is set to `false` and won't be overwritten. returnBool[i] = false pkPoints = append(pkPoints, getIdentityPoint()) - flatSigs = append(flatSigs, identityBLSSignature...) + flatSigs = append(flatSigs, g1Serialization...) } else { pkPoints = append(pkPoints, pkBLS.point) flatSigs = append(flatSigs, sigs[i]...) diff --git a/crypto/bls_test.go b/crypto/bls_test.go index d8561ccc5f6..7ea369a5b73 100644 --- a/crypto/bls_test.go +++ b/crypto/bls_test.go @@ -29,6 +29,9 @@ func TestBLSMainMethods(t *testing.T) { // - signature decoding only accepts reduced x-coordinates to avoid signature malleability t.Run("invalid x coordinate larger than p", func(t *testing.T) { + if !isG1Compressed() { + t.Skip() + } msg, err := hex.DecodeString("7f26ba692dc2da7ff828ef4675ff1cd6ab855fca0637b6dab295f1df8e51bc8bb1b8f0c6610aabd486cf1f098f2ddbc6691d94e10f928816f890a3d366ce46249836a595c7ea1828af52e899ba2ab627ab667113bb563918c5d5a787c414399487b4e3a7") require.NoError(t, err) validSig, err := hex.DecodeString("80b0cac2a0f4f8881913edf2b29065675dfed6f6f4e17e9b5d860a845d4e7d476b277d06a493b81482e63d8131f9f2fa") @@ -190,7 +193,7 @@ func TestBLSEncodeDecode(t *testing.T) { t.Run("infinity public key", func(t *testing.T) { // decode an identity public key pkBytes := make([]byte, PubKeyLenBLSBLS12381) - pkBytes[0] = infinityPointHeader + pkBytes[0] = g2SerHeader pk, err := DecodePublicKey(BLSBLS12381, pkBytes) require.NoError(t, err, "decoding identity public key should succeed") assert.True(t, pk.Equals(IdentityBLSPublicKey())) @@ -543,12 +546,15 @@ func TestBLSAggregatePublicKeys(t *testing.T) { assert.True(t, blsKey.isIdentity) // check of encoding header pkBytes := aggPK.Encode() - assert.Equal(t, infinityPointHeader, pkBytes[0]) + assert.Equal(t, g2SerHeader, pkBytes[0]) }) t.Run("Identity public key from opposite points", func(t *testing.T) { + if !isG2Compressed() { + t.Skip() + } pkBytes := pks[0].Encode() - negatePoint(pkBytes) + negateCompressedPoint(pkBytes) minusPk, err := DecodePublicKey(BLSBLS12381, pkBytes) require.NoError(t, err) // aggregated public keys @@ -561,7 +567,7 @@ func TestBLSAggregatePublicKeys(t *testing.T) { assert.True(t, blsKey.isIdentity) // check of encoding header pkBytes = aggPK.Encode() - assert.Equal(t, infinityPointHeader, pkBytes[0]) + assert.Equal(t, g2SerHeader, pkBytes[0]) }) } @@ -822,9 +828,9 @@ func TestBLSBatchVerify(t *testing.T) { } // Utility function that flips a point sign bit to negate the point -// this is shortcut which works only for zcash BLS12-381 compressed serialization -// Applicable to both signatures and public keys -func negatePoint(pointbytes []byte) { +// this is shortcut which works only for zcash BLS12-381 compressed serialization. +// Applicable to both signatures and public keys. +func negateCompressedPoint(pointbytes []byte) { pointbytes[0] ^= 0x20 } @@ -1190,10 +1196,14 @@ func TestBLSIdentity(t *testing.T) { hasher := NewExpandMsgXOFKMAC128("") t.Run("identity signature comparison", func(t *testing.T) { + if !isG1Compressed() { + t.Skip() + } // verify that constructed identity signatures are recognized as such by IsBLSSignatureIdentity. // construct identity signature by summing (aggregating) a random signature and its inverse. - assert.True(t, IsBLSSignatureIdentity(identityBLSSignature)) + // sanity check to start + assert.True(t, IsBLSSignatureIdentity(g1Serialization)) // sum up a random signature and its inverse to get identity sk := randomSK(t, rand) @@ -1201,7 +1211,7 @@ func TestBLSIdentity(t *testing.T) { require.NoError(t, err) oppositeSig := make([]byte, SignatureLenBLSBLS12381) copy(oppositeSig, sig) - negatePoint(oppositeSig) + negateCompressedPoint(oppositeSig) aggSig, err := AggregateBLSSignatures([]Signature{sig, oppositeSig}) require.NoError(t, err) assert.True(t, IsBLSSignatureIdentity(aggSig)) diff --git a/crypto/bls_thresholdsign_include.h b/crypto/bls_thresholdsign_include.h index 3937f8ce965..7c27c3b97b8 100644 --- a/crypto/bls_thresholdsign_include.h +++ b/crypto/bls_thresholdsign_include.h @@ -1,5 +1,5 @@ -#ifndef _REL_THRESHOLD_INCLUDE_H -#define _REL_THRESHOLD_INCLUDE_H +#ifndef _THRESHOLD_INCLUDE_H +#define _THRESHOLD_INCLUDE_H #include "bls_include.h" diff --git a/crypto/dkg_include.h b/crypto/dkg_include.h index c361d3ce861..7cd2b8b7d2d 100644 --- a/crypto/dkg_include.h +++ b/crypto/dkg_include.h @@ -1,5 +1,5 @@ -#ifndef _REL_DKG_INCLUDE_H -#define _REL_DKG_INCLUDE_H +#ifndef _DKG_INCLUDE_H +#define _DKG_INCLUDE_H #include "bls12381_utils.h" diff --git a/crypto/sign.go b/crypto/sign.go index ff4348f3b09..d400898d97d 100644 --- a/crypto/sign.go +++ b/crypto/sign.go @@ -65,19 +65,18 @@ func newSigner(algo SigningAlgorithm) (signer, error) { // Initialize the context of all algos func init() { - // P-256 + // ECDSA p256Instance = &(ecdsaAlgo{ curve: elliptic.P256(), algo: ECDSAP256, }) - - // secp256k1 secp256k1Instance = &(ecdsaAlgo{ curve: btcec.S256(), algo: ECDSASecp256k1, }) - // bls12-381 + // BLS + initBLS12381() blsInstance = &blsBLS12381Algo{ algo: BLSBLS12381, } diff --git a/crypto/spock_test.go b/crypto/spock_test.go index 75de3dea838..59498a42f6f 100644 --- a/crypto/spock_test.go +++ b/crypto/spock_test.go @@ -69,7 +69,7 @@ func TestSPOCKProveVerifyAgainstData(t *testing.T) { t.Run("identity proof", func(t *testing.T) { // verifying with a pair of (proof, publicKey) equal to (identity_signature, identity_key) should // return false - identityProof := identityBLSSignature + identityProof := g1Serialization result, err := SPOCKVerifyAgainstData(IdentityBLSPublicKey(), identityProof, data, kmac) assert.NoError(t, err) assert.False(t, result) @@ -166,7 +166,7 @@ func TestSPOCKProveVerify(t *testing.T) { t.Run("identity proof", func(t *testing.T) { // verifying with either pair of (proof, publicKey) equal to (identity_signature, identity_key) should // return falsen with any other (proof, key) pair. - identityProof := identityBLSSignature + identityProof := g1Serialization result, err := SPOCKVerify(IdentityBLSPublicKey(), identityProof, sk2.PublicKey(), pr2) assert.NoError(t, err) assert.False(t, result) From 9ed47f1975689db2a296c6c22da0e446d7a0158c Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Tue, 30 May 2023 18:23:51 -0600 Subject: [PATCH 10/11] update code base to work for G2 serialization defined as uncompressed --- crypto/bls.go | 16 +++++----- crypto/bls12381_utils.c | 55 +++++++++++++---------------------- crypto/bls12381_utils.go | 5 ++-- crypto/bls12381_utils.h | 2 +- crypto/bls12381_utils_test.go | 18 +++++++----- crypto/bls_test.go | 5 +++- crypto/sign_test_utils.go | 26 +++++++++-------- 7 files changed, 62 insertions(+), 65 deletions(-) diff --git a/crypto/bls.go b/crypto/bls.go index 7f884a73c49..447a203033b 100644 --- a/crypto/bls.go +++ b/crypto/bls.go @@ -286,7 +286,7 @@ func (a *blsBLS12381Algo) generatePrivateKey(ikm []byte) (PrivateKey, error) { const invalidBLSSignatureHeader = byte(0xE0) // BLSInvalidSignature returns an invalid signature that fails when verified -// with any message and public key. +// with any message and public key, which can be used for testing. // // The signature bytes represent an invalid serialization of a point which // makes the verification fail early. The verification would return (false, nil). @@ -475,15 +475,17 @@ func (a *pubKeyBLSBLS12381) EncodeCompressed() []byte { if !isG2Compressed() { panic("library is not configured to use compressed public key serialization") } - dest := make([]byte, g2BytesLen) - writePointE2(dest, &a.point) - return dest + return a.Encode() } -// Encode returns a byte encoding of the public key. -// Since we use a compressed encoding by default, this delegates to EncodeCompressed +// Encode returns a byte encoding of the public key (a G2 point). +// The current encoding is a compressed serialization of G2 following [zcash] https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-zcash-serialization-format- +// +// The function should evolve in the future to support uncompressed compresion too. func (a *pubKeyBLSBLS12381) Encode() []byte { - return a.EncodeCompressed() + dest := make([]byte, g2BytesLen) + writePointE2(dest, &a.point) + return dest } // Equals checks is two public keys are equal diff --git a/crypto/bls12381_utils.c b/crypto/bls12381_utils.c index 30f8b862aa0..d88bfa3aaa8 100644 --- a/crypto/bls12381_utils.c +++ b/crypto/bls12381_utils.c @@ -792,17 +792,23 @@ ERROR E2_read_bytes(E2* a, const byte *bin, const int len) { if (ret != VALID) { return ret; } + Fp2* a_x = &(a->x); + Fp_to_montg(&real(a_x), &real(a_x)); + Fp_to_montg(&imag(a_x), &imag(a_x)); // set a.z to 1 Fp2* a_z = &(a->z); Fp_copy(&real(a_z), &BLS12_381_pR); Fp_set_zero(&imag(a_z)); + Fp2* a_y = &(a->y); if (G2_SERIALIZATION == UNCOMPRESSED) { - ret = Fp2_read_bytes(&(a->y), bin + Fp2_BYTES, sizeof(a->y)); + ret = Fp2_read_bytes(a_y, bin + Fp2_BYTES, sizeof(a->y)); if (ret != VALID){ return ret; } + Fp_to_montg(&real(a_y), &real(a_y)); + Fp_to_montg(&imag(a_y), &imag(a_y)); // check read point is on curve if (!E2_affine_on_curve(a)) { return POINT_NOT_ON_CURVE; @@ -811,14 +817,9 @@ ERROR E2_read_bytes(E2* a, const byte *bin, const int len) { } // compute the possible square root - Fp2* a_x = &(a->x); - Fp_to_montg(&real(a_x), &real(a_x)); - Fp_to_montg(&imag(a_x), &imag(a_x)); - - Fp2* a_y = &(a->y); Fp2_squ_montg(a_y, a_x); - Fp2_mul_montg(a_y, a_y, a_x); - Fp2_add(a_y, a_y, &B_E2); // B_E2 is already in Montg form + Fp2_mul_montg(a_y, a_y, a_x); // x^3 + Fp2_add(a_y, a_y, &B_E2); // B_E2 is already in Montg form if (!Fp2_sqrt_montg(a_y, a_y)) // check whether x^3+b is a quadratic residue return POINT_NOT_ON_CURVE; @@ -976,33 +977,19 @@ void unsafe_map_bytes_to_G2(E2* p, const byte* bytes, int len) { G2_mult_gen(p, &log); } -// attempts to map `bytes` to a point in E2\G2 and stores it in p. -// `len` should be at least G2_SER_BYTES. It returns VALID only if mapping -// succeeds. -// For now, function only works when E2 serialization is compressed. +// maps `bytes` to a point in E2\G2 and stores it in p. +// `len` should be at least 192. // this is a testing tool only, it should not be used in any protocol! -ERROR unsafe_map_bytes_to_G2complement(E2* p, const byte* bytes, int len) { - assert(G2_SERIALIZATION == COMPRESSED); - assert(len >= G2_SER_BYTES); - - // attempt to deserilize a compressed E2 point from input bytes - // after fixing the header 2 bits - byte copy[G2_SER_BYTES]; - memcpy(copy, bytes, sizeof(copy)); - copy[0] |= 1<<7; // set compression bit - copy[0] &= ~(1<<6); // clear infinity bit - point is not infinity - - ERROR ser = E2_read_bytes(p, copy, G2_SER_BYTES); - if (ser != VALID) { - return ser; - } - - // map the point to E2\G2 by clearing G2 order - E2_mult(p, p, (const Fr*)BLS12_381_r); - E2_to_affine(p, p); - - assert(E2_affine_on_curve(p)); // sanity check to make sure p is in E2 - return VALID; +void unsafe_map_bytes_to_G2complement(E2* p, const byte* bytes, int len) { + assert(len >= 192); + Fp2 u; + map_96_bytes_to_Fp(&real(&u), bytes, 96); + map_96_bytes_to_Fp(&imag(&u), bytes+96, 96); + // map to E2's isogenous and then to E2 + map_to_isogenous_E2((POINTonE2 *)p, u); + isogeny_map_to_E2((POINTonE2 *)p, (POINTonE2 *)p); + // clear G2 order + E2_mult(p, p, (Fr*)&BLS12_381_r); } // ------------------- Pairing utilities diff --git a/crypto/bls12381_utils.go b/crypto/bls12381_utils.go index b9535d39955..87a515f3b31 100644 --- a/crypto/bls12381_utils.go +++ b/crypto/bls12381_utils.go @@ -295,9 +295,8 @@ func unsafeMapToG2(pt *pointE2, seed []byte) { // unsafeMapToG2Complement is a test function, it wraps a call to C since cgo can't be used in go test files. // It generates a random point in E2\G2 and stores it in input point. -func unsafeMapToG2Complement(pt *pointE2, seed []byte) bool { - res := C.unsafe_map_bytes_to_G2complement((*C.E2)(pt), (*C.uchar)(&seed[0]), (C.int)(len(seed))) - return int(res) == valid +func unsafeMapToG2Complement(pt *pointE2, seed []byte) { + C.unsafe_map_bytes_to_G2complement((*C.E2)(pt), (*C.uchar)(&seed[0]), (C.int)(len(seed))) } // This is only a TEST function. diff --git a/crypto/bls12381_utils.h b/crypto/bls12381_utils.h index ccbb4c9655c..b9c8ab755a7 100644 --- a/crypto/bls12381_utils.h +++ b/crypto/bls12381_utils.h @@ -117,7 +117,7 @@ void E2_sum_vector(E2*, const E2*, const int); void E2_subtract_vector(E2* res, const E2* x, const E2* y, const int len); bool E2_in_G2(const E2*); void unsafe_map_bytes_to_G2(E2*, const byte*, int); -ERROR unsafe_map_bytes_to_G2complement(E2*, const byte*, int); +void unsafe_map_bytes_to_G2complement(E2*, const byte*, int); // pairing and Fp12 bool Fp12_is_one(Fp12*); diff --git a/crypto/bls12381_utils_test.go b/crypto/bls12381_utils_test.go index 7741238278e..067ac979f7e 100644 --- a/crypto/bls12381_utils_test.go +++ b/crypto/bls12381_utils_test.go @@ -22,6 +22,9 @@ func TestScalarMultBLS12381(t *testing.T) { // Note that generator and random point multiplications // are implemented with the same algorithm t.Run("G1", func(t *testing.T) { + if !isG1Compressed() { + t.Skip() + } var p pointE1 generatorScalarMultG1(&p, &expo) expected, err := hex.DecodeString("96484ca50719f5d2533047960878b6bae8289646c0f00a942a1e6992be9981a9e0c7a51e9918f9b19d178cf04a8018a4") @@ -35,6 +38,9 @@ func TestScalarMultBLS12381(t *testing.T) { // Note that generator and random point multiplications // are implemented with the same algorithm t.Run("G2", func(t *testing.T) { + if !isG2Compressed() { + t.Skip() + } var p pointE2 generatorScalarMultG2(&p, &expo) expected, err := hex.DecodeString("b35f5043f166848805b98da62dcb9c5d2f25e497bd0d9c461d4a00d19e4e67cc1e813de3c99479d5a2c62fb754fd7df40c4fd60c46834c8ae665343a3ff7dc3cc929de34ad62b7b55974f4e3fd20990d3e564b96e4d33de87716052d58cf823e") @@ -81,6 +87,9 @@ func BenchmarkScalarMult(b *testing.B) { // Sanity-check of the map-to-G1 with regards to the IETF draft hash-to-curve func TestMapToG1(t *testing.T) { + if !isG1Compressed() { + t.Skip() + } // test vectors from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-J.9.1 dst := []byte("QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_") @@ -130,7 +139,7 @@ func BenchmarkMapToG1(b *testing.B) { // test subgroup membership check in G1 and G2 func TestSubgroupCheck(t *testing.T) { prg := getPRG(t) - seed := make([]byte, g2BytesLen) + seed := make([]byte, 192) _, err := prg.Read(seed) require.NoError(t, err) @@ -148,12 +157,7 @@ func TestSubgroupCheck(t *testing.T) { unsafeMapToG2(&p, seed) // point in G2 assert.True(t, checkMembershipG2(&p)) - inG2 := false - for !inG2 { - _, err := prg.Read(seed) - require.NoError(t, err) - inG2 = unsafeMapToG2Complement(&p, seed) // point in E2\G2 - } + unsafeMapToG2Complement(&p, seed) // point in E2\G2 assert.False(t, checkMembershipG2(&p)) }) } diff --git a/crypto/bls_test.go b/crypto/bls_test.go index 7ea369a5b73..4047967be9b 100644 --- a/crypto/bls_test.go +++ b/crypto/bls_test.go @@ -29,7 +29,7 @@ func TestBLSMainMethods(t *testing.T) { // - signature decoding only accepts reduced x-coordinates to avoid signature malleability t.Run("invalid x coordinate larger than p", func(t *testing.T) { - if !isG1Compressed() { + if !isG1Compressed() || !isG2Compressed() { t.Skip() } msg, err := hex.DecodeString("7f26ba692dc2da7ff828ef4675ff1cd6ab855fca0637b6dab295f1df8e51bc8bb1b8f0c6610aabd486cf1f098f2ddbc6691d94e10f928816f890a3d366ce46249836a595c7ea1828af52e899ba2ab627ab667113bb563918c5d5a787c414399487b4e3a7") @@ -221,6 +221,9 @@ func TestBLSEncodeDecode(t *testing.T) { // may implicitely rely on the property. t.Run("public key with non-reduced coordinates", func(t *testing.T) { + if !isG2Compressed() { + t.Skip() + } // valid pk with x[0] < p and x[1] < p validPk, err := hex.DecodeString("818d72183e3e908af5bd6c2e37494c749b88f0396d3fbc2ba4d9ea28f1c50d1c6a540ec8fe06b6d860f72ec9363db3b8038360809700d36d761cb266af6babe9a069dc7364d3502e84536bd893d5f09ec2dd4f07cae1f8a178ffacc450f9b9a2") require.NoError(t, err) diff --git a/crypto/sign_test_utils.go b/crypto/sign_test_utils.go index 8362df83c7f..06179a01989 100644 --- a/crypto/sign_test_utils.go +++ b/crypto/sign_test_utils.go @@ -5,7 +5,6 @@ import ( "fmt" mrand "math/rand" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,7 +13,7 @@ import ( ) func getPRG(t *testing.T) *mrand.Rand { - random := time.Now().UnixNano() + random := int64(1685491239186156000) //time.Now().UnixNano() t.Logf("rng seed is %d", random) rng := mrand.New(mrand.NewSource(random)) return rng @@ -186,13 +185,13 @@ func testEncodeDecode(t *testing.T, salg SigningAlgorithm) { skCheckBytes := skCheck.Encode() assert.Equal(t, skBytes, skCheckBytes, "keys should be equal") distinctSkBytes := distinctSk.Encode() - assert.NotEqual(t, skBytes, distinctSkBytes, "keys should be different") + assert.NotEqual(t, skBytes, distinctSkBytes) // check public key encoding pk := sk.PublicKey() pkBytes := pk.Encode() pkCheck, err := DecodePublicKey(salg, pkBytes) - require.Nil(t, err, "the key decoding failed") + require.Nil(t, err) assert.True(t, pk.Equals(pkCheck), "key equality check failed") pkCheckBytes := pkCheck.Encode() assert.Equal(t, pkBytes, pkCheckBytes, "keys should be equal") @@ -200,14 +199,17 @@ func testEncodeDecode(t *testing.T, salg SigningAlgorithm) { assert.NotEqual(t, pkBytes, distinctPkBytes, "keys should be different") // same for the compressed encoding - pkComprBytes := pk.EncodeCompressed() - pkComprCheck, err := DecodePublicKeyCompressed(salg, pkComprBytes) - require.Nil(t, err, "the key decoding failed") - assert.True(t, pk.Equals(pkComprCheck), "key equality check failed") - pkCheckComprBytes := pkComprCheck.EncodeCompressed() - assert.Equal(t, pkComprBytes, pkCheckComprBytes, "keys should be equal") - distinctPkComprBytes := distinctSk.PublicKey().EncodeCompressed() - assert.NotEqual(t, pkComprBytes, distinctPkComprBytes, "keys should be different") + // skip is BLS is used and compression isn't supported + if !(salg == BLSBLS12381 && !isG2Compressed()) { + pkComprBytes := pk.EncodeCompressed() + pkComprCheck, err := DecodePublicKeyCompressed(salg, pkComprBytes) + require.Nil(t, err, "the key decoding failed") + assert.True(t, pk.Equals(pkComprCheck), "key equality check failed") + pkCheckComprBytes := pkComprCheck.EncodeCompressed() + assert.Equal(t, pkComprBytes, pkCheckComprBytes, "keys should be equal") + distinctPkComprBytes := distinctSk.PublicKey().EncodeCompressed() + assert.NotEqual(t, pkComprBytes, distinctPkComprBytes, "keys should be different") + } } }) From 01b64c560343ef605c7d4737c802c2b7ac2b2ac7 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Tue, 30 May 2023 21:34:18 -0600 Subject: [PATCH 11/11] make sure older compilers recognize uintx_t --- crypto/bls12381_utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/bls12381_utils.h b/crypto/bls12381_utils.h index b9c8ab755a7..d35e0298c59 100644 --- a/crypto/bls12381_utils.h +++ b/crypto/bls12381_utils.h @@ -6,6 +6,7 @@ #define _BLS12_381_UTILS_H #include +#include #include "blst_include.h" typedef uint8_t byte;