def test_count_bits(self): self.assertEquals(0, util.count_bits(0b0)) self.assertEquals(1, util.count_bits(0b1)) self.assertEquals(2, util.count_bits(0b10)) self.assertEquals(2, util.count_bits(0b11)) self.assertEquals(3, util.count_bits(0b100)) self.assertEquals(3, util.count_bits(0b101)) self.assertEquals(3, util.count_bits(0b111)) self.assertEquals(4, util.count_bits(0b1000)) self.assertEquals(599, util.count_bits(2**599 - 1)) self.assertEquals(600, util.count_bits(2**599))
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 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_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)
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)