add fast_aggregate_verify

This commit is contained in:
Mamy Ratsimbazafy 2023-01-23 01:54:40 +01:00
parent 4be89d309f
commit 188f3e710c
No known key found for this signature in database
GPG Key ID: 6227262F49BE273A
3 changed files with 140 additions and 28 deletions

View File

@ -85,6 +85,7 @@ type
cttBLS_PointNotInSubgroup cttBLS_PointNotInSubgroup
cttBLS_ZeroSecretKey cttBLS_ZeroSecretKey
cttBLS_SecretKeyLargerThanCurveOrder cttBLS_SecretKeyLargerThanCurveOrder
cttBLS_ZeroLengthAggregation
# Comparisons # Comparisons
# ------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------
@ -401,6 +402,42 @@ func verify*[T: byte|char](public_key: PublicKey, message: openarray[T], signatu
return cttBLS_PointAtInfinity return cttBLS_PointAtInfinity
let verified = coreVerify(public_key.raw, message, signature.raw, sha256, 128, augmentation = "", DST) let verified = coreVerify(public_key.raw, message, signature.raw, sha256, 128, augmentation = "", DST)
if verified:
return cttBLS_Success
return cttBLS_VerificationFailure
func fast_aggregate_verify*[T: byte|char](public_keys: openArray[PublicKey], message: openarray[T], signature: Signature): CttBLSStatus =
## Check that a signature is valid for a message
## under the aggregate of provided public keys.
## returns `true` if the signature is valid, `false` otherwise.
##
## For message domain separation purpose, the tag is `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_`
##
## Input:
## - Public keys initialized by one of the key derivation or deserialization procedure.
## Or validated via validate_pubkey
## - A message
## - A signature initialized by one of the key derivation or deserialization procedure.
## Or validated via validate_pubkey
##
## In particular, the public keys and signature are assumed to be on curve subgroup checked.
if public_keys.len == 0:
# IETF spec precondition
return cttBLS_ZeroLengthAggregation
# Deal with cases were pubkey or signature were mistakenly zero-init, due to a generic aggregation tentative for example
if signature.raw.isInf().bool:
return cttBLS_PointAtInfinity
for i in 0 ..< public_keys.len:
if public_keys[i].raw.isInf().bool:
return cttBLS_PointAtInfinity
let verified = fastAggregateVerify(
toOpenArray(cast[ptr UncheckedArray[typeof public_keys[0].raw]](public_keys[0].raw.unsafeAddr), public_keys.low, public_keys.high),
message, signature.raw,
sha256, 128, augmentation = "", DST)
if verified: if verified:
return cttBLS_Success return cttBLS_Success
return cttBLS_VerificationFailure return cttBLS_VerificationFailure

View File

@ -8,6 +8,7 @@
import import
../math/[ec_shortweierstrass, pairings], ../math/[ec_shortweierstrass, pairings],
../math/elliptic/ec_shortweierstrass_batch_ops,
../math/constants/zoo_generators, ../math/constants/zoo_generators,
../hash_to_curve/hash_to_curve, ../hash_to_curve/hash_to_curve,
../hashes ../hashes
@ -94,7 +95,7 @@ func coreSign*[B1, B2, B3: byte|char, Sig, SecKey](
signature.affine(sig) signature.affine(sig)
func coreVerify*[B1, B2, B3: byte|char, Pubkey, Sig]( func coreVerify*[B1, B2, B3: byte|char, Pubkey, Sig](
pubKey: Pubkey, pubkey: Pubkey,
message: openarray[B1], message: openarray[B1],
signature: Sig, signature: Sig,
H: type CryptoHash, H: type CryptoHash,
@ -119,8 +120,45 @@ func coreVerify*[B1, B2, B3: byte|char, Pubkey, Sig](
# e(PK, H(msg))*e(sig, -G) == 1 # e(PK, H(msg))*e(sig, -G) == 1
when Sig.G == G2: when Sig.G == G2:
pairing(gt, [pubKey, negG], [Q, signature]) pairing(gt, [pubkey, negG], [Q, signature])
else: else:
pairing(gt, [Q, signature], [pubKey, negG]) pairing(gt, [Q, signature], [pubkey, negG])
return gt.isOne().bool() return gt.isOne().bool()
# ############################################################
#
# Aggregate verification
#
# ############################################################
#
# Terminology:
#
# - fastAggregateVerify:
# Verify the aggregate of multiple signatures by multiple pubkeys
# on the same message.
#
# - aggregateVerify:
# Verify the aggregate of multiple signatures by multiple (pubkey, message) pairs
#
# - batchVerify:
# Verify that all (pubkey, message, signature) triplets are valid
func fastAggregateVerify*[B1, B2, B3: byte|char, Pubkey, Sig](
pubkeys: openArray[Pubkey],
message: openarray[B1],
signature: Sig,
H: type CryptoHash,
k: static int,
augmentation: openarray[B2],
domainSepTag: openarray[B3]): bool =
## Verify the aggregate of multiple signatures by multiple pubkeys
## on the same message.
var accum {.noinit.}: ECP_ShortW_Jac[Pubkey.F, Pubkey.G]
accum.sum_batch_vartime(pubkeys)
var aggPubkey {.noinit.}: Pubkey
aggPubkey.affine(accum)
aggPubkey.coreVerify(message, signature, H, k, augmentation, domainSepTag)

View File

@ -13,13 +13,16 @@ import
../constantine/math/io/io_bigints ../constantine/math/io/io_bigints
type type
# https://github.com/ethereum/bls12-381-tests/blob/master/formats/
PubkeyField = object PubkeyField = object
pubkey: array[48, byte] pubkey: array[48, byte]
SignatureField =object
signature: array[96, byte]
DeserG1_test = object DeserG1_test = object
input: PubkeyField input: PubkeyField
output: bool output: bool
SignatureField =object
signature: array[96, byte]
DeserG2_test = object DeserG2_test = object
input: SignatureField input: SignatureField
output: bool output: bool
@ -27,7 +30,6 @@ type
InputSign = object InputSign = object
privkey: array[32, byte] privkey: array[32, byte]
message: array[32, byte] message: array[32, byte]
Sign_test = object Sign_test = object
input: InputSign input: InputSign
output: array[96, byte] output: array[96, byte]
@ -36,11 +38,18 @@ type
pubkey: array[48, byte] pubkey: array[48, byte]
message: array[32, byte] message: array[32, byte]
signature: array[96, byte] signature: array[96, byte]
Verify_test = object Verify_test = object
input: InputVerify input: InputVerify
output: bool output: bool
InputFastAggregateVerify = object
pubkeys: seq[array[48, byte]]
message: array[32, byte]
signature: array[96, byte]
FastAggregateVerify_test = object
input: InputFastAggregateVerify
output: bool
proc parseHook*[N: static int](src: string, pos: var int, value: var array[N, byte]) = proc parseHook*[N: static int](src: string, pos: var int, value: var array[N, byte]) =
var str: string var str: string
parseHook(src, pos, str) parseHook(src, pos, str)
@ -73,7 +82,7 @@ template testGen*(name, testData, TestType, body: untyped): untyped =
var count = 0 # Need to fail if walkDir doesn't return anything var count = 0 # Need to fail if walkDir doesn't return anything
var skipped = 0 var skipped = 0
for dir, file in walkTests(astToStr(name), skipped): for dir, file in walkTests(astToStr(name), skipped):
stdout.write(" " & astToStr(name) & " test: " & alignLeft(file, 60)) stdout.write(" " & astToStr(name) & " test: " & alignLeft(file, 70))
let testFile = readFile(dir/file) let testFile = readFile(dir/file)
let testData = testFile.fromJson(TestType) let testData = testFile.fromJson(TestType)
@ -169,7 +178,7 @@ testGen(verify, testVector, Verify_test):
var var
pubkey{.noInit.}: PublicKey pubkey{.noInit.}: PublicKey
signature{.noInit.}: Signature signature{.noInit.}: Signature
status = cttBLS_Success status = cttBLS_VerificationFailure
block testChecks: block testChecks:
status = pubkey.deserialize_public_key_compressed(testVector.input.pubkey) status = pubkey.deserialize_public_key_compressed(testVector.input.pubkey)
@ -181,26 +190,52 @@ testGen(verify, testVector, Verify_test):
# For point at infinity, we want to make sure that "verify" itself handles them. # For point at infinity, we want to make sure that "verify" itself handles them.
break testChecks break testChecks
status = pubkey.verify(testVector.input.message, signature) status = pubkey.verify(testVector.input.message, signature)
let success = status == cttBLS_Success
doAssert success == testVector.output, block: let success = status == cttBLS_Success
"\Verification differs from expected \n" & doAssert success == testVector.output, block:
" valid sig? " & $success & " (" & $status & ")\n" & "\Verification differs from expected \n" &
" expected: " & $testVector.output " valid sig? " & $success & " (" & $status & ")\n" &
" expected: " & $testVector.output
if success: # Extra codec testing if success: # Extra codec testing
block: block:
var output{.noInit.}: array[48, byte] var output{.noInit.}: array[48, byte]
let s = output.serialize_public_key_compressed(pubkey) let s = output.serialize_public_key_compressed(pubkey)
doAssert s == cttBLS_Success doAssert s == cttBLS_Success
doAssert output == testVector.input.pubkey doAssert output == testVector.input.pubkey
block: block:
var output{.noInit.}: array[96, byte] var output{.noInit.}: array[96, byte]
let s = output.serialize_signature_compressed(signature) let s = output.serialize_signature_compressed(signature)
doAssert s == cttBLS_Success doAssert s == cttBLS_Success
doAssert output == testVector.input.signature doAssert output == testVector.input.signature
testGen(fast_aggregate_verify, testVector, FastAggregateVerify_test):
var
pubkeys = newSeq[PublicKey](testVector.input.pubkeys.len)
signature{.noInit.}: Signature
status = cttBLS_VerificationFailure
block testChecks:
for i in 0 ..< testVector.input.pubkeys.len:
status = pubkeys[i].deserialize_public_key_compressed(testVector.input.pubkeys[i])
if status notin {cttBLS_Success, cttBLS_PointAtInfinity}:
# For point at infinity, we want to make sure that "verify" itself handles them.
break testChecks
status = signature.deserialize_signature_compressed(testVector.input.signature)
if status notin {cttBLS_Success, cttBLS_PointAtInfinity}:
# For point at infinity, we want to make sure that "verify" itself handles them.
break testChecks
status = pubkeys.fast_aggregate_verify(testVector.input.message, signature)
let success = status == cttBLS_Success
doAssert success == testVector.output, block:
"\Verification differs from expected \n" &
" valid sig? " & $success & " (" & $status & ")\n" &
" expected: " & $testVector.output
suite "BLS signature on BLS12381G3 - ETH 2.0 test vectors": suite "BLS signature on BLS12381G3 - ETH 2.0 test vectors":
test "Deserialization_G1(PublicKey) -> bool": test "Deserialization_G1(PublicKey) -> bool":
@ -210,4 +245,6 @@ suite "BLS signature on BLS12381G3 - ETH 2.0 test vectors":
test "sign(SecretKey, message) -> Signature": test "sign(SecretKey, message) -> Signature":
test_sign() test_sign()
test "verify(PublicKey, message, Signature) -> bool": test "verify(PublicKey, message, Signature) -> bool":
test_verify() test_verify()
test "fast_aggregate_verify(seq[PublicKey], message, Signature) -> bool":
test_fast_aggregate_verify()