Skip to content

Commit

Permalink
Merge pull request #4681 from onflow/tarak/blst-misc
Browse files Browse the repository at this point in the history
[Crypto] misc updates
  • Loading branch information
tarakby authored Sep 6, 2023
2 parents 6bb393c + 14c9e3d commit 140edc3
Show file tree
Hide file tree
Showing 14 changed files with 106 additions and 52 deletions.
3 changes: 0 additions & 3 deletions crypto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,6 @@ All signature schemes use the generic interfaces of `PrivateKey` and `PublicKey`
public keys, using a binary tree of aggregations.
* SPoCK scheme based on BLS: verifies two signatures have been generated from the same message that is unknown to the verifier.

* Future features:
* support minimal-pubkey-size variant

### PRNG

* ChaCha20-based CSPRNG
Expand Down
3 changes: 0 additions & 3 deletions crypto/bls.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ package crypto
// - SPoCK scheme based on BLS: verifies two signatures are generated from the same message,
// even though the message is unknown to the verifier.

// future features:
// - implement a G1/G2 swap (minimal-pubkey-size variant)

// #include "bls_include.h"
import "C"

Expand Down
75 changes: 54 additions & 21 deletions crypto/bls12381_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
// compile all blst C src along with this file
#include "blst_src.c"

// make sure flow crypto types are consistent with BLST types
void types_sanity(void) {
assert(sizeof(Fp) == sizeof(vec384));
assert(sizeof(E1) == sizeof(POINTonE1));
assert(sizeof(E2) == sizeof(POINTonE2));
}

// ------------------- Fr utilities

// Montgomery constant R related to the curve order r
Expand Down Expand Up @@ -133,16 +140,15 @@ ERROR Fr_read_bytes(Fr *a, const byte *bin, int len) {
if (len != Fr_BYTES) {
return BAD_ENCODING;
}
// compare to r using the BLST tool
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!
pow256_from_be_bytes(tmp, bin);
// (check_mod_256 compares pow256 against a vec256!)
if (!check_mod_256(tmp, BLS12_381_r)) {
return BAD_VALUE;
}
vec_zero(tmp, sizeof(tmp));
limbs_from_be_bytes((limb_t *)a, bin, Fr_BYTES); // TODO: check endianness!!
limbs_from_be_bytes((limb_t *)a, bin, Fr_BYTES);
return VALID;
}

Expand Down Expand Up @@ -170,11 +176,16 @@ void Fr_write_bytes(byte *bin, const Fr *a) {
be_bytes_from_limbs(bin, (limb_t *)a, Fr_BYTES);
}

// maps big-endian bytes into an Fr element using modular reduction
// Input is byte-big-endian, output is Fr (internally vec256)
// TODO: check redc_mont_256(vec256 ret, const vec512 a, const vec256 p, limb_t
// n0);
// maps big-endian bytes of any size into an Fr element using modular reduction.
// Input is byte-big-endian, output is Fr (internally vec256).
//
// Note: could use redc_mont_256(vec256 ret, const vec512 a, const vec256 p,
// limb_t n0) to reduce 512 bits at a time.
static void Fr_from_be_bytes(Fr *out, const byte *bytes, size_t n) {
// input can be written in base 2^|R|, with R the Montgomery constant
// N = l_1 + L_2*2^|R| .. + L_n*2^(|R|*(n-1))
// Therefore N mod p can be expressed using R as:
// N mod p = l_1 + L_2*R .. + L_n*R^(n-1)
Fr digit, radix;
Fr_set_zero(out);
Fr_copy(&radix, (Fr *)BLS12_381_rRR); // R^2
Expand All @@ -193,7 +204,7 @@ static void Fr_from_be_bytes(Fr *out, const byte *bytes, size_t n) {
limbs_from_be_bytes((limb_t *)&digit, p - n, n);
Fr_mul_montg(&digit, &digit, &radix);
Fr_add(out, out, &digit);
// at this point : out = l_1*R + L_2*R^2 .. + L_n*R^n
// at this point : out = l_1*R + L_2*R^2 .. + L_n*R^n,
// reduce the extra R
Fr_from_montg(out, out);
// clean up possible sensitive data
Expand Down Expand Up @@ -456,8 +467,8 @@ bool E1_in_G1(const E1 *p) {
// - 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?
// Note: could use POINTonE1_Deserialize_BE and POINTonE1_Uncompress_Z,
// but needs to update the logic around G2 subgroup check
ERROR E1_read_bytes(E1 *a, const byte *bin, const int len) {
// check the length
if (len != G1_SER_BYTES) {
Expand Down Expand Up @@ -701,16 +712,18 @@ const E2 *BLS12_381_minus_g2 = (const E2 *)&BLS12_381_NEG_G2;
// E2_read_bytes imports a E2(Fp^2) point from a buffer in a compressed or
// uncompressed form. The resulting point is guaranteed to be on curve E2 (no G2
// check is included).
// E2 point is in affine coordinates. This avoids further conversions
// when the point is used in multiple pairing computation.
//
// returns:
// - 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?
//
// Note: can use with POINTonE2_Deserialize_BE and POINTonE2_Uncompress_Z,
// and update the logic around G2 subgroup check.
ERROR E2_read_bytes(E2 *a, const byte *bin, const int len) {
// check the length
if (len != G2_SER_BYTES) {
Expand Down Expand Up @@ -871,7 +884,7 @@ void E2_add(E2 *res, const E2 *a, const E2 *b) {
}

// generic point double that must handle point at infinity
void E2_double(E2 *res, const E2 *a) {
static void E2_double(E2 *res, const E2 *a) {
POINTonE2_double((POINTonE2 *)res, (POINTonE2 *)a);
}

Expand Down Expand Up @@ -927,6 +940,15 @@ void G2_mult_gen(E2 *res, const Fr *expo) {
vec_zero(&tmp, sizeof(tmp));
}

// Exponentiation of generator g2 of G2, res = expo.g2
//
// This is useful for results being used multiple times in pairings.
// Conversion to affine saves later pre-pairing conversions.
void G2_mult_gen_to_affine(E2 *res, const Fr *expo) {
G2_mult_gen(res, expo);
E2_to_affine(res, res);
}

// checks if input E2 point is on the subgroup G2.
// It assumes input `p` is on E2.
bool E2_in_G2(const E2 *p) {
Expand All @@ -942,6 +964,16 @@ void E2_sum_vector(E2 *sum, const E2 *y, const int len) {
}
}

// computes the sum of the E2 array elements `y[i]`, converts it
// to affine coordinates, and writes it in `sum`.
//
// This is useful for results being used multiple times in pairings.
// Conversion to affine saves later pre-pairing conversions.
void E2_sum_vector_to_affine(E2 *sum, const E2 *y, const int len) {
E2_sum_vector(sum, y, len);
E2_to_affine(sum, sum);
}

// 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) {
Expand Down Expand Up @@ -1007,15 +1039,16 @@ void Fp12_multi_pairing(Fp12 *res, const E1 *p, const E2 *q, const int len) {
continue;
}
// `miller_loop_n` expects affine coordinates in a `POINTonEx_affine` array.
// `POINTonEx_affine` has a different size than `POINTonEx` or `Ex` !
// `POINTonEx_affine` has a different size than `POINTonEx` and `Ex` !
E1 tmp1;
E1_to_affine(&tmp1, p + i);
vec_copy(p_aff + n, &tmp1, sizeof(POINTonE1_affine));
E2 tmp2;
E2_to_affine(&tmp2, q + i);
vec_copy(q_aff + n, &tmp2, sizeof(POINTonE2_affine));
n++;
if (n == N_MAX) { // if p_ and q_ are filled, batch `N_MAX` miller loops
// if p_aff and q_aff are filled, batch `N_MAX` miller loops
if (n == N_MAX) {
if (!init_flag) {
miller_loop_n(res_vec, q_aff, p_aff, N_MAX);
init_flag = 1;
Expand All @@ -1027,8 +1060,8 @@ void Fp12_multi_pairing(Fp12 *res, const E1 *p, const E2 *q, const int len) {
n = 0;
}
}
// if p_ and q_ aren't empty,
// remaining couples are also batched in `n` miller loops
// if p_aff and q_aff aren't empty,
// the remaining couples are also batched in `n` miller loops
if (n > 0) {
if (!init_flag) {
miller_loop_n(res_vec, q_aff, p_aff, n);
Expand Down
11 changes: 9 additions & 2 deletions crypto/bls12381_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package crypto
// these tools are shared by the BLS signature scheme, the BLS based threshold signature
// and the BLS distributed key generation protocols

// #cgo CFLAGS: -I${SRCDIR}/ -I${SRCDIR}/blst_src -I${SRCDIR}/blst_src/build -D__BLST_CGO__ -fno-builtin-memcpy -fno-builtin-memset -Wall -Wno-unused-function -Wno-unused-macros
// #cgo CFLAGS: -I${SRCDIR}/ -I${SRCDIR}/blst_src -I${SRCDIR}/blst_src/build -D__BLST_CGO__ -Wall -fno-builtin-memcpy -fno-builtin-memset -Wno-unused-function -Wno-unused-macros -Wno-unused-variable
// #cgo amd64 CFLAGS: -D__ADX__ -mno-avx
// #cgo mips64 mips64le ppc64 ppc64le riscv64 s390x CFLAGS: -D__BLST_NO_ASM__
// #include "bls12381_utils.h"
Expand Down Expand Up @@ -71,6 +71,8 @@ var g2PublicKey pubKeyBLSBLS12381

// initialization of BLS12-381 curve
func initBLS12381() {
C.types_sanity()

if isG1Compressed() {
g1SerHeader = 0xC0
} else {
Expand Down Expand Up @@ -110,8 +112,13 @@ func generatorScalarMultG1(res *pointE1, expo *scalar) {
}

// Scalar multiplication of generator g2 in G2
//
// This often results in a public key that is used in
// multiple pairing computation. Therefore, convert the
// resulting point to affine coordinate to save pre-pairing
// conversions.
func generatorScalarMultG2(res *pointE2, expo *scalar) {
C.G2_mult_gen((*C.E2)(res), (*C.Fr)(expo))
C.G2_mult_gen_to_affine((*C.E2)(res), (*C.Fr)(expo))
}

// comparison in Fr where r is the group order of G1/G2
Expand Down
14 changes: 10 additions & 4 deletions crypto/bls12381_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,17 @@ typedef enum {
#define G2_BYTES (2 * Fp2_BYTES)

// Compressed and uncompressed points
#define COMPRESSED 1
#define UNCOMPRESSED 0
#define COMPRESSED (UNCOMPRESSED ^ 1)
#define G1_SERIALIZATION (COMPRESSED)
#define G2_SERIALIZATION (COMPRESSED)
#define G1_SER_BYTES (G1_BYTES / (G1_SERIALIZATION + 1))
#define G2_SER_BYTES (G2_BYTES / (G2_SERIALIZATION + 1))
#define G1_SER_BYTES \
(G1_SERIALIZATION == UNCOMPRESSED ? G1_BYTES : (G1_BYTES / 2))
#define G2_SER_BYTES \
(G2_SERIALIZATION == UNCOMPRESSED ? G2_BYTES : (G2_BYTES / 2))

// init-related functions
void types_sanity(void);

// Fr utilities
extern const Fr BLS12_381_rR;
Expand Down Expand Up @@ -109,12 +114,13 @@ void E2_to_affine(E2 *, const E2 *);
ERROR E2_read_bytes(E2 *, const byte *, const int);
void E2_write_bytes(byte *, const E2 *);
void G2_mult_gen(E2 *, const Fr *);
void G2_mult_gen_to_affine(E2 *, const Fr *);
void E2_mult(E2 *, const E2 *, const Fr *);
void E2_mult_small_expo(E2 *, const E2 *, const byte);
void E2_add(E2 *res, const E2 *a, const E2 *b);
void E2_double(E2 *res, const E2 *a);
void E2_neg(E2 *, const E2 *);
void E2_sum_vector(E2 *, const E2 *, const int);
void E2_sum_vector_to_affine(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);
Expand Down
2 changes: 1 addition & 1 deletion crypto/bls_multisig.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func AggregateBLSPublicKeys(keys []PublicKey) (PublicKey, error) {
}

var sum pointE2
C.E2_sum_vector((*C.E2)(&sum), (*C.E2)(&points[0]),
C.E2_sum_vector_to_affine((*C.E2)(&sum), (*C.E2)(&points[0]),
(C.int)(len(points)))

sumKey := newPubKeyBLSBLS12381(&sum)
Expand Down
1 change: 0 additions & 1 deletion crypto/bls_thresholdsign.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package crypto

// #cgo CFLAGS:
// #include "bls_thresholdsign_include.h"
import "C"

Expand Down
2 changes: 1 addition & 1 deletion crypto/blst_include.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ typedef vec384 Fp;
// curve E_1 (over F_p)
// E_1 points are represented in Jacobian coordinates (x,y,z),
// where x, y, x are elements of F_p (type `Fp`).
// `E1` is equivelent to type `POINTonE1` (used internally by BLST for Jacobian
// `E1` is equivalent to type `POINTonE1` (used internally by BLST for Jacobian
// E1 elements) `E1` is defined as a struct to be exportable through cgo to the
// Go layer. `E1` is also used to represent all subgroup G_1 elements.
typedef struct {
Expand Down
1 change: 1 addition & 0 deletions crypto/blst_src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ To upgrade the BLST version:
- [ ] copy the folder `<blst>/build/` into this folder.
- [ ] move `./blst_src/build/assembly.S` to `./blst_src/build/blst_assembly.S`.
- [ ] copy `<blst>/bindings/blst.h` and `<blst>/bindings/blst_aux.h` into this folder.
- [ ] check that C flags in `./bls12381_utils.go` still match the C flags in `<blst>/bindings/go/blst.go`.
- [ ] solve all breaking changes that may occur.
- [ ] update the commit version on this `README`.

Expand Down
28 changes: 22 additions & 6 deletions crypto/dkg_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,33 @@ 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)
ERROR E2_vector_read_bytes(E2 *A, const byte *src, const int len) {
// The function imports an array of `n` E2 points from a concatenated array of
// bytes. The bytes array is supposed to be of size (n * G2_SER_BYTES).
//
// If return is `VALID`, output vector is guaranteed to be in G2.
// It returns other errors if at least one input isn't a serialization of a E2
// point, or an input E2 point isn't in G2.
// returns:
// - BAD_ENCODING if the serialization header bits of at least one input are
// invalid.
// - BAD_VALUE if Fp^2 coordinates of at least one input couldn't
// deserialize.
// - POINT_NOT_ON_CURVE if at least one input deserialized point isn't on
// E2.
// - POINT_NOT_IN_GROUP if at least one E2 point isn't in G2.
// - VALID if deserialization of all points to G2 is valid.
ERROR G2_vector_read_bytes(E2 *A, const byte *src, const int n) {
byte *p = (byte *)src;
for (int i = 0; i < len; i++) {
for (int i = 0; i < n; i++) {
int read_ret = E2_read_bytes(&A[i], p, G2_SER_BYTES);
if (read_ret != VALID)
if (read_ret != VALID) {
return read_ret;
}
if (!E2_in_G2(&A[i])) {
return POINT_NOT_IN_GROUP;
}
p += G2_SER_BYTES;
}
// TODO: add G2 subgroup check?
return VALID;
}

Expand Down
11 changes: 5 additions & 6 deletions crypto/dkg_feldmanvss.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package crypto

// #cgo CFLAGS:
// #include "dkg_include.h"
import "C"

Expand Down Expand Up @@ -398,7 +397,7 @@ func (s *feldmanVSSstate) receiveShare(origin index, data []byte) {

// receives the public vector from the
func (s *feldmanVSSstate) receiveVerifVector(origin index, data []byte) {
// only accept the verification vector from the .
// only accept the verification vector from the dealer.
if origin != s.dealerIndex {
return
}
Expand Down Expand Up @@ -457,18 +456,18 @@ func writeVerifVector(dest []byte, A []pointE2) {
)
}

// readVerifVector imports A vector from an array of bytes,
// assuming the slice length matches the vector length
// readVerifVector imports A vector (G2 points) from an array of bytes,
// assuming the slice length matches the vector length.
func readVerifVector(A []pointE2, src []byte) error {
read := C.E2_vector_read_bytes(
read := C.G2_vector_read_bytes(
(*C.E2)(&A[0]),
(*C.uchar)(&src[0]),
(C.int)(len(A)))
if read == valid {
return nil
}
// invalid A vector
return invalidInputsErrorf("the verifcation vector does not serialize valid E2 points: error code %d", read)
return invalidInputsErrorf("the verification vector does not serialize valid G2 points: error code %d", read)
}

func (s *feldmanVSSstate) verifyShare() bool {
Expand Down
1 change: 0 additions & 1 deletion crypto/dkg_feldmanvssq.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package crypto

// #cgo CFLAGS:
// #include "dkg_include.h"
import "C"

Expand Down
2 changes: 1 addition & 1 deletion crypto/dkg_include.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ void Fr_polynomial_image(Fr *out, E2 *y, const Fr *a, const int deg,
const byte x);
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);
ERROR E2_vector_read_bytes(E2 *A, const byte *src, const int len);
ERROR G2_vector_read_bytes(E2 *A, const byte *src, const int len);
bool G2_check_log(const Fr *x, const E2 *y);

#endif
Loading

0 comments on commit 140edc3

Please sign in to comment.