1import unittest 2from util import * 3 4FLAG_ECDSA, FLAG_SCHNORR, FLAG_GRIND_R, FLAG_RECOVERABLE = 1, 2, 4, 8 5EX_PRIV_KEY_LEN, EC_PUBLIC_KEY_LEN, EC_PUBLIC_KEY_UNCOMPRESSED_LEN = 32, 33, 65 6EC_SIGNATURE_LEN, EC_SIGNATURE_DER_MAX_LEN = 64, 72 7BITCOIN_MESSAGE_HASH_FLAG = 1 8 9class SignTests(unittest.TestCase): 10 11 def get_sign_cases(self): 12 lines = [] 13 with open(root_dir + 'src/data/ecdsa_secp256k1_vectors.txt', 'r') as f: 14 for l in f.readlines(): 15 if len(l.strip()) and not l.startswith('#'): 16 lines.append(self.cbufferize(l.strip().split(','))) 17 return lines 18 19 def cbufferize(self, values): 20 conv = lambda v: make_cbuffer(v)[0] if type(v) is str else v 21 return [conv(v) for v in values] 22 23 def sign(self, priv_key, msg, flags, out_buf, out_len=None): 24 blen = lambda b: 0 if b is None else len(b) 25 if out_len is None: 26 out_len = blen(out_buf) 27 return wally_ec_sig_from_bytes(priv_key, blen(priv_key), 28 msg, blen(msg), flags, out_buf, out_len) 29 30 31 def test_sign_and_verify(self): 32 sig, sig2, sig_low_r = self.cbufferize(['00' * EC_SIGNATURE_LEN] * 3) 33 der, der_len = make_cbuffer('00' * EC_SIGNATURE_DER_MAX_LEN) 34 35 for case in self.get_sign_cases(): 36 priv_key, msg, nonce, r, s = case 37 38 if wally_ec_private_key_verify(priv_key, len(priv_key)) != WALLY_OK: 39 # Some test vectors have invalid private keys which other 40 # libraries allow. secp fails these keys so don't test them. 41 continue 42 43 # Sign 44 set_fake_ec_nonce(nonce) 45 ret = self.sign(priv_key, msg, FLAG_ECDSA, sig) 46 self.assertEqual(ret, WALLY_OK) 47 self.assertEqual(h(r), h(sig[0:32])) 48 self.assertEqual(h(s), h(sig[32:64])) 49 50 # Also sign with low-R grinding 51 set_fake_ec_nonce(None) 52 ret = self.sign(priv_key, msg, FLAG_ECDSA|FLAG_GRIND_R, sig_low_r) 53 self.assertEqual(ret, WALLY_OK) 54 55 # Check signature conversions 56 ret, written = wally_ec_sig_to_der(sig, len(sig), der, der_len) 57 self.assertEqual(ret, WALLY_OK) 58 ret = wally_ec_sig_from_der(der, written, sig2, len(sig2)) 59 self.assertEqual((ret, h(sig)), (WALLY_OK, h(sig2))) 60 ret = wally_ec_sig_normalize(sig2, len(sig2), sig, len(sig)) 61 self.assertEqual((ret, h(sig)), (WALLY_OK, h(sig2))) # All sigs low-s 62 63 # Verify 64 pub_key, _ = make_cbuffer('00' * 33) 65 ret = wally_ec_public_key_from_private_key(priv_key, len(priv_key), 66 pub_key, len(pub_key)) 67 self.assertEqual(ret, WALLY_OK) 68 for s in [sig, sig_low_r]: 69 ret = wally_ec_sig_verify(pub_key, len(pub_key), msg, len(msg), 70 FLAG_ECDSA, s, len(s)) 71 self.assertEqual(ret, WALLY_OK) 72 73 # Validate public key 74 pub_unc, _ = make_cbuffer('00' * 65) 75 ret = wally_ec_public_key_decompress(pub_key, len(pub_key), pub_unc, len(pub_unc)) 76 self.assertEqual(ret, WALLY_OK) 77 self.assertEqual(wally_ec_public_key_verify(pub_key, len(pub_key)), WALLY_OK) 78 self.assertEqual(wally_ec_public_key_verify(pub_unc, len(pub_unc)), WALLY_OK) 79 80 set_fake_ec_nonce(None) 81 82 83 def test_invalid_inputs(self): 84 out_buf, out_len = make_cbuffer('00' * EC_SIGNATURE_LEN) 85 86 priv_key, msg = self.cbufferize(['11' * 32, '22' * 32]) 87 priv_bad, msg_bad = self.cbufferize(['FF' * 32, '22' * 33]) 88 FLAGS_BOTH = FLAG_ECDSA | FLAG_SCHNORR 89 90 # Signing 91 cases = [(None, msg, FLAG_ECDSA), # Null priv_key 92 (('11' * 33), msg, FLAG_ECDSA), # Wrong priv_key len 93 (priv_bad, msg, FLAG_ECDSA), # Bad private key 94 (priv_key, None, FLAG_ECDSA), # Null message 95 (priv_key, msg_bad, FLAG_ECDSA), # Wrong message len 96 (priv_key, msg, 0), # No flags set 97 (priv_key, msg, FLAGS_BOTH), # Mutually exclusive 98 (priv_key, msg, 0x4)] # Unknown flag 99 100 for priv_key, msg, flags in cases: 101 ret = self.sign(priv_key, msg, flags, out_buf) 102 self.assertEqual(ret, WALLY_EINVAL) 103 104 for o, o_len in [(None, 32), (out_buf, -1)]: # Null out, Wrong out len 105 ret = self.sign(priv_key, msg, FLAG_ECDSA, o, o_len) 106 self.assertEqual(ret, WALLY_EINVAL) 107 108 # wally_ec_private_key_verify 109 for pk, pk_len in [(None, len(priv_key)), # Null priv_key 110 (priv_key, 10), # Wrong priv_key len 111 (priv_bad, len(priv_key))]: # Bad private key 112 self.assertEqual(wally_ec_private_key_verify(pk, pk_len), WALLY_EINVAL) 113 114 # wally_ec_public_key_verify 115 pub_key, _ = make_cbuffer('02' + '22' * 32) 116 pub_bad, _ = make_cbuffer('02' + '00' * 32) 117 for pub, pub_len in [(None, len(pub_key)), # Null pub_key 118 (pub_key, 32), # Wrong pub_key len 119 (pub_bad, len(pub_key))]: # Bad public key 120 self.assertEqual(wally_ec_public_key_verify(pub, pub_len), WALLY_EINVAL) 121 122 # wally_ec_public_key_decompress 123 pub, _ = make_cbuffer('02' + '22' * 32) 124 out_buf, out_len = make_cbuffer('00' * EC_PUBLIC_KEY_UNCOMPRESSED_LEN) 125 126 cases = [(None, len(pub), out_buf, out_len), # Null pub 127 (pub, 32, out_buf, out_len), # Wrong pub len 128 (pub, len(pub), None, out_len), # Null out 129 (pub, len(pub), out_buf, 64)] # Wrong out len 130 131 for p, p_len, o, o_len in cases: 132 ret = wally_ec_public_key_decompress(p, p_len, o, o_len) 133 self.assertEqual(ret, WALLY_EINVAL) 134 135 # wally_ec_sig_to_der 136 sig, _ = make_cbuffer('13' * EC_SIGNATURE_LEN) 137 out_buf, out_len = make_cbuffer('00' * EC_SIGNATURE_DER_MAX_LEN) 138 139 cases = [(None, len(sig), out_buf, out_len), # Null sig 140 (sig, 15, out_buf, out_len), # Wrong sig len 141 (sig, len(sig), None, out_len), # Null out 142 (sig, len(sig), out_buf, 15)] # Wrong out len 143 144 for s, s_len, o, o_len in cases: 145 ret, written = wally_ec_sig_to_der(s, s_len, o, o_len) 146 self.assertEqual((ret, written), (WALLY_EINVAL, 0)) 147 148 # wally_ec_public_key_from_private_key 149 out_buf, out_len = make_cbuffer('00' * EC_PUBLIC_KEY_LEN) 150 cases = [(None, len(priv_key), out_buf, len(out_buf)), # Null priv_key 151 (priv_key, 10, out_buf, len(out_buf)), # Wrong priv_key len 152 (priv_bad, len(priv_key), out_buf, len(out_buf)), # Bad private key 153 (priv_key, len(priv_key), None, len(out_buf)), # Null out 154 (priv_key, len(priv_key), out_buf, 10)] # Wrong out len 155 156 for pk, pk_len, o, o_len in cases: 157 ret = wally_ec_public_key_from_private_key(pk, pk_len, o, o_len); 158 self.assertEqual(ret, WALLY_EINVAL) 159 160 161 def test_format_message(self): 162 PREFIX, MAX_LEN = b'\x18Bitcoin Signed Message:\n', 64 * 1024 - 64 163 out_buf, out_len = make_cbuffer('00' * 64 * 1024) 164 cases = [(b'a', b'\x01'), 165 (b'aaa', b'\x03'), 166 (b'a' * 252, b'\xfc'), 167 (b'a' * 253, b'\xfd\xfd\x00'), 168 (b'a' * 254, b'\xfd\xfe\x00'), 169 (b'a' * 255, b'\xfd\xff\x00'), 170 (b'a' * 256, b'\xfd\x00\x01'), 171 (b'a' * 257, b'\xfd\x01\x01'), 172 (b'a' * MAX_LEN, b'\xfd\xc0\xff')] 173 for msg, varint, in cases: 174 fn = lambda flags, ol: wally_format_bitcoin_message(msg, len(msg), flags, 175 out_buf, ol) 176 for flags in (0, BITCOIN_MESSAGE_HASH_FLAG): 177 expected = PREFIX + varint + msg 178 if flags: 179 buf, buf_len = make_cbuffer('00'*32) 180 self.assertEqual(WALLY_OK, wally_sha256d(expected, len(expected), buf, buf_len)) 181 expected = buf 182 183 ret, written = fn(flags, out_len) 184 self.assertEqual((ret, written), (WALLY_OK, len(expected))) 185 self.assertEqual(out_buf[:written], expected) 186 187 ret, written = fn(flags, 1) # Short length 188 self.assertEqual((ret, written), (WALLY_OK, len(expected))) 189 190 191 # Invalid cases 192 msg = 'a' 193 cases = [(None, len(msg), 0, out_buf, out_len), # Null message 194 (msg, 0, 0, out_buf, out_len), # Zero length message 195 (msg, MAX_LEN + 1, 0, out_buf, out_len), # Message too large 196 (msg, len(msg), 2, out_buf, out_len), # Bad flags 197 (msg, len(msg), 0, None, out_len)] # Null output 198 for msg, msg_len, flags, o, o_len in cases: 199 ret, written = wally_format_bitcoin_message(msg, msg_len, flags, o, o_len) 200 self.assertEqual(ret, WALLY_EINVAL) 201 202 203 def test_recoverable_sig(self): 204 priv_key, msg, out1, out2, pub_key, pub_key_rec = self.cbufferize( 205 ['11' * 32, '22' * 32, '00' * 65, '00' * 64, '00' * 32, '00' * 32]) 206 207 flags = FLAG_ECDSA | FLAG_RECOVERABLE 208 self.assertEqual(WALLY_OK, self.sign(priv_key, msg, flags, out1)) 209 210 self.assertEqual(WALLY_OK, wally_ec_public_key_from_private_key( 211 priv_key, 32, pub_key, 33)) 212 i = 1 if h(pub_key[:1]) == '02' else 0 213 self.assertEqual(27 + i + 4, int(h(out1[:1]), 16)) 214 215 flags = FLAG_ECDSA 216 self.assertEqual(WALLY_OK, self.sign(priv_key, msg, flags, out2)) 217 218 self.assertEqual(out1[1:], out2) 219 220 self.assertEqual(WALLY_OK, wally_ec_sig_to_public_key(msg, 32, out1, 65, pub_key_rec, 33)) 221 self.assertEqual(pub_key, pub_key_rec) 222 223 self.assertEqual(WALLY_OK, wally_ec_sig_verify(pub_key, 33, msg, 32, FLAG_ECDSA, out2, 64)) 224 self.assertEqual(WALLY_EINVAL, wally_ec_sig_verify(pub_key, 33, msg, 32, FLAG_ECDSA, out1, 65)) 225 self.assertEqual(WALLY_OK, wally_ec_sig_verify(pub_key, 33, msg, 32, FLAG_ECDSA, out1[1:], 64)) 226 227 # Invalid cases 228 for args in [ 229 (priv_key, msg, FLAG_RECOVERABLE, out1, 65), # Singing algorithm not specified 230 (priv_key, msg, FLAG_ECDSA, out1, 65), # Incorrect length 231 (priv_key, msg, FLAG_ECDSA | FLAG_RECOVERABLE, out1, 64), # Incorrect length 232 (priv_key, msg, FLAG_SCHNORR | FLAG_RECOVERABLE, out1, 65), # Mutually exclusive 233 ]: 234 self.assertEqual(WALLY_EINVAL, self.sign(*args)) 235 236 for args in [ 237 (None, 32, out1, 65, pub_key, 33), # Missing message 238 (msg, 31, out1, 65, pub_key, 33), # Incorrect messsage length 239 (msg, 32, None, 65, pub_key, 33), # Missing signature 240 (msg, 32, out1, 64, pub_key, 33), # Incorrect signature length 241 (msg, 32, out1, 65, None, 33), # Missing pubkey 242 (msg, 32, out1, 65, pub_key, 32), # Incorrect pubkey length 243 (priv_key, 32, out1, 65, pub_key, 32), # Incorrect signature 244 ]: 245 self.assertEqual(WALLY_EINVAL, wally_ec_sig_to_public_key(*args)) 246 247 def test_s2c(self): 248 priv_key, pub_key, msg, s2c_data, sig_out, s2c_opening_out = self.cbufferize( 249 ['11' * 32, '00' * 32, '22' * 32, '33' * 32, '00' * 64, '00' * 33]) 250 251 flags = FLAG_ECDSA 252 253 self.assertEqual(WALLY_OK, wally_s2c_sig_from_bytes(priv_key, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64)) 254 255 self.assertEqual(WALLY_OK, wally_ec_public_key_from_private_key(priv_key, 32, pub_key, 33)) 256 self.assertEqual(WALLY_OK, wally_ec_sig_verify(pub_key, 33, msg, 32, flags, sig_out, 64)) 257 258 self.assertEqual(WALLY_OK, wally_s2c_commitment_verify(sig_out, 64, s2c_data, 32, s2c_opening_out, 33, flags)) 259 260 # Invalid cases 261 for args in [ 262 (None, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64), # Missing privkey 263 (priv_key, 31, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64), # Incorrect privkey length 264 (priv_key, 32, None, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64), # Missing message 265 (priv_key, 32, msg, 31, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64), # Incorrect message length 266 (priv_key, 32, msg, 32, None, 32, flags, s2c_opening_out, 33, sig_out, 64), # Missing s2c data 267 (priv_key, 32, msg, 32, s2c_data, 31, flags, s2c_opening_out, 33, sig_out, 64), # Incorrect s2c data length 268 (priv_key, 32, msg, 32, s2c_data, 32, 0, s2c_opening_out, 33, sig_out, 64), # Unsupported flags 269 (priv_key, 32, msg, 32, s2c_data, 32, flags, None, 33, sig_out, 64), # Missing s2c opening 270 (priv_key, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 32, sig_out, 64), # Incorrect s2c opening length 271 (priv_key, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, None, 64), # Missing sig 272 (priv_key, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 63), # Incorrect sig length 273 ]: 274 self.assertEqual(WALLY_EINVAL, wally_s2c_sig_from_bytes(*args)) 275 276 inv_sig, inv_s2c_data, inv_s2c_opening = self.cbufferize( 277 ['ff' * 64, 'ff' * 32, 'ff' * 33]) 278 279 for args in [ 280 (None, 64, s2c_data, 32, s2c_opening_out, 33, flags), # Missing signature 281 (sig_out, 63, s2c_data, 32, s2c_opening_out, 33, flags), # Incorrect signature length 282 (inv_sig, 64, s2c_data, 32, s2c_opening_out, 33, flags), # Invalid signature 283 (sig_out, 64, None, 32, s2c_opening_out, 33, flags), # Missing s2c data 284 (sig_out, 64, s2c_data, 31, s2c_opening_out, 33, flags), # Incorrect s2c data length 285 (sig_out, 64, inv_s2c_data, 32, s2c_opening_out, 33, flags), # Invalid s2c data 286 (sig_out, 64, s2c_data, 32, None, 33, flags), # Missing s2c opening 287 (sig_out, 64, s2c_data, 32, s2c_opening_out, 32, flags), # Incorrect s2c opening length 288 (sig_out, 64, s2c_data, 32, inv_s2c_opening, 33, flags), # Invalid s2c opening 289 (sig_out, 64, s2c_data, 32, s2c_opening_out, 33, 0), # Unsupported flags 290 ]: 291 self.assertEqual(WALLY_EINVAL, wally_s2c_commitment_verify(*args)) 292 293 294if __name__ == '__main__': 295 unittest.main() 296