mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 18:07:59 +00:00
12d5b00d62
encodelib.py is a Python library which implements some handy SSH-2 encoding primitives; samplekex.py uses that to fabricate the start of an SSH connection, up to the point where key exchange totally fails its crypto. The idea is that you adapt samplekex.py to construct initial-kex sequences with particular properties, in order to test robustness and security fixes that affect the initial-kex sequence. For example, I used an adaptation of this to test the Diffie-Hellman range check that's just gone into 0.64.
116 lines
2.9 KiB
Python
116 lines
2.9 KiB
Python
# Python module to make it easy to manually encode SSH packets, by
|
|
# supporting the various uint32, string, mpint primitives.
|
|
#
|
|
# The idea of this is that you can use it to manually construct key
|
|
# exchange sequences of interesting kinds, for testing purposes.
|
|
|
|
import struct, random
|
|
|
|
def boolean(b):
|
|
return "\1" if b else "\0"
|
|
|
|
def byte(b):
|
|
assert 0 <= b < 0x100
|
|
return chr(b)
|
|
|
|
def uint32(u):
|
|
assert 0 <= u < 0x100000000
|
|
return struct.pack(">I", u)
|
|
|
|
def uint64(u):
|
|
assert 0 <= u < 0x10000000000000000
|
|
return struct.pack(">L", u)
|
|
|
|
def string(s):
|
|
return uint32(len(s)) + s
|
|
|
|
def mpint(m):
|
|
s = ""
|
|
lastbyte = 0
|
|
while m > 0:
|
|
lastbyte = m & 0xFF
|
|
s = chr(lastbyte) + s
|
|
m >>= 8
|
|
if lastbyte & 0x80:
|
|
s = "\0" + s
|
|
return string(s)
|
|
|
|
def name_list(ns):
|
|
s = ""
|
|
for n in ns:
|
|
assert "," not in n
|
|
if s != "":
|
|
s += ","
|
|
s += n
|
|
return string(s)
|
|
|
|
def ssh_rsa_key_blob(modulus, exponent):
|
|
return string(string("ssh-rsa") + mpint(modulus) + mpint(exponent))
|
|
|
|
def ssh_rsa_signature_blob(signature):
|
|
return string(string("ssh-rsa") + mpint(signature))
|
|
|
|
def greeting(string):
|
|
# Greeting at the start of an SSH connection.
|
|
return string + "\r\n"
|
|
|
|
# Packet types.
|
|
SSH2_MSG_DISCONNECT = 1
|
|
SSH2_MSG_IGNORE = 2
|
|
SSH2_MSG_UNIMPLEMENTED = 3
|
|
SSH2_MSG_DEBUG = 4
|
|
SSH2_MSG_SERVICE_REQUEST = 5
|
|
SSH2_MSG_SERVICE_ACCEPT = 6
|
|
SSH2_MSG_KEXINIT = 20
|
|
SSH2_MSG_NEWKEYS = 21
|
|
SSH2_MSG_KEXDH_INIT = 30
|
|
SSH2_MSG_KEXDH_REPLY = 31
|
|
SSH2_MSG_KEX_DH_GEX_REQUEST = 30
|
|
SSH2_MSG_KEX_DH_GEX_GROUP = 31
|
|
SSH2_MSG_KEX_DH_GEX_INIT = 32
|
|
SSH2_MSG_KEX_DH_GEX_REPLY = 33
|
|
SSH2_MSG_KEXRSA_PUBKEY = 30
|
|
SSH2_MSG_KEXRSA_SECRET = 31
|
|
SSH2_MSG_KEXRSA_DONE = 32
|
|
SSH2_MSG_USERAUTH_REQUEST = 50
|
|
SSH2_MSG_USERAUTH_FAILURE = 51
|
|
SSH2_MSG_USERAUTH_SUCCESS = 52
|
|
SSH2_MSG_USERAUTH_BANNER = 53
|
|
SSH2_MSG_USERAUTH_PK_OK = 60
|
|
SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ = 60
|
|
SSH2_MSG_USERAUTH_INFO_REQUEST = 60
|
|
SSH2_MSG_USERAUTH_INFO_RESPONSE = 61
|
|
SSH2_MSG_GLOBAL_REQUEST = 80
|
|
SSH2_MSG_REQUEST_SUCCESS = 81
|
|
SSH2_MSG_REQUEST_FAILURE = 82
|
|
SSH2_MSG_CHANNEL_OPEN = 90
|
|
SSH2_MSG_CHANNEL_OPEN_CONFIRMATION = 91
|
|
SSH2_MSG_CHANNEL_OPEN_FAILURE = 92
|
|
SSH2_MSG_CHANNEL_WINDOW_ADJUST = 93
|
|
SSH2_MSG_CHANNEL_DATA = 94
|
|
SSH2_MSG_CHANNEL_EXTENDED_DATA = 95
|
|
SSH2_MSG_CHANNEL_EOF = 96
|
|
SSH2_MSG_CHANNEL_CLOSE = 97
|
|
SSH2_MSG_CHANNEL_REQUEST = 98
|
|
SSH2_MSG_CHANNEL_SUCCESS = 99
|
|
SSH2_MSG_CHANNEL_FAILURE = 100
|
|
SSH2_MSG_USERAUTH_GSSAPI_RESPONSE = 60
|
|
SSH2_MSG_USERAUTH_GSSAPI_TOKEN = 61
|
|
SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63
|
|
SSH2_MSG_USERAUTH_GSSAPI_ERROR = 64
|
|
SSH2_MSG_USERAUTH_GSSAPI_ERRTOK = 65
|
|
SSH2_MSG_USERAUTH_GSSAPI_MIC = 66
|
|
|
|
def clearpkt(msgtype, *stuff):
|
|
# SSH-2 binary packet, in the cleartext format used for initial
|
|
# setup and kex.
|
|
s = byte(msgtype)
|
|
for thing in stuff:
|
|
s += thing
|
|
padlen = 0
|
|
while padlen < 4 or len(s) % 8 != 3:
|
|
padlen += 1
|
|
s += byte(random.randint(0,255))
|
|
s = byte(padlen) + s
|
|
return string(s)
|