~ubuntu-branches/debian/stretch/electrum/stretch

« back to all changes in this revision

Viewing changes to ecdsa/util.py

  • Committer: Package Import Robot
  • Author(s): Vasudev Kamath
  • Date: 2013-06-19 21:44:47 UTC
  • Revision ID: package-import@ubuntu.com-20130619214447-8n0u7ryn1ftu25w8
Tags: upstream-1.8
ImportĀ upstreamĀ versionĀ 1.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
import os
 
3
import math
 
4
import binascii
 
5
from hashlib import sha256
 
6
import der
 
7
from curves import orderlen
 
8
 
 
9
# RFC5480:
 
10
#   The "unrestricted" algorithm identifier is:
 
11
#     id-ecPublicKey OBJECT IDENTIFIER ::= {
 
12
#       iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
 
13
 
 
14
oid_ecPublicKey = (1, 2, 840, 10045, 2, 1)
 
15
encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey)
 
16
 
 
17
def randrange(order, entropy=None):
 
18
    """Return a random integer k such that 1 <= k < order, uniformly
 
19
    distributed across that range. For simplicity, this only behaves well if
 
20
    'order' is fairly close (but below) a power of 256. The try-try-again
 
21
    algorithm we use takes longer and longer time (on average) to complete as
 
22
    'order' falls, rising to a maximum of avg=512 loops for the worst-case
 
23
    (256**k)+1 . All of the standard curves behave well. There is a cutoff at
 
24
    10k loops (which raises RuntimeError) to prevent an infinite loop when
 
25
    something is really broken like the entropy function not working.
 
26
 
 
27
    Note that this function is not declared to be forwards-compatible: we may
 
28
    change the behavior in future releases. The entropy= argument (which
 
29
    should get a callable that behaves like os.entropy) can be used to
 
30
    achieve stability within a given release (for repeatable unit tests), but
 
31
    should not be used as a long-term-compatible key generation algorithm.
 
32
    """
 
33
    # we could handle arbitrary orders (even 256**k+1) better if we created
 
34
    # candidates bit-wise instead of byte-wise, which would reduce the
 
35
    # worst-case behavior to avg=2 loops, but that would be more complex. The
 
36
    # change would be to round the order up to a power of 256, subtract one
 
37
    # (to get 0xffff..), use that to get a byte-long mask for the top byte,
 
38
    # generate the len-1 entropy bytes, generate one extra byte and mask off
 
39
    # the top bits, then combine it with the rest. Requires jumping back and
 
40
    # forth between strings and integers a lot.
 
41
 
 
42
    if entropy is None:
 
43
        entropy = os.urandom
 
44
    assert order > 1
 
45
    bytes = orderlen(order)
 
46
    dont_try_forever = 10000 # gives about 2**-60 failures for worst case
 
47
    while dont_try_forever > 0:
 
48
        dont_try_forever -= 1
 
49
        candidate = string_to_number(entropy(bytes)) + 1
 
50
        if 1 <= candidate < order:
 
51
            return candidate
 
52
        continue
 
53
    raise RuntimeError("randrange() tried hard but gave up, either something"
 
54
                       " is very wrong or you got realllly unlucky. Order was"
 
55
                       " %x" % order)
 
56
 
 
57
class PRNG:
 
58
    # this returns a callable which, when invoked with an integer N, will
 
59
    # return N pseudorandom bytes. Note: this is a short-term PRNG, meant
 
60
    # primarily for the needs of randrange_from_seed__trytryagain(), which
 
61
    # only needs to run it a few times per seed. It does not provide
 
62
    # protection against state compromise (forward security).
 
63
    def __init__(self, seed):
 
64
        self.generator = self.block_generator(seed)
 
65
 
 
66
    def __call__(self, numbytes):
 
67
        return "".join([self.generator.next() for i in range(numbytes)])
 
68
 
 
69
    def block_generator(self, seed):
 
70
        counter = 0
 
71
        while True:
 
72
            for byte in sha256("prng-%d-%s" % (counter, seed)).digest():
 
73
                yield byte
 
74
            counter += 1
 
75
 
 
76
def randrange_from_seed__overshoot_modulo(seed, order):
 
77
    # hash the data, then turn the digest into a number in [1,order).
 
78
    #
 
79
    # We use David-Sarah Hopwood's suggestion: turn it into a number that's
 
80
    # sufficiently larger than the group order, then modulo it down to fit.
 
81
    # This should give adequate (but not perfect) uniformity, and simple
 
82
    # code. There are other choices: try-try-again is the main one.
 
83
    base = PRNG(seed)(2*orderlen(order))
 
84
    number = (int(binascii.hexlify(base), 16) % (order-1)) + 1
 
85
    assert 1 <= number < order, (1, number, order)
 
86
    return number
 
87
 
 
88
def lsb_of_ones(numbits):
 
89
    return (1 << numbits) - 1
 
90
def bits_and_bytes(order):
 
91
    bits = int(math.log(order-1, 2)+1)
 
92
    bytes = bits // 8
 
93
    extrabits = bits % 8
 
94
    return bits, bytes, extrabits
 
95
 
 
96
# the following randrange_from_seed__METHOD() functions take an
 
97
# arbitrarily-sized secret seed and turn it into a number that obeys the same
 
98
# range limits as randrange() above. They are meant for deriving consistent
 
99
# signing keys from a secret rather than generating them randomly, for
 
100
# example a protocol in which three signing keys are derived from a master
 
101
# secret. You should use a uniformly-distributed unguessable seed with about
 
102
# curve.baselen bytes of entropy. To use one, do this:
 
103
#   seed = os.urandom(curve.baselen) # or other starting point
 
104
#   secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order)
 
105
#   sk = SigningKey.from_secret_exponent(secexp, curve)
 
106
 
 
107
def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256):
 
108
    # hash the seed, then turn the digest into a number in [1,order), but
 
109
    # don't worry about trying to uniformly fill the range. This will lose,
 
110
    # on average, four bits of entropy.
 
111
    bits, bytes, extrabits = bits_and_bytes(order)
 
112
    if extrabits:
 
113
        bytes += 1
 
114
    base = hashmod(seed).digest()[:bytes]
 
115
    base = "\x00"*(bytes-len(base)) + base
 
116
    number = 1+int(binascii.hexlify(base), 16)
 
117
    assert 1 <= number < order
 
118
    return number
 
119
 
 
120
def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256):
 
121
    # like string_to_randrange_truncate_bytes, but only lose an average of
 
122
    # half a bit
 
123
    bits = int(math.log(order-1, 2)+1)
 
124
    maxbytes = (bits+7) // 8
 
125
    base = hashmod(seed).digest()[:maxbytes]
 
126
    base = "\x00"*(maxbytes-len(base)) + base
 
127
    topbits = 8*maxbytes - bits
 
128
    if topbits:
 
129
        base = chr(ord(base[0]) & lsb_of_ones(topbits)) + base[1:]
 
130
    number = 1+int(binascii.hexlify(base), 16)
 
131
    assert 1 <= number < order
 
132
    return number
 
133
 
 
134
def randrange_from_seed__trytryagain(seed, order):
 
135
    # figure out exactly how many bits we need (rounded up to the nearest
 
136
    # bit), so we can reduce the chance of looping to less than 0.5 . This is
 
137
    # specified to feed from a byte-oriented PRNG, and discards the
 
138
    # high-order bits of the first byte as necessary to get the right number
 
139
    # of bits. The average number of loops will range from 1.0 (when
 
140
    # order=2**k-1) to 2.0 (when order=2**k+1).
 
141
    assert order > 1
 
142
    bits, bytes, extrabits = bits_and_bytes(order)
 
143
    generate = PRNG(seed)
 
144
    while True:
 
145
        extrabyte = ""
 
146
        if extrabits:
 
147
            extrabyte = chr(ord(generate(1)) & lsb_of_ones(extrabits))
 
148
        guess = string_to_number(extrabyte + generate(bytes)) + 1
 
149
        if 1 <= guess < order:
 
150
            return guess
 
151
 
 
152
 
 
153
def number_to_string(num, order):
 
154
    l = orderlen(order)
 
155
    fmt_str = "%0" + str(2*l) + "x"
 
156
    string = binascii.unhexlify(fmt_str % num)
 
157
    assert len(string) == l, (len(string), l)
 
158
    return string
 
159
 
 
160
def string_to_number(string):
 
161
    return int(binascii.hexlify(string), 16)
 
162
 
 
163
def string_to_number_fixedlen(string, order):
 
164
    l = orderlen(order)
 
165
    assert len(string) == l, (len(string), l)
 
166
    return int(binascii.hexlify(string), 16)
 
167
 
 
168
# these methods are useful for the sigencode= argument to SK.sign() and the
 
169
# sigdecode= argument to VK.verify(), and control how the signature is packed
 
170
# or unpacked.
 
171
 
 
172
def sigencode_strings(r, s, order):
 
173
    r_str = number_to_string(r, order)
 
174
    s_str = number_to_string(s, order)
 
175
    return (r_str, s_str)
 
176
 
 
177
def sigencode_string(r, s, order):
 
178
    # for any given curve, the size of the signature numbers is
 
179
    # fixed, so just use simple concatenation
 
180
    r_str, s_str = sigencode_strings(r, s, order)
 
181
    return r_str + s_str
 
182
 
 
183
def sigencode_der(r, s, order):
 
184
    return der.encode_sequence(der.encode_integer(r), der.encode_integer(s))
 
185
 
 
186
 
 
187
def sigdecode_string(signature, order):
 
188
    l = orderlen(order)
 
189
    assert len(signature) == 2*l, (len(signature), 2*l)
 
190
    r = string_to_number_fixedlen(signature[:l], order)
 
191
    s = string_to_number_fixedlen(signature[l:], order)
 
192
    return r, s
 
193
 
 
194
def sigdecode_strings(rs_strings, order):
 
195
    (r_str, s_str) = rs_strings
 
196
    l = orderlen(order)
 
197
    assert len(r_str) == l, (len(r_str), l)
 
198
    assert len(s_str) == l, (len(s_str), l)
 
199
    r = string_to_number_fixedlen(r_str, order)
 
200
    s = string_to_number_fixedlen(s_str, order)
 
201
    return r, s
 
202
 
 
203
def sigdecode_der(sig_der, order):
 
204
    #return der.encode_sequence(der.encode_integer(r), der.encode_integer(s))
 
205
    rs_strings, empty = der.remove_sequence(sig_der)
 
206
    if empty != "":
 
207
        raise der.UnexpectedDER("trailing junk after DER sig: %s" %
 
208
                                binascii.hexlify(empty))
 
209
    r, rest = der.remove_integer(rs_strings)
 
210
    s, empty = der.remove_integer(rest)
 
211
    if empty != "":
 
212
        raise der.UnexpectedDER("trailing junk after DER numbers: %s" %
 
213
                                binascii.hexlify(empty))
 
214
    return r, s
 
215