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_ZeroSecretKey
cttBLS_SecretKeyLargerThanCurveOrder
cttBLS_ZeroLengthAggregation
# Comparisons
# ------------------------------------------------------------------------------------------------
@ -404,3 +405,39 @@ func verify*[T: byte|char](public_key: PublicKey, message: openarray[T], signatu
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:
return cttBLS_Success
return cttBLS_VerificationFailure

View File

@ -8,6 +8,7 @@
import
../math/[ec_shortweierstrass, pairings],
../math/elliptic/ec_shortweierstrass_batch_ops,
../math/constants/zoo_generators,
../hash_to_curve/hash_to_curve,
../hashes
@ -94,7 +95,7 @@ func coreSign*[B1, B2, B3: byte|char, Sig, SecKey](
signature.affine(sig)
func coreVerify*[B1, B2, B3: byte|char, Pubkey, Sig](
pubKey: Pubkey,
pubkey: Pubkey,
message: openarray[B1],
signature: Sig,
H: type CryptoHash,
@ -119,8 +120,45 @@ func coreVerify*[B1, B2, B3: byte|char, Pubkey, Sig](
# e(PK, H(msg))*e(sig, -G) == 1
when Sig.G == G2:
pairing(gt, [pubKey, negG], [Q, signature])
pairing(gt, [pubkey, negG], [Q, signature])
else:
pairing(gt, [Q, signature], [pubKey, negG])
pairing(gt, [Q, signature], [pubkey, negG])
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
type
# https://github.com/ethereum/bls12-381-tests/blob/master/formats/
PubkeyField = object
pubkey: array[48, byte]
SignatureField =object
signature: array[96, byte]
DeserG1_test = object
input: PubkeyField
output: bool
SignatureField =object
signature: array[96, byte]
DeserG2_test = object
input: SignatureField
output: bool
@ -27,7 +30,6 @@ type
InputSign = object
privkey: array[32, byte]
message: array[32, byte]
Sign_test = object
input: InputSign
output: array[96, byte]
@ -36,11 +38,18 @@ type
pubkey: array[48, byte]
message: array[32, byte]
signature: array[96, byte]
Verify_test = object
input: InputVerify
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]) =
var str: string
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 skipped = 0
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 testData = testFile.fromJson(TestType)
@ -169,7 +178,7 @@ testGen(verify, testVector, Verify_test):
var
pubkey{.noInit.}: PublicKey
signature{.noInit.}: Signature
status = cttBLS_Success
status = cttBLS_VerificationFailure
block testChecks:
status = pubkey.deserialize_public_key_compressed(testVector.input.pubkey)
@ -181,8 +190,8 @@ testGen(verify, testVector, Verify_test):
# For point at infinity, we want to make sure that "verify" itself handles them.
break testChecks
status = pubkey.verify(testVector.input.message, signature)
let success = status == cttBLS_Success
doAssert success == testVector.output, block:
"\Verification differs from expected \n" &
@ -202,6 +211,32 @@ testGen(verify, testVector, Verify_test):
doAssert s == cttBLS_Success
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":
test "Deserialization_G1(PublicKey) -> bool":
test_deserialization_G1()
@ -211,3 +246,5 @@ suite "BLS signature on BLS12381G3 - ETH 2.0 test vectors":
test_sign()
test "verify(PublicKey, message, Signature) -> bool":
test_verify()
test "fast_aggregate_verify(seq[PublicKey], message, Signature) -> bool":
test_fast_aggregate_verify()