-
Notifications
You must be signed in to change notification settings - Fork 0
/
tests.py
216 lines (178 loc) · 7.11 KB
/
tests.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# -*- coding: utf-8 -*-
import random
import pytest
random.seed(12312421412)
from bitcoin import privtopub, encode_pubkey
from bitcoin import ecdsa_raw_sign as b_ecdsa_raw_sign
from bitcoin import ecdsa_raw_recover as b_ecdsa_raw_recover
from bitcoin import ecdsa_recover as b_ecdsa_recover_der
import time
from c_secp256k1 import ecdsa_recover_compact as c_ecdsa_recover_compact
from c_secp256k1 import ecdsa_sign_compact as c_ecdsa_sign_compact
from c_secp256k1 import ecdsa_verify_compact as c_ecdsa_verify_compact
from c_secp256k1 import ecdsa_sign_raw as c_ecdsa_sign_raw
from c_secp256k1 import ecdsa_recover_raw as c_ecdsa_recover_raw
from c_secp256k1 import ecdsa_verify_raw as c_ecdsa_verify_raw
from c_secp256k1 import ecdsa_sign_der as c_ecdsa_sign_der
from c_secp256k1 import ecdsa_recover_der as c_ecdsa_recover_der
from c_secp256k1 import ecdsa_verify_der as c_ecdsa_verify_der
from c_secp256k1 import InvalidPubkeyError, InvalidSignatureError
from wrapper import Secp256k1_raw
from wrapper import Secp256k1_compact
lr = Secp256k1_raw()
lc = Secp256k1_compact()
priv = ''.join(chr(random.randint(0, 255)) for i in range(32))
pub = privtopub(priv)
msg32 = ''.join(chr(random.randint(0, 255)) for i in range(32))
msgN = ''.join(chr(random.randint(0, 255)) for i in range(128))
def test_raw():
vrs1 = b_ecdsa_raw_sign(msg32, priv)
assert isinstance(vrs1, tuple)
assert len(vrs1) == 3
vrs3 = c_ecdsa_sign_raw(msg32, priv)
p1 = b_ecdsa_raw_recover(msg32, vrs1)
p3 = c_ecdsa_recover_raw(msg32, vrs1)
p4 = c_ecdsa_recover_raw(msg32, vrs3)
p5 = b_ecdsa_raw_recover(msg32, vrs3)
vrs2 = lr.ecdsa_sign_raw(msg32, priv)
p9 = b_ecdsa_raw_recover(msg32, vrs2)
p10 = c_ecdsa_recover_raw(msg32, vrs2)
p7 = lr.ecdsa_recover_raw(msg32, vrs2)
vrs4 = lc.ecdsa_sign_compact(msg32, priv)
p8 = lr.ecdsa_recover_raw(msg32, vrs1)
# Ensure that recovered pub key is the same
assert encode_pubkey(p1, 'bin') == pub
assert p4 == pub
assert p7 == pub
assert p8 == pub
assert encode_pubkey(p8, 'bin') == pub
assert p10 == pub
assert encode_pubkey(p5, 'bin') == pub
# check wrong pub
wrong_vrs = c_ecdsa_sign_raw(msg32, 'x' * 32)
wrong_vrs2 = lr.ecdsa_sign_raw(msg32, 'x' * 32)
p2 = c_ecdsa_recover_raw(msg32, wrong_vrs)
p3 = lr.ecdsa_recover_raw(msg32, wrong_vrs2)
assert encode_pubkey(p2, 'bin') != pub
assert encode_pubkey(p3, 'bin') != pub
# verify
assert lr.ecdsa_verify_raw(msg32, vrs2, p7)
assert lc.ecdsa_verify_compact(msg32, vrs4, p7)
# check wrong pub
sig_vrs2 = c_ecdsa_sign_raw(msg32, 'x' * 32)
p2 = c_ecdsa_recover_raw(msg32, sig_vrs2)
assert p2 != pub
# check wrong sig
false_sig_vrs = sig_vrs2
assert not c_ecdsa_verify_raw(msg32, false_sig_vrs, pub)
assert not lr.ecdsa_verify_raw(msg32, false_sig_vrs, pub)
def _tampered_65b(b):
assert len(b) == 65
assert b[20] != 'E'
return b[:20] + 'E' + b[21:]
def _tampered_64b(b):
assert len(b) == 64
assert b[20] != 'E'
return b[:20] + 'E' + b[21:]
def test_compact():
sig_compact = c_ecdsa_sign_compact(msg32, priv)
sig2_compact = lc.ecdsa_sign_compact(msg32, priv)
assert isinstance(sig_compact, bytes)
assert len(sig_compact) == 65
assert isinstance(sig2_compact[0], bytes)
assert len(sig2_compact[0]) == 64
# recover
p3 = c_ecdsa_recover_compact(msg32, sig_compact)
p4 = lc.ecdsa_recover_compact(msg32, sig2_compact)
p5 = lc.ecdsa_recover_compact(msg32, sig_compact)
# verify
assert p3 == pub
assert p4 == pub
assert p5 == pub
assert c_ecdsa_verify_compact(msg32, sig_compact, pub)
# check wrong pub
sig_compact_2 = c_ecdsa_sign_compact(msg32, 'x' * 32)
p4 = c_ecdsa_recover_compact(msg32, sig_compact_2)
assert p4 != pub
# check wrong sig
false_sig_compact = _tampered_65b(sig_compact)
assert not c_ecdsa_verify_compact(msg32, false_sig_compact, pub)
def test_robustness():
sig_compact = c_ecdsa_sign_compact(msg32, priv)
sig_compact2 = lc.ecdsa_sign_compact(msg32, priv)
sc = (_tampered_64b(sig_compact2[0]), sig_compact2[1])
# must not segfault
# c_ecdsa_recover_compact(msg32, _tampered_65b(sig_compact))
lc.ecdsa_recover_compact(msg32, sc)
with pytest.raises(InvalidSignatureError):
c_ecdsa_recover_compact(msg32, sig_compact[:-1] + 'x')
def test_der():
sig_der = c_ecdsa_sign_der(msgN, priv)
assert isinstance(sig_der, bytes)
p3 = c_ecdsa_recover_der(msgN, sig_der)
assert p3 == pub
p2 = b_ecdsa_recover_der(msgN, sig_der)
assert p2 == pub.encode('hex')
assert c_ecdsa_verify_der(msgN, sig_der, pub)
# check wrong pub
with pytest.raises(InvalidPubkeyError):
c_ecdsa_verify_der(msgN, sig_der, _tampered_65b(pub))
# Recovery with pure python solution
def test_ecrecover(rounds=100):
vrs1 = b_ecdsa_raw_sign(msg32, priv)
st = time.time()
for i in range(rounds):
p = b_ecdsa_raw_recover(msg32, vrs1)
elapsed = time.time() - st
print 'py took: %.2fsecs / %dμs per op / %d recoveries per sec' % \
(elapsed, elapsed / rounds * 10**6, rounds / elapsed)
# Recovery with same random private key using cffi
def test_cecrecover(rounds=100):
vrs_compact = c_ecdsa_sign_compact(msg32, priv)
st = time.time()
for i in range(rounds):
p = c_ecdsa_recover_compact(msg32, vrs_compact)
elapsed = time.time() - st
print 'cffi took: %.2fsecs / %dμs per op / %d recoveries per sec' % \
(elapsed, elapsed / rounds * 10**6, rounds / elapsed)
print 'c takes: 300μs per op / 3000 recoveries per sec' # c wraped in go, according to gustav
# Recovery with same random private key using cffi
def test_lecrecover(rounds=100):
vrs_compact = lc.ecdsa_sign_compact(msg32, priv)
st = time.time()
for i in range(rounds):
p = lc.ecdsa_recover_compact(msg32, vrs_compact)
elapsed = time.time() - st
print 'py secp256k1 took: %.2fsecs / %dμs per op / %d recoveries per sec' % \
(elapsed, elapsed / rounds * 10**6, rounds / elapsed)
def rand32bytes():
return ''.join(chr(random.randint(0, 255)) for i in range(32))
def perf(rounds=1000):
privkeys = [rand32bytes() for i in range(rounds)]
messages = [rand32bytes() for i in range(rounds)]
# test sign
signatures = []
st = time.time()
for priv, msg in zip(privkeys, messages):
s = lc.ecdsa_sign_compact(msg32, priv)
signatures.append(s)
elapsed = time.time() - st
print 'cffi took: %.2fsecs / %dμs per op / %d signs per sec' % \
(elapsed, elapsed / rounds * 10**6, rounds / elapsed)
# test recover
pubs = []
st = time.time()
for sig, msg in zip(signatures, messages):
p = lc.ecdsa_recover_compact(msg32, sig)
pubs.append(p)
elapsed = time.time() - st
print 'cffi took: %.2fsecs / %dμs per op / %d recovers per sec' % \
(elapsed, elapsed / rounds * 10**6, rounds / elapsed)
# check
for pub, privkey in zip(pubs, privkeys)[:100]:
assert privtopub(privkey) == pub
if __name__ == '__main__':
test_ecrecover(100)
test_cecrecover(10000)
test_lecrecover(10000)
perf(10000)