mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Python script that recovers DSA nonces.
I used this to confirm that the previous nonces generated by dsa_gen_k() were indeed biased, and to check that the new RFC6979 ones don't have the same problem. Recovering the DSA nonce value is equivalent to recovering the private key. One way round, this is well known: if you leak or reuse a nonce, your private key is compromised. But the other direction of the equivalence is also true - if you know the private key and have a signed message, you can retrieve the input nonce. This is much less obviously useful (certainly not to an attacker), but I found it convenient for this particular test purpose, because it can operate on the standard SSH data formats, without needing special access into the signing algorithm to retrieve its internal variables. So I was able to run this script unchanged against the 'before' and 'after' versions of testcrypt, and observe the difference.
This commit is contained in:
parent
4aa5d88fdb
commit
93c412b1a7
83
test/dsa_nonce_recover.py
Normal file
83
test/dsa_nonce_recover.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Recover the nonce value k used in integer DSA or NIST-style ECDSA,
|
||||||
|
# starting from the private key and the signature.
|
||||||
|
#
|
||||||
|
# _Without_ the private key, recovering the nonce is equivalent to
|
||||||
|
# recovering the private key itself. But with it, it's a trivial piece
|
||||||
|
# of modular arithmetic.
|
||||||
|
#
|
||||||
|
# This script generates a load of test signatures from various keys,
|
||||||
|
# recovers the nonces used, and prints them. This allows an eyeball
|
||||||
|
# check of whether they're evenly distributed.
|
||||||
|
|
||||||
|
from base64 import b64decode as b64
|
||||||
|
|
||||||
|
from eccref import *
|
||||||
|
from testcrypt import *
|
||||||
|
from ssh import *
|
||||||
|
|
||||||
|
def recover_nonce(order, hashalg, privint, transform_hash, r, s, message):
|
||||||
|
w = int(mp_invert(s, order))
|
||||||
|
|
||||||
|
h = ssh_hash_new(hashalg)
|
||||||
|
ssh_hash_update(h, message)
|
||||||
|
z = int(mp_from_bytes_be(ssh_hash_final(h)))
|
||||||
|
z = int(transform_hash(z))
|
||||||
|
|
||||||
|
return w * (z + r * privint) % order
|
||||||
|
|
||||||
|
def dsa_decode_sig(signature):
|
||||||
|
_, signature = ssh_decode_string(signature, return_rest=True)
|
||||||
|
signature = ssh_decode_string(signature)
|
||||||
|
assert len(signature) == 40
|
||||||
|
r = int(mp_from_bytes_be(signature[:20]))
|
||||||
|
s = int(mp_from_bytes_be(signature[20:]))
|
||||||
|
return r, s
|
||||||
|
|
||||||
|
def ecdsa_decode_sig(signature):
|
||||||
|
_, signature = ssh_decode_string(signature, return_rest=True)
|
||||||
|
signature = ssh_decode_string(signature)
|
||||||
|
r, signature = ssh_decode_string(signature, return_rest=True)
|
||||||
|
s, signature = ssh_decode_string(signature, return_rest=True)
|
||||||
|
r = int(mp_from_bytes_be(r))
|
||||||
|
s = int(mp_from_bytes_be(s))
|
||||||
|
return r, s
|
||||||
|
|
||||||
|
def test(privkey, decode_sig, transform_hash, order, hashalg, algid, obits):
|
||||||
|
print("----", algid)
|
||||||
|
print("k=0x{{:0{}b}}".format(obits).format(order))
|
||||||
|
privblob = ssh_key_private_blob(privkey)
|
||||||
|
privint = int(mp_from_bytes_be(ssh_decode_string(privblob)))
|
||||||
|
for message in (f"msg{i}".encode('ASCII') for i in range(100)):
|
||||||
|
signature = ssh_key_sign(privkey, message, 0)
|
||||||
|
r, s = decode_sig(signature)
|
||||||
|
nonce = recover_nonce(order, hashalg, privint, transform_hash,
|
||||||
|
r, s, message)
|
||||||
|
print("k=0x{{:0{}b}}".format(obits).format(nonce))
|
||||||
|
|
||||||
|
def test_dsa(pubblob, privblob):
|
||||||
|
privkey = ssh_key_new_priv('dsa', pubblob, privblob)
|
||||||
|
_, buf = ssh_decode_string(pubblob, return_rest=True)
|
||||||
|
p, buf = ssh_decode_string(buf, return_rest=True)
|
||||||
|
q, buf = ssh_decode_string(buf, return_rest=True)
|
||||||
|
g, buf = ssh_decode_string(buf, return_rest=True)
|
||||||
|
p = int(mp_from_bytes_be(p))
|
||||||
|
q = int(mp_from_bytes_be(q))
|
||||||
|
g = int(mp_from_bytes_be(g))
|
||||||
|
transform_hash = lambda h: h
|
||||||
|
test(privkey, dsa_decode_sig, transform_hash, q, 'sha1', 'dsa', 160)
|
||||||
|
|
||||||
|
def test_ecdsa(algid, curve, hashalg, pubblob, privblob):
|
||||||
|
privkey = ssh_key_new_priv(algid, pubblob, privblob)
|
||||||
|
obits = int(mp_get_nbits(curve.G_order))
|
||||||
|
def transform_hash(z):
|
||||||
|
shift = max(0, mp_get_nbits(z) - obits)
|
||||||
|
return mp_rshift_safe(z, shift)
|
||||||
|
test(privkey, ecdsa_decode_sig, transform_hash, curve.G_order, hashalg,
|
||||||
|
algid, obits)
|
||||||
|
|
||||||
|
test_dsa(b64('AAAAB3NzaC1kc3MAAABhAJyWZzjVddGdyc5JPu/WPrC07vKRAmlqO6TUi49ah96iRcM7/D1aRMVAdYBepQ2mf1fsQTmvoC9KgQa79nN3kHhz0voQBKOuKI1ZAodfVOgpP4xmcXgjaA73Vjz22n4newAAABUA6l7/vIveaiA33YYv+SKcKLQaA8cAAABgbErc8QLw/WDz7mhVRZrU+9x3Tfs68j3eW+B/d7Rz1ZCqMYDk7r/F8dlBdQlYhpQvhuSBgzoFa0+qPvSSxPmutgb94wNqhHlVIUb9ZOJNloNr2lXiPP//Wu51TxXAEvAAAAAAYQCcQ9mufXtZa5RyfwT4NuLivdsidP4HRoLXdlnppfFAbNdbhxE0Us8WZt+a/443bwKnYxgif8dgxv5UROnWTngWu0jbJHpaDcTc9lRyTeSUiZZK312s/Sl7qDk3/Du7RUI='), b64('AAAAFGx3ft7G8AQzFsjhle7PWardUXh3'))
|
||||||
|
test_ecdsa('p256', p256, 'sha256', b64('AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHkYQ0sQoq5LbJI1VMWhw3bV43TSYi3WVpqIgKcBKK91TcFFlAMZgceOHQ0xAFYcSczIttLvFu+xkcLXrRd4N7Q='), b64('AAAAIQCV/1VqiCsHZm/n+bq7lHEHlyy7KFgZBEbzqYaWtbx48Q=='))
|
||||||
|
test_ecdsa('p384', p384, 'sha384', b64('AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMYK8PUtfAlJwKaBTIGEuCzH0vqOMa4UbcjrBbTbkGVSUnfo+nuC80NCdj9JJMs1jvfF8GzKLc5z8H3nZyM741/BUFjV7rEHsQFDek4KyWvKkEgKiTlZid19VukNo1q2Hg=='), b64('AAAAMGsfTmdB4zHdbiQ2euTSdzM6UKEOnrVjMAWwHEYvmG5qUOcBnn62fJDRJy67L+QGdg=='))
|
||||||
|
test_ecdsa('p521', p521, 'sha512', b64('AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFrGthlKM152vu2Ghk+R7iO9/M6e+hTehNZ6+FBwof4HPkPB2/HHXj5+w5ynWyUrWiX5TI2riuJEIrJErcRH5LglADnJDX2w4yrKZ+wDHSz9lwh9p2F+B5R952es6gX3RJRkGA+qhKpKup8gKx78RMbleX8wgRtIu+4YMUnKb1edREiRg=='), b64('AAAAQgFh7VNJFUljWhhyAEiL0z+UPs/QggcMTd3Vv2aKDeBdCRl5di8r+BMm39L7bRzxRMEtW5NSKlDtE8MFEGdIE9khsw=='))
|
Loading…
Reference in New Issue
Block a user