Rewrite the testcrypt.c macro system.
Yesterday's commit 52ee636b092c199 which further extended the huge
pile of arity-specific annoying wrapper macros pushed me over the edge
and inspired me to give some harder thought to finding a way to handle
all arities at once. And this time I found one!
The new technique changes the syntax of the function specifications in
testcrypt.h. In particular, they now have to specify a _name_ for each
parameter as well as a type, because the macros generating the C
marshalling wrappers will need a structure field for each parameter
and cpp isn't flexible enough to generate names for those fields
automatically. Rather than tediously name them arg1, arg2 etc, I've
reused the names of the parameters from the prototypes or definitions
of the underlying real functions (via a one-off auto-extraction
process starting from the output of 'clang -Xclang -dump-ast' plus
some manual polishing), which means testcrypt.h is now a bit more
self-documenting.
The testcrypt.py end of the mechanism is rewritten to eat the new
format. Since it's got more complicated syntax and nested parens and
things, I've written something a bit like a separated lexer/parser
system in place of the previous crude regex matcher, which should
enforce that the whole header file really does conform to the
restricted syntax it has to fit into.
The new system uses a lot less code in testcrypt.c, but I've made up
for that by also writing a long comment explaining how it works, which
was another thing the previous system lacked! Similarly, the new
testcrypt.h has some long-overdue instructions at the top.
2021-11-21 10:27:30 +00:00
|
|
|
/*
|
|
|
|
* List of functions exported by the 'testcrypt' system to provide a
|
|
|
|
* Python API for running unit tests and auxiliary programs.
|
|
|
|
*
|
|
|
|
* Each function definition in this file has the form
|
|
|
|
*
|
2021-11-28 09:39:49 +00:00
|
|
|
* FUNC(return-type, function-name, ...)
|
Rewrite the testcrypt.c macro system.
Yesterday's commit 52ee636b092c199 which further extended the huge
pile of arity-specific annoying wrapper macros pushed me over the edge
and inspired me to give some harder thought to finding a way to handle
all arities at once. And this time I found one!
The new technique changes the syntax of the function specifications in
testcrypt.h. In particular, they now have to specify a _name_ for each
parameter as well as a type, because the macros generating the C
marshalling wrappers will need a structure field for each parameter
and cpp isn't flexible enough to generate names for those fields
automatically. Rather than tediously name them arg1, arg2 etc, I've
reused the names of the parameters from the prototypes or definitions
of the underlying real functions (via a one-off auto-extraction
process starting from the output of 'clang -Xclang -dump-ast' plus
some manual polishing), which means testcrypt.h is now a bit more
self-documenting.
The testcrypt.py end of the mechanism is rewritten to eat the new
format. Since it's got more complicated syntax and nested parens and
things, I've written something a bit like a separated lexer/parser
system in place of the previous crude regex matcher, which should
enforce that the whole header file really does conform to the
restricted syntax it has to fit into.
The new system uses a lot less code in testcrypt.c, but I've made up
for that by also writing a long comment explaining how it works, which
was another thing the previous system lacked! Similarly, the new
testcrypt.h has some long-overdue instructions at the top.
2021-11-21 10:27:30 +00:00
|
|
|
*
|
2021-11-28 09:39:49 +00:00
|
|
|
* where '...' in turn a variadic list of argument specifications of
|
|
|
|
* the form
|
Rewrite the testcrypt.c macro system.
Yesterday's commit 52ee636b092c199 which further extended the huge
pile of arity-specific annoying wrapper macros pushed me over the edge
and inspired me to give some harder thought to finding a way to handle
all arities at once. And this time I found one!
The new technique changes the syntax of the function specifications in
testcrypt.h. In particular, they now have to specify a _name_ for each
parameter as well as a type, because the macros generating the C
marshalling wrappers will need a structure field for each parameter
and cpp isn't flexible enough to generate names for those fields
automatically. Rather than tediously name them arg1, arg2 etc, I've
reused the names of the parameters from the prototypes or definitions
of the underlying real functions (via a one-off auto-extraction
process starting from the output of 'clang -Xclang -dump-ast' plus
some manual polishing), which means testcrypt.h is now a bit more
self-documenting.
The testcrypt.py end of the mechanism is rewritten to eat the new
format. Since it's got more complicated syntax and nested parens and
things, I've written something a bit like a separated lexer/parser
system in place of the previous crude regex matcher, which should
enforce that the whole header file really does conform to the
restricted syntax it has to fit into.
The new system uses a lot less code in testcrypt.c, but I've made up
for that by also writing a long comment explaining how it works, which
was another thing the previous system lacked! Similarly, the new
testcrypt.h has some long-overdue instructions at the top.
2021-11-21 10:27:30 +00:00
|
|
|
*
|
|
|
|
* ARG(argument-type, argument-name)
|
|
|
|
*
|
2021-11-28 09:39:49 +00:00
|
|
|
* An empty argument list must be marked by including a
|
|
|
|
* pseudo-argument VOID:
|
|
|
|
*
|
|
|
|
* FUNC(return-type, function-name, VOID)
|
|
|
|
*
|
Rewrite the testcrypt.c macro system.
Yesterday's commit 52ee636b092c199 which further extended the huge
pile of arity-specific annoying wrapper macros pushed me over the edge
and inspired me to give some harder thought to finding a way to handle
all arities at once. And this time I found one!
The new technique changes the syntax of the function specifications in
testcrypt.h. In particular, they now have to specify a _name_ for each
parameter as well as a type, because the macros generating the C
marshalling wrappers will need a structure field for each parameter
and cpp isn't flexible enough to generate names for those fields
automatically. Rather than tediously name them arg1, arg2 etc, I've
reused the names of the parameters from the prototypes or definitions
of the underlying real functions (via a one-off auto-extraction
process starting from the output of 'clang -Xclang -dump-ast' plus
some manual polishing), which means testcrypt.h is now a bit more
self-documenting.
The testcrypt.py end of the mechanism is rewritten to eat the new
format. Since it's got more complicated syntax and nested parens and
things, I've written something a bit like a separated lexer/parser
system in place of the previous crude regex matcher, which should
enforce that the whole header file really does conform to the
restricted syntax it has to fit into.
The new system uses a lot less code in testcrypt.c, but I've made up
for that by also writing a long comment explaining how it works, which
was another thing the previous system lacked! Similarly, the new
testcrypt.h has some long-overdue instructions at the top.
2021-11-21 10:27:30 +00:00
|
|
|
* Type names are always single identifiers, and they have some
|
|
|
|
* standard prefixes:
|
|
|
|
*
|
|
|
|
* 'val_' means that the type refers to something dynamically
|
|
|
|
* allocated, so that it has a persistent identity, needs to be freed
|
|
|
|
* when finished with (though this is done automatically by the
|
|
|
|
* testcrypt.py system via Python's reference counting), and may also
|
|
|
|
* be mutable. The argument type in C will be a pointer; in Python the
|
|
|
|
* corresponding argument will be an instance of a 'Value' object
|
|
|
|
* defined in testcrypt.py.
|
|
|
|
*
|
|
|
|
* 'opt_val_' is a modification of 'val_' to indicate that the pointer
|
|
|
|
* may be NULL. In Python this is translated by accepting (or
|
|
|
|
* returning) None as an alternative to a Value.
|
|
|
|
*
|
|
|
|
* 'out_' on an argument type indicates an additional output
|
|
|
|
* parameter. The argument type in C has an extra layer of
|
|
|
|
* indirection, e.g. an 'out_val_mpint' is an 'mpint **' instead of an
|
|
|
|
* 'mpint *', identifying a pointer variable where the returned
|
|
|
|
* pointer value will be written. In the Python API, these arguments
|
|
|
|
* do not appear in the argument list of the Python function; instead
|
|
|
|
* they cause the return value to become a tuple, with additional
|
|
|
|
* types appended. For example, a declaration like
|
|
|
|
*
|
2021-11-28 09:39:49 +00:00
|
|
|
* FUNC(val_foo, example, ARG(out_val_bar, bar), ARG(val_baz, baz))
|
Rewrite the testcrypt.c macro system.
Yesterday's commit 52ee636b092c199 which further extended the huge
pile of arity-specific annoying wrapper macros pushed me over the edge
and inspired me to give some harder thought to finding a way to handle
all arities at once. And this time I found one!
The new technique changes the syntax of the function specifications in
testcrypt.h. In particular, they now have to specify a _name_ for each
parameter as well as a type, because the macros generating the C
marshalling wrappers will need a structure field for each parameter
and cpp isn't flexible enough to generate names for those fields
automatically. Rather than tediously name them arg1, arg2 etc, I've
reused the names of the parameters from the prototypes or definitions
of the underlying real functions (via a one-off auto-extraction
process starting from the output of 'clang -Xclang -dump-ast' plus
some manual polishing), which means testcrypt.h is now a bit more
self-documenting.
The testcrypt.py end of the mechanism is rewritten to eat the new
format. Since it's got more complicated syntax and nested parens and
things, I've written something a bit like a separated lexer/parser
system in place of the previous crude regex matcher, which should
enforce that the whole header file really does conform to the
restricted syntax it has to fit into.
The new system uses a lot less code in testcrypt.c, but I've made up
for that by also writing a long comment explaining how it works, which
was another thing the previous system lacked! Similarly, the new
testcrypt.h has some long-overdue instructions at the top.
2021-11-21 10:27:30 +00:00
|
|
|
*
|
|
|
|
* would identify a function in C with the following prototype, which
|
|
|
|
* returns a 'foo *' directly and a 'bar *' by writing it through the
|
|
|
|
* provided 'bar **' pointer argument:
|
|
|
|
*
|
|
|
|
* foo *example(bar **extra_output, baz *input);
|
|
|
|
*
|
|
|
|
* and in Python this would become a function taking one argument of
|
|
|
|
* type 'baz' and returning a tuple of the form (foo, bar).
|
|
|
|
*
|
|
|
|
* 'out_' and 'opt_' can go together, if a function returns a second
|
|
|
|
* output value but it may in some cases be NULL.
|
|
|
|
*
|
|
|
|
* 'consumed_' on an argument type indicates that the C function
|
|
|
|
* receiving that argument frees it as a side effect.
|
|
|
|
*
|
|
|
|
* Any argument type which does not start 'val_' is plain old data
|
|
|
|
* with no dynamic allocation requirements. Ordinary C integers are
|
|
|
|
* sometimes handled this way (e.g. 'uint'). Other plain-data types
|
|
|
|
* are represented in Python as a string that must be one of a
|
|
|
|
* recognised set of keywords; in C these variously translate into
|
|
|
|
* enumeration types (e.g. argon2flavour, rsaorder) or pointers to
|
|
|
|
* const vtables of one kind or another (e.g. keyalg, hashalg,
|
|
|
|
* primegenpolicy).
|
2021-11-21 13:03:34 +00:00
|
|
|
*
|
|
|
|
* If a function definition begins with FUNC_WRAPPED rather than FUNC,
|
|
|
|
* it means that the underlying C function has a suffix "_wrapper",
|
|
|
|
* e.g. ssh_cipher_setiv_wrapper(). Those wrappers are defined in
|
|
|
|
* testcrypt.c itself, and change the API or semantics in a way that
|
|
|
|
* makes the function more Python-friendly.
|
Rewrite the testcrypt.c macro system.
Yesterday's commit 52ee636b092c199 which further extended the huge
pile of arity-specific annoying wrapper macros pushed me over the edge
and inspired me to give some harder thought to finding a way to handle
all arities at once. And this time I found one!
The new technique changes the syntax of the function specifications in
testcrypt.h. In particular, they now have to specify a _name_ for each
parameter as well as a type, because the macros generating the C
marshalling wrappers will need a structure field for each parameter
and cpp isn't flexible enough to generate names for those fields
automatically. Rather than tediously name them arg1, arg2 etc, I've
reused the names of the parameters from the prototypes or definitions
of the underlying real functions (via a one-off auto-extraction
process starting from the output of 'clang -Xclang -dump-ast' plus
some manual polishing), which means testcrypt.h is now a bit more
self-documenting.
The testcrypt.py end of the mechanism is rewritten to eat the new
format. Since it's got more complicated syntax and nested parens and
things, I've written something a bit like a separated lexer/parser
system in place of the previous crude regex matcher, which should
enforce that the whole header file really does conform to the
restricted syntax it has to fit into.
The new system uses a lot less code in testcrypt.c, but I've made up
for that by also writing a long comment explaining how it works, which
was another thing the previous system lacked! Similarly, the new
testcrypt.h has some long-overdue instructions at the top.
2021-11-21 10:27:30 +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.
2019-01-01 19:08:37 +00:00
|
|
|
/*
|
|
|
|
* mpint.h functions.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_mpint, mp_new, ARG(uint, maxbits))
|
|
|
|
FUNC(void, mp_clear, ARG(val_mpint, x))
|
|
|
|
FUNC(val_mpint, mp_from_bytes_le, ARG(val_string_ptrlen, bytes))
|
|
|
|
FUNC(val_mpint, mp_from_bytes_be, ARG(val_string_ptrlen, bytes))
|
|
|
|
FUNC(val_mpint, mp_from_integer, ARG(uint, n))
|
|
|
|
FUNC(val_mpint, mp_from_decimal_pl, ARG(val_string_ptrlen, decimal))
|
|
|
|
FUNC(val_mpint, mp_from_decimal, ARG(val_string_asciz, decimal))
|
|
|
|
FUNC(val_mpint, mp_from_hex_pl, ARG(val_string_ptrlen, hex))
|
|
|
|
FUNC(val_mpint, mp_from_hex, ARG(val_string_asciz, hex))
|
|
|
|
FUNC(val_mpint, mp_copy, ARG(val_mpint, x))
|
|
|
|
FUNC(val_mpint, mp_power_2, ARG(uint, power))
|
|
|
|
FUNC(uint, mp_get_byte, ARG(val_mpint, x), ARG(uint, byte))
|
|
|
|
FUNC(uint, mp_get_bit, ARG(val_mpint, x), ARG(uint, bit))
|
|
|
|
FUNC(void, mp_set_bit, ARG(val_mpint, x), ARG(uint, bit), ARG(uint, val))
|
|
|
|
FUNC(uint, mp_max_bytes, ARG(val_mpint, x))
|
|
|
|
FUNC(uint, mp_max_bits, ARG(val_mpint, x))
|
|
|
|
FUNC(uint, mp_get_nbits, ARG(val_mpint, x))
|
|
|
|
FUNC(val_string_asciz, mp_get_decimal, ARG(val_mpint, x))
|
|
|
|
FUNC(val_string_asciz, mp_get_hex, ARG(val_mpint, x))
|
|
|
|
FUNC(val_string_asciz, mp_get_hex_uppercase, ARG(val_mpint, x))
|
|
|
|
FUNC(uint, mp_cmp_hs, ARG(val_mpint, a), ARG(val_mpint, b))
|
|
|
|
FUNC(uint, mp_cmp_eq, ARG(val_mpint, a), ARG(val_mpint, b))
|
|
|
|
FUNC(uint, mp_hs_integer, ARG(val_mpint, x), ARG(uint, n))
|
|
|
|
FUNC(uint, mp_eq_integer, ARG(val_mpint, x), ARG(uint, n))
|
|
|
|
FUNC(void, mp_min_into, ARG(val_mpint, dest), ARG(val_mpint, x),
|
|
|
|
ARG(val_mpint, y))
|
|
|
|
FUNC(void, mp_max_into, ARG(val_mpint, dest), ARG(val_mpint, x),
|
|
|
|
ARG(val_mpint, y))
|
|
|
|
FUNC(val_mpint, mp_min, ARG(val_mpint, x), ARG(val_mpint, y))
|
|
|
|
FUNC(val_mpint, mp_max, ARG(val_mpint, x), ARG(val_mpint, y))
|
|
|
|
FUNC(void, mp_copy_into, ARG(val_mpint, dest), ARG(val_mpint, src))
|
|
|
|
FUNC(void, mp_select_into, ARG(val_mpint, dest), ARG(val_mpint, src0),
|
|
|
|
ARG(val_mpint, src1), ARG(uint, choose_src1))
|
|
|
|
FUNC(void, mp_add_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b))
|
|
|
|
FUNC(void, mp_sub_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b))
|
|
|
|
FUNC(void, mp_mul_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b))
|
|
|
|
FUNC(val_mpint, mp_add, ARG(val_mpint, x), ARG(val_mpint, y))
|
|
|
|
FUNC(val_mpint, mp_sub, ARG(val_mpint, x), ARG(val_mpint, y))
|
|
|
|
FUNC(val_mpint, mp_mul, ARG(val_mpint, x), ARG(val_mpint, y))
|
|
|
|
FUNC(void, mp_and_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b))
|
|
|
|
FUNC(void, mp_or_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b))
|
|
|
|
FUNC(void, mp_xor_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b))
|
|
|
|
FUNC(void, mp_bic_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b))
|
|
|
|
FUNC(void, mp_copy_integer_into, ARG(val_mpint, dest), ARG(uint, n))
|
|
|
|
FUNC(void, mp_add_integer_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(uint, n))
|
|
|
|
FUNC(void, mp_sub_integer_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(uint, n))
|
|
|
|
FUNC(void, mp_mul_integer_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(uint, n))
|
|
|
|
FUNC(void, mp_cond_add_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b), ARG(uint, yes))
|
|
|
|
FUNC(void, mp_cond_sub_into, ARG(val_mpint, dest), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b), ARG(uint, yes))
|
|
|
|
FUNC(void, mp_cond_swap, ARG(val_mpint, x0), ARG(val_mpint, x1),
|
|
|
|
ARG(uint, swap))
|
|
|
|
FUNC(void, mp_cond_clear, ARG(val_mpint, x), ARG(uint, clear))
|
|
|
|
FUNC(void, mp_divmod_into, ARG(val_mpint, n), ARG(val_mpint, d),
|
|
|
|
ARG(opt_val_mpint, q), ARG(opt_val_mpint, r))
|
|
|
|
FUNC(val_mpint, mp_div, ARG(val_mpint, n), ARG(val_mpint, d))
|
|
|
|
FUNC(val_mpint, mp_mod, ARG(val_mpint, x), ARG(val_mpint, modulus))
|
|
|
|
FUNC(val_mpint, mp_nthroot, ARG(val_mpint, y), ARG(uint, n),
|
|
|
|
ARG(opt_val_mpint, remainder))
|
|
|
|
FUNC(void, mp_reduce_mod_2to, ARG(val_mpint, x), ARG(uint, p))
|
|
|
|
FUNC(val_mpint, mp_invert_mod_2to, ARG(val_mpint, x), ARG(uint, p))
|
|
|
|
FUNC(val_mpint, mp_invert, ARG(val_mpint, x), ARG(val_mpint, modulus))
|
|
|
|
FUNC(void, mp_gcd_into, ARG(val_mpint, a), ARG(val_mpint, b),
|
|
|
|
ARG(opt_val_mpint, gcd_out), ARG(opt_val_mpint, A_out),
|
|
|
|
ARG(opt_val_mpint, B_out))
|
|
|
|
FUNC(val_mpint, mp_gcd, ARG(val_mpint, a), ARG(val_mpint, b))
|
|
|
|
FUNC(uint, mp_coprime, ARG(val_mpint, a), ARG(val_mpint, b))
|
|
|
|
FUNC(val_modsqrt, modsqrt_new, ARG(val_mpint, p),
|
|
|
|
ARG(val_mpint, any_nonsquare_mod_p))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
/* The modsqrt functions' 'success' pointer becomes a second return value */
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_mpint, mp_modsqrt, ARG(val_modsqrt, sc), ARG(val_mpint, x),
|
|
|
|
ARG(out_uint, success))
|
|
|
|
FUNC(val_monty, monty_new, ARG(val_mpint, modulus))
|
|
|
|
FUNC_WRAPPED(val_mpint, monty_modulus, ARG(val_monty, mc))
|
|
|
|
FUNC_WRAPPED(val_mpint, monty_identity, ARG(val_monty, mc))
|
|
|
|
FUNC(void, monty_import_into, ARG(val_monty, mc), ARG(val_mpint, dest),
|
|
|
|
ARG(val_mpint, x))
|
|
|
|
FUNC(val_mpint, monty_import, ARG(val_monty, mc), ARG(val_mpint, x))
|
|
|
|
FUNC(void, monty_export_into, ARG(val_monty, mc), ARG(val_mpint, dest),
|
|
|
|
ARG(val_mpint, x))
|
|
|
|
FUNC(val_mpint, monty_export, ARG(val_monty, mc), ARG(val_mpint, x))
|
|
|
|
FUNC(void, monty_mul_into, ARG(val_monty, mc), ARG(val_mpint, dest),
|
|
|
|
ARG(val_mpint, x), ARG(val_mpint, y))
|
|
|
|
FUNC(val_mpint, monty_add, ARG(val_monty, mc), ARG(val_mpint, x),
|
|
|
|
ARG(val_mpint, y))
|
|
|
|
FUNC(val_mpint, monty_sub, ARG(val_monty, mc), ARG(val_mpint, x),
|
|
|
|
ARG(val_mpint, y))
|
|
|
|
FUNC(val_mpint, monty_mul, ARG(val_monty, mc), ARG(val_mpint, x),
|
|
|
|
ARG(val_mpint, y))
|
|
|
|
FUNC(val_mpint, monty_pow, ARG(val_monty, mc), ARG(val_mpint, base),
|
|
|
|
ARG(val_mpint, exponent))
|
|
|
|
FUNC(val_mpint, monty_invert, ARG(val_monty, mc), ARG(val_mpint, x))
|
|
|
|
FUNC(val_mpint, monty_modsqrt, ARG(val_modsqrt, sc), ARG(val_mpint, mx),
|
|
|
|
ARG(out_uint, success))
|
|
|
|
FUNC(val_mpint, mp_modpow, ARG(val_mpint, base), ARG(val_mpint, exponent),
|
|
|
|
ARG(val_mpint, modulus))
|
|
|
|
FUNC(val_mpint, mp_modmul, ARG(val_mpint, x), ARG(val_mpint, y),
|
|
|
|
ARG(val_mpint, modulus))
|
|
|
|
FUNC(val_mpint, mp_modadd, ARG(val_mpint, x), ARG(val_mpint, y),
|
|
|
|
ARG(val_mpint, modulus))
|
|
|
|
FUNC(val_mpint, mp_modsub, ARG(val_mpint, x), ARG(val_mpint, y),
|
|
|
|
ARG(val_mpint, modulus))
|
|
|
|
FUNC(void, mp_lshift_safe_into, ARG(val_mpint, dest), ARG(val_mpint, x),
|
|
|
|
ARG(uint, shift))
|
|
|
|
FUNC(void, mp_rshift_safe_into, ARG(val_mpint, dest), ARG(val_mpint, x),
|
|
|
|
ARG(uint, shift))
|
|
|
|
FUNC(val_mpint, mp_rshift_safe, ARG(val_mpint, x), ARG(uint, shift))
|
|
|
|
FUNC(void, mp_lshift_fixed_into, ARG(val_mpint, dest), ARG(val_mpint, x),
|
|
|
|
ARG(uint, shift))
|
|
|
|
FUNC(void, mp_rshift_fixed_into, ARG(val_mpint, dest), ARG(val_mpint, x),
|
|
|
|
ARG(uint, shift))
|
|
|
|
FUNC(val_mpint, mp_rshift_fixed, ARG(val_mpint, x), ARG(uint, shift))
|
|
|
|
FUNC(val_mpint, mp_random_bits, ARG(uint, bits))
|
|
|
|
FUNC(val_mpint, mp_random_in_range, ARG(val_mpint, lo), ARG(val_mpint, hi))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ecc.h functions.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_wcurve, ecc_weierstrass_curve, ARG(val_mpint, p), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b), ARG(opt_val_mpint, nonsquare_mod_p))
|
|
|
|
FUNC(val_wpoint, ecc_weierstrass_point_new_identity, ARG(val_wcurve, curve))
|
|
|
|
FUNC(val_wpoint, ecc_weierstrass_point_new, ARG(val_wcurve, curve),
|
|
|
|
ARG(val_mpint, x), ARG(val_mpint, y))
|
|
|
|
FUNC(val_wpoint, ecc_weierstrass_point_new_from_x, ARG(val_wcurve, curve),
|
|
|
|
ARG(val_mpint, x), ARG(uint, desired_y_parity))
|
|
|
|
FUNC(val_wpoint, ecc_weierstrass_point_copy, ARG(val_wpoint, orig))
|
|
|
|
FUNC(uint, ecc_weierstrass_point_valid, ARG(val_wpoint, P))
|
|
|
|
FUNC(val_wpoint, ecc_weierstrass_add_general, ARG(val_wpoint, P),
|
|
|
|
ARG(val_wpoint, Q))
|
|
|
|
FUNC(val_wpoint, ecc_weierstrass_add, ARG(val_wpoint, P), ARG(val_wpoint, Q))
|
|
|
|
FUNC(val_wpoint, ecc_weierstrass_double, ARG(val_wpoint, P))
|
|
|
|
FUNC(val_wpoint, ecc_weierstrass_multiply, ARG(val_wpoint, B),
|
|
|
|
ARG(val_mpint, n))
|
|
|
|
FUNC(uint, ecc_weierstrass_is_identity, ARG(val_wpoint, P))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
/* The output pointers in get_affine all become extra output values */
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(void, ecc_weierstrass_get_affine, ARG(val_wpoint, P),
|
|
|
|
ARG(out_val_mpint, x), ARG(out_val_mpint, y))
|
|
|
|
FUNC(val_mcurve, ecc_montgomery_curve, ARG(val_mpint, p), ARG(val_mpint, a),
|
|
|
|
ARG(val_mpint, b))
|
|
|
|
FUNC(val_mpoint, ecc_montgomery_point_new, ARG(val_mcurve, curve),
|
|
|
|
ARG(val_mpint, x))
|
|
|
|
FUNC(val_mpoint, ecc_montgomery_point_copy, ARG(val_mpoint, orig))
|
|
|
|
FUNC(val_mpoint, ecc_montgomery_diff_add, ARG(val_mpoint, P),
|
|
|
|
ARG(val_mpoint, Q), ARG(val_mpoint, PminusQ))
|
|
|
|
FUNC(val_mpoint, ecc_montgomery_double, ARG(val_mpoint, P))
|
|
|
|
FUNC(val_mpoint, ecc_montgomery_multiply, ARG(val_mpoint, B), ARG(val_mpint, n))
|
|
|
|
FUNC(void, ecc_montgomery_get_affine, ARG(val_mpoint, P), ARG(out_val_mpint, x))
|
|
|
|
FUNC(boolean, ecc_montgomery_is_identity, ARG(val_mpoint, P))
|
|
|
|
FUNC(val_ecurve, ecc_edwards_curve, ARG(val_mpint, p), ARG(val_mpint, d),
|
|
|
|
ARG(val_mpint, a), ARG(opt_val_mpint, nonsquare_mod_p))
|
|
|
|
FUNC(val_epoint, ecc_edwards_point_new, ARG(val_ecurve, curve),
|
|
|
|
ARG(val_mpint, x), ARG(val_mpint, y))
|
|
|
|
FUNC(val_epoint, ecc_edwards_point_new_from_y, ARG(val_ecurve, curve),
|
|
|
|
ARG(val_mpint, y), ARG(uint, desired_x_parity))
|
|
|
|
FUNC(val_epoint, ecc_edwards_point_copy, ARG(val_epoint, orig))
|
|
|
|
FUNC(val_epoint, ecc_edwards_add, ARG(val_epoint, P), ARG(val_epoint, Q))
|
|
|
|
FUNC(val_epoint, ecc_edwards_multiply, ARG(val_epoint, B), ARG(val_mpint, n))
|
|
|
|
FUNC(uint, ecc_edwards_eq, ARG(val_epoint, P), ARG(val_epoint, Q))
|
|
|
|
FUNC(void, ecc_edwards_get_affine, ARG(val_epoint, P), ARG(out_val_mpint, x),
|
|
|
|
ARG(out_val_mpint, y))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(opt_val_hash, ssh_hash_new, ARG(hashalg, alg))
|
|
|
|
FUNC(void, ssh_hash_reset, ARG(val_hash, h))
|
|
|
|
FUNC(val_hash, ssh_hash_copy, ARG(val_hash, orig))
|
|
|
|
FUNC_WRAPPED(val_string, ssh_hash_digest, ARG(val_hash, h))
|
|
|
|
FUNC_WRAPPED(val_string, ssh_hash_final, ARG(consumed_val_hash, h))
|
|
|
|
FUNC(void, ssh_hash_update, ARG(val_hash, h), ARG(val_string_ptrlen, data))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(opt_val_hash, blake2b_new_general, ARG(uint, hashlen))
|
2021-02-13 14:47:26 +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.
2019-01-01 19:08:37 +00:00
|
|
|
/*
|
Merge the ssh1_cipher type into ssh2_cipher.
The aim of this reorganisation is to make it easier to test all the
ciphers in PuTTY in a uniform way. It was inconvenient that there were
two separate vtable systems for the ciphers used in SSH-1 and SSH-2
with different functionality.
Now there's only one type, called ssh_cipher. But really it's the old
ssh2_cipher, just renamed: I haven't made any changes to the API on
the SSH-2 side. Instead, I've removed ssh1_cipher completely, and
adapted the SSH-1 BPP to use the SSH-2 style API.
(The relevant differences are that ssh1_cipher encapsulated both the
sending and receiving directions in one object - so now ssh1bpp has to
make a separate cipher instance per direction - and that ssh1_cipher
automatically initialised the IV to all zeroes, which ssh1bpp now has
to do by hand.)
The previous ssh1_cipher vtable for single-DES has been removed
completely, because when converted into the new API it became
identical to the SSH-2 single-DES vtable; so now there's just one
vtable for DES-CBC which works in both protocols. The other two SSH-1
ciphers each had to stay separate, because 3DES is completely
different between SSH-1 and SSH-2 (three layers of CBC structure
versus one), and Blowfish varies in endianness and key length between
the two.
(Actually, while I'm here, I've only just noticed that the SSH-1
Blowfish cipher mis-describes itself in log messages as Blowfish-128.
In fact it passes the whole of the input key buffer, which has length
SSH1_SESSION_KEY_LENGTH == 32 bytes == 256 bits. So it's actually
Blowfish-256, and has been all along!)
2019-01-17 18:06:08 +00:00
|
|
|
* The ssh2_mac abstraction. Note the optional ssh_cipher parameter
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
* to ssh2_mac_new. Also, again, I've invented an ssh2_mac_update so
|
|
|
|
* you can put data into the MAC.
|
|
|
|
*/
|
2023-08-22 16:59:19 +00:00
|
|
|
FUNC(opt_val_mac, ssh2_mac_new, ARG(macalg, alg), ARG(opt_val_cipher, cipher))
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(void, ssh2_mac_setkey, ARG(val_mac, m), ARG(val_string_ptrlen, key))
|
|
|
|
FUNC(void, ssh2_mac_start, ARG(val_mac, m))
|
|
|
|
FUNC(void, ssh2_mac_update, ARG(val_mac, m), ARG(val_string_ptrlen, data))
|
2022-08-16 17:27:06 +00:00
|
|
|
FUNC(void, ssh2_mac_next_message, ARG(val_mac, m))
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC_WRAPPED(val_string, ssh2_mac_genresult, ARG(val_mac, m))
|
|
|
|
FUNC(val_string_asciz_const, ssh2_mac_text_name, ARG(val_mac, m))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
Implement AES-GCM using the @openssh.com protocol IDs.
I only recently found out that OpenSSH defined their own protocol IDs
for AES-GCM, defined to work the same as the standard ones except that
they fixed the semantics for how you select the linked cipher+MAC pair
during key exchange.
(RFC 5647 defines protocol ids for AES-GCM in both the cipher and MAC
namespaces, and requires that you MUST select both or neither - but
this contradicts the selection policy set out in the base SSH RFCs,
and there's no discussion of how you resolve a conflict between them!
OpenSSH's answer is to do it the same way ChaCha20-Poly1305 works,
because that will ensure the two suites don't fight.)
People do occasionally ask us for this linked cipher/MAC pair, and now
I know it's actually feasible, I've implemented it, including a pair
of vector implementations for x86 and Arm using their respective
architecture extensions for multiplying polynomials over GF(2).
Unlike ChaCha20-Poly1305, I've kept the cipher and MAC implementations
in separate objects, with an arm's-length link between them that the
MAC uses when it needs to encrypt single cipher blocks to use as the
inputs to the MAC algorithm. That enables the cipher and the MAC to be
independently selected from their hardware-accelerated versions, just
in case someone runs on a system that has polynomial multiplication
instructions but not AES acceleration, or vice versa.
There's a fourth implementation of the GCM MAC, which is a pure
software implementation of the same algorithm used in the vectorised
versions. It's too slow to use live, but I've kept it in the code for
future testing needs, and because it's a convenient place to dump my
design comments.
The vectorised implementations are fairly crude as far as optimisation
goes. I'm sure serious x86 _or_ Arm optimisation engineers would look
at them and laugh. But GCM is a fast MAC compared to HMAC-SHA-256
(indeed compared to HMAC-anything-at-all), so it should at least be
good enough to use. And we've got a working version with some tests
now, so if someone else wants to improve them, they can.
2022-08-16 17:36:58 +00:00
|
|
|
FUNC(void, aesgcm_set_prefix_lengths,
|
|
|
|
ARG(val_mac, m), ARG(uint, skip), ARG(uint, aad))
|
|
|
|
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_key, ssh_key_new_pub, ARG(keyalg, alg), ARG(val_string_ptrlen, pub))
|
|
|
|
FUNC(opt_val_key, ssh_key_new_priv, ARG(keyalg, alg),
|
|
|
|
ARG(val_string_ptrlen, pub), ARG(val_string_ptrlen, priv))
|
|
|
|
FUNC(opt_val_key, ssh_key_new_priv_openssh, ARG(keyalg, alg),
|
|
|
|
ARG(val_string_binarysource, src))
|
|
|
|
FUNC(opt_val_string_asciz, ssh_key_invalid, ARG(val_key, key), ARG(uint, flags))
|
|
|
|
FUNC(void, ssh_key_sign, ARG(val_key, key), ARG(val_string_ptrlen, data),
|
|
|
|
ARG(uint, flags), ARG(out_val_string_binarysink, sig))
|
|
|
|
FUNC(boolean, ssh_key_verify, ARG(val_key, key), ARG(val_string_ptrlen, sig),
|
|
|
|
ARG(val_string_ptrlen, data))
|
|
|
|
FUNC(void, ssh_key_public_blob, ARG(val_key, key),
|
|
|
|
ARG(out_val_string_binarysink, blob))
|
|
|
|
FUNC(void, ssh_key_private_blob, ARG(val_key, key),
|
|
|
|
ARG(out_val_string_binarysink, blob))
|
|
|
|
FUNC(void, ssh_key_openssh_blob, ARG(val_key, key),
|
|
|
|
ARG(out_val_string_binarysink, blob))
|
|
|
|
FUNC(val_string_asciz, ssh_key_cache_str, ARG(val_key, key))
|
|
|
|
FUNC(val_keycomponents, ssh_key_components, ARG(val_key, key))
|
|
|
|
FUNC(uint, ssh_key_public_bits, ARG(keyalg, self), ARG(val_string_ptrlen, blob))
|
Certificate-specific ssh_key method suite.
Certificate keys don't work the same as normal keys, so the rest of
the code is going to have to pay attention to whether a key is a
certificate, and if so, treat it differently and do cert-specific
stuff to it. So here's a collection of methods for that purpose.
With one exception, these methods of ssh_key are not expected to be
implemented at all in non-certificate key types: they should only ever
be called once you already know you're dealing with a certificate. So
most of the new method pointers can be left out of the ssh_keyalg
initialisers.
The exception is the base_key method, which retrieves the base key of
a certificate - the underlying one with the certificate stripped off.
It's convenient for non-certificate keys to implement this too, and
just return a pointer to themselves. So I've added an implementation
in nullkey.c doing that. (The returned pointer doesn't transfer
ownership; you have to use the new ssh_key_clone() if you want to keep
the base key after freeing the certificate key.)
The methods _only_ implemented in certificates:
Query methods to return the public key of the CA (for looking up in a
list of trusted ones), and to return the key id string (which exists
to be written into log files).
Obviously, we need a check_cert() method which will verify the CA's
actual signature, not to mention checking all the other details like
the principal and the validity period.
And there's another fiddly method for dealing with the RSA upgrade
system, called 'related_alg'. This is quite like alternate_ssh_id, in
that its job is to upgrade one key algorithm to a related one with
more modern RSA signing flags (or any other similar thing that might
later reuse the same mechanism). But where alternate_ssh_id took the
actual signing flags as an argument, this takes a pointer to the
upgraded base algorithm. So it answers the question "What is to this
key algorithm as you are to its base?" - if you call it on
opensshcert_ssh_rsa and give it ssh_rsa_sha512, it'll give you back
opensshcert_ssh_rsa_sha512.
(It's awkward to have to have another of these fiddly methods, and in
the longer term I'd like to try to clean up their proliferation a bit.
But I even more dislike the alternative of just going through
all_keyalgs looking for a cert algorithm with, say, ssh_rsa_sha512 as
the base: that approach would work fine now but it would be a lurking
time bomb for when all the -cert-v02@ methods appear one day. This
way, each certificate type can upgrade itself to the appropriately
related version. And at least related_alg is only needed if you _are_
a certificate key type - it's not adding yet another piece of
null-method boilerplate to the rest.)
2022-04-20 12:06:08 +00:00
|
|
|
FUNC_WRAPPED(val_key, ssh_key_base_key, ARG(val_key, key))
|
|
|
|
FUNC_WRAPPED(void, ssh_key_ca_public_blob, ARG(val_key, key),
|
|
|
|
ARG(out_val_string_binarysink, blob))
|
|
|
|
FUNC_WRAPPED(void, ssh_key_cert_id_string, ARG(val_key, key),
|
|
|
|
ARG(out_val_string_binarysink, blob))
|
|
|
|
FUNC_WRAPPED(boolean, ssh_key_check_cert, ARG(val_key, key),
|
|
|
|
ARG(boolean, host), ARG(val_string_ptrlen, principal),
|
2022-05-02 09:18:16 +00:00
|
|
|
ARG(uint, time), ARG(val_string_ptrlen, options),
|
|
|
|
ARG(out_val_string_binarysink, error))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
cmdgen: add a --dump option.
Also spelled '-O text', this takes a public or private key as input,
and produces on standard output a dump of all the actual numbers
involved in the key: the exponent and modulus for RSA, the p,q,g,y
parameters for DSA, the affine x and y coordinates of the public
elliptic curve point for ECC keys, and all the extra bits and pieces
in the private keys too.
Partly I expect this to be useful to me for debugging: I've had to
paste key files a few too many times through base64 decoders and hex
dump tools, then manually decode SSH marshalling and paste the result
into the Python REPL to get an integer object. Now I should be able to
get _straight_ to text I can paste into Python.
But also, it's a way that other applications can use the key
generator: if you need to generate, say, an RSA key in some format I
don't support (I've recently heard of an XML-based one, for example),
then you can run 'puttygen -t rsa --dump' and have it print the
elements of a freshly generated keypair on standard output, and then
all you have to do is understand the output format.
2020-02-17 19:53:19 +00:00
|
|
|
/*
|
|
|
|
* Accessors to retrieve the innards of a 'key_components'.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(uint, key_components_count, ARG(val_keycomponents, kc))
|
2021-11-21 15:01:58 +00:00
|
|
|
FUNC(opt_val_string_asciz_const, key_components_nth_name,
|
2021-11-28 09:39:49 +00:00
|
|
|
ARG(val_keycomponents, kc), ARG(uint, n))
|
2022-04-18 09:10:57 +00:00
|
|
|
FUNC(opt_val_string, key_components_nth_str,
|
2021-11-28 09:39:49 +00:00
|
|
|
ARG(val_keycomponents, kc), ARG(uint, n))
|
|
|
|
FUNC(opt_val_mpint, key_components_nth_mp, ARG(val_keycomponents, kc),
|
|
|
|
ARG(uint, n))
|
cmdgen: add a --dump option.
Also spelled '-O text', this takes a public or private key as input,
and produces on standard output a dump of all the actual numbers
involved in the key: the exponent and modulus for RSA, the p,q,g,y
parameters for DSA, the affine x and y coordinates of the public
elliptic curve point for ECC keys, and all the extra bits and pieces
in the private keys too.
Partly I expect this to be useful to me for debugging: I've had to
paste key files a few too many times through base64 decoders and hex
dump tools, then manually decode SSH marshalling and paste the result
into the Python REPL to get an integer object. Now I should be able to
get _straight_ to text I can paste into Python.
But also, it's a way that other applications can use the key
generator: if you need to generate, say, an RSA key in some format I
don't support (I've recently heard of an XML-based one, for example),
then you can run 'puttygen -t rsa --dump' and have it print the
elements of a freshly generated keypair on standard output, and then
all you have to do is understand the output format.
2020-02-17 19:53:19 +00:00
|
|
|
|
Switch to RFC 6979 for DSA nonce generation.
This fixes a vulnerability that compromises NIST P521 ECDSA keys when
they are used with PuTTY's existing DSA nonce generation code. The
vulnerability has been assigned the identifier CVE-2024-31497.
PuTTY has been doing its DSA signing deterministically for literally
as long as it's been doing it at all, because I didn't trust Windows's
entropy generation. Deterministic nonce generation was introduced in
commit d345ebc2a5a0b59, as part of the initial version of our DSA
signing routine. At the time, there was no standard for how to do it,
so we had to think up the details of our system ourselves, with some
help from the Cambridge University computer security group.
More than ten years later, RFC 6979 was published, recommending a
similar system for general use, naturally with all the details
different. We didn't switch over to doing it that way, because we had
a scheme in place already, and as far as I could see, the differences
were not security-critical - just the normal sort of variation you
expect when any two people design a protocol component of this kind
independently.
As far as I know, the _structure_ of our scheme is still perfectly
fine, in terms of what data gets hashed, how many times, and how the
hash output is converted into a nonce. But the weak spot is the choice
of hash function: inside our dsa_gen_k() function, we generate 512
bits of random data using SHA-512, and then reduce that to the output
range by modular reduction, regardless of what signature algorithm
we're generating a nonce for.
In the original use case, this introduced a theoretical bias (the
output size is an odd prime, which doesn't evenly divide the space of
2^512 possible inputs to the reduction), but the theory was that since
integer DSA uses a modulus prime only 160 bits long (being based on
SHA-1, at least in the form that SSH uses it), the bias would be too
small to be detectable, let alone exploitable.
Then we reused the same function for NIST-style ECDSA, when it
arrived. This is fine for the P256 curve, and even P384. But in P521,
the order of the base point is _greater_ than 2^512, so when we
generate a 512-bit number and reduce it, the reduction never makes any
difference, and our output nonces are all in the first 2^512 elements
of the range of about 2^521. So this _does_ introduce a significant
bias in the nonces, compared to the ideal of uniformly random
distribution over the whole range. And it's been recently discovered
that a bias of this kind is sufficient to expose private keys, given a
manageably small number of signatures to work from.
(Incidentally, none of this affects Ed25519. The spec for that system
includes its own idea of how you should do deterministic nonce
generation - completely different again, naturally - and we did it
that way rather than our way, so that we could use the existing test
vectors.)
The simplest fix would be to patch our existing nonce generator to use
a longer hash, or concatenate a couple of SHA-512 hashes, or something
similar. But I think a more robust approach is to switch it out
completely for what is now the standard system. The main reason why I
prefer that is that the standard system comes with test vectors, which
adds a lot of confidence that I haven't made some other mistake in
following my own design.
So here's a commit that adds an implementation of RFC 6979, and
removes the old dsa_gen_k() function. Tests are added based on the
RFC's appendix of test vectors (as many as are compatible with the
more limited API of PuTTY's crypto code, e.g. we lack support for the
NIST P192 curve, or for doing integer DSA with many different hash
functions). One existing test changes its expected outputs, namely the
one that has a sample key pair and signature for every key algorithm
we support.
2024-04-01 08:18:34 +00:00
|
|
|
/*
|
|
|
|
* DSA nonce generation.
|
|
|
|
*/
|
|
|
|
FUNC(opt_val_mpint, rfc6979, ARG(hashalg, hash), ARG(val_mpint, modulus),
|
|
|
|
ARG(val_mpint, private_key), ARG(val_string_ptrlen, message))
|
|
|
|
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
/*
|
Merge the ssh1_cipher type into ssh2_cipher.
The aim of this reorganisation is to make it easier to test all the
ciphers in PuTTY in a uniform way. It was inconvenient that there were
two separate vtable systems for the ciphers used in SSH-1 and SSH-2
with different functionality.
Now there's only one type, called ssh_cipher. But really it's the old
ssh2_cipher, just renamed: I haven't made any changes to the API on
the SSH-2 side. Instead, I've removed ssh1_cipher completely, and
adapted the SSH-1 BPP to use the SSH-2 style API.
(The relevant differences are that ssh1_cipher encapsulated both the
sending and receiving directions in one object - so now ssh1bpp has to
make a separate cipher instance per direction - and that ssh1_cipher
automatically initialised the IV to all zeroes, which ssh1bpp now has
to do by hand.)
The previous ssh1_cipher vtable for single-DES has been removed
completely, because when converted into the new API it became
identical to the SSH-2 single-DES vtable; so now there's just one
vtable for DES-CBC which works in both protocols. The other two SSH-1
ciphers each had to stay separate, because 3DES is completely
different between SSH-1 and SSH-2 (three layers of CBC structure
versus one), and Blowfish varies in endianness and key length between
the two.
(Actually, while I'm here, I've only just noticed that the SSH-1
Blowfish cipher mis-describes itself in log messages as Blowfish-128.
In fact it passes the whole of the input key buffer, which has length
SSH1_SESSION_KEY_LENGTH == 32 bytes == 256 bits. So it's actually
Blowfish-256, and has been all along!)
2019-01-17 18:06:08 +00:00
|
|
|
* The ssh_cipher abstraction. The in-place encrypt and decrypt
|
|
|
|
* functions are wrapped to replace them with versions that take one
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
* string and return a separate string.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(opt_val_cipher, ssh_cipher_new, ARG(cipheralg, alg))
|
|
|
|
FUNC_WRAPPED(void, ssh_cipher_setiv, ARG(val_cipher, c),
|
|
|
|
ARG(val_string_ptrlen, iv))
|
|
|
|
FUNC_WRAPPED(void, ssh_cipher_setkey, ARG(val_cipher, c),
|
|
|
|
ARG(val_string_ptrlen, key))
|
|
|
|
FUNC_WRAPPED(val_string, ssh_cipher_encrypt, ARG(val_cipher, c),
|
|
|
|
ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, ssh_cipher_decrypt, ARG(val_cipher, c),
|
|
|
|
ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, ssh_cipher_encrypt_length, ARG(val_cipher, c),
|
|
|
|
ARG(val_string_ptrlen, blk), ARG(uint, seq))
|
|
|
|
FUNC_WRAPPED(val_string, ssh_cipher_decrypt_length, ARG(val_cipher, c),
|
|
|
|
ARG(val_string_ptrlen, blk), ARG(uint, seq))
|
2022-08-16 17:27:06 +00:00
|
|
|
FUNC(void, ssh_cipher_next_message, ARG(val_cipher, c))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Integer Diffie-Hellman.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_dh, dh_setup_group, ARG(dh_group, group))
|
|
|
|
FUNC(val_dh, dh_setup_gex, ARG(val_mpint, p), ARG(val_mpint, g))
|
|
|
|
FUNC(uint, dh_modulus_bit_size, ARG(val_dh, ctx))
|
Stop using short exponents for Diffie-Hellman.
I recently encountered a paper [1] which catalogues all kinds of
things that can go wrong when one party in a discrete-log system
invents a prime and the other party chooses an exponent. In
particular, some choices of prime make it reasonable to use a short
exponent to save time, but others make that strategy very bad.
That paper is about the ElGamal encryption scheme used in OpenPGP,
which is basically integer Diffie-Hellman with one side's key being
persistent: a shared-secret integer is derived exactly as in DH, and
then it's used to communicate a message integer by simply multiplying
the shared secret by the message, mod p.
I don't _know_ that any problem of this kind arises in the SSH usage
of Diffie-Hellman: the standard integer DH groups in SSH are safe
primes, and as far as I know, the usual generation of prime moduli for
DH group exchange also picks safe primes. So the short exponents PuTTY
has been using _should_ be OK.
However, the range of imaginative other possibilities shown in that
paper make me nervous, even so! So I think I'm going to retire the
short exponent strategy, on general principles of overcaution.
This slows down 4096-bit integer DH by about a factor of 3-4 (which
would be worse if it weren't for the modpow speedup in the previous
commit). I think that's OK, because, firstly, computers are a lot
faster these days than when I originally chose to use short exponents,
and secondly, more and more implementations are now switching to
elliptic-curve DH, which is unaffected by this change (and with which
we've always been using maximum-length exponents).
[1] On the (in)security of ElGamal in OpenPGP. Luca De Feo, Bertram
Poettering, Alessandro Sorniotti. https://eprint.iacr.org/2021/923
2021-11-28 12:10:42 +00:00
|
|
|
FUNC(val_mpint, dh_create_e, ARG(val_dh, ctx))
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC_WRAPPED(boolean, dh_validate_f, ARG(val_dh, ctx), ARG(val_mpint, f))
|
|
|
|
FUNC(val_mpint, dh_find_K, ARG(val_dh, ctx), ARG(val_mpint, f))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Elliptic-curve Diffie-Hellman.
|
|
|
|
*/
|
2022-04-14 06:04:33 +00:00
|
|
|
FUNC(val_ecdh, ecdh_key_new, ARG(ecdh_alg, alg), ARG(boolean, is_server))
|
|
|
|
FUNC(void, ecdh_key_getpublic, ARG(val_ecdh, key),
|
2021-11-28 09:39:49 +00:00
|
|
|
ARG(out_val_string_binarysink, pub))
|
2022-04-14 06:04:33 +00:00
|
|
|
FUNC_WRAPPED(opt_val_string, ecdh_key_getkey, ARG(val_ecdh, key),
|
|
|
|
ARG(val_string_ptrlen, pub))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
Implement OpenSSH 9.x's NTRU Prime / Curve25519 kex.
This consists of DJB's 'Streamlined NTRU Prime' quantum-resistant
cryptosystem, currently in round 3 of the NIST post-quantum key
exchange competition; it's run in parallel with ordinary Curve25519,
and generates a shared secret combining the output of both systems.
(Hence, even if you don't trust this newfangled NTRU Prime thing at
all, it's at least no _less_ secure than the kex you were using
already.)
As the OpenSSH developers point out, key exchange is the most urgent
thing to make quantum-resistant, even before working quantum computers
big enough to break crypto become available, because a break of the
kex algorithm can be applied retroactively to recordings of your past
sessions. By contrast, authentication is a real-time protocol, and can
only be broken by a quantum computer if there's one available to
attack you _already_.
I've implemented both sides of the mechanism, so that PuTTY and Uppity
both support it. In my initial testing, the two sides can both
interoperate with the appropriate half of OpenSSH, and also (of
course, but it would be embarrassing to mess it up) with each other.
2022-04-15 16:19:47 +00:00
|
|
|
/*
|
|
|
|
* NTRU and its subroutines.
|
|
|
|
*/
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_ring_multiply, ARG(int16_list, a),
|
|
|
|
ARG(int16_list, b), ARG(uint, p), ARG(uint, q))
|
|
|
|
FUNC_WRAPPED(opt_int16_list, ntru_ring_invert, ARG(int16_list, r),
|
|
|
|
ARG(uint, p), ARG(uint, q))
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_mod3, ARG(int16_list, r),
|
|
|
|
ARG(uint, p), ARG(uint, q))
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_round3, ARG(int16_list, r),
|
|
|
|
ARG(uint, p), ARG(uint, q))
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_bias, ARG(int16_list, r),
|
|
|
|
ARG(uint, bias), ARG(uint, p), ARG(uint, q))
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_scale, ARG(int16_list, r),
|
|
|
|
ARG(uint, scale), ARG(uint, p), ARG(uint, q))
|
|
|
|
FUNC_WRAPPED(val_ntruencodeschedule, ntru_encode_schedule, ARG(int16_list, ms))
|
|
|
|
FUNC(uint, ntru_encode_schedule_length, ARG(val_ntruencodeschedule, sched))
|
|
|
|
FUNC_WRAPPED(void, ntru_encode, ARG(val_ntruencodeschedule, sched),
|
|
|
|
ARG(int16_list, rs), ARG(out_val_string_binarysink, data))
|
|
|
|
FUNC_WRAPPED(opt_int16_list, ntru_decode, ARG(val_ntruencodeschedule, sched),
|
|
|
|
ARG(val_string_ptrlen, data))
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_gen_short, ARG(uint, p), ARG(uint, w))
|
|
|
|
FUNC(val_ntrukeypair, ntru_keygen, ARG(uint, p), ARG(uint, q), ARG(uint, w))
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_pubkey, ARG(val_ntrukeypair, keypair))
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_encrypt, ARG(int16_list, plaintext),
|
|
|
|
ARG(int16_list, pubkey), ARG(uint, p), ARG(uint, q))
|
|
|
|
FUNC_WRAPPED(int16_list, ntru_decrypt, ARG(int16_list, ciphertext),
|
|
|
|
ARG(val_ntrukeypair, keypair))
|
|
|
|
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
/*
|
2019-12-15 20:12:36 +00:00
|
|
|
* RSA key exchange, and also the BinarySource get function
|
|
|
|
* get_ssh1_rsa_priv_agent, which is a convenient way to make an
|
|
|
|
* RSAKey for RSA kex testing purposes.
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_rsakex, ssh_rsakex_newkey, ARG(val_string_ptrlen, data))
|
|
|
|
FUNC(uint, ssh_rsakex_klen, ARG(val_rsakex, key))
|
|
|
|
FUNC(val_string, ssh_rsakex_encrypt, ARG(val_rsakex, key), ARG(hashalg, h),
|
|
|
|
ARG(val_string_ptrlen, plaintext))
|
|
|
|
FUNC(opt_val_mpint, ssh_rsakex_decrypt, ARG(val_rsakex, key), ARG(hashalg, h),
|
|
|
|
ARG(val_string_ptrlen, ciphertext))
|
|
|
|
FUNC(val_rsakex, get_rsa_ssh1_priv_agent, ARG(val_string_binarysource, src))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_rsa, rsa_new, VOID)
|
|
|
|
FUNC(void, get_rsa_ssh1_pub, ARG(val_string_binarysource, src),
|
|
|
|
ARG(val_rsa, key), ARG(rsaorder, order))
|
|
|
|
FUNC(void, get_rsa_ssh1_priv, ARG(val_string_binarysource, src),
|
|
|
|
ARG(val_rsa, key))
|
|
|
|
FUNC_WRAPPED(opt_val_string, rsa_ssh1_encrypt, ARG(val_string_ptrlen, data),
|
|
|
|
ARG(val_rsa, key))
|
|
|
|
FUNC(val_mpint, rsa_ssh1_decrypt, ARG(val_mpint, input), ARG(val_rsa, key))
|
|
|
|
FUNC_WRAPPED(val_string, rsa_ssh1_decrypt_pkcs1, ARG(val_mpint, input),
|
|
|
|
ARG(val_rsa, key))
|
|
|
|
FUNC(val_string_asciz, rsastr_fmt, ARG(val_rsa, key))
|
|
|
|
FUNC(val_string_asciz, rsa_ssh1_fingerprint, ARG(val_rsa, key))
|
|
|
|
FUNC(void, rsa_ssh1_public_blob, ARG(out_val_string_binarysink, blob),
|
|
|
|
ARG(val_rsa, key), ARG(rsaorder, order))
|
|
|
|
FUNC(int, rsa_ssh1_public_blob_len, ARG(val_string_ptrlen, data))
|
|
|
|
FUNC(void, rsa_ssh1_private_blob_agent, ARG(out_val_string_binarysink, blob),
|
|
|
|
ARG(val_rsa, key))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
Replace PuTTY's PRNG with a Fortuna-like system.
This tears out the entire previous random-pool system in sshrand.c. In
its place is a system pretty close to Ferguson and Schneier's
'Fortuna' generator, with the main difference being that I use SHA-256
instead of AES for the generation side of the system (rationale given
in comment).
The PRNG implementation lives in sshprng.c, and defines a self-
contained data type with no state stored outside the object, so you
can instantiate however many of them you like. The old sshrand.c still
exists, but in place of the previous random pool system, it's just
become a client of sshprng.c, whose job is to hold a single global
instance of the PRNG type, and manage its reference count, save file,
noise-collection timers and similar administrative business.
Advantages of this change include:
- Fortuna is designed with a more varied threat model in mind than my
old home-grown random pool. For example, after any request for
random numbers, it automatically re-seeds itself, so that if the
state of the PRNG should be leaked, it won't give enough
information to find out what past outputs _were_.
- The PRNG type can be instantiated with any hash function; the
instance used by the main tools is based on SHA-256, an improvement
on the old pool's use of SHA-1.
- The new PRNG only uses the completely standard interface to the
hash function API, instead of having to have privileged access to
the internal SHA-1 block transform function. This will make it
easier to revamp the hash code in general, and also it means that
hardware-accelerated versions of SHA-256 will automatically be used
for the PRNG as well as for everything else.
- The new PRNG can be _tested_! Because it has an actual (if not
quite explicit) specification for exactly what the output numbers
_ought_ to be derived from the hashes of, I can (and have) put
tests in cryptsuite that ensure the output really is being derived
in the way I think it is. The old pool could have been returning
any old nonsense and it would have been very hard to tell for sure.
2019-01-22 22:42:41 +00:00
|
|
|
/*
|
|
|
|
* The PRNG type. Similarly to hashes and MACs, I've invented an extra
|
|
|
|
* function prng_seed_update for putting seed data into the PRNG's
|
|
|
|
* exposed BinarySink.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_prng, prng_new, ARG(hashalg, hashalg))
|
|
|
|
FUNC(void, prng_seed_begin, ARG(val_prng, pr))
|
|
|
|
FUNC(void, prng_seed_update, ARG(val_prng, pr), ARG(val_string_ptrlen, data))
|
|
|
|
FUNC(void, prng_seed_finish, ARG(val_prng, pr))
|
|
|
|
FUNC_WRAPPED(val_string, prng_read, ARG(val_prng, pr), ARG(uint, size))
|
|
|
|
FUNC(void, prng_add_entropy, ARG(val_prng, pr), ARG(uint, source_id),
|
|
|
|
ARG(val_string_ptrlen, data))
|
Replace PuTTY's PRNG with a Fortuna-like system.
This tears out the entire previous random-pool system in sshrand.c. In
its place is a system pretty close to Ferguson and Schneier's
'Fortuna' generator, with the main difference being that I use SHA-256
instead of AES for the generation side of the system (rationale given
in comment).
The PRNG implementation lives in sshprng.c, and defines a self-
contained data type with no state stored outside the object, so you
can instantiate however many of them you like. The old sshrand.c still
exists, but in place of the previous random pool system, it's just
become a client of sshprng.c, whose job is to hold a single global
instance of the PRNG type, and manage its reference count, save file,
noise-collection timers and similar administrative business.
Advantages of this change include:
- Fortuna is designed with a more varied threat model in mind than my
old home-grown random pool. For example, after any request for
random numbers, it automatically re-seeds itself, so that if the
state of the PRNG should be leaked, it won't give enough
information to find out what past outputs _were_.
- The PRNG type can be instantiated with any hash function; the
instance used by the main tools is based on SHA-256, an improvement
on the old pool's use of SHA-1.
- The new PRNG only uses the completely standard interface to the
hash function API, instead of having to have privileged access to
the internal SHA-1 block transform function. This will make it
easier to revamp the hash code in general, and also it means that
hardware-accelerated versions of SHA-256 will automatically be used
for the PRNG as well as for everything else.
- The new PRNG can be _tested_! Because it has an actual (if not
quite explicit) specification for exactly what the output numbers
_ought_ to be derived from the hashes of, I can (and have) put
tests in cryptsuite that ensure the output really is being derived
in the way I think it is. The old pool could have been returning
any old nonsense and it would have been very hard to tell for sure.
2019-01-22 22:42:41 +00:00
|
|
|
|
2020-01-06 19:58:25 +00:00
|
|
|
/*
|
|
|
|
* Key load/save functions, or rather, the BinarySource / strbuf API
|
|
|
|
* that sits just inside the file I/O versions.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(boolean, ppk_encrypted_s, ARG(val_string_binarysource, src),
|
|
|
|
ARG(out_opt_val_string_asciz, comment))
|
|
|
|
FUNC(boolean, rsa1_encrypted_s, ARG(val_string_binarysource, src),
|
|
|
|
ARG(out_opt_val_string_asciz, comment))
|
|
|
|
FUNC(boolean, ppk_loadpub_s, ARG(val_string_binarysource, src),
|
|
|
|
ARG(out_opt_val_string_asciz, algorithm),
|
|
|
|
ARG(out_val_string_binarysink, blob),
|
|
|
|
ARG(out_opt_val_string_asciz, comment),
|
|
|
|
ARG(out_opt_val_string_asciz_const, error))
|
|
|
|
FUNC(int, rsa1_loadpub_s, ARG(val_string_binarysource, src),
|
|
|
|
ARG(out_val_string_binarysink, blob),
|
|
|
|
ARG(out_opt_val_string_asciz, comment),
|
|
|
|
ARG(out_opt_val_string_asciz_const, error))
|
|
|
|
FUNC_WRAPPED(opt_val_key, ppk_load_s, ARG(val_string_binarysource, src),
|
|
|
|
ARG(out_opt_val_string_asciz, comment),
|
|
|
|
ARG(opt_val_string_asciz, passphrase),
|
|
|
|
ARG(out_opt_val_string_asciz_const, error))
|
|
|
|
FUNC_WRAPPED(int, rsa1_load_s, ARG(val_string_binarysource, src),
|
|
|
|
ARG(val_rsa, key), ARG(out_opt_val_string_asciz, comment),
|
|
|
|
ARG(opt_val_string_asciz, passphrase),
|
|
|
|
ARG(out_opt_val_string_asciz_const, error))
|
|
|
|
FUNC_WRAPPED(val_string, ppk_save_sb, ARG(val_key, key),
|
|
|
|
ARG(opt_val_string_asciz, comment),
|
|
|
|
ARG(opt_val_string_asciz, passphrase), ARG(uint, fmt_version),
|
|
|
|
ARG(argon2flavour, flavour), ARG(uint, mem), ARG(uint, passes),
|
|
|
|
ARG(uint, parallel))
|
|
|
|
FUNC_WRAPPED(val_string, rsa1_save_sb, ARG(val_rsa, key),
|
|
|
|
ARG(opt_val_string_asciz, comment),
|
|
|
|
ARG(opt_val_string_asciz, passphrase))
|
2020-01-06 19:58:25 +00:00
|
|
|
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_string_asciz, ssh2_fingerprint_blob, ARG(val_string_ptrlen, blob),
|
|
|
|
ARG(fptype, fptype))
|
2021-03-13 09:52:56 +00:00
|
|
|
|
2021-02-13 17:30:12 +00:00
|
|
|
/*
|
|
|
|
* Password hashing.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC_WRAPPED(val_string, argon2, ARG(argon2flavour, flavour), ARG(uint, mem),
|
|
|
|
ARG(uint, passes), ARG(uint, parallel), ARG(uint, taglen),
|
|
|
|
ARG(val_string_ptrlen, P), ARG(val_string_ptrlen, S),
|
|
|
|
ARG(val_string_ptrlen, K), ARG(val_string_ptrlen, X))
|
|
|
|
FUNC(val_string, argon2_long_hash, ARG(uint, length),
|
|
|
|
ARG(val_string_ptrlen, data))
|
2021-12-24 09:56:30 +00:00
|
|
|
FUNC_WRAPPED(val_string, openssh_bcrypt, ARG(val_string_ptrlen, passphrase),
|
|
|
|
ARG(val_string_ptrlen, salt), ARG(uint, rounds),
|
|
|
|
ARG(uint, outbytes))
|
2021-02-13 17:30:12 +00:00
|
|
|
|
2020-01-09 07:21:30 +00:00
|
|
|
/*
|
|
|
|
* Key generation functions.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC_WRAPPED(val_key, rsa_generate, ARG(uint, bits), ARG(boolean, strong),
|
|
|
|
ARG(val_pgc, pgc))
|
|
|
|
FUNC_WRAPPED(val_key, dsa_generate, ARG(uint, bits), ARG(val_pgc, pgc))
|
|
|
|
FUNC_WRAPPED(opt_val_key, ecdsa_generate, ARG(uint, bits))
|
|
|
|
FUNC_WRAPPED(opt_val_key, eddsa_generate, ARG(uint, bits))
|
|
|
|
FUNC(val_rsa, rsa1_generate, ARG(uint, bits), ARG(boolean, strong),
|
|
|
|
ARG(val_pgc, pgc))
|
|
|
|
FUNC(val_pgc, primegen_new_context, ARG(primegenpolicy, policy))
|
|
|
|
FUNC_WRAPPED(opt_val_mpint, primegen_generate, ARG(val_pgc, ctx),
|
|
|
|
ARG(consumed_val_pcs, pcs))
|
|
|
|
FUNC(val_string, primegen_mpu_certificate, ARG(val_pgc, ctx), ARG(val_mpint, p))
|
|
|
|
FUNC(val_pcs, pcs_new, ARG(uint, bits))
|
|
|
|
FUNC(val_pcs, pcs_new_with_firstbits, ARG(uint, bits), ARG(uint, first),
|
|
|
|
ARG(uint, nfirst))
|
|
|
|
FUNC(void, pcs_require_residue, ARG(val_pcs, s), ARG(val_mpint, mod),
|
|
|
|
ARG(val_mpint, res))
|
|
|
|
FUNC(void, pcs_require_residue_1, ARG(val_pcs, s), ARG(val_mpint, mod))
|
|
|
|
FUNC(void, pcs_require_residue_1_mod_prime, ARG(val_pcs, s),
|
|
|
|
ARG(val_mpint, mod))
|
|
|
|
FUNC(void, pcs_avoid_residue_small, ARG(val_pcs, s), ARG(uint, mod),
|
|
|
|
ARG(uint, res))
|
|
|
|
FUNC(void, pcs_try_sophie_germain, ARG(val_pcs, s))
|
|
|
|
FUNC(void, pcs_set_oneshot, ARG(val_pcs, s))
|
|
|
|
FUNC(void, pcs_ready, ARG(val_pcs, s))
|
|
|
|
FUNC(void, pcs_inspect, ARG(val_pcs, pcs), ARG(out_val_mpint, limit_out),
|
|
|
|
ARG(out_val_mpint, factor_out), ARG(out_val_mpint, addend_out))
|
|
|
|
FUNC(val_mpint, pcs_generate, ARG(val_pcs, s))
|
|
|
|
FUNC(val_pockle, pockle_new, VOID)
|
|
|
|
FUNC(uint, pockle_mark, ARG(val_pockle, pockle))
|
|
|
|
FUNC(void, pockle_release, ARG(val_pockle, pockle), ARG(uint, mark))
|
|
|
|
FUNC(pocklestatus, pockle_add_small_prime, ARG(val_pockle, pockle),
|
|
|
|
ARG(val_mpint, p))
|
|
|
|
FUNC_WRAPPED(pocklestatus, pockle_add_prime, ARG(val_pockle, pockle),
|
|
|
|
ARG(val_mpint, p), ARG(mpint_list, factors),
|
|
|
|
ARG(val_mpint, witness))
|
|
|
|
FUNC(val_string, pockle_mpu, ARG(val_pockle, pockle), ARG(val_mpint, p))
|
|
|
|
FUNC(val_millerrabin, miller_rabin_new, ARG(val_mpint, p))
|
|
|
|
FUNC(mr_result, miller_rabin_test, ARG(val_millerrabin, mr), ARG(val_mpint, w))
|
2020-01-09 07:21:30 +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.
2019-01-01 19:08:37 +00:00
|
|
|
/*
|
|
|
|
* Miscellaneous.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(val_wpoint, ecdsa_public, ARG(val_mpint, private_key), ARG(keyalg, alg))
|
|
|
|
FUNC(val_epoint, eddsa_public, ARG(val_mpint, private_key), ARG(keyalg, alg))
|
|
|
|
FUNC_WRAPPED(val_string, des_encrypt_xdmauth, ARG(val_string_ptrlen, key),
|
|
|
|
ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, des_decrypt_xdmauth, ARG(val_string_ptrlen, key),
|
|
|
|
ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, des3_encrypt_pubkey, ARG(val_string_ptrlen, key),
|
|
|
|
ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, des3_decrypt_pubkey, ARG(val_string_ptrlen, key),
|
|
|
|
ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, des3_encrypt_pubkey_ossh, ARG(val_string_ptrlen, key),
|
|
|
|
ARG(val_string_ptrlen, iv), ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, des3_decrypt_pubkey_ossh, ARG(val_string_ptrlen, key),
|
|
|
|
ARG(val_string_ptrlen, iv), ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, aes256_encrypt_pubkey, ARG(val_string_ptrlen, key),
|
|
|
|
ARG(val_string_ptrlen, iv), ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC_WRAPPED(val_string, aes256_decrypt_pubkey, ARG(val_string_ptrlen, key),
|
|
|
|
ARG(val_string_ptrlen, iv), ARG(val_string_ptrlen, blk))
|
|
|
|
FUNC(uint, crc32_rfc1662, ARG(val_string_ptrlen, data))
|
|
|
|
FUNC(uint, crc32_ssh1, ARG(val_string_ptrlen, data))
|
|
|
|
FUNC(uint, crc32_update, ARG(uint, crc_input), ARG(val_string_ptrlen, data))
|
|
|
|
FUNC(boolean, crcda_detect, ARG(val_string_ptrlen, packet),
|
|
|
|
ARG(val_string_ptrlen, iv))
|
|
|
|
FUNC(val_string, get_implementations_commasep, ARG(val_string_ptrlen, alg))
|
|
|
|
FUNC(void, http_digest_response, ARG(out_val_string_binarysink, response),
|
|
|
|
ARG(val_string_ptrlen, username), ARG(val_string_ptrlen, password),
|
|
|
|
ARG(val_string_ptrlen, realm), ARG(val_string_ptrlen, method),
|
|
|
|
ARG(val_string_ptrlen, uri), ARG(val_string_ptrlen, qop),
|
|
|
|
ARG(val_string_ptrlen, nonce), ARG(val_string_ptrlen, opaque),
|
|
|
|
ARG(uint, nonce_count), ARG(httpdigesthash, hash),
|
|
|
|
ARG(boolean, hash_username))
|
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.
2019-01-01 19:08:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* These functions aren't part of PuTTY's own API, but are additions
|
|
|
|
* by testcrypt itself for administrative purposes.
|
|
|
|
*/
|
2021-11-28 09:39:49 +00:00
|
|
|
FUNC(void, random_queue, ARG(val_string_ptrlen, data))
|
|
|
|
FUNC(uint, random_queue_len, VOID)
|
|
|
|
FUNC(void, random_make_prng, ARG(hashalg, hashalg),
|
|
|
|
ARG(val_string_ptrlen, seed))
|
|
|
|
FUNC(void, random_clear, VOID)
|