Skip to content

bls12381 - [mainnet]

Contains functions for:

The minimum-pubkey-size variant of Boneh-Lynn-Shacham (BLS) signatures, where public keys are BLS12-381 elliptic-curve points in G1\mathbb{G}_1 and signatures are in G2\mathbb{G}_2, as per the IETF BLS draft standard.

use 0x1::error;
use 0x1::option;

Constants

One of the given inputs has the wrong size.s

const EWRONG_SIZE: u64 = 2;

The caller was supposed to input one or more public keys.

const EZERO_PUBKEYS: u64 = 1;

The number of signers does not match the number of messages to be signed.

const E_NUM_SIGNERS_MUST_EQ_NUM_MESSAGES: u64 = 3;

The public key size, in bytes

const PUBLIC_KEY_NUM_BYTES: u64 = 48;

Random signature generated by running cargo test — bls12381_sample_signature —nocapture —include-ignored in crates/aptos-crypto. The associated SK is 07416693b6b32c84abe45578728e2379f525729e5b94762435a31e65ecc728da.

const RANDOM_PK: vector<u8> = [138, 83, 231, 174, 82, 112, 227, 231, 101, 205, 138, 64, 50, 194, 231, 124, 111, 126, 135, 164, 78, 187, 133, 191, 40, 164, 215, 134, 85, 101, 105, 143, 151, 83, 70, 113, 66, 98, 249, 228, 124, 111, 62, 13, 93, 149, 22, 96];

Random signature generated by running cargo test — bls12381_sample_signature —nocapture —include-ignored in crates/aptos-crypto. The message signed is “Hello Aptos!” and the associated SK is 07416693b6b32c84abe45578728e2379f525729e5b94762435a31e65ecc728da.

const RANDOM_SIGNATURE: vector<u8> = [160, 26, 101, 133, 79, 152, 125, 52, 52, 20, 155, 127, 8, 247, 7, 48, 227, 11, 36, 25, 132, 232, 113, 43, 194, 172, 168, 133, 214, 50, 170, 252, 237, 76, 63, 102, 18, 9, 222, 187, 107, 28, 134, 1, 50, 102, 35, 204, 22, 202, 47, 108, 158, 220, 83, 183, 184, 139, 116, 53, 251, 107, 5, 221, 236, 228, 24, 210, 195, 77, 198, 172, 162, 245, 161, 26, 121, 230, 119, 116, 88, 44, 20, 8, 74, 1, 220, 183, 130, 14, 76, 180, 186, 208, 234, 141];

The signature size, in bytes

const SIGNATURE_SIZE: u64 = 96;

Structs

PublicKey

A validated public key that: (1) is a point in the prime-order subgroup of the BLS12-381 elliptic curve, and (2) is not the identity point

This struct can be used to verify a normal (non-aggregated) signature.

This struct can be combined with a ProofOfPossession struct in order to create a PublicKeyWithPop struct, which can be used to verify a multisignature.

struct PublicKey has copy, drop, store
Fields
bytes: vector<u8>

ProofOfPossession

A proof-of-possession (PoP). Given such a struct and a PublicKey struct, one can construct a PublicKeyWithPoP (see below).

struct ProofOfPossession has copy, drop, store
Fields
bytes: vector<u8>

PublicKeyWithPoP

A validated public key that had a successfully-verified proof-of-possession (PoP).

A vector of these structs can be either: (1) used to verify an aggregate signature (2) aggregated with other PublicKeyWithPoP structs into an AggrPublicKeysWithPoP, which in turn can be used to verify a multisignature

struct PublicKeyWithPoP has copy, drop, store
Fields
bytes: vector<u8>

AggrPublicKeysWithPoP

An aggregation of public keys with verified PoPs, which can be used to verify multisignatures.

struct AggrPublicKeysWithPoP has copy, drop, store
Fields
bytes: vector<u8>

Signature

A BLS signature. This can be either a: (1) normal (non-aggregated) signature (2) signature share (for a multisignature or aggregate signature)

struct Signature has copy, drop, store
Fields
bytes: vector<u8>

AggrOrMultiSignature

An aggregation of BLS signatures. This can be either a: (4) aggregated signature (i.e., an aggregation of signatures s_i, each on a message m_i) (3) multisignature (i.e., an aggregation of signatures s_i, each on the same message m)

We distinguish between a Signature type and a AggrOrMultiSignature type to prevent developers from interchangeably calling verify_multisignature and verify_signature_share to verify both multisignatures and signature shares, which could create problems down the line.

struct AggrOrMultiSignature has copy, drop, store
Fields
bytes: vector<u8>

Functions

public_key_from_bytes

Creates a new public key from a sequence of bytes.

public fun public_key_from_bytes(bytes: vector<u8>): option::Option<bls12381::PublicKey>
Implementation
public fun public_key_from_bytes(bytes: vector<u8>): Option<PublicKey> {
if (validate_pubkey_internal(bytes)) {
option::some(PublicKey {
bytes
})
} else {
option::none<PublicKey>()
}
}

public_key_to_bytes

Serializes a public key into 48 bytes.

public fun public_key_to_bytes(pk: &bls12381::PublicKey): vector<u8>
Implementation
public fun public_key_to_bytes(pk: &PublicKey): vector<u8> {
pk.bytes
}

proof_of_possession_from_bytes

Creates a new proof-of-possession (PoP) which can be later used to create a PublicKeyWithPoP struct,

public fun proof_of_possession_from_bytes(bytes: vector<u8>): bls12381::ProofOfPossession
Implementation
public fun proof_of_possession_from_bytes(bytes: vector<u8>): ProofOfPossession {
ProofOfPossession {
bytes
}
}

proof_of_possession_to_bytes

Serializes the signature into 96 bytes.

public fun proof_of_possession_to_bytes(pop: &bls12381::ProofOfPossession): vector<u8>
Implementation
public fun proof_of_possession_to_bytes(pop: &ProofOfPossession): vector<u8> {
pop.bytes
}

public_key_from_bytes_with_pop

Creates a PoP’d public key from a normal public key and a corresponding proof-of-possession.

public fun public_key_from_bytes_with_pop(pk_bytes: vector<u8>, pop: &bls12381::ProofOfPossession): option::Option<bls12381::PublicKeyWithPoP>
Implementation
public fun public_key_from_bytes_with_pop(pk_bytes: vector<u8>, pop: &ProofOfPossession): Option<PublicKeyWithPoP> {
if (verify_proof_of_possession_internal(pk_bytes, pop.bytes)) {
option::some(PublicKeyWithPoP {
bytes: pk_bytes
})
} else {
option::none<PublicKeyWithPoP>()
}
}

public_key_with_pop_to_normal

Creates a normal public key from a PoP’d public key.

public fun public_key_with_pop_to_normal(pkpop: &bls12381::PublicKeyWithPoP): bls12381::PublicKey
Implementation
public fun public_key_with_pop_to_normal(pkpop: &PublicKeyWithPoP): PublicKey {
PublicKey {
bytes: pkpop.bytes
}
}

public_key_with_pop_to_bytes

Serializes a PoP’d public key into 48 bytes.

public fun public_key_with_pop_to_bytes(pk: &bls12381::PublicKeyWithPoP): vector<u8>
Implementation
public fun public_key_with_pop_to_bytes(pk: &PublicKeyWithPoP): vector<u8> {
pk.bytes
}

signature_from_bytes

Creates a new signature from a sequence of bytes. Does not check the signature for prime-order subgroup membership since that is done implicitly during verification.

public fun signature_from_bytes(bytes: vector<u8>): bls12381::Signature
Implementation
public fun signature_from_bytes(bytes: vector<u8>): Signature {
Signature {
bytes
}
}

signature_to_bytes

Serializes the signature into 96 bytes.

public fun signature_to_bytes(sig: &bls12381::Signature): vector<u8>
Implementation
public fun signature_to_bytes(sig: &Signature): vector<u8> {
sig.bytes
}

signature_subgroup_check

Checks that the group element that defines a signature is in the prime-order subgroup. This check is implicitly performed when verifying any signature via this module, but we expose this functionality in case it might be useful for applications to easily dismiss invalid signatures early on.

public fun signature_subgroup_check(signature: &bls12381::Signature): bool
Implementation
public fun signature_subgroup_check(signature: &Signature): bool {
signature_subgroup_check_internal(signature.bytes)
}

aggregate_pubkeys

Given a vector of public keys with verified PoPs, combines them into an aggregated public key which can be used to verify multisignatures using verify_multisignature and aggregate signatures using verify_aggregate_signature. Aborts if no public keys are given as input.

public fun aggregate_pubkeys(public_keys: vector<bls12381::PublicKeyWithPoP>): bls12381::AggrPublicKeysWithPoP
Implementation
public fun aggregate_pubkeys(public_keys: vector<PublicKeyWithPoP>): AggrPublicKeysWithPoP {
let (bytes, success) = aggregate_pubkeys_internal(public_keys);
assert!(success, std::error::invalid_argument(EZERO_PUBKEYS));
AggrPublicKeysWithPoP {
bytes
}
}

aggregate_pubkey_to_bytes

Serializes an aggregate public key into 48 bytes.

public fun aggregate_pubkey_to_bytes(apk: &bls12381::AggrPublicKeysWithPoP): vector<u8>
Implementation
public fun aggregate_pubkey_to_bytes(apk: &AggrPublicKeysWithPoP): vector<u8> {
apk.bytes
}

aggregate_signatures

Aggregates the input signatures into an aggregate-or-multi-signature structure, which can be later verified via verify_aggregate_signature or verify_multisignature. Returns None if zero signatures are given as input or if some of the signatures are not valid group elements.

public fun aggregate_signatures(signatures: vector<bls12381::Signature>): option::Option<bls12381::AggrOrMultiSignature>
Implementation
public fun aggregate_signatures(signatures: vector<Signature>): Option<AggrOrMultiSignature> {
let (bytes, success) = aggregate_signatures_internal(signatures);
if (success) {
option::some(
AggrOrMultiSignature {
bytes
}
)
} else {
option::none<AggrOrMultiSignature>()
}
}

aggr_or_multi_signature_to_bytes

Serializes an aggregate-or-multi-signature into 96 bytes.

public fun aggr_or_multi_signature_to_bytes(sig: &bls12381::AggrOrMultiSignature): vector<u8>
Implementation
public fun aggr_or_multi_signature_to_bytes(sig: &AggrOrMultiSignature): vector<u8> {
sig.bytes
}

aggr_or_multi_signature_from_bytes

Deserializes an aggregate-or-multi-signature from 96 bytes.

public fun aggr_or_multi_signature_from_bytes(bytes: vector<u8>): bls12381::AggrOrMultiSignature
Implementation
public fun aggr_or_multi_signature_from_bytes(bytes: vector<u8>): AggrOrMultiSignature {
assert!(bytes.length() == SIGNATURE_SIZE, std::error::invalid_argument(EWRONG_SIZE));
AggrOrMultiSignature {
bytes
}
}

aggr_or_multi_signature_subgroup_check

Checks that the group element that defines an aggregate-or-multi-signature is in the prime-order subgroup.

public fun aggr_or_multi_signature_subgroup_check(signature: &bls12381::AggrOrMultiSignature): bool
Implementation
public fun aggr_or_multi_signature_subgroup_check(signature: &AggrOrMultiSignature): bool {
signature_subgroup_check_internal(signature.bytes)
}

verify_aggregate_signature

Verifies an aggregate signature, an aggregation of many signatures s_i, each on a different message m_i.

public fun verify_aggregate_signature(aggr_sig: &bls12381::AggrOrMultiSignature, public_keys: vector<bls12381::PublicKeyWithPoP>, messages: vector<vector<u8>>): bool
Implementation
public fun verify_aggregate_signature(
aggr_sig: &AggrOrMultiSignature,
public_keys: vector<PublicKeyWithPoP>,
messages: vector<vector<u8>>,
): bool {
verify_aggregate_signature_internal(aggr_sig.bytes, public_keys, messages)
}

verify_multisignature

Verifies a multisignature: an aggregation of many signatures, each on the same message m.

public fun verify_multisignature(multisig: &bls12381::AggrOrMultiSignature, aggr_public_key: &bls12381::AggrPublicKeysWithPoP, message: vector<u8>): bool
Implementation
public fun verify_multisignature(
multisig: &AggrOrMultiSignature,
aggr_public_key: &AggrPublicKeysWithPoP,
message: vector<u8>
): bool {
verify_multisignature_internal(multisig.bytes, aggr_public_key.bytes, message)
}

verify_normal_signature

Verifies a normal, non-aggregated signature.

public fun verify_normal_signature(signature: &bls12381::Signature, public_key: &bls12381::PublicKey, message: vector<u8>): bool
Implementation
public fun verify_normal_signature(
signature: &Signature,
public_key: &PublicKey,
message: vector<u8>
): bool {
verify_normal_signature_internal(signature.bytes, public_key.bytes, message)
}

verify_signature_share

Verifies a signature share in the multisignature share or an aggregate signature share.

public fun verify_signature_share(signature_share: &bls12381::Signature, public_key: &bls12381::PublicKeyWithPoP, message: vector<u8>): bool
Implementation
public fun verify_signature_share(
signature_share: &Signature,
public_key: &PublicKeyWithPoP,
message: vector<u8>
): bool {
verify_signature_share_internal(signature_share.bytes, public_key.bytes, message)
}

aggregate_pubkeys_internal

CRYPTOGRAPHY WARNING: This function assumes that the caller verified all public keys have a valid proof-of-possesion (PoP) using verify_proof_of_possession.

Given a vector of serialized public keys, combines them into an aggregated public key, returning (bytes, true), where bytes store the serialized public key. Aborts if no public keys are given as input.

fun aggregate_pubkeys_internal(public_keys: vector<bls12381::PublicKeyWithPoP>): (vector<u8>, bool)
Implementation
native fun aggregate_pubkeys_internal(public_keys: vector<PublicKeyWithPoP>): (vector<u8>, bool);

aggregate_signatures_internal

CRYPTOGRAPHY WARNING: This function can be safely called without verifying that the input signatures are elements of the prime-order subgroup of the BLS12-381 curve.

Given a vector of serialized signatures, combines them into an aggregate signature, returning (bytes, true), where bytes store the serialized signature. Does not check the input signatures nor the final aggregated signatures for prime-order subgroup membership. Returns (_, false) if no signatures are given as input. Does not abort.

fun aggregate_signatures_internal(signatures: vector<bls12381::Signature>): (vector<u8>, bool)
Implementation
native fun aggregate_signatures_internal(signatures: vector<Signature>): (vector<u8>, bool);

validate_pubkey_internal

Return true if the bytes in public_key are a valid BLS12-381 public key: (1) it is NOT the identity point, and (2) it is a BLS12-381 elliptic curve point, and (3) it is a prime-order point Return false otherwise. Does not abort.

fun validate_pubkey_internal(public_key: vector<u8>): bool
Implementation
native fun validate_pubkey_internal(public_key: vector<u8>): bool;

signature_subgroup_check_internal

Return true if the elliptic curve point serialized in signature: (1) is NOT the identity point, and (2) is a BLS12-381 elliptic curve point, and (3) is a prime-order point Return false otherwise. Does not abort.

fun signature_subgroup_check_internal(signature: vector<u8>): bool
Implementation
native fun signature_subgroup_check_internal(signature: vector<u8>): bool;

verify_aggregate_signature_internal

CRYPTOGRAPHY WARNING: First, this function assumes all public keys have a valid proof-of-possesion (PoP). This prevents both small-subgroup attacks and rogue-key attacks. Second, this function can be safely called without verifying that the aggregate signature is in the prime-order subgroup of the BLS12-381 curve.

Returns true if the aggregate signature aggsig on messages under public_keys verifies (where messages[i] should be signed by public_keys[i]).

Returns false if either:

  • no public keys or messages are given as input,
  • number of messages does not equal number of public keys
  • aggsig (1) is the identity point, or (2) is NOT a BLS12-381 elliptic curve point, or (3) is NOT a prime-order point Does not abort.
fun verify_aggregate_signature_internal(aggsig: vector<u8>, public_keys: vector<bls12381::PublicKeyWithPoP>, messages: vector<vector<u8>>): bool
Implementation
native fun verify_aggregate_signature_internal(
aggsig: vector<u8>,
public_keys: vector<PublicKeyWithPoP>,
messages: vector<vector<u8>>,
): bool;

verify_multisignature_internal

CRYPTOGRAPHY WARNING: This function assumes verified proofs-of-possesion (PoP) for the public keys used in computing the aggregate public key. This prevents small-subgroup attacks and rogue-key attacks.

Return true if the BLS multisignature on message verifies against the BLS aggregate public key agg_public_key. Returns false otherwise. Does not abort.

fun verify_multisignature_internal(multisignature: vector<u8>, agg_public_key: vector<u8>, message: vector<u8>): bool
Implementation
native fun verify_multisignature_internal(
multisignature: vector<u8>,
agg_public_key: vector<u8>,
message: vector<u8>
): bool;

verify_normal_signature_internal

CRYPTOGRAPHY WARNING: This function WILL check that the public key is a prime-order point, in order to prevent library users from misusing the library by forgetting to validate public keys before giving them as arguments to this function.

Returns true if the signature on message verifies under public key. Returns false otherwise. Does not abort.

fun verify_normal_signature_internal(signature: vector<u8>, public_key: vector<u8>, message: vector<u8>): bool
Implementation
native fun verify_normal_signature_internal(
signature: vector<u8>,
public_key: vector<u8>,
message: vector<u8>
): bool;

verify_proof_of_possession_internal

Return true if the bytes in public_key are a valid bls12381 public key (as per validate_pubkey) and this public key has a valid proof-of-possesion (PoP). Return false otherwise. Does not abort.

fun verify_proof_of_possession_internal(public_key: vector<u8>, proof_of_possesion: vector<u8>): bool
Implementation
native fun verify_proof_of_possession_internal(
public_key: vector<u8>,
proof_of_possesion: vector<u8>
): bool;

verify_signature_share_internal

CRYPTOGRAPHY WARNING: Assumes the public key has a valid proof-of-possesion (PoP). This prevents rogue-key attacks later on during signature aggregation.

Returns true if the signature_share on message verifies under public key. Returns false otherwise, similar to verify_multisignature. Does not abort.

fun verify_signature_share_internal(signature_share: vector<u8>, public_key: vector<u8>, message: vector<u8>): bool
Implementation
native fun verify_signature_share_internal(
signature_share: vector<u8>,
public_key: vector<u8>,
message: vector<u8>
): bool;

Specification

public_key_from_bytes

public fun public_key_from_bytes(bytes: vector<u8>): option::Option<bls12381::PublicKey>
aborts_if false;
ensures spec_validate_pubkey_internal(bytes) ==> (std::option::spec_is_some(result) && std::option::spec_borrow(result).bytes == bytes);
ensures !spec_validate_pubkey_internal(bytes) ==> std::option::spec_is_none(result);

public_key_from_bytes_with_pop

public fun public_key_from_bytes_with_pop(pk_bytes: vector<u8>, pop: &bls12381::ProofOfPossession): option::Option<bls12381::PublicKeyWithPoP>
pragma opaque;
aborts_if false;
ensures spec_verify_proof_of_possession_internal(pk_bytes, pop.bytes) ==> (std::option::spec_is_some(result) && std::option::spec_borrow(result).bytes == pk_bytes);
ensures !spec_verify_proof_of_possession_internal(pk_bytes, pop.bytes) ==> std::option::spec_is_none(result);
ensures [abstract] result == spec_public_key_from_bytes_with_pop(pk_bytes, pop);

aggregate_pubkeys

public fun aggregate_pubkeys(public_keys: vector<bls12381::PublicKeyWithPoP>): bls12381::AggrPublicKeysWithPoP
let bytes = spec_aggregate_pubkeys_internal_1(public_keys);
let success = spec_aggregate_pubkeys_internal_2(public_keys);
aborts_if !success;
ensures result.bytes == bytes;

aggregate_signatures

public fun aggregate_signatures(signatures: vector<bls12381::Signature>): option::Option<bls12381::AggrOrMultiSignature>
aborts_if false;
let bytes = spec_aggregate_signatures_internal_1(signatures);
let success = spec_aggregate_signatures_internal_2(signatures);
ensures success ==> (std::option::spec_is_some(result) && std::option::spec_borrow(result).bytes == bytes);
ensures !success ==> std::option::spec_is_none(result);

aggr_or_multi_signature_from_bytes

public fun aggr_or_multi_signature_from_bytes(bytes: vector<u8>): bls12381::AggrOrMultiSignature
aborts_if len(bytes) != SIGNATURE_SIZE;
ensures result.bytes == bytes;

aggr_or_multi_signature_subgroup_check

public fun aggr_or_multi_signature_subgroup_check(signature: &bls12381::AggrOrMultiSignature): bool
aborts_if false;
ensures result == spec_signature_subgroup_check_internal(signature.bytes);

verify_aggregate_signature

public fun verify_aggregate_signature(aggr_sig: &bls12381::AggrOrMultiSignature, public_keys: vector<bls12381::PublicKeyWithPoP>, messages: vector<vector<u8>>): bool
aborts_if false;
ensures result == spec_verify_aggregate_signature_internal(aggr_sig.bytes, public_keys, messages);

verify_multisignature

public fun verify_multisignature(multisig: &bls12381::AggrOrMultiSignature, aggr_public_key: &bls12381::AggrPublicKeysWithPoP, message: vector<u8>): bool
aborts_if false;
ensures result == spec_verify_multisignature_internal(multisig.bytes, aggr_public_key.bytes, message);

verify_normal_signature

public fun verify_normal_signature(signature: &bls12381::Signature, public_key: &bls12381::PublicKey, message: vector<u8>): bool
aborts_if false;
ensures result == spec_verify_normal_signature_internal(signature.bytes, public_key.bytes, message);

verify_signature_share

public fun verify_signature_share(signature_share: &bls12381::Signature, public_key: &bls12381::PublicKeyWithPoP, message: vector<u8>): bool
aborts_if false;
ensures result == spec_verify_signature_share_internal(signature_share.bytes, public_key.bytes, message);

aggregate_pubkeys_internal

fun aggregate_pubkeys_internal(public_keys: vector<bls12381::PublicKeyWithPoP>): (vector<u8>, bool)
pragma opaque;
aborts_if [abstract] false;
ensures result_1 == spec_aggregate_pubkeys_internal_1(public_keys);
ensures result_2 == spec_aggregate_pubkeys_internal_2(public_keys);

aggregate_signatures_internal

fun aggregate_signatures_internal(signatures: vector<bls12381::Signature>): (vector<u8>, bool)
pragma opaque;
aborts_if [abstract] false;
ensures result_1 == spec_aggregate_signatures_internal_1(signatures);
ensures result_2 == spec_aggregate_signatures_internal_2(signatures);

validate_pubkey_internal

fun validate_pubkey_internal(public_key: vector<u8>): bool
pragma opaque;
aborts_if [abstract] false;
ensures result == spec_validate_pubkey_internal(public_key);

signature_subgroup_check_internal

fun signature_subgroup_check_internal(signature: vector<u8>): bool
pragma opaque;
aborts_if [abstract] false;
ensures result == spec_signature_subgroup_check_internal(signature);

verify_aggregate_signature_internal

fun verify_aggregate_signature_internal(aggsig: vector<u8>, public_keys: vector<bls12381::PublicKeyWithPoP>, messages: vector<vector<u8>>): bool
pragma opaque;
aborts_if [abstract] false;
ensures result == spec_verify_aggregate_signature_internal(aggsig, public_keys, messages);

verify_multisignature_internal

fun verify_multisignature_internal(multisignature: vector<u8>, agg_public_key: vector<u8>, message: vector<u8>): bool
pragma opaque;
aborts_if [abstract] false;
ensures result == spec_verify_multisignature_internal(multisignature, agg_public_key, message);

verify_normal_signature_internal

fun verify_normal_signature_internal(signature: vector<u8>, public_key: vector<u8>, message: vector<u8>): bool
pragma opaque;
aborts_if [abstract] false;
ensures result == spec_verify_normal_signature_internal(signature, public_key, message);

verify_proof_of_possession_internal

fun verify_proof_of_possession_internal(public_key: vector<u8>, proof_of_possesion: vector<u8>): bool
pragma opaque;
aborts_if [abstract] false;
ensures result == spec_verify_proof_of_possession_internal(public_key, proof_of_possesion);

verify_signature_share_internal

fun verify_signature_share_internal(signature_share: vector<u8>, public_key: vector<u8>, message: vector<u8>): bool
pragma opaque;
aborts_if [abstract] false;
ensures result == spec_verify_signature_share_internal(signature_share, public_key, message);

Helper functions

fun spec_aggregate_pubkeys_internal_1(public_keys: vector<PublicKeyWithPoP>): vector<u8>;
fun spec_public_key_from_bytes_with_pop(pk_bytes: vector<u8>, pop: ProofOfPossession): Option<PublicKeyWithPoP>;
fun spec_aggregate_pubkeys_internal_2(public_keys: vector<PublicKeyWithPoP>): bool;
fun spec_aggregate_signatures_internal_1(signatures: vector<Signature>): vector<u8>;
fun spec_aggregate_signatures_internal_2(signatures: vector<Signature>): bool;
fun spec_validate_pubkey_internal(public_key: vector<u8>): bool;
fun spec_signature_subgroup_check_internal(signature: vector<u8>): bool;
fun spec_verify_aggregate_signature_internal(
aggsig: vector<u8>,
public_keys: vector<PublicKeyWithPoP>,
messages: vector<vector<u8>>,
): bool;
fun spec_verify_multisignature_internal(
multisignature: vector<u8>,
agg_public_key: vector<u8>,
message: vector<u8>
): bool;
fun spec_verify_normal_signature_internal(
signature: vector<u8>,
public_key: vector<u8>,
message: vector<u8>
): bool;
fun spec_verify_proof_of_possession_internal(
public_key: vector<u8>,
proof_of_possesion: vector<u8>
): bool;
fun spec_verify_signature_share_internal(
signature_share: vector<u8>,
public_key: vector<u8>,
message: vector<u8>
): bool;