Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 94 additions & 27 deletions src/state_transition/utils/bls.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,112 @@ const SecretKey = bls.SecretKey;
/// See https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#bls-signatures
const DST: []const u8 = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";

pub fn sign(secret_key: SecretKey, msg: []const u8) Signature {
return secret_key.sign(msg, DST, null);
pub fn sign(secret_key: SecretKey, message: []const u8) Signature {
return secret_key.sign(message, DST, null);
}
Comment on lines +10 to 12
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The repository style guide requires asserting all function arguments and maintaining an average of at least two assertions per function to ensure safety and document invariants (lines 51-55). Consider adding assertions for the message length and the DST constant to meet this requirement.

pub fn sign(secret_key: SecretKey, message: []const u8) Signature {
    std.debug.assert(message.len > 0);
    std.debug.assert(DST.len > 0);

    return secret_key.sign(message, DST, null);
}
References
  1. Assert all function arguments and return values, pre/postconditions and invariants. The assertion density of the code must average a minimum of two assertions per function. (link)


/// Verify a signature against a message and public key.
///
/// If `pk_validate` is `true`, the public key will be infinity and group checked.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: comment arg name not same with function arg name

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spiral-ladder is in_ pk_validate better or pk_validate?

///
/// If `sig_groupcheck` is `true`, the signature will be group checked.
pub fn verify(msg: []const u8, pk: *const PublicKey, sig: *const Signature, in_pk_validate: ?bool, in_sig_groupcheck: ?bool) bls.BlstError!void {
const sig_groupcheck = in_sig_groupcheck orelse false;
const pk_validate = in_pk_validate orelse false;
try sig.verify(sig_groupcheck, msg, DST, null, pk, pk_validate);
pub fn verify(message: []const u8, public_key: *const PublicKey, signature: *const Signature, pk_validate: ?bool, sig_groupcheck: ?bool) bls.BlstError!void {
const sig_groupcheck_flag = sig_groupcheck orelse false;
const pk_validate_flag = pk_validate orelse false;
try signature.verify(sig_groupcheck_flag, message, DST, null, public_key, pk_validate_flag);
}

pub fn fastAggregateVerify(msg: []const u8, pks: []const PublicKey, sig: *const Signature, in_pk_validate: ?bool, in_sigs_group_check: ?bool) !bool {
/// The `message` must be at least 32 bytes; only the first 32 are passed to
/// fast aggregate verification.
pub fn fastAggregateVerify(message: []const u8, public_keys: []const PublicKey, signature: *const Signature, pk_validate: ?bool, sig_groupcheck: ?bool) !bool {
std.debug.assert(message.len >= 32);

var pairing_buf: [bls.Pairing.sizeOf()]u8 align(bls.Pairing.buf_align) = undefined;

const sigs_groupcheck = in_sigs_group_check orelse false;
const pks_validate = in_pk_validate orelse false;
return sig.fastAggregateVerify(sigs_groupcheck, &pairing_buf, msg[0..32], DST, pks, pks_validate) catch return false;
const sig_groupcheck_flag = sig_groupcheck orelse false;
const pk_validate_flag = pk_validate orelse false;
return signature.fastAggregateVerify(sig_groupcheck_flag, &pairing_buf, message[0..32], DST, public_keys, pk_validate_flag) catch return false;
}

// TODO: unit tests
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps adding more positive tests for other functions if dropped this comment

const input_key_material_a: [32]u8 = [_]u8{
0x93, 0xad, 0x7e, 0x65, 0xde, 0xad, 0x05, 0x2a, 0x08, 0x3a,
0x91, 0x0c, 0x8b, 0x72, 0x85, 0x91, 0x46, 0x4c, 0xca, 0x56,
0x60, 0x5b, 0xb0, 0x56, 0xed, 0xfe, 0x2b, 0x60, 0xa6, 0x3c,
0x48, 0x99,
};
const input_key_material_b: [32]u8 = [_]u8{
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10,
};

test "bls - sanity" {
const ikm: [32]u8 = [_]u8{
0x93, 0xad, 0x7e, 0x65, 0xde, 0xad, 0x05, 0x2a, 0x08, 0x3a,
0x91, 0x0c, 0x8b, 0x72, 0x85, 0x91, 0x46, 0x4c, 0xca, 0x56,
0x60, 0x5b, 0xb0, 0x56, 0xed, 0xfe, 0x2b, 0x60, 0xa6, 0x3c,
0x48, 0x99,
};
const sk = try SecretKey.keyGen(ikm[0..], null);
const msg = [_]u8{1} ** 32;
const sig = sign(sk, &msg);
const pk = sk.toPublicKey();
try verify(&msg, &pk, &sig, null, null);

var pks = [_]PublicKey{pk};
var pks_slice: []const PublicKey = pks[0..1];
const result = try fastAggregateVerify(&msg, pks_slice[0..], &sig, null, null);
try std.testing.expect(result);
const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null);
const message = [_]u8{1} ** 32;
const signature = sign(secret_key, &message);
const public_key = secret_key.toPublicKey();
try verify(&message, &public_key, &signature, null, null);

const public_keys = [_]PublicKey{public_key};
const public_keys_slice: []const PublicKey = public_keys[0..1];
const fast_aggregate_verified = try fastAggregateVerify(&message, public_keys_slice, &signature, null, null);
try std.testing.expectEqual(true, fast_aggregate_verified);
}

test "bls - sign and verify round trip variable-length message" {
const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null);
const message = "ethereum consensus";
const signature = sign(secret_key, message);
const public_key = secret_key.toPublicKey();
try verify(message, &public_key, &signature, null, null);
}

test "bls - verify with pubkey and signature subgroup checks" {
const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null);
const message = [_]u8{0xab} ** 32;
const signature = sign(secret_key, &message);
const public_key = secret_key.toPublicKey();
try verify(&message, &public_key, &signature, true, true);
}

test "bls - fastAggregateVerify uses only first 32 bytes of longer buffer" {
const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null);
var message_64: [64]u8 = undefined;
@memset(message_64[32..], 0xcd);
@memset(message_64[0..32], 0x42);
const signature = sign(secret_key, message_64[0..32]);
const public_key = secret_key.toPublicKey();
var public_keys = [_]PublicKey{public_key};
const fast_aggregate_verified = try fastAggregateVerify(&message_64, public_keys[0..], &signature, null, null);
try std.testing.expectEqual(true, fast_aggregate_verified);
}

test "bls - verify fails on wrong message" {
const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null);
var message = [_]u8{1} ** 32;
const signature = sign(secret_key, &message);
const public_key = secret_key.toPublicKey();
message[0] ^= 1;
try std.testing.expectError(bls.BlstError.VerifyFail, verify(&message, &public_key, &signature, null, null));
}

test "bls - verify fails on wrong public key" {
const secret_key_a = try SecretKey.keyGen(input_key_material_a[0..], null);
const secret_key_b = try SecretKey.keyGen(input_key_material_b[0..], null);
const message = [_]u8{1} ** 32;
const signature = sign(secret_key_a, &message);
const public_key_b = secret_key_b.toPublicKey();
try std.testing.expectError(bls.BlstError.VerifyFail, verify(&message, &public_key_b, &signature, null, null));
}

test "bls - fastAggregateVerify false on wrong message" {
const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null);
var message = [_]u8{1} ** 32;
const signature = sign(secret_key, &message);
const public_key = secret_key.toPublicKey();
message[31] ^= 0xff;
var public_keys = [_]PublicKey{public_key};
const fast_aggregate_verified = try fastAggregateVerify(&message, public_keys[0..], &signature, null, null);
try std.testing.expectEqual(false, fast_aggregate_verified);
}
Loading