From 1ad8e56ac77c4d48c6854390e287647c082d84bd Mon Sep 17 00:00:00 2001 From: gorusys Date: Mon, 13 Apr 2026 19:37:40 +0200 Subject: [PATCH 1/3] test: add signature_sets unit tests and fix createSingleSignatureSetFromComponents --- src/state_transition/utils/signature_sets.zig | 118 +++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/src/state_transition/utils/signature_sets.zig b/src/state_transition/utils/signature_sets.zig index 53d6cff62..9430febf4 100644 --- a/src/state_transition/utils/signature_sets.zig +++ b/src/state_transition/utils/signature_sets.zig @@ -1,9 +1,12 @@ +const std = @import("std"); const types = @import("consensus_types"); pub const bls = @import("bls"); const PublicKey = bls.PublicKey; const Signature = bls.Signature; +const SecretKey = bls.SecretKey; const Root = types.primitive.Root.Type; const BLSSignature = types.primitive.BLSSignature.Type; +const sign = @import("./bls.zig").sign; const verify = @import("./bls.zig").verify; const fastAggregateVerify = @import("./bls.zig").fastAggregateVerify; @@ -41,7 +44,7 @@ pub fn verifyAggregatedSignatureSet(set: *const AggregatedSignatureSet) !bool { pub fn createSingleSignatureSetFromComponents(pubkey: *const PublicKey, signing_root: Root, signature: BLSSignature) SingleSignatureSet { return .{ - .pubkey = pubkey, + .pubkey = pubkey.*, .signing_root = signing_root, .signature = signature, }; @@ -55,4 +58,115 @@ pub fn createAggregateSignatureSetFromComponents(pubkeys: []const PublicKey, sig }; } -// TODO: unit tests +// Deterministic IKMs for tests only (not production secrets). +const test_sk_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 other_sk_ikm: [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 "verifySingleSignatureSet accepts valid set" { + const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const signing_root = [_]u8{1} ** 32; + const signature = sign(secret_key, &signing_root); + const public_key = secret_key.toPublicKey(); + const set = SingleSignatureSet{ + .pubkey = public_key, + .signing_root = signing_root, + .signature = signature.compress(), + }; + try std.testing.expect(try verifySingleSignatureSet(&set)); +} + +test "verifySingleSignatureSet returns false when signing root does not match signed message" { + const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + var signing_root = [_]u8{1} ** 32; + const signature = sign(secret_key, &signing_root); + const public_key = secret_key.toPublicKey(); + signing_root[0] ^= 1; + const set = SingleSignatureSet{ + .pubkey = public_key, + .signing_root = signing_root, + .signature = signature.compress(), + }; + try std.testing.expect(!try verifySingleSignatureSet(&set)); +} + +test "verifySingleSignatureSet returns false when pubkey is not the signer" { + const secret_key_a = try SecretKey.keyGen(test_sk_ikm[0..], null); + const secret_key_b = try SecretKey.keyGen(other_sk_ikm[0..], null); + const signing_root = [_]u8{1} ** 32; + const signature = sign(secret_key_a, &signing_root); + const set = SingleSignatureSet{ + .pubkey = secret_key_b.toPublicKey(), + .signing_root = signing_root, + .signature = signature.compress(), + }; + try std.testing.expect(!try verifySingleSignatureSet(&set)); +} + +test "verifySingleSignatureSet returns error when signature bytes are not valid compressed G2" { + const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const signing_root = [_]u8{1} ** 32; + const set = SingleSignatureSet{ + .pubkey = secret_key.toPublicKey(), + .signing_root = signing_root, + .signature = [_]u8{0} ** 96, + }; + try std.testing.expectError(bls.BlstError.BadEncoding, verifySingleSignatureSet(&set)); +} + +test "verifyAggregatedSignatureSet accepts valid single-key aggregate" { + const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const signing_root = [_]u8{1} ** 32; + const signature = sign(secret_key, &signing_root); + const public_key = secret_key.toPublicKey(); + var pubkeys = [_]PublicKey{public_key}; + const set = createAggregateSignatureSetFromComponents(pubkeys[0..], signing_root, signature.compress()); + try std.testing.expect(try verifyAggregatedSignatureSet(&set)); +} + +test "verifyAggregatedSignatureSet returns false when signing root does not match" { + const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + var signing_root = [_]u8{1} ** 32; + const signature = sign(secret_key, &signing_root); + const public_key = secret_key.toPublicKey(); + signing_root[31] ^= 0xff; + var pubkeys = [_]PublicKey{public_key}; + const set = createAggregateSignatureSetFromComponents(pubkeys[0..], signing_root, signature.compress()); + try std.testing.expect(!try verifyAggregatedSignatureSet(&set)); +} + +test "verifyAggregatedSignatureSet returns false when pubkeys are not the signers" { + const secret_key_a = try SecretKey.keyGen(test_sk_ikm[0..], null); + const secret_key_b = try SecretKey.keyGen(other_sk_ikm[0..], null); + const signing_root = [_]u8{1} ** 32; + const signature = sign(secret_key_a, &signing_root); + var pubkeys = [_]PublicKey{secret_key_b.toPublicKey()}; + const set = createAggregateSignatureSetFromComponents(pubkeys[0..], signing_root, signature.compress()); + try std.testing.expect(!try verifyAggregatedSignatureSet(&set)); +} + +test "createSingleSignatureSetFromComponents matches equivalent struct literal" { + const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + var signing_root: Root = undefined; + @memset(signing_root[0..], 0x77); + const signature = sign(secret_key, signing_root[0..]); + const public_key = secret_key.toPublicKey(); + const compressed = signature.compress(); + const from_fn = createSingleSignatureSetFromComponents(&public_key, signing_root, compressed); + const literal = SingleSignatureSet{ + .pubkey = public_key, + .signing_root = signing_root, + .signature = compressed, + }; + try std.testing.expect(try verifySingleSignatureSet(&from_fn)); + try std.testing.expect(try verifySingleSignatureSet(&literal)); +} From 80d3cee193756a9d2f717a23a0c05c9416cee002 Mon Sep 17 00:00:00 2001 From: gorusys Date: Mon, 13 Apr 2026 20:04:23 +0200 Subject: [PATCH 2/3] chore: rename the test ikm variables --- src/state_transition/utils/signature_sets.zig | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/state_transition/utils/signature_sets.zig b/src/state_transition/utils/signature_sets.zig index 9430febf4..cc87deb60 100644 --- a/src/state_transition/utils/signature_sets.zig +++ b/src/state_transition/utils/signature_sets.zig @@ -59,13 +59,13 @@ pub fn createAggregateSignatureSetFromComponents(pubkeys: []const PublicKey, sig } // Deterministic IKMs for tests only (not production secrets). -const test_sk_ikm: [32]u8 = [_]u8{ +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 other_sk_ikm: [32]u8 = [_]u8{ +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, @@ -73,7 +73,7 @@ const other_sk_ikm: [32]u8 = [_]u8{ }; test "verifySingleSignatureSet accepts valid set" { - const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null); const signing_root = [_]u8{1} ** 32; const signature = sign(secret_key, &signing_root); const public_key = secret_key.toPublicKey(); @@ -86,7 +86,7 @@ test "verifySingleSignatureSet accepts valid set" { } test "verifySingleSignatureSet returns false when signing root does not match signed message" { - const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null); var signing_root = [_]u8{1} ** 32; const signature = sign(secret_key, &signing_root); const public_key = secret_key.toPublicKey(); @@ -100,8 +100,8 @@ test "verifySingleSignatureSet returns false when signing root does not match si } test "verifySingleSignatureSet returns false when pubkey is not the signer" { - const secret_key_a = try SecretKey.keyGen(test_sk_ikm[0..], null); - const secret_key_b = try SecretKey.keyGen(other_sk_ikm[0..], null); + 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 signing_root = [_]u8{1} ** 32; const signature = sign(secret_key_a, &signing_root); const set = SingleSignatureSet{ @@ -113,7 +113,7 @@ test "verifySingleSignatureSet returns false when pubkey is not the signer" { } test "verifySingleSignatureSet returns error when signature bytes are not valid compressed G2" { - const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null); const signing_root = [_]u8{1} ** 32; const set = SingleSignatureSet{ .pubkey = secret_key.toPublicKey(), @@ -124,7 +124,7 @@ test "verifySingleSignatureSet returns error when signature bytes are not valid } test "verifyAggregatedSignatureSet accepts valid single-key aggregate" { - const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null); const signing_root = [_]u8{1} ** 32; const signature = sign(secret_key, &signing_root); const public_key = secret_key.toPublicKey(); @@ -134,7 +134,7 @@ test "verifyAggregatedSignatureSet accepts valid single-key aggregate" { } test "verifyAggregatedSignatureSet returns false when signing root does not match" { - const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null); var signing_root = [_]u8{1} ** 32; const signature = sign(secret_key, &signing_root); const public_key = secret_key.toPublicKey(); @@ -145,8 +145,8 @@ test "verifyAggregatedSignatureSet returns false when signing root does not matc } test "verifyAggregatedSignatureSet returns false when pubkeys are not the signers" { - const secret_key_a = try SecretKey.keyGen(test_sk_ikm[0..], null); - const secret_key_b = try SecretKey.keyGen(other_sk_ikm[0..], null); + 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 signing_root = [_]u8{1} ** 32; const signature = sign(secret_key_a, &signing_root); var pubkeys = [_]PublicKey{secret_key_b.toPublicKey()}; @@ -155,7 +155,7 @@ test "verifyAggregatedSignatureSet returns false when pubkeys are not the signer } test "createSingleSignatureSetFromComponents matches equivalent struct literal" { - const secret_key = try SecretKey.keyGen(test_sk_ikm[0..], null); + const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null); var signing_root: Root = undefined; @memset(signing_root[0..], 0x77); const signature = sign(secret_key, signing_root[0..]); From 9671f397333ad2c894f77c454b93ca57ed092439 Mon Sep 17 00:00:00 2001 From: gorusys Date: Thu, 16 Apr 2026 15:05:04 +0200 Subject: [PATCH 3/3] test: consolidate signature set verification cases --- src/state_transition/utils/signature_sets.zig | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/state_transition/utils/signature_sets.zig b/src/state_transition/utils/signature_sets.zig index cc87deb60..e7e387bb6 100644 --- a/src/state_transition/utils/signature_sets.zig +++ b/src/state_transition/utils/signature_sets.zig @@ -72,44 +72,49 @@ const input_key_material_b: [32]u8 = [_]u8{ 0x0f, 0x10, }; -test "verifySingleSignatureSet accepts valid set" { - const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null); - const signing_root = [_]u8{1} ** 32; - const signature = sign(secret_key, &signing_root); - const public_key = secret_key.toPublicKey(); - const set = SingleSignatureSet{ - .pubkey = public_key, - .signing_root = signing_root, - .signature = signature.compress(), - }; - try std.testing.expect(try verifySingleSignatureSet(&set)); -} - -test "verifySingleSignatureSet returns false when signing root does not match signed message" { - const secret_key = try SecretKey.keyGen(input_key_material_a[0..], null); - var signing_root = [_]u8{1} ** 32; - const signature = sign(secret_key, &signing_root); - const public_key = secret_key.toPublicKey(); - signing_root[0] ^= 1; - const set = SingleSignatureSet{ - .pubkey = public_key, - .signing_root = signing_root, - .signature = signature.compress(), - }; - try std.testing.expect(!try verifySingleSignatureSet(&set)); -} - -test "verifySingleSignatureSet returns false when pubkey is not the signer" { +test "verifySingleSignatureSet returns expected result for each scenario" { 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 signing_root = [_]u8{1} ** 32; - const signature = sign(secret_key_a, &signing_root); - const set = SingleSignatureSet{ - .pubkey = secret_key_b.toPublicKey(), - .signing_root = signing_root, - .signature = signature.compress(), + const valid_signing_root = [_]u8{1} ** 32; + var mismatched_signing_root = valid_signing_root; + mismatched_signing_root[0] ^= 1; + const signature = sign(secret_key_a, &valid_signing_root); + const signer_public_key = secret_key_a.toPublicKey(); + + const cases = [_]struct { + set: SingleSignatureSet, + expected: bool, + }{ + .{ + .set = .{ + .pubkey = signer_public_key, + .signing_root = valid_signing_root, + .signature = signature.compress(), + }, + .expected = true, + }, + .{ + .set = .{ + .pubkey = signer_public_key, + .signing_root = mismatched_signing_root, + .signature = signature.compress(), + }, + .expected = false, + }, + .{ + .set = .{ + .pubkey = secret_key_b.toPublicKey(), + .signing_root = valid_signing_root, + .signature = signature.compress(), + }, + .expected = false, + }, }; - try std.testing.expect(!try verifySingleSignatureSet(&set)); + + for (cases) |case| { + const actual = try verifySingleSignatureSet(&case.set); + try std.testing.expectEqual(case.expected, actual); + } } test "verifySingleSignatureSet returns error when signature bytes are not valid compressed G2" { @@ -141,7 +146,7 @@ test "verifyAggregatedSignatureSet returns false when signing root does not matc signing_root[31] ^= 0xff; var pubkeys = [_]PublicKey{public_key}; const set = createAggregateSignatureSetFromComponents(pubkeys[0..], signing_root, signature.compress()); - try std.testing.expect(!try verifyAggregatedSignatureSet(&set)); + try std.testing.expectEqual(false, try verifyAggregatedSignatureSet(&set)); } test "verifyAggregatedSignatureSet returns false when pubkeys are not the signers" { @@ -151,7 +156,7 @@ test "verifyAggregatedSignatureSet returns false when pubkeys are not the signer const signature = sign(secret_key_a, &signing_root); var pubkeys = [_]PublicKey{secret_key_b.toPublicKey()}; const set = createAggregateSignatureSetFromComponents(pubkeys[0..], signing_root, signature.compress()); - try std.testing.expect(!try verifyAggregatedSignatureSet(&set)); + try std.testing.expectEqual(false, try verifyAggregatedSignatureSet(&set)); } test "createSingleSignatureSetFromComponents matches equivalent struct literal" {