diff --git a/contrib/encodelib.py b/contrib/encodelib.py index b9d159e2..f64d9634 100644 --- a/contrib/encodelib.py +++ b/contrib/encodelib.py @@ -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] diff --git a/contrib/kh2reg.py b/contrib/kh2reg.py index 3f32f6cd..d6a61659 100755 --- a/contrib/kh2reg.py +++ b/contrib/kh2reg.py @@ -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. diff --git a/contrib/make1305.py b/contrib/make1305.py index 58dca67a..66b7881f 100755 --- a/contrib/make1305.py +++ b/contrib/make1305.py @@ -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 diff --git a/contrib/samplekex.py b/contrib/samplekex.py index 2332e6aa..16842893 100755 --- a/contrib/samplekex.py +++ b/contrib/samplekex.py @@ -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() diff --git a/icons/macicon.py b/icons/macicon.py index b7fed6b2..3b9ff75a 100755 --- a/icons/macicon.py +++ b/icons/macicon.py @@ -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) diff --git a/icons/mkicon.py b/icons/mkicon.py index d7d474ae..c50082db 100755 --- a/icons/mkicon.py +++ b/icons/mkicon.py @@ -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. diff --git a/test/agenttest.py b/test/agenttest.py index 9e44d556..0e063012 100755 --- a/test/agenttest.py +++ b/test/agenttest.py @@ -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' diff --git a/test/agenttestgen.py b/test/agenttestgen.py index bc2068bc..f0c2cb3a 100755 --- a/test/agenttestgen.py +++ b/test/agenttestgen.py @@ -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 diff --git a/test/cryptsuite.py b/test/cryptsuite.py index 16cbf656..b5a3d542 100755 --- a/test/cryptsuite.py +++ b/test/cryptsuite.py @@ -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)) diff --git a/test/desref.py b/test/desref.py index e09a07a1..bc3e51ad 100755 --- a/test/desref.py +++ b/test/desref.py @@ -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): diff --git a/test/eccref.py b/test/eccref.py index b1eb2b28..f39fa5e4 100644 --- a/test/eccref.py +++ b/test/eccref.py @@ -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): diff --git a/test/numbertheory.py b/test/numbertheory.py index 5c22e936..79d88fa8 100644 --- a/test/numbertheory.py +++ b/test/numbertheory.py @@ -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) diff --git a/test/ssh.py b/test/ssh.py index 2fe9f9a3..90eccb7e 100644 --- a/test/ssh.py +++ b/test/ssh.py @@ -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 diff --git a/test/testcrypt.py b/test/testcrypt.py index 5767f558..794373f3 100644 --- a/test/testcrypt.py +++ b/test/testcrypt.py @@ -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): diff --git a/windows/msifixup.py b/windows/msifixup.py index 4d4bb3e4..11606417 100755 --- a/windows/msifixup.py +++ b/windows/msifixup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import os