|
|
- from __future__ import division
-
- import os
- import math
- import binascii
- from hashlib import sha256
- from . import der
- from .curves import orderlen
- from .six import PY3, int2byte, b, next
-
- # RFC5480:
- # The "unrestricted" algorithm identifier is:
- # id-ecPublicKey OBJECT IDENTIFIER ::= {
- # iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
-
- oid_ecPublicKey = (1, 2, 840, 10045, 2, 1)
- encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey)
-
- def randrange(order, entropy=None):
- """Return a random integer k such that 1 <= k < order, uniformly
- distributed across that range. For simplicity, this only behaves well if
- 'order' is fairly close (but below) a power of 256. The try-try-again
- algorithm we use takes longer and longer time (on average) to complete as
- 'order' falls, rising to a maximum of avg=512 loops for the worst-case
- (256**k)+1 . All of the standard curves behave well. There is a cutoff at
- 10k loops (which raises RuntimeError) to prevent an infinite loop when
- something is really broken like the entropy function not working.
-
- Note that this function is not declared to be forwards-compatible: we may
- change the behavior in future releases. The entropy= argument (which
- should get a callable that behaves like os.urandom) can be used to
- achieve stability within a given release (for repeatable unit tests), but
- should not be used as a long-term-compatible key generation algorithm.
- """
- # we could handle arbitrary orders (even 256**k+1) better if we created
- # candidates bit-wise instead of byte-wise, which would reduce the
- # worst-case behavior to avg=2 loops, but that would be more complex. The
- # change would be to round the order up to a power of 256, subtract one
- # (to get 0xffff..), use that to get a byte-long mask for the top byte,
- # generate the len-1 entropy bytes, generate one extra byte and mask off
- # the top bits, then combine it with the rest. Requires jumping back and
- # forth between strings and integers a lot.
-
- if entropy is None:
- entropy = os.urandom
- assert order > 1
- bytes = orderlen(order)
- dont_try_forever = 10000 # gives about 2**-60 failures for worst case
- while dont_try_forever > 0:
- dont_try_forever -= 1
- candidate = string_to_number(entropy(bytes)) + 1
- if 1 <= candidate < order:
- return candidate
- continue
- raise RuntimeError("randrange() tried hard but gave up, either something"
- " is very wrong or you got realllly unlucky. Order was"
- " %x" % order)
-
- class PRNG:
- # this returns a callable which, when invoked with an integer N, will
- # return N pseudorandom bytes. Note: this is a short-term PRNG, meant
- # primarily for the needs of randrange_from_seed__trytryagain(), which
- # only needs to run it a few times per seed. It does not provide
- # protection against state compromise (forward security).
- def __init__(self, seed):
- self.generator = self.block_generator(seed)
-
- def __call__(self, numbytes):
- a = [next(self.generator) for i in range(numbytes)]
-
- if PY3:
- return bytes(a)
- else:
- return "".join(a)
-
-
- def block_generator(self, seed):
- counter = 0
- while True:
- for byte in sha256(("prng-%d-%s" % (counter, seed)).encode()).digest():
- yield byte
- counter += 1
-
- def randrange_from_seed__overshoot_modulo(seed, order):
- # hash the data, then turn the digest into a number in [1,order).
- #
- # We use David-Sarah Hopwood's suggestion: turn it into a number that's
- # sufficiently larger than the group order, then modulo it down to fit.
- # This should give adequate (but not perfect) uniformity, and simple
- # code. There are other choices: try-try-again is the main one.
- base = PRNG(seed)(2*orderlen(order))
- number = (int(binascii.hexlify(base), 16) % (order-1)) + 1
- assert 1 <= number < order, (1, number, order)
- return number
-
- def lsb_of_ones(numbits):
- return (1 << numbits) - 1
- def bits_and_bytes(order):
- bits = int(math.log(order-1, 2)+1)
- bytes = bits // 8
- extrabits = bits % 8
- return bits, bytes, extrabits
-
- # the following randrange_from_seed__METHOD() functions take an
- # arbitrarily-sized secret seed and turn it into a number that obeys the same
- # range limits as randrange() above. They are meant for deriving consistent
- # signing keys from a secret rather than generating them randomly, for
- # example a protocol in which three signing keys are derived from a master
- # secret. You should use a uniformly-distributed unguessable seed with about
- # curve.baselen bytes of entropy. To use one, do this:
- # seed = os.urandom(curve.baselen) # or other starting point
- # secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order)
- # sk = SigningKey.from_secret_exponent(secexp, curve)
-
- def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256):
- # hash the seed, then turn the digest into a number in [1,order), but
- # don't worry about trying to uniformly fill the range. This will lose,
- # on average, four bits of entropy.
- bits, bytes, extrabits = bits_and_bytes(order)
- if extrabits:
- bytes += 1
- base = hashmod(seed).digest()[:bytes]
- base = "\x00"*(bytes-len(base)) + base
- number = 1+int(binascii.hexlify(base), 16)
- assert 1 <= number < order
- return number
-
- def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256):
- # like string_to_randrange_truncate_bytes, but only lose an average of
- # half a bit
- bits = int(math.log(order-1, 2)+1)
- maxbytes = (bits+7) // 8
- base = hashmod(seed).digest()[:maxbytes]
- base = "\x00"*(maxbytes-len(base)) + base
- topbits = 8*maxbytes - bits
- if topbits:
- base = int2byte(ord(base[0]) & lsb_of_ones(topbits)) + base[1:]
- number = 1+int(binascii.hexlify(base), 16)
- assert 1 <= number < order
- return number
-
- def randrange_from_seed__trytryagain(seed, order):
- # figure out exactly how many bits we need (rounded up to the nearest
- # bit), so we can reduce the chance of looping to less than 0.5 . This is
- # specified to feed from a byte-oriented PRNG, and discards the
- # high-order bits of the first byte as necessary to get the right number
- # of bits. The average number of loops will range from 1.0 (when
- # order=2**k-1) to 2.0 (when order=2**k+1).
- assert order > 1
- bits, bytes, extrabits = bits_and_bytes(order)
- generate = PRNG(seed)
- while True:
- extrabyte = b("")
- if extrabits:
- extrabyte = int2byte(ord(generate(1)) & lsb_of_ones(extrabits))
- guess = string_to_number(extrabyte + generate(bytes)) + 1
- if 1 <= guess < order:
- return guess
-
-
- def number_to_string(num, order):
- l = orderlen(order)
- fmt_str = "%0" + str(2*l) + "x"
- string = binascii.unhexlify((fmt_str % num).encode())
- assert len(string) == l, (len(string), l)
- return string
-
- def number_to_string_crop(num, order):
- l = orderlen(order)
- fmt_str = "%0" + str(2*l) + "x"
- string = binascii.unhexlify((fmt_str % num).encode())
- return string[:l]
-
- def string_to_number(string):
- return int(binascii.hexlify(string), 16)
-
- def string_to_number_fixedlen(string, order):
- l = orderlen(order)
- assert len(string) == l, (len(string), l)
- return int(binascii.hexlify(string), 16)
-
- # these methods are useful for the sigencode= argument to SK.sign() and the
- # sigdecode= argument to VK.verify(), and control how the signature is packed
- # or unpacked.
-
- def sigencode_strings(r, s, order):
- r_str = number_to_string(r, order)
- s_str = number_to_string(s, order)
- return (r_str, s_str)
-
- def sigencode_string(r, s, order):
- # for any given curve, the size of the signature numbers is
- # fixed, so just use simple concatenation
- r_str, s_str = sigencode_strings(r, s, order)
- return r_str + s_str
-
- def sigencode_der(r, s, order):
- return der.encode_sequence(der.encode_integer(r), der.encode_integer(s))
-
- # canonical versions of sigencode methods
- # these enforce low S values, by negating the value (modulo the order) if above order/2
- # see CECKey::Sign() https://github.com/bitcoin/bitcoin/blob/master/src/key.cpp#L214
- def sigencode_strings_canonize(r, s, order):
- if s > order / 2:
- s = order - s
- return sigencode_strings(r, s, order)
-
- def sigencode_string_canonize(r, s, order):
- if s > order / 2:
- s = order - s
- return sigencode_string(r, s, order)
-
- def sigencode_der_canonize(r, s, order):
- if s > order / 2:
- s = order - s
- return sigencode_der(r, s, order)
-
-
- def sigdecode_string(signature, order):
- l = orderlen(order)
- assert len(signature) == 2*l, (len(signature), 2*l)
- r = string_to_number_fixedlen(signature[:l], order)
- s = string_to_number_fixedlen(signature[l:], order)
- return r, s
-
- def sigdecode_strings(rs_strings, order):
- (r_str, s_str) = rs_strings
- l = orderlen(order)
- assert len(r_str) == l, (len(r_str), l)
- assert len(s_str) == l, (len(s_str), l)
- r = string_to_number_fixedlen(r_str, order)
- s = string_to_number_fixedlen(s_str, order)
- return r, s
-
- def sigdecode_der(sig_der, order):
- #return der.encode_sequence(der.encode_integer(r), der.encode_integer(s))
- rs_strings, empty = der.remove_sequence(sig_der)
- if empty != b(""):
- raise der.UnexpectedDER("trailing junk after DER sig: %s" %
- binascii.hexlify(empty))
- r, rest = der.remove_integer(rs_strings)
- s, empty = der.remove_integer(rest)
- if empty != b(""):
- raise der.UnexpectedDER("trailing junk after DER numbers: %s" %
- binascii.hexlify(empty))
- return r, s
-
|