from __future__ import division import os import math import binascii import sys from hashlib import sha256 from six import PY3, int2byte, b, next from . import der from ._compat import normalise_bytes # 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) if sys.version > '3': def entropy_to_bits(ent_256): """Convert a bytestring to string of 0's and 1's""" return bin(int.from_bytes(ent_256, 'big'))[2:].zfill(len(ent_256)*8) else: def entropy_to_bits(ent_256): """Convert a bytestring to string of 0's and 1's""" return ''.join(bin(ord(x))[2:].zfill(8) for x in ent_256) if sys.version < '2.7': # Can't add a method to a built-in type so we are stuck with this def bit_length(x): return len(bin(x)) - 2 else: def bit_length(x): return x.bit_length() or 1 def orderlen(order): return (1+len("%x" % order))//2 # bytes def randrange(order, entropy=None): """Return a random integer k such that 1 <= k < order, uniformly distributed across that range. Worst case should be a mean of 2 loops at (2**k)+2. 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. """ assert order > 1 if entropy is None: entropy = os.urandom upper_2 = bit_length(order-2) upper_256 = upper_2//8 + 1 while True: # I don't think this needs a counter with bit-wise randrange ent_256 = entropy(upper_256) ent_2 = entropy_to_bits(ent_256) rand_num = int(ent_2[:upper_2], base=2) + 1 if 0 < rand_num < order: return rand_num 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): """ Encode the signature to raw format (:term:`raw encoding`) It's expected that this function will be used as a `sigencode=` parameter in :func:`ecdsa.keys.SigningKey.sign` method. :param int r: first parameter of the signature :param int s: second parameter of the signature :param int order: the order of the curve over which the signature was computed :return: raw encoding of ECDSA signature :rtype: bytes """ # 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): """ Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`. Encodes the signature to the following :term:`ASN.1` structure:: Ecdsa-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER } It's expected that this function will be used as a `sigencode=` parameter in :func:`ecdsa.keys.SigningKey.sign` method. :param int r: first parameter of the signature :param int s: second parameter of the signature :param int order: the order of the curve over which the signature was computed :return: DER encoding of ECDSA signature :rtype: bytes """ 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) class MalformedSignature(Exception): """ Raised by decoding functions when the signature is malformed. Malformed in this context means that the relevant strings or integers do not match what a signature over provided curve would create. Either because the byte strings have incorrect lengths or because the encoded values are too large. """ pass def sigdecode_string(signature, order): """ Decoder for :term:`raw encoding` of ECDSA signatures. raw encoding is a simple concatenation of the two integers that comprise the signature, with each encoded using the same amount of bytes depending on curve size/order. It's expected that this function will be used as the `sigdecode=` parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. :param signature: encoded signature :type signature: bytes like object :param order: order of the curve over which the signature was computed :type order: int :raises MalformedSignature: when the encoding of the signature is invalid :return: tuple with decoded 'r' and 's' values of signature :rtype: tuple of ints """ signature = normalise_bytes(signature) l = orderlen(order) if not len(signature) == 2 * l: raise MalformedSignature( "Invalid length of signature, expected {0} bytes long, " "provided string is {1} bytes long" .format(2 * l, len(signature))) 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): """ Decode the signature from two strings. First string needs to be a big endian encoding of 'r', second needs to be a big endian encoding of the 's' parameter of an ECDSA signature. It's expected that this function will be used as the `sigdecode=` parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. :param list rs_strings: list of two bytes-like objects, each encoding one parameter of signature :param int order: order of the curve over which the signature was computed :raises MalformedSignature: when the encoding of the signature is invalid :return: tuple with decoded 'r' and 's' values of signature :rtype: tuple of ints """ if not len(rs_strings) == 2: raise MalformedSignature( "Invalid number of strings provided: {0}, expected 2" .format(len(rs_strings))) (r_str, s_str) = rs_strings r_str = normalise_bytes(r_str) s_str = normalise_bytes(s_str) l = orderlen(order) if not len(r_str) == l: raise MalformedSignature( "Invalid length of first string ('r' parameter), " "expected {0} bytes long, provided string is {1} bytes long" .format(l, len(r_str))) if not len(s_str) == l: raise MalformedSignature( "Invalid length of second string ('s' parameter), " "expected {0} bytes long, provided string is {1} bytes long" .format(l, len(s_str))) 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): """ Decoder for DER format of ECDSA signatures. DER format of signature is one that uses the :term:`ASN.1` :term:`DER` rules to encode it as a sequence of two integers:: Ecdsa-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER } It's expected that this function will be used as as the `sigdecode=` parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. :param sig_der: encoded signature :type sig_der: bytes like object :param order: order of the curve over which the signature was computed :type order: int :raises UnexpectedDER: when the encoding of signature is invalid :return: tuple with decoded 'r' and 's' values of signature :rtype: tuple of ints """ sig_der = normalise_bytes(sig_der) # 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