add fast_aggregate_verify
This commit is contained in:
parent
4be89d309f
commit
188f3e710c
|
@ -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
|
|
@ -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)
|
|
@ -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()
|
Loading…
Reference in New Issue