2018-05-24 11:17:13 +03:00
|
|
|
#ifndef PUTTY_MARSHAL_H
|
|
|
|
#define PUTTY_MARSHAL_H
|
|
|
|
|
|
|
|
#include "defs.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A sort of 'abstract base class' or 'interface' or 'trait' which is
|
|
|
|
* the common feature of all types that want to accept data formatted
|
|
|
|
* using the SSH binary conventions of uint32, string, mpint etc.
|
|
|
|
*/
|
|
|
|
struct BinarySink {
|
|
|
|
void (*write)(BinarySink *sink, const void *data, size_t len);
|
|
|
|
BinarySink *binarysink_;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* To define a structure type as a valid target for binary formatted
|
|
|
|
* data, put 'BinarySink_IMPLEMENTATION' in its declaration, and when
|
|
|
|
* an instance is set up, use 'BinarySink_INIT' to initialise the
|
|
|
|
* 'base class' state, providing a function pointer to be the
|
|
|
|
* implementation of the write() call above.
|
|
|
|
*/
|
|
|
|
#define BinarySink_IMPLEMENTATION BinarySink binarysink_[1]
|
|
|
|
#define BinarySink_INIT(obj, writefn) \
|
|
|
|
((obj)->binarysink_->write = (writefn), \
|
|
|
|
(obj)->binarysink_->binarysink_ = (obj)->binarysink_)
|
|
|
|
|
2018-09-13 18:15:17 +03:00
|
|
|
/*
|
|
|
|
* To define a larger structure type as a valid BinarySink in such a
|
|
|
|
* way that it will delegate the write method to some other object,
|
|
|
|
* put 'BinarySink_DELEGATE_IMPLEMENTATION' in its declaration, and
|
|
|
|
* when an instance is set up, use 'BinarySink_DELEGATE_INIT' to point
|
|
|
|
* at the object it wants to delegate to.
|
|
|
|
*/
|
|
|
|
#define BinarySink_DELEGATE_IMPLEMENTATION BinarySink *binarysink_
|
|
|
|
#define BinarySink_DELEGATE_INIT(obj, othersink) \
|
|
|
|
((obj)->binarysink_ = BinarySink_UPCAST(othersink))
|
|
|
|
|
2018-05-24 11:17:13 +03:00
|
|
|
/*
|
|
|
|
* The implementing type's write function will want to downcast its
|
|
|
|
* 'BinarySink *' parameter back to the more specific type. Also,
|
|
|
|
* sometimes you'll want to upcast a pointer to a particular
|
|
|
|
* implementing type into an abstract 'BinarySink *' to pass to
|
|
|
|
* generic subroutines not defined in this file. These macros do that
|
|
|
|
* job.
|
|
|
|
*
|
|
|
|
* Importantly, BinarySink_UPCAST can also be applied to a BinarySink
|
|
|
|
* * itself (and leaves it unchanged). That's achieved by a small
|
|
|
|
* piece of C trickery: implementing structures and the BinarySink
|
|
|
|
* structure itself both contain a field called binarysink_, but in
|
|
|
|
* implementing objects it's a BinarySink[1] whereas in the abstract
|
|
|
|
* type it's a 'BinarySink *' pointing back to the same structure,
|
|
|
|
* meaning that you can say 'foo->binarysink_' in either case and get
|
|
|
|
* a pointer type by different methods.
|
|
|
|
*/
|
|
|
|
#define BinarySink_DOWNCAST(object, type) \
|
|
|
|
TYPECHECK((object) == ((type *)0)->binarysink_, \
|
|
|
|
((type *)(((char *)(object)) - offsetof(type, binarysink_))))
|
|
|
|
#define BinarySink_UPCAST(object) \
|
|
|
|
TYPECHECK((object)->binarysink_ == (BinarySink *)0, \
|
|
|
|
(object)->binarysink_)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If you structure-copy an object that's implementing BinarySink,
|
|
|
|
* then that tricky self-pointer in its trait subobject will point to
|
|
|
|
* the wrong place. You could call BinarySink_INIT again, but this
|
|
|
|
* macro is terser and does all that's needed to fix up the copied
|
|
|
|
* object.
|
|
|
|
*/
|
|
|
|
#define BinarySink_COPIED(obj) \
|
|
|
|
((obj)->binarysink_->binarysink_ = (obj)->binarysink_)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The put_* macros are the main client to this system. Any structure
|
|
|
|
* which implements the BinarySink 'trait' is valid for use as the
|
|
|
|
* first parameter of any of these put_* macros.
|
|
|
|
*/
|
|
|
|
|
2018-10-27 01:08:58 +03:00
|
|
|
/* Basic big-endian integer types. */
|
2018-05-24 11:17:13 +03:00
|
|
|
#define put_byte(bs, val) \
|
|
|
|
BinarySink_put_byte(BinarySink_UPCAST(bs), val)
|
|
|
|
#define put_uint16(bs, val) \
|
|
|
|
BinarySink_put_uint16(BinarySink_UPCAST(bs), val)
|
|
|
|
#define put_uint32(bs, val) \
|
|
|
|
BinarySink_put_uint32(BinarySink_UPCAST(bs), val)
|
|
|
|
#define put_uint64(bs, val) \
|
|
|
|
BinarySink_put_uint64(BinarySink_UPCAST(bs), val)
|
|
|
|
|
|
|
|
/* SSH booleans, encoded as a single byte storing either 0 or 1. */
|
|
|
|
#define put_bool(bs, val) \
|
|
|
|
BinarySink_put_bool(BinarySink_UPCAST(bs), val)
|
|
|
|
|
|
|
|
/* SSH strings, with a leading uint32 length field. 'stringz' is a
|
|
|
|
* convenience function that takes an ordinary C zero-terminated
|
|
|
|
* string as input. 'stringsb' takes a strbuf * as input, and
|
|
|
|
* finalises it as a side effect (handy for multi-level marshalling in
|
|
|
|
* which you use these same functions to format an inner blob of data
|
|
|
|
* that then gets wrapped into a string container in an outer one). */
|
|
|
|
#define put_string(bs, val, len) \
|
|
|
|
BinarySink_put_string(BinarySink_UPCAST(bs),val,len)
|
2018-05-27 18:56:51 +03:00
|
|
|
#define put_stringpl(bs, ptrlen) \
|
|
|
|
BinarySink_put_stringpl(BinarySink_UPCAST(bs),ptrlen)
|
2018-05-24 11:17:13 +03:00
|
|
|
#define put_stringz(bs, val) \
|
|
|
|
BinarySink_put_stringz(BinarySink_UPCAST(bs), val)
|
|
|
|
#define put_stringsb(bs, val) \
|
|
|
|
BinarySink_put_stringsb(BinarySink_UPCAST(bs), val)
|
|
|
|
|
|
|
|
/* Other string outputs: 'asciz' emits the string data directly into
|
|
|
|
* the output including the terminating \0, and 'pstring' emits the
|
|
|
|
* string in Pascal style with a leading _one_-byte length field.
|
|
|
|
* pstring can fail if the string is too long. */
|
|
|
|
#define put_asciz(bs, val) \
|
|
|
|
BinarySink_put_asciz(BinarySink_UPCAST(bs), val)
|
|
|
|
#define put_pstring(bs, val) \
|
|
|
|
BinarySink_put_pstring(BinarySink_UPCAST(bs), val)
|
|
|
|
|
|
|
|
/* Multiprecision integers, in both the SSH-1 and SSH-2 formats. */
|
|
|
|
#define put_mp_ssh1(bs, val) \
|
|
|
|
BinarySink_put_mp_ssh1(BinarySink_UPCAST(bs), val)
|
|
|
|
#define put_mp_ssh2(bs, val) \
|
|
|
|
BinarySink_put_mp_ssh2(BinarySink_UPCAST(bs), val)
|
|
|
|
|
2018-06-09 11:01:07 +03:00
|
|
|
/* Padding with a specified byte. */
|
2018-06-13 21:42:19 +03:00
|
|
|
#define put_padding(bs, len, padbyte) \
|
|
|
|
BinarySink_put_padding(BinarySink_UPCAST(bs), len, padbyte)
|
2018-06-09 11:01:07 +03:00
|
|
|
|
2018-05-24 11:17:13 +03:00
|
|
|
/* Fallback: just emit raw data bytes, using a syntax that matches the
|
|
|
|
* rest of these macros. */
|
|
|
|
#define put_data(bs, val, len) \
|
|
|
|
BinarySink_put_data(BinarySink_UPCAST(bs), val, len)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The underlying real C functions that implement most of those
|
|
|
|
* macros. Generally you won't want to call these directly, because
|
|
|
|
* they have such cumbersome names; you call the wrapper macros above
|
|
|
|
* instead.
|
|
|
|
*
|
|
|
|
* A few functions whose wrapper macros are defined above are actually
|
|
|
|
* declared in other headers, so as to guarantee that the
|
|
|
|
* declaration(s) of their other parameter type(s) are in scope.
|
|
|
|
*/
|
|
|
|
void BinarySink_put_data(BinarySink *, const void *data, size_t len);
|
2018-06-13 21:42:19 +03:00
|
|
|
void BinarySink_put_padding(BinarySink *, size_t len, unsigned char padbyte);
|
2018-05-24 11:17:13 +03:00
|
|
|
void BinarySink_put_byte(BinarySink *, unsigned char);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
void BinarySink_put_bool(BinarySink *, bool);
|
2018-05-24 11:17:13 +03:00
|
|
|
void BinarySink_put_uint16(BinarySink *, unsigned long);
|
|
|
|
void BinarySink_put_uint32(BinarySink *, unsigned long);
|
2018-10-27 01:08:58 +03:00
|
|
|
void BinarySink_put_uint64(BinarySink *, uint64_t);
|
2018-05-24 11:17:13 +03:00
|
|
|
void BinarySink_put_string(BinarySink *, const void *data, size_t len);
|
2018-05-27 18:56:51 +03:00
|
|
|
void BinarySink_put_stringpl(BinarySink *, ptrlen);
|
2018-05-24 11:17:13 +03:00
|
|
|
void BinarySink_put_stringz(BinarySink *, const char *str);
|
|
|
|
struct strbuf;
|
|
|
|
void BinarySink_put_stringsb(BinarySink *, struct strbuf *);
|
|
|
|
void BinarySink_put_asciz(BinarySink *, const char *str);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool BinarySink_put_pstring(BinarySink *, const char *str);
|
Complete rewrite of PuTTY's bignum library.
The old 'Bignum' data type is gone completely, and so is sshbn.c. In
its place is a new thing called 'mp_int', handled by an entirely new
library module mpint.c, with API differences both large and small.
The main aim of this change is that the new library should be free of
timing- and cache-related side channels. I've written the code so that
it _should_ - assuming I haven't made any mistakes - do all of its
work without either control flow or memory addressing depending on the
data words of the input numbers. (Though, being an _arbitrary_
precision library, it does have to at least depend on the sizes of the
numbers - but there's a 'formal' size that can vary separately from
the actual magnitude of the represented integer, so if you want to
keep it secret that your number is actually small, it should work fine
to have a very long mp_int and just happen to store 23 in it.) So I've
done all my conditionalisation by means of computing both answers and
doing bit-masking to swap the right one into place, and all loops over
the words of an mp_int go up to the formal size rather than the actual
size.
I haven't actually tested the constant-time property in any rigorous
way yet (I'm still considering the best way to do it). But this code
is surely at the very least a big improvement on the old version, even
if I later find a few more things to fix.
I've also completely rewritten the low-level elliptic curve arithmetic
from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c
than it is to the SSH end of the code. The new elliptic curve code
keeps all coordinates in Montgomery-multiplication transformed form to
speed up all the multiplications mod the same prime, and only converts
them back when you ask for the affine coordinates. Also, I adopted
extended coordinates for the Edwards curve implementation.
sshecc.c has also had a near-total rewrite in the course of switching
it over to the new system. While I was there, I've separated ECDSA and
EdDSA more completely - they now have separate vtables, instead of a
single vtable in which nearly every function had a big if statement in
it - and also made the externally exposed types for an ECDSA key and
an ECDH context different.
A minor new feature: since the new arithmetic code includes a modular
square root function, we can now support the compressed point
representation for the NIST curves. We seem to have been getting along
fine without that so far, but it seemed a shame not to put it in,
since it was suddenly easy.
In sshrsa.c, one major change is that I've removed the RSA blinding
step in rsa_privkey_op, in which we randomise the ciphertext before
doing the decryption. The purpose of that was to avoid timing leaks
giving away the plaintext - but the new arithmetic code should take
that in its stride in the course of also being careful enough to avoid
leaking the _private key_, which RSA blinding had no way to do
anything about in any case.
Apart from those specific points, most of the rest of the changes are
more or less mechanical, just changing type names and translating code
into the new API.
2018-12-31 16:53:41 +03:00
|
|
|
void BinarySink_put_mp_ssh1(BinarySink *bs, mp_int *x);
|
|
|
|
void BinarySink_put_mp_ssh2(BinarySink *bs, mp_int *x);
|
2018-05-24 11:17:13 +03:00
|
|
|
|
Introduce a centralised unmarshaller, 'BinarySource'.
This is the companion to the BinarySink system I introduced a couple
of weeks ago, and provides the same type-genericity which will let me
use the same get_* routines on an SSH packet, an SFTP packet or
anything else that chooses to include an implementing substructure.
However, unlike BinarySink which contained a (one-function) vtable,
BinarySource contains only mutable data fields - so another thing you
might very well want to do is to simply instantiate a bare one without
any containing object at all. I couldn't quite coerce C into letting
me use the same setup macro in both cases, so I've arranged a
BinarySource_INIT you can use on larger implementing objects and a
BinarySource_BARE_INIT you can use on a BinarySource not contained in
anything.
The API follows the general principle that even if decoding fails, the
decode functions will always return _some_ kind of value, with the
same dynamically-allocated-ness they would have used for a completely
successful value. But they also set an error flag in the BinarySource
which can be tested later. So instead of having to decode a 10-field
packet by means of 10 separate 'if (!get_foo(src)) throw error'
clauses, you can just write 10 'variable = get_foo(src)' statements
followed by a single check of get_err(src), and if the error check
fails, you have to do exactly the same set of frees you would have
after a successful decode.
2018-06-02 10:25:19 +03:00
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A complementary trait structure for _un_-marshalling.
|
|
|
|
*
|
|
|
|
* This structure contains client-visible data fields rather than
|
|
|
|
* methods, because that seemed more useful than leaving it totally
|
|
|
|
* opaque. But it's still got the self-pointer system that will allow
|
|
|
|
* the set of get_* macros to target one of these itself or any other
|
|
|
|
* type that 'derives' from it. So, for example, an SSH packet
|
|
|
|
* structure can act as a BinarySource while also having additional
|
|
|
|
* fields like the packet type.
|
|
|
|
*/
|
|
|
|
typedef enum BinarySourceError {
|
|
|
|
BSE_NO_ERROR,
|
|
|
|
BSE_OUT_OF_DATA,
|
|
|
|
BSE_INVALID
|
|
|
|
} BinarySourceError;
|
|
|
|
struct BinarySource {
|
|
|
|
/*
|
|
|
|
* (data, len) is the data block being decoded. pos is the current
|
|
|
|
* position within the block.
|
|
|
|
*/
|
|
|
|
const void *data;
|
|
|
|
size_t pos, len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 'err' indicates whether a decoding error has happened at any
|
|
|
|
* point. Once this has been set to something other than
|
|
|
|
* BSE_NO_ERROR, it shouldn't be changed by any unmarshalling
|
|
|
|
* function. So you can safely do a long sequence of get_foo()
|
|
|
|
* operations and then test err just once at the end, rather than
|
|
|
|
* having to conditionalise every single get.
|
|
|
|
*
|
|
|
|
* The unmarshalling functions should always return some value,
|
|
|
|
* even if a decoding error occurs. Generally on error they'll
|
|
|
|
* return zero (if numeric) or the empty string (if string-based),
|
|
|
|
* or some other appropriate default value for more complicated
|
|
|
|
* types.
|
|
|
|
*
|
|
|
|
* If the usual return value is dynamically allocated (e.g. a
|
Complete rewrite of PuTTY's bignum library.
The old 'Bignum' data type is gone completely, and so is sshbn.c. In
its place is a new thing called 'mp_int', handled by an entirely new
library module mpint.c, with API differences both large and small.
The main aim of this change is that the new library should be free of
timing- and cache-related side channels. I've written the code so that
it _should_ - assuming I haven't made any mistakes - do all of its
work without either control flow or memory addressing depending on the
data words of the input numbers. (Though, being an _arbitrary_
precision library, it does have to at least depend on the sizes of the
numbers - but there's a 'formal' size that can vary separately from
the actual magnitude of the represented integer, so if you want to
keep it secret that your number is actually small, it should work fine
to have a very long mp_int and just happen to store 23 in it.) So I've
done all my conditionalisation by means of computing both answers and
doing bit-masking to swap the right one into place, and all loops over
the words of an mp_int go up to the formal size rather than the actual
size.
I haven't actually tested the constant-time property in any rigorous
way yet (I'm still considering the best way to do it). But this code
is surely at the very least a big improvement on the old version, even
if I later find a few more things to fix.
I've also completely rewritten the low-level elliptic curve arithmetic
from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c
than it is to the SSH end of the code. The new elliptic curve code
keeps all coordinates in Montgomery-multiplication transformed form to
speed up all the multiplications mod the same prime, and only converts
them back when you ask for the affine coordinates. Also, I adopted
extended coordinates for the Edwards curve implementation.
sshecc.c has also had a near-total rewrite in the course of switching
it over to the new system. While I was there, I've separated ECDSA and
EdDSA more completely - they now have separate vtables, instead of a
single vtable in which nearly every function had a big if statement in
it - and also made the externally exposed types for an ECDSA key and
an ECDH context different.
A minor new feature: since the new arithmetic code includes a modular
square root function, we can now support the compressed point
representation for the NIST curves. We seem to have been getting along
fine without that so far, but it seemed a shame not to put it in,
since it was suddenly easy.
In sshrsa.c, one major change is that I've removed the RSA blinding
step in rsa_privkey_op, in which we randomise the ciphertext before
doing the decryption. The purpose of that was to avoid timing leaks
giving away the plaintext - but the new arithmetic code should take
that in its stride in the course of also being careful enough to avoid
leaking the _private key_, which RSA blinding had no way to do
anything about in any case.
Apart from those specific points, most of the rest of the changes are
more or less mechanical, just changing type names and translating code
into the new API.
2018-12-31 16:53:41 +03:00
|
|
|
* bignum, or a normal C 'char *' string), then the error value is
|
Introduce a centralised unmarshaller, 'BinarySource'.
This is the companion to the BinarySink system I introduced a couple
of weeks ago, and provides the same type-genericity which will let me
use the same get_* routines on an SSH packet, an SFTP packet or
anything else that chooses to include an implementing substructure.
However, unlike BinarySink which contained a (one-function) vtable,
BinarySource contains only mutable data fields - so another thing you
might very well want to do is to simply instantiate a bare one without
any containing object at all. I couldn't quite coerce C into letting
me use the same setup macro in both cases, so I've arranged a
BinarySource_INIT you can use on larger implementing objects and a
BinarySource_BARE_INIT you can use on a BinarySource not contained in
anything.
The API follows the general principle that even if decoding fails, the
decode functions will always return _some_ kind of value, with the
same dynamically-allocated-ness they would have used for a completely
successful value. But they also set an error flag in the BinarySource
which can be tested later. So instead of having to decode a 10-field
packet by means of 10 separate 'if (!get_foo(src)) throw error'
clauses, you can just write 10 'variable = get_foo(src)' statements
followed by a single check of get_err(src), and if the error check
fails, you have to do exactly the same set of frees you would have
after a successful decode.
2018-06-02 10:25:19 +03:00
|
|
|
* also dynamic in the same way. So you have to free exactly the
|
|
|
|
* same set of things whether or not there was a decoding error,
|
|
|
|
* which simplifies exit paths - for example, you could call a big
|
|
|
|
* pile of get_foo functions, then put the actual handling of the
|
|
|
|
* results under 'if (!get_err(src))', and then free everything
|
|
|
|
* outside that if.
|
|
|
|
*/
|
|
|
|
BinarySourceError err;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Self-pointer for the implicit derivation trick, same as
|
|
|
|
* BinarySink above.
|
|
|
|
*/
|
|
|
|
BinarySource *binarysource_;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Implementation macros, similar to BinarySink.
|
|
|
|
*/
|
|
|
|
#define BinarySource_IMPLEMENTATION BinarySource binarysource_[1]
|
|
|
|
#define BinarySource_INIT__(obj, data_, len_) \
|
|
|
|
((obj)->data = (data_), \
|
|
|
|
(obj)->len = (len_), \
|
|
|
|
(obj)->pos = 0, \
|
|
|
|
(obj)->err = BSE_NO_ERROR, \
|
|
|
|
(obj)->binarysource_ = (obj))
|
|
|
|
#define BinarySource_BARE_INIT(obj, data_, len_) \
|
|
|
|
TYPECHECK(&(obj)->binarysource_ == (BinarySource **)0, \
|
|
|
|
BinarySource_INIT__(obj, data_, len_))
|
|
|
|
#define BinarySource_INIT(obj, data_, len_) \
|
|
|
|
TYPECHECK(&(obj)->binarysource_ == (BinarySource (*)[1])0, \
|
|
|
|
BinarySource_INIT__(BinarySource_UPCAST(obj), data_, len_))
|
|
|
|
#define BinarySource_DOWNCAST(object, type) \
|
|
|
|
TYPECHECK((object) == ((type *)0)->binarysource_, \
|
|
|
|
((type *)(((char *)(object)) - offsetof(type, binarysource_))))
|
|
|
|
#define BinarySource_UPCAST(object) \
|
|
|
|
TYPECHECK((object)->binarysource_ == (BinarySource *)0, \
|
|
|
|
(object)->binarysource_)
|
|
|
|
#define BinarySource_COPIED(obj) \
|
|
|
|
((obj)->binarysource_->binarysource_ = (obj)->binarysource_)
|
|
|
|
|
|
|
|
#define get_data(src, len) \
|
|
|
|
BinarySource_get_data(BinarySource_UPCAST(src), len)
|
|
|
|
#define get_byte(src) \
|
|
|
|
BinarySource_get_byte(BinarySource_UPCAST(src))
|
|
|
|
#define get_bool(src) \
|
|
|
|
BinarySource_get_bool(BinarySource_UPCAST(src))
|
|
|
|
#define get_uint16(src) \
|
|
|
|
BinarySource_get_uint16(BinarySource_UPCAST(src))
|
|
|
|
#define get_uint32(src) \
|
|
|
|
BinarySource_get_uint32(BinarySource_UPCAST(src))
|
|
|
|
#define get_uint64(src) \
|
|
|
|
BinarySource_get_uint64(BinarySource_UPCAST(src))
|
|
|
|
#define get_string(src) \
|
|
|
|
BinarySource_get_string(BinarySource_UPCAST(src))
|
|
|
|
#define get_asciz(src) \
|
|
|
|
BinarySource_get_asciz(BinarySource_UPCAST(src))
|
|
|
|
#define get_pstring(src) \
|
|
|
|
BinarySource_get_pstring(BinarySource_UPCAST(src))
|
|
|
|
#define get_mp_ssh1(src) \
|
|
|
|
BinarySource_get_mp_ssh1(BinarySource_UPCAST(src))
|
|
|
|
#define get_mp_ssh2(src) \
|
|
|
|
BinarySource_get_mp_ssh2(BinarySource_UPCAST(src))
|
2018-06-03 10:23:07 +03:00
|
|
|
#define get_rsa_ssh1_pub(src, rsa, order) \
|
|
|
|
BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order)
|
2018-05-27 23:51:36 +03:00
|
|
|
#define get_rsa_ssh1_priv(src, rsa) \
|
|
|
|
BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa)
|
Introduce a centralised unmarshaller, 'BinarySource'.
This is the companion to the BinarySink system I introduced a couple
of weeks ago, and provides the same type-genericity which will let me
use the same get_* routines on an SSH packet, an SFTP packet or
anything else that chooses to include an implementing substructure.
However, unlike BinarySink which contained a (one-function) vtable,
BinarySource contains only mutable data fields - so another thing you
might very well want to do is to simply instantiate a bare one without
any containing object at all. I couldn't quite coerce C into letting
me use the same setup macro in both cases, so I've arranged a
BinarySource_INIT you can use on larger implementing objects and a
BinarySource_BARE_INIT you can use on a BinarySource not contained in
anything.
The API follows the general principle that even if decoding fails, the
decode functions will always return _some_ kind of value, with the
same dynamically-allocated-ness they would have used for a completely
successful value. But they also set an error flag in the BinarySource
which can be tested later. So instead of having to decode a 10-field
packet by means of 10 separate 'if (!get_foo(src)) throw error'
clauses, you can just write 10 'variable = get_foo(src)' statements
followed by a single check of get_err(src), and if the error check
fails, you have to do exactly the same set of frees you would have
after a successful decode.
2018-06-02 10:25:19 +03:00
|
|
|
|
|
|
|
#define get_err(src) (BinarySource_UPCAST(src)->err)
|
|
|
|
#define get_avail(src) (BinarySource_UPCAST(src)->len - \
|
|
|
|
BinarySource_UPCAST(src)->pos)
|
|
|
|
#define get_ptr(src) \
|
|
|
|
((const void *)( \
|
|
|
|
(const unsigned char *)(BinarySource_UPCAST(src)->data) + \
|
|
|
|
BinarySource_UPCAST(src)->pos))
|
|
|
|
|
|
|
|
ptrlen BinarySource_get_data(BinarySource *, size_t);
|
|
|
|
unsigned char BinarySource_get_byte(BinarySource *);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool BinarySource_get_bool(BinarySource *);
|
Introduce a centralised unmarshaller, 'BinarySource'.
This is the companion to the BinarySink system I introduced a couple
of weeks ago, and provides the same type-genericity which will let me
use the same get_* routines on an SSH packet, an SFTP packet or
anything else that chooses to include an implementing substructure.
However, unlike BinarySink which contained a (one-function) vtable,
BinarySource contains only mutable data fields - so another thing you
might very well want to do is to simply instantiate a bare one without
any containing object at all. I couldn't quite coerce C into letting
me use the same setup macro in both cases, so I've arranged a
BinarySource_INIT you can use on larger implementing objects and a
BinarySource_BARE_INIT you can use on a BinarySource not contained in
anything.
The API follows the general principle that even if decoding fails, the
decode functions will always return _some_ kind of value, with the
same dynamically-allocated-ness they would have used for a completely
successful value. But they also set an error flag in the BinarySource
which can be tested later. So instead of having to decode a 10-field
packet by means of 10 separate 'if (!get_foo(src)) throw error'
clauses, you can just write 10 'variable = get_foo(src)' statements
followed by a single check of get_err(src), and if the error check
fails, you have to do exactly the same set of frees you would have
after a successful decode.
2018-06-02 10:25:19 +03:00
|
|
|
unsigned BinarySource_get_uint16(BinarySource *);
|
|
|
|
unsigned long BinarySource_get_uint32(BinarySource *);
|
2018-10-27 01:08:58 +03:00
|
|
|
uint64_t BinarySource_get_uint64(BinarySource *);
|
Introduce a centralised unmarshaller, 'BinarySource'.
This is the companion to the BinarySink system I introduced a couple
of weeks ago, and provides the same type-genericity which will let me
use the same get_* routines on an SSH packet, an SFTP packet or
anything else that chooses to include an implementing substructure.
However, unlike BinarySink which contained a (one-function) vtable,
BinarySource contains only mutable data fields - so another thing you
might very well want to do is to simply instantiate a bare one without
any containing object at all. I couldn't quite coerce C into letting
me use the same setup macro in both cases, so I've arranged a
BinarySource_INIT you can use on larger implementing objects and a
BinarySource_BARE_INIT you can use on a BinarySource not contained in
anything.
The API follows the general principle that even if decoding fails, the
decode functions will always return _some_ kind of value, with the
same dynamically-allocated-ness they would have used for a completely
successful value. But they also set an error flag in the BinarySource
which can be tested later. So instead of having to decode a 10-field
packet by means of 10 separate 'if (!get_foo(src)) throw error'
clauses, you can just write 10 'variable = get_foo(src)' statements
followed by a single check of get_err(src), and if the error check
fails, you have to do exactly the same set of frees you would have
after a successful decode.
2018-06-02 10:25:19 +03:00
|
|
|
ptrlen BinarySource_get_string(BinarySource *);
|
|
|
|
const char *BinarySource_get_asciz(BinarySource *);
|
|
|
|
ptrlen BinarySource_get_pstring(BinarySource *);
|
Complete rewrite of PuTTY's bignum library.
The old 'Bignum' data type is gone completely, and so is sshbn.c. In
its place is a new thing called 'mp_int', handled by an entirely new
library module mpint.c, with API differences both large and small.
The main aim of this change is that the new library should be free of
timing- and cache-related side channels. I've written the code so that
it _should_ - assuming I haven't made any mistakes - do all of its
work without either control flow or memory addressing depending on the
data words of the input numbers. (Though, being an _arbitrary_
precision library, it does have to at least depend on the sizes of the
numbers - but there's a 'formal' size that can vary separately from
the actual magnitude of the represented integer, so if you want to
keep it secret that your number is actually small, it should work fine
to have a very long mp_int and just happen to store 23 in it.) So I've
done all my conditionalisation by means of computing both answers and
doing bit-masking to swap the right one into place, and all loops over
the words of an mp_int go up to the formal size rather than the actual
size.
I haven't actually tested the constant-time property in any rigorous
way yet (I'm still considering the best way to do it). But this code
is surely at the very least a big improvement on the old version, even
if I later find a few more things to fix.
I've also completely rewritten the low-level elliptic curve arithmetic
from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c
than it is to the SSH end of the code. The new elliptic curve code
keeps all coordinates in Montgomery-multiplication transformed form to
speed up all the multiplications mod the same prime, and only converts
them back when you ask for the affine coordinates. Also, I adopted
extended coordinates for the Edwards curve implementation.
sshecc.c has also had a near-total rewrite in the course of switching
it over to the new system. While I was there, I've separated ECDSA and
EdDSA more completely - they now have separate vtables, instead of a
single vtable in which nearly every function had a big if statement in
it - and also made the externally exposed types for an ECDSA key and
an ECDH context different.
A minor new feature: since the new arithmetic code includes a modular
square root function, we can now support the compressed point
representation for the NIST curves. We seem to have been getting along
fine without that so far, but it seemed a shame not to put it in,
since it was suddenly easy.
In sshrsa.c, one major change is that I've removed the RSA blinding
step in rsa_privkey_op, in which we randomise the ciphertext before
doing the decryption. The purpose of that was to avoid timing leaks
giving away the plaintext - but the new arithmetic code should take
that in its stride in the course of also being careful enough to avoid
leaking the _private key_, which RSA blinding had no way to do
anything about in any case.
Apart from those specific points, most of the rest of the changes are
more or less mechanical, just changing type names and translating code
into the new API.
2018-12-31 16:53:41 +03:00
|
|
|
mp_int *BinarySource_get_mp_ssh1(BinarySource *src);
|
|
|
|
mp_int *BinarySource_get_mp_ssh2(BinarySource *src);
|
Introduce a centralised unmarshaller, 'BinarySource'.
This is the companion to the BinarySink system I introduced a couple
of weeks ago, and provides the same type-genericity which will let me
use the same get_* routines on an SSH packet, an SFTP packet or
anything else that chooses to include an implementing substructure.
However, unlike BinarySink which contained a (one-function) vtable,
BinarySource contains only mutable data fields - so another thing you
might very well want to do is to simply instantiate a bare one without
any containing object at all. I couldn't quite coerce C into letting
me use the same setup macro in both cases, so I've arranged a
BinarySource_INIT you can use on larger implementing objects and a
BinarySource_BARE_INIT you can use on a BinarySource not contained in
anything.
The API follows the general principle that even if decoding fails, the
decode functions will always return _some_ kind of value, with the
same dynamically-allocated-ness they would have used for a completely
successful value. But they also set an error flag in the BinarySource
which can be tested later. So instead of having to decode a 10-field
packet by means of 10 separate 'if (!get_foo(src)) throw error'
clauses, you can just write 10 'variable = get_foo(src)' statements
followed by a single check of get_err(src), and if the error check
fails, you have to do exactly the same set of frees you would have
after a successful decode.
2018-06-02 10:25:19 +03:00
|
|
|
|
2018-05-24 11:17:13 +03:00
|
|
|
#endif /* PUTTY_MARSHAL_H */
|