samplekex.py 5.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/usr/bin/env python3
  2. # Example Python script to synthesise the server end of an SSH key exchange.
  3. # This script expects to be run with its standard input and output
  4. # channels both connected to PuTTY. Run it by means of a command such
  5. # as
  6. #
  7. # rm -f test.log && ./plink -sshrawlog test.log -v -proxycmd './contrib/samplekex.py' dummy
  8. #
  9. # It will conduct the whole of an SSH connection setup, up to the
  10. # point where it ought to present a valid host key signature and
  11. # switch over to the encrypted protocol; but because this is a simple
  12. # script (and also because at that point PuTTY would annoyingly give a
  13. # host key prompt), it doesn't actually bother to do either, and will
  14. # instead present a nonsense signature and terminate. The above sample
  15. # command will log the whole of the exchange from PuTTY's point of
  16. # view in 'test.log'.
  17. #
  18. # The intention is that this forms example code that can be easily
  19. # adapted to demonstrate bugs in our SSH connection setup. With more
  20. # effort it could be expanded into some kind of a regression-testing
  21. # suite, although in order to reliably test particular corner cases
  22. # that would probably also need PuTTY-side modifications to make the
  23. # random numbers deterministic.
  24. import sys, random
  25. from encodelib import *
  26. assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
  27. # A random Diffie-Hellman group, taken from an SSH server I made a
  28. # test connection to.
  29. groupgen = 5
  30. group = 0xf5d3849d2092fd427b4ebd838ea4830397a55f80b644626320dbbe51e8f63ed88148d787c94e7e67e4f393f26c565e1992b0cff8a47a953439462a4d0ffa5763ef60ff908f8ee6c4f6ef9f32b9ba50f01ad56fe7ebe90876a5cf61813a4ad4ba7ec0704303c9bf887d36abbd6c2aa9545fc2263232927e731060f5c701c96dc34016636df438ce30973715f121d767cfb98b5d09ae7b86fa36a051ad3c2941a295a68e2f583a56bc69913ec9d25abef4fdf1e31ede827a02620db058b9f041da051c8c0f13b132c17ceb893fa7c4cd8d8feebd82c5f9120cb221b8e88c5fe4dc17ca020a535484c92c7d4bee69c7703e1fa9a652d444c80065342c6ec0fac23c24de246e3dee72ca8bc8beccdade2b36771efcc350558268f5352ae53f2f71db62249ad9ac4fabdd6dfb099c6cff8c05bdea894390f9860f011cca046dfeb2f6ef81094e7980be526742706d1f3db920db107409291bb4c11f9a7dcbfaf26d808e6f9fe636b26b939de419129e86b1e632c60ec23b65c815723c5d861af068fd0ac8b37f4c06ecbd5cb2ef069ca8daac5cbd67c6182a65fed656d0dfbbb8a430b1dbac7bd6303bec8de078fe69f443a7bc8131a284d25dc2844f096240bfc61b62e91a87802987659b884c094c68741d29aa5ca19b9457e1f9df61c7dbbb13a61a79e4670b086027f20da2af4f5b020725f8828726379f429178926a1f0ea03f
  31. # An RSA key, generated specially for this script.
  32. rsaexp = 0x10001
  33. rsamod = 0xB98FE0C0BEE1E05B35FDDF5517B3E29D8A9A6A7834378B6783A19536968968F755E341B5822CAE15B465DECB80EE4116CF8D22DB5A6C85444A68D0D45D9D42008329619BE3CAC3B192EF83DD2B75C4BB6B567E11B841073BACE92108DA7E97E543ED7F032F454F7AC3C6D3F27DB34BC9974A85C7963C546662AE300A61CBABEE274481FD041C41D0145704F5FA9C77A5A442CD7A64827BB0F21FB56FDE388B596A20D7A7D1C5F22DA96C6C2171D90A673DABC66596CD99499D75AD82FEFDBE04DEC2CC7E1414A95388F668591B3F4D58249F80489646ED2C318E77D4F4E37EE8A588E79F2960620E6D28BF53653F1C974C91845F0BABFE5D167E1CA7044EE20D
  34. # 16 bytes of random data for the start of KEXINIT.
  35. cookie = bytes(random.randint(0,255) for i in range(16))
  36. def expect(var, expr):
  37. expected_val = eval(expr)
  38. if var != expected_val:
  39. sys.stderr.write("Expected %s (%s), got %s\n" % (
  40. expr, repr(expected_val), repr(var)))
  41. sys.exit(1)
  42. sys.stdout.buffer.write(greeting("SSH-2.0-Example KEX synthesis"))
  43. greeting = sys.stdin.buffer.readline()
  44. expect(greeting[:8].decode("ASCII"), '"SSH-2.0-"')
  45. sys.stdout.buffer.write(
  46. clearpkt(SSH2_MSG_KEXINIT,
  47. cookie,
  48. name_list(("diffie-hellman-group-exchange-sha256",)), # kex
  49. name_list(("ssh-rsa",)), # host keys
  50. name_list(("aes128-ctr",)), # client->server ciphers
  51. name_list(("aes128-ctr",)), # server->client ciphers
  52. name_list(("hmac-sha2-256",)), # client->server MACs
  53. name_list(("hmac-sha2-256",)), # server->client MACs
  54. name_list(("none",)), # client->server compression
  55. name_list(("none",)), # server->client compression
  56. name_list(()), # client->server languages
  57. name_list(()), # server->client languages
  58. boolean(False), # first kex packet does not follow
  59. uint32(0)))
  60. sys.stdout.buffer.flush()
  61. intype, inpkt = read_clearpkt(sys.stdin.buffer)
  62. expect(intype, "SSH2_MSG_KEXINIT")
  63. intype, inpkt = read_clearpkt(sys.stdin.buffer)
  64. expect(intype, "SSH2_MSG_KEX_DH_GEX_REQUEST")
  65. expect(inpkt, "uint32(0x400) + uint32(0x400) + uint32(0x2000)")
  66. sys.stdout.buffer.write(
  67. clearpkt(SSH2_MSG_KEX_DH_GEX_GROUP,
  68. mpint(group),
  69. mpint(groupgen)))
  70. sys.stdout.buffer.flush()
  71. intype, inpkt = read_clearpkt(sys.stdin.buffer)
  72. expect(intype, "SSH2_MSG_KEX_DH_GEX_INIT")
  73. sys.stdout.buffer.write(
  74. clearpkt(SSH2_MSG_KEX_DH_GEX_REPLY,
  75. ssh_rsa_key_blob(rsaexp, rsamod),
  76. mpint(random.randint(2, group-2)),
  77. ssh_rsa_signature_blob(random.randint(2, rsamod-2))))
  78. sys.stdout.buffer.flush()