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