1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00

New test system for mp_int and cryptography.

I've written a new standalone test program which incorporates all of
PuTTY's crypto code, including the mp_int and low-level elliptic curve
layers but also going all the way up to the implementations of the
MAC, hash, cipher, public key and kex abstractions.

The test program itself, 'testcrypt', speaks a simple line-oriented
protocol on standard I/O in which you write the name of a function
call followed by some inputs, and it gives you back a list of outputs
preceded by a line telling you how many there are. Dynamically
allocated objects are assigned string ids in the protocol, and there's
a 'free' function that tells testcrypt when it can dispose of one.

It's possible to speak that protocol by hand, but cumbersome. I've
also provided a Python module that wraps it, by running testcrypt as a
persistent subprocess and gatewaying all the function calls into
things that look reasonably natural to call from Python. The Python
module and testcrypt.c both read a carefully formatted header file
testcrypt.h which contains the name and signature of every exported
function, so it costs minimal effort to expose a given function
through this test API. In a few cases it's necessary to write a
wrapper in testcrypt.c that makes the function look more friendly, but
mostly you don't even need that. (Though that is one of the
motivations between a lot of API cleanups I've done recently!)

I considered doing Python integration in the more obvious way, by
linking parts of the PuTTY code directly into a native-code .so Python
module. I decided against it because this way is more flexible: I can
run the testcrypt program on its own, or compile it in a way that
Python wouldn't play nicely with (I bet compiling just that .so with
Leak Sanitiser wouldn't do what you wanted when Python loaded it!), or
attach a debugger to it. I can even recompile testcrypt for a
different CPU architecture (32- vs 64-bit, or even running it on a
different machine over ssh or under emulation) and still layer the
nice API on top of that via the local Python interpreter. All I need
is a bidirectional data channel.
This commit is contained in:
Simon Tatham 2019-01-01 19:08:37 +00:00
parent 3d06adce9f
commit 5b14abc30e
6 changed files with 1386 additions and 5 deletions

1
.gitignore vendored
View File

@ -42,6 +42,7 @@
/unix/PuTTY.app
/unix/Pterm.app
/fuzzterm
/testcrypt
/testzlib
/cgtest
/*.DSA

15
Recipe
View File

@ -251,11 +251,15 @@ NONSSH = telnet raw rlogin ldisc pinger
# SSH back end (putty, plink, pscp, psftp).
ARITH = mpint ecc
SSHCOMMON = sshcommon sshrand
+ sshverstring sshcrc sshdes sshmd5 sshrsa sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss ssharcf
+ sshaes sshccp sshsh256 sshsh512 ARITH sshmac marshal nullplug
+ sshgssc pgssapi sshecc wildcard ssh1censor ssh2censor ssh2bpp
SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512
+ sshrsa sshdss sshecc
+ sshdes sshblowf sshaes sshccp ssharcf
+ sshdh
SSHCOMMON = sshcommon sshrand SSHCRYPTO
+ sshverstring sshcrc
+ sshcrcda sshpubk sshzlib
+ sshmac marshal nullplug
+ sshgssc pgssapi wildcard ssh1censor ssh2censor ssh2bpp
+ ssh2transport ssh2transhk ssh2connection portfwd x11fwd
+ ssh1connection ssh1bpp
SSH = SSHCOMMON ssh ssh2bpp-bare
@ -374,6 +378,7 @@ osxlaunch : [UT] osxlaunch
fuzzterm : [UT] UXTERM CHARSET MISC version uxmisc uxucs fuzzterm time settings
+ uxstore be_none uxnogtk memory
testcrypt : [UT] testcrypt SSHCRYPTO marshal utils memory tree234
testzlib : [UT] testzlib sshzlib memory
uppity : [UT] uxserver SSHSERVER UXMISC uxsignal uxnoise uxgss uxnogtk

2
defs.h
View File

@ -19,6 +19,8 @@
/* Work around lack of inttypes.h in older MSVC */
#define PRIx32 "x"
#define PRIu64 "I64u"
#define PRIdMAX "I64d"
#define PRIXMAX "I64X"
#define SCNu64 "I64u"
#else
#include <inttypes.h>

226
test/testcrypt.py Normal file
View File

@ -0,0 +1,226 @@
import sys
import os
import numbers
import subprocess
import re
from binascii import hexlify
# 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
# Another pair of P2/P3 compatibility shims, to give a stream of
# integers corresponding to the byte values in a bytes object, and to
# take an integer and return a bytes object containing a byte with
# that value.
if b'A'[0] != b'A':
def bytevals(arg):
return arg # in P3 this is a no-op
def byte2str(arg):
return bytes([arg])
else:
def bytevals(arg):
return map(ord, arg) # in P2 you have to use ord()
def byte2str(arg):
return chr(arg)
class ChildProcess(object):
def __init__(self):
self.sp = None
self.debug = None
dbg = os.environ.get("PUTTY_TESTCRYPT_DEBUG")
if dbg is not None:
if dbg == "stderr":
self.debug = sys.stderr
else:
sys.stderr.write("Unknown value '{}' for PUTTY_TESTCRYPT_DEBUG"
" (try 'stderr'\n")
def start(self):
assert self.sp is None
override_command = os.environ.get("PUTTY_TESTCRYPT")
if override_command is None:
cmd = [os.path.join(putty_srcdir, "testcrypt")]
shell = False
else:
cmd = override_command
shell = True
self.sp = subprocess.Popen(
cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def write_line(self, line):
if self.debug is not None:
self.debug.write("send: {}\n".format(line))
self.sp.stdin.write(line + b"\n")
self.sp.stdin.flush()
def read_line(self):
line = self.sp.stdout.readline().rstrip(b"\r\n")
if self.debug is not None:
self.debug.write("recv: {}\n".format(line))
return line
def funcall(self, cmd, args):
if self.sp is None:
self.start()
self.write_line(unicode_to_bytes(cmd) + b" " + b" ".join(
unicode_to_bytes(arg) for arg in args))
argcount = int(self.read_line())
return [self.read_line() for arg in range(argcount)]
def check_return_status(self):
assert self.sp is not None
self.sp.stdin.close()
status = self.sp.wait()
if status != 0:
raise Exception("testcrypt returned exit status {}".format(status))
childprocess = ChildProcess()
class Value(object):
def __init__(self, typename, ident):
self.typename = typename
self.ident = ident
def consumed(self):
self.ident = None
def __repr__(self):
return "Value({!r}, {!r})".format(self.typename, self.ident)
def __del__(self):
if self.ident is not None:
childprocess.funcall("free", [self.ident])
def __long__(self):
if self.typename != "val_mpint":
raise TypeError("testcrypt values of types other than mpint"
" cannot be converted to integer")
hexval = childprocess.funcall("mp_dump", [self.ident])[0]
return 0 if len(hexval) == 0 else int(hexval, 16)
def __int__(self):
return int(self.__long__())
def make_argword(arg, argtype, fnname, argindex, to_preserve):
typename, consumed = argtype
if typename.startswith("opt_"):
if arg is None:
return "NULL"
typename = typename[4:]
if typename == "val_string":
arg = unicode_to_bytes(arg)
if isinstance(arg, bytes):
retwords = childprocess.funcall(
"newstring", ["".join("%{:02x}".format(b)
for b in bytevals(arg))])
arg = make_retvals([typename], retwords, unpack_strings=False)[0]
to_preserve.append(arg)
if typename == "val_mpint" and isinstance(arg, numbers.Integral):
retwords = childprocess.funcall("mp_literal", ["0x{:x}".format(arg)])
arg = make_retvals([typename], retwords)[0]
to_preserve.append(arg)
if isinstance(arg, Value):
if arg.typename != typename:
raise TypeError(
"{}() argument {:d} should be {} ({} given)".format(
fnname, argindex, typename, arg.typename))
ident = arg.ident
if consumed:
arg.consumed()
return ident
if typename == "uint" and isinstance(arg, numbers.Integral):
return "0x{:x}".format(arg)
if typename in {
"hashalg", "macalg", "keyalg", "ssh1_cipheralg", "ssh2_cipheralg",
"dh_group", "ecdh_alg", "rsaorder"}:
arg = unicode_to_bytes(arg)
if isinstance(arg, bytes) and b" " not in arg:
return arg
raise TypeError(
"Can't convert {}() argument {:d} to {} (value was {!r})".format(
fnname, argindex, typename, arg))
def make_retval(rettype, word, unpack_strings):
if rettype == "val_string" and unpack_strings:
retwords = childprocess.funcall("getstring", [word])
childprocess.funcall("free", [word])
return re.sub(b"%[0-9A-F][0-9A-F]",
lambda m: byte2str(int(m.group(0)[1:], 16)),
retwords[0])
if rettype.startswith("val_"):
return Value(rettype, word)
elif rettype == "uint":
return int(word, 0)
elif rettype == "boolean":
assert word == b"true" or word == b"false"
return word == b"true"
raise TypeError("Can't deal with return value {!r} of type {!r}"
.format(rettype, word))
def make_retvals(rettypes, retwords, unpack_strings=True):
assert len(rettypes) == len(retwords) # FIXME: better exception
return [make_retval(rettype, word, unpack_strings)
for rettype, word in zip(rettypes, retwords)]
class Function(object):
def __init__(self, fnname, rettypes, argtypes):
self.fnname = fnname
self.rettypes = rettypes
self.argtypes = argtypes
def __repr__(self):
return "<Function {}>".format(self.fnname)
def __call__(self, *args):
if len(args) != len(self.argtypes):
raise TypeError(
"{}() takes exactly {} arguments ({} given)".format(
self.fnname, len(self.argtypes), len(args)))
to_preserve = []
retwords = childprocess.funcall(
self.fnname, [make_argword(args[i], self.argtypes[i],
self.fnname, i, to_preserve)
for i in range(len(args))])
retvals = make_retvals(self.rettypes, retwords)
if len(retvals) == 0:
return None
if len(retvals) == 1:
return retvals[0]
return tuple(retvals)
def _setup(scope):
header_file = os.path.join(putty_srcdir, "testcrypt.h")
prefix, suffix = "FUNC(", ")"
valprefix = "val_"
outprefix = "out_"
consprefix = "consumed_"
def trim_argtype(arg):
if (arg.startswith(valprefix) and
"_" in arg[len(valprefix):]):
# Strip suffixes like val_string_asciz
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(" ", "")
if line.startswith(prefix) and line.endswith(suffix):
words = line[len(prefix):-len(suffix)].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))
scope[function] = Function(function, rettypes, argtypes)
_setup(globals())
del _setup

921
testcrypt.c Normal file
View File

@ -0,0 +1,921 @@
/*
* testcrypt: a standalone test program that provides direct access to
* PuTTY's cryptography and mp_int code.
*/
/*
* This program speaks a line-oriented protocol on standard input and
* standard output. It's a half-duplex protocol: it expects to read
* one line of command, and then produce a fixed amount of output
* (namely a line containing a decimal integer, followed by that many
* lines each containing one return value).
*
* The protocol is human-readable enough to make it debuggable, but
* verbose enough that you probably wouldn't want to speak it by hand
* at any great length. The Python program test/testcrypt.py wraps it
* to give a more useful user-facing API, by invoking this binary as a
* subprocess.
*
* (I decided that was a better idea than making this program an
* actual Python module, partly because you can rewrap the same binary
* in another scripting language if you prefer, but mostly because
* it's easy to attach a debugger to testcrypt or to run it under
* sanitisers or valgrind or what have you.)
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "defs.h"
#include "ssh.h"
#include "misc.h"
#include "mpint.h"
#include "ecc.h"
static NORETURN void fatal_error(const char *p, ...)
{
va_list ap;
fprintf(stderr, "testcrypt: ");
va_start(ap, p);
vfprintf(stderr, p, ap);
va_end(ap);
fputc('\n', stderr);
exit(1);
}
void out_of_memory(void) { fatal_error("out of memory"); }
static bufchain random_data_queue;
int random_byte(void)
{
unsigned char u;
if (bufchain_try_fetch_consume(&random_data_queue, &u, 1))
return u;
fatal_error("No random data in queue");
return 0;
}
#define VALUE_TYPES(X) \
X(string, strbuf *, strbuf_free(v)) \
X(mpint, mp_int *, mp_free(v)) \
X(modsqrt, ModsqrtContext *, modsqrt_free(v)) \
X(monty, MontyContext *, monty_free(v)) \
X(wcurve, WeierstrassCurve *, ecc_weierstrass_curve_free(v)) \
X(wpoint, WeierstrassPoint *, ecc_weierstrass_point_free(v)) \
X(mcurve, MontgomeryCurve *, ecc_montgomery_curve_free(v)) \
X(mpoint, MontgomeryPoint *, ecc_montgomery_point_free(v)) \
X(ecurve, EdwardsCurve *, ecc_edwards_curve_free(v)) \
X(epoint, EdwardsPoint *, ecc_edwards_point_free(v)) \
X(hash, ssh_hash *, ssh_hash_free(v)) \
X(key, ssh_key *, ssh_key_free(v)) \
X(ssh1_cipher, ssh1_cipher *, ssh1_cipher_free(v)) \
X(ssh2_cipher, ssh2_cipher *, ssh2_cipher_free(v)) \
X(mac, ssh2_mac *, ssh2_mac_free(v)) \
X(dh, dh_ctx *, dh_cleanup(v)) \
X(ecdh, ecdh_key *, ssh_ecdhkex_freekey(v)) \
X(rsakex, RSAKey *, ssh_rsakex_freekey(v)) \
X(rsa, RSAKey *, rsa_free(v)) \
/* end of list */
typedef struct Value Value;
typedef enum ValueType ValueType;
enum ValueType {
#define VALTYPE_ENUM(n,t,f) VT_##n,
VALUE_TYPES(VALTYPE_ENUM)
#undef VALTYPE_ENUM
};
const char *const type_names[] = {
#define VALTYPE_NAME(n,t,f) #n,
VALUE_TYPES(VALTYPE_NAME)
#undef VALTYPE_NAME
};
struct Value {
/*
* Protocol identifier assigned to this value when it was created.
* Lives in the same malloced block as this Value object itself.
*/
ptrlen id;
/*
* Type of the value.
*/
ValueType type;
/*
* Union of all the things it could hold.
*/
union {
#define VALTYPE_UNION(n,t,f) t vu_##n;
VALUE_TYPES(VALTYPE_UNION)
#undef VALTYPE_UNION
};
};
static int valuecmp(void *av, void *bv)
{
Value *a = (Value *)av, *b = (Value *)bv;
return ptrlen_strcmp(a->id, b->id);
}
static int valuefind(void *av, void *bv)
{
ptrlen *a = (ptrlen *)av;
Value *b = (Value *)bv;
return ptrlen_strcmp(*a, b->id);
}
static tree234 *values;
static Value *value_new(ValueType vt)
{
static uint64_t next_index = 0;
char *name = dupprintf("%s%"PRIu64, type_names[vt], next_index++);
size_t namelen = strlen(name);
Value *val = snew_plus(Value, namelen+1);
memcpy(snew_plus_get_aux(val), name, namelen+1);
val->id.ptr = snew_plus_get_aux(val);
val->id.len = namelen;
val->type = vt;
Value *added = add234(values, val);
assert(added == val);
sfree(name);
return val;
}
#define VALTYPE_RETURNFN(n,t,f) \
void return_val_##n(strbuf *out, t v) { \
Value *val = value_new(VT_##n); \
val->vu_##n = v; \
put_datapl(out, val->id); \
put_byte(out, '\n'); \
}
VALUE_TYPES(VALTYPE_RETURNFN)
#undef VALTYPE_RETURNFN
static ptrlen get_word(BinarySource *in)
{
ptrlen toret;
toret.ptr = get_ptr(in);
toret.len = 0;
while (get_avail(in) && get_byte(in) != ' ')
toret.len++;
return toret;
}
static const ssh_hashalg *get_hashalg(BinarySource *in)
{
static const struct {
const char *key;
const ssh_hashalg *value;
} algs[] = {
{"md5", &ssh_md5},
{"sha1", &ssh_sha1},
{"sha256", &ssh_sha256},
{"sha384", &ssh_sha384},
{"sha512", &ssh_sha512},
};
ptrlen name = get_word(in);
for (size_t i = 0; i < lenof(algs); i++)
if (ptrlen_eq_string(name, algs[i].key))
return algs[i].value;
fatal_error("hashalg '%.*s': not found", PTRLEN_PRINTF(name));
}
static const ssh2_macalg *get_macalg(BinarySource *in)
{
static const struct {
const char *key;
const ssh2_macalg *value;
} algs[] = {
{"hmac_md5", &ssh_hmac_md5},
{"hmac_sha1", &ssh_hmac_sha1},
{"hmac_sha1_buggy", &ssh_hmac_sha1_buggy},
{"hmac_sha1_96", &ssh_hmac_sha1_96},
{"hmac_sha1_96_buggy", &ssh_hmac_sha1_96_buggy},
{"hmac_sha256", &ssh_hmac_sha256},
{"poly1305", &ssh2_poly1305},
};
ptrlen name = get_word(in);
for (size_t i = 0; i < lenof(algs); i++)
if (ptrlen_eq_string(name, algs[i].key))
return algs[i].value;
fatal_error("macalg '%.*s': not found", PTRLEN_PRINTF(name));
}
static const ssh_keyalg *get_keyalg(BinarySource *in)
{
static const struct {
const char *key;
const ssh_keyalg *value;
} algs[] = {
{"dsa", &ssh_dss},
{"rsa", &ssh_rsa},
{"ed25519", &ssh_ecdsa_ed25519},
{"p256", &ssh_ecdsa_nistp256},
{"p384", &ssh_ecdsa_nistp384},
{"521", &ssh_ecdsa_nistp521},
};
ptrlen name = get_word(in);
for (size_t i = 0; i < lenof(algs); i++)
if (ptrlen_eq_string(name, algs[i].key))
return algs[i].value;
fatal_error("keyalg '%.*s': not found", PTRLEN_PRINTF(name));
}
static const ssh1_cipheralg *get_ssh1_cipheralg(BinarySource *in)
{
static const struct {
const char *key;
const ssh1_cipheralg *value;
} algs[] = {
{"3des", &ssh1_3des},
{"des", &ssh1_des},
{"blowfish", &ssh1_blowfish},
};
ptrlen name = get_word(in);
for (size_t i = 0; i < lenof(algs); i++)
if (ptrlen_eq_string(name, algs[i].key))
return algs[i].value;
fatal_error("ssh1_cipheralg '%.*s': not found", PTRLEN_PRINTF(name));
}
static const ssh2_cipheralg *get_ssh2_cipheralg(BinarySource *in)
{
static const struct {
const char *key;
const ssh2_cipheralg *value;
} algs[] = {
{"3des_ctr", &ssh_3des_ssh2_ctr},
{"3des", &ssh_3des_ssh2},
{"des", &ssh_des_ssh2},
{"des_sshcom", &ssh_des_sshcom_ssh2},
{"aes256_ctr", &ssh_aes256_ctr},
{"aes256", &ssh_aes256},
{"aes192_ctr", &ssh_aes192_ctr},
{"aes192", &ssh_aes192},
{"aes128_ctr", &ssh_aes128_ctr},
{"aes128", &ssh_aes128},
{"blowfish", &ssh_blowfish_ssh2_ctr},
{"blowfish", &ssh_blowfish_ssh2},
{"arcfour256", &ssh_arcfour256_ssh2},
{"arcfour128", &ssh_arcfour128_ssh2},
{"chacha20_poly1305", &ssh2_chacha20_poly1305},
};
ptrlen name = get_word(in);
for (size_t i = 0; i < lenof(algs); i++)
if (ptrlen_eq_string(name, algs[i].key))
return algs[i].value;
fatal_error("ssh2_cipheralg '%.*s': not found", PTRLEN_PRINTF(name));
}
static const struct ssh_kex *get_dh_group(BinarySource *in)
{
static const struct {
const char *key;
const struct ssh_kexes *value;
} algs[] = {
{"group1", &ssh_diffiehellman_group1},
{"group14", &ssh_diffiehellman_group14},
};
ptrlen name = get_word(in);
for (size_t i = 0; i < lenof(algs); i++)
if (ptrlen_eq_string(name, algs[i].key))
return algs[i].value->list[0];
fatal_error("dh_group '%.*s': not found", PTRLEN_PRINTF(name));
}
static const struct ssh_kex *get_ecdh_alg(BinarySource *in)
{
static const struct {
const char *key;
const struct ssh_kex *value;
} algs[] = {
{"curve25519", &ssh_ec_kex_curve25519},
{"nistp256", &ssh_ec_kex_nistp256},
{"nistp384", &ssh_ec_kex_nistp384},
{"nistp521", &ssh_ec_kex_nistp521},
};
ptrlen name = get_word(in);
for (size_t i = 0; i < lenof(algs); i++)
if (ptrlen_eq_string(name, algs[i].key))
return algs[i].value;
fatal_error("ecdh_alg '%.*s': not found", PTRLEN_PRINTF(name));
}
static RsaSsh1Order get_rsaorder(BinarySource *in)
{
static const struct {
const char *key;
RsaSsh1Order value;
} orders[] = {
{"exponent_first", RSA_SSH1_EXPONENT_FIRST},
{"modulus_first", RSA_SSH1_MODULUS_FIRST},
};
ptrlen name = get_word(in);
for (size_t i = 0; i < lenof(orders); i++)
if (ptrlen_eq_string(name, orders[i].key))
return orders[i].value;
fatal_error("rsaorder '%.*s': not found", PTRLEN_PRINTF(name));
}
static uintmax_t get_uint(BinarySource *in)
{
ptrlen word = get_word(in);
char *string = mkstr(word);
uintmax_t toret = strtoumax(string, NULL, 0);
sfree(string);
return toret;
}
static Value *lookup_value(ptrlen word)
{
Value *val = find234(values, &word, valuefind);
if (!val)
fatal_error("id '%.*s': not found", PTRLEN_PRINTF(word));
return val;
}
static Value *get_value(BinarySource *in)
{
return lookup_value(get_word(in));
}
typedef void (*finaliser_fn_t)(strbuf *out, void *ctx);
struct finaliser {
finaliser_fn_t fn;
void *ctx;
};
static struct finaliser *finalisers;
size_t nfinalisers, finalisersize;
static void add_finaliser(finaliser_fn_t fn, void *ctx)
{
if (nfinalisers >= finalisersize) {
finalisersize = nfinalisers * 5 / 4 + 16;
finalisers = sresize(finalisers, finalisersize, struct finaliser);
}
finalisers[nfinalisers].fn = fn;
finalisers[nfinalisers].ctx = ctx;
nfinalisers++;
}
static void run_finalisers(strbuf *out)
{
for (size_t i = 0; i < nfinalisers; i++)
finalisers[i].fn(out, finalisers[i].ctx);
nfinalisers = 0;
}
static void finaliser_return_value(strbuf *out, void *ctx)
{
Value *val = (Value *)ctx;
put_datapl(out, val->id);
put_byte(out, '\n');
}
#define VALTYPE_GETFN(n,t,f) \
static Value *unwrap_value_##n(Value *val) { \
ValueType expected = VT_##n; \
if (expected != val->type) \
fatal_error("id '%.*s': expected %s, got %s", \
PTRLEN_PRINTF(val->id), \
type_names[expected], type_names[val->type]); \
return val; \
} \
static Value *get_value_##n(BinarySource *in) { \
return unwrap_value_##n(get_value(in)); \
} \
static t get_val_##n(BinarySource *in) { \
return get_value_##n(in)->vu_##n; \
}
VALUE_TYPES(VALTYPE_GETFN)
#undef VALTYPE_GETFN
static ptrlen get_val_string_ptrlen(BinarySource *in)
{
return ptrlen_from_strbuf(get_val_string(in));
}
static char *get_val_string_asciz(BinarySource *in)
{
return get_val_string(in)->s;
}
static mp_int **get_out_val_mpint(BinarySource *in)
{
Value *val = value_new(VT_mpint);
add_finaliser(finaliser_return_value, val);
return &val->vu_mpint;
}
static void finaliser_return_uint(strbuf *out, void *ctx)
{
unsigned *uval = (unsigned *)ctx;
strbuf_catf(out, "%u\n", *uval);
sfree(uval);
}
static unsigned *get_out_uint(BinarySource *in)
{
unsigned *uval = snew(unsigned);
add_finaliser(finaliser_return_uint, uval);
return uval;
}
static BinarySink *get_out_val_string_binarysink(BinarySource *in)
{
Value *val = value_new(VT_string);
val->vu_string = strbuf_new();
add_finaliser(finaliser_return_value, val);
return BinarySink_UPCAST(val->vu_string);
}
static void finaliser_sfree(strbuf *out, void *ctx)
{
sfree(ctx);
}
static BinarySource *get_val_string_binarysource(BinarySource *in)
{
strbuf *sb = get_val_string(in);
BinarySource *src = snew(BinarySource);
BinarySource_BARE_INIT(src, sb->u, sb->len);
add_finaliser(finaliser_sfree, src);
return src;
}
static ssh_hash *get_consumed_val_hash(BinarySource *in)
{
Value *val = get_value_hash(in);
ssh_hash *toret = val->vu_hash;
del234(values, val);
sfree(val);
return toret;
}
static void return_int(strbuf *out, intmax_t u)
{
strbuf_catf(out, "%"PRIdMAX"\n", u);
}
static void return_uint(strbuf *out, uintmax_t u)
{
strbuf_catf(out, "0x%"PRIXMAX"\n", u);
}
static void return_boolean(strbuf *out, bool b)
{
strbuf_catf(out, "%s\n", b ? "true" : "false");
}
static void return_val_string_asciz(strbuf *out, char *s)
{
strbuf *sb = strbuf_new();
put_data(sb, s, strlen(s));
sfree(s);
return_val_string(out, sb);
}
static void handle_hello(BinarySource *in, strbuf *out)
{
strbuf_catf(out, "hello, world");
}
static void rsa_free(RSAKey *rsa)
{
freersakey(rsa);
sfree(rsa);
}
static void free_value(Value *val)
{
switch (val->type) {
#define VALTYPE_FREE(n,t,f) case VT_##n: { t v = val->vu_##n; (f); break; }
VALUE_TYPES(VALTYPE_FREE)
#undef VALTYPE_FREE
}
sfree(val);
}
static void handle_free(BinarySource *in, strbuf *out)
{
Value *val = get_value(in);
del234(values, val);
free_value(val);
}
static void handle_newstring(BinarySource *in, strbuf *out)
{
strbuf *sb = strbuf_new();
while (get_avail(in)) {
char c = get_byte(in);
if (c == '%') {
char hex[3];
hex[0] = get_byte(in);
if (hex[0] != '%') {
hex[1] = get_byte(in);
hex[2] = '\0';
c = strtoul(hex, NULL, 16);
}
}
put_byte(sb, c);
}
return_val_string(out, sb);
}
static void handle_getstring(BinarySource *in, strbuf *out)
{
strbuf *sb = get_val_string(in);
for (size_t i = 0; i < sb->len; i++) {
char c = sb->s[i];
if (c > ' ' && c < 0x7F && c != '%') {
put_byte(out, c);
} else {
strbuf_catf(out, "%%%02X", 0xFFU & (unsigned)c);
}
}
put_byte(out, '\n');
}
static void handle_mp_literal(BinarySource *in, strbuf *out)
{
ptrlen pl = get_word(in);
char *str = mkstr(pl);
mp_int *mp = mp__from_string_literal(str);
sfree(str);
return_val_mpint(out, mp);
}
static void handle_mp_dump(BinarySource *in, strbuf *out)
{
mp_int *mp = get_val_mpint(in);
for (size_t i = mp_max_bytes(mp); i-- > 0 ;)
strbuf_catf(out, "%02X", mp_get_byte(mp, i));
put_byte(out, '\n');
}
static void random_queue(ptrlen pl)
{
bufchain_add(&random_data_queue, pl.ptr, pl.len);
}
static size_t random_queue_len(void)
{
return bufchain_size(&random_data_queue);
}
static void random_clear(void)
{
bufchain_clear(&random_data_queue);
}
#define WRAP_monty_identity ,
mp_int *monty_identity_wrapper(MontyContext *mc)
{
return mp_copy(monty_identity(mc));
}
#define WRAP_monty_modulus ,
mp_int *monty_modulus_wrapper(MontyContext *mc)
{
return mp_copy(monty_modulus(mc));
}
#define WRAP_ssh_hash_final ,
strbuf *ssh_hash_final_wrapper(ssh_hash *h)
{
strbuf *sb = strbuf_new();
void *p = strbuf_append(sb, ssh_hash_alg(h)->hlen);
ssh_hash_final(h, p);
return sb;
}
#define WRAP_ssh1_cipher_sesskey ,
void ssh1_cipher_sesskey_wrapper(ssh1_cipher *c, ptrlen key)
{
if (key.len != 32)
fatal_error("ssh1_cipher_sesskey: needs exactly 32 bytes");
ssh1_cipher_sesskey(c, key.ptr);
}
#define WRAP_ssh1_cipher_encrypt ,
strbuf *ssh1_cipher_encrypt_wrapper(ssh1_cipher *c, ptrlen input)
{
if (input.len % c->vt->blksize)
fatal_error("ssh1_cipher_encrypt: needs a multiple of %d bytes",
c->vt->blksize);
strbuf *sb = strbuf_new();
put_datapl(sb, input);
ssh1_cipher_encrypt(c, sb->u, sb->len);
return sb;
}
#define WRAP_ssh1_cipher_decrypt ,
strbuf *ssh1_cipher_decrypt_wrapper(ssh1_cipher *c, ptrlen input)
{
if (input.len % c->vt->blksize)
fatal_error("ssh1_cipher_decrypt: needs a multiple of %d bytes",
c->vt->blksize);
strbuf *sb = strbuf_new();
put_datapl(sb, input);
ssh1_cipher_decrypt(c, sb->u, sb->len);
return sb;
}
#define WRAP_ssh2_cipher_setiv ,
void ssh2_cipher_setiv_wrapper(ssh2_cipher *c, ptrlen key)
{
if (key.len != ssh2_cipher_alg(c)->blksize)
fatal_error("ssh2_cipher_setiv: needs exactly %d bytes",
ssh2_cipher_alg(c)->blksize);
ssh2_cipher_setiv(c, key.ptr);
}
#define WRAP_ssh2_cipher_setkey ,
void ssh2_cipher_setkey_wrapper(ssh2_cipher *c, ptrlen key)
{
if (key.len != ssh2_cipher_alg(c)->padded_keybytes)
fatal_error("ssh2_cipher_setkey: needs exactly %d bytes",
ssh2_cipher_alg(c)->padded_keybytes);
ssh2_cipher_setkey(c, key.ptr);
}
#define WRAP_ssh2_cipher_encrypt ,
strbuf *ssh2_cipher_encrypt_wrapper(ssh2_cipher *c, ptrlen input)
{
if (input.len % ssh2_cipher_alg(c)->blksize)
fatal_error("ssh2_cipher_encrypt: needs a multiple of %d bytes",
ssh2_cipher_alg(c)->blksize);
strbuf *sb = strbuf_new();
put_datapl(sb, input);
ssh2_cipher_encrypt(c, sb->u, sb->len);
return sb;
}
#define WRAP_ssh2_cipher_decrypt ,
strbuf *ssh2_cipher_decrypt_wrapper(ssh2_cipher *c, ptrlen input)
{
if (input.len % ssh2_cipher_alg(c)->blksize)
fatal_error("ssh2_cipher_decrypt: needs a multiple of %d bytes",
ssh2_cipher_alg(c)->blksize);
strbuf *sb = strbuf_new();
put_datapl(sb, input);
ssh2_cipher_decrypt(c, sb->u, sb->len);
return sb;
}
#define WRAP_ssh2_cipher_encrypt_length ,
strbuf *ssh2_cipher_encrypt_length_wrapper(ssh2_cipher *c, ptrlen input,
unsigned long seq)
{
if (input.len != 4)
fatal_error("ssh2_cipher_encrypt_length: needs exactly 4 bytes");
strbuf *sb = strbuf_new();
put_datapl(sb, input);
ssh2_cipher_encrypt_length(c, sb->u, sb->len, seq);
return sb;
}
#define WRAP_ssh2_cipher_decrypt_length ,
strbuf *ssh2_cipher_decrypt_length_wrapper(ssh2_cipher *c, ptrlen input,
unsigned long seq)
{
if (input.len % ssh2_cipher_alg(c)->blksize)
fatal_error("ssh2_cipher_decrypt_length: needs exactly 4 bytes");
strbuf *sb = strbuf_new();
put_datapl(sb, input);
ssh2_cipher_decrypt_length(c, sb->u, sb->len, seq);
return sb;
}
#define WRAP_ssh2_mac_genresult ,
strbuf *ssh2_mac_genresult_wrapper(ssh2_mac *m)
{
strbuf *sb = strbuf_new();
void *u = strbuf_append(sb, ssh2_mac_alg(m)->len);
ssh2_mac_genresult(m, u);
return sb;
}
#define WRAP_dh_validate_f ,
bool dh_validate_f_wrapper(dh_ctx *dh, mp_int *f)
{
return dh_validate_f(dh, f) == NULL;
}
void ssh_hash_update(ssh_hash *h, ptrlen pl)
{
put_datapl(h, pl);
}
void ssh2_mac_update(ssh2_mac *m, ptrlen pl)
{
put_datapl(m, pl);
}
static RSAKey *rsa_new(void)
{
RSAKey *rsa = snew(RSAKey);
memset(rsa, 0, sizeof(RSAKey));
return rsa;
}
#define WRAP_rsa_ssh1_encrypt ,
strbuf *rsa_ssh1_encrypt_wrapper(ptrlen input, RSAKey *key)
{
/* Fold the boolean return value in C into the string return value
* for this purpose, by returning the empty string on failure */
strbuf *sb = strbuf_new();
put_datapl(sb, input);
if (!rsa_ssh1_encrypt(sb->u, sb->len, key))
sb->len = 0;
return sb;
}
#define WRAP_rsa_ssh1_decrypt_pkcs1 ,
strbuf *rsa_ssh1_decrypt_pkcs1_wrapper(mp_int *input, RSAKey *key)
{
/* Again, return "" on failure */
strbuf *sb = strbuf_new();
if (!rsa_ssh1_decrypt_pkcs1(input, key, sb))
sb->len = 0;
return sb;
}
#define return_void(out, expression) (expression)
#define VALTYPE_TYPEDEF(n,t,f) \
typedef t TD_val_##n; \
typedef t *TD_out_val_##n;
VALUE_TYPES(VALTYPE_TYPEDEF)
#undef VALTYPE_TYPEDEF
#define OPTIONAL_PTR_FUNC(type) \
typedef TD_val_##type TD_opt_val_##type; \
static TD_opt_val_##type get_opt_val_##type(BinarySource *in) { \
ptrlen word = get_word(in); \
if (ptrlen_eq_string(word, "NULL")) \
return NULL; \
return unwrap_value_##type(lookup_value(word))->vu_##type; \
}
OPTIONAL_PTR_FUNC(ssh2_cipher)
OPTIONAL_PTR_FUNC(mpint)
typedef uintmax_t TD_uint;
typedef ptrlen TD_val_string_ptrlen;
typedef char *TD_val_string_asciz;
typedef BinarySource *TD_val_string_binarysource;
typedef unsigned *TD_out_uint;
typedef BinarySink *TD_out_val_string_binarysink;
typedef ssh_hash *TD_consumed_val_hash;
typedef const ssh_hashalg *TD_hashalg;
typedef const ssh2_macalg *TD_macalg;
typedef const ssh_keyalg *TD_keyalg;
typedef const ssh1_cipheralg *TD_ssh1_cipheralg;
typedef const ssh2_cipheralg *TD_ssh2_cipheralg;
typedef const struct ssh_kex *TD_dh_group;
typedef const struct ssh_kex *TD_ecdh_alg;
typedef RsaSsh1Order TD_rsaorder;
#define WRAPPED_NAME_INNER(a, b, ...) b
#define WRAPPED_NAME_OUTER(...) WRAPPED_NAME_INNER(__VA_ARGS__)
#define WRAPPED_NAME(func) WRAPPED_NAME_OUTER(WRAP_##func func##_wrapper, func)
#define FUNC0(rettype, function) \
static void handle_##function(BinarySource *in, strbuf *out) { \
return_##rettype(out, WRAPPED_NAME(function)()); \
}
#define FUNC1(rettype, function, type1) \
static void handle_##function(BinarySource *in, strbuf *out) { \
TD_##type1 arg1 = get_##type1(in); \
return_##rettype(out, WRAPPED_NAME(function)(arg1)); \
}
#define FUNC2(rettype, function, type1, type2) \
static void handle_##function(BinarySource *in, strbuf *out) { \
TD_##type1 arg1 = get_##type1(in); \
TD_##type2 arg2 = get_##type2(in); \
return_##rettype(out, WRAPPED_NAME(function)(arg1, arg2)); \
}
#define FUNC3(rettype, function, type1, type2, type3) \
static void handle_##function(BinarySource *in, strbuf *out) { \
TD_##type1 arg1 = get_##type1(in); \
TD_##type2 arg2 = get_##type2(in); \
TD_##type3 arg3 = get_##type3(in); \
return_##rettype(out, WRAPPED_NAME(function)(arg1, arg2, arg3)); \
}
#define FUNC4(rettype, function, type1, type2, type3, type4) \
static void handle_##function(BinarySource *in, strbuf *out) { \
TD_##type1 arg1 = get_##type1(in); \
TD_##type2 arg2 = get_##type2(in); \
TD_##type3 arg3 = get_##type3(in); \
TD_##type4 arg4 = get_##type4(in); \
return_##rettype(out, WRAPPED_NAME(function)(arg1, arg2, arg3, arg4)); \
}
#define FUNC_SELECT_OUTER(...) \
FUNC_SELECT_INNER(__VA_ARGS__,FUNC4,FUNC3,FUNC2,FUNC1,FUNC0)(__VA_ARGS__)
#define FUNC_SELECT_INNER(r,f,a1,a2,a3,a4,m,...) m
#define FUNC FUNC_SELECT_OUTER
#include "testcrypt.h"
#undef FUNC
static void process_line(BinarySource *in, strbuf *out)
{
ptrlen id = get_word(in);
#define DISPATCH_COMMAND(cmd) \
if (ptrlen_eq_string(id, #cmd)) { \
handle_##cmd(in, out); \
return; \
}
DISPATCH_COMMAND(hello);
DISPATCH_COMMAND(free);
DISPATCH_COMMAND(newstring);
DISPATCH_COMMAND(getstring);
DISPATCH_COMMAND(mp_literal);
DISPATCH_COMMAND(mp_dump);
#define FUNC(rettype, function, ...) DISPATCH_COMMAND(function);
#include "testcrypt.h"
#undef FUNC
fatal_error("command '%.*s': unrecognised", PTRLEN_PRINTF(id));
}
static void free_all_values(void)
{
for (Value *val; (val = delpos234(values, 0)) != NULL ;)
free_value(val);
freetree234(values);
}
int main(int argc, char **argv)
{
FILE *fp = stdin;
if (argc > 1) {
fp = fopen(argv[1], "r");
if (!fp) {
fprintf(stderr, "%s: open: %s\n", argv[1], strerror(errno));
return 1;
}
}
values = newtree234(valuecmp);
atexit(free_all_values);
for (char *line; (line = chomp(fgetline(fp))) != NULL ;) {
BinarySource src[1];
BinarySource_BARE_INIT(src, line, strlen(line));
strbuf *sb = strbuf_new();
process_line(src, sb);
run_finalisers(sb);
size_t lines = 0;
for (size_t i = 0; i < sb->len; i++)
if (sb->s[i] == '\n')
lines++;
printf("%zu\n%s", lines, sb->s);
fflush(stdout);
strbuf_free(sb);
sfree(line);
}
if (fp != stdin)
fclose(fp);
return 0;
}

226
testcrypt.h Normal file
View File

@ -0,0 +1,226 @@
/*
* mpint.h functions.
*/
FUNC(val_mpint, mp_new, uint)
FUNC(void, mp_clear, val_mpint)
FUNC(val_mpint, mp_from_bytes_le, val_string_ptrlen)
FUNC(val_mpint, mp_from_bytes_be, val_string_ptrlen)
FUNC(val_mpint, mp_from_integer, uint)
FUNC(val_mpint, mp_from_decimal_pl, val_string_ptrlen)
FUNC(val_mpint, mp_from_decimal, val_string_asciz)
FUNC(val_mpint, mp_from_hex_pl, val_string_ptrlen)
FUNC(val_mpint, mp_from_hex, val_string_asciz)
FUNC(val_mpint, mp_copy, val_mpint)
FUNC(val_mpint, mp_power_2, uint)
FUNC(uint, mp_get_byte, val_mpint, uint)
FUNC(uint, mp_get_bit, val_mpint, uint)
FUNC(void, mp_set_bit, val_mpint, uint, uint)
FUNC(uint, mp_max_bytes, val_mpint)
FUNC(uint, mp_max_bits, val_mpint)
FUNC(uint, mp_get_nbits, val_mpint)
FUNC(val_string_asciz, mp_get_decimal, val_mpint)
FUNC(val_string_asciz, mp_get_hex, val_mpint)
FUNC(val_string_asciz, mp_get_hex_uppercase, val_mpint)
FUNC(uint, mp_cmp_hs, val_mpint, val_mpint)
FUNC(uint, mp_cmp_eq, val_mpint, val_mpint)
FUNC(uint, mp_hs_integer, val_mpint, uint)
FUNC(uint, mp_eq_integer, val_mpint, uint)
FUNC(void, mp_min_into, val_mpint, val_mpint, val_mpint)
FUNC(val_mpint, mp_min, val_mpint, val_mpint)
FUNC(void, mp_copy_into, val_mpint, val_mpint)
FUNC(void, mp_select_into, val_mpint, val_mpint, val_mpint, uint)
FUNC(void, mp_add_into, val_mpint, val_mpint, val_mpint)
FUNC(void, mp_sub_into, val_mpint, val_mpint, val_mpint)
FUNC(void, mp_mul_into, val_mpint, val_mpint, val_mpint)
FUNC(val_mpint, mp_add, val_mpint, val_mpint)
FUNC(val_mpint, mp_sub, val_mpint, val_mpint)
FUNC(val_mpint, mp_mul, val_mpint, val_mpint)
FUNC(void, mp_add_integer_into, val_mpint, val_mpint, uint)
FUNC(void, mp_sub_integer_into, val_mpint, val_mpint, uint)
FUNC(void, mp_mul_integer_into, val_mpint, val_mpint, uint)
FUNC(void, mp_cond_add_into, val_mpint, val_mpint, val_mpint, uint)
FUNC(void, mp_cond_sub_into, val_mpint, val_mpint, val_mpint, uint)
FUNC(void, mp_cond_swap, val_mpint, val_mpint, uint)
FUNC(void, mp_cond_clear, val_mpint, uint)
FUNC(void, mp_divmod_into, val_mpint, val_mpint, val_mpint, val_mpint)
FUNC(val_mpint, mp_div, val_mpint, val_mpint)
FUNC(val_mpint, mp_mod, val_mpint, val_mpint)
FUNC(void, mp_reduce_mod_2to, val_mpint, uint)
FUNC(val_mpint, mp_invert_mod_2to, val_mpint, uint)
FUNC(val_mpint, mp_invert, val_mpint, val_mpint)
FUNC(val_modsqrt, modsqrt_new, val_mpint, val_mpint)
/* The modsqrt functions' 'success' pointer becomes a second return value */
FUNC(val_mpint, mp_modsqrt, val_modsqrt, val_mpint, out_uint)
FUNC(val_monty, monty_new, val_mpint)
FUNC(val_mpint, monty_modulus, val_monty)
FUNC(val_mpint, monty_identity, val_monty)
FUNC(void, monty_import_into, val_monty, val_mpint, val_mpint)
FUNC(val_mpint, monty_import, val_monty, val_mpint)
FUNC(void, monty_export_into, val_monty, val_mpint, val_mpint)
FUNC(val_mpint, monty_export, val_monty, val_mpint)
FUNC(void, monty_mul_into, val_monty, val_mpint, val_mpint, val_mpint)
FUNC(val_mpint, monty_add, val_monty, val_mpint, val_mpint)
FUNC(val_mpint, monty_sub, val_monty, val_mpint, val_mpint)
FUNC(val_mpint, monty_mul, val_monty, val_mpint, val_mpint)
FUNC(val_mpint, monty_pow, val_monty, val_mpint, val_mpint)
FUNC(val_mpint, monty_invert, val_monty, val_mpint)
FUNC(val_mpint, monty_modsqrt, val_modsqrt, val_mpint, out_uint)
FUNC(val_mpint, mp_modpow, val_mpint, val_mpint, val_mpint)
FUNC(val_mpint, mp_modmul, val_mpint, val_mpint, val_mpint)
FUNC(val_mpint, mp_modadd, val_mpint, val_mpint, val_mpint)
FUNC(val_mpint, mp_modsub, val_mpint, val_mpint, val_mpint)
FUNC(val_mpint, mp_rshift_safe, val_mpint, uint)
FUNC(void, mp_lshift_fixed_into, val_mpint, val_mpint, uint)
FUNC(void, mp_rshift_fixed_into, val_mpint, val_mpint, uint)
FUNC(val_mpint, mp_rshift_fixed, val_mpint, uint)
FUNC(val_mpint, mp_random_bits, uint)
FUNC(val_mpint, mp_random_in_range, val_mpint, val_mpint)
/*
* ecc.h functions.
*/
FUNC(val_wcurve, ecc_weierstrass_curve, val_mpint, val_mpint, val_mpint, opt_val_mpint)
FUNC(val_wpoint, ecc_weierstrass_point_new_identity, val_wcurve)
FUNC(val_wpoint, ecc_weierstrass_point_new, val_wcurve, val_mpint, val_mpint)
FUNC(val_wpoint, ecc_weierstrass_point_new_from_x, val_wcurve, val_mpint, uint)
FUNC(val_wpoint, ecc_weierstrass_point_copy, val_wpoint)
FUNC(uint, ecc_weierstrass_point_valid, val_wpoint)
FUNC(val_wpoint, ecc_weierstrass_add_general, val_wpoint, val_wpoint)
FUNC(val_wpoint, ecc_weierstrass_add, val_wpoint, val_wpoint)
FUNC(val_wpoint, ecc_weierstrass_double, val_wpoint)
FUNC(val_wpoint, ecc_weierstrass_multiply, val_wpoint, val_mpint)
FUNC(uint, ecc_weierstrass_is_identity, val_wpoint)
/* The output pointers in get_affine all become extra output values */
FUNC(void, ecc_weierstrass_get_affine, val_wpoint, out_val_mpint, out_val_mpint)
FUNC(val_mcurve, ecc_montgomery_curve, val_mpint, val_mpint, val_mpint)
FUNC(val_mpoint, ecc_montgomery_point_new, val_mcurve, val_mpint)
FUNC(val_mpoint, ecc_montgomery_point_copy, val_mpoint)
FUNC(val_mpoint, ecc_montgomery_diff_add, val_mpoint, val_mpoint, val_mpoint)
FUNC(val_mpoint, ecc_montgomery_double, val_mpoint)
FUNC(val_mpoint, ecc_montgomery_multiply, val_mpoint, val_mpint)
FUNC(void, ecc_montgomery_get_affine, val_mpoint, out_val_mpint)
FUNC(val_ecurve, ecc_edwards_curve, val_mpint, val_mpint, val_mpint, opt_val_mpint)
FUNC(val_epoint, ecc_edwards_point_new, val_ecurve, val_mpint, val_mpint)
FUNC(val_epoint, ecc_edwards_point_new_from_y, val_ecurve, val_mpint, uint)
FUNC(val_epoint, ecc_edwards_point_copy, val_epoint)
FUNC(val_epoint, ecc_edwards_add, val_epoint, val_epoint)
FUNC(val_epoint, ecc_edwards_multiply, val_epoint, val_mpint)
FUNC(uint, ecc_edwards_eq, val_epoint, val_epoint)
FUNC(void, ecc_edwards_get_affine, val_epoint, out_val_mpint, out_val_mpint)
/*
* The ssh_hash abstraction. Note the 'consumed', indicating that
* ssh_hash_final puts its input ssh_hash beyond use.
*
* ssh_hash_update is an invention of testcrypt, handled in the real C
* API by the hash object also functioning as a BinarySink.
*/
FUNC(val_hash, ssh_hash_new, hashalg)
FUNC(val_hash, ssh_hash_copy, val_hash)
FUNC(val_string, ssh_hash_final, consumed_val_hash)
FUNC(void, ssh_hash_update, val_hash, val_string_ptrlen)
/*
* The ssh2_mac abstraction. Note the optional ssh2_cipher parameter
* to ssh2_mac_new. Also, again, I've invented an ssh2_mac_update so
* you can put data into the MAC.
*/
FUNC(val_mac, ssh2_mac_new, macalg, opt_val_ssh2_cipher)
FUNC(void, ssh2_mac_setkey, val_mac, val_string_ptrlen)
FUNC(void, ssh2_mac_start, val_mac)
FUNC(void, ssh2_mac_update, val_mac, val_string_ptrlen)
FUNC(val_string, ssh2_mac_genresult, val_mac)
/*
* The ssh_key abstraction. All the uses of BinarySink and
* BinarySource in parameters are replaced with ordinary strings for
* the testing API: new_priv_openssh just takes a string input, and
* all the functions that output key and signature blobs do it by
* returning a string.
*/
FUNC(val_key, ssh_key_new_pub, keyalg, val_string_ptrlen)
FUNC(val_key, ssh_key_new_priv, keyalg, val_string_ptrlen, val_string_ptrlen)
FUNC(val_key, ssh_key_new_priv_openssh, keyalg, val_string_binarysource)
FUNC(void, ssh_key_sign, val_key, val_string_ptrlen, uint, out_val_string_binarysink)
FUNC(boolean, ssh_key_verify, val_key, val_string_ptrlen, val_string_ptrlen)
FUNC(void, ssh_key_public_blob, val_key, out_val_string_binarysink)
FUNC(void, ssh_key_private_blob, val_key, out_val_string_binarysink)
FUNC(void, ssh_key_openssh_blob, val_key, out_val_string_binarysink)
FUNC(val_string_asciz, ssh_key_cache_str, val_key)
FUNC(uint, ssh_key_public_bits, keyalg, val_string_ptrlen)
/*
* The ssh1_cipher abstraction. The in-place encrypt and decrypt
* functions are wrapped to replace them with a pair that take one
* string and return a separate string.
*/
FUNC(val_ssh1_cipher, ssh1_cipher_new, ssh1_cipheralg)
FUNC(void, ssh1_cipher_sesskey, val_ssh1_cipher, val_string_ptrlen)
FUNC(val_string, ssh1_cipher_encrypt, val_ssh1_cipher, val_string_ptrlen)
FUNC(val_string, ssh1_cipher_decrypt, val_ssh1_cipher, val_string_ptrlen)
/*
* The ssh2_cipher abstraction, with similar modifications.
*/
FUNC(val_ssh2_cipher, ssh2_cipher_new, ssh2_cipheralg)
FUNC(void, ssh2_cipher_setiv, val_ssh2_cipher, val_string_ptrlen)
FUNC(void, ssh2_cipher_setkey, val_ssh2_cipher, val_string_ptrlen)
FUNC(val_string, ssh2_cipher_encrypt, val_ssh2_cipher, val_string_ptrlen)
FUNC(val_string, ssh2_cipher_decrypt, val_ssh2_cipher, val_string_ptrlen)
FUNC(val_string, ssh2_cipher_encrypt_length, val_ssh2_cipher, val_string_ptrlen, uint)
FUNC(val_string, ssh2_cipher_decrypt_length, val_ssh2_cipher, val_string_ptrlen, uint)
/*
* Integer Diffie-Hellman.
*/
FUNC(val_dh, dh_setup_group, dh_group)
FUNC(val_dh, dh_setup_gex, val_mpint, val_mpint)
FUNC(uint, dh_modulus_bit_size, val_dh)
FUNC(val_mpint, dh_create_e, val_dh, uint)
FUNC(boolean, dh_validate_f, val_dh, val_mpint)
FUNC(val_mpint, dh_find_K, val_dh, val_mpint)
/*
* Elliptic-curve Diffie-Hellman.
*/
FUNC(val_ecdh, ssh_ecdhkex_newkey, ecdh_alg)
FUNC(void, ssh_ecdhkex_getpublic, val_ecdh, out_val_string_binarysink)
FUNC(val_mpint, ssh_ecdhkex_getkey, val_ecdh, val_string_ptrlen)
/*
* RSA key exchange.
*/
FUNC(val_rsakex, ssh_rsakex_newkey, val_string_ptrlen)
FUNC(uint, ssh_rsakex_klen, val_rsakex)
FUNC(val_string, ssh_rsakex_encrypt, val_rsakex, hashalg, val_string_ptrlen)
FUNC(val_mpint, ssh_rsakex_decrypt, val_rsakex, hashalg, val_string_ptrlen)
/*
* Bare RSA keys as used in SSH-1. The construction API functions
* write into an existing RSAKey object, so I've invented an 'rsa_new'
* function to make one in the first place.
*/
FUNC(val_rsa, rsa_new)
FUNC(void, get_rsa_ssh1_pub, val_string_binarysource, val_rsa, rsaorder)
FUNC(void, get_rsa_ssh1_priv, val_string_binarysource, val_rsa)
FUNC(val_string, rsa_ssh1_encrypt, val_string_ptrlen, val_rsa)
FUNC(val_mpint, rsa_ssh1_decrypt, val_mpint, val_rsa)
FUNC(val_string, rsa_ssh1_decrypt_pkcs1, val_mpint, val_rsa)
FUNC(val_string_asciz, rsastr_fmt, val_rsa)
FUNC(val_string_asciz, rsa_ssh1_fingerprint, val_rsa)
FUNC(void, rsa_ssh1_public_blob, out_val_string_binarysink, val_rsa, rsaorder)
FUNC(int, rsa_ssh1_public_blob_len, val_string_ptrlen)
/*
* Miscellaneous.
*/
FUNC(val_wpoint, ecdsa_public, val_mpint, keyalg)
FUNC(val_epoint, eddsa_public, val_mpint, keyalg)
/*
* These functions aren't part of PuTTY's own API, but are additions
* by testcrypt itself for administrative purposes.
*/
FUNC(void, random_queue, val_string_ptrlen)
FUNC(uint, random_queue_len)
FUNC(void, random_clear)