mirror of
https://github.com/bitcoin/bips.git
synced 2025-02-26 00:14:27 +01:00
Switch to 32 byte public keys in bip-schnorr
This commit is contained in:
parent
1faf705388
commit
ed01c1a776
4 changed files with 288 additions and 54 deletions
|
@ -35,6 +35,7 @@ propose a new standard, a number of improvements not specific to Schnorr signatu
|
|||
made:
|
||||
|
||||
* '''Signature encoding''': Instead of [https://en.wikipedia.org/wiki/X.690#DER_encoding DER]-encoding for signatures (which are variable size, and up to 72 bytes), we can use a simple fixed 64-byte format.
|
||||
* '''Public key encoding''': Instead of ''compressed'' 33-byte encoding of elliptic curve points which are common in Bitcoin, public keys in this proposal are encoded as 32 bytes.
|
||||
* '''Batch verification''': The specific formulation of ECDSA signatures that is standardized cannot be verified more efficiently in batch compared to individually, unless additional witness data is added. Changing the signature scheme offers an opportunity to avoid this.
|
||||
|
||||
[[File:bip-schnorr/speedup-batch.png|frame|This graph shows the ratio between the time it takes to verify ''n'' signatures individually and to verify a batch of ''n'' signatures. This ratio goes up logarithmically with the number of signatures, or in other words: the total time to verify ''n'' signatures grows with ''O(n / log n)''.]]
|
||||
|
@ -57,24 +58,35 @@ We choose the ''R''-option to support batch verification.
|
|||
|
||||
'''Key prefixing''' When using the verification rule above directly, it is possible for a third party to convert a signature ''(R,s)'' for key ''P'' into a signature ''(R,s + aH(R || m))'' for key ''P + aG'' and the same message, for any integer ''a''. This is not a concern for Bitcoin currently, as all signature hashes indirectly commit to the public keys. However, this may change with proposals such as SIGHASH_NOINPUT ([https://github.com/bitcoin/bips/blob/master/bip-0118.mediawiki BIP 118]), or when the signature scheme is used for other purposes—especially in combination with schemes like [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]'s unhardened derivation. To combat this, we choose ''key prefixed''<ref>A limitation of committing to the public key (rather than to a short hash of it, or not at all) is that it removes the ability for public key recovery or verifying signatures against a short public key hash. These constructions are generally incompatible with batch verification.</ref> Schnorr signatures; changing the equation to ''sG = R + H(R || P || m)P''.
|
||||
|
||||
'''Encoding the sign of R''' As we chose the ''R''-option above, we're required to encode the point ''R'' into the signature. Several possibilities exist:
|
||||
'''Encoding the sign of P and the sign of R''' There exist several possibilities for encoding the sign of the public key point:
|
||||
# Encoding the full X and Y coordinate of P, resulting in a 64-byte public key.
|
||||
# Encoding the full X coordinate, but only whether Y is even or odd (like compressed public keys). This would result in 33-byte public keys.
|
||||
# Encoding only the X coordinate, resulting in 32-byte public keys.
|
||||
|
||||
As we chose the ''R''-option above, we're required to encode the point ''R'' into the signature. The possibilities are similar to the encoding of ''P'':
|
||||
# Encoding the full X and Y coordinate of R, resulting in a 96-byte signature.
|
||||
# Encoding the full X coordinate, but only whether Y is even or odd (like compressed public keys). This would result in 65-byte signatures.
|
||||
# Encoding only the X coordinate, leaving us with 64-byte signature.
|
||||
|
||||
Using the first option would be slightly more efficient for verification (around 5%), but we prioritize compactness, and therefore choose option 3.
|
||||
Using the first option for both ''P'' and ''R'' would be slightly more efficient for verification (around 5%), but we prioritize compactness, and therefore choose option 3.
|
||||
|
||||
'''Implicit Y coordinate''' In order to support batch verification, the Y coordinate of ''R'' cannot be ambiguous (every valid X coordinate has two possible Y coordinates). We have a choice between several options for symmetry breaking:
|
||||
'''Implicit Y coordinates''' In order to support batch verification, the Y coordinate of ''P'' and of ''R'' cannot be ambiguous (every valid X coordinate has two possible Y coordinates). We have a choice between several options for symmetry breaking:
|
||||
# Implicitly choosing the Y coordinate that is in the lower half.
|
||||
# Implicitly choosing the Y coordinate that is even<ref>Since ''p'' is odd, negation modulo ''p'' will map even numbers to odd numbers and the other way around. This means that for a valid X coordinate, one of the corresponding Y coordinates will be even, and the other will be odd.</ref>.
|
||||
# Implicitly choosing the Y coordinate that is a quadratic residue (has a square root modulo the field size)<ref>A product of two numbers is a quadratic residue when either both or none of the factors are quadratic residues. As ''-1'' is not a quadratic residue, and the two Y coordinates corresponding to a given X coordinate are each other's negation, this means exactly one of the two must be a quadratic residue.</ref>.
|
||||
|
||||
The third option is slower at signing time but a bit faster to verify, as the quadratic residue of the Y coordinate can be computed directly for points represented in
|
||||
In the case of ''R'' the third option is slower at signing time but a bit faster to verify, as the quadratic residue of the Y coordinate can be computed directly for points represented in
|
||||
[https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates Jacobian coordinates] (a common optimization to avoid modular inverses
|
||||
for elliptic curve operations). The two other options require a possibly
|
||||
expensive conversion to affine coordinates first. This would even be the case if the sign or oddness were explicitly coded (option 2 in the previous design choice). We therefore choose option 3.
|
||||
|
||||
'''Final scheme''' As a result, our final scheme ends up using signatures ''(r,s)'' where ''r'' is the X coordinate of a point ''R'' on the curve whose Y coordinate is a quadratic residue, and which satisfies ''sG = R + H(r || P || m)P''.
|
||||
For ''P'' the speed of signing and verification is not significantly different between any of the three options because affine coordinates of the point have to computed anyway. We therefore choose the same option as for ''R''. The signing algorithm ensures that the signature is valid under the correct public key by negating the secret key if necessary.
|
||||
|
||||
It is important to not mix up the 32 byte public key format and other existing public key formats. Concretely, a verifier should only accept 32 byte public keys and not, for example, convert a 33 byte public key by throwing away the first byte. Otherwise, two public keys would be valid for a single signature which can result in subtle malleability issues.
|
||||
|
||||
Implicit Y coordinates are not a reduction in security when expressed as the number of elliptic curve operations an attacker is expected to perform to compute the secret key. An attacker can normalize any given public key to a point whose Y coordinate is a quadratic residue by negating the point if necessary. This is just a subtraction of field elements and not an elliptic curve operation.
|
||||
|
||||
'''Final scheme''' As a result, our final scheme ends up using public keys ''p'' where ''p'' is the X coordinate of a point ''P'' on the curve whose Y coordinate is a quadratic residue and signatures ''(r,s)'' where ''r'' is the X coordinate of a point ''R'' whose Y coordinate is a quadratic residue. The signature satisfies ''sG = R + H(r || p || m)P''.
|
||||
|
||||
=== Specification ===
|
||||
|
||||
|
@ -94,26 +106,27 @@ The following convention is used, with constants as defined for secp256k1:
|
|||
** ''||'' refers to byte array concatenation.
|
||||
** The function ''x[i:j]'', where ''x'' is a byte array, 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 ''bytes(P)'', where ''P'' is a point, returns ''bytes(0x02 + (y(P) & 1)) || bytes(x(P))''<ref>This matches the ''compressed'' encoding for elliptic curve points used in Bitcoin already, following section 2.3.3 of the [http://www.secg.org/sec1-v2.pdf SEC 1] standard.</ref>.
|
||||
** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))'.
|
||||
** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte encoding is ''x''.
|
||||
** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..p-1'', returns the point ''P'' for which ''x(P) = x'' and ''y(P)'' is a quadratic residue modulo ''p'', or fails if no such point exists<ref>Given an candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. Given a candidate ''x'', the valid Y coordinates are the square roots of ''c = x<sup>3</sup> + 7 mod p'' and they can be computed as ''y = ±c<sup>(p+1)/4</sup> mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. Due to [https://en.wikipedia.org/wiki/Euler%27s_criterion Euler's criterion] it then holds that ''c<sup>(p-1)/2</sup> = 1 mod p''. The same criterion applied to ''y'' results in ''y<sup>(p-1)/2</sup> mod p = ±c<sup>((p+1)/4)((p-1)/2)</sup> mod p = ±1 mod p''. Therefore ''y = +c<sup>(p+1)/4</sup> mod p'' is a quadratic residue and ''-y mod p'' is not.</ref>. The function ''lift_x(x)'' is equivalent to the following pseudocode:
|
||||
*** Let ''y = c<sup>(p+1)/4</sup> mod p''.
|
||||
*** Fail if ''c ≠ y<sup>2</sup> mod p''.
|
||||
*** Return ''(r, y)''.
|
||||
** The function ''point(x)'', where ''x'' is a 33-byte array, returns the point ''P'' for which ''x(P) = int(x[1:33])'' and ''y(P) & 1 = int(x[0:1]) - 0x02)'', or fails if no such point exists. The function ''point(x)'' is equivalent to the following pseudocode:
|
||||
*** Fail if (''x[0:1] ≠ 0x02'' and ''x[0:1] ≠ 0x03'').
|
||||
*** Set flag ''odd'' if ''x[0:1] = 0x03''.
|
||||
*** Let ''(r, y) = lift_x(x)''; fail if ''lift_x(x)'' fails.
|
||||
*** If (flag ''odd'' is set and ''y'' is an even integer) or (flag ''odd'' is not set and ''y'' is an odd integer):
|
||||
**** Let ''y = p - y''.
|
||||
*** Return ''(r, y)''.
|
||||
** The function ''point(x)'', where ''x'' is a 32-byte array, returns the point ''P = lift_x(int(x))''.
|
||||
** The function ''hash(x)'', where ''x'' is a byte array, returns the 32-byte SHA256 hash of ''x''.
|
||||
** The function ''jacobi(x)'', where ''x'' is an integer, returns the [https://en.wikipedia.org/wiki/Jacobi_symbol Jacobi symbol] of ''x / p''. It is equal to ''x<sup>(p-1)/2</sup> mod p'' ([https://en.wikipedia.org/wiki/Euler%27s_criterion Euler's criterion])<ref>For points ''P'' on the secp256k1 curve it holds that ''jacobi(y(P)) ≠ 0''.</ref>.
|
||||
|
||||
=== Public Key Generation ===
|
||||
|
||||
Input:
|
||||
* The secret key ''d'': an integer in the range ''1..n-1'' chosen uniformly at random.
|
||||
|
||||
The public key corresponding to secret key ''d'' is ''bytes(dG)''.
|
||||
|
||||
==== Verification ====
|
||||
|
||||
Input:
|
||||
* The public key ''pk'': a 33-byte array
|
||||
* The public key ''pk'': a 32-byte array
|
||||
* The message ''m'': a 32-byte array
|
||||
* A signature ''sig'': a 64-byte array
|
||||
|
||||
|
@ -130,7 +143,7 @@ The signature is valid if and only if the algorithm below does not fail.
|
|||
|
||||
Input:
|
||||
* The number ''u'' of signatures
|
||||
* The public keys ''pk<sub>1..u</sub>'': ''u'' 33-byte arrays
|
||||
* The public keys ''pk<sub>1..u</sub>'': ''u'' 32-byte arrays
|
||||
* The messages ''m<sub>1..u</sub>'': ''u'' 32-byte arrays
|
||||
* The signatures ''sig<sub>1..u</sub>'': ''u'' 64-byte arrays
|
||||
|
||||
|
@ -147,16 +160,18 @@ All provided signatures are valid with overwhelming probability if and only if t
|
|||
==== Signing ====
|
||||
|
||||
Input:
|
||||
* The secret key ''d'': an integer in the range ''1..n-1''.
|
||||
* The secret key ''d' '': an integer in the range ''1..n-1'' chosen uniformly at random.
|
||||
* The message ''m'': a 32-byte array
|
||||
|
||||
To sign ''m'' for public key ''dG'':
|
||||
To sign ''m'' for public key ''bytes(dG)'':
|
||||
* Let ''P = dG''
|
||||
* Let ''d = d' '' if ''jacobi(y(P)) = 1'', otherwise let ''d = n - d' ''.
|
||||
* Let ''k' = int(hash(bytes(d) || m)) mod n''<ref>Note that in general, taking the output of a hash function modulo the curve order will produce an unacceptably biased result. However, for the secp256k1 curve, the order is sufficiently close to ''2<sup>256</sup>'' that this bias is not observable (''1 - n / 2<sup>256</sup>'' is around ''1.27 * 2<sup>-128</sup>'').</ref>.
|
||||
* Fail if ''k' = 0''.
|
||||
* Let ''R = k'G''.
|
||||
* Let ''k = k' '' if ''jacobi(y(R)) = 1'', otherwise let ''k = n - k' ''.
|
||||
* Let ''e = int(hash(bytes(x(R)) || bytes(dG) || m)) mod n''.
|
||||
* The signature is ''bytes(x(R)) || bytes((k + ed) mod n)''.
|
||||
* Let ''e = int(hash(bytes(R) || bytes(P) || m)) mod n''.
|
||||
* The signature is ''bytes(R) || bytes((k + ed) mod n)''.
|
||||
|
||||
'''Above deterministic derivation of ''R'' is designed specifically for this signing algorithm and may not be secure when used in other signature schemes.'''
|
||||
For example, using the same derivation in the MuSig multi-signature scheme leaks the secret key (see the [https://eprint.iacr.org/2018/068 MuSig paper] for details).
|
||||
|
|
|
@ -31,19 +31,14 @@ def bytes_from_int(x):
|
|||
return x.to_bytes(32, byteorder="big")
|
||||
|
||||
def bytes_from_point(P):
|
||||
return (b'\x03' if P[1] & 1 else b'\x02') + bytes_from_int(P[0])
|
||||
return bytes_from_int(P[0])
|
||||
|
||||
def point_from_bytes(b):
|
||||
if b[0:1] in [b'\x02', b'\x03']:
|
||||
odd = b[0] - 0x02
|
||||
else:
|
||||
return None
|
||||
x = int_from_bytes(b[1:33])
|
||||
x = int_from_bytes(b)
|
||||
y_sq = (pow(x, 3, p) + 7) % p
|
||||
y0 = pow(y_sq, (p + 1) // 4, p)
|
||||
if pow(y0, 2, p) != y_sq:
|
||||
y = pow(y_sq, (p + 1) // 4, p)
|
||||
if pow(y, 2, p) != y_sq:
|
||||
return None
|
||||
y = p - y0 if y0 & 1 != odd else y0
|
||||
return [x, y]
|
||||
|
||||
def int_from_bytes(b):
|
||||
|
@ -55,24 +50,30 @@ def hash_sha256(b):
|
|||
def jacobi(x):
|
||||
return pow(x, (p - 1) // 2, p)
|
||||
|
||||
def schnorr_sign(msg, seckey):
|
||||
def pubkey_gen(seckey):
|
||||
P = point_mul(G, seckey)
|
||||
return bytes_from_point(P)
|
||||
|
||||
def schnorr_sign(msg, seckey0):
|
||||
if len(msg) != 32:
|
||||
raise ValueError('The message must be a 32-byte array.')
|
||||
if not (1 <= seckey <= n - 1):
|
||||
if not (1 <= seckey0 <= n - 1):
|
||||
raise ValueError('The secret key must be an integer in the range 1..n-1.')
|
||||
P = point_mul(G, seckey0)
|
||||
seckey = seckey0 if (jacobi(P[1]) == 1) else n - seckey0
|
||||
k0 = int_from_bytes(hash_sha256(bytes_from_int(seckey) + msg)) % n
|
||||
if k0 == 0:
|
||||
raise RuntimeError('Failure. This happens only with negligible probability.')
|
||||
R = point_mul(G, k0)
|
||||
k = n - k0 if (jacobi(R[1]) != 1) else k0
|
||||
e = int_from_bytes(hash_sha256(bytes_from_int(R[0]) + bytes_from_point(point_mul(G, seckey)) + msg)) % n
|
||||
return bytes_from_int(R[0]) + bytes_from_int((k + e * seckey) % n)
|
||||
e = int_from_bytes(hash_sha256(bytes_from_point(R) + bytes_from_point(P) + msg)) % n
|
||||
return bytes_from_point(R) + bytes_from_int((k + e * seckey) % n)
|
||||
|
||||
def schnorr_verify(msg, pubkey, sig):
|
||||
if len(msg) != 32:
|
||||
raise ValueError('The message must be a 32-byte array.')
|
||||
if len(pubkey) != 33:
|
||||
raise ValueError('The public key must be a 33-byte array.')
|
||||
if len(pubkey) != 32:
|
||||
raise ValueError('The public key must be a 32-byte array.')
|
||||
if len(sig) != 64:
|
||||
raise ValueError('The signature must be a 64-byte array.')
|
||||
P = point_from_bytes(pubkey)
|
||||
|
@ -82,7 +83,7 @@ def schnorr_verify(msg, pubkey, sig):
|
|||
s = int_from_bytes(sig[32:64])
|
||||
if (r >= p or s >= n):
|
||||
return False
|
||||
e = int_from_bytes(hash_sha256(sig[0:32] + bytes_from_point(P) + msg)) % n
|
||||
e = int_from_bytes(hash_sha256(sig[0:32] + pubkey + msg)) % n
|
||||
R = point_add(point_mul(G, s), point_mul(P, n - e))
|
||||
if R is None or jacobi(R[1]) != 1 or R[0] != r:
|
||||
return False
|
||||
|
@ -107,20 +108,25 @@ def test_vectors():
|
|||
print('\nTest vector #%-3i: ' % int(index))
|
||||
if seckey != '':
|
||||
seckey = int(seckey, 16)
|
||||
pubkey_actual = pubkey_gen(seckey)
|
||||
if pubkey != pubkey_actual:
|
||||
print(' * Failed key generation.')
|
||||
print(' Expected key:', pubkey.hex().upper())
|
||||
print(' Actual key:', pubkey_actual.hex().upper())
|
||||
sig_actual = schnorr_sign(msg, seckey)
|
||||
if sig == sig_actual:
|
||||
print(' * Passed signing test.')
|
||||
else:
|
||||
print(' * Failed signing test.')
|
||||
print(' Excepted signature:', sig.hex())
|
||||
print(' Actual signature:', sig_actual.hex())
|
||||
print(' Expected signature:', sig.hex().upper())
|
||||
print(' Actual signature:', sig_actual.hex().upper())
|
||||
all_passed = False
|
||||
result_actual = schnorr_verify(msg, pubkey, sig)
|
||||
if result == result_actual:
|
||||
print(' * Passed verification test.')
|
||||
else:
|
||||
print(' * Failed verification test.')
|
||||
print(' Excepted verification result:', result)
|
||||
print(' Expected verification result:', result)
|
||||
print(' Actual verification result:', result_actual)
|
||||
if comment:
|
||||
print(' Comment:', comment)
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
index,secret key,public key,message,signature,verification result,comment
|
||||
1,0000000000000000000000000000000000000000000000000000000000000001,0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,0000000000000000000000000000000000000000000000000000000000000000,787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF67031A98831859DC34DFFEEDDA86831842CCD0079E1F92AF177F7F22CC1DCED05,TRUE,
|
||||
2,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD,TRUE,
|
||||
3,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C7,03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B,5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BE00880371D01766935B92D2AB4CD5C8A2A5837EC57FED7660773A05F0DE142380,TRUE,
|
||||
4,,03DEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6302A8DC32E64E86A333F20EF56EAC9BA30B7246D6D25E22ADB8C6BE1AEB08D49D,TRUE,
|
||||
5,,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,0000000000000000000000000000000000000000000000000000000000000000,52818579ACA59767E3291D91B76B637BEF062083284992F2D95F564CA6CB4E3530B1DA849C8E8304ADC0CFE870660334B3CFC18E825EF1DB34CFAE3DFC5D8187,TRUE,"test fails if jacobi symbol of x(R) instead of y(R) is used"
|
||||
6,,03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,570DD4CA83D4E6317B8EE6BAE83467A1BF419D0767122DE409394414B05080DCE9EE5F237CBD108EABAE1E37759AE47F8E4203DA3532EB28DB860F33D62D49BD,TRUE,"test fails if msg is reduced modulo p or n"
|
||||
7,,03EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6302A8DC32E64E86A333F20EF56EAC9BA30B7246D6D25E22ADB8C6BE1AEB08D49D,FALSE,"public key not on the curve"
|
||||
8,,02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1DFA16AEE06609280A19B67A24E1977E4697712B5FD2943914ECD5F730901B4AB7,FALSE,"incorrect R residuosity"
|
||||
9,,03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B,5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C, 00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BED092F9D860F1776A1F7412AD8A1EB50DACCC222BC8C0E26B2056DF2F273EFDEC,FALSE,"negated message"
|
||||
10,,0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,0000000000000000000000000000000000000000000000000000000000000000,787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF68FCE5677CE7A623CB20011225797CE7A8DE1DC6CCD4F754A47DA6C600E59543C,FALSE,"negated s value"
|
||||
11,,03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD,FALSE,"negated public key"
|
||||
12,,02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000009E9D01AF988B5CEDCE47221BFA9B222721F3FA408915444A4B489021DB55775F,FALSE,"sG - eP is infinite. Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 0"
|
||||
13,,02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000001D37DDF0254351836D84B1BD6A795FD5D523048F298C4214D187FE4892947F728,FALSE,"sG - eP is infinite. Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 1"
|
||||
14,,02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD,FALSE,"sig[0:32] is not an X coordinate on the curve"
|
||||
15,,02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2F1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD,FALSE,"sig[0:32] is equal to field size"
|
||||
16,,02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,"sig[32:64] is equal to curve order"
|
||||
0,0000000000000000000000000000000000000000000000000000000000000001,79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,0000000000000000000000000000000000000000000000000000000000000000,787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF6166FCCEE14F021B31AF22A90D0639CC010C2B764C304A8FFF266ABBC01A0A880,TRUE,
|
||||
1,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,3C4D3ADB1F05C3E948216ECB6007D1534D97D35D5589EDB226AD0370C0293C780AEA9BA361509DA2FDF7BC6E5E38926E40B6ACCD24EA9E06B9DA8244A1D0B691,TRUE,
|
||||
2,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C7,FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B,5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,33380A613013115518CD6030ABC8220B2DED273FD3ABD13DF0882DC6A928E5AFAC72DF3248F8FAC522EF1A819EE5EE5BEA81D92D0E19A6FAB228E5D1D61BF833,TRUE,
|
||||
3,0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710,25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7C4E8B0C2575A77A01AF41EE6678EF9C41F97CC4D15FF6D6CA45D73BC6FF7FF4919D246589AF2FA6306F4B7A392857E9C4A17CB21DBE72C38A48C99979EEDCB8,TRUE,test fails if msg is reduced modulo p or n
|
||||
4,,D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C63F84A709CFFD89AD94FCBD808D41BD26BF62F263AA253527134DDC4A4715BF491,TRUE,
|
||||
5,,EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,3C4D3ADB1F05C3E948216ECB6007D1534D97D35D5589EDB226AD0370C0293C780AEA9BA361509DA2FDF7BC6E5E38926E40B6ACCD24EA9E06B9DA8244A1D0B691,FALSE,public key not on the curve
|
||||
6,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F98631F4EF21D5F231A1000ED069E0348ED057EDF4FB1B6672009EDE9DBB2DEE14,FALSE,incorrect R residuosity
|
||||
7,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,07636CBE3092D11AFCA4DD1D32E50F039EB5FF5FB2AB72A7DC2BA0F3A4E7ED418C312ED6ABF6A41446D28789DF4AA43A15E166F001D072536ADC6E49C21DC419,FALSE,negated message
|
||||
8,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,3C4D3ADB1F05C3E948216ECB6007D1534D97D35D5589EDB226AD0370C0293C78F515645C9EAF625D02084391A1C76D9079F830198A5E023505F7DC482E658AB0,FALSE,negated s value
|
||||
9,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000000221A96FEEBA7AD29F11B675DB394948A83A220FD8FE181A7667BDBEE178011B1,FALSE,sG - eP is infinite. Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 0
|
||||
10,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000011A39648F31350D8E591106B43B9EB364F8617BCD52DC96A3FA8C4E50347F31DE,FALSE,sG - eP is infinite. Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 1
|
||||
11,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D0AEA9BA361509DA2FDF7BC6E5E38926E40B6ACCD24EA9E06B9DA8244A1D0B691,FALSE,sig[0:32] is not an X coordinate on the curve
|
||||
12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F0AEA9BA361509DA2FDF7BC6E5E38926E40B6ACCD24EA9E06B9DA8244A1D0B691,FALSE,sig[0:32] is equal to field size
|
||||
13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,3C4D3ADB1F05C3E948216ECB6007D1534D97D35D5589EDB226AD0370C0293C78FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order
|
||||
|
|
|
215
bip-schnorr/test-vectors.py
Normal file
215
bip-schnorr/test-vectors.py
Normal file
|
@ -0,0 +1,215 @@
|
|||
import sys
|
||||
from reference import *
|
||||
|
||||
def vector0():
|
||||
seckey = 1
|
||||
msg = bytes_from_int(0)
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
pubkey = pubkey_gen(seckey)
|
||||
|
||||
# The point reconstructed from the public key has an even Y coordinate.
|
||||
pubkey_point = point_from_bytes(pubkey)
|
||||
assert(pubkey_point[1] & 1 == 0)
|
||||
|
||||
return (bytes_from_int(seckey), pubkey, msg, sig, "TRUE", None)
|
||||
|
||||
def vector1():
|
||||
seckey = 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF
|
||||
msg = bytes_from_int(0x243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89)
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
pubkey = pubkey_gen(seckey)
|
||||
|
||||
# The point reconstructed from the public key has an odd Y coordinate.
|
||||
pubkey_point = point_from_bytes(pubkey)
|
||||
assert(pubkey_point[1] & 1 == 1)
|
||||
|
||||
return (bytes_from_int(seckey), pubkey, msg, sig, "TRUE", None)
|
||||
|
||||
def vector2():
|
||||
seckey = 0xC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C7
|
||||
msg = bytes_from_int(0x5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C)
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
|
||||
# This signature does not verify vector if the implementer would check the
|
||||
# jacobi symbol of the X coordinate of R instead of the Y coordinate.
|
||||
R = point_from_bytes(sig[0:32])
|
||||
assert(jacobi(R[0]) != 1)
|
||||
|
||||
return (bytes_from_int(seckey), pubkey_gen(seckey), msg, sig, "TRUE", None)
|
||||
|
||||
def vector3():
|
||||
seckey = 0x0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710
|
||||
msg = bytes_from_int(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
return (bytes_from_int(seckey), pubkey_gen(seckey), msg, sig, "TRUE", "test fails if msg is reduced modulo p or n")
|
||||
|
||||
# Signs with a given nonce. Results in an invalid signature if y(kG) is not a
|
||||
# quadratic residue.
|
||||
def schnorr_sign_fixed_nonce(msg, seckey0, k):
|
||||
if len(msg) != 32:
|
||||
raise ValueError('The message must be a 32-byte array.')
|
||||
if not (1 <= seckey0 <= n - 1):
|
||||
raise ValueError('The secret key must be an integer in the range 1..n-1.')
|
||||
P = point_mul(G, seckey0)
|
||||
seckey = seckey0 if (jacobi(P[1]) == 1) else n - seckey0
|
||||
R = point_mul(G, k)
|
||||
e = int_from_bytes(hash_sha256(bytes_from_point(R) + bytes_from_point(P) + msg)) % n
|
||||
return bytes_from_point(R) + bytes_from_int((k + e * seckey) % n)
|
||||
|
||||
# Creates a singature with a small x(R) by using k = 1/2
|
||||
def vector4():
|
||||
one_half = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0
|
||||
seckey = 0x763758E5CBEEDEE4F7D3FC86F531C36578933228998226672F13C4F0EBE855EB
|
||||
msg = bytes_from_int(0x4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703)
|
||||
sig = schnorr_sign_fixed_nonce(msg, seckey, one_half)
|
||||
return (None, pubkey_gen(seckey), msg, sig, "TRUE", None)
|
||||
|
||||
default_seckey = 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF
|
||||
default_msg = bytes_from_int(0x243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89)
|
||||
|
||||
def vector5():
|
||||
seckey = default_seckey
|
||||
msg = default_msg
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
|
||||
# Public key is not on the curve
|
||||
pubkey = bytes_from_int(0xEEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34)
|
||||
assert(point_from_bytes(pubkey) is None)
|
||||
|
||||
return (None, pubkey, msg, sig, "FALSE", "public key not on the curve")
|
||||
|
||||
def vector6():
|
||||
seckey = default_seckey
|
||||
msg = default_msg
|
||||
k = 3
|
||||
sig = schnorr_sign_fixed_nonce(msg, seckey, k)
|
||||
|
||||
# Y coordinate of R is not a quadratic residue
|
||||
R = point_mul(G, k)
|
||||
assert(jacobi(R[1]) != 1)
|
||||
|
||||
return (None, pubkey_gen(seckey), msg, sig, "FALSE", "incorrect R residuosity")
|
||||
|
||||
def vector7():
|
||||
seckey = default_seckey
|
||||
msg = int_from_bytes(default_msg)
|
||||
neg_msg = bytes_from_int(n - msg)
|
||||
sig = schnorr_sign(neg_msg, seckey)
|
||||
return (None, pubkey_gen(seckey), bytes_from_int(msg), sig, "FALSE", "negated message")
|
||||
|
||||
def vector8():
|
||||
seckey = default_seckey
|
||||
msg = default_msg
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
sig = sig[0:32] + bytes_from_int(n - int_from_bytes(sig[32:64]))
|
||||
return (None, pubkey_gen(seckey), msg, sig, "FALSE", "negated s value")
|
||||
|
||||
def bytes_from_point_inf0(P):
|
||||
if P == None:
|
||||
return bytes_from_int(0)
|
||||
return bytes_from_int(P[0])
|
||||
|
||||
def vector9():
|
||||
seckey = default_seckey
|
||||
msg = default_msg
|
||||
|
||||
# Override bytes_from_point in schnorr_sign to allow creating a signature
|
||||
# with k = 0.
|
||||
k = 0
|
||||
bytes_from_point_tmp = bytes_from_point.__code__
|
||||
bytes_from_point.__code__ = bytes_from_point_inf0.__code__
|
||||
sig = schnorr_sign_fixed_nonce(msg, seckey, k)
|
||||
bytes_from_point.__code__ = bytes_from_point_tmp
|
||||
|
||||
return (None, pubkey_gen(seckey), msg, sig, "FALSE", "sG - eP is infinite. Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 0")
|
||||
|
||||
def bytes_from_point_inf1(P):
|
||||
if P == None:
|
||||
return bytes_from_int(1)
|
||||
return bytes_from_int(P[0])
|
||||
|
||||
def vector10():
|
||||
seckey = default_seckey
|
||||
msg = default_msg
|
||||
|
||||
# Override bytes_from_point in schnorr_sign to allow creating a signature
|
||||
# with k = 0.
|
||||
k = 0
|
||||
bytes_from_point_tmp = bytes_from_point.__code__
|
||||
bytes_from_point.__code__ = bytes_from_point_inf1.__code__
|
||||
sig = schnorr_sign_fixed_nonce(msg, seckey, k)
|
||||
bytes_from_point.__code__ = bytes_from_point_tmp
|
||||
|
||||
return (None, pubkey_gen(seckey), msg, sig, "FALSE", "sG - eP is infinite. Test fails in single verification if jacobi(y(inf)) is defined as 1 and x(inf) as 1")
|
||||
|
||||
# It's cryptographically impossible to create a test vector that fails if run
|
||||
# in an implementation which merely misses the check that sig[0:32] is an X
|
||||
# coordinate on the curve. This test vector just increases test coverage.
|
||||
def vector11():
|
||||
seckey = default_seckey
|
||||
msg = default_msg
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
|
||||
# Replace R's X coordinate with an X coordinate that's not on the curve
|
||||
x_not_on_curve = bytes_from_int(0x4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D)
|
||||
assert(point_from_bytes(x_not_on_curve) is None)
|
||||
sig = x_not_on_curve + sig[32:64]
|
||||
|
||||
return (None, pubkey_gen(seckey), msg, sig, "FALSE", "sig[0:32] is not an X coordinate on the curve")
|
||||
|
||||
# It's cryptographically impossible to create a test vector that fails if run
|
||||
# in an implementation which merely misses the check that sig[0:32] is smaller
|
||||
# than the field size. This test vector just increases test coverage.
|
||||
def vector12():
|
||||
seckey = default_seckey
|
||||
msg = default_msg
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
|
||||
# Replace R's X coordinate with an X coordinate that's equal to field size
|
||||
sig = bytes_from_int(p) + sig[32:64]
|
||||
|
||||
return (None, pubkey_gen(seckey), msg, sig, "FALSE", "sig[0:32] is equal to field size")
|
||||
|
||||
# It's cryptographically impossible to create a test vector that fails if run
|
||||
# in an implementation which merely misses the check that sig[32:64] is smaller
|
||||
# than the curve order. This test vector just increases test coverage.
|
||||
def vector13():
|
||||
seckey = default_seckey
|
||||
msg = default_msg
|
||||
sig = schnorr_sign(msg, seckey)
|
||||
|
||||
# Replace s with a number that's equal to the curve order
|
||||
sig = sig[0:32] + bytes_from_int(n)
|
||||
|
||||
return (None, pubkey_gen(seckey), msg, sig, "FALSE", "sig[32:64] is equal to curve order")
|
||||
|
||||
vectors = [
|
||||
vector0(),
|
||||
vector1(),
|
||||
vector2(),
|
||||
vector3(),
|
||||
vector4(),
|
||||
vector5(),
|
||||
vector6(),
|
||||
vector7(),
|
||||
vector8(),
|
||||
vector9(),
|
||||
vector10(),
|
||||
vector11(),
|
||||
vector12(),
|
||||
vector13(),
|
||||
]
|
||||
|
||||
# Converts the byte strings of a test vector into hex strings
|
||||
def bytes_to_hex(seckey, pubkey, msg, sig, result, comment):
|
||||
return (seckey.hex().upper() if seckey is not None else None, pubkey.hex().upper(), msg.hex().upper(), sig.hex().upper(), result, comment)
|
||||
|
||||
vectors = list(map(lambda vector: bytes_to_hex(vector[0], vector[1], vector[2], vector[3], vector[4], vector[5]), vectors))
|
||||
|
||||
def print_csv(vectors):
|
||||
writer = csv.writer(sys.stdout)
|
||||
writer.writerow(("index", "secret key", "public key", "message", "signature", "verification result", "comment"))
|
||||
for (i,v) in enumerate(vectors):
|
||||
writer.writerow((i,)+v)
|
||||
|
||||
print_csv(vectors)
|
Loading…
Add table
Reference in a new issue