constantine/tests/t_blssig_pop_on_bls12381_g2...

326 lines
12 KiB
Nim

# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
std/[os, unittest, strutils],
pkg/jsony,
../constantine/blssig_pop_on_bls12381_g2,
../constantine/platforms/codecs,
../constantine/hashes
type
# https://github.com/ethereum/bls12-381-tests/blob/master/formats/
PubkeyField = object
pubkey: array[48, byte]
DeserG1_test = object
input: PubkeyField
output: bool
SignatureField =object
signature: array[96, byte]
DeserG2_test = object
input: SignatureField
output: bool
InputSign = object
privkey: array[32, byte]
message: array[32, byte]
Sign_test = object
input: InputSign
output: array[96, byte]
InputVerify = object
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
InputAggregateVerify = object
pubkeys: seq[array[48, byte]]
messages: seq[array[32, byte]]
signature: array[96, byte]
AggregateVerify_test = object
input: InputAggregateVerify
output: bool
InputBatchVerify = object
pubkeys: seq[array[48, byte]]
messages: seq[array[32, byte]]
signatures: seq[array[96, byte]]
BatchVerify_test = object
input: InputBatchVerify
output: bool
proc parseHook*[N: static int](src: string, pos: var int, value: var array[N, byte]) =
var str: string
parseHook(src, pos, str)
value.paddedFromHex(str, bigEndian)
const SkippedTests = [
# By construction, input MUST be 48 bytes, which is enforced at the type-system level.
"deserialization_fails_too_many_bytes.json"
]
const TestDir = currentSourcePath.rsplit(DirSep, 1)[0] / "protocol_blssig_pop_on_bls12381_g2_test_vectors_v0.1.1"
iterator walkTests*(category: string, skipped: var int): (string, string) =
let testDir = TestDir/category
for file in walkDirRec(testDir, relative = true):
if file in SkippedTests:
echo "[WARNING] Skipping - ", file
inc skipped
continue
yield (testDir, file)
template testGen*(name, testData, TestType, body: untyped): untyped =
## Generates a test proc
## with identifier "test_name"
## The test vector data is available as JsonNode under the
## the variable passed as `testData`
proc `test _ name`() =
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, 70))
let testFile = readFile(dir/file)
let testData = testFile.fromJson(TestType)
body
stdout.write "[" & $status & "]\n"
inc count
doAssert count > 0, "Empty or inexisting test folder: " & astToStr(name)
if skipped > 0:
echo "[Warning]: ", skipped, " tests skipped."
testGen(deserialization_G1, testVector, DeserG1_test):
var pubkey{.noInit.}: PublicKey
let status = pubkey.deserialize_public_key_compressed(testVector.input.pubkey)
let success = status == cttBLS_Success or status == cttBLS_PointAtInfinity
doAssert success == testVector.output, block:
"\nDeserialization differs from expected \n" &
" deserializable? " & $success & " (" & $status & ")\n" &
" expected: " & $testVector.output
if success: # Roundtrip
var s{.noInit.}: array[48, byte]
let status2 = s.serialize_public_key_compressed(pubkey)
doAssert status2 == cttBLS_Success
doAssert s == testVector.input.pubkey, block:
"\nSerialization roundtrip differs from expected \n" &
" serialized: 0x" & $s.toHex() & " (" & $status2 & ")\n" &
" expected: 0x" & $testVector.input.pubkey.toHex()
testGen(deserialization_G2, testVector, DeserG2_test):
var sig{.noInit.}: Signature
let status = sig.deserialize_signature_compressed(testVector.input.signature)
let success = status == cttBLS_Success or status == cttBLS_PointAtInfinity
doAssert success == testVector.output, block:
"\nDeserialization differs from expected \n" &
" deserializable? " & $success & " (" & $status & ")\n" &
" expected: " & $testVector.output
if success: # Roundtrip
var s{.noInit.}: array[96, byte]
let status2 = s.serialize_signature_compressed(sig)
doAssert status2 == cttBLS_Success
doAssert s == testVector.input.signature, block:
"\nSerialization roundtrip differs from expected \n" &
" serialized: 0x" & $s.toHex() & " (" & $status2 & ")\n" &
" expected: 0x" & $testVector.input.signature.toHex()
testGen(sign, testVector, Sign_test):
var seckey{.noInit.}: SecretKey
var sig{.noInit.}: Signature
let status = seckey.deserialize_secret_key(testVector.input.privkey)
if status != cttBLS_Success:
doAssert testVector.output == default(array[96, byte])
let status2 = sig.sign(seckey, testVector.input.message)
doAssert status2 != cttBLS_Success
else:
let status2 = sig.sign(seckey, testVector.input.message)
doAssert status2 == cttBLS_Success
block: # deserialize the output for extra codec testing
var output{.noInit.}: Signature
let status3 = output.deserialize_signature_compressed(testVector.output)
doAssert status3 == cttBLS_Success
doAssert sig == output, block:
var sig_bytes{.noInit.}: array[96, byte]
var roundtrip{.noInit.}: array[96, byte]
let sb_status = sig_bytes.serialize_signature_compressed(sig)
let rt_status = roundtrip.serialize_signature_compressed(output)
"\nResult signature differs from expected \n" &
" computed: 0x" & $sig_bytes.toHex() & " (" & $sb_status & ")\n" &
" roundtrip: 0x" & $roundtrip.toHex() & " (" & $rt_status & ")\n" &
" expected: 0x" & $testVector.output.toHex()
block: # serialize the result for extra codec testing
var sig_bytes{.noInit.}: array[96, byte]
let status3 = sig_bytes.serialize_signature_compressed(sig)
doAssert status3 == cttBLS_Success
doAssert sig_bytes == testVector.output, block:
"\nResult signature differs from expected \n" &
" computed: 0x" & $sig_bytes.toHex() & " (" & $status3 & ")\n" &
" expected: 0x" & $testVector.output.toHex()
testGen(verify, testVector, Verify_test):
var
pubkey{.noInit.}: PublicKey
signature{.noInit.}: Signature
status = cttBLS_VerificationFailure
block testChecks:
status = pubkey.deserialize_public_key_compressed(testVector.input.pubkey)
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 = pubkey.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
if success: # Extra codec testing
block:
var output{.noInit.}: array[48, byte]
let s = output.serialize_public_key_compressed(pubkey)
doAssert s == cttBLS_Success
doAssert output == testVector.input.pubkey
block:
var output{.noInit.}: array[96, byte]
let s = output.serialize_signature_compressed(signature)
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
testGen(aggregate_verify, testVector, AggregateVerify_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.aggregate_verify(testVector.input.messages, signature)
let success = status == cttBLS_Success
doAssert success == testVector.output, block:
"Verification differs from expected \n" &
" valid sig? " & $success & " (" & $status & ")\n" &
" expected: " & $testVector.output
testGen(batch_verify, testVector, BatchVerify_test):
var
pubkeys = newSeq[PublicKey](testVector.input.pubkeys.len)
signatures = newSeq[Signature](testVector.input.signatures.len)
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
for i in 0 ..< testVector.input.signatures.len:
status = signatures[i].deserialize_signature_compressed(testVector.input.signatures[i])
if status notin {cttBLS_Success, cttBLS_PointAtInfinity}:
# For point at infinity, we want to make sure that "verify" itself handles them.
break testChecks
let randomBytes = sha256.hash("totally non-secure source of entropy")
status = pubkeys.batch_verify(testVector.input.messages, signatures, randomBytes)
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()
test "Deserialization_G2(Signature) -> bool":
test_deserialization_G2()
test "sign(SecretKey, message) -> Signature":
test_sign()
test "verify(PublicKey, message, Signature) -> bool":
test_verify()
test "fast_aggregate_verify(seq[PublicKey], message, Signature) -> bool":
test_fast_aggregate_verify()
test "aggregate_verify(seq[PublicKey], seq[message], Signature) -> bool":
test_aggregate_verify()
test "batch_verify(seq[PublicKey], seq[message], seq[Signature]) -> bool":
test_batch_verify()