BIP459: Add DahLIAS fully aggregated signatures for secp256k1

This commit is contained in:
Fabian Jahr committed 2026-11-03 21:39:57 +09:00
1 parent bd478d5087
commit f3d1668891
11 files changed
+1632

No files matched your search

+435
View File
@@ -0,0 +1,435 @@
<pre>
BIP: 459
Layer: Applications
Title: DahLIAS fully aggregated signatures for secp256k1
Authors: Fabian Jahr <fjahr@protonmail.com>
Status: Draft
Type: Specification
Assigned: 2026-07-10
License: CC0-1.0 OR MIT
Discussion: 2025-04-17: https://groups.google.com/g/bitcoindev/c/eothFkxAvK0/m/IZVGezcdDAAJ
2026-07-06: https://groups.google.com/g/bitcoindev/c/TF5mPfy58RQ/m/lvSC2FdYAAAJ
Requires: 340
</pre>
== Introduction ==
=== Abstract ===
This document describes ''full-aggregation'', a standard for Schnorr-style aggregate signatures based on the DahLIAS<ref>Jonas Nick, Tim Ruffing, Yannick Seurin: ''DahLIAS: Discrete Logarithm-Based Interactive Aggregate Signatures''. Cryptology ePrint Archive, Report 2025/692. https://eprint.iacr.org/2025/692</ref> interactive aggregate signature scheme.
Full-aggregation is an interactive process in which a group of signers produces a single aggregate signature for a collection of public key and message pairs.
The resulting aggregate signature has the same size as a single BIP 340 signature (64 bytes), regardless of the number of signers.
=== Copyright ===
This document, along with the [[bip-0459/reference.py|reference implementation]] and [[bip-0459/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-0459/secp256k1lab/|bip-0459/secp256k1lab]] is licensed under the MIT License only, as stated in its [[bip-0459/secp256k1lab/COPYING|COPYING]] file.
=== Motivation ===
Full-aggregation applies whenever a verifier must check multiple signatures.
The signers jointly produce a single constant-size aggregate signature, and verifying the aggregate gives the same assurance as verifying an individual signature for each public key and message pair.
The primary purpose is to reduce the amount of data sent to the verifier.
While ''n'' BIP 340 signatures are ''64*n'' bytes, a fully aggregated signature is always 64 bytes.
Verifying the aggregate is also faster than verifying each signature separately.
One application is cross-input signature aggregation (CISA) within the Bitcoin consensus protocol, which reduces the size and verification cost of multi-input transactions.
Deploying CISA requires a consensus change that this document does not specify.
This document only specifies the signature scheme and consensus integration is left to a future proposal.
Full-aggregation is interactive.
It requires cooperation among all signers, following a two-round protocol whose first round can be preprocessed before the messages to be signed are known.
This makes full-aggregation unsuitable for scenarios where signers cannot coordinate.
However, when all input owners already cooperate to construct and sign a transaction, the interaction is not a significant burden.
Historical average transactions would see an [https://hrf.org/latest/cisa-research-paper/ average size reduction of 20.5% and a weight reduction of 7.3%].
Full-aggregation is particularly beneficial for large transactions such as CoinJoins, PayJoins and consolidations, because the savings grow with the number of inputs.
For example, a hypothetical CoinJoin of 300 inputs and 300 outputs would see a size reduction of 42.7% and a weight reduction of 15.9%.
A typical consolidation transaction of 150 inputs and one output would see a weight reduction of 27.4%.
CISA with full-aggregation can even reduce the cost of privacy since participating in CoinJoin and PayJoin transactions can become cheaper than sending individual transactions.
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 for a single message, full-aggregation does not combine the signers' keys.
Each signer signs their own message under their own public key, and the aggregate signature is verified against the list of individual public key and message pairs.
== Overview ==
Implementers must make sure to understand this section thoroughly to avoid subtle mistakes that may lead to catastrophic failure.
=== General Signing Flow ===
The signing protocol involves ''u'' signers, each with their own secret key, public key, and message, and a ''coordinator'' who aggregates their contributions.
The basic order of operations to create a fully aggregated signature is as follows:
'''First broadcast round:'''
The signers start the signing session by running ''NonceGen'' to compute ''secnonce'' and ''pubnonce''.<ref>We treat the ''secnonce'' and ''pubnonce'' as grammatically singular even though they include serializations of two scalars and two elliptic curve points, respectively.</ref>
Then, the signers send their ''pubnonce'' to the coordinator.
The coordinator runs ''NonceAgg'' to compute an aggregate nonce ''aggnonce'' and sends it back to the signers, along with the ordered list of all ''(pk, m, pubnonce)'' triples.
'''Second broadcast round:'''
At this point, every signer has the required data to sign.
Every signer computes a partial signature by running ''Sign'' with their secret key, their ''secnonce'', the message they intend to sign, and the session parameters.
Then, the signers send their partial signatures to the coordinator, who runs ''SigAgg'' to obtain the final 64-byte aggregate signature.
If all signers behaved honestly, the result passes ''Verify''.
If the aggregate signature is invalid, the coordinator can run ''PartialSigVerify'' on each partial signature to identify the disruptive signer.
'''IMPORTANT''': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''.
Otherwise, it is possible to extract the signer's secret key from the partial signatures output by three executions of ''Sign'' in different sessions (see [[#nonce-generation|Nonce Generation]]).
To avoid accidental reuse of ''secnonce'', an implementation may securely erase the ''secnonce'' argument by overwriting it with 64 zero bytes after it has been read by ''Sign''.
A ''secnonce'' consisting of only zero bytes is invalid for ''Sign'' and will cause it to fail.
To simplify the specification of the algorithms, some intermediary values are unnecessarily recomputed from scratch, e.g., when executing ''GetSessionValues'' multiple times.
Actual implementations can cache these values.
As a result, the session parameters may look different in implementations or may not exist as an explicit data structure.
However, computation of ''GetSessionValues'' and storage of the result must be protected against modification from an untrusted third party.
=== Nonce Generation ===
'''IMPORTANT''': ''NonceGen'' must have access to a high-quality random generator to draw an unbiased, uniformly random value ''rand' ''.
In contrast to BIP 340 signing, the nonce values '''must not be derived deterministically''' from the signing key and message because otherwise active adversaries can trick the victim into reusing a nonce.<ref>An attacker who participates as a co-signer can run multiple signing sessions with the victim for the same message while varying its own nonce contributions. With deterministic derivation the victim reuses its secret nonces across these sessions while the aggregate nonce and therefore the nonce coefficient ''b'' and the challenge change. Three such sessions yield a system of linear equations that reveals the victim's secret key. Deriving the nonces from additional session data does not prevent this since the other signers' nonces are unknown to the victim when its nonces are generated. This threat is not limited to sessions with malicious co-signers. With deterministic derivation, the nonce is reused whenever the same key and message are signed again in a session whose other public keys, messages, or nonces have changed. Any party who observes the resulting partial signatures, such as a coordinator, can then extract the secret key after three such sessions. Re-signing the same message in a changed session can arise naturally, for example when a transaction signed with ''SIGHASH_ANYONECANPAY'' is modified for a fee bump and signed again.</ref>
The optional arguments to ''NonceGen'' enable a defense-in-depth mechanism that may prevent secret key exposure if ''rand' '' is accidentally not drawn uniformly at random.
If the value ''rand' '' was identical in two ''NonceGen'' invocations but any other argument was different, the ''secnonce'' would still be different, avoiding accidental nonce reuse.
Therefore, it is recommended to provide the optional argument ''sk'' if available during nonce generation.
The auxiliary input ''extra_in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs, e.g., a session identifier or a counter.
However, the protection provided by the optional arguments should only be viewed as a last resort.
In some applications, it is beneficial to generate and send a ''pubnonce'' before the other signers, their public keys, or the messages to sign are known.
In this case, the ''NonceGen'' algorithm is run during a preprocessing phase, and the ''Sign'' algorithm can be run immediately when the messages and set of signers are determined.
This way, the final signature is created quicker and with fewer round trips.
However, applications that use this method presumably store the nonces for a longer time and must therefore be even more careful not to reuse them.
In general, signers are stateful in the sense that they first generate ''secnonce'' and then need to store it until they receive the coordinator's response containing the aggregate nonce and session parameters.
However, it is possible for one of the signers to be stateless: this signer waits until it receives the ''pubnonce'' of all other signers and until session parameters are determined.
Then, the signer can run ''NonceGen'', ''NonceAgg'', and ''Sign'' in sequence and send out its ''pubnonce'' along with its partial signature.
=== Role of the Coordinator ===
Instead of every signer sending their nonce and partial signature to every other signer, it is possible to use an untrusted ''coordinator'' to reduce the communication complexity from quadratic to linear in the number of signers.
In each of the two rounds, the coordinator collects all signers' contributions (nonces or partial signatures), processes them, and sends the results back to the signers.
A malicious coordinator can force the signing session to fail to produce a valid aggregate signature but cannot negatively affect the unforgeability of the scheme, i.e., even a malicious coordinator colluding with all but one signer cannot forge a signature.
If there is no dedicated coordinator, one of the signers can act as the coordinator.
Unlike BIP 327 where the coordinator only needs to send the aggregate nonce back to signers, the coordinator in this scheme must also send each signer the individual public nonces (specifically, the ''R<sub>2</sub>'' components) of all signers.
This is because each signer must verify that their own ''R<sub>2</sub>'' value appears exactly once in the session context (see [[#sign|Sign]]).
In many applications, the signers already know the list of public keys and messages before the signing session starts and only the ''aggnonce'' and individual public nonces need to be communicated.
=== List Ordering ===
The aggregate signature depends on the order of the list of public key and message pairs.
A signature valid for a list ''L'' will not be valid for any non-trivial permutation of ''L'' (except with negligible probability).
If an application does not have a natural ordering of signers, the list should be sorted using a predetermined order before being provided to the coordinator, and the verifier must use the same order.
== 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 ≥ 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 ''xbytes(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''bytes(x(P))''.
** The function ''cbytes(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns the 33-byte encoding consisting of the single byte ''0x02'' if ''has_even_y(P)'', or ''0x03'' otherwise, prepended to ''bytes(x(P))''.
** 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'' and ''has_even_y(P)'', or fails if ''x ≥ p'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode:
*** Fail if ''x ≥ p''.
*** Let ''c = x<sup>3</sup> + 7 mod p''.
*** Let ''y = c<sup>(p+1)/4</sup> mod p''.
*** Fail if ''c ≠ 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 ''cpoint(x)'', where ''x'' is a 33-byte array, sets ''P'' to the point on the curve whose X coordinate is ''int(x[1:33])''. Fail if ''x[0]'' is not ''0x02'' or ''0x03'', if ''int(x[1:33]) ≥ p'', or if no point with that X coordinate exists on the curve. If ''x[0] = 0x02'', return the point with even y; if ''x[0] = 0x03'', return the point with odd y.
** 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 [[bip-0459/reference.py|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)''
|-
| ''cpoint(x)'' || ''GE.from_bytes_compressed(x)''
|-
| ''int(x)'', failing if ''&ge; n'' || ''Scalar.from_bytes_checked(x)''
|-
| ''int(x) mod n'' || ''Scalar.from_bytes_wrapping(x)''
|-
| ''xbytes(P)'' || ''P.to_bytes_xonly()''
|-
| ''cbytes(P)'' || ''P.to_bytes_compressed()''
|-
| ''bytes(s)'' || ''s.to_bytes()''
|-
| ''has_even_y(P)'' || ''P.has_even_y()''
|-
| ''hash<sub>tag</sub>(x)'' || ''tagged_hash(tag, x)''
|}
=== Algorithms ===
The following specification of the algorithms has been written with a focus on clarity.
As a result, the specified algorithms are not always optimal in terms of computation and space.
In particular, some values are recomputed but can be cached in actual implementations (see [[#general-signing-flow|General Signing Flow]]).
Unless noted otherwise, any of the ''pk<sub>0..u-1</sub>'', ''m<sub>0..u-1</sub>'', ''pubnonce<sub>0..u-1</sub>'', and ''psig<sub>0..u-1</sub>'' arrays that an algorithm takes have the same length ''u'', with ''u ≥ 1'', and the algorithm fails otherwise.
Each message is a 32-byte array, and an algorithm fails on any message of a different length.
==== NonceGen ====
''NonceGen'' generates a secret nonce and a corresponding public nonce for a single signer.
Input:
* ''sk'': a 32-byte secret key (optional argument)
* ''extra_in'': a byte array (optional argument)
'''''NonceGen(sk, extra_in)'''''<ref>See [[#nonce-generation|Nonce Generation]] in the Overview for important security requirements regarding randomness and nonce reuse.</ref>:
* Let ''rand' '' be a 32-byte array freshly drawn uniformly at random.
* If the optional argument ''sk'' is present:
** Let ''rand'' be the byte-wise xor of ''sk'' and ''hash<sub>BIP0459/aux</sub>(rand')''.
* Else:
** Let ''rand = rand' ''.
* If the optional argument ''extra_in'' is not present:
** Let ''extra_in'' be an empty byte array.
* Let ''r<sub>1</sub> = int(hash<sub>BIP0459/nonce</sub>(rand || extra_in || 0x00)) mod n''; fail if ''r<sub>1</sub> = 0''.
* Let ''r<sub>2</sub> = int(hash<sub>BIP0459/nonce</sub>(rand || extra_in || 0x01)) mod n''; fail if ''r<sub>2</sub> = 0''.
* Let ''R<sub>1</sub> = r<sub>1</sub>⋅G''.
* Let ''R<sub>2</sub> = r<sub>2</sub>⋅G''.
* Let ''secnonce = bytes(r<sub>1</sub>) || bytes(r<sub>2</sub>)''.
* Let ''pubnonce = cbytes(R<sub>1</sub>) || cbytes(R<sub>2</sub>)''.
* Return ''(secnonce, pubnonce)''.
==== NonceAgg ====
''NonceAgg'' aggregates all signers' public nonces into a single aggregate nonce.
Input:
* ''pubnonce<sub>0..u-1</sub>'': an array of ''u'' 66-byte public nonces
'''''NonceAgg(pubnonce<sub>0..u-1</sub>)''''':
* For ''i = 0 .. u-1'':
** Let ''R<sub>1,i</sub> = cpoint(pubnonce<sub>i</sub>[0:33])''; fail if that fails.
** Let ''R<sub>2,i</sub> = cpoint(pubnonce<sub>i</sub>[33:66])''; fail if that fails.
* Let ''R<sub>1</sub> = R<sub>1,0</sub> + R<sub>1,1</sub> + ... + R<sub>1,u-1</sub>''.
* Let ''R<sub>2</sub> = R<sub>2,0</sub> + R<sub>2,1</sub> + ... + R<sub>2,u-1</sub>''.
* Fail if ''is_infinite(R<sub>1</sub>)'' or ''is_infinite(R<sub>2</sub>)''.
* Let ''aggnonce = cbytes(R<sub>1</sub>) || cbytes(R<sub>2</sub>)''.
* Return ''aggnonce''.
==== GetSessionValues ====
''GetSessionValues'' is a helper function that computes the derived values shared by ''Sign'', ''SigAgg'', and ''PartialSigVerify''.
Input:
* ''aggnonce'': a 66-byte aggregate nonce
* ''pk<sub>0..u-1</sub>'': an array of ''u'' 32-byte public keys
* ''m<sub>0..u-1</sub>'': an array of ''u'' 32-byte messages
* ''pubnonce<sub>0..u-1</sub>'': an array of ''u'' 66-byte public nonces
'''''GetSessionValues(aggnonce, pk<sub>0..u-1</sub>, m<sub>0..u-1</sub>, pubnonce<sub>0..u-1</sub>)''''':
* Let ''R<sub>1</sub> = cpoint(aggnonce[0:33])''; fail if that fails.
* Let ''R<sub>2</sub> = cpoint(aggnonce[33:66])''; fail if that fails.
* For ''i = 0 .. u-1'':
** Let ''R<sub>2,i</sub> = cpoint(pubnonce<sub>i</sub>[33:66])''; fail if that fails.
* Let ''nonce_data = cbytes(R<sub>1</sub>) || cbytes(R<sub>2</sub>)''
* For ''i = 0 .. u-1'':
** Let ''nonce_data = nonce_data || pk<sub>i</sub> || m<sub>i</sub> || cbytes(R<sub>2,i</sub>)''
* Let ''b = int(hash<sub>BIP0459/noncecoef</sub>(nonce_data)) mod n''
* Let ''R = R<sub>1</sub> + b⋅R<sub>2</sub>''
* Fail if ''is_infinite(R)''.
* Return ''(R, b)''
==== Sign ====
''Sign'' produces a partial signature for a signer.
Only the signer's own entry in the list of public key and message pairs is checked, but the resulting aggregate signature is valid for exactly this list.
Any condition the caller requires of the other entries, such as the list matching one agreed upon before the session, must therefore be checked by the caller before invoking ''Sign''.
Input:
* ''secnonce'': a 64-byte secret nonce
* ''sk'': a 32-byte secret key
* ''m'': the 32-byte message this signer intends to sign
* ''aggnonce'': a 66-byte aggregate nonce
* ''pk<sub>0..u-1</sub>'': an array of ''u'' 32-byte public keys
* ''m<sub>0..u-1</sub>'': an array of ''u'' 32-byte messages
* ''pubnonce<sub>0..u-1</sub>'': an array of ''u'' 66-byte public nonces
'''''Sign(secnonce, sk, m, aggnonce, pk<sub>0..u-1</sub>, m<sub>0..u-1</sub>, pubnonce<sub>0..u-1</sub>)''''':
* Let ''r<sub>1</sub> = int(secnonce[0:32])''; fail if ''r<sub>1</sub> = 0'' or ''r<sub>1</sub> ≥ n''.
* Let ''r<sub>2</sub> = int(secnonce[32:64])''; fail if ''r<sub>2</sub> = 0'' or ''r<sub>2</sub> ≥ n''.
* Let ''d = int(sk)''; fail if ''d = 0'' or ''d ≥ n''.
* Let ''P = d⋅G''.
* Let ''R<sub>2</sub> = r<sub>2</sub>⋅G''.
* '''Index lookup and uniqueness check''': Find the unique index ''j'' in ''0..u-1'' such that ''pubnonce<sub>j</sub>[33:66] = cbytes(R<sub>2</sub>)''. Fail if no such index exists or if more than one such index exists.<ref>The uniqueness check applies to the ''R<sub>2</sub>'' value alone. Checking instead that the combination of public key, message, and ''R<sub>2</sub>'' occurs exactly once is not sufficient. If the signer's ''R<sub>2</sub>'' appears at a second index with a different public key or message, ''Sign'' must fail, because signing such a session enables a key extraction attack (see Section 4.2 of the [https://eprint.iacr.org/2025/692 DahLIAS paper]).</ref>
* Fail if ''pk<sub>j</sub> ≠ xbytes(P)'' or ''m<sub>j</sub> ≠ m'', i.e., the public key or message at the signer's position does not match the signer's secret key or intended message.
* Let ''(R, b) = GetSessionValues(aggnonce, pk<sub>0..u-1</sub>, m<sub>0..u-1</sub>, pubnonce<sub>0..u-1</sub>)''; fail if that fails.
* Let ''e = 1'' if ''has_even_y(R)'', otherwise let ''e = n - 1''.
* Let ''d' = d'' if ''has_even_y(P)'', otherwise let ''d' = n - d''.
* Let ''L = pk<sub>0</sub> || m<sub>0</sub> || ... || pk<sub>u-1</sub> || m<sub>u-1</sub>''.
* Let ''c<sub>j</sub> = int(hash<sub>BIP0459/sig</sub>(L || xbytes(R) || pk<sub>j</sub> || m<sub>j</sub>)) mod n''.
* Let ''s<sub>j</sub> = (e⋅(r<sub>1</sub> + b⋅r<sub>2</sub>) + c<sub>j</sub>⋅d') mod n''.
* Let ''psig = bytes(s<sub>j</sub>)''.
* Return ''psig''.
==== SigAgg ====
''SigAgg'' aggregates all partial signatures into a final 64-byte aggregate signature.
Input:
* ''aggnonce'': a 66-byte aggregate nonce
* ''pk<sub>0..u-1</sub>'': an array of ''u'' 32-byte public keys
* ''m<sub>0..u-1</sub>'': an array of ''u'' 32-byte messages
* ''pubnonce<sub>0..u-1</sub>'': an array of ''u'' 66-byte public nonces
* ''psig<sub>0..u-1</sub>'': an array of ''u'' 32-byte partial signatures
'''''SigAgg(aggnonce, pk<sub>0..u-1</sub>, m<sub>0..u-1</sub>, pubnonce<sub>0..u-1</sub>, psig<sub>0..u-1</sub>)''''':
* Let ''(R, b) = GetSessionValues(aggnonce, pk<sub>0..u-1</sub>, m<sub>0..u-1</sub>, pubnonce<sub>0..u-1</sub>)''; fail if that fails.
* For ''i = 0 .. u-1'':
** Let ''s<sub>i</sub> = int(psig<sub>i</sub>)''; fail if ''s<sub>i</sub> ≥ n''.
* Let ''s = (s<sub>0</sub> + s<sub>1</sub> + ... + s<sub>u-1</sub>) mod n''.
* Let ''sig = xbytes(R) || bytes(s)''.
* Return ''sig''.
==== PartialSigVerify ====
''PartialSigVerify'' verifies a single signer's partial signature and is used to identify disruptive signers.
It recomputes the aggregate nonce from the public nonces rather than taking it as an argument, so its result cannot be influenced by the coordinator.
If the public nonces are received over authenticated channels and ''PartialSigVerify'' succeeds for the partial signatures of all ''u'' signers, then ''SigAgg'' called with ''aggnonce = NonceAgg(pubnonce<sub>0..u-1</sub>)'' produces a valid aggregate signature.
Input:
* ''psig'': a 32-byte partial signature
* ''pk<sub>0..u-1</sub>'': an array of ''u'' 32-byte public keys
* ''m<sub>0..u-1</sub>'': an array of ''u'' 32-byte messages
* ''pubnonce<sub>0..u-1</sub>'': an array of ''u'' 66-byte public nonces
* ''signer_index'': the index of the signer in the list (integer in ''0..u-1'')
'''''PartialSigVerify(psig, pk<sub>0..u-1</sub>, m<sub>0..u-1</sub>, pubnonce<sub>0..u-1</sub>, signer_index)''''':
* Let ''i = signer_index''.
* Fail if ''i < 0'' or ''i ≥ u''.
* Let ''s = int(psig)''; fail if ''s ≥ n''.
* Let ''R<sub>1,i</sub> = cpoint(pubnonce<sub>i</sub>[0:33])''; fail if that fails.
* Let ''R<sub>2,i</sub> = cpoint(pubnonce<sub>i</sub>[33:66])''; fail if that fails.
* Let ''P<sub>i</sub> = lift_x(int(pk<sub>i</sub>))''; fail if that fails.
* Let ''aggnonce = NonceAgg(pubnonce<sub>0..u-1</sub>)''; fail if that fails.
* Let ''(R, b) = GetSessionValues(aggnonce, pk<sub>0..u-1</sub>, m<sub>0..u-1</sub>, pubnonce<sub>0..u-1</sub>)''; fail if that fails.
* Let ''e = 1'' if ''has_even_y(R)'', otherwise let ''e = n - 1''.
* Let ''L = pk<sub>0</sub> || m<sub>0</sub> || ... || pk<sub>u-1</sub> || m<sub>u-1</sub>''.
* Let ''c<sub>i</sub> = int(hash<sub>BIP0459/sig</sub>(L || xbytes(R) || pk<sub>i</sub> || m<sub>i</sub>)) mod n''.
* Let ''R'<sub>i</sub> = R<sub>1,i</sub> + b⋅R<sub>2,i</sub>''.
* Fail if ''s⋅G ≠ e⋅R'<sub>i</sub> + c<sub>i</sub>⋅P<sub>i</sub>''.
* Return success iff no failure occurred before reaching this point.
==== Verify ====
''Verify'' verifies a given 64-byte aggregate signature against an ordered list of public key and message pairs.
The inputs to ''Verify'' are the only data required by a verifier.
No nonces or other session data are needed.
Input:
* ''pk<sub>0..u-1</sub>'': an array of ''u'' 32-byte public keys
* ''m<sub>0..u-1</sub>'': an array of ''u'' 32-byte messages
* ''sig'': a 64-byte aggregate signature
'''''Verify(pk<sub>0..u-1</sub>, m<sub>0..u-1</sub>, sig)'''''<ref>An [https://bitcoin.stackexchange.com/a/80702/109853 efficient algorithm for computing a sum of multiple EC multiplications] can significantly speed up verification.</ref>:
* Let ''r = sig[0:32]''.
* Let ''s = int(sig[32:64])''; fail if ''s ≥ n''.
* Let ''R = lift_x(int(r))''; fail if that fails.
* For ''i = 0 .. u-1'':
** Let ''P<sub>i</sub> = lift_x(int(pk<sub>i</sub>))''; fail if that fails.
* Let ''L = pk<sub>0</sub> || m<sub>0</sub> || ... || pk<sub>u-1</sub> || m<sub>u-1</sub>''.
* For ''i = 0 .. u-1'':
** Let ''c<sub>i</sub> = int(hash<sub>BIP0459/sig</sub>(L || r || pk<sub>i</sub> || m<sub>i</sub>)) mod n''.
* Fail if ''s⋅G ≠ R + c<sub>0</sub>⋅P<sub>0</sub> + c<sub>1</sub>⋅P<sub>1</sub> + ... + c<sub>u-1</sub>⋅P<sub>u-1</sub>''.
* Return success iff no failure occurred before reaching this point.
=== Key Tweaking ===
The scheme supports additive key tweaking, allowing signers to use tweaked key pairs without changes to the verification algorithm.
A tweak ''t'' is a 256-bit integer with ''0 ≤ t < 2<sup>256</sup>''.
In both variants below, tweaking fails if ''t ≥ n'' or if ''is_infinite(P')''.
The tweaked public key ''xbytes(P')'' appears in the public key list provided to ''Verify'', and the signer uses ''sk' '' as its secret key in ''Sign''.
'''Plain tweaking''' modifies a key pair ''(sk, P)'' with ''P = sk⋅G'' and is used for BIP 32 unhardened derivation, where ''t'' is the child offset:
* ''sk' = (sk + t) mod n''
* ''P' = P + t⋅G''
'''X-only tweaking''' is used for BIP 341 Taproot key path spending.
Because an X-only public key denotes the point with even Y, the input key is first normalized to even Y.
Let ''d = sk'' if ''has_even_y(P)'', otherwise ''d = n - sk'', so that ''d⋅G = lift_x(x(P))''.
Then:
* ''sk' = (d + t) mod n''
* ''P' = lift_x(x(P)) + t⋅G''
For BIP 341, ''t = int(hash<sub>TapTweak</sub>(xbytes(P) || merkle_root))'' where ''xbytes(P)'' is the X-only internal public key.
The resulting ''P' '' may have odd Y.
== Rationale ==
DahLIAS is the first aggregate signature scheme with constant-size signatures based directly on discrete logarithms in pairing-free groups.
* A fully aggregated signature has the same shape as a standard Schnorr signature: a pair ''(R, s)'' serialized as 64 bytes. The verification algorithm is different from BIP 340 verification and takes a ''list'' of public key and message pairs as input rather than a single pair.
* Every message is exactly 32 bytes. This is the size of a transaction sighash, the intended input for cross-input signature aggregation. Fixing the length also keeps the encoding of the list ''L'' unambiguous, since the challenge binds to the entire list rather than to a single message as in BIP 340 and BIP 327. An application that needs to sign data of a different size should pre-hash it to a 32-byte digest, for example with a tagged hash.
* Since the challenge binds to the entire list, ''L || xbytes(R)'' is a common prefix of every challenge hash input. Verifiers should hash this prefix into a single hash state once and finalize a copy of that state with ''pk<sub>i</sub> || m<sub>i</sub>'' for each ''i'', which keeps the hashing linear in the number of signers instead of quadratic.
* Each signer contributes two nonces rather than one. A single nonce per signer would leave the scheme open to the [https://eprint.iacr.org/2020/945 ROS attack] mounted across many concurrent signing sessions. Deriving the common nonce as ''R = R<sub>1</sub> + b⋅R<sub>2</sub>'', with ''b'' bound to the full session context, prevents this, following MuSig2 ([[bip-0327.mediawiki|BIP 327]]). A variant of the attack that exploits a signer signing multiple messages is in turn prevented by the ''R<sub>2</sub>'' uniqueness and message checks in ''Sign'' (see [[#security|Security]]).
* A single nonce coefficient ''b'' is applied uniformly to all signers' second nonce contributions. This keeps signing cost constant per signer (three group exponentiations) rather than requiring a multi-exponentiation of size ''u''. The trade-off is the need for a uniqueness check on the second nonce component ''R<sub>2</sub>'' during signing.
* The scheme supports additive key tweaking, enabling compatibility with BIP 32 key derivation and BIP 341 Taproot outputs.
* This document does not specify an optimized signing algorithm for a single party that knows all secret keys, e.g., a wallet spending several of its own inputs. Such a party can act as all signers and the coordinator and run the protocol locally. A more efficient method [https://groups.google.com/g/bitcoindev/c/eothFkxAvK0/m/PgRaFPovEQAJ has been outlined] in the mailing list discussion but requires additional security analysis before it can be specified.
A non-interactive alternative is half-aggregation ([[bip-0458.mediawiki|BIP 458]]), which requires no cooperation between the signers but compresses ''n'' signatures to ''32*n + 32'' bytes rather than to a constant 64 bytes.
== Security ==
'''Nonce generation and reuse.''' As detailed in [[#nonce-generation|Nonce Generation]] and [[#general-signing-flow|General Signing Flow]], ''NonceGen'' must draw ''rand' '' from a high-quality random number generator, nonces must not be derived deterministically, and a ''secnonce'' must never be used in more than one ''Sign'' invocation.
Violating any of these can allow an adversary to extract the signer's secret key.
'''Signer verification checks.''' The checks performed by ''Sign'' after recomputing ''R<sub>2</sub>'' are security-critical.
The signer locates its position by finding the unique index ''j'' whose ''R<sub>2</sub>'' matches its own.
These ''R<sub>2</sub>'' values act as ephemeral identifiers that distinguish signers even when they share a public key, and omitting the uniqueness check enables key extraction.
The signer must also confirm that ''both'' the public key and the message at index ''j'' match its own.
Verifying only the public key would let a malicious coordinator substitute a different message at the signer's position.
These checks cover only the signer's own entry, so any requirement on the other entries of the list must be verified by the caller before invoking ''Sign'' (see [[#sign|Sign]]).
'''Partial signatures.''' A partial signature is ''not'' a signature.
By choosing its nonces accordingly, an adversary can produce a partial signature that verifies for any public key.
''PartialSigVerify'' therefore serves only to identify disruptive signers and does not prove that a signer knows the corresponding secret key.
'''Untrusted coordinator.''' The coordinator can cause a signing session to abort but cannot forge a signature, even when colluding with all but one signer (see [[#role-of-the-coordinator|Role of the Coordinator]]).
'''Security model.''' The scheme is proven secure in the concurrent setting, including when signers use key tweaking, under the algebraic one-more discrete logarithm (AOMDL) assumption, the same assumption used for [[bip-0327.mediawiki|BIP 327]].<ref>The precise notion is cosigners-aware existential unforgeability (co-EUF-CMA) as defined in the DahLIAS paper. Each signer sees the entire list of public key and message pairs before finalizing its contribution, and unforgeability guarantees that no valid aggregate signature can attribute to an honest signer a message and cosigner list it never agreed to sign. This all-or-nothing guarantee over the whole list distinguishes interactive aggregate signatures from multisignatures like MuSig2, which produce a signature for a single message under a single aggregate key.</ref>
AOMDL is a falsifiable and weaker variant of the well-studied OMDL problem.
The proof models ''hash<sub>BIP0459/sig</sub>'' as a random oracle and requires only that ''hash<sub>BIP0459/noncecoef</sub>'' be collision-resistant.
The scheme is also strongly binding, meaning it is infeasible to produce a single signature valid for two distinct lists of public key and message pairs.<ref>DahLIAS strong binding additionally requires the verifier to reject any public key equal to the point at infinity. This condition is satisfied implicitly in this specification: public keys are 32-byte X-only values that are decoded with ''lift_x'', which never yields the point at infinity, so no such public key can appear in the list.</ref>
== Backward Compatibility ==
Full-aggregation specifies a new, optional interactive signing protocol for producing an aggregate signature for a list of BIP 340 public key and message pairs.
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.
== Test Vectors and Reference Code ==
A reference implementation in Python is provided in [[bip-0459/reference.py|reference.py]].
Note that the reference implementation depends on a vendored copy of the [https://github.com/secp256k1lab/secp256k1lab secp256k1lab library].
Test vectors are provided in [[bip-0459/vectors/|vectors]] and are generated with a [[bip-0459/gen_test_vectors.py|generator script]].
The reference implementation can be executed with the test vectors by running [[bip-0459/run_test_vectors.py|the run_test_vectors script]].
== Acknowledgements ==
The scheme specified in this document is DahLIAS (Discrete Logarithm-Based Interactive Aggregate Signatures) by Jonas Nick, Tim Ruffing, and Yannick Seurin.
Thanks to waxwing for his feedback on the mailing list, which, among other things, led to clarifying the uniqueness check in ''Sign'' and adding test vectors for it.
Thanks to w0xlt for reviewing and contributing additional test vectors.
== Footnotes ==
<references />
+605
View File
@@ -0,0 +1,605 @@
#!/usr/bin/env python3
"""
Generate test vectors for BIP 459 DahLIAS fully aggregated signatures
for secp256k1.
Usage:
python gen_test_vectors.py
Outputs:
- vectors/test_vectors_nonce_gen.csv (NonceGen with a fixed rand')
- vectors/test_vectors_nonce_agg.csv (NonceAgg: success and failure cases)
- vectors/test_vectors_sign.csv (end-to-end signing with fixed nonces)
- vectors/test_vectors_sign_error.csv (sessions in which Sign must fail)
- vectors/test_vectors_verify.csv (Verify: success and failure cases)
- vectors/test_vectors_partial_sig_verify.csv (PartialSigVerify cases)
- vectors/test_vectors_tweak.csv (plain and X-only key tweaking)
"""
import csv
import os
import sys
from pathlib import Path
from typing import List
sys.path.insert(0, str(Path(__file__).parent / "secp256k1lab/src"))
from secp256k1lab.secp256k1 import FE, G, GE, Scalar
from secp256k1lab.util import bytes_from_int, int_from_bytes, tagged_hash
from reference import (
NonceGenInternal,
NonceAgg,
Sign,
SigAgg,
Verify,
PartialSigVerify,
TweakSK,
TweakPK,
has_even_y,
n,
FULLAGG_TAG_SIG,
)
VECTORS_DIR = Path(__file__).resolve().parent / "vectors"
NONCE_GEN_VECTORS = VECTORS_DIR / "test_vectors_nonce_gen.csv"
NONCE_AGG_VECTORS = VECTORS_DIR / "test_vectors_nonce_agg.csv"
SIGN_VECTORS = VECTORS_DIR / "test_vectors_sign.csv"
SIGN_ERROR_VECTORS = VECTORS_DIR / "test_vectors_sign_error.csv"
VERIFY_VECTORS = VECTORS_DIR / "test_vectors_verify.csv"
PARTIAL_SIG_VERIFY_VECTORS = VECTORS_DIR / "test_vectors_partial_sig_verify.csv"
TWEAK_VECTORS = VECTORS_DIR / "test_vectors_tweak.csv"
def scalar_from_byte(b: int) -> Scalar:
"""Scalar whose 32-byte big-endian encoding is bytes([b]*32). Safe for 0x01..0xee."""
assert 1 <= b <= 0xee
return Scalar.from_bytes_checked(bytes([b] * 32))
def make_signer(i: int):
"""Deterministic (sk, pk, msg, secnonce, pubnonce). sk is normalized so pk has even y."""
sk = scalar_from_byte(i * 3 + 1)
pk = sk * G
if not has_even_y(pk):
sk = -sk
pk = sk * G
r1 = scalar_from_byte(i * 3 + 2)
r2 = scalar_from_byte(i * 3 + 3)
R1, R2 = r1 * G, r2 * G
msg = bytes([0x80 + i] * 32)
return sk, pk, msg, (r1, r2), (R1, R2)
def make_same_pk_signers():
"""Two signers sharing one key, signing different messages."""
sk = scalar_from_byte(1)
pk = sk * G
if not has_even_y(pk):
sk = -sk
pk = sk * G
msg_a = bytes([0xA0] * 32)
msg_b = bytes([0xA1] * 32)
r1_a, r2_a = scalar_from_byte(11), scalar_from_byte(12)
r1_b, r2_b = scalar_from_byte(13), scalar_from_byte(14)
return [
(sk, pk, msg_a, (r1_a, r2_a), (r1_a * G, r2_a * G)),
(sk, pk, msg_b, (r1_b, r2_b), (r1_b * G, r2_b * G)),
]
def make_tweaked_signer(i: int, tweak_byte: int, is_xonly: bool):
"""Signer with a tweaked key pair, using an odd-y base for X-only tweaks to exercise normalization."""
sk, pk, msg, secnonce, pubnonce = make_signer(i)
if is_xonly:
sk, pk = -sk, -pk
t = scalar_from_byte(tweak_byte)
return TweakSK(sk, t, is_xonly), TweakPK(pk, t, is_xonly), msg, secnonce, pubnonce
def make_duplicate_pm_signers():
"""Two signers with identical (pk, m) but distinct nonces."""
sk = scalar_from_byte(1)
pk = sk * G
if not has_even_y(pk):
sk = -sk
pk = sk * G
msg = bytes([0xC0] * 32)
r1_a, r2_a = scalar_from_byte(20), scalar_from_byte(21)
r1_b, r2_b = scalar_from_byte(22), scalar_from_byte(23)
return [
(sk, pk, msg, (r1_a, r2_a), (r1_a * G, r2_a * G)),
(sk, pk, msg, (r1_b, r2_b), (r1_b * G, r2_b * G)),
]
def serialize_pubnonce(pubnonce) -> bytes:
R1, R2 = pubnonce
return R1.to_bytes_compressed() + R2.to_bytes_compressed()
def serialize_secnonce(secnonce) -> bytes:
r1, r2 = secnonce
return r1.to_bytes() + r2.to_bytes()
def serialize_sig(sig) -> bytes:
R, s = sig
return R.to_bytes_xonly() + s.to_bytes()
def serialize_pk(pk: GE) -> bytes:
return pk.to_bytes_xonly()
def format_list(items: List[bytes], sep: str = ";") -> str:
if not items:
return ""
return sep.join(item.hex().upper() for item in items)
def sign_session(signers):
"""Run NonceAgg -> Sign (per signer) -> SigAgg with deterministic inputs."""
sks = [s[0] for s in signers]
pks = [GE.from_bytes_xonly(s[1].to_bytes_xonly()) for s in signers]
msgs = [s[2] for s in signers]
secnonces = [s[3] for s in signers]
pubnonces = [s[4] for s in signers]
aggnonce = NonceAgg(pubnonces)
psigs = [
Sign(secnonces[i], sks[i], msgs[i], aggnonce, pks, msgs, pubnonces)
for i in range(len(sks))
]
sig = SigAgg(aggnonce, pks, msgs, pubnonces, psigs)
return aggnonce, psigs, sig
def sign_unchecked(signers, msgs, r: Scalar):
"""Aggregate signature from a party holding all secret keys, skipping the message length check."""
R = r * G
if not has_even_y(R):
r = -r
R = r * G
L = b"".join(serialize_pk(s[1]) + m for s, m in zip(signers, msgs))
s = r
for (sk, pk, *_), m in zip(signers, msgs):
d = sk if has_even_y(pk) else -sk
c = Scalar.from_bytes_wrapping(tagged_hash(FULLAGG_TAG_SIG, L + R.to_bytes_xonly() + serialize_pk(pk) + m))
s = s + c * d
return R, s
def gen_nonce_gen_vectors(f):
writer = csv.writer(f)
writer.writerow((
"index", "rand_", "secret_key", "extra_in",
"expected_secnonce", "expected_pubnonce", "comment",
))
rand_prime = bytes([0x0F] * 32)
def row(idx, sk, extra_in, comment):
secnonce, pubnonce = NonceGenInternal(rand_prime, sk, extra_in)
writer.writerow((
idx, rand_prime.hex().upper(),
sk.to_bytes().hex().upper() if sk is not None else "",
extra_in.hex().upper(),
serialize_secnonce(secnonce).hex().upper(),
serialize_pubnonce(pubnonce).hex().upper(),
comment,
))
sk = scalar_from_byte(0x02)
extra_in = bytes([0x08] * 32)
row(0, sk, extra_in, "Secret key and extra_in")
row(1, sk, b"", "Secret key and no extra_in")
row(2, None, extra_in, "No secret key and extra_in")
row(3, None, b"", "No secret key and no extra_in")
def gen_nonce_agg_vectors(f):
writer = csv.writer(f)
writer.writerow((
"index", "pubnonces", "expected_result", "expected_aggnonce", "comment",
))
def nonce_agg_bytes(pubnonces):
return NonceAgg([(GE.from_bytes_compressed(p[:33]), GE.from_bytes_compressed(p[33:]))
for p in pubnonces])
def row(idx, pubnonces, comment):
aggnonce = serialize_pubnonce(nonce_agg_bytes(pubnonces))
writer.writerow((idx, format_list(pubnonces), "TRUE", aggnonce.hex().upper(), comment))
def error_row(idx, pubnonces, comment):
try:
nonce_agg_bytes(pubnonces)
except ValueError:
pass
else:
raise RuntimeError(f"NonceAgg unexpectedly succeeded: {comment}")
writer.writerow((idx, format_list(pubnonces), "FALSE", "", comment))
signers = [make_signer(i) for i in range(3)]
pubnonces = [serialize_pubnonce(s[4]) for s in signers]
(R1_0, R2_0), (R1_1, R2_1) = signers[0][4], signers[1][4]
row(0, pubnonces[:2], "Two signers")
row(1, pubnonces, "Three signers")
error_row(2, [pubnonces[0], b"\x04" + pubnonces[1][1:]],
"Signer 1's pubnonce first half has prefix 0x04")
error_row(3, [pubnonces[0][:33] + b"\x02" + bytes(32), pubnonces[1]],
"Signer 0's pubnonce second half is not on the curve")
error_row(4, [pubnonces[0][:33] + b"\x02" + bytes_from_int(FE.SIZE + 1), pubnonces[1]],
"Signer 0's pubnonce second half exceeds field size")
error_row(5, [pubnonces[0], serialize_pubnonce((-R1_0, R2_1))],
"Aggregate nonce R1 is infinity")
error_row(6, [pubnonces[0], serialize_pubnonce((R1_1, -R2_0))],
"Aggregate nonce R2 is infinity")
def gen_sign_vectors(f):
writer = csv.writer(f)
writer.writerow((
"index",
"secret_keys",
"messages",
"secnonces",
"expected_aggnonce",
"expected_partial_sigs",
"expected_sig",
"comment",
))
def write_row(idx, signers, comment):
aggnonce, psigs, sig = sign_session(signers)
writer.writerow((
idx,
format_list([s[0].to_bytes() for s in signers]),
format_list([s[2] for s in signers]),
format_list([serialize_secnonce(s[3]) for s in signers]),
serialize_pubnonce(aggnonce).hex().upper(),
format_list([p.to_bytes() for p in psigs]),
serialize_sig(sig).hex().upper(),
comment,
))
pks = [s[1] for s in signers]
msgs = [s[2] for s in signers]
pubnonces = [s[4] for s in signers]
for i in range(len(signers)):
assert PartialSigVerify(psigs[i], pks, msgs, pubnonces, i), \
f"generated psig failed self-verification: {comment}"
assert Verify(pks, msgs, sig), \
f"generated sig failed self-verification: {comment}"
write_row(0, [make_signer(0)], "Single signer")
write_row(1, [make_signer(i) for i in range(2)], "Two signers")
write_row(2, [make_signer(i) for i in range(3)], "Three signers")
write_row(3, [make_signer(i) for i in range(5)], "Five signers")
write_row(4, make_same_pk_signers(), "Same pubkey signs two different messages")
write_row(5, [make_tweaked_signer(0, 0x21, False), make_tweaked_signer(1, 0x22, True)],
"Signers with tweaked keys")
write_row(6, make_duplicate_pm_signers(), "Exact duplicate (pk, m) entries")
def gen_sign_error_vectors(f):
writer = csv.writer(f)
writer.writerow((
"index", "secret_key", "message", "secnonce", "aggnonce",
"pubkeys", "messages", "pubnonces",
"comment",
))
def write_row(idx, signer, pks, msgs, pubnonces, comment, aggnonce=None):
sk, _, msg, secnonce, _ = signer
if aggnonce is None:
aggnonce = serialize_pubnonce(NonceAgg(pubnonces))
try:
R1, R2 = GE.from_bytes_compressed(aggnonce[:33]), GE.from_bytes_compressed(aggnonce[33:])
Sign(secnonce, sk, msg, (R1, R2), pks, msgs, pubnonces)
except ValueError:
pass
else:
raise RuntimeError(f"Sign unexpectedly succeeded: {comment}")
writer.writerow((
idx,
sk.to_bytes().hex().upper(),
msg.hex().upper(),
serialize_secnonce(secnonce).hex().upper(),
aggnonce.hex().upper(),
format_list([serialize_pk(p) for p in pks]),
format_list(msgs),
format_list([serialize_pubnonce(p) for p in pubnonces]),
comment,
))
signers = [make_signer(i) for i in range(3)]
pks = [GE.from_bytes_xonly(s[1].to_bytes_xonly()) for s in signers]
msgs = [s[2] for s in signers]
pubnonces = [s[4] for s in signers]
dup_pubnonces = list(pubnonces)
dup_pubnonces[2] = (pubnonces[2][0], pubnonces[0][1])
write_row(0, signers[0], pks, msgs, dup_pubnonces,
"Signer's R2 appears at two indices")
missing_pubnonces = list(pubnonces)
missing_pubnonces[0] = (pubnonces[0][0], scalar_from_byte(0x63) * G)
write_row(1, signers[0], pks, msgs, missing_pubnonces,
"Signer's R2 appears at no index")
wrong_msgs = list(msgs)
wrong_msgs[0] = bytes([0xEE] * 32)
write_row(2, signers[0], pks, wrong_msgs, pubnonces,
"Message at signer's index was substituted")
wrong_pks = list(pks)
wrong_pks[0] = GE.from_bytes_xonly((scalar_from_byte(0x55) * G).to_bytes_xonly())
write_row(3, signers[0], wrong_pks, msgs, pubnonces,
"Public key at signer's index was substituted")
sk, pk, msg, secnonce, pubnonce = signers[0]
write_row(4, (sk, pk, msg, (Scalar(0), Scalar(0)), pubnonce), pks, msgs, pubnonces,
"Secnonce is all zeros")
write_row(5, (Scalar(0), pk, msg, secnonce, pubnonce), pks, msgs, pubnonces,
"Secret key is zero")
aggnonce = serialize_pubnonce(NonceAgg(pubnonces))
write_row(6, signers[0], pks, msgs, pubnonces,
"Aggregate nonce first half is all zeros", bytes(33) + aggnonce[33:])
write_row(7, signers[0], pks, msgs, pubnonces,
"Aggregate nonce second half is not on the curve", aggnonce[:33] + b"\x02" + bytes(32))
write_row(8, signers[0], pks, msgs, pubnonces,
"Aggregate nonce second half exceeds field size",
aggnonce[:33] + b"\x02" + bytes_from_int(FE.SIZE + 1))
def gen_verify_vectors(f):
writer = csv.writer(f)
writer.writerow((
"index", "pubkeys", "messages", "sig", "expected_result", "comment",
))
idx = 0
signers = [make_signer(i) for i in range(2)]
_, _, sig = sign_session(signers)
pks = [serialize_pk(s[1]) for s in signers]
msgs = [s[2] for s in signers]
sig_bytes = serialize_sig(sig)
writer.writerow((idx, format_list(pks), format_list(msgs),
sig_bytes.hex().upper(), "TRUE",
"Two-signer valid aggregate"))
idx += 1
signers3 = [make_signer(i) for i in range(3)]
_, _, sig3 = sign_session(signers3)
pks3 = [serialize_pk(s[1]) for s in signers3]
msgs3 = [s[2] for s in signers3]
sig3_bytes = serialize_sig(sig3)
writer.writerow((idx, format_list(pks3), format_list(msgs3),
sig3_bytes.hex().upper(), "TRUE",
"Three-signer valid aggregate"))
idx += 1
wrong_msgs = [msgs[0], bytes([0xff] * 32)]
writer.writerow((idx, format_list(pks), format_list(wrong_msgs),
sig_bytes.hex().upper(), "FALSE",
"Wrong message for second signer"))
idx += 1
writer.writerow((idx, format_list([pks[1], pks[0]]), format_list(msgs),
sig_bytes.hex().upper(), "FALSE",
"Pubkey order swapped"))
idx += 1
writer.writerow((idx, format_list([pks[0]]), format_list([msgs[0]]),
sig_bytes.hex().upper(), "FALSE",
"Dropped second signer"))
idx += 1
s_int = int_from_bytes(sig_bytes[32:64])
tampered = sig_bytes[:32] + bytes_from_int(s_int ^ 1)
writer.writerow((idx, format_list(pks), format_list(msgs),
tampered.hex().upper(), "FALSE",
"Tampered s (bit flip)"))
idx += 1
s_zero = sig_bytes[:32] + bytes(32)
writer.writerow((idx, format_list(pks), format_list(msgs),
s_zero.hex().upper(), "FALSE",
"s = 0 (in range, wrong)"))
idx += 1
s_eq_n = sig_bytes[:32] + bytes_from_int(n)
writer.writerow((idx, format_list(pks), format_list(msgs),
s_eq_n.hex().upper(), "FALSE",
"s = n (out of range)"))
idx += 1
s_max = sig_bytes[:32] + bytes([0xff] * 32)
writer.writerow((idx, format_list(pks), format_list(msgs),
s_max.hex().upper(), "FALSE",
"s = 2^256 - 1"))
idx += 1
def find_off_curve_x() -> bytes:
for i in range(1, 256):
cand = bytes([i] * 32)
try:
GE.from_bytes_xonly(cand)
except Exception:
return cand
raise RuntimeError("could not find an off-curve x")
off_curve = find_off_curve_x()
bad_R_sig = off_curve + sig_bytes[32:64]
writer.writerow((idx, format_list(pks), format_list(msgs),
bad_R_sig.hex().upper(), "FALSE",
"R x-coord not on curve"))
idx += 1
writer.writerow((idx, format_list([off_curve, pks[1]]), format_list(msgs),
sig_bytes.hex().upper(), "FALSE",
"Public key not on curve"))
idx += 1
writer.writerow((idx, format_list(pks), format_list(msgs),
sig_bytes[:-1].hex().upper(), "FALSE",
"Signature too short"))
idx += 1
writer.writerow((idx, format_list(pks), format_list(msgs),
(sig_bytes + b"\x00").hex().upper(), "FALSE",
"Signature too long"))
idx += 1
writer.writerow((idx, format_list(pks), format_list([msgs[0]]),
sig_bytes.hex().upper(), "FALSE",
"Pubkey/message count mismatch"))
idx += 1
writer.writerow((idx, "", "",
sig_bytes.hex().upper(), "FALSE",
"Empty signer list"))
idx += 1
for bad_m0, comment in ((msgs[0][:31], "Message too short"), (msgs[0] + b"\x00", "Message too long")):
bad_msgs = [bad_m0, msgs[1]]
bad_sig = serialize_sig(sign_unchecked(signers, bad_msgs, scalar_from_byte(0x70)))
writer.writerow((idx, format_list(pks), format_list(bad_msgs),
bad_sig.hex().upper(), "FALSE", comment))
idx += 1
def gen_partial_sig_verify_vectors(f):
writer = csv.writer(f)
writer.writerow((
"index", "signer_index", "psig",
"pubkeys", "messages", "pubnonces",
"expected_result", "comment",
))
signers = [make_signer(i) for i in range(2)]
aggnonce, psigs, _ = sign_session(signers)
pks_hex = format_list([serialize_pk(s[1]) for s in signers])
msgs_hex = format_list([s[2] for s in signers])
pubnonces_hex = format_list([serialize_pubnonce(s[4]) for s in signers])
def write_row(idx, signer_index, psig_bytes, expected, comment):
writer.writerow((
idx, signer_index, psig_bytes.hex().upper(),
pks_hex, msgs_hex, pubnonces_hex,
expected, comment,
))
idx = 0
write_row(idx, 0, psigs[0].to_bytes(), "TRUE", "Honest partial sig (signer 0)")
idx += 1
write_row(idx, 1, psigs[1].to_bytes(), "TRUE", "Honest partial sig (signer 1)")
idx += 1
write_row(idx, 1, psigs[0].to_bytes(), "FALSE",
"Honest psig verified at the wrong signer_index")
idx += 1
write_row(idx, 0, bytes_from_int(int(psigs[0]) ^ 1), "FALSE",
"Tampered partial sig (bit flip)")
idx += 1
write_row(idx, 0, bytes(32), "FALSE", "psig = 0 (in range, wrong)")
idx += 1
write_row(idx, 0, bytes_from_int(n), "FALSE", "psig = n (out of range)")
idx += 1
write_row(idx, 2, psigs[0].to_bytes(), "FALSE", "signer_index out of range")
idx += 1
fake_aggnonce = (aggnonce[0] + G, aggnonce[1])
psig_wrong = Sign(signers[0][3], signers[0][0], signers[0][2], fake_aggnonce,
[s[1] for s in signers], [s[2] for s in signers],
[s[4] for s in signers])
write_row(idx, 0, psig_wrong.to_bytes(), "FALSE", "psig for a different aggregate nonce")
idx += 1
R1_0, R2_0 = signers[0][4]
_, R2_1 = signers[1][4]
inf_pubnonces = format_list([serialize_pubnonce((R1_0, R2_0)),
serialize_pubnonce((-R1_0, R2_1))])
writer.writerow((idx, 0, psigs[0].to_bytes().hex().upper(),
pks_hex, msgs_hex, inf_pubnonces,
"FALSE", "Aggregate nonce R1 is infinity"))
def gen_tweak_vectors(f):
writer = csv.writer(f)
writer.writerow((
"index", "internal_secret_key", "tweak", "is_xonly", "expected_result",
"expected_secret_key", "expected_pubkey", "comment",
))
def row(idx, sk, t, is_xonly, comment):
sk_out = TweakSK(sk, t, is_xonly)
pk_out = TweakPK(sk * G, t, is_xonly)
assert sk_out * G == pk_out
writer.writerow((
idx, sk.to_bytes().hex().upper(), t.to_bytes().hex().upper(),
"TRUE" if is_xonly else "FALSE", "TRUE",
sk_out.to_bytes().hex().upper(), pk_out.to_bytes_compressed().hex().upper(),
comment,
))
def error_row(idx, sk, t, is_xonly, comment):
for tweak_fn, key in ((TweakSK, sk), (TweakPK, sk * G)):
try:
tweak_fn(key, t, is_xonly)
except ValueError:
continue
raise RuntimeError(f"{tweak_fn.__name__} unexpectedly succeeded: {comment}")
writer.writerow((
idx, sk.to_bytes().hex().upper(), t.to_bytes().hex().upper(),
"TRUE" if is_xonly else "FALSE", "FALSE", "", "",
comment,
))
sk1 = scalar_from_byte(1)
even = sk1 if has_even_y(sk1 * G) else -sk1
odd = -even
t = scalar_from_byte(0x02)
row(0, even, t, False, "Plain tweak of an even-y key")
row(1, odd, t, False, "Plain tweak of an odd-y key")
row(2, even, t, True, "X-only tweak of an even-y key")
row(3, odd, t, True, "X-only tweak of an odd-y key")
error_row(4, odd, -odd, False, "Plain tweak resulting in the point at infinity")
error_row(5, odd, odd, True, "X-only tweak resulting in the point at infinity")
writer.writerow((6, even.to_bytes().hex().upper(), bytes_from_int(n).hex().upper(),
"FALSE", "FALSE", "", "", "Tweak = n (out of range)"))
if __name__ == "__main__":
os.makedirs(VECTORS_DIR, exist_ok=True)
print(f"Generating {NONCE_GEN_VECTORS}")
with open(NONCE_GEN_VECTORS, "w", newline="", encoding="utf-8") as f:
gen_nonce_gen_vectors(f)
print(f"Generating {NONCE_AGG_VECTORS}")
with open(NONCE_AGG_VECTORS, "w", newline="", encoding="utf-8") as f:
gen_nonce_agg_vectors(f)
print(f"Generating {SIGN_VECTORS}")
with open(SIGN_VECTORS, "w", newline="", encoding="utf-8") as f:
gen_sign_vectors(f)
print(f"Generating {SIGN_ERROR_VECTORS}")
with open(SIGN_ERROR_VECTORS, "w", newline="", encoding="utf-8") as f:
gen_sign_error_vectors(f)
print(f"Generating {VERIFY_VECTORS}")
with open(VERIFY_VECTORS, "w", newline="", encoding="utf-8") as f:
gen_verify_vectors(f)
print(f"Generating {PARTIAL_SIG_VERIFY_VECTORS}")
with open(PARTIAL_SIG_VERIFY_VECTORS, "w", newline="", encoding="utf-8") as f:
gen_partial_sig_verify_vectors(f)
print(f"Generating {TWEAK_VECTORS}")
with open(TWEAK_VECTORS, "w", newline="", encoding="utf-8") as f:
gen_tweak_vectors(f)
print("Done.")
+211
View File
@@ -0,0 +1,211 @@
#!/usr/bin/env python3
"""
Reference implementation of BIP 459 DahLIAS fully aggregated signatures
for secp256k1.
WARNING: This implementation is for demonstration purposes only and is not
optimized for production use.
"""
from pathlib import Path
from typing import List, Tuple, Optional
import secrets
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, xor_bytes
n = GE.ORDER
FULLAGG_TAG_AUX = "BIP0459/aux"
FULLAGG_TAG_NONCE = "BIP0459/nonce"
FULLAGG_TAG_NONCECOEF = "BIP0459/noncecoef"
FULLAGG_TAG_SIG = "BIP0459/sig"
#
# Helpers
#
def has_even_y(P: GE) -> bool:
return P.has_even_y()
def cbytes(P: GE) -> bytes:
return P.to_bytes_compressed()
def xbytes(P: GE) -> bytes:
return P.to_bytes_xonly()
#
# Key Tweaking
#
def TweakSK(sk: Scalar, t: Scalar, is_xonly: bool) -> Scalar:
d = sk if (not is_xonly or has_even_y(sk * G)) else -sk
sk_out = d + t
if sk_out == 0:
raise ValueError("tweaked secret key is zero")
return sk_out
def TweakPK(pk: GE, t: Scalar, is_xonly: bool) -> GE:
P = pk if (not is_xonly or has_even_y(pk)) else -pk
Q = P + t * G
if Q.infinity:
raise ValueError("tweaked public key is the point at infinity")
return Q
#
# Nonce Generation and Aggregation
#
def NonceGenInternal(rand_prime: bytes, sk: Optional[Scalar] = None,
extra_in: bytes = b'') -> Tuple[Tuple[Scalar, Scalar], Tuple[GE, GE]]:
if sk is not None:
rand = xor_bytes(sk.to_bytes(), tagged_hash(FULLAGG_TAG_AUX, rand_prime))
else:
rand = rand_prime
r1 = Scalar.from_bytes_wrapping(tagged_hash(FULLAGG_TAG_NONCE, rand + extra_in + b'\x00'))
r2 = Scalar.from_bytes_wrapping(tagged_hash(FULLAGG_TAG_NONCE, rand + extra_in + b'\x01'))
assert r1 != 0 and r2 != 0
R1, R2 = r1 * G, r2 * G
return (r1, r2), (R1, R2)
def NonceGen(sk: Optional[Scalar] = None,
extra_in: bytes = b'') -> Tuple[Tuple[Scalar, Scalar], Tuple[GE, GE]]:
rand_prime = secrets.token_bytes(32)
return NonceGenInternal(rand_prime, sk, extra_in)
def NonceAgg(pubnonces: List[Tuple[GE, GE]]) -> Tuple[GE, GE]:
u = len(pubnonces)
R1, R2 = pubnonces[0]
for i in range(1, u):
R1 = R1 + pubnonces[i][0]
R2 = R2 + pubnonces[i][1]
if R1.infinity or R2.infinity:
raise ValueError("aggregate nonce is the point at infinity")
return R1, R2
#
# Session Values
#
def GetSessionValues(aggnonce: Tuple[GE, GE], pks: List[GE], msgs: List[bytes],
pubnonces: List[Tuple[GE, GE]]) -> Tuple[GE, Scalar]:
R1, R2 = aggnonce
u = len(pks)
nonce_data = cbytes(R1) + cbytes(R2)
for i in range(u):
nonce_data += xbytes(pks[i]) + msgs[i] + cbytes(pubnonces[i][1])
b = Scalar.from_bytes_wrapping(tagged_hash(FULLAGG_TAG_NONCECOEF, nonce_data))
R = R1 + b * R2
assert not R.infinity
return R, b
#
# Signing
#
def Sign(secnonce: Tuple[Scalar, Scalar], sk: Scalar, m: bytes,
aggnonce: Tuple[GE, GE], pks: List[GE], msgs: List[bytes],
pubnonces: List[Tuple[GE, GE]]) -> Scalar:
if not (len(pks) == len(msgs) == len(pubnonces) >= 1):
raise ValueError("list lengths must match and be nonzero")
if len(m) != 32 or any(len(mi) != 32 for mi in msgs):
raise ValueError("messages must be 32 bytes")
r1, r2 = secnonce
if r1 == 0 or r2 == 0:
raise ValueError("secnonce value is zero")
if sk == 0:
raise ValueError("secret key is zero")
P = sk * G
R2_local = r2 * G
# Index lookup and uniqueness check
matches = [j for j in range(len(pubnonces)) if pubnonces[j][1] == R2_local]
if len(matches) != 1:
raise ValueError("signer's R2 must appear exactly once")
j = matches[0]
if xbytes(pks[j]) != xbytes(P) or msgs[j] != m:
raise ValueError("public key or message at signer's index does not match")
R, b = GetSessionValues(aggnonce, pks, msgs, pubnonces)
e = Scalar(1) if has_even_y(R) else -Scalar(1)
d_prime = sk if has_even_y(P) else -sk
L = b''
for i in range(len(pks)):
L += xbytes(pks[i]) + msgs[i]
c_j = Scalar.from_bytes_wrapping(tagged_hash(FULLAGG_TAG_SIG, L + xbytes(R) + xbytes(pks[j]) + msgs[j]))
s_j = e * (r1 + b * r2) + c_j * d_prime
return s_j
#
# Aggregation
#
def SigAgg(aggnonce: Tuple[GE, GE], pks: List[GE], msgs: List[bytes],
pubnonces: List[Tuple[GE, GE]], psigs: List[Scalar]) -> Tuple[GE, Scalar]:
if not (len(pks) == len(msgs) == len(pubnonces) == len(psigs) >= 1):
raise ValueError("list lengths must match and be nonzero")
if any(len(mi) != 32 for mi in msgs):
raise ValueError("messages must be 32 bytes")
R, _ = GetSessionValues(aggnonce, pks, msgs, pubnonces)
s = Scalar.sum(*psigs)
return R, s
#
# Partial Signature Verification
#
def PartialSigVerify(psig: Scalar, pks: List[GE], msgs: List[bytes],
pubnonces: List[Tuple[GE, GE]], signer_index: int) -> bool:
if not (len(pks) == len(msgs) == len(pubnonces) >= 1) or any(len(mi) != 32 for mi in msgs):
return False
if not (0 <= signer_index < len(pks)):
return False
i = signer_index
R1_i, R2_i = pubnonces[i]
P_i = GE.from_bytes_xonly(xbytes(pks[i]))
R, b = GetSessionValues(NonceAgg(pubnonces), pks, msgs, pubnonces)
e = Scalar(1) if has_even_y(R) else -Scalar(1)
L = b''
for k in range(len(pks)):
L += xbytes(pks[k]) + msgs[k]
c_i = Scalar.from_bytes_wrapping(tagged_hash(FULLAGG_TAG_SIG, L + xbytes(R) + xbytes(pks[i]) + msgs[i]))
R_eff_i = R1_i + b * R2_i
return psig * G == e * R_eff_i + c_i * P_i
#
# Aggregate Signature Verification
#
def Verify(pks: List[GE], msgs: List[bytes], sig: Tuple[GE, Scalar]) -> bool:
R_point, s = sig
if not (len(pks) == len(msgs) >= 1) or any(len(mi) != 32 for mi in msgs):
return False
u = len(pks)
R = GE.from_bytes_xonly(xbytes(R_point))
Ps = [GE.from_bytes_xonly(xbytes(pks[i])) for i in range(u)]
L = b''
for i in range(u):
L += xbytes(pks[i]) + msgs[i]
rhs = R
for i in range(u):
c_i = Scalar.from_bytes_wrapping(tagged_hash(FULLAGG_TAG_SIG, L + xbytes(R) + xbytes(pks[i]) + msgs[i]))
rhs = rhs + c_i * Ps[i]
return s * G == rhs
+314
View File
@@ -0,0 +1,314 @@
#!/usr/bin/env python3
"""
Run test vectors for BIP 459 DahLIAS fully aggregated signatures
for secp256k1.
Usage:
python run_test_vectors.py
Expects CSV files generated by gen_test_vectors.py in vectors/ directory.
"""
import csv
import os
import sys
from pathlib import Path
from typing import List, Tuple
sys.path.insert(0, str(Path(__file__).parent / "secp256k1lab/src"))
from secp256k1lab.secp256k1 import G, GE, Scalar
from reference import NonceGenInternal, NonceAgg, Sign, SigAgg, Verify, PartialSigVerify, TweakSK, TweakPK
VECTORS_DIR = Path(__file__).resolve().parent / "vectors"
NONCE_GEN_VECTORS = VECTORS_DIR / "test_vectors_nonce_gen.csv"
NONCE_AGG_VECTORS = VECTORS_DIR / "test_vectors_nonce_agg.csv"
SIGN_VECTORS = VECTORS_DIR / "test_vectors_sign.csv"
SIGN_ERROR_VECTORS = VECTORS_DIR / "test_vectors_sign_error.csv"
VERIFY_VECTORS = VECTORS_DIR / "test_vectors_verify.csv"
PARTIAL_SIG_VERIFY_VECTORS = VECTORS_DIR / "test_vectors_partial_sig_verify.csv"
TWEAK_VECTORS = VECTORS_DIR / "test_vectors_tweak.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_pubnonce(b: bytes) -> Tuple[GE, GE]:
if len(b) != 66:
raise ValueError("pubnonce must be 66 bytes")
return GE.from_bytes_compressed(b[:33]), GE.from_bytes_compressed(b[33:])
def parse_secnonce(b: bytes) -> Tuple[Scalar, Scalar]:
if len(b) != 64:
raise ValueError("secnonce must be 64 bytes")
return Scalar.from_bytes_checked(b[:32]), Scalar.from_bytes_checked(b[32:])
def parse_sig(b: bytes) -> Tuple[GE, Scalar]:
"""Parse R (32-byte x-only) || s (32-byte big-endian).
Lifts R to even y and raises ValueError on length mismatch, off-curve R, or
s >= n.
"""
if len(b) != 64:
raise ValueError("signature must be 64 bytes")
R = GE.from_bytes_xonly(b[:32])
s = Scalar.from_bytes_checked(b[32:])
return R, s
def run_nonce_gen_tests() -> List[str]:
failures = []
with open(NONCE_GEN_VECTORS, newline="", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader)
for row in reader:
(index, rand_str, sk_str, extra_in_str,
expected_secnonce_str, expected_pubnonce_str, comment) = row
sk = Scalar.from_bytes_checked(bytes.fromhex(sk_str)) if sk_str else None
(r1, r2), (R1, R2) = NonceGenInternal(bytes.fromhex(rand_str), sk, bytes.fromhex(extra_in_str))
secnonce = r1.to_bytes() + r2.to_bytes()
pubnonce = R1.to_bytes_compressed() + R2.to_bytes_compressed()
if (secnonce.hex().upper() != expected_secnonce_str
or pubnonce.hex().upper() != expected_pubnonce_str):
failures.append(f"NonceGen #{index}: {comment}")
return failures
def run_nonce_agg_tests() -> List[str]:
failures = []
with open(NONCE_AGG_VECTORS, newline="", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader)
for row in reader:
index, pubnonces_str, expected_result, expected_aggnonce_str, comment = row
expected = (expected_result == "TRUE")
try:
pubnonces = [parse_pubnonce(b) for b in parse_hex_list(pubnonces_str)]
R1, R2 = NonceAgg(pubnonces)
aggnonce = R1.to_bytes_compressed() + R2.to_bytes_compressed()
except ValueError:
aggnonce = None
if expected:
ok = aggnonce is not None and aggnonce.hex().upper() == expected_aggnonce_str
else:
ok = aggnonce is None
if not ok:
failures.append(f"NonceAgg #{index}: {comment}")
return failures
def run_sign_tests() -> List[str]:
failures = []
with open(SIGN_VECTORS, newline="", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader)
for row in reader:
(index, sks_str, msgs_str, secnonces_str,
expected_aggnonce_str, expected_psigs_str,
expected_sig_str, comment) = row
try:
sks = [Scalar.from_bytes_checked(b) for b in parse_hex_list(sks_str)]
msgs = parse_hex_list(msgs_str)
secnonces = [parse_secnonce(b) for b in parse_hex_list(secnonces_str)]
expected_aggnonce = bytes.fromhex(expected_aggnonce_str)
expected_psigs = parse_hex_list(expected_psigs_str)
expected_sig = bytes.fromhex(expected_sig_str)
pks = [GE.from_bytes_xonly((sk * G).to_bytes_xonly()) for sk in sks]
pubnonces = [(r1 * G, r2 * G) for (r1, r2) in secnonces]
aggnonce = NonceAgg(pubnonces)
aggnonce_bytes = (aggnonce[0].to_bytes_compressed()
+ aggnonce[1].to_bytes_compressed())
if aggnonce_bytes != expected_aggnonce:
failures.append(f"Sign #{index}: {comment} (aggnonce mismatch)")
continue
psigs = [
Sign(secnonces[i], sks[i], msgs[i], aggnonce, pks, msgs, pubnonces)
for i in range(len(sks))
]
derived_psigs = [p.to_bytes() for p in psigs]
if derived_psigs != expected_psigs:
failures.append(f"Sign #{index}: {comment} (partial sig mismatch)")
continue
for i in range(len(sks)):
if not PartialSigVerify(psigs[i], pks, msgs, pubnonces, i):
failures.append(f"Sign #{index}: {comment} (PartialSigVerify failed for signer {i})")
break
else:
R, s = SigAgg(aggnonce, pks, msgs, pubnonces, psigs)
actual_sig = R.to_bytes_xonly() + s.to_bytes()
if actual_sig != expected_sig:
failures.append(f"Sign #{index}: {comment} (aggsig mismatch)")
elif not Verify(pks, msgs, (R, s)):
failures.append(f"Sign #{index}: {comment} (Verify failed)")
except ValueError as e:
failures.append(f"Sign #{index}: {comment} (raised {e!r})")
return failures
def run_sign_error_tests() -> List[str]:
failures = []
with open(SIGN_ERROR_VECTORS, newline="", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader)
for row in reader:
(index, sk_str, msg_str, secnonce_str, aggnonce_str,
pks_str, msgs_str, pubnonces_str, comment) = row
sk = Scalar.from_bytes_checked(bytes.fromhex(sk_str))
m = bytes.fromhex(msg_str)
secnonce = parse_secnonce(bytes.fromhex(secnonce_str))
pks = [GE.from_bytes_xonly(b) for b in parse_hex_list(pks_str)]
msgs = parse_hex_list(msgs_str)
pubnonces = [parse_pubnonce(b) for b in parse_hex_list(pubnonces_str)]
try:
aggnonce = parse_pubnonce(bytes.fromhex(aggnonce_str))
Sign(secnonce, sk, m, aggnonce, pks, msgs, pubnonces)
except ValueError:
continue
failures.append(f"SignError #{index}: {comment} (Sign did not fail)")
return failures
def run_partial_sig_verify_tests() -> List[str]:
failures = []
with open(PARTIAL_SIG_VERIFY_VECTORS, newline="", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader)
for row in reader:
(index, signer_index_str, psig_str,
pks_str, msgs_str, pubnonces_str,
expected_result, comment) = row
expected = (expected_result == "TRUE")
try:
signer_index = int(signer_index_str)
psig = Scalar.from_bytes_checked(bytes.fromhex(psig_str))
pks = [GE.from_bytes_xonly(b) for b in parse_hex_list(pks_str)]
msgs = parse_hex_list(msgs_str)
pubnonces = [parse_pubnonce(b) for b in parse_hex_list(pubnonces_str)]
result = PartialSigVerify(psig, pks, msgs, pubnonces, signer_index)
except ValueError:
result = False
if result != expected:
failures.append(f"PartialSigVerify #{index}: {comment}")
return failures
def run_verify_tests() -> List[str]:
failures = []
with open(VERIFY_VECTORS, newline="", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader)
for row in reader:
index, pks_str, msgs_str, sig_str, expected_result, comment = row
expected = (expected_result == "TRUE")
try:
pks = [GE.from_bytes_xonly(b) for b in parse_hex_list(pks_str)]
msgs = parse_hex_list(msgs_str)
R, s = parse_sig(bytes.fromhex(sig_str))
result = Verify(pks, msgs, (R, s))
except ValueError:
result = False
if result != expected:
failures.append(f"Verify #{index}: {comment}")
return failures
def run_tweak_tests() -> List[str]:
failures = []
with open(TWEAK_VECTORS, newline="", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader)
for row in reader:
(index, sk_str, t_str, is_xonly_str, expected_result,
exp_sk_str, exp_pk_str, comment) = row
sk = Scalar.from_bytes_checked(bytes.fromhex(sk_str))
is_xonly = (is_xonly_str == "TRUE")
expected = (expected_result == "TRUE")
results = []
for tweak_fn, key in ((TweakSK, sk), (TweakPK, sk * G)):
try:
t = Scalar.from_bytes_checked(bytes.fromhex(t_str))
results.append(tweak_fn(key, t, is_xonly))
except ValueError:
results.append(None)
sk_out, pk_out = results
if expected:
ok = (sk_out is not None and pk_out is not None
and sk_out.to_bytes().hex().upper() == exp_sk_str
and pk_out.to_bytes_compressed().hex().upper() == exp_pk_str)
else:
ok = sk_out is None and pk_out is None
if not ok:
failures.append(f"Tweak #{index}: {comment}")
return failures
def main():
for filename in [NONCE_GEN_VECTORS, NONCE_AGG_VECTORS, SIGN_VECTORS, SIGN_ERROR_VECTORS,
VERIFY_VECTORS, PARTIAL_SIG_VERIFY_VECTORS, TWEAK_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_nonce_gen_tests())
failures.extend(run_nonce_agg_tests())
failures.extend(run_sign_tests())
failures.extend(run_sign_error_tests())
failures.extend(run_partial_sig_verify_tests())
failures.extend(run_verify_tests())
failures.extend(run_tweak_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,8 @@
index,pubnonces,expected_result,expected_aggnonce,comment
0,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,TRUE,02989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Two signers
1,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,TRUE,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7,Three signers
2,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0462C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,,Signer 1's pubnonce first half has prefix 0x04
3,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766020000000000000000000000000000000000000000000000000000000000000000;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,,Signer 0's pubnonce second half is not on the curve
4,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,,Signer 0's pubnonce second half exceeds field size
5,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;034D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076603F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,,Aggregate nonce R1 is infinity
6,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,FALSE,,Aggregate nonce R2 is infinity
1 index pubnonces expected_result expected_aggnonce comment
2 0 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A TRUE 02989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Two signers
3 1 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 TRUE 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7 Three signers
4 2 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0462C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE Signer 1's pubnonce first half has prefix 0x04
5 3 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766020000000000000000000000000000000000000000000000000000000000000000;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE Signer 0's pubnonce second half is not on the curve
6 4 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE Signer 0's pubnonce second half exceeds field size
7 5 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;034D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076603F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE Aggregate nonce R1 is infinity
8 6 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337 FALSE Aggregate nonce R2 is infinity
@@ -0,0 +1,5 @@
index,rand_,secret_key,extra_in,expected_secnonce,expected_pubnonce,comment
0,0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F,0202020202020202020202020202020202020202020202020202020202020202,0808080808080808080808080808080808080808080808080808080808080808,496C8F2EBAEAB2FD7189A9F217BE77ABA036B87CD947EDEFA001CF8D0BF452D55CB6CF68A9A528BBCB71DB15B150806ABB7E92E724FCEE78AEB5B9024EB34CE4,031E006EEDAE3D3820BE8C8EFCD4CE67715EE9B2AB2218CD6330AA1F025AB79FA803538EF9EDCC35E77A69C7C4720D3E6BE495CBA5E3D8ADAFAE7E0F5FFCABC6AC8A,Secret key and extra_in
1,0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F,0202020202020202020202020202020202020202020202020202020202020202,,94EB69603F7D12C1C511F187C8AC92B4DC61ED8E76CF42ACEFD9904C1720F46F7249D0E01A33C71BA9E14AE1CBC749C103A16D3E5DD7B172B4397E5E16180866,02E1DB4796E1D36F6CDD734CCAECB9D35530BAA46CBB0C33EBB689E6B5E6550B9F033FEC0D4E18F85C00F484DE0CFD867ED0F4258227F8739DC0F6033C920E8EDC48,Secret key and no extra_in
2,0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F,,0808080808080808080808080808080808080808080808080808080808080808,78BA95FD557F793BD321CC690E0C695E72B330201BFACC489CF0D42485C850AE2A5C817B42B11ADF4B96B2C94BCEBD9072426A2AC632C9B7BDF810CE5283A556,03C1829FEABAADD69DC5EEAE3778AF61AF82B56AE8D7F1CA980D95DAA6677FD75F038E523C53F4FDBF0F4AB9D0FBD615DE191D7ABD764B03355DCE7331D1F39A9E94,No secret key and extra_in
3,0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F,,,01E41FFC41183A6C0CC981D66F98CB0E460AC059F4F2B9AFA5EA9FA34636098B090315C8AFB605FFB1791365F90B8602E79D4E0D32FD6916F61A7F82ECA20132,027E7AAC81E61DF4F2BAEA16F5544592D2E556B7BFB034B7811064E3DAC46883F70382EDACAD18AF9ADF8395B4F3D1198F3FD5A7E9365177000D4769B92F6ECFFBEA,No secret key and no extra_in
1 index rand_ secret_key extra_in expected_secnonce expected_pubnonce comment
2 0 0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F 0202020202020202020202020202020202020202020202020202020202020202 0808080808080808080808080808080808080808080808080808080808080808 496C8F2EBAEAB2FD7189A9F217BE77ABA036B87CD947EDEFA001CF8D0BF452D55CB6CF68A9A528BBCB71DB15B150806ABB7E92E724FCEE78AEB5B9024EB34CE4 031E006EEDAE3D3820BE8C8EFCD4CE67715EE9B2AB2218CD6330AA1F025AB79FA803538EF9EDCC35E77A69C7C4720D3E6BE495CBA5E3D8ADAFAE7E0F5FFCABC6AC8A Secret key and extra_in
3 1 0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F 0202020202020202020202020202020202020202020202020202020202020202 94EB69603F7D12C1C511F187C8AC92B4DC61ED8E76CF42ACEFD9904C1720F46F7249D0E01A33C71BA9E14AE1CBC749C103A16D3E5DD7B172B4397E5E16180866 02E1DB4796E1D36F6CDD734CCAECB9D35530BAA46CBB0C33EBB689E6B5E6550B9F033FEC0D4E18F85C00F484DE0CFD867ED0F4258227F8739DC0F6033C920E8EDC48 Secret key and no extra_in
4 2 0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F 0808080808080808080808080808080808080808080808080808080808080808 78BA95FD557F793BD321CC690E0C695E72B330201BFACC489CF0D42485C850AE2A5C817B42B11ADF4B96B2C94BCEBD9072426A2AC632C9B7BDF810CE5283A556 03C1829FEABAADD69DC5EEAE3778AF61AF82B56AE8D7F1CA980D95DAA6677FD75F038E523C53F4FDBF0F4AB9D0FBD615DE191D7ABD764B03355DCE7331D1F39A9E94 No secret key and extra_in
5 3 0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F 01E41FFC41183A6C0CC981D66F98CB0E460AC059F4F2B9AFA5EA9FA34636098B090315C8AFB605FFB1791365F90B8602E79D4E0D32FD6916F61A7F82ECA20132 027E7AAC81E61DF4F2BAEA16F5544592D2E556B7BFB034B7811064E3DAC46883F70382EDACAD18AF9ADF8395B4F3D1198F3FD5A7E9365177000D4769B92F6ECFFBEA No secret key and no extra_in
@@ -0,0 +1,10 @@
index,signer_index,psig,pubkeys,messages,pubnonces,expected_result,comment
0,0,AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,TRUE,Honest partial sig (signer 0)
1,1,B83BBCAA5F0D6E0DEBEE0000E41B21D2E9304D0387660F96D3664735B0B954A3,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,TRUE,Honest partial sig (signer 1)
2,1,AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,Honest psig verified at the wrong signer_index
3,0,AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AB,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,Tampered partial sig (bit flip)
4,0,0000000000000000000000000000000000000000000000000000000000000000,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,"psig = 0 (in range, wrong)"
5,0,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,psig = n (out of range)
6,2,AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,signer_index out of range
7,0,2873FA643DD95EDDDCE81DE64A0C7A8C8166E11E5C687261AAC94F9044EBC9E6,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,psig for a different aggregate nonce
8,0,AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;034D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076603F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A,FALSE,Aggregate nonce R1 is infinity
1 index signer_index psig pubkeys messages pubnonces expected_result comment
2 0 0 AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A TRUE Honest partial sig (signer 0)
3 1 1 B83BBCAA5F0D6E0DEBEE0000E41B21D2E9304D0387660F96D3664735B0B954A3 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A TRUE Honest partial sig (signer 1)
4 2 1 AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE Honest psig verified at the wrong signer_index
5 3 0 AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AB 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE Tampered partial sig (bit flip)
6 4 0 0000000000000000000000000000000000000000000000000000000000000000 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE psig = 0 (in range, wrong)
7 5 0 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE psig = n (out of range)
8 6 2 AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE signer_index out of range
9 7 0 2873FA643DD95EDDDCE81DE64A0C7A8C8166E11E5C687261AAC94F9044EBC9E6 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE psig for a different aggregate nonce
10 8 0 AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;034D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076603F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A FALSE Aggregate nonce R1 is infinity
+8
View File
@@ -0,0 +1,8 @@
index,secret_keys,messages,secnonces,expected_aggnonce,expected_partial_sigs,expected_sig,comment
0,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,52127919CC414D892F6FD0FE262E3811F188A02C14867A7A1D4B69BCC771FC61,C8F3414CAF7ED655F450E7AA8B0164FFA38882DAD001CED9E82AFA11806DFD3B52127919CC414D892F6FD0FE262E3811F188A02C14867A7A1D4B69BCC771FC61,Single signer
1,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFAB6AAD8E2AB449C37BBCE5A88CC323D3D,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303;05050505050505050505050505050505050505050505050505050505050505050606060606060606060606060606060606060606060606060606060606060606,02989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA;B83BBCAA5F0D6E0DEBEE0000E41B21D2E9304D0387660F96D3664735B0B954A3,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,Two signers
2,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFAB6AAD8E2AB449C37BBCE5A88CC323D3D;0707070707070707070707070707070707070707070707070707070707070707,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303;05050505050505050505050505050505050505050505050505050505050505050606060606060606060606060606060606060606060606060606060606060606;08080808080808080808080808080808080808080808080808080808080808080909090909090909090909090909090909090909090909090909090909090909,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7,9567B1D92D32A863462A5373E775095C64F36F6546CDE60A972550EC9D27E963;3780F8549BB5C8704A3A75F3B6A9E93EF3D844B80C3F8BC730F0658B4F729B08;E42ABB02908EEB24A91CB5530551A45D9501026E16EEDBC1B15FB38088030F54,2F02469D98943B63C85904CA5F34AD0525B650A79B55D4C5730FBE1AAC7C004FB113653059775BF839817EBAA37096FA331DD9A4BAB3AD57B9A30B6BA467527E,Three signers
3,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFAB6AAD8E2AB449C37BBCE5A88CC323D3D;0707070707070707070707070707070707070707070707070707070707070707;F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F4B0A4D2DCA53E9631B5C85482C62C3737;0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282;8383838383838383838383838383838383838383838383838383838383838383;8484848484848484848484848484848484848484848484848484848484848484,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303;05050505050505050505050505050505050505050505050505050505050505050606060606060606060606060606060606060606060606060606060606060606;08080808080808080808080808080808080808080808080808080808080808080909090909090909090909090909090909090909090909090909090909090909;0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C;0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F,02BA72A6E8BA53E8B971AD0C9823968AEF4D78CE8AF255AB43DFF83003C902FB8D03948B53DA97FDF674C0877315ACBCC8761AA3B9A582B439982FBAFAC99F97210F,40BD244B3077B384CC4FB475EA4B75F00534F8C8AC9495DF5D501AB8EBEFE95C;7413D7A5D78AF26516FE5CD9D7D769DA04B4934162CF212761F8C6BF145F33A6;EC4BA1816B30306CE38A02FFE0D742DB131069DFF013C1CCC95DEC12816E0FAE;1A3D61F95D6C6FC23871AA5795E0F7DC8B2C4AB7A70D7AFAA7D3C51FB0F646E2;CBEF31FEF7E60491D7E986CF11E8F6739FD215A8098BAD81E03F29ADDAFA9DAD,8E1F9F4D2716EBA94D4DCA8A484F08B121EC7CB56331C056E7CA68955AB55D018749316AC8854AAAD73345764AC410F7D29A9C7C517F60D89114FF3E6D418EBD,Five signers
4,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0;A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1,0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C;0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E,0329CE758BB2D21DEF214CEA069ADDBD16896EC77924BF0EC9219E00DCF077605E02DCEC806DEB8DA0774EC5A5D0F5097FFDE1F70A9F40D44363BD3DC2DC2590CA5D,F9E5D159B41C9BAC7B61B034E88DE52F6E5035700312ACCCAC9518B123F172E8;F1EC4EB02363C0A81FF82BD9500A990BFA59E3AE38102D59FF41B1C451B67D4E,6EF86E78B8043F5F2683D1A4018F57527D1992B2A8090770ECD375B45310FEE4EBD22009D7805C549B59DC0E38987E3CADFB3C378BDA39EAEC046BE8A571AEF5,Same pubkey signs two different messages
5,2020202020202020202020202020202020202020202020202020202020202020;1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303;05050505050505050505050505050505050505050505050505050505050505050606060606060606060606060606060606060606060606060606060606060606,02989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,20DCE3CE1C51AE4F82A282B06989EC59206BC6E5EE42507FC7E4A49118182977;1D1F36CADF491EA12E121FA6520BCE32B2BAFE31AEFD0E840B1725A10F9FE76B,C01306054091CC81F54D5A49DD591706CB36BF621C984E9F7FEBA26E0C544E553DFC1A98FB9ACCF0B0B4A256BB95BA8BD326C5179D3F5F03D2FBCA3227B810E2,Signers with tweaked keys
6,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0;C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0,14141414141414141414141414141414141414141414141414141414141414141515151515151515151515151515151515151515151515151515151515151515;16161616161616161616161616161616161616161616161616161616161616161717171717171717171717171717171717171717171717171717171717171717,035BE5E9478209674A96E60F1F037F6176540FD001FA1D64694770C56A7709C42C028F5304E2373E56EE7D774CB89E9F1AFECF0EE7E3E3757F189908F069DAA36C60,1AF1A3721CA970F3A158EB32176A71B69C8886AF98C7EF9F50E14D8E11B1A2A9;C0B6BBB2725E30F338DB5C032959AE8D766B0E8389077A4F217DF88FB190260C,9669004E9B03D7C34670914CD09024A65DAA635497C42BE33F21C56CD740BBDEDBA85F248F07A1E6DA34473540C4204412F3953321CF69EE725F461DC341C8B5,"Exact duplicate (pk, m) entries"
1 index secret_keys messages secnonces expected_aggnonce expected_partial_sigs expected_sig comment
2 0 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337 52127919CC414D892F6FD0FE262E3811F188A02C14867A7A1D4B69BCC771FC61 C8F3414CAF7ED655F450E7AA8B0164FFA38882DAD001CED9E82AFA11806DFD3B52127919CC414D892F6FD0FE262E3811F188A02C14867A7A1D4B69BCC771FC61 Single signer
3 1 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFAB6AAD8E2AB449C37BBCE5A88CC323D3D 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303;05050505050505050505050505050505050505050505050505050505050505050606060606060606060606060606060606060606060606060606060606060606 02989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 AC7EA4711D03C9C5F0D0CDD761B81FC27D18187655A75D37E4EE8879545756AA;B83BBCAA5F0D6E0DEBEE0000E41B21D2E9304D0387660F96D3664735B0B954A3 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C Two signers
4 2 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFAB6AAD8E2AB449C37BBCE5A88CC323D3D;0707070707070707070707070707070707070707070707070707070707070707 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303;05050505050505050505050505050505050505050505050505050505050505050606060606060606060606060606060606060606060606060606060606060606;08080808080808080808080808080808080808080808080808080808080808080909090909090909090909090909090909090909090909090909090909090909 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7 9567B1D92D32A863462A5373E775095C64F36F6546CDE60A972550EC9D27E963;3780F8549BB5C8704A3A75F3B6A9E93EF3D844B80C3F8BC730F0658B4F729B08;E42ABB02908EEB24A91CB5530551A45D9501026E16EEDBC1B15FB38088030F54 2F02469D98943B63C85904CA5F34AD0525B650A79B55D4C5730FBE1AAC7C004FB113653059775BF839817EBAA37096FA331DD9A4BAB3AD57B9A30B6BA467527E Three signers
5 3 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFAB6AAD8E2AB449C37BBCE5A88CC323D3D;0707070707070707070707070707070707070707070707070707070707070707;F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F4B0A4D2DCA53E9631B5C85482C62C3737;0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282;8383838383838383838383838383838383838383838383838383838383838383;8484848484848484848484848484848484848484848484848484848484848484 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303;05050505050505050505050505050505050505050505050505050505050505050606060606060606060606060606060606060606060606060606060606060606;08080808080808080808080808080808080808080808080808080808080808080909090909090909090909090909090909090909090909090909090909090909;0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C;0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F 02BA72A6E8BA53E8B971AD0C9823968AEF4D78CE8AF255AB43DFF83003C902FB8D03948B53DA97FDF674C0877315ACBCC8761AA3B9A582B439982FBAFAC99F97210F 40BD244B3077B384CC4FB475EA4B75F00534F8C8AC9495DF5D501AB8EBEFE95C;7413D7A5D78AF26516FE5CD9D7D769DA04B4934162CF212761F8C6BF145F33A6;EC4BA1816B30306CE38A02FFE0D742DB131069DFF013C1CCC95DEC12816E0FAE;1A3D61F95D6C6FC23871AA5795E0F7DC8B2C4AB7A70D7AFAA7D3C51FB0F646E2;CBEF31FEF7E60491D7E986CF11E8F6739FD215A8098BAD81E03F29ADDAFA9DAD 8E1F9F4D2716EBA94D4DCA8A484F08B121EC7CB56331C056E7CA68955AB55D018749316AC8854AAAD73345764AC410F7D29A9C7C517F60D89114FF3E6D418EBD Five signers
6 4 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0;A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1 0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C;0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0D0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E 0329CE758BB2D21DEF214CEA069ADDBD16896EC77924BF0EC9219E00DCF077605E02DCEC806DEB8DA0774EC5A5D0F5097FFDE1F70A9F40D44363BD3DC2DC2590CA5D F9E5D159B41C9BAC7B61B034E88DE52F6E5035700312ACCCAC9518B123F172E8;F1EC4EB02363C0A81FF82BD9500A990BFA59E3AE38102D59FF41B1C451B67D4E 6EF86E78B8043F5F2683D1A4018F57527D1992B2A8090770ECD375B45310FEE4EBD22009D7805C549B59DC0E38987E3CADFB3C378BDA39EAEC046BE8A571AEF5 Same pubkey signs two different messages
7 5 2020202020202020202020202020202020202020202020202020202020202020;1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303;05050505050505050505050505050505050505050505050505050505050505050606060606060606060606060606060606060606060606060606060606060606 02989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 20DCE3CE1C51AE4F82A282B06989EC59206BC6E5EE42507FC7E4A49118182977;1D1F36CADF491EA12E121FA6520BCE32B2BAFE31AEFD0E840B1725A10F9FE76B C01306054091CC81F54D5A49DD591706CB36BF621C984E9F7FEBA26E0C544E553DFC1A98FB9ACCF0B0B4A256BB95BA8BD326C5179D3F5F03D2FBCA3227B810E2 Signers with tweaked keys
8 6 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040;FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0;C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0 14141414141414141414141414141414141414141414141414141414141414141515151515151515151515151515151515151515151515151515151515151515;16161616161616161616161616161616161616161616161616161616161616161717171717171717171717171717171717171717171717171717171717171717 035BE5E9478209674A96E60F1F037F6176540FD001FA1D64694770C56A7709C42C028F5304E2373E56EE7D774CB89E9F1AFECF0EE7E3E3757F189908F069DAA36C60 1AF1A3721CA970F3A158EB32176A71B69C8886AF98C7EF9F50E14D8E11B1A2A9;C0B6BBB2725E30F338DB5C032959AE8D766B0E8389077A4F217DF88FB190260C 9669004E9B03D7C34670914CD09024A65DAA635497C42BE33F21C56CD740BBDEDBA85F248F07A1E6DA34473540C4204412F3953321CF69EE725F461DC341C8B5 Exact duplicate (pk, m) entries
@@ -0,0 +1,10 @@
index,secret_key,message,secnonce,aggnonce,pubkeys,messages,pubnonces,comment
0,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574030F0FB9A244AD31A369EE02B7ABFBBB0BFA3812B9A39ED93346D03D67D412D177,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B02531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,Signer's R2 appears at two indices
1,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D5957403B83130DE0D1386592FE7B9F407F5F1AE8F1DB91D772E484B3D81DF0FA2E88F24,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766031195A8046DCBB8E17034BCA630065E7A0982E4E36F6F7E5A8D4554E4846FCD99;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Signer's R2 appears at no index
2,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Message at signer's index was substituted
3,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7,9AC20335EB38768D2052BE1DBBC3C8F6178407458E51E6B4AD22F1D91758895B;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Public key at signer's index was substituted
4,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Secnonce is all zeros
5,0000000000000000000000000000000000000000000000000000000000000000,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Secret key is zero
6,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,000000000000000000000000000000000000000000000000000000000000000000036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Aggregate nonce first half is all zeros
7,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574020000000000000000000000000000000000000000000000000000000000000000,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Aggregate nonce second half is not on the curve
8,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,8080808080808080808080808080808080808080808080808080808080808080,02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303,021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D5957402FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967,Aggregate nonce second half exceeds field size
1 index secret_key message secnonce aggnonce pubkeys messages pubnonces comment
2 0 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574030F0FB9A244AD31A369EE02B7ABFBBB0BFA3812B9A39ED93346D03D67D412D177 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B02531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337 Signer's R2 appears at two indices
3 1 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D5957403B83130DE0D1386592FE7B9F407F5F1AE8F1DB91D772E484B3D81DF0FA2E88F24 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766031195A8046DCBB8E17034BCA630065E7A0982E4E36F6F7E5A8D4554E4846FCD99;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Signer's R2 appears at no index
4 2 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Message at signer's index was substituted
5 3 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7 9AC20335EB38768D2052BE1DBBC3C8F6178407458E51E6B4AD22F1D91758895B;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Public key at signer's index was substituted
6 4 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Secnonce is all zeros
7 5 0000000000000000000000000000000000000000000000000000000000000000 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Secret key is zero
8 6 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 000000000000000000000000000000000000000000000000000000000000000000036360E856310CE5D294E8BE33FC807077DC56AC80D95D9CD4DDBD21325EFF73F7 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Aggregate nonce first half is all zeros
9 7 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D59574020000000000000000000000000000000000000000000000000000000000000000 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Aggregate nonce second half is not on the curve
10 8 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 8080808080808080808080808080808080808080808080808080808080808080 02020202020202020202020202020202020202020202020202020202020202020303030303030303030303030303030303030303030303030303030303030303 021A7A569E91DBF60581509C7FC946D1003B60C7DEE85299538DB6353538D5957402FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D076602531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337;0362C0A046DACCE86DDD0343C6D3C7C79C2208BA0D9C9CF24A6D046D21D21F90F703F006A18D5653C4EDF5391FF23A61F03FF83D237E880EE61187FA9F379A028E0A;03F991F944D1E1954A7FC8B9BF62E0D78F015F4C07762D505E20E6C45260A3661B0256B328B30C8BF5839E24058747879408BDB36241DC9C2E7C619FAA12B2920967 Aggregate nonce second half exceeds field size
+8
View File
@@ -0,0 +1,8 @@
index,internal_secret_key,tweak,is_xonly,expected_result,expected_secret_key,expected_pubkey,comment
0,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,0202020202020202020202020202020202020202020202020202020202020202,FALSE,TRUE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,Plain tweak of an even-y key
1,0101010101010101010101010101010101010101010101010101010101010101,0202020202020202020202020202020202020202020202020202020202020202,FALSE,TRUE,0303030303030303030303030303030303030303030303030303030303030303,02531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,Plain tweak of an odd-y key
2,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,0202020202020202020202020202020202020202020202020202020202020202,TRUE,TRUE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,X-only tweak of an even-y key
3,0101010101010101010101010101010101010101010101010101010101010101,0202020202020202020202020202020202020202020202020202020202020202,TRUE,TRUE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,X-only tweak of an odd-y key
4,0101010101010101010101010101010101010101010101010101010101010101,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,FALSE,FALSE,,,Plain tweak resulting in the point at infinity
5,0101010101010101010101010101010101010101010101010101010101010101,0101010101010101010101010101010101010101010101010101010101010101,TRUE,FALSE,,,X-only tweak resulting in the point at infinity
6,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,FALSE,,,Tweak = n (out of range)
1 index internal_secret_key tweak is_xonly expected_result expected_secret_key expected_pubkey comment
2 0 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 0202020202020202020202020202020202020202020202020202020202020202 FALSE TRUE 0101010101010101010101010101010101010101010101010101010101010101 031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F Plain tweak of an even-y key
3 1 0101010101010101010101010101010101010101010101010101010101010101 0202020202020202020202020202020202020202020202020202020202020202 FALSE TRUE 0303030303030303030303030303030303030303030303030303030303030303 02531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337 Plain tweak of an odd-y key
4 2 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 0202020202020202020202020202020202020202020202020202020202020202 TRUE TRUE 0101010101010101010101010101010101010101010101010101010101010101 031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F X-only tweak of an even-y key
5 3 0101010101010101010101010101010101010101010101010101010101010101 0202020202020202020202020202020202020202020202020202020202020202 TRUE TRUE 0101010101010101010101010101010101010101010101010101010101010101 031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F X-only tweak of an odd-y key
6 4 0101010101010101010101010101010101010101010101010101010101010101 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 FALSE FALSE Plain tweak resulting in the point at infinity
7 5 0101010101010101010101010101010101010101010101010101010101010101 0101010101010101010101010101010101010101010101010101010101010101 TRUE FALSE X-only tweak resulting in the point at infinity
8 6 FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 FALSE FALSE Tweak = n (out of range)
+18
View File
@@ -0,0 +1,18 @@
index,pubkeys,messages,sig,expected_result,comment
0,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,TRUE,Two-signer valid aggregate
1,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282,2F02469D98943B63C85904CA5F34AD0525B650A79B55D4C5730FBE1AAC7C004FB113653059775BF839817EBAA37096FA331DD9A4BAB3AD57B9A30B6BA467527E,TRUE,Three-signer valid aggregate
2,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,FALSE,Wrong message for second signer
3,462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,FALSE,Pubkey order swapped
4,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,8080808080808080808080808080808080808080808080808080808080808080,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,FALSE,Dropped second signer
5,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0D,FALSE,Tampered s (bit flip)
6,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D0000000000000000000000000000000000000000000000000000000000000000,FALSE,"s = 0 (in range, wrong)"
7,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,s = n (out of range)
8,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FALSE,s = 2^256 - 1
9,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,030303030303030303030303030303030303030303030303030303030303030364BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,FALSE,R x-coord not on curve
10,0303030303030303030303030303030303030303030303030303030303030303;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,FALSE,Public key not on curve
11,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A,FALSE,Signature too short
12,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C00,FALSE,Signature too long
13,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,8080808080808080808080808080808080808080808080808080808080808080,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,FALSE,Pubkey/message count mismatch
14,,,5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C,FALSE,Empty signer list
15,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,80808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181,9FBE8AD98EB962605C09D4541BF3EA4F95323236E3D729824E946376160F703175198F905A3DA7CDF8B558F83DBE9D0BB43BA9BD4072DDEB4204ED49C48DBA2E,FALSE,Message too short
16,1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B,808080808080808080808080808080808080808080808080808080808080808000;8181818181818181818181818181818181818181818181818181818181818181,9FBE8AD98EB962605C09D4541BF3EA4F95323236E3D729824E946376160F70315EFE5CFEF5DE1A0B3B0A26CD6C5C4BC088A703C78AD72ADFB9E8F619122CAA77,FALSE,Message too long
1 index pubkeys messages sig expected_result comment
2 0 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C TRUE Two-signer valid aggregate
3 1 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;989C0B76CB563971FDC9BEF31EC06C3560F3249D6EE9E5D83C57625596E05F6F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181;8282828282828282828282828282828282828282828282828282828282828282 2F02469D98943B63C85904CA5F34AD0525B650A79B55D4C5730FBE1AAC7C004FB113653059775BF839817EBAA37096FA331DD9A4BAB3AD57B9A30B6BA467527E TRUE Three-signer valid aggregate
4 2 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C FALSE Wrong message for second signer
5 3 462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B;1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C FALSE Pubkey order swapped
6 4 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F 8080808080808080808080808080808080808080808080808080808080808080 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C FALSE Dropped second signer
7 5 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0D FALSE Tampered s (bit flip)
8 6 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D0000000000000000000000000000000000000000000000000000000000000000 FALSE s = 0 (in range, wrong)
9 7 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 FALSE s = n (out of range)
10 8 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FALSE s = 2^256 - 1
11 9 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 030303030303030303030303030303030303030303030303030303030303030364BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C FALSE R x-coord not on curve
12 10 0303030303030303030303030303030303030303030303030303030303030303;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C FALSE Public key not on curve
13 11 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A FALSE Signature too short
14 12 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C00 FALSE Signature too long
15 13 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 8080808080808080808080808080808080808080808080808080808080808080 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C FALSE Pubkey/message count mismatch
16 14 5485290A0E23A1912901F3CD0F2F8884CF8528B089F86ACFE5184670ECB8F06D64BA611B7C1137D3DCBECDD845D34196AB9988932DC4CC92F882712234DA6A0C FALSE Empty signer list
17 15 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 80808080808080808080808080808080808080808080808080808080808080;8181818181818181818181818181818181818181818181818181818181818181 9FBE8AD98EB962605C09D4541BF3EA4F95323236E3D729824E946376160F703175198F905A3DA7CDF8B558F83DBE9D0BB43BA9BD4072DDEB4204ED49C48DBA2E FALSE Message too short
18 16 1B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F;462779AD4AAD39514614751A71085F2F10E1C7A593E4E030EFB5B8721CE55B0B 808080808080808080808080808080808080808080808080808080808080808000;8181818181818181818181818181818181818181818181818181818181818181 9FBE8AD98EB962605C09D4541BF3EA4F95323236E3D729824E946376160F70315EFE5CFEF5DE1A0B3B0A26CD6C5C4BC088A703C78AD72ADFB9E8F619122CAA77 FALSE Message too long