Skip to content

Commit

Permalink
C API for BLS signature aggregate_verify & batch_verify (incl par…
Browse files Browse the repository at this point in the history
…allel) (#381)

* fix up BLS sig `batch_verify` for C API

We had a leftover generic and the `messages` were passed not as an
openArray as required

* [bls] fix `update` for `View` messages

The previous code recursed into itself instead of the main `update` above

* [bls] fix up batch_verify_parallel C API signature, convert openArray

* [lib] build bls_signature_parallel into libconstantine

* [C] add C API header for aggregate_verify, batch_verify

* [examples] add example for batch_verify C API

* [examples] extend README for C API on how to compile (and run)

* [C] add C API header for batch_verify_parallel, add to `constantine.h`

* [examples] extend example to use parallel batch_verify version

* [examples] use clang, fix typo

* update naming ByteView -> ctt_span following C++20 naming

* [C] fix name of `aggregate_verify` by adding prefix

* [example] use `sysrand` to fill secureRandomBytes array
  • Loading branch information
Vindaar authored May 18, 2024
1 parent e48d2f0 commit 070e6eb
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 14 deletions.
1 change: 1 addition & 0 deletions bindings/lib_constantine.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import
../constantine/csprngs,
# Protocols
../constantine/ethereum_bls_signatures,
../constantine/ethereum_bls_signatures_parallel,
../constantine/trusted_setups/ethereum_kzg_srs,
../constantine/ethereum_eip4844_kzg,
../constantine/ethereum_eip4844_kzg_parallel,
Expand Down
12 changes: 6 additions & 6 deletions constantine/ethereum_bls_signatures.nim
Original file line number Diff line number Diff line change
Expand Up @@ -411,11 +411,11 @@ func aggregate_verify*[Msg](pubkeys: openArray[PublicKey], messages: openArray[M
return cttEthBls_VerificationFailure

# C FFI
func batch_verify*[Msg](pubkeys: ptr UncheckedArray[PublicKey],
messages: ptr UncheckedArray[View[byte]],
signatures: ptr UncheckedArray[Signature],
len: int,
secureRandomBytes: array[32, byte]): cttEthBlsStatus {.libPrefix: prefix_ffi.} =
func batch_verify*(pubkeys: ptr UncheckedArray[PublicKey],
messages: ptr UncheckedArray[View[byte]],
signatures: ptr UncheckedArray[Signature],
len: int,
secureRandomBytes: array[32, byte]): cttEthBlsStatus {.libPrefix: prefix_ffi.} =
## Verify that all (pubkey, message, signature) triplets are valid
## returns `true` if all signatures are valid, `false` if at least one is invalid.
##
Expand Down Expand Up @@ -454,7 +454,7 @@ func batch_verify*[Msg](pubkeys: ptr UncheckedArray[PublicKey],

let verified = batchVerify(
pubkeys.toOpenArray(len).unwrap(),
messages,
messages.toOpenArray(len),
signatures.toOpenArray(len).unwrap(),
sha256, 128, DomainSeparationTag, secureRandomBytes)
if verified:
Expand Down
4 changes: 2 additions & 2 deletions constantine/ethereum_bls_signatures_parallel.nim
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import
{.push checks: off.}

# C FFI
proc batch_verify_parallel*[Msg](
proc batch_verify_parallel*(
tp: Threadpool,
pubkeys: ptr UncheckedArray[PublicKey],
messages: ptr UncheckedArray[View[byte]],
Expand Down Expand Up @@ -80,7 +80,7 @@ proc batch_verify_parallel*[Msg](

let verified = tp.batchVerify_parallel(
pubkeys.toOpenArray(len).unwrap(),
messages,
messages.toOpenArray(len),
signatures.toOpenArray(len).unwrap(),
sha256, 128, DomainSeparationTag, secureRandomBytes)
if verified:
Expand Down
2 changes: 1 addition & 1 deletion constantine/signatures/bls_signatures.nim
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ func update*[Pubkey, Sig: ECP_ShortW_Aff](
pubkey: Pubkey,
message: View[byte],
signature: Sig): bool {.inline.} =
ctx.update(pubkey, message, signature)
ctx.update(pubkey, message.toOpenArray(), signature)

func handover*(ctx: var BLSBatchSigAccumulator) {.inline.} =
## Prepare accumulator for cheaper merging.
Expand Down
24 changes: 23 additions & 1 deletion examples-c/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,26 @@

This folder holds tests (prefixed with `t_`) and examples for the C bindings.

Headers are located in `include/` and the static and dynamic libraries in `lib/`
Headers are located in `include/` and the static and dynamic libraries
in `lib/`.

To compile and run the example / test case, for example:

```sh
clang ethereum_bls_signatures.c -o ethereum_bls_signatures -I../include -L../lib -lconstantine
```

For the test case, you also need to link in `-lgmp`.

And depending on your setup you might need to specify where
`libconstantine.so` can be found:

```sh
LD_LIBRARY_PATH=../lib ./ethereum_bls_signatures
```

(in case you compile and run from this directory).

The above of course assumes you have already compiled
`libconstantine.so` (using `nimble make_lib` from the root directory
of the repository).
53 changes: 51 additions & 2 deletions examples-c/ethereum_bls_signatures.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,56 @@ int main(){
printf("Signature verification failure: status %d - %s\n", bls_status, ctt_eth_bls_status_to_string(bls_status));
exit(1);
}

printf("Example BLS signature/verification protocol completed successfully\n");


// ------------------------------
// Batch verification
// ------------------------------

// try to use batch verify; We just reuse the data from above 3 times
const ctt_eth_bls_pubkey pkeys[3] = { pubkey, pubkey, pubkey };
ctt_span messages[3] = { // already hashed message, reuse 3 times
{ message, 32 },
{ message, 32 },
{ message, 32 }
};
const ctt_eth_bls_signature sigs[3] = { sig, sig, sig };

// Use constantine's `sysrand` to fill the secure random bytes
byte srb[32];
if(!ctt_csprng_sysrand(srb, 32)){
printf("Failed to fill `srb` using `sysrand`\n");
exit(1);
}

bls_status = ctt_eth_bls_batch_verify(pkeys, messages, sigs, 3, srb);
if (bls_status != cttEthBls_Success) {
printf("Batch verification failure: status %d - %s\n", bls_status, ctt_eth_bls_status_to_string(bls_status));
exit(1);
}

printf("Example BLS batch verification completed successfully\n");

// ------------------------------
// Batch verification, parallel
// ------------------------------

// and now try to use a threadpool and do the same in parallel

struct ctt_threadpool* tp = ctt_threadpool_new(4);
printf("Constantine: Threadpool init successful.\n");
bls_status = ctt_eth_bls_batch_verify_parallel(tp, pkeys, messages, sigs, 3, srb);
if (bls_status != cttEthBls_Success) {
printf("Batch verification failure: status %d - %s\n", bls_status, ctt_eth_bls_status_to_string(bls_status));
exit(1);
}
printf("Example parallel BLS batch verification completed successfully\n");

ctt_threadpool_shutdown(tp);
printf("Constantine: Threadpool shutdown successful.\n");



return 0;
}
}
3 changes: 2 additions & 1 deletion include/constantine.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@

// Protocols
#include "constantine/protocols/ethereum_bls_signatures.h"
#include "constantine/protocols/ethereum_bls_signatures_parallel.h"
#include "constantine/protocols/ethereum_eip4844_kzg.h"
#include "constantine/protocols/ethereum_eip4844_kzg_parallel.h"

#endif
#endif
67 changes: 66 additions & 1 deletion include/constantine/protocols/ethereum_bls_signatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ static const char* ctt_eth_bls_status_to_string(ctt_eth_bls_status status) {
return "cttEthBls_InvalidStatusCode";
}

// Wrapper of the View[T] Nim type for the common case of View[byte]
//
// type View*[byte] = object # with T = byte
// data: ptr UncheckedArray[byte] # 8 bytes
// len*: int # 8 bytes (Nim `int` is a 64bit int type)
// `span` naming following C++20 std::span<T>
typedef struct { byte* data; size_t len; } ctt_span;

// Comparisons
// ------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -228,8 +236,65 @@ ctt_eth_bls_status ctt_eth_bls_fast_aggregate_verify(const ctt_eth_bls_pubkey pu
const byte* message, ptrdiff_t message_len,
const ctt_eth_bls_signature* aggregate_sig) __attribute__((warn_unused_result));


/** Verify the aggregated signature of multiple (pubkey, message) pairs
* returns `true` if the signature is valid, `false` otherwise.
*
* For message domain separation purpose, the tag is `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_`
*
* Input:
* - Public keys initialized by one of the key derivation or deserialization procedure.
* Or validated via validate_pubkey
* - Messages
* - `len`: Number of elements in the `pubkeys` and `messages` arrays.
* - a signature initialized by one of the key derivation or deserialization procedure.
* Or validated via validate_signature
*
* In particular, the public keys and signature are assumed to be on curve subgroup checked.
*
* To avoid splitting zeros and rogue keys attack:
* 1. Public keys signing the same message MUST be aggregated and checked for 0 before calling this function.
* 2. Augmentation or Proof of possessions must used for each public keys.
*/
ctt_eth_bls_status ctt_eth_bls_aggregate_verify(const ctt_eth_bls_pubkey* pubkeys,
const ctt_span messages[],
ptrdiff_t len,
const ctt_eth_bls_signature* aggregate_sig) __attribute__((warn_unused_result));


/** Verify that all (pubkey, message, signature) triplets are valid
* returns `true` if all signatures are valid, `false` if at least one is invalid.
*
* For message domain separation purpose, the tag is `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_`
*
* Input:
* - Public keys initialized by one of the key derivation or deserialization procedure.
* Or validated via validate_pubkey
* - Messages
* - Signatures initialized by one of the key derivation or deserialization procedure.
* Or validated via validate_signature
*
* In particular, the public keys and signature are assumed to be on curve subgroup checked.
*
* To avoid splitting zeros and rogue keys attack:
* 1. Cryptographically-secure random bytes must be provided.
* 2. Augmentation or Proof of possessions must used for each public keys.
*
* The secureRandomBytes will serve as input not under the attacker control to foil potential splitting zeros inputs.
* The scheme assumes that the attacker cannot
* resubmit 2^64 times forged (publickey, message, signature) triplets
* against the same `secureRandomBytes`
*/

ctt_eth_bls_status ctt_eth_bls_batch_verify(const ctt_eth_bls_pubkey pubkeys[],
const ctt_span messages[],
const ctt_eth_bls_signature signatures[],
ptrdiff_t len,
const byte secure_random_bytes[32]
) __attribute__((warn_unused_result));

#ifdef __cplusplus
}
#endif

#endif // __CTT_H_ETHEREUM_BLS_SIGNATURES__
#endif // __CTT_H_ETHEREUM_BLS_SIGNATURES__
64 changes: 64 additions & 0 deletions include/constantine/protocols/ethereum_bls_signatures_parallel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/** Constantine
* Copyright (c) 2018-2019 Status Research & Development GmbH
* Copyright (c) 2020-Present Mamy André-Ratsimbazafy
* Licensed and distributed under either of
* * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
* * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
* at your option. This file may not be copied, modified, or distributed except according to those terms.
*/
#ifndef __CTT_H_ETHEREUM_BLS_SIGNATURES_PARALLEL__
#define __CTT_H_ETHEREUM_BLS_SIGNATURES_PARALLEL__

#include "constantine/core/datatypes.h"
#include "constantine/core/threadpool.h"
#include "constantine/protocols/ethereum_bls_signatures.h"

#ifdef __cplusplus
extern "C" {
#endif

// Ethereum BLS signatures parallel interface
// ------------------------------------------------------------------------------------------------

/**
* Verify that all (pubkey, message, signature) triplets are valid
* returns `true` if all signatures are valid, `false` if at least one is invalid.
*
* For message domain separation purpose, the tag is `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_`
*
* Input:
* - Public keys initialized by one of the key derivation or deserialization procedure.
* Or validated via validate_pubkey
* - Messages as an anonymous struct of `(data = byte*, length = size_t)` pairs
* (the `View` type on the Nim side uses `int` for the length field, which depends on the
* system)
* - Signatures initialized by one of the key derivation or deserialization procedure.
* Or validated via validate_signature
* - `len`: number of elements in `pubkey`, `messages`, `sig` arrays
*
* In particular, the public keys and signature are assumed to be on curve subgroup checked.
*
* To avoid splitting zeros and rogue keys attack:
* 1. Cryptographically-secure random bytes must be provided.
* 2. Augmentation or Proof of possessions must used for each public keys.
*
* The secureRandomBytes will serve as input not under the attacker control to foil potential splitting zeros inputs.
* The scheme assumes that the attacker cannot
* resubmit 2^64 times forged (publickey, message, signature) triplets
* against the same `secureRandomBytes`
*/
ctt_eth_bls_status ctt_eth_bls_batch_verify_parallel(
const ctt_threadpool* tp,
const ctt_eth_bls_pubkey pubkey[],
const ctt_span messages[],
const ctt_eth_bls_signature sig[],
ptrdiff_t len,
const byte secure_random_bytes[32]
) __attribute__((warn_unused_result));


#ifdef __cplusplus
}
#endif

#endif // __CTT_H_ETHEREUM_BLS_SIGNATURES_PARALLEL__

0 comments on commit 070e6eb

Please sign in to comment.