Skip to content

Commit 96566c7

Browse files
authored
Merge pull request #169 from tomato42/bitwise-randrange
Bitwise randrange
2 parents fed45a6 + 515fb67 commit 96566c7

File tree

1 file changed

+27
-33
lines changed

1 file changed

+27
-33
lines changed

src/ecdsa/util.py

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import math
55
import binascii
6+
import sys
67
from hashlib import sha256
78
from six import PY3, int2byte, b, next
89
from . import der
@@ -16,12 +17,22 @@
1617
oid_ecPublicKey = (1, 2, 840, 10045, 2, 1)
1718
encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey)
1819

20+
if sys.version > '3':
21+
def entropy_to_bits(ent_256):
22+
"""Convert a bytestring to string of 0's and 1's"""
23+
return bin(int.from_bytes(ent_256, 'big'))[2:].zfill(len(ent_256)*8)
24+
else:
25+
def entropy_to_bits(ent_256):
26+
"""Convert a bytestring to string of 0's and 1's"""
27+
return ''.join(bin(ord(x))[2:].zfill(8) for x in ent_256)
1928

20-
def bit_length(num):
21-
# http://docs.python.org/dev/library/stdtypes.html#int.bit_length
22-
s = bin(num) # binary representation: bin(-37) --> '-0b100101'
23-
s = s.lstrip('-0b') # remove leading zeros and minus sign
24-
return len(s) # len('100101') --> 6
29+
30+
if sys.version < '2.7': #Can't add a method to a built-in type so we are stuck with this
31+
def bit_length(x):
32+
return len(bin(x)) - 2
33+
else:
34+
def bit_length(x):
35+
return x.bit_length() or 1
2536

2637

2738
def orderlen(order):
@@ -30,43 +41,26 @@ def orderlen(order):
3041

3142
def randrange(order, entropy=None):
3243
"""Return a random integer k such that 1 <= k < order, uniformly
33-
distributed across that range. For simplicity, this only behaves well if
34-
'order' is fairly close (but below) a power of 256. The try-try-again
35-
algorithm we use takes longer and longer time (on average) to complete as
36-
'order' falls, rising to a maximum of avg=512 loops for the worst-case
37-
(256**k)+1 . All of the standard curves behave well. There is a cutoff at
38-
10k loops (which raises RuntimeError) to prevent an infinite loop when
39-
something is really broken like the entropy function not working.
44+
distributed across that range. Worst case should be a mean of 2 loops at
45+
(2**k)+2.
4046
4147
Note that this function is not declared to be forwards-compatible: we may
4248
change the behavior in future releases. The entropy= argument (which
4349
should get a callable that behaves like os.urandom) can be used to
4450
achieve stability within a given release (for repeatable unit tests), but
4551
should not be used as a long-term-compatible key generation algorithm.
4652
"""
47-
# we could handle arbitrary orders (even 256**k+1) better if we created
48-
# candidates bit-wise instead of byte-wise, which would reduce the
49-
# worst-case behavior to avg=2 loops, but that would be more complex. The
50-
# change would be to round the order up to a power of 256, subtract one
51-
# (to get 0xffff..), use that to get a byte-long mask for the top byte,
52-
# generate the len-1 entropy bytes, generate one extra byte and mask off
53-
# the top bits, then combine it with the rest. Requires jumping back and
54-
# forth between strings and integers a lot.
55-
53+
assert order > 1
5654
if entropy is None:
5755
entropy = os.urandom
58-
assert order > 1
59-
bytes = orderlen(order)
60-
dont_try_forever = 10000 # gives about 2**-60 failures for worst case
61-
while dont_try_forever > 0:
62-
dont_try_forever -= 1
63-
candidate = string_to_number(entropy(bytes)) + 1
64-
if 1 <= candidate < order:
65-
return candidate
66-
continue
67-
raise RuntimeError("randrange() tried hard but gave up, either something"
68-
" is very wrong or you got realllly unlucky. Order was"
69-
" %x" % order)
56+
upper_2 = bit_length(order-2)
57+
upper_256 = upper_2//8 + 1
58+
while True: # I don't think this needs a counter with bit-wise randrange
59+
ent_256 = entropy(upper_256)
60+
ent_2 = entropy_to_bits(ent_256)
61+
rand_num = int(ent_2[:upper_2], base=2) + 1
62+
if 0 < rand_num < order:
63+
return rand_num
7064

7165

7266
class PRNG:

0 commit comments

Comments
 (0)