1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-25 01:02:24 +00:00

Clean up ssh_keyalg APIs and implementations.

Quite a few of the function pointers in the ssh_keyalg vtable now take
ptrlen arguments in place of separate pointer and length pairs.
Meanwhile, the various key types' implementations of those functions
now work by initialising a BinarySource with the input ptrlen and
using the new decode functions to walk along it.

One exception is the openssh_createkey method which reads a private
key in the wire format used by OpenSSH's SSH-2 agent protocol, which
has to consume a prefix of a larger data stream, and tell the caller
how much of that data was the private key. That function now takes an
actual BinarySource, and passes that directly to the decode functions,
so that on return the caller finds that the BinarySource's read
pointer has been advanced exactly past the private key.

This let me throw away _several_ reimplementations of mpint-reading
functions, one in each of sshrsa, sshdss.c and sshecc.c. Worse still,
they didn't all have exactly the SSH-2 semantics, because the thing in
sshrsa.c whose name suggested it was an mpint-reading function
actually tolerated the wrong number of leading zero bytes, which it
had to be able to do to cope with the "ssh-rsa" signature format which
contains a thing that isn't quite an SSH-2 mpint. Now that deviation
is clearly commented!
This commit is contained in:
Simon Tatham 2018-05-31 18:40:51 +01:00
parent 5be57af173
commit ae3edcdfc0
9 changed files with 192 additions and 378 deletions

View File

@ -857,9 +857,8 @@ int main(int argc, char **argv)
&origcomment, &error)) { &origcomment, &error)) {
ssh2algf = find_pubkey_alg(ssh2alg); ssh2algf = find_pubkey_alg(ssh2alg);
if (ssh2algf) if (ssh2algf)
bits = ssh2algf->pubkey_bits(ssh2algf, bits = ssh2algf->pubkey_bits(
ssh2blob->s, ssh2algf, make_ptrlen(ssh2blob->s, ssh2blob->len));
ssh2blob->len);
else else
bits = -1; bits = -1;
} else { } else {

View File

@ -670,8 +670,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
put_mp_ssh2_from_string(blob, privkey.data.ptr, privkey.data.len); put_mp_ssh2_from_string(blob, privkey.data.ptr, privkey.data.len);
retkey->data = retkey->alg->createkey( retkey->data = retkey->alg->createkey(
retkey->alg, blob->u, publen, retkey->alg, make_ptrlen(blob->u, publen),
blob->u + publen, blob->len - publen); make_ptrlen(blob->u + publen, blob->len - publen));
if (!retkey->data) { if (!retkey->data) {
sfree(retkey); sfree(retkey);
@ -742,7 +742,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
retkey = snew(struct ssh2_userkey); retkey = snew(struct ssh2_userkey);
retkey->alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss); retkey->alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
retkey->data = retkey->alg->createkey( retkey->data = retkey->alg->createkey(
retkey->alg, blob->u, privptr, blob->u+privptr, blob->len-privptr); retkey->alg, make_ptrlen(blob->u, privptr),
make_ptrlen(blob->u+privptr, blob->len-privptr));
if (!retkey->data) { if (!retkey->data) {
sfree(retkey); sfree(retkey);
@ -1466,13 +1467,13 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
thiskey.len = (const char *)get_ptr(src) - (const char *)thiskey.ptr; thiskey.len = (const char *)get_ptr(src) - (const char *)thiskey.ptr;
if (key_index == key->key_wanted) { if (key_index == key->key_wanted) {
const unsigned char *blobptr = thiskey.ptr; BinarySource src[1];
int bloblen = thiskey.len; BinarySource_BARE_INIT(src, thiskey.ptr, thiskey.len);
retkey = snew(struct ssh2_userkey); retkey = snew(struct ssh2_userkey);
retkey->comment = NULL; retkey->comment = NULL;
retkey->alg = alg; retkey->alg = alg;
retkey->data = alg->openssh_createkey(alg, &blobptr, &bloblen); retkey->data = alg->openssh_createkey(alg, src);
if (!retkey->data) { if (!retkey->data) {
errmsg = "unable to create key data structure"; errmsg = "unable to create key data structure";
goto error; goto error;
@ -2173,8 +2174,9 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
retkey = snew(struct ssh2_userkey); retkey = snew(struct ssh2_userkey);
retkey->alg = alg; retkey->alg = alg;
retkey->data = alg->createkey(alg, blob->u, publen, retkey->data = alg->createkey(
blob->u + publen, blob->len - publen); alg, make_ptrlen(blob->u, publen),
make_ptrlen(blob->u + publen, blob->len - publen));
if (!retkey->data) { if (!retkey->data) {
sfree(retkey); sfree(retkey);
errmsg = "unable to create key data structure"; errmsg = "unable to create key data structure";

View File

@ -430,14 +430,7 @@ void pageant_handle_msg(BinarySink *bs,
goto add2_cleanup; goto add2_cleanup;
} }
{ key->data = key->alg->openssh_createkey(key->alg, msg);
const unsigned char *p = get_ptr(msg);
int len = get_avail(msg);
key->data = key->alg->openssh_createkey(key->alg, &p, &len);
assert(len >= 0);
assert(len < get_avail(msg));
msg->pos += get_avail(msg) - len;
}
if (!key->data) { if (!key->data) {
pageant_failure_msg(bs, "key setup failed", logctx, logfn); pageant_failure_msg(bs, "key setup failed", logctx, logfn);

18
ssh.c
View File

@ -7192,8 +7192,7 @@ static void do_ssh2_transport(void *vctx)
} }
set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */ set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
s->hostkeydata = get_string(pktin); s->hostkeydata = get_string(pktin);
s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
s->hostkeydata.ptr, s->hostkeydata.len);
s->f = get_mp_ssh2(pktin); s->f = get_mp_ssh2(pktin);
s->sigdata = get_string(pktin); s->sigdata = get_string(pktin);
if (get_err(pktin)) { if (get_err(pktin)) {
@ -7264,8 +7263,7 @@ static void do_ssh2_transport(void *vctx)
s->hostkeydata = get_string(pktin); s->hostkeydata = get_string(pktin);
put_stringpl(ssh->exhash_bs, s->hostkeydata); put_stringpl(ssh->exhash_bs, s->hostkeydata);
s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
s->hostkeydata.ptr, s->hostkeydata.len);
{ {
strbuf *pubpoint = strbuf_new(); strbuf *pubpoint = strbuf_new();
@ -7469,8 +7467,7 @@ static void do_ssh2_transport(void *vctx)
s->hostkeydata = get_string(pktin); s->hostkeydata = get_string(pktin);
if (ssh->hostkey) { if (ssh->hostkey) {
s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hkey = ssh->hostkey->newkey(ssh->hostkey,
s->hostkeydata.ptr, s->hostkeydata);
s->hostkeydata.len);
put_string(ssh->exhash_bs, put_string(ssh->exhash_bs,
s->hostkeydata.ptr, s->hostkeydata.len); s->hostkeydata.ptr, s->hostkeydata.len);
} }
@ -7564,8 +7561,7 @@ static void do_ssh2_transport(void *vctx)
s->hostkeydata = get_string(pktin); s->hostkeydata = get_string(pktin);
put_stringpl(ssh->exhash_bs, s->hostkeydata); put_stringpl(ssh->exhash_bs, s->hostkeydata);
s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata);
s->hostkeydata.ptr, s->hostkeydata.len);
rsakeydata = get_string(pktin); rsakeydata = get_string(pktin);
@ -7704,9 +7700,9 @@ static void do_ssh2_transport(void *vctx)
crStopV; crStopV;
} }
if (!ssh->hostkey->verifysig(s->hkey, s->sigdata.ptr, s->sigdata.len, if (!ssh->hostkey->verifysig(
s->exchange_hash, s->hkey, s->sigdata,
ssh->kex->hash->hlen)) { make_ptrlen(s->exchange_hash, ssh->kex->hash->hlen))) {
#ifndef FUZZING #ifndef FUZZING
bombout(("Server's host key did not match the signature " bombout(("Server's host key did not match the signature "
"supplied")); "supplied"));

16
ssh.h
View File

@ -394,17 +394,13 @@ struct ssh_kexes {
}; };
struct ssh_keyalg { struct ssh_keyalg {
ssh_key *(*newkey) (const ssh_keyalg *self, ssh_key *(*newkey) (const ssh_keyalg *self, ptrlen data);
const void *data, int len);
void (*freekey) (ssh_key *key); void (*freekey) (ssh_key *key);
char *(*fmtkey) (ssh_key *key); char *(*fmtkey) (ssh_key *key);
void (*public_blob)(ssh_key *key, BinarySink *); void (*public_blob)(ssh_key *key, BinarySink *);
void (*private_blob)(ssh_key *key, BinarySink *); void (*private_blob)(ssh_key *key, BinarySink *);
ssh_key *(*createkey) (const ssh_keyalg *self, ssh_key *(*createkey) (const ssh_keyalg *self, ptrlen pub, ptrlen priv);
const void *pub_blob, int pub_len, ssh_key *(*openssh_createkey) (const ssh_keyalg *self, BinarySource *);
const void *priv_blob, int priv_len);
ssh_key *(*openssh_createkey) (const ssh_keyalg *self,
const unsigned char **blob, int *len);
void (*openssh_fmtkey) (ssh_key *key, BinarySink *); void (*openssh_fmtkey) (ssh_key *key, BinarySink *);
/* OpenSSH private key blobs, as created by openssh_fmtkey and /* OpenSSH private key blobs, as created by openssh_fmtkey and
* consumed by openssh_createkey, always (at least so far...) take * consumed by openssh_createkey, always (at least so far...) take
@ -415,10 +411,8 @@ struct ssh_keyalg {
* skip over the right number to find the next key in the file. * skip over the right number to find the next key in the file.
* openssh_private_npieces gives that information. */ * openssh_private_npieces gives that information. */
int openssh_private_npieces; int openssh_private_npieces;
int (*pubkey_bits) (const ssh_keyalg *self, int (*pubkey_bits) (const ssh_keyalg *self, ptrlen blob);
const void *blob, int len); int (*verifysig) (ssh_key *key, ptrlen sig, ptrlen data);
int (*verifysig) (ssh_key *key, const void *sig, int siglen,
const void *data, int datalen);
void (*sign) (ssh_key *key, const void *data, int datalen, BinarySink *); void (*sign) (ssh_key *key, const void *data, int datalen, BinarySink *);
const char *name; const char *name;
const char *keytype; /* for host key cache */ const char *keytype; /* for host key cache */

171
sshdss.c
View File

@ -9,86 +9,25 @@
#include "ssh.h" #include "ssh.h"
#include "misc.h" #include "misc.h"
static void getstring(const char **data, int *datalen,
const char **p, int *length)
{
*p = NULL;
if (*datalen < 4)
return;
*length = toint(GET_32BIT(*data));
if (*length < 0)
return;
*datalen -= 4;
*data += 4;
if (*datalen < *length)
return;
*p = *data;
*data += *length;
*datalen -= *length;
}
static Bignum getmp(const char **data, int *datalen)
{
const char *p;
int length;
Bignum b;
getstring(data, datalen, &p, &length);
if (!p)
return NULL;
if (p[0] & 0x80)
return NULL; /* negative mp */
b = bignum_from_bytes(p, length);
return b;
}
static Bignum get160(const char **data, int *datalen)
{
Bignum b;
if (*datalen < 20)
return NULL;
b = bignum_from_bytes(*data, 20);
*data += 20;
*datalen -= 20;
return b;
}
static void dss_freekey(ssh_key *key); /* forward reference */ static void dss_freekey(ssh_key *key); /* forward reference */
static ssh_key *dss_newkey(const ssh_keyalg *self, static ssh_key *dss_newkey(const ssh_keyalg *self, ptrlen data)
const void *vdata, int len)
{ {
const char *data = (const char *)vdata; BinarySource src[1];
const char *p;
int slen;
struct dss_key *dss; struct dss_key *dss;
dss = snew(struct dss_key); BinarySource_BARE_INIT(src, data.ptr, data.len);
getstring(&data, &len, &p, &slen); if (!ptrlen_eq_string(get_string(src), "ssh-dss"))
#ifdef DEBUG_DSS
{
int i;
printf("key:");
for (i = 0; i < len; i++)
printf(" %02x", (unsigned char) (data[i]));
printf("\n");
}
#endif
if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {
sfree(dss);
return NULL; return NULL;
}
dss->p = getmp(&data, &len); dss = snew(struct dss_key);
dss->q = getmp(&data, &len); dss->p = get_mp_ssh2(src);
dss->g = getmp(&data, &len); dss->q = get_mp_ssh2(src);
dss->y = getmp(&data, &len); dss->g = get_mp_ssh2(src);
dss->y = get_mp_ssh2(src);
dss->x = NULL; dss->x = NULL;
if (!dss->p || !dss->q || !dss->g || !dss->y || if (get_err(src) ||
!bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) { !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) {
/* Invalid key. */ /* Invalid key. */
dss_freekey(&dss->sshk); dss_freekey(&dss->sshk);
@ -164,29 +103,19 @@ static char *dss_fmtkey(ssh_key *key)
return p; return p;
} }
static int dss_verifysig(ssh_key *key, const void *vsig, int siglen, static int dss_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
const void *data, int datalen)
{ {
struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk); struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
const char *sig = (const char *)vsig; BinarySource src[1];
const char *p; unsigned char hash[20];
int slen;
char hash[20];
Bignum r, s, w, gu1p, yu2p, gu1yu2p, u1, u2, sha, v; Bignum r, s, w, gu1p, yu2p, gu1yu2p, u1, u2, sha, v;
int ret; int ret;
if (!dss->p) if (!dss->p)
return 0; return 0;
#ifdef DEBUG_DSS BinarySource_BARE_INIT(src, sig.ptr, sig.len);
{
int i;
printf("sig:");
for (i = 0; i < siglen; i++)
printf(" %02x", (unsigned char) (sig[i]));
printf("\n");
}
#endif
/* /*
* Commercial SSH (2.0.13) and OpenSSH disagree over the format * Commercial SSH (2.0.13) and OpenSSH disagree over the format
* of a DSA signature. OpenSSH is in line with RFC 4253: * of a DSA signature. OpenSSH is in line with RFC 4253:
@ -198,15 +127,18 @@ static int dss_verifysig(ssh_key *key, const void *vsig, int siglen,
* the length: length 40 means the commercial-SSH bug, anything * the length: length 40 means the commercial-SSH bug, anything
* else is assumed to be RFC-compliant. * else is assumed to be RFC-compliant.
*/ */
if (siglen != 40) { /* bug not present; read admin fields */ if (sig.len != 40) { /* bug not present; read admin fields */
getstring(&sig, &siglen, &p, &slen); ptrlen type = get_string(src);
if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) { sig = get_string(src);
return 0;
} if (get_err(src) || !ptrlen_eq_string(type, "ssh-dss") ||
sig += 4, siglen -= 4; /* skip yet another length field */ sig.len != 40)
return 0;
} }
r = get160(&sig, &siglen);
s = get160(&sig, &siglen); /* Now we're sitting on a 40-byte string for sure. */
r = bignum_from_bytes(sig.ptr, 20);
s = bignum_from_bytes((const char *)sig.ptr + 20, 20);
if (!r || !s) { if (!r || !s) {
if (r) if (r)
freebn(r); freebn(r);
@ -234,10 +166,8 @@ static int dss_verifysig(ssh_key *key, const void *vsig, int siglen,
/* /*
* Step 2. u1 <- SHA(message) * w mod q. * Step 2. u1 <- SHA(message) * w mod q.
*/ */
SHA_Simple(data, datalen, (unsigned char *)hash); SHA_Simple(data.ptr, data.len, hash);
p = hash; sha = bignum_from_bytes(hash, 20);
slen = 20;
sha = get160(&p, &slen);
u1 = modmul(sha, w, dss->q); u1 = modmul(sha, w, dss->q);
/* /*
@ -291,26 +221,24 @@ static void dss_private_blob(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, dss->x); put_mp_ssh2(bs, dss->x);
} }
static ssh_key *dss_createkey(const ssh_keyalg *self, static ssh_key *dss_createkey(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
const void *pub_blob, int pub_len,
const void *priv_blob, int priv_len)
{ {
BinarySource src[1];
ssh_key *sshk; ssh_key *sshk;
struct dss_key *dss; struct dss_key *dss;
const char *pb = (const char *) priv_blob; ptrlen hash;
const char *hash;
int hashlen;
SHA_State s; SHA_State s;
unsigned char digest[20]; unsigned char digest[20];
Bignum ytest; Bignum ytest;
sshk = dss_newkey(self, pub_blob, pub_len); sshk = dss_newkey(self, pub);
if (!sshk) if (!sshk)
return NULL; return NULL;
dss = FROMFIELD(sshk, struct dss_key, sshk); dss = FROMFIELD(sshk, struct dss_key, sshk);
dss->x = getmp(&pb, &priv_len); BinarySource_BARE_INIT(src, priv.ptr, priv.len);
if (!dss->x) { dss->x = get_mp_ssh2(src);
if (get_err(src)) {
dss_freekey(&dss->sshk); dss_freekey(&dss->sshk);
return NULL; return NULL;
} }
@ -318,15 +246,14 @@ static ssh_key *dss_createkey(const ssh_keyalg *self,
/* /*
* Check the obsolete hash in the old DSS key format. * Check the obsolete hash in the old DSS key format.
*/ */
hashlen = -1; hash = get_string(src);
getstring(&pb, &priv_len, &hash, &hashlen); if (hash.len == 20) {
if (hashlen == 20) {
SHA_Init(&s); SHA_Init(&s);
put_mp_ssh2(&s, dss->p); put_mp_ssh2(&s, dss->p);
put_mp_ssh2(&s, dss->q); put_mp_ssh2(&s, dss->q);
put_mp_ssh2(&s, dss->g); put_mp_ssh2(&s, dss->g);
SHA_Final(&s, digest); SHA_Final(&s, digest);
if (0 != memcmp(hash, digest, 20)) { if (0 != memcmp(hash.ptr, digest, 20)) {
dss_freekey(&dss->sshk); dss_freekey(&dss->sshk);
return NULL; return NULL;
} }
@ -347,20 +274,19 @@ static ssh_key *dss_createkey(const ssh_keyalg *self,
} }
static ssh_key *dss_openssh_createkey(const ssh_keyalg *self, static ssh_key *dss_openssh_createkey(const ssh_keyalg *self,
const unsigned char **blob, int *len) BinarySource *src)
{ {
const char **b = (const char **) blob;
struct dss_key *dss; struct dss_key *dss;
dss = snew(struct dss_key); dss = snew(struct dss_key);
dss->p = getmp(b, len); dss->p = get_mp_ssh2(src);
dss->q = getmp(b, len); dss->q = get_mp_ssh2(src);
dss->g = getmp(b, len); dss->g = get_mp_ssh2(src);
dss->y = getmp(b, len); dss->y = get_mp_ssh2(src);
dss->x = getmp(b, len); dss->x = get_mp_ssh2(src);
if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x || if (get_err(src) ||
!bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) { !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) {
/* Invalid key. */ /* Invalid key. */
dss_freekey(&dss->sshk); dss_freekey(&dss->sshk);
@ -381,14 +307,13 @@ static void dss_openssh_fmtkey(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, dss->x); put_mp_ssh2(bs, dss->x);
} }
static int dss_pubkey_bits(const ssh_keyalg *self, static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
const void *blob, int len)
{ {
ssh_key *sshk; ssh_key *sshk;
struct dss_key *dss; struct dss_key *dss;
int ret; int ret;
sshk = dss_newkey(self, blob, len); sshk = dss_newkey(self, pub);
if (!sshk) if (!sshk)
return -1; return -1;

189
sshecc.c
View File

@ -1590,47 +1590,12 @@ static void _ecdsa_sign(const Bignum privateKey, const struct ec_curve *curve,
* Misc functions * Misc functions
*/ */
static void getstring(const char **data, int *datalen, static Bignum BinarySource_get_mp_le(BinarySource *src)
const char **p, int *length)
{ {
*p = NULL; ptrlen mp_str = get_string(src);
if (*datalen < 4) return bignum_from_bytes_le(mp_str.ptr, mp_str.len);
return;
*length = toint(GET_32BIT(*data));
if (*length < 0)
return;
*datalen -= 4;
*data += 4;
if (*datalen < *length)
return;
*p = *data;
*data += *length;
*datalen -= *length;
}
static Bignum getmp(const char **data, int *datalen)
{
const char *p;
int length;
getstring(data, datalen, &p, &length);
if (!p)
return NULL;
if (p[0] & 0x80)
return NULL; /* negative mp */
return bignum_from_bytes(p, length);
}
static Bignum getmp_le(const char **data, int *datalen)
{
const char *p;
int length;
getstring(data, datalen, &p, &length);
if (!p)
return NULL;
return bignum_from_bytes_le(p, length);
} }
#define get_mp_le(src) BinarySource_get_mp_le(BinarySource_UPCAST(src))
static int decodepoint_ed(const char *p, int length, struct ec_point *point) static int decodepoint_ed(const char *p, int length, struct ec_point *point)
{ {
@ -1709,15 +1674,13 @@ static int decodepoint(const char *p, int length, struct ec_point *point)
return 1; return 1;
} }
static int getmppoint(const char **data, int *datalen, struct ec_point *point) static int BinarySource_get_point(BinarySource *src, struct ec_point *point)
{ {
const char *p; ptrlen str = get_string(src);
int length; if (get_err(src)) return 0;
return decodepoint(str.ptr, str.len, point);
getstring(data, datalen, &p, &length);
if (!p) return 0;
return decodepoint(p, length, point);
} }
#define get_point(src, pt) BinarySource_get_point(BinarySource_UPCAST(src), pt)
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
* Exposed ECDSA interface * Exposed ECDSA interface
@ -1750,30 +1713,24 @@ static void ecdsa_freekey(ssh_key *key)
sfree(ec); sfree(ec);
} }
static ssh_key *ecdsa_newkey(const ssh_keyalg *self, static ssh_key *ecdsa_newkey(const ssh_keyalg *self, ptrlen data)
const void *vdata, int len)
{ {
const struct ecsign_extra *extra = const struct ecsign_extra *extra =
(const struct ecsign_extra *)self->extra; (const struct ecsign_extra *)self->extra;
const char *data = (const char *)vdata; BinarySource src[1];
const char *p;
int slen;
struct ec_key *ec; struct ec_key *ec;
struct ec_curve *curve; struct ec_curve *curve;
getstring(&data, &len, &p, &slen); BinarySource_BARE_INIT(src, data.ptr, data.len);
get_string(src);
if (!p) {
return NULL;
}
curve = extra->curve(); curve = extra->curve();
assert(curve->type == EC_WEIERSTRASS || curve->type == EC_EDWARDS); assert(curve->type == EC_WEIERSTRASS || curve->type == EC_EDWARDS);
/* Curve name is duplicated for Weierstrass form */ /* Curve name is duplicated for Weierstrass form */
if (curve->type == EC_WEIERSTRASS) { if (curve->type == EC_WEIERSTRASS) {
getstring(&data, &len, &p, &slen); if (!ptrlen_eq_string(get_string(src), curve->name))
if (!p) return NULL; return NULL;
if (!match_ssh_id(slen, p, curve->name)) return NULL;
} }
ec = snew(struct ec_key); ec = snew(struct ec_key);
@ -1785,7 +1742,7 @@ static ssh_key *ecdsa_newkey(const ssh_keyalg *self,
ec->publicKey.y = NULL; ec->publicKey.y = NULL;
ec->publicKey.z = NULL; ec->publicKey.z = NULL;
ec->privateKey = NULL; ec->privateKey = NULL;
if (!getmppoint(&data, &len, &ec->publicKey)) { if (!get_point(src, &ec->publicKey)) {
ecdsa_freekey(&ec->sshk); ecdsa_freekey(&ec->sshk);
return NULL; return NULL;
} }
@ -1908,19 +1865,19 @@ static void ecdsa_private_blob(ssh_key *key, BinarySink *bs)
} }
static ssh_key *ecdsa_createkey(const ssh_keyalg *self, static ssh_key *ecdsa_createkey(const ssh_keyalg *self,
const void *pub_blob, int pub_len, ptrlen pub, ptrlen priv)
const void *priv_blob, int priv_len)
{ {
BinarySource src[1];
ssh_key *sshk; ssh_key *sshk;
struct ec_key *ec; struct ec_key *ec;
struct ec_point *publicKey; struct ec_point *publicKey;
const char *pb = (const char *) priv_blob;
sshk = ecdsa_newkey(self, pub_blob, pub_len); sshk = ecdsa_newkey(self, pub);
if (!sshk) if (!sshk)
return NULL; return NULL;
ec = FROMFIELD(sshk, struct ec_key, sshk); ec = FROMFIELD(sshk, struct ec_key, sshk);
BinarySource_BARE_INIT(src, priv.ptr, priv.len);
if (ec->publicKey.curve->type != EC_WEIERSTRASS if (ec->publicKey.curve->type != EC_WEIERSTRASS
&& ec->publicKey.curve->type != EC_EDWARDS) { && ec->publicKey.curve->type != EC_EDWARDS) {
@ -1929,9 +1886,9 @@ static ssh_key *ecdsa_createkey(const ssh_keyalg *self,
} }
if (ec->publicKey.curve->type == EC_EDWARDS) { if (ec->publicKey.curve->type == EC_EDWARDS) {
ec->privateKey = getmp_le(&pb, &priv_len); ec->privateKey = get_mp_le(src);
} else { } else {
ec->privateKey = getmp(&pb, &priv_len); ec->privateKey = get_mp_ssh2(src);
} }
if (!ec->privateKey) { if (!ec->privateKey) {
ecdsa_freekey(&ec->sshk); ecdsa_freekey(&ec->sshk);
@ -1954,18 +1911,16 @@ static ssh_key *ecdsa_createkey(const ssh_keyalg *self,
} }
static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self, static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
const unsigned char **blob, int *len) BinarySource *src)
{ {
struct ec_key *ec; struct ec_key *ec;
struct ec_point *publicKey; struct ec_point *publicKey;
const char *p, *q; ptrlen p, q;
int plen, qlen;
getstring((const char**)blob, len, &p, &plen); p = get_string(src);
if (!p) q = get_string(src);
{ if (get_err(src) || p.len != 32 || q.len != 64)
return NULL; return NULL;
}
ec = snew(struct ec_key); ec = snew(struct ec_key);
@ -1977,19 +1932,13 @@ static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
ec->publicKey.z = NULL; ec->publicKey.z = NULL;
ec->publicKey.y = NULL; ec->publicKey.y = NULL;
if (!decodepoint_ed(p, plen, &ec->publicKey)) if (!decodepoint_ed(p.ptr, p.len, &ec->publicKey))
{ {
ecdsa_freekey(&ec->sshk); ecdsa_freekey(&ec->sshk);
return NULL; return NULL;
} }
getstring((const char**)blob, len, &q, &qlen); ec->privateKey = bignum_from_bytes_le(q.ptr, 32);
if (!q || qlen != 64) {
ecdsa_freekey(&ec->sshk);
return NULL;
}
ec->privateKey = bignum_from_bytes_le(q, 32);
/* Check that private key generates public key */ /* Check that private key generates public key */
publicKey = ec_public(ec->privateKey, ec->publicKey.curve); publicKey = ec_public(ec->privateKey, ec->publicKey.curve);
@ -2009,7 +1958,7 @@ static ssh_key *ed25519_openssh_createkey(const ssh_keyalg *self,
* correct as well, otherwise the key we think we've imported * correct as well, otherwise the key we think we've imported
* won't behave identically to the way OpenSSH would have treated * won't behave identically to the way OpenSSH would have treated
* it. */ * it. */
if (plen != 32 || 0 != memcmp(q + 32, p, 32)) { if (0 != memcmp((const char *)q.ptr + 32, p.ptr, 32)) {
ecdsa_freekey(&ec->sshk); ecdsa_freekey(&ec->sshk);
return NULL; return NULL;
} }
@ -2054,22 +2003,16 @@ static void ed25519_openssh_fmtkey(ssh_key *key, BinarySink *bs)
} }
static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self, static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
const unsigned char **blob, int *len) BinarySource *src)
{ {
const struct ecsign_extra *extra = const struct ecsign_extra *extra =
(const struct ecsign_extra *)self->extra; (const struct ecsign_extra *)self->extra;
const char **b = (const char **) blob;
const char *p;
int slen;
struct ec_key *ec; struct ec_key *ec;
struct ec_curve *curve; struct ec_curve *curve;
struct ec_point *publicKey; struct ec_point *publicKey;
getstring(b, len, &p, &slen); get_string(src);
if (!p) {
return NULL;
}
curve = extra->curve(); curve = extra->curve();
assert(curve->type == EC_WEIERSTRASS); assert(curve->type == EC_WEIERSTRASS);
@ -2081,7 +2024,7 @@ static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
ec->publicKey.x = NULL; ec->publicKey.x = NULL;
ec->publicKey.y = NULL; ec->publicKey.y = NULL;
ec->publicKey.z = NULL; ec->publicKey.z = NULL;
if (!getmppoint(b, len, &ec->publicKey)) { if (!get_point(src, &ec->publicKey)) {
ecdsa_freekey(&ec->sshk); ecdsa_freekey(&ec->sshk);
return NULL; return NULL;
} }
@ -2095,7 +2038,7 @@ static ssh_key *ecdsa_openssh_createkey(const ssh_keyalg *self,
return NULL; return NULL;
} }
ec->privateKey = getmp(b, len); ec->privateKey = get_mp_ssh2(src);
if (ec->privateKey == NULL) if (ec->privateKey == NULL)
{ {
ecdsa_freekey(&ec->sshk); ecdsa_freekey(&ec->sshk);
@ -2147,14 +2090,13 @@ static void ecdsa_openssh_fmtkey(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, ec->privateKey); put_mp_ssh2(bs, ec->privateKey);
} }
static int ecdsa_pubkey_bits(const ssh_keyalg *self, static int ecdsa_pubkey_bits(const ssh_keyalg *self, ptrlen blob)
const void *blob, int len)
{ {
ssh_key *sshk; ssh_key *sshk;
struct ec_key *ec; struct ec_key *ec;
int ret; int ret;
sshk = ecdsa_newkey(self, blob, len); sshk = ecdsa_newkey(self, blob);
if (!sshk) if (!sshk)
return -1; return -1;
@ -2165,39 +2107,35 @@ static int ecdsa_pubkey_bits(const ssh_keyalg *self,
return ret; return ret;
} }
static int ecdsa_verifysig(ssh_key *key, const void *vsig, int siglen, static int ecdsa_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
const void *vdata, int datalen)
{ {
struct ec_key *ec = FROMFIELD(key, struct ec_key, sshk); struct ec_key *ec = FROMFIELD(key, struct ec_key, sshk);
const char *sig = (const char *)vsig;
const char *data = (const char *)vdata;
const struct ecsign_extra *extra = const struct ecsign_extra *extra =
(const struct ecsign_extra *)ec->signalg->extra; (const struct ecsign_extra *)ec->signalg->extra;
const char *p; BinarySource src[1];
int slen; ptrlen sigstr;
int digestLen;
int ret; int ret;
if (!ec->publicKey.x || !ec->publicKey.y || !ec->publicKey.curve) if (!ec->publicKey.x || !ec->publicKey.y || !ec->publicKey.curve)
return 0; return 0;
/* Check the signature starts with the algorithm name */ BinarySource_BARE_INIT(src, sig.ptr, sig.len);
getstring(&sig, &siglen, &p, &slen);
if (!p) { /* Check the signature starts with the algorithm name */
return 0; if (!ptrlen_eq_string(get_string(src), ec->signalg->name))
} return 0;
if (!match_ssh_id(slen, p, ec->signalg->name)) {
return 0; sigstr = get_string(src);
} if (get_err(src))
return 0;
getstring(&sig, &siglen, &p, &slen);
if (!p) return 0;
if (ec->publicKey.curve->type == EC_EDWARDS) { if (ec->publicKey.curve->type == EC_EDWARDS) {
struct ec_point *r; struct ec_point *r;
int pointlen = ec->publicKey.curve->fieldBits / 8;
Bignum s, h; Bignum s, h;
/* Check that the signature is two times the length of a point */ /* Check that the signature is two times the length of a point */
if (slen != (ec->publicKey.curve->fieldBits / 8) * 2) { if (sigstr.len != pointlen * 2) {
return 0; return 0;
} }
@ -2211,25 +2149,23 @@ static int ecdsa_verifysig(ssh_key *key, const void *vsig, int siglen,
if (!r) { if (!r) {
return 0; return 0;
} }
if (!decodepoint(p, ec->publicKey.curve->fieldBits / 8, r)) { if (!decodepoint(sigstr.ptr, pointlen, r)) {
ec_point_free(r); ec_point_free(r);
return 0; return 0;
} }
s = bignum_from_bytes_le((unsigned char*)p + (ec->publicKey.curve->fieldBits / 8), s = bignum_from_bytes_le(
ec->publicKey.curve->fieldBits / 8); (const char *)sigstr.ptr + pointlen, pointlen);
/* Get the hash of the encoded value of R + encoded value of pk + message */ /* Get the hash of the encoded value of R + encoded value of pk + message */
{ {
int i, pointlen; int i;
unsigned char digest[512 / 8]; unsigned char digest[512 / 8];
SHA512_State hs; SHA512_State hs;
SHA512_Init(&hs); SHA512_Init(&hs);
pointlen = ec->publicKey.curve->fieldBits / 8;
/* Add encoded r (no need to encode it again, it was in /* Add encoded r (no need to encode it again, it was in
* the signature) */ * the signature) */
put_data(&hs, p, pointlen); put_data(&hs, sigstr.ptr, pointlen);
/* Encode pk and add it */ /* Encode pk and add it */
for (i = 0; i < pointlen - 1; ++i) for (i = 0; i < pointlen - 1; ++i)
@ -2239,7 +2175,7 @@ static int ecdsa_verifysig(ssh_key *key, const void *vsig, int siglen,
(bignum_bit(ec->publicKey.x, 0) << 7))); (bignum_bit(ec->publicKey.x, 0) << 7)));
/* Add the message itself */ /* Add the message itself */
put_data(&hs, data, datalen); put_data(&hs, data.ptr, data.len);
/* Get the hash */ /* Get the hash */
SHA512_Final(&hs, digest); SHA512_Final(&hs, digest);
@ -2291,20 +2227,23 @@ static int ecdsa_verifysig(ssh_key *key, const void *vsig, int siglen,
} else { } else {
Bignum r, s; Bignum r, s;
unsigned char digest[512 / 8]; unsigned char digest[512 / 8];
int digestLen;
void *hashctx; void *hashctx;
r = getmp(&p, &slen); BinarySource_BARE_INIT(src, sigstr.ptr, sigstr.len);
if (!r) return 0;
s = getmp(&p, &slen); r = get_mp_ssh2(src);
if (!s) { s = get_mp_ssh2(src);
if (get_err(src)) {
freebn(r); freebn(r);
freebn(s);
return 0; return 0;
} }
digestLen = extra->hash->hlen; digestLen = extra->hash->hlen;
assert(digestLen <= sizeof(digest)); assert(digestLen <= sizeof(digest));
hashctx = extra->hash->init(); hashctx = extra->hash->init();
put_data(extra->hash->sink(hashctx), data, datalen); put_data(extra->hash->sink(hashctx), data.ptr, data.len);
extra->hash->final(hashctx, digest); extra->hash->final(hashctx, digest);
/* Verify the signature */ /* Verify the signature */

View File

@ -799,8 +799,9 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
ret = snew(struct ssh2_userkey); ret = snew(struct ssh2_userkey);
ret->alg = alg; ret->alg = alg;
ret->comment = comment; ret->comment = comment;
ret->data = alg->createkey(alg, public_blob->u, public_blob->len, ret->data = alg->createkey(
private_blob->u, private_blob->len); alg, make_ptrlen(public_blob->u, public_blob->len),
make_ptrlen(private_blob->u, private_blob->len));
if (!ret->data) { if (!ret->data) {
sfree(ret); sfree(ret);
ret = NULL; ret = NULL;
@ -1509,7 +1510,7 @@ char *ssh2_fingerprint_blob(const void *blob, int bloblen)
if (!get_err(src)) { if (!get_err(src)) {
alg = find_pubkey_alg_len(algname); alg = find_pubkey_alg_len(algname);
if (alg) { if (alg) {
int bits = alg->pubkey_bits(alg, blob, bloblen); int bits = alg->pubkey_bits(alg, make_ptrlen(blob, bloblen));
return dupprintf("%.*s %d %s", PTRLEN_PRINTF(algname), return dupprintf("%.*s %d %s", PTRLEN_PRINTF(algname),
bits, fingerprint_str); bits, fingerprint_str);
} else { } else {

137
sshrsa.c
View File

@ -520,62 +520,27 @@ void freersakey(struct RSAKey *key)
* Implementation of the ssh-rsa signing key type. * Implementation of the ssh-rsa signing key type.
*/ */
static void getstring(const char **data, int *datalen,
const char **p, int *length)
{
*p = NULL;
if (*datalen < 4)
return;
*length = toint(GET_32BIT(*data));
if (*length < 0)
return;
*datalen -= 4;
*data += 4;
if (*datalen < *length)
return;
*p = *data;
*data += *length;
*datalen -= *length;
}
static Bignum getmp(const char **data, int *datalen)
{
const char *p;
int length;
Bignum b;
getstring(data, datalen, &p, &length);
if (!p)
return NULL;
b = bignum_from_bytes(p, length);
return b;
}
static void rsa2_freekey(ssh_key *key); /* forward reference */ static void rsa2_freekey(ssh_key *key); /* forward reference */
static ssh_key *rsa2_newkey(const ssh_keyalg *self, static ssh_key *rsa2_newkey(const ssh_keyalg *self, ptrlen data)
const void *vdata, int len)
{ {
const char *p; BinarySource src[1];
const char *data = (const char *)vdata;
int slen;
struct RSAKey *rsa; struct RSAKey *rsa;
rsa = snew(struct RSAKey); BinarySource_BARE_INIT(src, data.ptr, data.len);
getstring(&data, &len, &p, &slen); if (!ptrlen_eq_string(get_string(src), "ssh-rsa"))
if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) {
sfree(rsa);
return NULL; return NULL;
}
rsa->exponent = getmp(&data, &len); rsa = snew(struct RSAKey);
rsa->modulus = getmp(&data, &len); rsa->exponent = get_mp_ssh2(src);
rsa->modulus = get_mp_ssh2(src);
rsa->private_exponent = NULL; rsa->private_exponent = NULL;
rsa->p = rsa->q = rsa->iqmp = NULL; rsa->p = rsa->q = rsa->iqmp = NULL;
rsa->comment = NULL; rsa->comment = NULL;
if (!rsa->exponent || !rsa->modulus) { if (get_err(src)) {
rsa2_freekey(&rsa->sshk); rsa2_freekey(&rsa->sshk);
return NULL; return NULL;
} }
return &rsa->sshk; return &rsa->sshk;
@ -620,24 +585,24 @@ static void rsa2_private_blob(ssh_key *key, BinarySink *bs)
} }
static ssh_key *rsa2_createkey(const ssh_keyalg *self, static ssh_key *rsa2_createkey(const ssh_keyalg *self,
const void *pub_blob, int pub_len, ptrlen pub, ptrlen priv)
const void *priv_blob, int priv_len)
{ {
BinarySource src[1];
ssh_key *sshk; ssh_key *sshk;
struct RSAKey *rsa; struct RSAKey *rsa;
const char *pb = (const char *) priv_blob;
sshk = rsa2_newkey(self, pub_blob, pub_len); sshk = rsa2_newkey(self, pub);
if (!sshk) if (!sshk)
return NULL; return NULL;
rsa = FROMFIELD(sshk, struct RSAKey, sshk); rsa = FROMFIELD(sshk, struct RSAKey, sshk);
rsa->private_exponent = getmp(&pb, &priv_len); BinarySource_BARE_INIT(src, priv.ptr, priv.len);
rsa->p = getmp(&pb, &priv_len); rsa->private_exponent = get_mp_ssh2(src);
rsa->q = getmp(&pb, &priv_len); rsa->p = get_mp_ssh2(src);
rsa->iqmp = getmp(&pb, &priv_len); rsa->q = get_mp_ssh2(src);
rsa->iqmp = get_mp_ssh2(src);
if (!rsa_verify(rsa)) { if (get_err(src) || !rsa_verify(rsa)) {
rsa2_freekey(&rsa->sshk); rsa2_freekey(&rsa->sshk);
return NULL; return NULL;
} }
@ -646,28 +611,21 @@ static ssh_key *rsa2_createkey(const ssh_keyalg *self,
} }
static ssh_key *rsa2_openssh_createkey(const ssh_keyalg *self, static ssh_key *rsa2_openssh_createkey(const ssh_keyalg *self,
const unsigned char **blob, int *len) BinarySource *src)
{ {
const char **b = (const char **) blob;
struct RSAKey *rsa; struct RSAKey *rsa;
rsa = snew(struct RSAKey); rsa = snew(struct RSAKey);
rsa->comment = NULL; rsa->comment = NULL;
rsa->modulus = getmp(b, len); rsa->modulus = get_mp_ssh2(src);
rsa->exponent = getmp(b, len); rsa->exponent = get_mp_ssh2(src);
rsa->private_exponent = getmp(b, len); rsa->private_exponent = get_mp_ssh2(src);
rsa->iqmp = getmp(b, len); rsa->iqmp = get_mp_ssh2(src);
rsa->p = getmp(b, len); rsa->p = get_mp_ssh2(src);
rsa->q = getmp(b, len); rsa->q = get_mp_ssh2(src);
if (!rsa->modulus || !rsa->exponent || !rsa->private_exponent || if (get_err(src) || !rsa_verify(rsa)) {
!rsa->iqmp || !rsa->p || !rsa->q) {
rsa2_freekey(&rsa->sshk);
return NULL;
}
if (!rsa_verify(rsa)) {
rsa2_freekey(&rsa->sshk); rsa2_freekey(&rsa->sshk);
return NULL; return NULL;
} }
@ -687,14 +645,13 @@ static void rsa2_openssh_fmtkey(ssh_key *key, BinarySink *bs)
put_mp_ssh2(bs, rsa->q); put_mp_ssh2(bs, rsa->q);
} }
static int rsa2_pubkey_bits(const ssh_keyalg *self, static int rsa2_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
const void *blob, int len)
{ {
ssh_key *sshk; ssh_key *sshk;
struct RSAKey *rsa; struct RSAKey *rsa;
int ret; int ret;
sshk = rsa2_newkey(self, blob, len); sshk = rsa2_newkey(self, pub);
if (!sshk) if (!sshk)
return -1; return -1;
@ -736,24 +693,32 @@ static const unsigned char asn1_weird_stuff[] = {
#define ASN1_LEN ( (int) sizeof(asn1_weird_stuff) ) #define ASN1_LEN ( (int) sizeof(asn1_weird_stuff) )
static int rsa2_verifysig(ssh_key *key, const void *vsig, int siglen, static int rsa2_verifysig(ssh_key *key, ptrlen sig, ptrlen data)
const void *data, int datalen)
{ {
struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk); struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
const char *sig = (const char *)vsig; BinarySource src[1];
ptrlen type, in_pl;
Bignum in, out; Bignum in, out;
const char *p;
int slen;
int bytes, i, j, ret; int bytes, i, j, ret;
unsigned char hash[20]; unsigned char hash[20];
getstring(&sig, &siglen, &p, &slen); BinarySource_BARE_INIT(src, sig.ptr, sig.len);
if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) { type = get_string(src);
/*
* RFC 4253 section 6.6: the signature integer in an ssh-rsa
* signature is 'without lengths or padding'. That is, we _don't_
* expect the usual leading zero byte if the topmost bit of the
* first byte is set. (However, because of the possibility of
* BUG_SSH2_RSA_PADDING at the other end, we tolerate it if it's
* there.) So we can't use get_mp_ssh2, which enforces that
* leading-byte scheme; instead we use get_string and
* bignum_from_bytes, which will tolerate anything.
*/
in_pl = get_string(src);
if (get_err(src) || !ptrlen_eq_string(type, "ssh-rsa"))
return 0; return 0;
}
in = getmp(&sig, &siglen); in = bignum_from_bytes(in_pl.ptr, in_pl.len);
if (!in)
return 0;
out = modpow(in, rsa->exponent, rsa->modulus); out = modpow(in, rsa->exponent, rsa->modulus);
freebn(in); freebn(in);
@ -777,7 +742,7 @@ static int rsa2_verifysig(ssh_key *key, const void *vsig, int siglen,
ret = 0; ret = 0;
} }
/* Finally, we expect to see the SHA-1 hash of the signed data. */ /* Finally, we expect to see the SHA-1 hash of the signed data. */
SHA_Simple(data, datalen, hash); SHA_Simple(data.ptr, data.len, hash);
for (i = 19, j = 0; i >= 0; i--, j++) { for (i = 19, j = 0; i >= 0; i--, j++) {
if (bignum_byte(out, i) != hash[j]) if (bignum_byte(out, i) != hash[j])
ret = 0; ret = 0;
@ -846,7 +811,7 @@ const ssh_keyalg ssh_rsa = {
struct RSAKey *ssh_rsakex_newkey(const void *data, int len) struct RSAKey *ssh_rsakex_newkey(const void *data, int len)
{ {
ssh_key *sshk = rsa2_newkey(&ssh_rsa, data, len); ssh_key *sshk = rsa2_newkey(&ssh_rsa, make_ptrlen(data, len));
if (!sshk) if (!sshk)
return NULL; return NULL;
return FROMFIELD(sshk, struct RSAKey, sshk); return FROMFIELD(sshk, struct RSAKey, sshk);