mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
25b034ee39
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.
276 lines
7.4 KiB
C
276 lines
7.4 KiB
C
/*
|
|
* Diffie-Hellman implementation for PuTTY.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "ssh.h"
|
|
#include "misc.h"
|
|
#include "mpint.h"
|
|
|
|
struct dh_ctx {
|
|
mp_int *x, *e, *p, *q, *g;
|
|
};
|
|
|
|
struct dh_extra {
|
|
bool gex;
|
|
void (*construct)(struct dh_ctx *ctx);
|
|
};
|
|
|
|
static void dh_group1_construct(struct dh_ctx *ctx)
|
|
{
|
|
ctx->p = MP_LITERAL(0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF);
|
|
ctx->g = mp_from_integer(2);
|
|
}
|
|
|
|
static void dh_group14_construct(struct dh_ctx *ctx)
|
|
{
|
|
ctx->p = MP_LITERAL(0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF);
|
|
ctx->g = mp_from_integer(2);
|
|
}
|
|
|
|
static const struct dh_extra extra_group1 = {
|
|
false, dh_group1_construct,
|
|
};
|
|
|
|
static const struct ssh_kex ssh_diffiehellman_group1_sha1 = {
|
|
"diffie-hellman-group1-sha1", "group1",
|
|
KEXTYPE_DH, &ssh_sha1, &extra_group1,
|
|
};
|
|
|
|
static const struct ssh_kex *const group1_list[] = {
|
|
&ssh_diffiehellman_group1_sha1
|
|
};
|
|
|
|
const struct ssh_kexes ssh_diffiehellman_group1 = {
|
|
sizeof(group1_list) / sizeof(*group1_list),
|
|
group1_list
|
|
};
|
|
|
|
static const struct dh_extra extra_group14 = {
|
|
false, dh_group14_construct,
|
|
};
|
|
|
|
static const struct ssh_kex ssh_diffiehellman_group14_sha256 = {
|
|
"diffie-hellman-group14-sha256", "group14",
|
|
KEXTYPE_DH, &ssh_sha256, &extra_group14,
|
|
};
|
|
|
|
static const struct ssh_kex ssh_diffiehellman_group14_sha1 = {
|
|
"diffie-hellman-group14-sha1", "group14",
|
|
KEXTYPE_DH, &ssh_sha1, &extra_group14,
|
|
};
|
|
|
|
static const struct ssh_kex *const group14_list[] = {
|
|
&ssh_diffiehellman_group14_sha256,
|
|
&ssh_diffiehellman_group14_sha1
|
|
};
|
|
|
|
const struct ssh_kexes ssh_diffiehellman_group14 = {
|
|
sizeof(group14_list) / sizeof(*group14_list),
|
|
group14_list
|
|
};
|
|
|
|
static const struct dh_extra extra_gex = { true };
|
|
|
|
static const struct ssh_kex ssh_diffiehellman_gex_sha256 = {
|
|
"diffie-hellman-group-exchange-sha256", NULL,
|
|
KEXTYPE_DH, &ssh_sha256, &extra_gex,
|
|
};
|
|
|
|
static const struct ssh_kex ssh_diffiehellman_gex_sha1 = {
|
|
"diffie-hellman-group-exchange-sha1", NULL,
|
|
KEXTYPE_DH, &ssh_sha1, &extra_gex,
|
|
};
|
|
|
|
static const struct ssh_kex *const gex_list[] = {
|
|
&ssh_diffiehellman_gex_sha256,
|
|
&ssh_diffiehellman_gex_sha1
|
|
};
|
|
|
|
const struct ssh_kexes ssh_diffiehellman_gex = {
|
|
sizeof(gex_list) / sizeof(*gex_list),
|
|
gex_list
|
|
};
|
|
|
|
/*
|
|
* Suffix on GSSAPI SSH protocol identifiers that indicates Kerberos 5
|
|
* as the mechanism.
|
|
*
|
|
* This suffix is the base64-encoded MD5 hash of the byte sequence
|
|
* 06 09 2A 86 48 86 F7 12 01 02 02, which in turn is the ASN.1 DER
|
|
* encoding of the object ID 1.2.840.113554.1.2.2 which designates
|
|
* Kerberos v5.
|
|
*
|
|
* (The same encoded OID, minus the two-byte DER header, is defined in
|
|
* pgssapi.c as GSS_MECH_KRB5.)
|
|
*/
|
|
#define GSS_KRB5_OID_HASH "toWM5Slw5Ew8Mqkay+al2g=="
|
|
|
|
static const struct ssh_kex ssh_gssk5_diffiehellman_gex_sha1 = {
|
|
"gss-gex-sha1-" GSS_KRB5_OID_HASH, NULL,
|
|
KEXTYPE_GSS, &ssh_sha1, &extra_gex,
|
|
};
|
|
|
|
static const struct ssh_kex ssh_gssk5_diffiehellman_group14_sha1 = {
|
|
"gss-group14-sha1-" GSS_KRB5_OID_HASH, "group14",
|
|
KEXTYPE_GSS, &ssh_sha1, &extra_group14,
|
|
};
|
|
|
|
static const struct ssh_kex ssh_gssk5_diffiehellman_group1_sha1 = {
|
|
"gss-group1-sha1-" GSS_KRB5_OID_HASH, "group1",
|
|
KEXTYPE_GSS, &ssh_sha1, &extra_group1,
|
|
};
|
|
|
|
static const struct ssh_kex *const gssk5_sha1_kex_list[] = {
|
|
&ssh_gssk5_diffiehellman_gex_sha1,
|
|
&ssh_gssk5_diffiehellman_group14_sha1,
|
|
&ssh_gssk5_diffiehellman_group1_sha1
|
|
};
|
|
|
|
const struct ssh_kexes ssh_gssk5_sha1_kex = {
|
|
sizeof(gssk5_sha1_kex_list) / sizeof(*gssk5_sha1_kex_list),
|
|
gssk5_sha1_kex_list
|
|
};
|
|
|
|
/*
|
|
* Common DH initialisation.
|
|
*/
|
|
static void dh_init(struct dh_ctx *ctx)
|
|
{
|
|
ctx->q = mp_rshift_fixed(ctx->p, 1);
|
|
ctx->x = ctx->e = NULL;
|
|
}
|
|
|
|
bool dh_is_gex(const struct ssh_kex *kex)
|
|
{
|
|
const struct dh_extra *extra = (const struct dh_extra *)kex->extra;
|
|
return extra->gex;
|
|
}
|
|
|
|
/*
|
|
* Initialise DH for a standard group.
|
|
*/
|
|
struct dh_ctx *dh_setup_group(const struct ssh_kex *kex)
|
|
{
|
|
const struct dh_extra *extra = (const struct dh_extra *)kex->extra;
|
|
assert(!extra->gex);
|
|
struct dh_ctx *ctx = snew(struct dh_ctx);
|
|
extra->construct(ctx);
|
|
dh_init(ctx);
|
|
return ctx;
|
|
}
|
|
|
|
/*
|
|
* Initialise DH for a server-supplied group.
|
|
*/
|
|
struct dh_ctx *dh_setup_gex(mp_int *pval, mp_int *gval)
|
|
{
|
|
struct dh_ctx *ctx = snew(struct dh_ctx);
|
|
ctx->p = mp_copy(pval);
|
|
ctx->g = mp_copy(gval);
|
|
dh_init(ctx);
|
|
return ctx;
|
|
}
|
|
|
|
/*
|
|
* Return size of DH modulus p.
|
|
*/
|
|
int dh_modulus_bit_size(const struct dh_ctx *ctx)
|
|
{
|
|
return mp_get_nbits(ctx->p);
|
|
}
|
|
|
|
/*
|
|
* Clean up and free a context.
|
|
*/
|
|
void dh_cleanup(struct dh_ctx *ctx)
|
|
{
|
|
mp_free(ctx->x);
|
|
mp_free(ctx->e);
|
|
mp_free(ctx->p);
|
|
mp_free(ctx->g);
|
|
mp_free(ctx->q);
|
|
sfree(ctx);
|
|
}
|
|
|
|
/*
|
|
* DH stage 1: invent a number x between 1 and q, and compute e =
|
|
* g^x mod p. Return e.
|
|
*
|
|
* If `nbits' is greater than zero, it is used as an upper limit
|
|
* for the number of bits in x. This is safe provided that (a) you
|
|
* use twice as many bits in x as the number of bits you expect to
|
|
* use in your session key, and (b) the DH group is a safe prime
|
|
* (which SSH demands that it must be).
|
|
*
|
|
* P. C. van Oorschot, M. J. Wiener
|
|
* "On Diffie-Hellman Key Agreement with Short Exponents".
|
|
* Advances in Cryptology: Proceedings of Eurocrypt '96
|
|
* Springer-Verlag, May 1996.
|
|
*/
|
|
mp_int *dh_create_e(struct dh_ctx *ctx, int nbits)
|
|
{
|
|
/*
|
|
* Lower limit is just 2.
|
|
*/
|
|
mp_int *lo = mp_from_integer(2);
|
|
|
|
/*
|
|
* Upper limit.
|
|
*/
|
|
mp_int *hi = mp_copy(ctx->q);
|
|
mp_sub_integer_into(hi, hi, 1);
|
|
if (nbits) {
|
|
mp_int *pow2 = mp_power_2(nbits+1);
|
|
mp_min_into(pow2, pow2, hi);
|
|
mp_free(hi);
|
|
hi = pow2;
|
|
}
|
|
|
|
/*
|
|
* Make a random number in that range.
|
|
*/
|
|
ctx->x = mp_random_in_range(lo, hi);
|
|
mp_free(lo);
|
|
mp_free(hi);
|
|
|
|
/*
|
|
* Now compute e = g^x mod p.
|
|
*/
|
|
ctx->e = mp_modpow(ctx->g, ctx->x, ctx->p);
|
|
|
|
return ctx->e;
|
|
}
|
|
|
|
/*
|
|
* DH stage 2-epsilon: given a number f, validate it to ensure it's in
|
|
* range. (RFC 4253 section 8: "Values of 'e' or 'f' that are not in
|
|
* the range [1, p-1] MUST NOT be sent or accepted by either side."
|
|
* Also, we rule out 1 and p-1 too, since that's easy to do and since
|
|
* they lead to obviously weak keys that even a passive eavesdropper
|
|
* can figure out.)
|
|
*/
|
|
const char *dh_validate_f(struct dh_ctx *ctx, mp_int *f)
|
|
{
|
|
if (!mp_hs_integer(f, 2)) {
|
|
return "f value received is too small";
|
|
} else {
|
|
mp_int *pm1 = mp_copy(ctx->p);
|
|
mp_sub_integer_into(pm1, pm1, 1);
|
|
unsigned cmp = mp_cmp_hs(f, pm1);
|
|
mp_free(pm1);
|
|
if (cmp)
|
|
return "f value received is too large";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* DH stage 2: given a number f, compute K = f^x mod p.
|
|
*/
|
|
mp_int *dh_find_K(struct dh_ctx *ctx, mp_int *f)
|
|
{
|
|
return mp_modpow(f, ctx->x, ctx->p);
|
|
}
|