Skip to content
Merged
Show file tree
Hide file tree
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
27 changes: 15 additions & 12 deletions bindings/napi/blst.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const bls = @import("bls");
const NativePublicKey = bls.PublicKey;
const NativeSignature = bls.Signature;
const NativeSecretKey = bls.SecretKey;
const SigningRoot = bls.SigningRoot;
const Pairing = bls.Pairing;
const AggregatePublicKey = bls.AggregatePublicKey;
const AggregateSignature = bls.AggregateSignature;
Expand Down Expand Up @@ -329,8 +330,9 @@ pub const SecretKey = struct {

/// Signs a message with this `SecretKey`, returns a `Signature`.
pub fn sign(self: *const SecretKey, msg: js.Uint8Array) !Signature {
const slice = try msg.toSlice();
return .{ .raw = self.raw.sign(slice, DST, null) };
const msg_bytes = try msg.toSlice();
if (msg_bytes.len != @sizeOf(SigningRoot)) return error.InvalidMessageLength;
return .{ .raw = self.raw.sign(msg_bytes[0..@sizeOf(SigningRoot)], DST, null) };
}

/// Derives the PublicKey from this SecretKey.
Expand Down Expand Up @@ -360,11 +362,12 @@ pub const SecretKey = struct {
/// 4) pk_validate: ?bool
/// 5) sig_groupcheck: ?bool
pub fn verify(msg: js.Uint8Array, pk: PublicKey, sig: Signature, pk_validate: ?js.Boolean, sig_groupcheck: ?js.Boolean) !js.Boolean {
const msg_slice = try msg.toSlice();
const msg_bytes = try msg.toSlice();
if (msg_bytes.len != @sizeOf(SigningRoot)) return error.InvalidMessageLength;

sig.raw.verify(
try boolOrDefault(sig_groupcheck, false),
msg_slice,
msg_bytes[0..@sizeOf(SigningRoot)],
DST,
null,
&pk.raw,
Expand All @@ -387,7 +390,7 @@ pub fn aggregateVerify(msgs: js.Array, pks: js.Array, sig: Signature, pks_valida
return error.InvalidAggregateVerifyInput;
}

const msg_bufs = try allocator.alloc([32]u8, msgs_len);
const msg_bufs = try allocator.alloc(SigningRoot, msgs_len);
defer allocator.free(msg_bufs);

const pk_ptrs = try allocator.alloc(*NativePublicKey, pks_len);
Expand All @@ -396,8 +399,8 @@ pub fn aggregateVerify(msgs: js.Array, pks: js.Array, sig: Signature, pks_valida
for (0..msgs_len) |i| {
const msg_value = try msgs.get(@intCast(i));
const msg_bytes = try uint8SliceFromValue(msg_value);
if (msg_bytes.len != 32) return error.InvalidMessageLength;
@memcpy(&msg_bufs[i], msg_bytes[0..32]);
if (msg_bytes.len != @sizeOf(SigningRoot)) return error.InvalidMessageLength;
msg_bufs[i] = msg_bytes[0..@sizeOf(SigningRoot)].*;

const wrapped_pk = try unwrapClass(PublicKey, try pks.get(@intCast(i)));
pk_ptrs[i] = &wrapped_pk.raw;
Expand Down Expand Up @@ -428,8 +431,8 @@ pub fn aggregateVerify(msgs: js.Array, pks: js.Array, sig: Signature, pks_valida
/// 3) sig: Signature
/// 4) sigs_groupcheck: ?bool
pub fn fastAggregateVerify(msg: js.Uint8Array, pks: js.Array, sig: Signature, sigs_groupcheck: ?js.Boolean) !js.Boolean {
const msg_slice = try msg.toSlice();
if (msg_slice.len != 32) return error.InvalidMessageLength;
const msg_bytes = try msg.toSlice();
if (msg_bytes.len != @sizeOf(SigningRoot)) return error.InvalidMessageLength;

const pks_len = try pks.length();
if (pks_len == 0) return js.Boolean.from(false);
Expand All @@ -447,7 +450,7 @@ pub fn fastAggregateVerify(msg: js.Uint8Array, pks: js.Array, sig: Signature, si
const result = sig.raw.fastAggregateVerify(
try boolOrDefault(sigs_groupcheck, false),
&pairing_buf,
msg_slice[0..32],
msg_bytes[0..@sizeOf(SigningRoot)],
DST,
native_pks,
false,
Expand Down Expand Up @@ -483,14 +486,14 @@ pub fn verifyMultipleAggregateSignatures(sets: js.Array, pks_validate: ?js.Boole

const msg_napi = try set.getNamedProperty("msg");
const msg_bytes = try uint8SliceFromValue(.{ .val = msg_napi });
if (msg_bytes.len != 32) return error.InvalidMessageLength;
if (msg_bytes.len != @sizeOf(SigningRoot)) return error.InvalidMessageLength;
const pk_napi = try set.getNamedProperty("pk");
const wrapped_pk = try unwrapClass(PublicKey, .{ .val = pk_napi });

const sig_napi = try set.getNamedProperty("sig");
const wrapped_sig = try unwrapClass(Signature, .{ .val = sig_napi });
items[i] = .{
.message = msg_bytes,
.message = msg_bytes[0..@sizeOf(SigningRoot)].*,
.public_key = &wrapped_pk.raw,
.signature = &wrapped_sig.raw,
.randomness = undefined,
Expand Down
10 changes: 6 additions & 4 deletions bindings/src/blst.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class SecretKey {
static fromBytes(bytes: Uint8Array): SecretKey;
static fromHex(hex: string): SecretKey;
static fromKeygen(ikm: Uint8Array, keyInfo?: Uint8Array): SecretKey;
/** Sign an exact 32-byte Ethereum consensus signing root. */
sign(msg: Uint8Array): Signature;
toPublicKey(): PublicKey;
toBytes(): Uint8Array;
Expand Down Expand Up @@ -85,6 +86,7 @@ export class Signature {
}

export interface SignatureSet {
/** Exact 32-byte Ethereum consensus signing root. */
msg: Uint8Array;
pk: PublicKey;
sig: Signature;
Expand All @@ -101,7 +103,7 @@ export interface PkAndSig {
}

/**
* Verify a signature against a message and public key.
* Verify a signature against an exact 32-byte Ethereum consensus signing root and public key.
*
* If `pkValidate` is `true`, the public key will be infinity and group checked.
*
Expand All @@ -116,7 +118,7 @@ export function verify(
): boolean;

/**
* Verify an aggregated signature against multiple messages and multiple public keys.
* Verify an aggregated signature against exact 32-byte Ethereum consensus signing roots and multiple public keys.
*
* If `pksValidate` is `true`, the public keys will be infinity and group checked.
*
Expand All @@ -131,7 +133,7 @@ export function aggregateVerify(
): boolean;

/**
* Verify an aggregated signature against a single message and multiple public keys.
* Verify an aggregated signature against a single exact 32-byte Ethereum consensus signing root and multiple public keys.
*
* Proof-of-possession is required for public keys.
*
Expand All @@ -145,7 +147,7 @@ export function fastAggregateVerify(
): boolean;

/**
* Verify multiple aggregated signatures against multiple messages and multiple public keys.
* Verify multiple aggregated signatures against exact 32-byte Ethereum consensus signing roots and multiple public keys.
*
* If `pksValidate` is `true`, the public keys will be infinity and group checked.
*
Expand Down
48 changes: 41 additions & 7 deletions bindings/test/blst.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,18 @@ describe("blst", () => {
});
});
describe("sign", () => {
it("should create a valid Signature", () => {
const sig = SecretKey.fromKeygen(KEY_MATERIAL, undefined).sign(Buffer.from("some fancy message"));
it("should create a valid Signature for a 32-byte signing root", () => {
const sig = SecretKey.fromKeygen(KEY_MATERIAL, undefined).sign(new Uint8Array(32));
expect(sig).to.be.instanceOf(Signature);
expect(sig.validate(false)).to.be.undefined;
});

for (const length of [31, 33]) {
it(`should throw InvalidMessageLength for a ${length}-byte signing root`, () => {
const sk = SecretKey.fromKeygen(KEY_MATERIAL, undefined);
expect(() => sk.sign(new Uint8Array(length))).toThrow("InvalidMessageLength");
});
}
});
});
});
Expand All @@ -258,6 +265,14 @@ describe("blst", () => {
const result = verify(wrongMessage, pk, sig, false, false);
expect(result).toBe(false);
});

for (const length of [31, 33]) {
it(`should throw InvalidMessageLength for a ${length}-byte signing root`, () => {
const pk = PublicKey.fromHex(TEST_VECTORS.publicKey.compressed);
const sig = Signature.fromHex(TEST_VECTORS.signature.compressed);
expect(() => verify(new Uint8Array(length), pk, sig, false, false)).toThrow("InvalidMessageLength");
});
}
});

describe("aggregateVerify", () => {
Expand All @@ -278,6 +293,14 @@ describe("blst", () => {
const sig = Signature.fromHex(TEST_VECTORS.signature.compressed);
expect(aggregateVerify([TEST_VECTORS.message], [pk], sig)).to.be.true;
});

for (const length of [31, 33]) {
it(`should throw InvalidMessageLength for a ${length}-byte signing root`, () => {
const pk = PublicKey.fromHex(TEST_VECTORS.publicKey.compressed);
const sig = Signature.fromHex(TEST_VECTORS.signature.compressed);
expect(() => aggregateVerify([new Uint8Array(length)], [pk], sig)).toThrow("InvalidMessageLength");
});
}
});

describe("fastAggregateVerify", () => {
Expand All @@ -302,11 +325,13 @@ describe("blst", () => {
expect(result).toBe(false);
});

it("should throw on wrong message length", () => {
const pk = PublicKey.fromHex(TEST_VECTORS.publicKey.compressed);
const sig = Signature.fromHex(TEST_VECTORS.signature.compressed);
expect(() => fastAggregateVerify(new Uint8Array(31), [pk], sig, false)).toThrow();
});
for (const length of [31, 33]) {
it(`should throw InvalidMessageLength for a ${length}-byte signing root`, () => {
const pk = PublicKey.fromHex(TEST_VECTORS.publicKey.compressed);
const sig = Signature.fromHex(TEST_VECTORS.signature.compressed);
expect(() => fastAggregateVerify(new Uint8Array(length), [pk], sig, false)).toThrow("InvalidMessageLength");
});
}
});

describe("verifyMultipleAggregateSignatures", () => {
Expand All @@ -329,6 +354,15 @@ describe("blst", () => {
verifyMultipleAggregateSignatures([{msg, pk, sig: pk as unknown as Signature}], false, false)
).toThrow("TypeMismatch");
});

for (const length of [31, 33]) {
it(`should throw InvalidMessageLength for a ${length}-byte signing root`, () => {
const [set] = getTestSets(1);
expect(() => verifyMultipleAggregateSignatures([{...set, msg: new Uint8Array(length)}], false, false)).toThrow(
"InvalidMessageLength"
);
});
}
});

describe("aggregatePublicKeys", () => {
Expand Down
13 changes: 7 additions & 6 deletions src/bls/Pairing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub fn aggregate(
pk_validate: bool,
sig: ?*const Signature,
sig_groupcheck: bool,
msg: []const u8,
msg: *const SigningRoot,
aug: ?[]const u8,
) BlstError!void {
try errorFromInt(
Expand All @@ -64,8 +64,8 @@ pub fn aggregate(
pk_validate,
if (sig) |s| &s.point else null,
sig_groupcheck,
msg.ptr,
msg.len,
msg,
@sizeOf(SigningRoot),
if (aug) |a| a.ptr else null,
if (aug) |a| a.len else 0,
),
Expand All @@ -83,7 +83,7 @@ pub fn mulAndAggregate(
sig_groupcheck: bool,
scalar: []const u8,
nbits: usize,
msg: []const u8,
msg: *const SigningRoot,
) BlstError!void {
try errorFromInt(
c.blst_pairing_chk_n_mul_n_aggr_pk_in_g1(
Expand All @@ -94,8 +94,8 @@ pub fn mulAndAggregate(
sig_groupcheck,
scalar.ptr,
nbits,
msg.ptr,
32,
msg,
@sizeOf(SigningRoot),
null,
0,
),
Expand Down Expand Up @@ -153,3 +153,4 @@ const errorFromInt = @import("error.zig").errorFromInt;
const blst = @import("root.zig");
const PublicKey = blst.PublicKey;
const Signature = blst.Signature;
const SigningRoot = blst.SigningRoot;
7 changes: 4 additions & 3 deletions src/bls/SecretKey.zig
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,13 @@ pub fn toPublicKey(self: *const Self) PublicKey {
}

/// Sign a message with this `SecretKey`. Returns the `Signature` for the message.
pub fn sign(self: *const Self, msg: []const u8, dst: []const u8, aug: ?[]const u8) Signature {
pub fn sign(self: *const Self, msg: *const SigningRoot, dst: []const u8, aug: ?[]const u8) Signature {
var sig = Signature{};
var q = @import("AggregateSignature.zig"){};
c.blst_hash_to_g2(
@ptrCast(&q.point),
msg.ptr,
msg.len,
msg,
@sizeOf(SigningRoot),
dst.ptr,
dst.len,
if (aug) |a| a.ptr else null,
Expand Down Expand Up @@ -163,6 +163,7 @@ const check = @import("error.zig").check;
const blst = @import("root.zig");
const PublicKey = blst.PublicKey;
const Signature = blst.Signature;
const SigningRoot = blst.SigningRoot;

const c = @import("root.zig").c;

Expand Down
26 changes: 14 additions & 12 deletions src/bls/Signature.zig
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub fn validate(self: *const Self, sig_infcheck: bool) BlstError!void {
pub fn verify(
self: *const Self,
sig_groupcheck: bool,
msg: []const u8,
msg: *const SigningRoot,
dst: []const u8,
aug: ?[]const u8,
pk: *const PublicKey,
Expand All @@ -36,16 +36,16 @@ pub fn verify(
if (sig_groupcheck) try self.validate(false);
if (pk_validate) try pk.validate();

if (msg.len == 0 or dst.len == 0) {
if (dst.len == 0) {
return BlstError.BadEncoding;
}

const chk = errorFromInt(c.blst_core_verify_pk_in_g1(
@ptrCast(&pk.point),
&self.point,
true,
msg.ptr,
msg.len,
msg,
@sizeOf(SigningRoot),
dst.ptr,
dst.len,
if (aug) |a| a.ptr else null,
Expand All @@ -62,7 +62,7 @@ pub fn aggregateVerify(
self: *const Self,
sig_groupcheck: bool,
buffer: *align(Pairing.buf_align) [Pairing.sizeOf()]u8,
msgs: []const [32]u8,
msgs: []const SigningRoot,
dst: []const u8,
pks: []const PublicKey,
pks_validate: bool,
Expand Down Expand Up @@ -106,7 +106,7 @@ pub fn fastAggregateVerify(
self: *const Self,
sig_groupcheck: bool,
buffer: *align(Pairing.buf_align) [Pairing.sizeOf()]u8,
msg: *const [32]u8,
msg: *const SigningRoot,
dst: []const u8,
pks: []const PublicKey,
pks_validate: bool,
Expand All @@ -131,15 +131,15 @@ pub fn fastAggregateVerifyPreAggregated(
self: *const Self,
sig_groupcheck: bool,
buffer: *align(Pairing.buf_align) [Pairing.sizeOf()]u8,
msg: *const [32]u8,
msg: *const SigningRoot,
dst: []const u8,
pk: *const PublicKey,
) BlstError!bool {
const pks: [*]const PublicKey = @ptrCast(pk);
return try self.aggregateVerify(
sig_groupcheck,
buffer,
@ptrCast(msg),
@as([*]const SigningRoot, @ptrCast(msg))[0..1],
dst,
pks[0..1],
false,
Expand Down Expand Up @@ -216,6 +216,7 @@ const c = @import("root.zig").c;
const BlstError = @import("error.zig").BlstError;
const errorFromInt = @import("error.zig").errorFromInt;
const PublicKey = @import("root.zig").PublicKey;
const SigningRoot = @import("root.zig").SigningRoot;
const AggregatePublicKey = @import("AggregatePublicKey.zig");
const AggregateSignature = @import("AggregateSignature.zig");
const Pairing = @import("Pairing.zig");
Expand All @@ -231,7 +232,8 @@ const ikm: [32]u8 = [_]u8{

test uncompress {
const sk = try SecretKey.keyGen(&ikm, null);
const sig = sk.sign("hello foo", DST, null);
const signing_root = [_]u8{0x42} ** 32;
const sig = sk.sign(&signing_root, DST, null);
const sig_comp = sig.compress();

// Valid compressed bytes round-trip.
Expand All @@ -257,13 +259,13 @@ test "test_sign_n_verify" {
const pk = sk.toPublicKey();

const dst = DST;
const msg = "hello foo";
const sig = sk.sign(msg, dst, null);
const signing_root = [_]u8{0x42} ** 32;
const sig = sk.sign(&signing_root, dst, null);

// aug is null
try sig.verify(
true,
msg,
&signing_root,
dst,
null,
&pk,
Expand Down
Loading
Loading