1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-08 08:58:00 +00:00

Migrate all Python scripts to Python 3.

Most of them are now _mandatory_ P3 scripts, because I'm tired of
maintaining everything to be compatible with both versions.

The current exceptions are gdb.py (which has to live with whatever gdb
gives it), and kh2reg.py (which is actually designed for other people
to use, and some of them might still be stuck on P2 for the moment).
This commit is contained in:
Simon Tatham 2020-03-04 21:23:49 +00:00
parent cdffb995df
commit 2ec2b796ed
15 changed files with 116 additions and 93 deletions

View File

@ -4,14 +4,21 @@
# 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
import sys
import struct
import random
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
def tobytes(s):
return s if isinstance(s, bytes) else s.encode('ASCII')
def boolean(b):
return "\1" if b else "\0"
return b"\1" if b else b"\0"
def byte(b):
assert 0 <= b < 0x100
return chr(b)
return bytes([b])
def uint32(u):
assert 0 <= u < 0x100000000
@ -22,25 +29,24 @@ def uint64(u):
return struct.pack(">L", u)
def string(s):
return uint32(len(s)) + s
return uint32(len(s)) + tobytes(s)
def mpint(m):
s = ""
lastbyte = 0
s = []
while m > 0:
lastbyte = m & 0xFF
s = chr(lastbyte) + s
s.append(m & 0xFF)
m >>= 8
if lastbyte & 0x80:
s = "\0" + s
return string(s)
if len(s) > 0 and (s[-1] & 0x80):
s.append(0)
s.reverse()
return string(bytes(s))
def name_list(ns):
s = ""
for n in ns:
assert "," not in n
if s != "":
s += ","
s = b""
for n in map(tobytes, ns):
assert b"," not in n
if s != b"":
s += b","
s += n
return string(s)
@ -52,7 +58,7 @@ def ssh_rsa_signature_blob(signature):
def greeting(string):
# Greeting at the start of an SSH connection.
return string + "\r\n"
return tobytes(string) + b"\r\n"
# Packet types.
SSH2_MSG_DISCONNECT = 1
@ -122,8 +128,5 @@ def decode_uint32(s):
def read_clearpkt(fh):
length_field = fh.read(4)
s = fh.read(decode_uint32(length_field))
import sys
padlen = ord(s[0])
s = s[1:-padlen]
msgtype = ord(s[0])
return msgtype, s[1:]
padlen, msgtype = s[:2]
return msgtype, s[2:-padlen]

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python
#!/usr/bin/env python3
# Convert OpenSSH known_hosts and known_hosts2 files to "new format" PuTTY
# host keys.

View File

@ -1,9 +1,11 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import sys
import string
from collections import namedtuple
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
class Multiprecision(object):
def __init__(self, target, minval, maxval, words):
self.target = target
@ -89,21 +91,21 @@ class Multiprecision(object):
for i in range(nwords):
srcpos = i * self.target.bits + start
maxbits = min(self.target.bits, start + bits - srcpos)
wordindex = srcpos / self.target.bits
wordindex = srcpos // self.target.bits
if srcpos % self.target.bits == 0:
word = self.getword(srcpos / self.target.bits)
word = self.getword(srcpos // self.target.bits)
elif (wordindex+1 >= len(self.words) or
srcpos % self.target.bits + maxbits < self.target.bits):
word = self.target.new_value(
"(%%s) >> %d" % (srcpos % self.target.bits),
self.getword(srcpos / self.target.bits))
self.getword(srcpos // self.target.bits))
else:
word = self.target.new_value(
"((%%s) >> %d) | ((%%s) << %d)" % (
srcpos % self.target.bits,
self.target.bits - (srcpos % self.target.bits)),
self.getword(srcpos / self.target.bits),
self.getword(srcpos / self.target.bits + 1))
self.getword(srcpos // self.target.bits),
self.getword(srcpos // self.target.bits + 1))
if maxbits < self.target.bits and maxbits < bits:
word = self.target.new_value(
"(%%s) & ((((BignumInt)1) << %d)-1)" % maxbits,
@ -127,11 +129,11 @@ class CodegenTarget(object):
self.valindex = 0
self.stmts = []
self.generators = {}
self.bv_words = (130 + self.bits - 1) / self.bits
self.bv_words = (130 + self.bits - 1) // self.bits
self.carry_index = 0
def nwords(self, maxval):
return (maxval.bit_length() + self.bits - 1) / self.bits
return (maxval.bit_length() + self.bits - 1) // self.bits
def stmt(self, stmt, needed=False):
index = len(self.stmts)
@ -149,7 +151,7 @@ class CodegenTarget(object):
return name
def bigval_input(self, name, bits):
words = (bits + self.bits - 1) / self.bits
words = (bits + self.bits - 1) // self.bits
# Expect not to require an entire extra word
assert words == self.bv_words

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Example Python script to synthesise the server end of an SSH key exchange.
@ -27,6 +27,8 @@
import sys, random
from encodelib import *
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
# A random Diffie-Hellman group, taken from an SSH server I made a
# test connection to.
groupgen = 5
@ -37,7 +39,7 @@ rsaexp = 0x10001
rsamod = 0xB98FE0C0BEE1E05B35FDDF5517B3E29D8A9A6A7834378B6783A19536968968F755E341B5822CAE15B465DECB80EE4116CF8D22DB5A6C85444A68D0D45D9D42008329619BE3CAC3B192EF83DD2B75C4BB6B567E11B841073BACE92108DA7E97E543ED7F032F454F7AC3C6D3F27DB34BC9974A85C7963C546662AE300A61CBABEE274481FD041C41D0145704F5FA9C77A5A442CD7A64827BB0F21FB56FDE388B596A20D7A7D1C5F22DA96C6C2171D90A673DABC66596CD99499D75AD82FEFDBE04DEC2CC7E1414A95388F668591B3F4D58249F80489646ED2C318E77D4F4E37EE8A588E79F2960620E6D28BF53653F1C974C91845F0BABFE5D167E1CA7044EE20D
# 16 bytes of random data for the start of KEXINIT.
cookie = "".join([chr(random.randint(0,255)) for i in range(16)])
cookie = bytes(random.randint(0,255) for i in range(16))
def expect(var, expr):
expected_val = eval(expr)
@ -46,12 +48,12 @@ def expect(var, expr):
expr, repr(expected_val), repr(var)))
sys.exit(1)
sys.stdout.write(greeting("SSH-2.0-Example KEX synthesis"))
sys.stdout.buffer.write(greeting("SSH-2.0-Example KEX synthesis"))
greeting = sys.stdin.readline()
expect(greeting[:8], '"SSH-2.0-"')
greeting = sys.stdin.buffer.readline()
expect(greeting[:8].decode("ASCII"), '"SSH-2.0-"')
sys.stdout.write(
sys.stdout.buffer.write(
clearpkt(SSH2_MSG_KEXINIT,
cookie,
name_list(("diffie-hellman-group-exchange-sha256",)), # kex
@ -66,27 +68,27 @@ sys.stdout.write(
name_list(()), # server->client languages
boolean(False), # first kex packet does not follow
uint32(0)))
sys.stdout.flush()
sys.stdout.buffer.flush()
intype, inpkt = read_clearpkt(sys.stdin)
intype, inpkt = read_clearpkt(sys.stdin.buffer)
expect(intype, "SSH2_MSG_KEXINIT")
intype, inpkt = read_clearpkt(sys.stdin)
intype, inpkt = read_clearpkt(sys.stdin.buffer)
expect(intype, "SSH2_MSG_KEX_DH_GEX_REQUEST")
expect(inpkt, "uint32(0x400) + uint32(0x400) + uint32(0x2000)")
sys.stdout.write(
sys.stdout.buffer.write(
clearpkt(SSH2_MSG_KEX_DH_GEX_GROUP,
mpint(group),
mpint(groupgen)))
sys.stdout.flush()
sys.stdout.buffer.flush()
intype, inpkt = read_clearpkt(sys.stdin)
intype, inpkt = read_clearpkt(sys.stdin.buffer)
expect(intype, "SSH2_MSG_KEX_DH_GEX_INIT")
sys.stdout.write(
sys.stdout.buffer.write(
clearpkt(SSH2_MSG_KEX_DH_GEX_REPLY,
ssh_rsa_key_blob(rsaexp, rsamod),
mpint(random.randint(2, group-2)),
ssh_rsa_signature_blob(random.randint(2, rsamod-2))))
sys.stdout.flush()
sys.stdout.buffer.flush()

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Generate Mac OS X .icns files, or at least the simple subformats
# that don't involve JPEG encoding and the like.
@ -10,6 +10,8 @@ import sys
import struct
import subprocess
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
# The file format has a typical IFF-style (type, length, data) chunk
# structure, with one outer chunk containing subchunks for various
# different icon sizes and formats.
@ -33,18 +35,18 @@ def make_mono_icon(size, rgba):
for index in range(len(rgba))]
# Encode in 1-bit big-endian format.
data = ""
data = b''
for i in range(0, len(pixels), 8):
byte = 0
for j in range(8):
if pixels[i+j] >= 0x80:
byte |= 0x80 >> j
data += chr(byte)
data += bytes(byte)
# This size-32 chunk id is an anomaly in what would otherwise be a
# consistent system of using {s,l,h,t} for {16,32,48,128}-pixel
# icon sizes.
chunkid = { 16: "ics#", 32: "ICN#", 48: "ich#" }[size]
chunkid = { 16: b"ics#", 32: b"ICN#", 48: b"ich#" }[size]
return make_chunk(chunkid, data)
# Mask for full-colour icons: a chunk containing an 8 bpp alpha
@ -52,9 +54,9 @@ def make_mono_icon(size, rgba):
def make_colour_mask(size, rgba):
assert len(rgba) == size * size
data = "".join(map(lambda pix: chr(pix[3]), rgba))
data = bytes(map(lambda pix: pix[3], rgba))
chunkid = { 16: "s8mk", 32: "l8mk", 48: "h8mk", 128: "t8mk" }[size]
chunkid = { 16: b"s8mk", 32: b"l8mk", 48: b"h8mk", 128: b"t8mk" }[size]
return make_chunk(chunkid, data)
# Helper routine for deciding when to start and stop run-length
@ -69,19 +71,18 @@ def runof3(string, position):
def make_colour_icon(size, rgba):
assert len(rgba) == size * size
data = ""
data = b""
# Mysterious extra zero header word appearing only in the size-128
# icon chunk. libicns doesn't know what it's for, and neither do
# I.
if size == 128:
data += "\0\0\0\0"
data += b"\0\0\0\0"
# Handle R,G,B channels in sequence. (Ignore the alpha channel; it
# goes into the separate mask chunk constructed above.)
for chan in range(3):
pixels = "".join([chr(rgba[index][chan])
for index in range(len(rgba))])
pixels = bytes([rgba[index][chan] for index in range(len(rgba))])
# Run-length encode each channel using the following format:
# * byte 0x80-0xFF followed by one literal byte means repeat
@ -98,15 +99,15 @@ def make_colour_icon(size, rgba):
pos < len(pixels) and
pixels[pos] == pixval):
pos += 1
data += chr(0x80 + pos-start - 3) + pixval
data += bytes(0x80 + pos-start - 3) + pixval
else:
while (pos - start < 128 and
pos < len(pixels) and
not runof3(pixels, pos)):
pos += 1
data += chr(0x00 + pos-start - 1) + pixels[start:pos]
data += bytes(0x00 + pos-start - 1) + pixels[start:pos]
chunkid = { 16: "is32", 32: "il32", 48: "ih32", 128: "it32" }[size]
chunkid = { 16: b"is32", 32: b"il32", 48: b"ih32", 128: b"it32" }[size]
return make_chunk(chunkid, data)
# Load an image file from disk and turn it into a simple list of
@ -117,10 +118,10 @@ def make_colour_icon(size, rgba):
# here that the file is in RGBA .pam format (as mkicon.py will have
# generated it).
def load_rgba(filename):
with open(filename) as f:
assert f.readline() == "P7\n"
with open(filename, "rb") as f:
assert f.readline() == b"P7\n"
for line in iter(f.readline, ''):
words = line.rstrip("\n").split()
words = line.decode("ASCII").rstrip("\n").split()
if words[0] == "WIDTH":
width = int(words[1])
elif words[0] == "HEIGHT":
@ -135,10 +136,10 @@ def load_rgba(filename):
assert width == height
data = f.read()
assert len(data) == width*height*4
rgba = [map(ord, data[i:i+4]) for i in range(0, len(data), 4)]
rgba = [list(data[i:i+4]) for i in range(0, len(data), 4)]
return width, rgba
data = ""
data = b""
# Trivial argument format: each argument is a filename prefixed with
# "mono:", "colour:" or "output:". The first two indicate image files
@ -157,7 +158,7 @@ for arg in sys.argv[1:]:
else:
assert False, "bad argument '%s'" % arg
data = make_chunk("icns", data)
data = make_chunk(b"icns", data)
with open(outfile, "w") as f:
with open(outfile, "wb") as f:
f.write(data)

View File

@ -2,9 +2,12 @@
from __future__ import division
import sys
import decimal
import math
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
# Python code which draws the PuTTY icon components at a range of
# sizes.

View File

@ -10,6 +10,8 @@ import collections
from ssh import *
import agenttestdata
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
test_session_id = b'Test16ByteSessId'
assert len(test_session_id) == 16
test_message_to_sign = b'test message to sign'

View File

@ -1,5 +1,9 @@
#!/usr/bin/env python3
import sys
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
def generate():
import hashlib

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python3
import sys
import unittest
import struct
import itertools
@ -17,6 +18,8 @@ from eccref import *
from testcrypt import *
from ssh import *
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
try:
base64decode = base64.decodebytes
except AttributeError:
@ -90,7 +93,7 @@ def last(iterable):
def queued_random_data(nbytes, seed):
hashsize = 512 // 8
data = b''.join(
hashlib.sha512(unicode_to_bytes("preimage:{:d}:{}".format(i, seed)))
hashlib.sha512("preimage:{:d}:{}".format(i, seed).encode('ascii'))
.digest() for i in range((nbytes + hashsize - 1) // hashsize))
data = data[:nbytes]
random_queue(data)
@ -199,9 +202,9 @@ class mpint(MyTestBase):
n = mp_from_hex(hexstr)
i = int(hexstr, 16)
self.assertEqual(mp_get_hex(n),
unicode_to_bytes("{:x}".format(i)))
"{:x}".format(i).encode('ascii'))
self.assertEqual(mp_get_hex_uppercase(n),
unicode_to_bytes("{:X}".format(i)))
"{:X}".format(i).encode('ascii'))
checkHex("0")
checkHex("f")
checkHex("00000000000000000000000000000000000000000000000000")
@ -212,7 +215,7 @@ class mpint(MyTestBase):
n = mp_from_hex(hexstr)
i = int(hexstr, 16)
self.assertEqual(mp_get_decimal(n),
unicode_to_bytes("{:d}".format(i)))
"{:d}".format(i).encode('ascii'))
checkDec("0")
checkDec("f")
checkDec("00000000000000000000000000000000000000000000000000")
@ -1857,10 +1860,10 @@ culpa qui officia deserunt mollit anim id est laborum.
# both parts. Other than that, we don't do much to
# make this a rigorous cryptographic test.
for n, d in [(1,3),(2,3)]:
sigbytes = list(bytevals(sigblob))
sigbytes = list(sigblob)
bit = 8 * len(sigbytes) * n // d
sigbytes[bit // 8] ^= 1 << (bit % 8)
badsig = valbytes(sigbytes)
badsig = bytes(sigbytes)
for key in [pubkey, privkey, privkey2]:
self.assertFalse(ssh_key_verify(
key, badsig, test_message))

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Reference implementation of DES.
#
@ -15,6 +15,8 @@ import struct
import functools
import argparse
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
def bitor(x, y):
return x | y
def split_words(val, width=32):

View File

@ -1,6 +1,9 @@
import sys
import numbers
import itertools
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
from numbertheory import *
class AffinePoint(object):

View File

@ -1,7 +1,10 @@
import sys
import numbers
import itertools
import unittest
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
def invert(a, b):
"Multiplicative inverse of a mod b. a,b must be coprime."
A = (a, 1, 0)

View File

@ -1,6 +1,9 @@
import sys
import struct
import itertools
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
def nbits(n):
# Mimic mp_get_nbits for ordinary Python integers.
assert 0 <= n

View File

@ -6,21 +6,14 @@ import re
import struct
from binascii import hexlify
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
# Expect to be run from the 'test' subdirectory, one level down from
# the main source
putty_srcdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def unicode_to_bytes(arg):
# Slightly fiddly way to do this which should work in Python 2 and 3
if isinstance(arg, type(u'a')) and not isinstance(arg, type(b'a')):
arg = arg.encode("UTF-8")
return arg
def bytevals(b):
return struct.unpack("{:d}B".format(len(b)), b)
def valbytes(b):
b = list(b)
return struct.pack("{:d}B".format(len(b)), *b)
def coerce_to_bytes(arg):
return arg.encode("UTF-8") if isinstance(arg, str) else arg
class ChildProcessFailure(Exception):
pass
@ -75,8 +68,8 @@ class ChildProcess(object):
if self.sp is None:
assert self.exitstatus is None
self.start()
self.write_line(unicode_to_bytes(cmd) + b" " + b" ".join(
unicode_to_bytes(arg) for arg in args))
self.write_line(coerce_to_bytes(cmd) + b" " + b" ".join(
coerce_to_bytes(arg) for arg in args))
argcount = int(self.read_line())
return [self.read_line() for arg in range(argcount)]
def wait_for_exit(self):
@ -154,11 +147,10 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve):
return "NULL"
typename = typename[4:]
if typename == "val_string":
arg = unicode_to_bytes(arg)
arg = coerce_to_bytes(arg)
if isinstance(arg, bytes):
retwords = childprocess.funcall(
"newstring", ["".join("%{:02x}".format(b)
for b in bytevals(arg))])
"newstring", ["".join("%{:02x}".format(b) for b in arg)])
arg = make_retvals([typename], retwords, unpack_strings=False)[0]
to_preserve.append(arg)
if typename == "val_mpint" and isinstance(arg, numbers.Integral):
@ -179,7 +171,7 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve):
if typename in {
"hashalg", "macalg", "keyalg", "cipheralg",
"dh_group", "ecdh_alg", "rsaorder", "primegenpolicy"}:
arg = unicode_to_bytes(arg)
arg = coerce_to_bytes(arg)
if isinstance(arg, bytes) and b" " not in arg:
return arg
if typename == "mpint_list":
@ -188,7 +180,7 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve):
for val in arg:
sublist.append(make_argword(val, ("val_mpint", False),
fnname, argindex, to_preserve))
return b" ".join(unicode_to_bytes(sub) for sub in sublist)
return b" ".join(coerce_to_bytes(sub) for sub in sublist)
raise TypeError(
"Can't convert {}() argument {:d} to {} (value was {!r})".format(
fnname, argindex, typename, arg))
@ -197,7 +189,7 @@ def unpack_string(identifier):
retwords = childprocess.funcall("getstring", [identifier])
childprocess.funcall("free", [identifier])
return re.sub(b"%[0-9A-F][0-9A-F]",
lambda m: valbytes([int(m.group(0)[1:], 16)]),
lambda m: bytes([int(m.group(0)[1:], 16)]),
retwords[0])
def unpack_mp(identifier):

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import argparse
import os