bip-0458: Add half-aggregation of BIP 340 signatures

This commit is contained in:
Fabian Jahr committed 2026-11-01 07:56:15 +01:00
1 parent 1eb496599c
commit 2d439430b1
7 files changed
+1463

No files matched your search

+218
View File
@@ -0,0 +1,218 @@
<pre>
BIP: 458
Layer: Applications
Title: Half-Aggregation of BIP 340 Signatures
Authors: Tim Ruffing <crypto@timruffing.de>
Jonas Nick <jonas@n-ck.net>
Fabian Jahr <fjahr@protonmail.com>
Status: Draft
Type: Specification
Assigned: 2026-07-02
License: CC0-1.0 OR MIT
Discussion: 2022-07-08: https://gnusha.org/pi/bitcoindev/33f275c2-06b1-4b4a-2a75-cafe36836503%40gmail.com/
Requires: 340
</pre>
== Introduction ==
=== Abstract ===
This document describes ''half-aggregation'' of BIP 340 signatures.
Half-aggregation is a non-interactive process for aggregating a collection of signatures into a single aggregate signature.
The size of the resulting aggregate signature is approximately half of the combined size of the original signatures.
=== Copyright ===
This document, along with the [[bip-0458/halfagg.py|reference implementation]] and [[bip-0458/vectors/|test vectors]], is licensed under CC0-1.0 or the MIT License, at your option. The vendored copy of the secp256k1lab library in [[bip-0458/secp256k1lab/|bip-0458/secp256k1lab]] is licensed under the MIT License only, as stated in its [[bip-0458/secp256k1lab/COPYING|COPYING]] file.
=== Motivation ===
Half-aggregation is applicable if there is a verifier that needs to verify multiple signatures.
Instead of sending individual signatures to the verifier, the signatures can be compressed into a single aggregate signature and sent to the verifier.
If the verifier can successfully verify the aggregate signature, the verifier can be sure that the individual signatures would have passed verification.
The purpose of half-aggregation is to reduce the size of the data that is sent to the verifier.
While ''n'' BIP 340 signatures are ''64*n'' bytes, a half-aggregate of the same signatures is ''32*n + 32'' bytes.
The process of half-aggregation is straightforward: it is a pure function of the input signatures, public keys, and messages.
It is non-interactive and does ''not'' require cooperation from other parties, including signers or verifiers.
This is unlike interactive multisignature schemes such as MuSig2 ([[bip-0327.mediawiki|BIP 327]]), in which signers cooperate to produce a single signature under a single aggregate public key.
In half-aggregation, there is no aggregate key, and the aggregate signature is verified against the individual public keys and messages.
There are a variety of scenarios where half-aggregation of BIP 340 signatures is useful.
To keep this section brief and avoid getting outdated quickly, we focus on listing example applications and defer the detailed discussion of the application-specific trade-offs to other places.
One example is the Lightning Network routing gossip protocol, which [https://github.com/lightning/bolts/blob/2e8f2095a36afb9de38da0f3f0051c7dc16dfc36/07-routing-gossip.md as of this writing] involves messages that contain ECDSA signatures.
If the signature scheme was changed to BIP 340, half-aggregation could reduce the total amount of gossiped data.
Instead of sending individual gossip messages, nodes could assemble a batch of messages and half-aggregate the signatures of the individual messages into a single signature for the batch.
Another application of half-aggregation is within the Bitcoin consensus protocol.
In particular, it has been discussed in the context of the [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-February/015700.html Graftroot proposal].
Half-aggregation would allow Graftroot spending to be as efficient as best-case Taproot spends by aggregating the signature of the ''surrogate script'' and signatures that satisfy this script.
Moreover, half-aggregation improves the efficiency of proposed Bitcoin script opcodes that verify multiple signatures, such as [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-February/019926.html OP_EVICT].
We can also imagine adding a Bitcoin script opcode that verifies a half-aggregate signature, allowing for more efficient non-interactive multi and threshold signatures.
Half-aggregation can also be applied to the signatures in the inputs of Bitcoin transactions, a process known as cross-input signature aggregation (CISA).
This [https://github.com/ElementsProject/cross-input-aggregation/blob/master/savings.org reduces the size of an average transaction] by 20.6% and the weight by 7.6%.
A known downside of using half-aggregation is that some uses of adaptor signature protocols [https://github.com/ElementsProject/cross-input-aggregation#half-aggregation-and-adaptor-signatures may be incompatible].
Usually, CISA is proposed with interactive ''full'' signature aggregation instead of non-interactive half-aggregation because creating a valid transaction already requires cooperation, and full signature aggregation is more efficient.
However, the difference in complexity between half-aggregation and full aggregation is so significant that basing a CISA on half-aggregation is a legitimate approach.
The most invasive application to Bitcoin's consensus would be block-wide signature aggregation.
It refers to a process where block producers aggregate as many transaction signatures as possible.
In the best case, a full block would only have a single half-aggregate signature.
While this is attractive from the efficiency perspective, block-wide aggregation requires more research (and, in particular, special attention to handling
[https://github.com/ElementsProject/cross-input-aggregation#half-aggregation-and-reorgs reorgs]).
=== Rationale ===
The idea for half-aggregation of Schnorr signatures was brought up in the context of block-wide signature aggregation [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014272.html by Tadge Dryja on the Bitcoin mailing list] in 2017.
The scheme had a security flaw that was [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014306.html noticed] and [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014308.html fixed] shortly after by Russell O'Connor and Andrew Poelstra.
In 2021 [https://eprint.iacr.org/2021/350 Chalkias, Garillot, Kondi and Nikolaenko] published a security proof in the random oracle model (ROM) that reduces the security of half-aggregation to the security of Schnorr signatures.
[https://eprint.iacr.org/2022/222.pdf Chen and Zhao] were able to produce a tight proof in the ROM and algebraic group model in the following year.
Moreover, they came up with an elegant approach to incremental aggregation that is used in this document.
* Incremental aggregation allows non-interactively aggregating additional BIP 340 signatures into an existing half-aggregate signature.
* A half-aggregate signature of ''u'' BIP 340 input signatures is serialized as the ''(u+1)⋅32''-byte array ''r<sub>0</sub> || ... || r<sub>u-1</sub> || bytes(s)'' where ''r<sub>i</sub>'' is a 32-byte array from input signature ''i'' and ''s'' is a scalar aggregate (see below for details).
* This document does ''not'' specify the aggregation of multiple aggregate signatures (yet). It is possible, but requires changing the encoding of an aggregate signature. Since it is not possible to undo the aggregation of the s-values, when verifying of such an aggregate signature the randomizers need to be the same as when verifying the individual aggregate signature. Therefore, the aggregate signature needs to encode a tree that reveals how the individual signatures were aggregated and how the resulting aggregate signatures were reaggregated.
* The first randomizer ''z<sub>0</sub>'' is fixed to the constant ''1'', which speeds up verification because ''z<sub>0</sub>⋅R<sub>0</sub> = R<sub>0</sub>''. This optimization has been suggested and proven secure by [https://eprint.iacr.org/2022/222.pdf Chen and Zhao].
* This BIP does not impose a maximum number of signatures that can be aggregated in a single half-aggregate signature. Implementations may enforce limits, e.g., to facilitate testing, to prevent resource exhaustion and integer overflows. Additionally, applications should enforce a (possibly lower) limit that suits their needs, taking into account that processing large aggregates may take a considerable amount of time. We note that there must be agreement over the limits in consensus-critical applications.
* Messages must be exactly 32 bytes, the size of a transaction sighash. This keeps the input of each randomizer hash unambiguous, since it concatenates r-values, public keys and messages without length prefixes.
* The input of each randomizer hash is a prefix of the input of the next one. Implementations should stream the concatenation through a single hash state once and finalize a copy of that state for each randomizer, which keeps the hashing linear in the number of signatures instead of quadratic.
== Description ==
=== Specification ===
The specification is written in Python and can be found in [[bip-0458/halfagg.py|halfagg.py]].
Note that the specification depends on a vendored copy of the [https://github.com/secp256k1lab/secp256k1lab secp256k1lab library].
=== Test Vectors ===
Test vectors are provided in [[bip-0458/vectors/|vectors]] and are generated with a [[bip-0458/gen_test_vectors.py|generator script]].
The specification can be executed with the test vectors by running [[bip-0458/run_test_vectors.py|the run_test_vectors script]].
=== Pseudocode ===
The following pseudocode is ''not'' a specification but is only intended to augment the actual Python [[#specification|specification]].
==== Notation ====
The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme<ref>Among other pitfalls, using the specification with a curve whose order is not close to the size of the range of the nonce derivation function is insecure.</ref>.
* Lowercase variables represent integers or byte arrays.
** The constant ''p'' refers to the field size, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F''.
** The constant ''n'' refers to the curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''.
* Uppercase variables refer to points on the curve with equation ''y<sup>2</sup> = x<sup>3</sup> + 7'' over the integers modulo ''p''.
** ''is_infinite(P)'' returns whether or not ''P'' is the point at infinity.
** ''x(P)'' and ''y(P)'' are integers in the range ''0..p-1'' and refer to the X and Y coordinates of a point ''P'' (assuming it is not infinity).
** The constant ''G'' refers to the base point, for which ''x(G) = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'' and ''y(G) = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8''.
** Addition of points refers to the usual [https://en.wikipedia.org/wiki/Elliptic_curve#The_group_law elliptic curve group operation].
** [https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication Multiplication (⋅) of an integer and a point] refers to the repeated application of the group operation.
* Functions and operations:
** ''||'' refers to byte array concatenation.
** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j &ge; 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''.
** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first.
** The function ''len(x)'' where ''x'' is a byte array returns the length of the array.
** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''.
** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''.
** The function ''lift_x(x)'', where ''x'' is a 256-bit unsigned integer, returns the point ''P'' for which ''x(P) = x''<ref>
Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x<sup>3</sup> + 7 mod p'' and they can be computed as ''y = &plusmn;c<sup>(p+1)/4</sup> mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''.</ref> and ''has_even_y(P)'', or fails if ''x'' is greater than ''p-1'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode:
*** Fail if ''x &ge; p''.
*** Let ''c = x<sup>3</sup> + 7 mod p''.
*** Let ''y = c<sup>(p+1)/4</sup> mod p''.
*** Fail if ''c &ne; y<sup>2</sup> mod p''.
*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y'' if ''y mod 2 = 0'' or ''y(P) = p-y'' otherwise.
** The function ''hash<sub>tag</sub>(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''.
* Other:
** Tuples are written by listing the elements within parentheses and separated by commas. For example, ''(2, 3, 1)'' is a tuple.
In the [[#specification|reference implementation]], these operations map to the following [https://github.com/secp256k1lab/secp256k1lab secp256k1lab] library methods, where ''GE'' and ''Scalar'' represent points and scalars:
{| class="wikitable"
! Notation !! secp256k1lab
|-
| ''lift_x(int(x))'' || ''GE.from_bytes_xonly(x)''
|-
| ''int(x)'', failing if ''&ge; n'' || ''Scalar.from_bytes_checked(x)''
|-
| ''int(x) mod n'' || ''Scalar.from_bytes_wrapping(x)''
|-
| ''bytes(s)'' || ''s.to_bytes()''
|-
| ''hash<sub>tag</sub>(x)'' || ''tagged_hash(tag, x)''
|}
==== Aggregate ====
''Aggregate'' takes an array of public key, message and signature triples and returns an aggregate signature.
If every triple ''(p, m, s)'' is valid (i.e., ''Verify(p, m, s)'' as defined in BIP 340 returns true), then the returned aggregate signature and the array of ''(p, m)'' tuples passes ''VerifyAggregate''.
(However, the inverse does not hold: given suitable valid triples, it is possible to construct an input array to ''Aggregate'' which contains invalid triples, but for which ''VerifyAggregate'' will accept the aggregate signature returned by ''Aggregate''. If this is undesired, input triples should be verified individually before passing them to ''Aggregate''.)
Input:
* ''pms<sub>0..u-1</sub>'': an array of ''u'' triples, where the first element of each triple is a 32-byte public key, the second element is a 32-byte message and the third element is a 64-byte BIP 340 signature
'''''Aggregate(pms<sub>0..u-1</sub>)''''':
* Let ''aggsig = bytes(0)''
* Let ''pm_aggd'' be an empty array
* Return ''IncAggregate(aggsig, pm_aggd, pms<sub>0..u-1</sub>)''; fail if that fails.
==== IncAggregate ====
''IncAggregate'' takes an aggregate signature, an array of public key and message tuples corresponding to the aggregate signature, and an additional array of public key, message and signature triples.
It aggregates the additional array of triples into the existing aggregate signature and returns the resulting new aggregate signature.
In other words, if ''VerifyAggregate(aggsig, pm_aggd)'' passes and every triple ''(p, m, s)'' in ''pms_to_agg'' is valid (i.e., ''Verify(p, m, s)'' as defined in BIP 340 returns true), then the returned aggregate signature along with the array of ''(p, m)'' tuples of ''pm_aggd'' and ''pms_to_agg'' passes ''VerifyAggregate''.
(However, the inverse does not hold: given a suitable valid aggregate signature and suitable valid triples, it is possible to construct inputs to ''IncAggregate'' which contain an invalid aggregate signature or invalid triples, but for which ''VerifyAggregate'' will accept the aggregate signature returned by ''IncAggregate''. If this is undesired, the input triples and the input aggregate signature should be verified individually before passing them to ''IncAggregate''.)
Input:
* ''aggsig'' : a byte array
* ''pm_aggd<sub>0..v-1</sub>'': an array of ''v'' tuples, where the first element of each tuple is a 32-byte public key and the second element is a 32-byte message
* ''pms_to_agg<sub>0..u-1</sub>'': an array of ''u'' triples, where the first element of each tuple is a 32-byte public key, the second element is a 32-byte message and the third element is a 64-byte BIP 340 signature
'''''IncAggregate(aggsig, pm_aggd<sub>0..v-1</sub>, pms_to_agg<sub>0..u-1</sub>)''''':
* Fail if ''len(aggsig) &ne; 32 * (v + 1)''
* For ''i = 0 .. v-1'':
** Let ''(pk<sub>i</sub>, m<sub>i</sub>) = pm_aggd<sub>i</sub>''
** Let ''r<sub>i</sub> = aggsig[i⋅32:(i+1)⋅32]''
* For ''i = v .. v+u-1'':
** Let ''(pk<sub>i</sub>, m<sub>i</sub>, sig<sub>i</sub>) = pms_to_agg<sub>i-v</sub>''
** Let ''r<sub>i</sub> = sig<sub>i</sub>[0:32]''
** Let ''s<sub>i</sub> = int(sig<sub>i</sub>[32:64])''; fail if ''s<sub>i</sub> &ge; n''
** If ''i = 0'':
*** Let ''z<sub>i</sub> = 1''
** Else:
*** Let ''z<sub>i</sub> = int(hash<sub>BIP0458/randomizer</sub>(r<sub>0</sub> || pk<sub>0</sub> || m<sub>0</sub> || ... || r<sub>i</sub> || pk<sub>i</sub> || m<sub>i</sub>)) mod n''
* Let ''s<sub>agg</sub> = int(aggsig[v⋅32:(v+1)⋅32])''; fail if ''s<sub>agg</sub> &ge; n''
* Let ''s = s<sub>agg</sub> + z<sub>v</sub>⋅s<sub>v</sub> + ... + z<sub>v+u-1</sub>⋅s<sub>v+u-1</sub> mod n''
* Return ''r<sub>0</sub> || ... || r<sub>v+u-1</sub> || bytes(s)''
==== VerifyAggregate ====
''VerifyAggregate'' verifies a given aggregate signature against an array of public key and message tuples.
Input:
* ''aggsig'' : a byte array
* ''pm_aggd<sub>0..u-1</sub>'': an array of ''u'' tuples, where the first element of each tuple is a 32-byte public key and the second element is a 32-byte message
'''''VerifyAggregate(aggsig, pm_aggd<sub>0..u-1</sub>)''''':
The algorithm ''VerifyAggregate(aggsig, pm_aggd<sub>0..u-1</sub>)'' is defined as:
* Fail if ''len(aggsig) &ne; 32 * (u + 1)''
* For ''i = 0 .. u-1'':
** Let ''(pk<sub>i</sub>, m<sub>i</sub>) = pm_aggd<sub>i</sub>''
** Let ''P<sub>i</sub> = lift_x(int(pk<sub>i</sub>))''; fail if that fails
** Let ''r<sub>i</sub> = aggsig[i⋅32:(i+1)⋅32]''
** Let ''R<sub>i</sub> = lift_x(int(r<sub>i</sub>))''; fail if that fails
** Let ''e<sub>i</sub> = int(hash<sub>BIP0340/challenge</sub>(r<sub>i</sub> || pk<sub>i</sub> || m<sub>i</sub>)) mod n''
** If ''i = 0'':
*** Let ''z<sub>i</sub> = 1''
** Else:
*** Let ''z<sub>i</sub> = int(hash<sub>BIP0458/randomizer</sub>(r<sub>0</sub> || pk<sub>0</sub> || m<sub>0</sub> || ... || r<sub>i</sub> || pk<sub>i</sub> || m<sub>i</sub>)) mod n''
* Let ''s = int(aggsig[u⋅32:(u+1)⋅32])''; fail if ''s &ge; n''
* Fail if ''s⋅G &ne; z<sub>0</sub>⋅(R<sub>0</sub> + e<sub>0</sub>⋅P<sub>0</sub>) + ... + z<sub>u-1</sub>⋅(R<sub>u-1</sub> + e<sub>u-1</sub>⋅P<sub>u-1</sub>)''
* Return success iff no failure occurred before reaching this point.
The verification algorithm is similar to [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#batch-verification BIP340 Batch Verification]. As in BIP340, using an [https://bitcoin.stackexchange.com/a/80702/109853 efficient algorithm for computing the sum of multiple EC multiplications] can significantly speed up verification.
== Backward Compatibility ==
Half-aggregation specifies a new, optional construction for aggregating existing BIP 340 signatures.
It does not change the validation of individual BIP 340 signatures and does not affect any existing Bitcoin consensus rules or protocol behavior.
Therefore, this BIP introduces no backward compatibility concerns.
+831
View File
@@ -0,0 +1,831 @@
#!/usr/bin/env python3
"""
Generate test vectors for Schnorr signature half-aggregation.
Usage:
python gen_test_vectors.py
Outputs:
- vectors/test_vectors_aggregate.csv
- vectors/test_vectors_incaggregate.csv
- vectors/test_vectors_verify.csv
"""
import csv
import os
from pathlib import Path
from typing import List, Tuple
from halfagg import Aggregate, IncAggregate, hashHalfAgg_randomizer
from secp256k1lab.secp256k1 import GE, FE, Scalar
from secp256k1lab.util import bytes_from_int
from secp256k1lab.bip340 import pubkey_gen, schnorr_sign, schnorr_verify
ROOT = Path(__file__).resolve().parent
VECTORS_DIR = ROOT / "vectors"
AGGREGATE_VECTORS = VECTORS_DIR / 'test_vectors_aggregate.csv'
INCAGGREGATE_VECTORS = VECTORS_DIR / 'test_vectors_incaggregate.csv'
VERIFY_VECTORS = VECTORS_DIR / 'test_vectors_verify.csv'
n = GE.ORDER
p = FE.SIZE
def create_signature(index: int, msg_len: int = 32) -> Tuple[bytes, bytes, bytes]:
"""Create a deterministic (pubkey, message, signature) triple."""
sk = bytes([index + 1] * 32)
pk = pubkey_gen(sk)
msg = bytes([index + 2] * msg_len)
aux = bytes([index + 3] * 32)
sig = schnorr_sign(msg, sk, aux)
return pk, msg, sig
def format_list(items: List[bytes], sep: str = ";") -> str:
"""Format a list of bytes as semicolon-separated hex strings."""
if not items:
return ""
return sep.join(item.hex().upper() for item in items)
def gen_aggregate_vectors(f):
"""Generate test vectors for Aggregate function."""
writer = csv.writer(f)
writer.writerow((
"index",
"pubkeys",
"messages",
"signatures",
"expected_result",
"expected_aggsig",
"comment"
))
idx = 0
sigs = [create_signature(i) for i in range(5)]
# Success: Empty aggregation
aggsig_empty = Aggregate([])
writer.writerow((
idx, "", "", "",
"TRUE",
aggsig_empty.hex().upper(),
"Empty signature list"
))
idx += 1
# Success: Single signature
pk0, msg0, sig0 = sigs[0]
aggsig_1 = Aggregate([(pk0, msg0, sig0)])
writer.writerow((
idx,
pk0.hex().upper(),
msg0.hex().upper(),
sig0.hex().upper(),
"TRUE",
aggsig_1.hex().upper(),
"Single signature"
))
idx += 1
# Success: Two signatures
pk1, msg1, sig1 = sigs[1]
pms_2 = [(pk0, msg0, sig0), (pk1, msg1, sig1)]
aggsig_2 = Aggregate(pms_2)
writer.writerow((
idx,
format_list([pk0, pk1]),
format_list([msg0, msg1]),
format_list([sig0, sig1]),
"TRUE",
aggsig_2.hex().upper(),
"Two signatures"
))
idx += 1
# Success: Three signatures
pk2, msg2, sig2 = sigs[2]
pms_3 = [(pk0, msg0, sig0), (pk1, msg1, sig1), (pk2, msg2, sig2)]
aggsig_3 = Aggregate(pms_3)
writer.writerow((
idx,
format_list([pk0, pk1, pk2]),
format_list([msg0, msg1, msg2]),
format_list([sig0, sig1, sig2]),
"TRUE",
aggsig_3.hex().upper(),
"Three signatures"
))
idx += 1
# Success: Five signatures
pms_5 = [sigs[i] for i in range(5)]
aggsig_5 = Aggregate(pms_5)
writer.writerow((
idx,
format_list([s[0] for s in pms_5]),
format_list([s[1] for s in pms_5]),
format_list([s[2] for s in pms_5]),
"TRUE",
aggsig_5.hex().upper(),
"Five signatures"
))
idx += 1
# Success: Strange aggregation - individual invalid sigs that are valid
# in aggregate
pk_a, msg_a, sig_a = sigs[0]
pk_b, msg_b, sig_b = sigs[1]
pms_valid = [(pk_a, msg_a, sig_a), (pk_b, msg_b, sig_b)]
aggsig_valid = Aggregate(pms_valid)
pmr = []
z = []
for i in range(2):
pk, msg, sig = pms_valid[i]
pmr.append((pk, msg, sig[:32]))
z.append(hashHalfAgg_randomizer(pmr))
sagg = Scalar.from_bytes_wrapping(aggsig_valid[64:96])
s1_new = Scalar.from_int_wrapping(0x123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0)
s0_new = (sagg - z[1] * s1_new) / z[0]
sig_a_invalid = sig_a[:32] + s0_new.to_bytes()
assert not schnorr_verify(msg_a, pk_a, sig_a_invalid), "sig_a_invalid should not verify"
sig_b_invalid = sig_b[:32] + s1_new.to_bytes()
assert not schnorr_verify(msg_b, pk_b, sig_b_invalid), "sig_b_invalid should not verify"
pms_strange = [(pk_a, msg_a, sig_a_invalid), (pk_b, msg_b, sig_b_invalid)]
aggsig_strange = Aggregate(pms_strange)
writer.writerow((
idx,
format_list([pk_a, pk_b]),
format_list([msg_a, msg_b]),
format_list([sig_a_invalid, sig_b_invalid]),
"TRUE",
aggsig_strange.hex().upper(),
"Strange aggregation - invalid individual sigs, valid aggregate"
))
idx += 1
# Failure: Signature with s = n
invalid_sig_n = sig0[0:32] + bytes_from_int(n)
writer.writerow((
idx,
pk0.hex().upper(),
msg0.hex().upper(),
invalid_sig_n.hex().upper(),
"FALSE",
"",
"Signature s = n (at boundary)"
))
idx += 1
# Failure: Signature with s > n
invalid_sig_gt_n = sig0[0:32] + bytes_from_int(n + 1000)
writer.writerow((
idx,
pk0.hex().upper(),
msg0.hex().upper(),
invalid_sig_gt_n.hex().upper(),
"FALSE",
"",
"Signature s > n"
))
idx += 1
# Failure: Signature with s = 2^256 - 1
invalid_sig_max = sig0[0:32] + bytes([0xff] * 32)
writer.writerow((
idx,
pk0.hex().upper(),
msg0.hex().upper(),
invalid_sig_max.hex().upper(),
"FALSE",
"",
"Signature s = 2^256 - 1"
))
idx += 1
# Failure: Public key too short
writer.writerow((
idx,
pk0[0:31].hex().upper(),
msg0.hex().upper(),
sig0.hex().upper(),
"FALSE",
"",
"Public key too short"
))
idx += 1
# Failure: Message too long
pk_long, msg_long, sig_long = create_signature(0, 33)
writer.writerow((
idx,
pk_long.hex().upper(),
msg_long.hex().upper(),
sig_long.hex().upper(),
"FALSE",
"",
"Message too long"
))
idx += 1
# Failure: Signature too short
writer.writerow((
idx,
pk0.hex().upper(),
msg0.hex().upper(),
sig0[0:63].hex().upper(),
"FALSE",
"",
"Signature too short"
))
idx += 1
# Failure: Signature too long
writer.writerow((
idx,
pk0.hex().upper(),
msg0.hex().upper(),
(sig0 + bytes([0])).hex().upper(),
"FALSE",
"",
"Signature too long"
))
idx += 1
# Failure: Public key too long
writer.writerow((
idx,
(bytes([2]) + pk0).hex().upper(),
msg0.hex().upper(),
sig0.hex().upper(),
"FALSE",
"",
"Public key too long"
))
idx += 1
# Failure: Message too short
pk_short, msg_short, sig_short = create_signature(0, 31)
writer.writerow((
idx,
pk_short.hex().upper(),
msg_short.hex().upper(),
sig_short.hex().upper(),
"FALSE",
"",
"Message too short"
))
idx += 1
def gen_incaggregate_vectors(f):
"""Generate test vectors for IncAggregate function."""
writer = csv.writer(f)
writer.writerow((
"index",
"aggsig",
"pm_aggd_pubkeys",
"pm_aggd_messages",
"pms_pubkeys",
"pms_messages",
"pms_signatures",
"expected_result",
"expected_aggsig",
"comment"
))
idx = 0
sigs = [create_signature(i) for i in range(4)]
pk0, msg0, sig0 = sigs[0]
pk1, msg1, sig1 = sigs[1]
pk2, msg2, sig2 = sigs[2]
# Success: Increment empty aggregate with single signature
empty_aggsig = bytes([0] * 32)
result_0 = IncAggregate(empty_aggsig, [], [(pk0, msg0, sig0)])
writer.writerow((
idx,
empty_aggsig.hex().upper(),
"", "",
pk0.hex().upper(),
msg0.hex().upper(),
sig0.hex().upper(),
"TRUE",
result_0.hex().upper(),
"Add single signature to empty aggregate"
))
idx += 1
# Success: Increment single-sig aggregate with another signature
aggsig_1 = Aggregate([(pk0, msg0, sig0)])
result_1 = IncAggregate(aggsig_1, [(pk0, msg0)], [(pk1, msg1, sig1)])
writer.writerow((
idx,
aggsig_1.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
pk1.hex().upper(),
msg1.hex().upper(),
sig1.hex().upper(),
"TRUE",
result_1.hex().upper(),
"Add second signature to single-sig aggregate"
))
idx += 1
# Success: Add two signatures at once
result_2 = IncAggregate(aggsig_1, [(pk0, msg0)], [(pk1, msg1, sig1), (pk2, msg2, sig2)])
writer.writerow((
idx,
aggsig_1.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
format_list([pk1, pk2]),
format_list([msg1, msg2]),
format_list([sig1, sig2]),
"TRUE",
result_2.hex().upper(),
"Add two signatures at once"
))
idx += 1
# Success: Increment with empty new signatures (no change)
result_3 = IncAggregate(aggsig_1, [(pk0, msg0)], [])
writer.writerow((
idx,
aggsig_1.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"", "", "",
"TRUE",
result_3.hex().upper(),
"Add no new signatures (unchanged)"
))
idx += 1
# Failure: Wrong aggsig length (too long)
wrong_len_aggsig = bytes([0] * 64)
writer.writerow((
idx,
wrong_len_aggsig.hex().upper(),
"", "",
"", "", "",
"FALSE",
"",
"Aggregate signature wrong length"
))
idx += 1
# Failure: Empty aggsig
writer.writerow((
idx,
"",
"", "",
"", "", "",
"FALSE",
"",
"Aggregate signature empty"
))
idx += 1
# Failure: Existing aggsig with s = n
invalid_aggsig_s = bytes_from_int(n)
writer.writerow((
idx,
invalid_aggsig_s.hex().upper(),
"", "",
"", "", "",
"FALSE",
"",
"Existing aggregate has s = n"
))
idx += 1
# Failure: New signature with s = n
invalid_new_sig = sig0[0:32] + bytes_from_int(n)
writer.writerow((
idx,
empty_aggsig.hex().upper(),
"", "",
pk0.hex().upper(),
msg0.hex().upper(),
invalid_new_sig.hex().upper(),
"FALSE",
"",
"New signature has s = n"
))
idx += 1
# Failure: Existing public key too short
writer.writerow((
idx,
aggsig_1.hex().upper(),
pk0[0:31].hex().upper(),
msg0.hex().upper(),
pk1.hex().upper(),
msg1.hex().upper(),
sig1.hex().upper(),
"FALSE",
"",
"Existing public key too short"
))
idx += 1
# Failure: Existing message too long
pk_long, msg_long, sig_long = create_signature(0, 33)
writer.writerow((
idx,
sig_long.hex().upper(),
pk_long.hex().upper(),
msg_long.hex().upper(),
pk1.hex().upper(),
msg1.hex().upper(),
sig1.hex().upper(),
"FALSE",
"",
"Existing message too long"
))
idx += 1
# Failure: Existing public key too long
writer.writerow((
idx,
aggsig_1.hex().upper(),
(bytes([2]) + pk0).hex().upper(),
msg0.hex().upper(),
pk1.hex().upper(),
msg1.hex().upper(),
sig1.hex().upper(),
"FALSE",
"",
"Existing public key too long"
))
idx += 1
# Failure: Existing message too short
pk_short, msg_short, sig_short = create_signature(0, 31)
writer.writerow((
idx,
sig_short.hex().upper(),
pk_short.hex().upper(),
msg_short.hex().upper(),
pk1.hex().upper(),
msg1.hex().upper(),
sig1.hex().upper(),
"FALSE",
"",
"Existing message too short"
))
idx += 1
def gen_verify_vectors(f):
"""Generate test vectors for VerifyAggregate function."""
writer = csv.writer(f)
writer.writerow((
"index",
"aggsig",
"pubkeys",
"messages",
"expected_result",
"comment"
))
idx = 0
sigs = [create_signature(i) for i in range(5)]
pk0, msg0, sig0 = sigs[0]
pk1, msg1, sig1 = sigs[1]
pk2, msg2, sig2 = sigs[2]
# Success: Empty signature list
writer.writerow((
idx,
"0000000000000000000000000000000000000000000000000000000000000000",
"", "",
"TRUE",
"Empty signature list"
))
idx += 1
# Success: Single signature
writer.writerow((
idx,
"B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F",
"1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F",
"0202020202020202020202020202020202020202020202020202020202020202",
"TRUE",
"Single signature"
))
idx += 1
# Success: Two signatures
pk3, msg3, sig3 = sigs[3]
aggsig_2 = Aggregate([(pk0, msg0, sig0), (pk3, msg3, sig3)])
writer.writerow((
idx,
aggsig_2.hex().upper(),
format_list([pk0, pk3]),
format_list([msg0, msg3]),
"TRUE",
"Two signatures"
))
idx += 1
# Success: Three signatures
aggsig_3 = Aggregate([(pk0, msg0, sig0), (pk1, msg1, sig1), (pk2, msg2, sig2)])
writer.writerow((
idx,
aggsig_3.hex().upper(),
format_list([pk0, pk1, pk2]),
format_list([msg0, msg1, msg2]),
"TRUE",
"Three signatures"
))
idx += 1
# Success: Five signatures
pms_5 = [sigs[i] for i in range(5)]
aggsig_5 = Aggregate(pms_5)
writer.writerow((
idx,
aggsig_5.hex().upper(),
format_list([s[0] for s in pms_5]),
format_list([s[1] for s in pms_5]),
"TRUE",
"Five signatures"
))
idx += 1
# Failure: Public key not on curve
aggsig_1 = Aggregate([(pk0, msg0, sig0)])
invalid_x = 0x4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D # From BIP340
invalid_pk = bytes_from_int(invalid_x)
writer.writerow((
idx,
aggsig_1.hex().upper(),
invalid_pk.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Public key not on curve"
))
idx += 1
# Failure: Public key is zero
zero_pk = bytes([0] * 32)
writer.writerow((
idx,
aggsig_1.hex().upper(),
zero_pk.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Public key is zero"
))
idx += 1
# Failure: Public key >= field size
pk_too_large = bytes_from_int(p)
writer.writerow((
idx,
aggsig_1.hex().upper(),
pk_too_large.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Public key >= field size"
))
idx += 1
# Failure: R value not on curve
invalid_r = bytes_from_int(invalid_x)
invalid_r_aggsig = invalid_r + sig0[32:64]
writer.writerow((
idx,
invalid_r_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"R value not on curve"
))
idx += 1
# Failure: R value is zero
zero_r_aggsig = bytes([0] * 32) + sig0[32:64]
writer.writerow((
idx,
zero_r_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"R value is zero"
))
idx += 1
# Failure: R value >= field size
r_too_large_aggsig = bytes_from_int(p + 100) + sig0[32:64]
writer.writerow((
idx,
r_too_large_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"R value >= field size"
))
idx += 1
# Failure: Aggregate s = n
s_n_aggsig = sig0[0:32] + bytes_from_int(n)
writer.writerow((
idx,
s_n_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Aggregate s = n"
))
idx += 1
# Failure: Aggregate s > n
s_gt_n_aggsig = sig0[0:32] + bytes_from_int(n + 1000)
writer.writerow((
idx,
s_gt_n_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Aggregate s > n"
))
idx += 1
# Failure: Aggregate s = 2^256 - 1
max_s_aggsig = sig0[0:32] + bytes([0xff] * 32)
writer.writerow((
idx,
max_s_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Aggregate s = 2^256 - 1"
))
idx += 1
# Failure: Wrong message
wrong_msg = bytes([0xff] * 32)
writer.writerow((
idx,
aggsig_1.hex().upper(),
pk0.hex().upper(),
wrong_msg.hex().upper(),
"FALSE",
"Wrong message"
))
idx += 1
# Failure: Wrong public key
writer.writerow((
idx,
aggsig_1.hex().upper(),
pk1.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Wrong public key"
))
idx += 1
# Failure: Tampered signature
tampered_aggsig = aggsig_1[:-1] + bytes([(aggsig_1[-1] + 1) % 256])
writer.writerow((
idx,
tampered_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Tampered signature (bit flip)"
))
idx += 1
# Failure: Swapped order
aggsig_2 = Aggregate([(pk0, msg0, sig0), (pk1, msg1, sig1)])
writer.writerow((
idx,
aggsig_2.hex().upper(),
format_list([pk1, pk0]),
format_list([msg1, msg0]),
"FALSE",
"Swapped order"
))
idx += 1
# Failure: s = n - 1 (valid range, wrong signature)
s_n_minus_1_aggsig = sig0[0:32] + bytes_from_int(n - 1)
writer.writerow((
idx,
s_n_minus_1_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"s = n-1 (valid range, wrong sig)"
))
idx += 1
# Failure: s = 0 (valid range, wrong signature)
s_zero_aggsig = sig0[0:32] + bytes([0] * 32)
writer.writerow((
idx,
s_zero_aggsig.hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"s = 0 (valid range, wrong sig)"
))
idx += 1
# Failure: Aggsig too short
writer.writerow((
idx,
sig0[0:31].hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Aggregate signature too short"
))
idx += 1
# Failure: Aggsig too long
writer.writerow((
idx,
(aggsig_1 + bytes([0] * 32)).hex().upper(),
pk0.hex().upper(),
msg0.hex().upper(),
"FALSE",
"Aggregate signature too long"
))
idx += 1
# Failure: Public key too short
writer.writerow((
idx,
aggsig_1.hex().upper(),
pk0[0:31].hex().upper(),
msg0.hex().upper(),
"FALSE",
"Public key too short"
))
idx += 1
# Failure: Message too long
pk_long, msg_long, sig_long = create_signature(0, 33)
writer.writerow((
idx,
sig_long.hex().upper(),
pk_long.hex().upper(),
msg_long.hex().upper(),
"FALSE",
"Message too long"
))
idx += 1
# Failure: Public key too long
writer.writerow((
idx,
aggsig_1.hex().upper(),
(bytes([2]) + pk0).hex().upper(),
msg0.hex().upper(),
"FALSE",
"Public key too long"
))
idx += 1
# Failure: Message too short
pk_short, msg_short, sig_short = create_signature(0, 31)
writer.writerow((
idx,
sig_short.hex().upper(),
pk_short.hex().upper(),
msg_short.hex().upper(),
"FALSE",
"Message too short"
))
idx += 1
if __name__ == "__main__":
os.makedirs(VECTORS_DIR, exist_ok=True)
print(f"Generating {AGGREGATE_VECTORS}")
with open(AGGREGATE_VECTORS, "w", newline='', encoding="utf-8") as f:
gen_aggregate_vectors(f)
print(f"Generating {INCAGGREGATE_VECTORS}")
with open(INCAGGREGATE_VECTORS, "w", newline='', encoding="utf-8") as f:
gen_incaggregate_vectors(f)
print(f"Generating {VERIFY_VECTORS}")
with open(VERIFY_VECTORS, "w", newline='', encoding="utf-8") as f:
gen_verify_vectors(f)
print("Done.")
+197
View File
@@ -0,0 +1,197 @@
#!/usr/bin/env python3
"""
Schnorr signature half-aggregation reference implementation
WARNING: This implementation is for demonstration purposes only and _not_ to
be used in production environments.
"""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent / "secp256k1lab/src"))
from secp256k1lab.secp256k1 import (
G,
GE,
Scalar,
)
from secp256k1lab.util import (
tagged_hash,
)
def Aggregate(pms):
"""
Aggregates an array of triples (public key, message, signature) into a single aggregate signature.
:param pms: An array of triples (public key, message, signature).
:return: The aggregate signature.
"""
# Let aggsig = bytes(0)
aggsig = bytes([0] * 32)
pm_aggd = []
# Return IncAggregate(aggsig, pms0..u-1); fail if that fails.
return IncAggregate(aggsig, pm_aggd, pms)
# IncAggregate(aggsig, pm_aggd0..v-1, pms_to_agg0..u-1)
def IncAggregate(aggsig, pm_aggd, pms_to_agg):
"""
Incrementally aggregates an additional array of triples (public key, message, signature)
into an existing aggregate signature.
:param aggsig: A byte array representing the aggregate signature.
:param pm_aggd: An array of tuples (public key, message).
:param pms_to_agg: An array of triples (public key, message, signature).
:return: The new aggregate signature.
"""
v = len(pm_aggd)
u = len(pms_to_agg)
# Fail if len(aggsig) ≠ 32 * (v + 1)
if len(aggsig) != 32 * (v + 1):
raise ValueError("Length of aggsig must be 32 * (v + 1)")
r_values = []
pmr_to_agg = []
# For i = 0 .. v-1:
for i in range(v):
# Let (pki, mi) = pm_aggdi
(pki, mi) = pm_aggd[i]
if len(pki) != 32 or len(mi) != 32:
raise ValueError("Public key and message must be 32 bytes")
# Let ri = aggsig[i⋅32:(i+1)⋅32]
ri = aggsig[i * 32:(i + 1) * 32]
r_values.append(ri)
pmr_to_agg.append((pki, mi, ri))
z_values = []
s_values = []
# For i = v .. v+u-1:
for i in range(v, v + u):
# Let (pki, mi, sigi) = pms_to_aggi-v
(pki, mi, sigi) = pms_to_agg[i - v]
if len(pki) != 32 or len(mi) != 32 or len(sigi) != 64:
raise ValueError("Public key and message must be 32 bytes and signature 64 bytes")
# Let ri = sigi[0:32]
ri = sigi[0:32]
r_values.append(ri)
# Let si = int(sigi[32:64]); fail if si ≥ n
si = Scalar.from_bytes_checked(sigi[32:64])
s_values.append(si)
# If i = 0:
# Let zi = 1
# Else:
# Let zi = int(hashBIP0458/randomizer(r0 || pk0 || m0 || ... || ri || pki || mi)) mod n
pmr_to_agg.append((pki, mi, ri))
z_values.append(hashHalfAgg_randomizer(pmr_to_agg))
# Let sagg = int(aggsig[v⋅32:(v+1)⋅32]); fail if sagg ≥ n
s = Scalar.from_bytes_checked(aggsig[v * 32:(v + 1) * 32])
# Let s = sagg + zv⋅sv + ... + zv+u-1⋅sv+u-1 mod n
for i in range(u):
s = s + z_values[i] * s_values[i]
# Return r0 || ... || rv+u-1 || bytes(s)
return b''.join(r_values) + s.to_bytes()
# VerifyAggregate(aggsig, pm_aggd0..u-1)
def VerifyAggregate(aggsig, pm_aggd):
"""
Verifies an aggregate signature against an array of public key and message tuples.
:param aggsig: A byte array representing the aggregate signature.
:param pm_aggd: An array of tuples (public key, message).
:return: Boolean indicating whether the verification is successful.
"""
u = len(pm_aggd)
# Fail if len(aggsig) ≠ 32 * (u + 1)
if len(aggsig) != 32 * (u + 1):
raise ValueError("Length of aggsig must be 32 * (u + 1)")
z_values = []
R_values = []
P_values = []
e_values = []
r_values = []
# For i = 0 .. u-1:
for i in range(u):
# Let (pki, mi) = pm_aggdi
(pki, mi) = pm_aggd[i]
if len(pki) != 32 or len(mi) != 32:
raise ValueError("Public key and message must be 32 bytes")
# Let Pi = lift_x(int(pki)); fail if that fails
try:
Pi = GE.from_bytes_xonly(pki)
except ValueError:
return False
P_values.append(Pi)
# Let ri = aggsig[i⋅32:(i+1)⋅32]
ri = aggsig[i * 32:(i + 1) * 32]
# Let Ri = lift_x(int(ri)); fail if that fails
try:
Ri = GE.from_bytes_xonly(ri)
except ValueError:
return False
R_values.append(Ri)
r_values.append(ri)
# Let ei = int(hashBIP0340/challenge(ri || pki || mi)) mod n
ei = Scalar.from_bytes_wrapping(hashBIP0340_challenge(ri, pki, mi))
e_values.append(ei)
# If i = 0:
# Let zi = 1
# Else:
# Let zi = int(hashBIP0458/randomizer(r0 || pk0 || m0 || ... || ri || pki || mi)) mod n
pmr = [(pki, mi, ri) for (pki, mi), ri in zip(pm_aggd, r_values)]
z_values.append(hashHalfAgg_randomizer(pmr))
# Let s = int(aggsig[u⋅32:(u+1)⋅32]); fail if s ≥ n
try:
s = Scalar.from_bytes_checked(aggsig[u * 32:(u + 1) * 32])
except ValueError:
return False
# Fail if s⋅G ≠ z0⋅(R0 + e0⋅P0) + ... + zu-1⋅(Ru-1 + eu-1⋅Pu-1)
lhs = s * G
rhs = GE()
for i in range(u):
rhs += z_values[i] * (R_values[i] + e_values[i] * P_values[i])
return lhs == rhs
def hashBIP0340_challenge(sig, pubkey, msg):
return tagged_hash("BIP0340/challenge", sig + pubkey + msg)
def hashHalfAgg_randomizer(pmr):
if len(pmr) == 1:
return Scalar(1)
random_input = bytes()
for (pki, mi, ri) in pmr:
random_input += ri
random_input += pki
random_input += mi
return Scalar.from_bytes_wrapping(tagged_hash("BIP0458/randomizer", random_input))
+161
View File
@@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
Run test vectors for Schnorr signature half-aggregation.
Usage:
python run_test_vectors.py
Expects CSV files generated by gen_test_vectors.py in vectors/ directory.
"""
import csv
import os
from pathlib import Path
import sys
from typing import List, Tuple
from halfagg import Aggregate, IncAggregate, VerifyAggregate
ROOT = Path(__file__).resolve().parent
VECTORS_DIR = ROOT / "vectors"
AGGREGATE_VECTORS = VECTORS_DIR / 'test_vectors_aggregate.csv'
INCAGGREGATE_VECTORS = VECTORS_DIR / 'test_vectors_incaggregate.csv'
VERIFY_VECTORS = VECTORS_DIR / 'test_vectors_verify.csv'
def parse_hex_list(hex_str: str) -> List[bytes]:
if not hex_str:
return []
return [bytes.fromhex(h) for h in hex_str.split(';')]
def parse_pms(pks_str: str, msgs_str: str, sigs_str: str) -> List[Tuple[bytes, bytes, bytes]]:
pks = parse_hex_list(pks_str)
msgs = parse_hex_list(msgs_str)
sigs = parse_hex_list(sigs_str)
if not pks:
return []
return list(zip(pks, msgs, sigs))
def parse_pm(pks_str: str, msgs_str: str) -> List[Tuple[bytes, bytes]]:
pks = parse_hex_list(pks_str)
msgs = parse_hex_list(msgs_str)
if not pks:
return []
return list(zip(pks, msgs))
def run_aggregate_tests() -> List[str]:
"""Run Aggregate test vectors. Returns list of failed test descriptions."""
failures = []
with open(AGGREGATE_VECTORS, newline='', encoding='utf-8') as f:
reader = csv.reader(f)
next(reader)
for row in reader:
index, pks_str, msgs_str, sigs_str, expected_result, expected_aggsig, comment = row
pms = parse_pms(pks_str, msgs_str, sigs_str)
if expected_result == "FALSE":
try:
Aggregate(pms)
failures.append(f"Aggregate #{index}: {comment}")
except ValueError:
pass
else:
try:
actual = Aggregate(pms)
if actual != bytes.fromhex(expected_aggsig):
failures.append(f"Aggregate #{index}: {comment}")
except ValueError:
failures.append(f"Aggregate #{index}: {comment}")
return failures
def run_incaggregate_tests() -> List[str]:
"""Run IncAggregate test vectors. Returns list of failed test descriptions."""
failures = []
with open(INCAGGREGATE_VECTORS, newline='', encoding='utf-8') as f:
reader = csv.reader(f)
next(reader)
for row in reader:
(index, aggsig_str, pm_pks_str, pm_msgs_str,
pms_pks_str, pms_msgs_str, pms_sigs_str,
expected_result, expected_aggsig, comment) = row
aggsig = bytes.fromhex(aggsig_str) if aggsig_str else bytes()
pm_aggd = parse_pm(pm_pks_str, pm_msgs_str)
pms_to_agg = parse_pms(pms_pks_str, pms_msgs_str, pms_sigs_str)
if expected_result == "FALSE":
try:
IncAggregate(aggsig, pm_aggd, pms_to_agg)
failures.append(f"IncAggregate #{index}: {comment}")
except ValueError:
pass
else:
try:
actual = IncAggregate(aggsig, pm_aggd, pms_to_agg)
if actual != bytes.fromhex(expected_aggsig):
failures.append(f"IncAggregate #{index}: {comment}")
except ValueError:
failures.append(f"IncAggregate #{index}: {comment}")
return failures
def run_verify_tests() -> List[str]:
"""Run VerifyAggregate test vectors. Returns list of failed test descriptions."""
failures = []
with open(VERIFY_VECTORS, newline='', encoding='utf-8') as f:
reader = csv.reader(f)
next(reader)
for row in reader:
index, aggsig_str, pks_str, msgs_str, expected_result, comment = row
aggsig = bytes.fromhex(aggsig_str)
pm = parse_pm(pks_str, msgs_str)
try:
result = VerifyAggregate(aggsig, pm)
expected = (expected_result == "TRUE")
if result != expected:
failures.append(f"VerifyAggregate #{index}: {comment}")
except ValueError:
if expected_result == "TRUE":
failures.append(f"VerifyAggregate #{index}: {comment}")
return failures
def main():
for filename in [AGGREGATE_VECTORS, INCAGGREGATE_VECTORS, VERIFY_VECTORS]:
if not os.path.exists(filename):
print(f"ERROR: Missing {filename}. Run gen_test_vectors.py first.")
sys.exit(1)
failures = []
failures.extend(run_aggregate_tests())
failures.extend(run_incaggregate_tests())
failures.extend(run_verify_tests())
if failures:
print("FAILED:")
for f in failures:
print(f" {f}")
sys.exit(1)
else:
print("All tests have passed.")
sys.exit(0)
if __name__ == "__main__":
main()
@@ -0,0 +1,16 @@
index,pubkeys,messages,signatures,expected_result,expected_aggsig,comment
0,,,,TRUE,0000000000000000000000000000000000000000000000000000000000000000,Empty signature list
1,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,Single signature
2,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766,0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F;C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F51B04478B464890B540F1BBE15E5E5965A348F2B3D09BD4021D9E1F67D9D774,Two signatures
3,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F;C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57;F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F923A17EA0CD56ED3A824AA14BC67839CED465BA5269700F600EDFC3DAAC8A6B069,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92B7A5E19F2F438EAE2D3D9630B4D69921A7E920CBF6E54585261B21D7E1395493,Three signatures
4,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;62C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F7,0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404;0505050505050505050505050505050505050505050505050505050505050505;0606060606060606060606060606060606060606060606060606060606060606,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F;C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57;F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F923A17EA0CD56ED3A824AA14BC67839CED465BA5269700F600EDFC3DAAC8A6B069;A3AFBDB45A6A34BF7C8C00F1B6D7E7D375B54540F13716C87B62E51E2F4F22FF3E57DF859581383FA53C564BC72EC8A60145C502E91B0B508A6F1BD7961FC45D;C1319F3743A9B8A5A30408FA90E96B3A8BB3FD4FF5B790935C465DDC68859B2935B87C950EB399CCCD956D5BE6327B164D4C4080671A6B2CB39FD6A755C5987E,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92A3AFBDB45A6A34BF7C8C00F1B6D7E7D375B54540F13716C87B62E51E2F4F22FFC1319F3743A9B8A5A30408FA90E96B3A8BB3FD4FF5B790935C465DDC68859B2917D482DF65A9338B797D020D0FC24439F80BBBAFAE0C01FE9C60E6BB6C1E9BFE,Five signatures
5,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766,0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA83B5F55690F0A13C12CBC1B569D10E3D919CE0E799D4AD29CEF1AE51DA64F9B57;C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F51B04478B464890B540F1BBE15E5E5965A348F2B3D09BD4021D9E1F67D9D774,"Strange aggregation - invalid individual sigs, valid aggregate"
6,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,,Signature s = n (at boundary)
7,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364529,FALSE,,Signature s > n
8,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FALSE,,Signature s = 2^256 - 1
9,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD07,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,FALSE,,Public key too short
10,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,020202020202020202020202020202020202020202020202020202020202020202,80B68E6A009D95B162880A6942C2308A04F6E75037EF10646F826FF4F658A9B139DE000C1E00C3B70E0490D6A034EB25090761400B08DE7A92390D5B3B2220B6,FALSE,,Message too long
11,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C22,FALSE,,Signature too short
12,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F00,FALSE,,Signature too long
13,021B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,FALSE,,Public key too long
14,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,02020202020202020202020202020202020202020202020202020202020202,BBE3DE7700230815FC246C1001266C4730A272B118B5651F2BFB656BA27930332DD2503A5B9CEDA204DF1636E0E578142211FFC98497B49C769A33989889B740,FALSE,,Message too short
1 index pubkeys messages signatures expected_result expected_aggsig comment
2 0 TRUE 0000000000000000000000000000000000000000000000000000000000000000 Empty signature list
3 1 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F Single signature
4 2 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766 0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F;C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57 TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F51B04478B464890B540F1BBE15E5E5965A348F2B3D09BD4021D9E1F67D9D774 Two signatures
5 3 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337 0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F;C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57;F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F923A17EA0CD56ED3A824AA14BC67839CED465BA5269700F600EDFC3DAAC8A6B069 TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92B7A5E19F2F438EAE2D3D9630B4D69921A7E920CBF6E54585261B21D7E1395493 Three signatures
6 4 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;62C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F7 0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404;0505050505050505050505050505050505050505050505050505050505050505;0606060606060606060606060606060606060606060606060606060606060606 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F;C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57;F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F923A17EA0CD56ED3A824AA14BC67839CED465BA5269700F600EDFC3DAAC8A6B069;A3AFBDB45A6A34BF7C8C00F1B6D7E7D375B54540F13716C87B62E51E2F4F22FF3E57DF859581383FA53C564BC72EC8A60145C502E91B0B508A6F1BD7961FC45D;C1319F3743A9B8A5A30408FA90E96B3A8BB3FD4FF5B790935C465DDC68859B2935B87C950EB399CCCD956D5BE6327B164D4C4080671A6B2CB39FD6A755C5987E TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92A3AFBDB45A6A34BF7C8C00F1B6D7E7D375B54540F13716C87B62E51E2F4F22FFC1319F3743A9B8A5A30408FA90E96B3A8BB3FD4FF5B790935C465DDC68859B2917D482DF65A9338B797D020D0FC24439F80BBBAFAE0C01FE9C60E6BB6C1E9BFE Five signatures
7 5 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766 0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA83B5F55690F0A13C12CBC1B569D10E3D919CE0E799D4AD29CEF1AE51DA64F9B57;C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0 TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F51B04478B464890B540F1BBE15E5E5965A348F2B3D09BD4021D9E1F67D9D774 Strange aggregation - invalid individual sigs, valid aggregate
8 6 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 FALSE Signature s = n (at boundary)
9 7 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364529 FALSE Signature s > n
10 8 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FALSE Signature s = 2^256 - 1
11 9 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD07 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F FALSE Public key too short
12 10 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 020202020202020202020202020202020202020202020202020202020202020202 80B68E6A009D95B162880A6942C2308A04F6E75037EF10646F826FF4F658A9B139DE000C1E00C3B70E0490D6A034EB25090761400B08DE7A92390D5B3B2220B6 FALSE Message too long
13 11 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C22 FALSE Signature too short
14 12 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F00 FALSE Signature too long
15 13 021B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F FALSE Public key too long
16 14 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 02020202020202020202020202020202020202020202020202020202020202 BBE3DE7700230815FC246C1001266C4730A272B118B5651F2BFB656BA27930332DD2503A5B9CEDA204DF1636E0E578142211FFC98497B49C769A33989889B740 FALSE Message too short
@@ -0,0 +1,13 @@
index,aggsig,pm_aggd_pubkeys,pm_aggd_messages,pms_pubkeys,pms_messages,pms_signatures,expected_result,expected_aggsig,comment
0,0000000000000000000000000000000000000000000000000000000000000000,,,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,Add single signature to empty aggregate
1,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766,0303030303030303030303030303030303030303030303030303030303030303,C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F51B04478B464890B540F1BBE15E5E5965A348F2B3D09BD4021D9E1F67D9D774,Add second signature to single-sig aggregate
2,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404,C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57;F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F923A17EA0CD56ED3A824AA14BC67839CED465BA5269700F600EDFC3DAAC8A6B069,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92B7A5E19F2F438EAE2D3D9630B4D69921A7E920CBF6E54585261B21D7E1395493,Add two signatures at once
3,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,,,,TRUE,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,Add no new signatures (unchanged)
4,00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,,,,,,FALSE,,Aggregate signature wrong length
5,,,,,,,FALSE,,Aggregate signature empty
6,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,,,,,,FALSE,,Existing aggregate has s = n
7,0000000000000000000000000000000000000000000000000000000000000000,,,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,,New signature has s = n
8,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD07,0202020202020202020202020202020202020202020202020202020202020202,4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766,0303030303030303030303030303030303030303030303030303030303030303,C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57,FALSE,,Existing public key too short
9,80B68E6A009D95B162880A6942C2308A04F6E75037EF10646F826FF4F658A9B139DE000C1E00C3B70E0490D6A034EB25090761400B08DE7A92390D5B3B2220B6,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,020202020202020202020202020202020202020202020202020202020202020202,4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766,0303030303030303030303030303030303030303030303030303030303030303,C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57,FALSE,,Existing message too long
10,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,021B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766,0303030303030303030303030303030303030303030303030303030303030303,C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57,FALSE,,Existing public key too long
11,BBE3DE7700230815FC246C1001266C4730A272B118B5651F2BFB656BA27930332DD2503A5B9CEDA204DF1636E0E578142211FFC98497B49C769A33989889B740,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,02020202020202020202020202020202020202020202020202020202020202,4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766,0303030303030303030303030303030303030303030303030303030303030303,C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57,FALSE,,Existing message too short
1 index aggsig pm_aggd_pubkeys pm_aggd_messages pms_pubkeys pms_messages pms_signatures expected_result expected_aggsig comment
2 0 0000000000000000000000000000000000000000000000000000000000000000 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F Add single signature to empty aggregate
3 1 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766 0303030303030303030303030303030303030303030303030303030303030303 C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57 TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F51B04478B464890B540F1BBE15E5E5965A348F2B3D09BD4021D9E1F67D9D774 Add second signature to single-sig aggregate
4 2 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337 0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404 C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57;F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F923A17EA0CD56ED3A824AA14BC67839CED465BA5269700F600EDFC3DAAC8A6B069 TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92B7A5E19F2F438EAE2D3D9630B4D69921A7E920CBF6E54585261B21D7E1395493 Add two signatures at once
5 3 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 TRUE B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F Add no new signatures (unchanged)
6 4 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 FALSE Aggregate signature wrong length
7 5 FALSE Aggregate signature empty
8 6 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 FALSE Existing aggregate has s = n
9 7 0000000000000000000000000000000000000000000000000000000000000000 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 FALSE New signature has s = n
10 8 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD07 0202020202020202020202020202020202020202020202020202020202020202 4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766 0303030303030303030303030303030303030303030303030303030303030303 C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57 FALSE Existing public key too short
11 9 80B68E6A009D95B162880A6942C2308A04F6E75037EF10646F826FF4F658A9B139DE000C1E00C3B70E0490D6A034EB25090761400B08DE7A92390D5B3B2220B6 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 020202020202020202020202020202020202020202020202020202020202020202 4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766 0303030303030303030303030303030303030303030303030303030303030303 C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57 FALSE Existing message too long
12 10 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 021B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766 0303030303030303030303030303030303030303030303030303030303030303 C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57 FALSE Existing public key too long
13 11 BBE3DE7700230815FC246C1001266C4730A272B118B5651F2BFB656BA27930332DD2503A5B9CEDA204DF1636E0E578142211FFC98497B49C769A33989889B740 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 02020202020202020202020202020202020202020202020202020202020202 4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766 0303030303030303030303030303030303030303030303030303030303030303 C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9233428AEB77468852520B0256DE84562A29629194DAEAC45444C7711CD379E57 FALSE Existing message too short
+27
View File
@@ -0,0 +1,27 @@
index,aggsig,pubkeys,messages,expected_result,comment
0,0000000000000000000000000000000000000000000000000000000000000000,,,TRUE,Empty signature list
1,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,TRUE,Single signature
2,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8A3AFBDB45A6A34BF7C8C00F1B6D7E7D375B54540F13716C87B62E51E2F4F22FFAC87D631EE4A69E8D25F1977AFB86DD3A8FD3BEA9116F25A0CB523C25BCB5A41,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,0202020202020202020202020202020202020202020202020202020202020202;0505050505050505050505050505050505050505050505050505050505050505,TRUE,Two signatures
3,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92B7A5E19F2F438EAE2D3D9630B4D69921A7E920CBF6E54585261B21D7E1395493,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404,TRUE,Three signatures
4,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92A3AFBDB45A6A34BF7C8C00F1B6D7E7D375B54540F13716C87B62E51E2F4F22FFC1319F3743A9B8A5A30408FA90E96B3A8BB3FD4FF5B790935C465DDC68859B2917D482DF65A9338B797D020D0FC24439F80BBBAFAE0C01FE9C60E6BB6C1E9BFE,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;62C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F7,0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404;0505050505050505050505050505050505050505050505050505050505050505;0606060606060606060606060606060606060606060606060606060606060606,TRUE,Five signatures
5,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Public key not on curve
6,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,0000000000000000000000000000000000000000000000000000000000000000,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Public key is zero
7,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Public key >= field size
8,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D0E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,R value not on curve
9,00000000000000000000000000000000000000000000000000000000000000000E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,R value is zero
10,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC930E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,R value >= field size
11,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Aggregate s = n
12,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364529,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Aggregate s > n
13,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Aggregate s = 2^256 - 1
14,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FALSE,Wrong message
15,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Wrong public key
16,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C2260,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Tampered signature (bit flip)
17,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F51B04478B464890B540F1BBE15E5E5965A348F2B3D09BD4021D9E1F67D9D774,4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0303030303030303030303030303030303030303030303030303030303030303;0202020202020202020202020202020202020202020202020202020202020202,FALSE,Swapped order
18,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,"s = n-1 (valid range, wrong sig)"
19,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80000000000000000000000000000000000000000000000000000000000000000,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,"s = 0 (valid range, wrong sig)"
20,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4A,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Aggregate signature too short
21,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F0000000000000000000000000000000000000000000000000000000000000000,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Aggregate signature too long
22,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD07,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Public key too short
23,80B68E6A009D95B162880A6942C2308A04F6E75037EF10646F826FF4F658A9B139DE000C1E00C3B70E0490D6A034EB25090761400B08DE7A92390D5B3B2220B6,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,020202020202020202020202020202020202020202020202020202020202020202,FALSE,Message too long
24,B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F,021B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0202020202020202020202020202020202020202020202020202020202020202,FALSE,Public key too long
25,BBE3DE7700230815FC246C1001266C4730A272B118B5651F2BFB656BA27930332DD2503A5B9CEDA204DF1636E0E578142211FFC98497B49C769A33989889B740,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,02020202020202020202020202020202020202020202020202020202020202,FALSE,Message too short
1 index aggsig pubkeys messages expected_result comment
2 0 0000000000000000000000000000000000000000000000000000000000000000 TRUE Empty signature list
3 1 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 TRUE Single signature
4 2 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8A3AFBDB45A6A34BF7C8C00F1B6D7E7D375B54540F13716C87B62E51E2F4F22FFAC87D631EE4A69E8D25F1977AFB86DD3A8FD3BEA9116F25A0CB523C25BCB5A41 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 0202020202020202020202020202020202020202020202020202020202020202;0505050505050505050505050505050505050505050505050505050505050505 TRUE Two signatures
5 3 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92B7A5E19F2F438EAE2D3D9630B4D69921A7E920CBF6E54585261B21D7E1395493 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337 0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404 TRUE Three signatures
6 4 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F62B58B1908197B6D732F0564AD2AD5C6E438D24C65AF758DA9ACCCCCF5B3F92A3AFBDB45A6A34BF7C8C00F1B6D7E7D375B54540F13716C87B62E51E2F4F22FFC1319F3743A9B8A5A30408FA90E96B3A8BB3FD4FF5B790935C465DDC68859B2917D482DF65A9338B797D020D0FC24439F80BBBAFAE0C01FE9C60E6BB6C1E9BFE 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;62C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F7 0202020202020202020202020202020202020202020202020202020202020202;0303030303030303030303030303030303030303030303030303030303030303;0404040404040404040404040404040404040404040404040404040404040404;0505050505050505050505050505050505050505050505050505050505050505;0606060606060606060606060606060606060606060606060606060606060606 TRUE Five signatures
7 5 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D 0202020202020202020202020202020202020202020202020202020202020202 FALSE Public key not on curve
8 6 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 0000000000000000000000000000000000000000000000000000000000000000 0202020202020202020202020202020202020202020202020202020202020202 FALSE Public key is zero
9 7 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F 0202020202020202020202020202020202020202020202020202020202020202 FALSE Public key >= field size
10 8 4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D0E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE R value not on curve
11 9 00000000000000000000000000000000000000000000000000000000000000000E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE R value is zero
12 10 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC930E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE R value >= field size
13 11 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE Aggregate s = n
14 12 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364529 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE Aggregate s > n
15 13 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE Aggregate s = 2^256 - 1
16 14 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FALSE Wrong message
17 15 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766 0202020202020202020202020202020202020202020202020202020202020202 FALSE Wrong public key
18 16 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C2260 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE Tampered signature (bit flip)
19 17 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8C9D9B1CCE8C0A99AA79F56185358CD82B7649D6132DAACB31612C7C9B14CE3A9F51B04478B464890B540F1BBE15E5E5965A348F2B3D09BD4021D9E1F67D9D774 4D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766;1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0303030303030303030303030303030303030303030303030303030303030303;0202020202020202020202020202020202020202020202020202020202020202 FALSE Swapped order
20 18 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE s = n-1 (valid range, wrong sig)
21 19 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80000000000000000000000000000000000000000000000000000000000000000 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE s = 0 (valid range, wrong sig)
22 20 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4A 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE Aggregate signature too short
23 21 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F0000000000000000000000000000000000000000000000000000000000000000 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE Aggregate signature too long
24 22 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD07 0202020202020202020202020202020202020202020202020202020202020202 FALSE Public key too short
25 23 80B68E6A009D95B162880A6942C2308A04F6E75037EF10646F826FF4F658A9B139DE000C1E00C3B70E0490D6A034EB25090761400B08DE7A92390D5B3B2220B6 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 020202020202020202020202020202020202020202020202020202020202020202 FALSE Message too long
26 24 B070AAFCEA439A4F6F1BBFC2EB66D29D24B0CAB74D6B745C3CFB009CC8FE4AA80E066C34819936549FF49B6FD4D41EDFC401A367B87DDD59FEE38177961C225F 021B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 0202020202020202020202020202020202020202020202020202020202020202 FALSE Public key too long
27 25 BBE3DE7700230815FC246C1001266C4730A272B118B5651F2BFB656BA27930332DD2503A5B9CEDA204DF1636E0E578142211FFC98497B49C769A33989889B740 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 02020202020202020202020202020202020202020202020202020202020202 FALSE Message too short