/
ecdsa.py
125 lines (86 loc) · 3.18 KB
/
ecdsa.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
# -*- coding: utf-8 -*-
# Copyright (C) 2015 Björn Edström <be@bjrn.se>
import asymmetric
import curve
import util
import numbertheory
def break_ecdsa(curve_obj, hash_int, hash_num_bits, sig1, sig2, msg1, msg2):
"""Recover the private key from two ECDSA signatures that have been
generated by accidentally re-using the same random number k.
Given two signatures (r, s) and (r, s') and two messages msg1,
msg2, calculate the random number k and using that recover the
private key, completely breaking the system.
This is to show the catastrophic failure scenarios inherit in
using random numbers with (EC)DSA.
Returns a tuple (k, <private key>).
"""
(r, s) = sig1
(r_, s_) = sig2
if r != r_:
raise ValueError('cannot attack this')
e = hash_int(msg1)
L_n = util.count_bits(curve_obj.order)
z = e >> max(hash_num_bits - L_n, 0)
e_ = hash_int(msg2)
z_ = e_ >> max(hash_num_bits - L_n, 0)
def div(a, b):
return (a * numbertheory.inverse_of(b, curve_obj.order)) % curve_obj.order
k = div(z - z_, s - s_)
priv = div(s*k - z, r)
return (k, priv)
def ecdsa_sign(curve_obj, hash_int, hash_num_bits, private_key, message, k=None):
"""Sign message using the private key.
Returns the signature (r, s).
@param hash_int: a hash function mapping to an integer, for example
lambda m: util.be2int(hashlib.sha256(m).digest())
@param hash_num_bits: the number of bits in the digest above, for
example 256.
"""
n = curve_obj.order
if k is None:
k = util.randint(1, n - 1)
e = hash_int(message)
L_n = util.count_bits(n)
z = e >> max(hash_num_bits - L_n, 0)
while True:
(x1, y1) = curve.mul(k, curve_obj.base_point, curve_obj.curve)
r = x1 % curve_obj.order
if r == 0:
continue
k_neg = numbertheory.inverse_of(k, n)
s = (k_neg * (z + r * private_key)) % n
if s == 0:
continue
break
return (r, s)
def ecdsa_verify(curve_obj, hash_int, hash_num_bits, public_key, message, signature):
"""Verify that signature is over the message using the public key.
Returns True if so, False otherwise.
Otherwise similar to ecdsa_sign() in usage.
"""
n = curve_obj.order
# Verify
if public_key == curve_obj.base_point or \
curve_obj.curve.invert_point(public_key) == curve_obj.base_point: # XXX: Check inverted too?
return False
if not curve_obj.curve.point_on_curve(public_key):
return False
if not curve.mul(curve_obj.order, public_key, curve_obj.curve) == curve_obj.curve.neutral_point():
return False
(r, s) = signature
if not 1 <= r <= n - 1:
return False
if not 1 <= s <= n - 1:
return False
e = hash_int(message)
L_n = util.count_bits(n)
z = e >> max(hash_num_bits - L_n, 0)
# Verify
w = numbertheory.inverse_of(s, curve_obj.order) % n
u_1 = (z * w) % n
u_2 = (r * w) % n
(x1, y1) = curve_obj.curve.add_points(
curve.mul(u_1, curve_obj.base_point, curve_obj.curve),
curve.mul(u_2, public_key, curve_obj.curve)
)
return (r % n) == (x1 % n)