1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

dsa_nonce_recover.py: feature to talk to an agent.

This commit is contained in:
Simon Tatham 2024-04-07 13:23:37 +01:00
parent a8601a72a9
commit d1a2d215b8

101
test/dsa_nonce_recover.py Normal file → Executable file
View File

@ -1,21 +1,26 @@
#!/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.
'''
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.
'''
import argparse
from base64 import b64decode as b64
from eccref import *
from testcrypt import *
from ssh import *
from agenttest import agent_query
def recover_nonce(order, hashalg, privint, transform_hash, r, s, message):
w = int(mp_invert(s, order))
@ -44,19 +49,23 @@ def ecdsa_decode_sig(signature):
s = int(mp_from_bytes_be(s))
return r, s
def test(privkey, decode_sig, transform_hash, order, hashalg, algid, obits):
class SignerBase:
def test(self, 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)))
self.setup_key(privkey)
for message in (f"msg{i}".encode('ASCII') for i in range(100)):
signature = ssh_key_sign(privkey, message, 0)
signature = self.sign(privkey, message)
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))
self.cleanup_key(privkey)
def test_dsa(pubblob, privblob):
def test_dsa(self, 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)
@ -66,18 +75,68 @@ def test_dsa(pubblob, privblob):
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)
self.test(privkey, dsa_decode_sig, transform_hash, q, 'sha1', 'dsa',
160)
def test_ecdsa(algid, curve, hashalg, pubblob, privblob):
def test_ecdsa(self, 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)
self.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=='))
class TestcryptSigner(SignerBase):
def setup_key(self, key):
pass
def cleanup_key(self, key):
pass
def sign(self, key, message):
return ssh_key_sign(key, message, 0)
class AgentSigner(SignerBase):
def setup_key(self, key):
alg = ssh_decode_string(key.public_blob())
msg = (ssh_byte(SSH2_AGENTC_ADD_IDENTITY) +
ssh_string(alg) +
key.openssh_blob() +
ssh_string(b"dsa_nonce_recover test key"))
result = agent_query(msg)
assert result == ssh_byte(SSH_AGENT_SUCCESS)
def cleanup_key(self, key):
msg = (ssh_byte(SSH2_AGENTC_REMOVE_IDENTITY) +
ssh_string(key.public_blob()))
result = agent_query(msg)
assert result == ssh_byte(SSH_AGENT_SUCCESS)
def sign(self, key, message):
msg = (ssh_byte(SSH2_AGENTC_SIGN_REQUEST) +
ssh_string(key.public_blob()) +
ssh_string(message))
rsp = agent_query(msg)
t, rsp = ssh_decode_byte(rsp, True)
assert t == SSH2_AGENT_SIGN_RESPONSE
sig, rsp = ssh_decode_string(rsp, True)
assert len(rsp) == 0
return sig
def main():
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--agent", action="store_true",
help="Test an SSH agent instead of testcrypt. "
"(Still needs testcrypt.)")
args = parser.parse_args()
signer = AgentSigner() if args.agent else TestcryptSigner()
signer.test_dsa(b64('AAAAB3NzaC1kc3MAAABhAJyWZzjVddGdyc5JPu/WPrC07vKRAmlqO6TUi49ah96iRcM7/D1aRMVAdYBepQ2mf1fsQTmvoC9KgQa79nN3kHhz0voQBKOuKI1ZAodfVOgpP4xmcXgjaA73Vjz22n4newAAABUA6l7/vIveaiA33YYv+SKcKLQaA8cAAABgbErc8QLw/WDz7mhVRZrU+9x3Tfs68j3eW+B/d7Rz1ZCqMYDk7r/F8dlBdQlYhpQvhuSBgzoFa0+qPvSSxPmutgb94wNqhHlVIUb9ZOJNloNr2lXiPP//Wu51TxXAEvAAAAAAYQCcQ9mufXtZa5RyfwT4NuLivdsidP4HRoLXdlnppfFAbNdbhxE0Us8WZt+a/443bwKnYxgif8dgxv5UROnWTngWu0jbJHpaDcTc9lRyTeSUiZZK312s/Sl7qDk3/Du7RUI='), b64('AAAAFGx3ft7G8AQzFsjhle7PWardUXh3'))
signer.test_ecdsa('p256', p256, 'sha256', b64('AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHkYQ0sQoq5LbJI1VMWhw3bV43TSYi3WVpqIgKcBKK91TcFFlAMZgceOHQ0xAFYcSczIttLvFu+xkcLXrRd4N7Q='), b64('AAAAIQCV/1VqiCsHZm/n+bq7lHEHlyy7KFgZBEbzqYaWtbx48Q=='))
signer.test_ecdsa('p384', p384, 'sha384', b64('AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMYK8PUtfAlJwKaBTIGEuCzH0vqOMa4UbcjrBbTbkGVSUnfo+nuC80NCdj9JJMs1jvfF8GzKLc5z8H3nZyM741/BUFjV7rEHsQFDek4KyWvKkEgKiTlZid19VukNo1q2Hg=='), b64('AAAAMGsfTmdB4zHdbiQ2euTSdzM6UKEOnrVjMAWwHEYvmG5qUOcBnn62fJDRJy67L+QGdg=='))
signer.test_ecdsa('p521', p521, 'sha512', b64('AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFrGthlKM152vu2Ghk+R7iO9/M6e+hTehNZ6+FBwof4HPkPB2/HHXj5+w5ynWyUrWiX5TI2riuJEIrJErcRH5LglADnJDX2w4yrKZ+wDHSz9lwh9p2F+B5R952es6gX3RJRkGA+qhKpKup8gKx78RMbleX8wgRtIu+4YMUnKb1edREiRg=='), b64('AAAAQgFh7VNJFUljWhhyAEiL0z+UPs/QggcMTd3Vv2aKDeBdCRl5di8r+BMm39L7bRzxRMEtW5NSKlDtE8MFEGdIE9khsw=='))
if __name__ == '__main__':
main()