You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

183 lines
8.1 KiB

import unittest
from util import *
from hashlib import sha256
FLAG_ECDSA, FLAG_SCHNORR = 1, 2
EX_PRIV_KEY_LEN, EC_PUBIC_KEY_LEN, EC_PUBIC_KEY_UNCOMPRESSED_LEN = 32, 33, 65
EC_SIGNATURE_LEN, EC_SIGNATURE_DER_MAX_LEN = 64, 72
BITCOIN_MESSAGE_HASH_FLAG = 1
class SignTests(unittest.TestCase):
def get_sign_cases(self):
lines = []
with open(root_dir + 'src/data/ecdsa_secp256k1_vectors.txt', 'r') as f:
for l in f.readlines():
if len(l.strip()) and not l.startswith('#'):
lines.append(self.cbufferize(l.strip().split(',')))
return lines
def cbufferize(self, values):
conv = lambda v: make_cbuffer(v)[0] if type(v) is str else v
return [conv(v) for v in values]
def sign(self, priv_key, msg, flags, out_buf, out_len=None):
blen = lambda b: 0 if b is None else len(b)
if out_len is None:
out_len = blen(out_buf)
return wally_ec_sig_from_bytes(priv_key, blen(priv_key),
msg, blen(msg), flags, out_buf, out_len)
def test_sign_and_verify(self):
sig, sig2 = self.cbufferize(['00' * EC_SIGNATURE_LEN] * 2)
der, der_len = make_cbuffer('00' * EC_SIGNATURE_DER_MAX_LEN)
for case in self.get_sign_cases():
priv_key, msg, nonce, r, s = case
if wally_ec_private_key_verify(priv_key, len(priv_key)) != WALLY_OK:
# Some test vectors have invalid private keys which other
# libraries allow. secp fails these keys so don't test them.
continue
# Sign
set_fake_ec_nonce(nonce)
ret = self.sign(priv_key, msg, FLAG_ECDSA, sig)
self.assertEqual(ret, WALLY_OK)
self.assertEqual(h(r), h(sig[0:32]))
self.assertEqual(h(s), h(sig[32:64]))
# Check signature conversions
ret, written = wally_ec_sig_to_der(sig, len(sig), der, der_len)
self.assertEqual(ret, WALLY_OK)
ret = wally_ec_sig_from_der(der, written, sig2, len(sig2))
self.assertEqual((ret, h(sig)), (WALLY_OK, h(sig2)))
ret = wally_ec_sig_normalize(sig2, len(sig2), sig, len(sig))
self.assertEqual((ret, h(sig)), (WALLY_OK, h(sig2))) # All sigs low-s
# Verify
pub_key, _ = make_cbuffer('00' * 33)
ret = wally_ec_public_key_from_private_key(priv_key, len(priv_key),
pub_key, len(pub_key))
self.assertEqual(ret, WALLY_OK)
ret = wally_ec_sig_verify(pub_key, len(pub_key), msg, len(msg),
FLAG_ECDSA, sig, len(sig))
self.assertEqual(ret, WALLY_OK)
set_fake_ec_nonce(None)
def test_invalid_inputs(self):
out_buf, out_len = make_cbuffer('00' * EC_SIGNATURE_LEN)
priv_key, msg = self.cbufferize(['11' * 32, '22' * 32])
priv_bad, msg_bad = self.cbufferize(['FF' * 32, '22' * 33])
FLAGS_BOTH = FLAG_ECDSA | FLAG_SCHNORR
# Signing
cases = [(None, msg, FLAG_ECDSA), # Null priv_key
(('11' * 33), msg, FLAG_ECDSA), # Wrong priv_key len
(priv_bad, msg, FLAG_ECDSA), # Bad private key
(priv_key, None, FLAG_ECDSA), # Null message
(priv_key, msg_bad, FLAG_ECDSA), # Wrong message len
(priv_key, msg, 0), # No flags set
(priv_key, msg, FLAGS_BOTH), # Mutually exclusive
(priv_key, msg, 0x4)] # Unknown flag
for priv_key, msg, flags in cases:
ret = self.sign(priv_key, msg, flags, out_buf)
self.assertEqual(ret, WALLY_EINVAL)
for o, o_len in [(None, 32), (out_buf, -1)]: # Null out, Wrong out len
ret = self.sign(priv_key, msg, FLAG_ECDSA, o, o_len)
self.assertEqual(ret, WALLY_EINVAL)
# wally_ec_private_key_verify
for pk, pk_len in [(None, len(priv_key)), # Null priv_key
(priv_key, 10), # Wrong priv_key len
(priv_bad, len(priv_key))]: # Bad private key
self.assertEqual(wally_ec_private_key_verify(pk, pk_len), WALLY_EINVAL)
# wally_ec_public_key_decompress
sig, _ = make_cbuffer('13' * EC_SIGNATURE_LEN)
out_buf, out_len = make_cbuffer('00' * EC_PUBIC_KEY_UNCOMPRESSED_LEN)
cases = [(None, len(sig), out_buf, out_len), # Null sig
(sig, 15, out_buf, out_len), # Wrong sig len
(sig, len(sig), None, out_len), # Null out
(sig, len(sig), out_buf, 15)] # Wrong out len
for s, s_len, o, o_len in cases:
ret, written = wally_ec_sig_to_der(s, s_len, o, o_len)
self.assertEqual((ret, written), (WALLY_EINVAL, 0))
# wally_ec_sig_to_der
sig, _ = make_cbuffer('13' * EC_SIGNATURE_LEN)
out_buf, out_len = make_cbuffer('00' * EC_SIGNATURE_DER_MAX_LEN)
cases = [(None, len(sig), out_buf, out_len), # Null sig
(sig, 15, out_buf, out_len), # Wrong sig len
(sig, len(sig), None, out_len), # Null out
(sig, len(sig), out_buf, 15)] # Wrong out len
for s, s_len, o, o_len in cases:
ret, written = wally_ec_sig_to_der(s, s_len, o, o_len)
self.assertEqual((ret, written), (WALLY_EINVAL, 0))
# wally_ec_public_key_from_private_key
out_buf, out_len = make_cbuffer('00' * EC_PUBIC_KEY_LEN)
cases = [(None, len(priv_key), out_buf, len(out_buf)), # Null priv_key
(priv_key, 10, out_buf, len(out_buf)), # Wrong priv_key len
(priv_bad, len(priv_key), out_buf, len(out_buf)), # Bad private key
(priv_key, len(priv_key), None, len(out_buf)), # Null out
(priv_key, len(priv_key), out_buf, 10)] # Wrong out len
for pk, pk_len, o, o_len in cases:
ret = wally_ec_public_key_from_private_key(pk, pk_len, o, o_len);
self.assertEqual(ret, WALLY_EINVAL)
def test_format_message(self):
PREFIX, MAX_LEN = b'\x18Bitcoin Signed Message:\n', 64 * 1024 - 64
out_buf, out_len = make_cbuffer('00' * 64 * 1024)
cases = [(b'a', b'\x01'),
(b'aaa', b'\x03'),
(b'a' * 252, b'\xfc'),
(b'a' * 253, b'\xfd\xfd\x00'),
(b'a' * 254, b'\xfd\xfe\x00'),
(b'a' * 255, b'\xfd\xff\x00'),
(b'a' * 256, b'\xfd\x00\x01'),
(b'a' * 257, b'\xfd\x01\x01'),
(b'a' * MAX_LEN, b'\xfd\xc0\xff')]
for msg, varint, in cases:
fn = lambda flags, ol: wally_format_bitcoin_message(msg, len(msg), flags,
out_buf, ol)
for flags in (0, BITCOIN_MESSAGE_HASH_FLAG):
expected = PREFIX + varint + msg
if flags:
expected = sha256(sha256(expected).digest()).digest()
ret, written = fn(flags, out_len)
self.assertEqual((ret, written), (WALLY_OK, len(expected)))
self.assertEqual(out_buf[:written], expected)
ret, written = fn(flags, 1) # Short length
self.assertEqual((ret, written), (WALLY_OK, len(expected)))
# Invalid cases
msg = 'a'
cases = [(None, len(msg), 0, out_buf, out_len), # Null message
(msg, 0, 0, out_buf, out_len), # Zero length message
(msg, MAX_LEN + 1, 0, out_buf, out_len), # Message too large
(msg, len(msg), 2, out_buf, out_len), # Bad flags
(msg, len(msg), 0, None, out_len)] # Null output
for msg, msg_len, flags, o, o_len in cases:
ret, written = wally_format_bitcoin_message(msg, msg_len, flags, o, o_len)
self.assertEqual(ret, WALLY_EINVAL)
if __name__ == '__main__':
unittest.main()