Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'test/testcrypt.py')
-rw-r--r--test/testcrypt.py234
1 files changed, 176 insertions, 58 deletions
diff --git a/test/testcrypt.py b/test/testcrypt.py
index 686302c8..66f63d5c 100644
--- a/test/testcrypt.py
+++ b/test/testcrypt.py
@@ -3,6 +3,7 @@ import os
import numbers
import subprocess
import re
+import string
import struct
from binascii import hexlify
@@ -86,22 +87,25 @@ class ChildProcess(object):
childprocess = ChildProcess()
method_prefixes = {
- 'val_wpoint': 'ecc_weierstrass_',
- 'val_mpoint': 'ecc_montgomery_',
- 'val_epoint': 'ecc_edwards_',
- 'val_hash': 'ssh_hash_',
- 'val_mac': 'ssh2_mac_',
- 'val_key': 'ssh_key_',
- 'val_cipher': 'ssh_cipher_',
- 'val_dh': 'dh_',
- 'val_ecdh': 'ssh_ecdhkex_',
- 'val_rsakex': 'ssh_rsakex_',
- 'val_prng': 'prng_',
- 'val_pcs': 'pcs_',
- 'val_pockle': 'pockle_',
+ 'val_wpoint': ['ecc_weierstrass_'],
+ 'val_mpoint': ['ecc_montgomery_'],
+ 'val_epoint': ['ecc_edwards_'],
+ 'val_hash': ['ssh_hash_'],
+ 'val_mac': ['ssh2_mac_'],
+ 'val_key': ['ssh_key_'],
+ 'val_cipher': ['ssh_cipher_'],
+ 'val_dh': ['dh_'],
+ 'val_ecdh': ['ssh_ecdhkex_'],
+ 'val_rsakex': ['ssh_rsakex_'],
+ 'val_prng': ['prng_'],
+ 'val_pcs': ['pcs_'],
+ 'val_pockle': ['pockle_'],
+ 'val_ntruencodeschedule': ['ntru_encode_schedule_', 'ntru_'],
}
method_lists = {t: [] for t in method_prefixes}
+checked_enum_values = {}
+
class Value(object):
def __init__(self, typename, ident):
self._typename = typename
@@ -148,7 +152,7 @@ def marshal_string(val):
else "%{:02x}".format(b)
for b in val)
-def make_argword(arg, argtype, fnname, argindex, to_preserve):
+def make_argword(arg, argtype, fnname, argindex, argname, to_preserve):
typename, consumed = argtype
if typename.startswith("opt_"):
if arg is None:
@@ -165,8 +169,8 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve):
if isinstance(arg, Value):
if arg._typename != typename:
raise TypeError(
- "{}() argument {:d} should be {} ({} given)".format(
- fnname, argindex, typename, arg._typename))
+ "{}() argument #{:d} ({}) should be {} ({} given)".format(
+ fnname, argindex, argname, typename, arg._typename))
ident = arg._ident
if consumed:
arg._consumed()
@@ -178,20 +182,33 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve):
if typename in {
"hashalg", "macalg", "keyalg", "cipheralg",
"dh_group", "ecdh_alg", "rsaorder", "primegenpolicy",
- "argon2flavour", "fptype"}:
+ "argon2flavour", "fptype", "httpdigesthash"}:
arg = coerce_to_bytes(arg)
if isinstance(arg, bytes) and b" " not in arg:
- return arg
+ dictkey = (typename, arg)
+ if dictkey not in checked_enum_values:
+ retwords = childprocess.funcall("checkenum", [typename, arg])
+ assert len(retwords) == 1
+ checked_enum_values[dictkey] = (retwords[0] == b"ok")
+ if checked_enum_values[dictkey]:
+ return arg
if typename == "mpint_list":
sublist = [make_argword(len(arg), ("uint", False),
- fnname, argindex, to_preserve)]
+ fnname, argindex, argname, to_preserve)]
for val in arg:
sublist.append(make_argword(val, ("val_mpint", False),
- fnname, argindex, to_preserve))
+ fnname, argindex, argname, to_preserve))
+ return b" ".join(coerce_to_bytes(sub) for sub in sublist)
+ if typename == "int16_list":
+ sublist = [make_argword(len(arg), ("uint", False),
+ fnname, argindex, argname, to_preserve)]
+ for val in arg:
+ sublist.append(make_argword(val & 0xFFFF, ("uint", False),
+ fnname, argindex, argname, to_preserve))
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))
+ "Can't convert {}() argument #{:d} ({}) to {} (value was {!r})".format(
+ fnname, argindex, argname, typename, arg))
def unpack_string(identifier):
retwords = childprocess.funcall("getstring", [identifier])
@@ -235,8 +252,10 @@ def make_retval(rettype, word, unpack_strings):
elif rettype == "boolean":
assert word == b"true" or word == b"false"
return word == b"true"
- elif rettype == "pocklestatus":
+ elif rettype in {"pocklestatus", "mr_result"}:
return word.decode("ASCII")
+ elif rettype == "int16_list":
+ return list(map(int, word.split(b',')))
raise TypeError("Can't deal with return value {!r} of type {!r}"
.format(word, rettype))
@@ -246,12 +265,20 @@ def make_retvals(rettypes, retwords, unpack_strings=True):
for rettype, word in zip(rettypes, retwords)]
class Function(object):
- def __init__(self, fnname, rettypes, argtypes):
+ def __init__(self, fnname, rettypes, retnames, argtypes, argnames):
self.fnname = fnname
self.rettypes = rettypes
+ self.retnames = retnames
self.argtypes = argtypes
+ self.argnames = argnames
def __repr__(self):
- return "<Function {}>".format(self.fnname)
+ return "<Function {}({}) -> ({})>".format(
+ self.fnname,
+ ", ".join(("consumed " if c else "")+t+" "+n
+ for (t,c),n in zip(self.argtypes, self.argnames)),
+ ", ".join((t+" "+n if n is not None else t)
+ for t,n in zip(self.rettypes, self.retnames)),
+ )
def __call__(self, *args):
if len(args) != len(self.argtypes):
raise TypeError(
@@ -260,7 +287,8 @@ class Function(object):
to_preserve = []
retwords = childprocess.funcall(
self.fnname, [make_argword(args[i], self.argtypes[i],
- self.fnname, i, to_preserve)
+ self.fnname, i, self.argnames[i],
+ to_preserve)
for i in range(len(args))])
retvals = make_retvals(self.rettypes, retwords)
if len(retvals) == 0:
@@ -269,10 +297,94 @@ class Function(object):
return retvals[0]
return tuple(retvals)
-def _setup(scope):
- header_file = os.path.join(putty_srcdir, "testcrypt.h")
+def _lex_testcrypt_header(header):
+ pat = re.compile(
+ # Skip any combination of whitespace and comments
+ '(?:{})*'.format('|'.join((
+ '[ \t\n]', # whitespace
+ '/\\*(?:.|\n)*?\\*/', # C90-style /* ... */ comment, ended eagerly
+ '//[^\n]*\n', # C99-style comment to end-of-line
+ ))) +
+ # And then match a token
+ '({})'.format('|'.join((
+ # Punctuation
+ '\(',
+ '\)',
+ ',',
+ # Identifier
+ '[A-Za-z_][A-Za-z0-9_]*',
+ # End of string
+ '$',
+ )))
+ )
+
+ pos = 0
+ end = len(header)
+ while pos < end:
+ m = pat.match(header, pos)
+ assert m is not None, (
+ "Failed to lex testcrypt-func.h at byte position {:d}".format(pos))
+
+ pos = m.end()
+ tok = m.group(1)
+ if len(tok) == 0:
+ assert pos == end, (
+ "Empty token should only be returned at end of string")
+ yield tok, m.start(1)
+
+def _parse_testcrypt_header(tokens):
+ def is_id(tok):
+ return tok[0] in string.ascii_letters+"_"
+ def expect(what, why, eof_ok=False):
+ tok, pos = next(tokens)
+ if tok == '' and eof_ok:
+ return None
+ if hasattr(what, '__call__'):
+ description = lambda: ""
+ ok = what(tok)
+ elif isinstance(what, set):
+ description = lambda: " or ".join("'"+x+"' " for x in sorted(what))
+ ok = tok in what
+ else:
+ description = lambda: "'"+what+"' "
+ ok = tok == what
+ if not ok:
+ sys.exit("testcrypt-func.h:{:d}: expected {}{}".format(
+ pos, description(), why))
+ return tok
+
+ while True:
+ tok = expect({"FUNC", "FUNC_WRAPPED"},
+ "at start of function specification", eof_ok=True)
+ if tok is None:
+ break
+
+ expect("(", "after FUNC")
+ rettype = expect(is_id, "return type")
+ expect(",", "after return type")
+ funcname = expect(is_id, "function name")
+ expect(",", "after function name")
+ args = []
+ firstargkind = expect({"ARG", "VOID"}, "at start of argument list")
+ if firstargkind == "VOID":
+ expect(")", "after VOID")
+ else:
+ while True:
+ # Every time we come back to the top of this loop, we've
+ # just seen 'ARG'
+ expect("(", "after ARG")
+ argtype = expect(is_id, "argument type")
+ expect(",", "after argument type")
+ argname = expect(is_id, "argument name")
+ args.append((argtype, argname))
+ expect(")", "at end of ARG")
+ punct = expect({",", ")"}, "after argument")
+ if punct == ")":
+ break
+ expect("ARG", "to begin next argument")
+ yield funcname, rettype, args
- linere = re.compile(r'^FUNC\d+\((.*)\)$')
+def _setup(scope):
valprefix = "val_"
outprefix = "out_"
optprefix = "opt_"
@@ -288,36 +400,42 @@ def _setup(scope):
arg = arg[:arg.index("_", len(valprefix))]
return arg
- with open(header_file) as f:
- for line in iter(f.readline, ""):
- line = line.rstrip("\r\n").replace(" ", "")
- m = linere.match(line)
- if m is not None:
- words = m.group(1).split(",")
- function = words[1]
- rettypes = []
- argtypes = []
- argsconsumed = []
- if words[0] != "void":
- rettypes.append(trim_argtype(words[0]))
- for arg in words[2:]:
- if arg.startswith(outprefix):
- rettypes.append(trim_argtype(arg[len(outprefix):]))
- else:
- consumed = False
- if arg.startswith(consprefix):
- arg = arg[len(consprefix):]
- consumed = True
- arg = trim_argtype(arg)
- argtypes.append((arg, consumed))
- func = Function(function, rettypes, argtypes)
- scope[function] = func
- if len(argtypes) > 0:
- t = argtypes[0][0]
- if (t in method_prefixes and
- function.startswith(method_prefixes[t])):
- methodname = function[len(method_prefixes[t]):]
+ with open(os.path.join(putty_srcdir, "test", "testcrypt-func.h")) as f:
+ header = f.read()
+ tokens = _lex_testcrypt_header(header)
+ for function, rettype, arglist in _parse_testcrypt_header(tokens):
+ rettypes = []
+ retnames = []
+ if rettype != "void":
+ rettypes.append(trim_argtype(rettype))
+ retnames.append(None)
+
+ argtypes = []
+ argnames = []
+ argsconsumed = []
+ for arg, argname in arglist:
+ if arg.startswith(outprefix):
+ rettypes.append(trim_argtype(arg[len(outprefix):]))
+ retnames.append(argname)
+ else:
+ consumed = False
+ if arg.startswith(consprefix):
+ arg = arg[len(consprefix):]
+ consumed = True
+ arg = trim_argtype(arg)
+ argtypes.append((arg, consumed))
+ argnames.append(argname)
+ func = Function(function, rettypes, retnames,
+ argtypes, argnames)
+ scope[function] = func
+ if len(argtypes) > 0:
+ t = argtypes[0][0]
+ if t in method_prefixes:
+ for prefix in method_prefixes[t]:
+ if function.startswith(prefix):
+ methodname = function[len(prefix):]
method_lists[t].append((methodname, func))
+ break
_setup(globals())
del _setup