Skip to content

Commit

Permalink
Fix BLST aggregate verify with proof-of-possession (#118)
Browse files Browse the repository at this point in the history
In #100 a regression was introduced to BLST `fastAggregateVerify`.

Previous code:
```
  var aggAffine{.noInit.}: PublicKey
  aggAffine.point.blst_p1_to_affine(aggregate)
  return coreVerifyNoGroupCheck(aggAffine, message, signature, DST)
```

New code introducing regression:
```
  var aggAffine{.noInit.}: PublicKey
  aggAffine.finish(aggAffine)
  return coreVerifyNoGroupCheck(aggAffine, message, signature, DST)
```

This change led to a compilation error when using `fastAggregateVerify`
with proof-of-possession.

Secondly, `aggregateVerify` with proof-possession also fails to compile.
This was never working, ever since BLST support was introduced in #68.

Problematic code:
```
  if publicKeys.len != proofs.len or publicKeys != messages.len:
     return false
```

This patch addresses both compilation problems and extends the existing
tests to also cover proof-of-possession functionality. Because the Eth2
vectors do not include proof-of-possession data, the test generator was
temporarily extended to produce such reference data. A copy of that data
is hardcoded in the eth2_vectors tests.
  • Loading branch information
etan-k authored Jul 27, 2021
1 parent 19f6db4 commit 215a665
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 3 deletions.
4 changes: 2 additions & 2 deletions blscurve/bls_sig_min_pubkey.nim
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func aggregateVerify*(
## Compared to the IETF spec API, it is modified to
## enforce proper usage of the proof-of-possessions
# Note: we can't have openarray of openarrays until openarrays are first-class value types
if publicKeys.len != proofs.len or publicKeys != messages.len:
if publicKeys.len != proofs.len or publicKeys.len != messages.len:
return false
if not(publicKeys.len >= 1):
# Spec precondition
Expand Down Expand Up @@ -228,7 +228,7 @@ func fastAggregateVerify*[T: byte|char](
aggPK.aggregate(publicKeys[i])

var aggAffine{.noInit.}: PublicKey
aggAffine.finish(aggAffine)
aggAffine.finish(aggPK)
return coreVerifyNoGroupCheck(aggAffine, message, signature, DST)

func fastAggregateVerify*[T: byte|char](
Expand Down
98 changes: 97 additions & 1 deletion tests/eth2_vectors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,77 @@ import
# Status libraries
stew/byteutils,
# Public API
../blscurve,
../blscurve, ../blscurve/bls_sig_min_pubkey,
# Test helpers
./test_locator

type InOut = enum
Input
Output

# Eth2 vectors do not include proof-of-possession data.
# By adding proof data here, we can leverage the existing tests to also cover proof-of-possession functionality.
# See https://github.com/ethereum/eth2.0-specs/blob/dev/tests/generators/bls/main.py#L45-L51
const knownSeckeys = [
"263dbd792f5b1be47ed85f8938c0f29586af0d3ac7b977f21c278fe1462040e3",
"47b8192d77bf871b62e87859d653922725724a5c031afeabc60bcef5ff665138",
"328388aff0d4a5b7dc9205abd374e7e98f3cd9f3418edb4eafda5fb16473d216",
]
let knownPubkeys = [
"a491d1b0ecd9bb917989f0e74f0dea0422eac4a873e5e2644f368dffb9a6e20fd6e10c1b77654d067c0618f6e5a7f79a",
"b301803f8b5ac4a1133581fc676dfedc60d891dd5fa99028805e5ea5b08d3491af75d0707adab3b70c6a6a580217bf81",
"b53d21a4cfd562c469cc81514d4ce5a6b577d8403d32a394dc265dd190b47fa9f829fdd7963afdf972e5e77854051f6f",
]
let knownProofs = [
"b803eb0ed93ea10224a73b6b9c725796be9f5fefd215ef7a5b97234cc956cf6870db6127b7e4d824ec62276078e787db05584ce1adbf076bc0808ca0f15b73d59060254b25393d95dfc7abe3cda566842aaedf50bbb062aae1bbb6ef3b1f77e1",
"88bb31b27eae23038e14f9d9d1b628a39f5881b5278c3c6f0249f81ba0deb1f68aa5f8847854d6554051aa810fdf1cdb02df4af7a5647b1aa4afb60ec6d446ee17af24a8a50876ffdaf9bf475038ec5f8ebeda1c1c6a3220293e23b13a9a5d26",
"88873ea58f5017a33facc9bf04efaf5e2f34f7bc9ce564d0481dd469326c04ef43552f50e99de8a13315dcd37a4fb9ef036d1a54e5febf5d20b6aa488f3e3c917e6a96ce6461f609ec7e0a1fd8950380922e46c3654fa7542436603f833462da",
]
for i in 0 ..< knownSeckeys.len:
var
sk{.noInit.}: SecretKey
pk{.noInit.}: PublicKey
proof{.noInit.}: ProofOfPossession
doAssert sk.fromHex(knownSeckeys[i])
doAssert pk.fromHex(knownPubkeys[i])
doAssert proof.fromHex(knownProofs[i])

var pk2{.noInit.}: PublicKey
doAssert pk2.publicFromSecret(sk)
doAssert pk2 == pk

let proof2 = sk.popProve(pk)
let proof3 = sk.popProve
doAssert proof3 == proof2
doAssert proof2 == proof
doAssert pk.popVerify(proof)

var wrongPk{.noInit.}: PublicKey
doAssert wrongPk.fromHex(knownPubkeys[(i + 1) mod knownPubkeys.len])
doAssert not wrongPk.popVerify(proof)

template withProof(pk: PublicKey, body: untyped): untyped =
block:
let i = knownPubkeys.find(pk.toHex())
doAssert i > -1, block: "\nProof for pubkey not known: " & pk.toHex()
var proof{.inject, noInit.}, wrongProof{.inject, noInit.}: ProofOfPossession
doAssert proof.fromHex(knownProofs[i])
doAssert wrongProof.fromHex(knownProofs[(i + 1) mod knownProofs.len])
body

template withProofs(pks: openarray[PublicKey], body: untyped): untyped =
block:
var proofs{.inject.}, wrongProofs{.inject.}: seq[ProofOfPossession]
for pk in pks:
let i = knownPubkeys.find(pk.toHex())
doAssert i > -1, block: "\nProof for pubkey not known: " & pk.toHex()
var proof, wrongProof: ProofOfPossession
doAssert proof.fromHex(knownProofs[i])
doAssert wrongProof.fromHex(knownProofs[(i + 1) mod knownProofs.len])
proofs.add proof
wrongProofs.add wrongProof
body

template testGen*(name, testJson, body: untyped): untyped =
## Generates a test proc
## with identifier "test_name"
Expand Down Expand Up @@ -152,6 +215,17 @@ testGen(verify, test):
" computed: " & $libValid & "\n" &
" expected: " & $expected.val

withProof(pubKey.val):
let libValid = pubKey.val.verify(proof, message.val, signature.val)

doAssert libValid == expected.val, block:
"\nVerification with proof-of-possession differs from expected \n" &
" computed: " & $libValid & "\n" &
" expected: " & $expected.val

doAssert not pubKey.val.verify(wrongProof, message.val, signature.val), block:
"\nVerification with wrong proof-of-possession succeeded"

testGen(aggregate, test):
let sigs = seq[Signature].getFrom(test, Input)
let expectedAgg = Signature.getFrom(test, Output)
Expand Down Expand Up @@ -186,6 +260,17 @@ testGen(fast_aggregate_verify, test):
"\nFast Aggregate Verification differs from expected \n" &
" computed: " & $libValid & "\n" &
" expected: " & $expected.val

withProofs(pubKeys.val):
let libValid = pubKeys.val.fastAggregateVerify(proofs, message.val, signature.val)

doAssert libValid == expected.val, block:
"\nFast Aggregate Verification with proof-of-possession differs from expected \n" &
" computed: " & $libValid & "\n" &
" expected: " & $expected.val

doAssert not pubKeys.val.fastAggregateVerify(wrongProofs, message.val, signature.val), block:
"\nFast Aggregate Verification with wrong proof-of-possession succeeded"

testGen(aggregate_verify, test):
let
Expand Down Expand Up @@ -215,6 +300,17 @@ testGen(aggregate_verify, test):
" computed: " & $libSoAValid & "\n" &
" expected: " & $expected.val

withProofs(pubkeys.val):
let libValid = pubkeys.val.aggregateVerify(proofs, msgs.val, signature.val)

doAssert libValid == expected.val, block:
"\nAggregate Verification with proof-of-possession differs from expected \n" &
" computed: " & $libValid & "\n" &
" expected: " & $expected.val

doAssert not pubkeys.val.aggregateVerify(wrongProofs, msgs.val, signature.val), block:
"\nAggregate Verification with wrong proof-of-possession succeeded"

suite "ETH 2.0 " & BLS_ETH2_SPEC & " test vectors - " & $BLS_BACKEND:
test "[" & BLS_ETH2_SPEC & "] sign(SecretKey, message) -> Signature":
test_sign()
Expand Down

0 comments on commit 215a665

Please sign in to comment.