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

Use BinarySink to tidy up key export code.

The output routines in import.c and sshpubk.c were further horrifying
hotbeds of manual length-counting. Reworked it all so that it builds
up key file components in strbufs, and uses the now boringly standard
put_* functions to write into those strbufs.

This removes the write_* functions in import.c, which I had to hastily
rename a few commits ago when I introduced the new marshalling system
in the first place.

However, I wasn't quite able to get rid of _all_ of import.c's local
formatting functions; there are a couple still there (but now with new
BinarySink-style API) which output multiprecision integers in a couple
of different formats starting from an existing big-endian binary
representation, as opposed to starting from an internal Bignum.
This commit is contained in:
Simon Tatham 2018-05-24 13:11:56 +01:00
parent e27ddf6d28
commit 8ce0a67028
3 changed files with 284 additions and 507 deletions

2
Recipe
View File

@ -271,7 +271,7 @@ UXMISC = MISCNET uxstore uxsel uxnet uxpeer uxmisc uxproxy time
# import.c and dependencies, for PuTTYgen-like utilities that have to # import.c and dependencies, for PuTTYgen-like utilities that have to
# load foreign key files. # load foreign key files.
IMPORT = import sshbcrypt sshblowf IMPORT = import sshbcrypt sshblowf marshal
# Character set library, for use in pterm. # Character set library, for use in pterm.
CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc

574
import.c
View File

@ -219,17 +219,14 @@ static int ber_read_id_len(void *source, int sourcelen,
* Will avoid writing anything if dest is NULL, but still return * Will avoid writing anything if dest is NULL, but still return
* amount of space required. * amount of space required.
*/ */
static int ber_write_id_len(void *dest, int id, int length, int flags) static void BinarySink_put_ber_id_len(BinarySink *bs,
int id, int length, int flags)
{ {
unsigned char *d = (unsigned char *)dest;
int len = 0;
if (id <= 30) { if (id <= 30) {
/* /*
* Identifier is one byte. * Identifier is one byte.
*/ */
len++; put_byte(bs, id | flags);
if (d) *d++ = id | flags;
} else { } else {
int n; int n;
/* /*
@ -238,22 +235,18 @@ static int ber_write_id_len(void *dest, int id, int length, int flags)
* the identifier, 7 bits at a time, with the top bit of * the identifier, 7 bits at a time, with the top bit of
* each byte 1 except the last one which is 0. * each byte 1 except the last one which is 0.
*/ */
len++; put_byte(bs, 0x1F | flags);
if (d) *d++ = 0x1F | flags;
for (n = 1; (id >> (7*n)) > 0; n++) for (n = 1; (id >> (7*n)) > 0; n++)
continue; /* count the bytes */ continue; /* count the bytes */
while (n--) { while (n--)
len++; put_byte(bs, (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F));
if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F);
}
} }
if (length < 128) { if (length < 128) {
/* /*
* Length is one byte. * Length is one byte.
*/ */
len++; put_byte(bs, length);
if (d) *d++ = length;
} else { } else {
int n; int n;
/* /*
@ -263,56 +256,15 @@ static int ber_write_id_len(void *dest, int id, int length, int flags)
*/ */
for (n = 1; (length >> (8*n)) > 0; n++) for (n = 1; (length >> (8*n)) > 0; n++)
continue; /* count the bytes */ continue; /* count the bytes */
len++; put_byte(bs, 0x80 | n);
if (d) *d++ = 0x80 | n; while (n--)
while (n--) { put_byte(bs, (length >> (8*n)) & 0xFF);
len++;
if (d) *d++ = (length >> (8*n)) & 0xFF;
}
}
return len;
}
static int write_uint32(void *target, unsigned val)
{
unsigned char *d = (unsigned char *)target;
PUT_32BIT(d, val);
return 4;
}
static int write_string(void *target, const void *data, int len)
{
unsigned char *d = (unsigned char *)target;
PUT_32BIT(d, len);
memcpy(d+4, data, len);
return len+4;
}
static int write_string_z(void *target, const char *string)
{
return write_string(target, string, strlen(string));
}
static int put_mp(void *target, void *data, int len)
{
unsigned char *d = (unsigned char *)target;
unsigned char *i = (unsigned char *)data;
if (*i & 0x80) {
PUT_32BIT(d, len+1);
d[4] = 0;
memcpy(d+5, data, len);
return len+5;
} else {
PUT_32BIT(d, len);
memcpy(d+4, data, len);
return len+4;
} }
} }
#define put_ber_id_len(bs, id, len, flags) \
BinarySink_put_ber_id_len(BinarySink_UPCAST(bs), id, len, flags)
/* Simple structure to point to an mp-int within a blob. */ /* Simple structure to point to an mp-int within a blob. */
struct mpint_pos { void *start; int bytes; }; struct mpint_pos { void *start; int bytes; };
@ -358,6 +310,25 @@ struct openssh_pem_key {
int keyblob_len, keyblob_size; int keyblob_len, keyblob_size;
}; };
void BinarySink_put_mp_ssh2_from_string(
BinarySink *bs, const void *bytesv, int nbytes)
{
const unsigned char *bytes = (const unsigned char *)bytesv;
while (nbytes > 0 && bytes[0] == 0) {
nbytes--;
bytes++;
}
if (nbytes > 0 && bytes[0] & 0x80) {
put_uint32(bs, nbytes + 1);
put_byte(bs, 0);
} else {
put_uint32(bs, nbytes);
}
put_data(bs, bytes, nbytes);
}
#define put_mp_ssh2_from_string(bs, val, len) \
BinarySink_put_mp_ssh2_from_string(BinarySink_UPCAST(bs), val, len)
static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename, static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
const char **errmsg_p) const char **errmsg_p)
{ {
@ -570,18 +541,16 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
{ {
struct openssh_pem_key *key = load_openssh_pem_key(filename, errmsg_p); struct openssh_pem_key *key = load_openssh_pem_key(filename, errmsg_p);
struct ssh2_userkey *retkey; struct ssh2_userkey *retkey;
unsigned char *p, *q; unsigned char *p;
int ret, id, len, flags; int ret, id, len, flags;
int i, num_integers; int i, num_integers;
struct ssh2_userkey *retval = NULL; struct ssh2_userkey *retval = NULL;
const char *errmsg; const char *errmsg;
unsigned char *blob; strbuf *blob = strbuf_new();
int blobsize = 0, blobptr, privptr; int privptr = 0, publen;
char *modptr = NULL; char *modptr = NULL;
int modlen = 0; int modlen = 0;
blob = NULL;
if (!key) if (!key)
return NULL; return NULL;
@ -682,7 +651,6 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
int privlen; int privlen;
const struct ssh_signkey *alg; const struct ssh_signkey *alg;
const struct ec_curve *curve; const struct ec_curve *curve;
int algnamelen, curvenamelen;
/* Read INTEGER 1 */ /* Read INTEGER 1 */
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags); &id, &len, &flags);
@ -762,40 +730,16 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
goto error; goto error;
} }
retkey->alg = alg; retkey->alg = alg;
blob = snewn((4+19 + 4+8 + 4+len) + (4+1+privlen), unsigned char);
if (!blob) {
sfree(retkey);
errmsg = "out of memory";
goto error;
}
q = blob; put_stringz(blob, alg->name);
put_stringz(blob, curve->name);
put_string(blob, p, len);
publen = blob->len;
put_mp_ssh2_from_string(blob, priv, privlen);
algnamelen = strlen(alg->name); retkey->data = retkey->alg->createkey(
PUT_32BIT(q, algnamelen); q += 4; retkey->alg, blob->u, publen,
memcpy(q, alg->name, algnamelen); q += algnamelen; blob->u + publen, blob->len - publen);
curvenamelen = strlen(curve->name);
PUT_32BIT(q, curvenamelen); q += 4;
memcpy(q, curve->name, curvenamelen); q += curvenamelen;
PUT_32BIT(q, len); q += 4;
memcpy(q, p, len); q += len;
/*
* To be acceptable to our createkey(), the private blob must
* contain a valid mpint, i.e. without the top bit set. But
* the input private string may have the top bit set, so we
* prefix a zero byte to ensure createkey() doesn't fail for
* that reason.
*/
PUT_32BIT(q, privlen+1);
q[4] = 0;
memcpy(q+5, priv, privlen);
retkey->data = retkey->alg->createkey(retkey->alg,
blob, q-blob,
q, 5+privlen);
if (!retkey->data) { if (!retkey->data) {
sfree(retkey); sfree(retkey);
@ -805,18 +749,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
} else if (key->keytype == OP_RSA || key->keytype == OP_DSA) { } else if (key->keytype == OP_RSA || key->keytype == OP_DSA) {
/* put_stringz(blob, key->keytype == OP_DSA ? "ssh-dss" : "ssh-rsa");
* Space to create key blob in.
*/
blobsize = 256+key->keyblob_len;
blob = snewn(blobsize, unsigned char);
PUT_32BIT(blob, 7);
if (key->keytype == OP_DSA)
memcpy(blob+4, "ssh-dss", 7);
else if (key->keytype == OP_RSA)
memcpy(blob+4, "ssh-rsa", 7);
blobptr = 4+7;
privptr = -1;
for (i = 0; i < num_integers; i++) { for (i = 0; i < num_integers; i++) {
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
@ -849,14 +782,10 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
modptr = (char *)p; modptr = (char *)p;
modlen = len; modlen = len;
} else if (i != 6 && i != 7) { } else if (i != 6 && i != 7) {
PUT_32BIT(blob+blobptr, len); put_mp_ssh2_from_string(blob, p, len);
memcpy(blob+blobptr+4, p, len);
blobptr += 4+len;
if (i == 2) { if (i == 2) {
PUT_32BIT(blob+blobptr, modlen); put_mp_ssh2_from_string(blob, modptr, modlen);
memcpy(blob+blobptr+4, modptr, modlen); privptr = blob->len;
blobptr += 4+modlen;
privptr = blobptr;
} }
} }
} else if (key->keytype == OP_DSA) { } else if (key->keytype == OP_DSA) {
@ -864,11 +793,9 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
* Integers 1-4 go into the public blob; integer 5 goes * Integers 1-4 go into the public blob; integer 5 goes
* into the private blob. * into the private blob.
*/ */
PUT_32BIT(blob+blobptr, len); put_mp_ssh2_from_string(blob, p, len);
memcpy(blob+blobptr+4, p, len);
blobptr += 4+len;
if (i == 4) if (i == 4)
privptr = blobptr; privptr = blob->len;
} }
/* Skip past the number. */ /* Skip past the number. */
@ -884,9 +811,9 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
assert(privptr > 0); /* should have bombed by now if not */ assert(privptr > 0); /* should have bombed by now if not */
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->alg, blob, privptr, retkey->data = retkey->alg->createkey(
blob+privptr, retkey->alg, blob->u, privptr, blob->u+privptr, blob->len-privptr);
blobptr-privptr);
if (!retkey->data) { if (!retkey->data) {
sfree(retkey); sfree(retkey);
errmsg = "unable to create key data structure"; errmsg = "unable to create key data structure";
@ -909,10 +836,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
retval = retkey; retval = retkey;
error: error:
if (blob) { strbuf_free(blob);
smemclr(blob, blobsize);
sfree(blob);
}
smemclr(key->keyblob, key->keyblob_size); smemclr(key->keyblob, key->keyblob_size);
sfree(key->keyblob); sfree(key->keyblob);
smemclr(key, sizeof(*key)); smemclr(key, sizeof(*key));
@ -924,13 +848,11 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key, int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
char *passphrase) char *passphrase)
{ {
strbuf *pubblob, *privblob; strbuf *pubblob, *privblob, *outblob;
unsigned char *spareblob; unsigned char *spareblob;
int sparelen = 0; int sparelen = 0;
unsigned char *outblob;
int outlen;
struct mpint_pos numbers[9]; struct mpint_pos numbers[9];
int nnumbers, pos, len, seqlen, i; int nnumbers, i;
const char *header, *footer; const char *header, *footer;
char zero[1]; char zero[1];
unsigned char iv[8]; unsigned char iv[8];
@ -944,16 +866,17 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
key->alg->public_blob(key->data, BinarySink_UPCAST(pubblob)); key->alg->public_blob(key->data, BinarySink_UPCAST(pubblob));
privblob = strbuf_new(); privblob = strbuf_new();
key->alg->private_blob(key->data, BinarySink_UPCAST(privblob)); key->alg->private_blob(key->data, BinarySink_UPCAST(privblob));
spareblob = outblob = NULL; spareblob = NULL;
outblob = NULL; outblob = strbuf_new();
len = 0;
/* /*
* Encode the OpenSSH key blob, and also decide on the header * Encode the OpenSSH key blob, and also decide on the header
* line. * line.
*/ */
if (key->alg == &ssh_rsa || key->alg == &ssh_dss) { if (key->alg == &ssh_rsa || key->alg == &ssh_dss) {
strbuf *seq;
/* /*
* The RSA and DSS handlers share some code because the two * The RSA and DSS handlers share some code because the two
* key types have very similar ASN.1 representations, as a * key types have very similar ASN.1 representations, as a
@ -1049,40 +972,21 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
footer = "-----END DSA PRIVATE KEY-----\n"; footer = "-----END DSA PRIVATE KEY-----\n";
} }
/* seq = strbuf_new();
* Now count up the total size of the ASN.1 encoded integers,
* so as to determine the length of the containing SEQUENCE.
*/
len = 0;
for (i = 0; i < nnumbers; i++) { for (i = 0; i < nnumbers; i++) {
len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); put_ber_id_len(seq, 2, numbers[i].bytes, 0);
len += numbers[i].bytes; put_data(seq, numbers[i].start, numbers[i].bytes);
}
seqlen = len;
/* Now add on the SEQUENCE header. */
len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
/*
* Now we know how big outblob needs to be. Allocate it.
*/
outblob = snewn(len, unsigned char);
/*
* And write the data into it.
*/
pos = 0;
pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
for (i = 0; i < nnumbers; i++) {
pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);
memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
pos += numbers[i].bytes;
} }
put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED);
put_data(outblob, seq->s, seq->len);
strbuf_free(seq);
} else if (key->alg == &ssh_ecdsa_nistp256 || } else if (key->alg == &ssh_ecdsa_nistp256 ||
key->alg == &ssh_ecdsa_nistp384 || key->alg == &ssh_ecdsa_nistp384 ||
key->alg == &ssh_ecdsa_nistp521) { key->alg == &ssh_ecdsa_nistp521) {
const unsigned char *oid; const unsigned char *oid;
int oidlen; int oidlen;
int pointlen; int pointlen;
strbuf *seq, *sub;
/* /*
* Structure of asn1: * Structure of asn1:
@ -1098,47 +1002,43 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
pointlen = (((struct ec_key *)key->data)->publicKey.curve->fieldBits pointlen = (((struct ec_key *)key->data)->publicKey.curve->fieldBits
+ 7) / 8 * 2; + 7) / 8 * 2;
len = ber_write_id_len(NULL, 2, 1, 0); seq = strbuf_new();
len += 1;
len += ber_write_id_len(NULL, 4, privblob->len - 4, 0);
len+= privblob->len - 4;
len += ber_write_id_len(NULL, 0, oidlen +
ber_write_id_len(NULL, 6, oidlen, 0),
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
len += ber_write_id_len(NULL, 6, oidlen, 0);
len += oidlen;
len += ber_write_id_len(NULL, 1, 2 + pointlen +
ber_write_id_len(NULL, 3, 2 + pointlen, 0),
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
len += ber_write_id_len(NULL, 3, 2 + pointlen, 0);
len += 2 + pointlen;
seqlen = len; /* INTEGER 1 */
len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); put_ber_id_len(seq, 2, 1, 0);
put_byte(seq, 1);
outblob = snewn(len, unsigned char); /* OCTET STRING private key */
assert(outblob); put_ber_id_len(seq, 4, privblob->len - 4, 0);
put_data(seq, privblob->s + 4, privblob->len - 4);
pos = 0; /* Subsidiary OID */
pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); sub = strbuf_new();
pos += ber_write_id_len(outblob+pos, 2, 1, 0); put_ber_id_len(sub, 6, oidlen, 0);
outblob[pos++] = 1; put_data(sub, oid, oidlen);
pos += ber_write_id_len(outblob+pos, 4, privblob->len - 4, 0);
memcpy(outblob+pos, privblob->u + 4, privblob->len - 4); /* Append the OID to the sequence */
pos += privblob->len - 4; put_ber_id_len(seq, 0, sub->len,
pos += ber_write_id_len(outblob+pos, 0, oidlen +
ber_write_id_len(NULL, 6, oidlen, 0),
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED); ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
pos += ber_write_id_len(outblob+pos, 6, oidlen, 0); put_data(seq, sub->s, sub->len);
memcpy(outblob+pos, oid, oidlen); strbuf_free(sub);
pos += oidlen;
pos += ber_write_id_len(outblob+pos, 1, 2 + pointlen + /* Subsidiary BIT STRING */
ber_write_id_len(NULL, 3, 2 + pointlen, 0), sub = strbuf_new();
put_ber_id_len(sub, 3, 2 + pointlen, 0);
put_byte(sub, 0);
put_data(sub, pubblob->s+39, 1 + pointlen);
/* Append the BIT STRING to the sequence */
put_ber_id_len(seq, 1, sub->len,
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED); ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
pos += ber_write_id_len(outblob+pos, 3, 2 + pointlen, 0); put_data(seq, sub->s, sub->len);
outblob[pos++] = 0; strbuf_free(sub);
memcpy(outblob+pos, pubblob->u+39, 1 + pointlen);
pos += 1 + pointlen; /* Write the full sequence with header to the output blob. */
put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED);
put_data(outblob, seq->s, seq->len);
strbuf_free(seq);
header = "-----BEGIN EC PRIVATE KEY-----\n"; header = "-----BEGIN EC PRIVATE KEY-----\n";
footer = "-----END EC PRIVATE KEY-----\n"; footer = "-----END EC PRIVATE KEY-----\n";
@ -1156,19 +1056,7 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
if (passphrase) { if (passphrase) {
struct MD5Context md5c; struct MD5Context md5c;
unsigned char keybuf[32]; unsigned char keybuf[32];
int origlen, outlen, pad, i;
/*
* Round up to the cipher block size, ensuring we have at
* least one byte of padding (see below).
*/
outlen = (len+8) &~ 7;
{
unsigned char *tmp = snewn(outlen, unsigned char);
memcpy(tmp, outblob, len);
smemclr(outblob, len);
sfree(outblob);
outblob = tmp;
}
/* /*
* Padding on OpenSSH keys is deterministic. The number of * Padding on OpenSSH keys is deterministic. The number of
@ -1187,10 +1075,11 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
* with the same value. Those are all removed and the rest is * with the same value. Those are all removed and the rest is
* returned. * returned.
*/ */
assert(pos == len); origlen = outblob->len;
while (pos < outlen) { outlen = (origlen + 8) &~ 7;
outblob[pos++] = outlen - len; pad = outlen - origlen;
} for (i = 0; i < pad; i++)
put_byte(outblob, pad);
/* /*
* Invent an iv. Then derive encryption key from passphrase * Invent an iv. Then derive encryption key from passphrase
@ -1217,16 +1106,11 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
/* /*
* Now encrypt the key blob. * Now encrypt the key blob.
*/ */
des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen); des3_encrypt_pubkey_ossh(keybuf, iv,
outblob->u, outlen);
smemclr(&md5c, sizeof(md5c)); smemclr(&md5c, sizeof(md5c));
smemclr(keybuf, sizeof(keybuf)); smemclr(keybuf, sizeof(keybuf));
} else {
/*
* If no encryption, the blob has exactly its original
* cleartext size.
*/
outlen = len;
} }
/* /*
@ -1243,16 +1127,14 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
fprintf(fp, "%02X", iv[i]); fprintf(fp, "%02X", iv[i]);
fprintf(fp, "\n\n"); fprintf(fp, "\n\n");
} }
base64_encode(fp, outblob, outlen, 64); base64_encode(fp, outblob->u, outblob->len, 64);
fputs(footer, fp); fputs(footer, fp);
fclose(fp); fclose(fp);
ret = 1; ret = 1;
error: error:
if (outblob) { if (outblob)
smemclr(outblob, outlen); strbuf_free(outblob);
sfree(outblob);
}
if (spareblob) { if (spareblob) {
smemclr(spareblob, sparelen); smemclr(spareblob, sparelen);
sfree(spareblob); sfree(spareblob);
@ -1746,10 +1628,8 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
int openssh_new_write(const Filename *filename, struct ssh2_userkey *key, int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
char *passphrase) char *passphrase)
{ {
strbuf *pubblob, *privblob; strbuf *pubblob, *privblob, *cblob;
unsigned char *outblob, *p; int padvalue, i;
unsigned char *private_section_start, *private_section_length_field;
int commentlen, maxsize, padvalue, i;
unsigned checkint; unsigned checkint;
int ret = 0; int ret = 0;
unsigned char bcrypt_salt[16]; unsigned char bcrypt_salt[16];
@ -1763,92 +1643,64 @@ int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
key->alg->public_blob(key->data, BinarySink_UPCAST(pubblob)); key->alg->public_blob(key->data, BinarySink_UPCAST(pubblob));
privblob = strbuf_new(); privblob = strbuf_new();
key->alg->openssh_fmtkey(key->data, BinarySink_UPCAST(privblob)); key->alg->openssh_fmtkey(key->data, BinarySink_UPCAST(privblob));
commentlen = strlen(key->comment);
/*
* Allocate enough space for the full binary key format. No need
* to be absolutely precise here.
*/
maxsize = (16 + /* magic number */
32 + /* cipher name string */
32 + /* kdf name string */
64 + /* kdf options string */
4 + /* key count */
4+pubblob->len + /* public key string */
4 + /* string header for private section */
8 + /* checkint x 2 */
4+strlen(key->alg->name) + /* key type string */
privblob->len + /* private blob */
4+commentlen + /* comment string */
16); /* padding at end of private section */
outblob = snewn(maxsize, unsigned char);
/* /*
* Construct the cleartext version of the blob. * Construct the cleartext version of the blob.
*/ */
p = outblob; cblob = strbuf_new();
/* Magic number. */ /* Magic number. */
memcpy(p, "openssh-key-v1\0", 15); put_asciz(cblob, "openssh-key-v1");
p += 15;
/* Cipher and kdf names, and kdf options. */ /* Cipher and kdf names, and kdf options. */
if (!passphrase) { if (!passphrase) {
memset(bcrypt_salt, 0, sizeof(bcrypt_salt)); /* prevent warnings */ memset(bcrypt_salt, 0, sizeof(bcrypt_salt)); /* prevent warnings */
p += write_string_z(p, "none"); put_stringz(cblob, "none");
p += write_string_z(p, "none"); put_stringz(cblob, "none");
p += write_string_z(p, ""); put_stringz(cblob, "");
} else { } else {
unsigned char *q; strbuf *substr;
for (i = 0; i < (int)sizeof(bcrypt_salt); i++) for (i = 0; i < (int)sizeof(bcrypt_salt); i++)
bcrypt_salt[i] = random_byte(); bcrypt_salt[i] = random_byte();
p += write_string_z(p, "aes256-ctr"); put_stringz(cblob, "aes256-ctr");
p += write_string_z(p, "bcrypt"); put_stringz(cblob, "bcrypt");
q = p; substr = strbuf_new();
p += 4; put_string(substr, bcrypt_salt, sizeof(bcrypt_salt));
p += write_string(p, bcrypt_salt, sizeof(bcrypt_salt)); put_uint32(substr, bcrypt_rounds);
p += write_uint32(p, bcrypt_rounds); put_stringsb(cblob, substr);
PUT_32BIT_MSB_FIRST(q, (unsigned)(p - (q+4)));
} }
/* Number of keys. */ /* Number of keys. */
p += write_uint32(p, 1); put_uint32(cblob, 1);
/* Public blob. */ /* Public blob. */
p += write_string(p, pubblob->u, pubblob->len); put_string(cblob, pubblob->s, pubblob->len);
/* Begin private section. */ /* Private section. */
private_section_length_field = p; {
p += 4; strbuf *cpblob = strbuf_new();
private_section_start = p;
/* checkint. */ /* checkint. */
checkint = 0; checkint = 0;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
checkint = (checkint << 8) + random_byte(); checkint = (checkint << 8) + random_byte();
p += write_uint32(p, checkint); put_uint32(cpblob, checkint);
p += write_uint32(p, checkint); put_uint32(cpblob, checkint);
/* Private key. The main private blob goes inline, with no string /* Private key. The main private blob goes inline, with no string
* wrapper. */ * wrapper. */
p += write_string_z(p, key->alg->name); put_stringz(cpblob, key->alg->name);
memcpy(p, privblob->u, privblob->len); put_data(cpblob, privblob->s, privblob->len);
p += privblob->len;
/* Comment. */ /* Comment. */
p += write_string_z(p, key->comment); put_stringz(cpblob, key->comment);
/* Pad out the encrypted section. */ /* Pad out the encrypted section. */
padvalue = 1; padvalue = 1;
do { do {
*p++ = padvalue++; put_byte(cpblob, padvalue++);
} while ((p - private_section_start) & 15); } while (cpblob->len & 15);
assert(p - outblob < maxsize);
/* Go back and fill in the length field for the private section. */
PUT_32BIT_MSB_FIRST(private_section_length_field,
p - private_section_start);
if (passphrase) { if (passphrase) {
/* /*
@ -1865,13 +1717,16 @@ int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
ctx = aes_make_context(); ctx = aes_make_context();
aes256_key(ctx, keybuf); aes256_key(ctx, keybuf);
aes_iv(ctx, keybuf + 32); aes_iv(ctx, keybuf + 32);
aes_ssh2_sdctr(ctx, private_section_start, aes_ssh2_sdctr(ctx, cpblob->u,
p - private_section_start); cpblob->len);
aes_free_context(ctx); aes_free_context(ctx);
smemclr(keybuf, sizeof(keybuf)); smemclr(keybuf, sizeof(keybuf));
} }
put_stringsb(cblob, cpblob);
}
/* /*
* And save it. We'll use Unix line endings just in case it's * And save it. We'll use Unix line endings just in case it's
* subsequently transferred in binary mode. * subsequently transferred in binary mode.
@ -1880,16 +1735,14 @@ int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
if (!fp) if (!fp)
goto error; goto error;
fputs("-----BEGIN OPENSSH PRIVATE KEY-----\n", fp); fputs("-----BEGIN OPENSSH PRIVATE KEY-----\n", fp);
base64_encode(fp, outblob, p - outblob, 64); base64_encode(fp, cblob->u, cblob->len, 64);
fputs("-----END OPENSSH PRIVATE KEY-----\n", fp); fputs("-----END OPENSSH PRIVATE KEY-----\n", fp);
fclose(fp); fclose(fp);
ret = 1; ret = 1;
error: error:
if (outblob) { if (cblob)
smemclr(outblob, maxsize); strbuf_free(cblob);
sfree(outblob);
}
if (privblob) if (privblob)
strbuf_free(privblob); strbuf_free(privblob);
if (pubblob) if (pubblob)
@ -2232,30 +2085,32 @@ static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret)
return len; /* ensure further calls fail as well */ return len; /* ensure further calls fail as well */
} }
static int sshcom_put_mpint(void *target, void *data, int len) void BinarySink_put_mp_sshcom_from_string(
BinarySink *bs, const void *bytesv, int nbytes)
{ {
unsigned char *d = (unsigned char *)target; const unsigned char *bytes = (const unsigned char *)bytesv;
unsigned char *i = (unsigned char *)data; int bits = nbytes * 8 - 1;
int bits = len * 8 - 1;
while (bits > 0) { while (bits > 0) {
if (*i & (1 << (bits & 7))) if (*bytes & (1 << (bits & 7)))
break; break;
if (!(bits-- & 7)) if (!(bits-- & 7))
i++, len--; bytes++, nbytes--;
} }
PUT_32BIT(d, bits+1); put_uint32(bs, bits+1);
memcpy(d+4, i, len); put_data(bs, bytes, nbytes);
return len+4;
} }
#define put_mp_sshcom_from_string(bs, val, len) \
BinarySink_put_mp_sshcom_from_string(BinarySink_UPCAST(bs), val, len)
struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase, struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
const char **errmsg_p) const char **errmsg_p)
{ {
struct sshcom_key *key = load_sshcom_key(filename, errmsg_p); struct sshcom_key *key = load_sshcom_key(filename, errmsg_p);
const char *errmsg; const char *errmsg;
int pos, len; int pos, len, publen;
const char prefix_rsa[] = "if-modn{sign{rsa"; const char prefix_rsa[] = "if-modn{sign{rsa";
const char prefix_dsa[] = "dl-modp{sign{dsa"; const char prefix_dsa[] = "dl-modp{sign{dsa";
enum { RSA, DSA } type; enum { RSA, DSA } type;
@ -2264,8 +2119,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
int cipherlen; int cipherlen;
struct ssh2_userkey *ret = NULL, *retkey; struct ssh2_userkey *ret = NULL, *retkey;
const struct ssh_signkey *alg; const struct ssh_signkey *alg;
unsigned char *blob = NULL; strbuf *blob = NULL;
int blobsize = 0, publen, privlen;
if (!key) if (!key)
return NULL; return NULL;
@ -2401,9 +2255,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
* construct public and private blobs in our own format, and * construct public and private blobs in our own format, and
* end up feeding them to alg->createkey(). * end up feeding them to alg->createkey().
*/ */
blobsize = cipherlen + 256; blob = strbuf_new();
blob = snewn(blobsize, unsigned char);
privlen = 0;
if (type == RSA) { if (type == RSA) {
struct mpint_pos n, e, d, u, p, q; struct mpint_pos n, e, d, u, p, q;
int pos = 0; int pos = 0;
@ -2419,16 +2271,14 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
} }
alg = &ssh_rsa; alg = &ssh_rsa;
pos = 0; put_stringz(blob, "ssh-rsa");
pos += write_string(blob+pos, "ssh-rsa", 7); put_mp_ssh2_from_string(blob, e.start, e.bytes);
pos += put_mp(blob+pos, e.start, e.bytes); put_mp_ssh2_from_string(blob, n.start, n.bytes);
pos += put_mp(blob+pos, n.start, n.bytes); publen = blob->len;
publen = pos; put_string(blob, d.start, d.bytes);
pos += write_string(blob+pos, d.start, d.bytes); put_mp_ssh2_from_string(blob, q.start, q.bytes);
pos += put_mp(blob+pos, q.start, q.bytes); put_mp_ssh2_from_string(blob, p.start, p.bytes);
pos += put_mp(blob+pos, p.start, p.bytes); put_mp_ssh2_from_string(blob, u.start, u.bytes);
pos += put_mp(blob+pos, u.start, u.bytes);
privlen = pos - publen;
} else { } else {
struct mpint_pos p, q, g, x, y; struct mpint_pos p, q, g, x, y;
int pos = 4; int pos = 4;
@ -2450,22 +2300,19 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
} }
alg = &ssh_dss; alg = &ssh_dss;
pos = 0; put_stringz(blob, "ssh-dss");
pos += write_string(blob+pos, "ssh-dss", 7); put_mp_ssh2_from_string(blob, p.start, p.bytes);
pos += put_mp(blob+pos, p.start, p.bytes); put_mp_ssh2_from_string(blob, q.start, q.bytes);
pos += put_mp(blob+pos, q.start, q.bytes); put_mp_ssh2_from_string(blob, g.start, g.bytes);
pos += put_mp(blob+pos, g.start, g.bytes); put_mp_ssh2_from_string(blob, y.start, y.bytes);
pos += put_mp(blob+pos, y.start, y.bytes); publen = blob->len;
publen = pos; put_mp_ssh2_from_string(blob, x.start, x.bytes);
pos += put_mp(blob+pos, x.start, x.bytes);
privlen = pos - publen;
} }
assert(privlen > 0); /* should have bombed by now if not */
retkey = snew(struct ssh2_userkey); retkey = snew(struct ssh2_userkey);
retkey->alg = alg; retkey->alg = alg;
retkey->data = alg->createkey(alg, blob, publen, blob+publen, privlen); retkey->data = alg->createkey(alg, blob->u, publen,
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";
@ -2478,8 +2325,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
error: error:
if (blob) { if (blob) {
smemclr(blob, blobsize); strbuf_free(blob);
sfree(blob);
} }
smemclr(key->keyblob, key->keyblob_size); smemclr(key->keyblob, key->keyblob_size);
sfree(key->keyblob); sfree(key->keyblob);
@ -2492,11 +2338,9 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
int sshcom_write(const Filename *filename, struct ssh2_userkey *key, int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
char *passphrase) char *passphrase)
{ {
strbuf *pubblob, *privblob; strbuf *pubblob, *privblob, *outblob;
unsigned char *outblob;
int outlen;
struct mpint_pos numbers[6]; struct mpint_pos numbers[6];
int nnumbers, initial_zero, pos, lenpos, i; int nnumbers, initial_zero, lenpos, i;
const char *type; const char *type;
char *ciphertext; char *ciphertext;
int cipherlen; int cipherlen;
@ -2577,54 +2421,38 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
exit(1); /* XXX: GCC doesn't understand assert() on some systems. */ exit(1); /* XXX: GCC doesn't understand assert() on some systems. */
} }
/* outblob = strbuf_new();
* Total size of key blob will be somewhere under 512 plus
* combined length of integers. We'll calculate the more
* precise size as we construct the blob.
*/
outlen = 512;
for (i = 0; i < nnumbers; i++)
outlen += 4 + numbers[i].bytes;
outblob = snewn(outlen, unsigned char);
/* /*
* Create the unencrypted key blob. * Create the unencrypted key blob.
*/ */
pos = 0; put_uint32(outblob, SSHCOM_MAGIC_NUMBER);
PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4; put_uint32(outblob, 0); /* length field, fill in later */
pos += 4; /* length field, fill in later */ put_stringz(outblob, type);
pos += write_string(outblob+pos, type, strlen(type)); put_stringz(outblob, passphrase ? "3des-cbc" : "none");
{ lenpos = outblob->len; /* remember this position */
const char *ciphertype = passphrase ? "3des-cbc" : "none"; put_uint32(outblob, 0); /* encrypted-blob size */
pos += write_string(outblob+pos, ciphertype, strlen(ciphertype)); put_uint32(outblob, 0); /* encrypted-payload size */
} if (initial_zero)
lenpos = pos; /* remember this position */ put_uint32(outblob, 0);
pos += 4; /* encrypted-blob size */
pos += 4; /* encrypted-payload size */
if (initial_zero) {
PUT_32BIT(outblob+pos, 0);
pos += 4;
}
for (i = 0; i < nnumbers; i++) for (i = 0; i < nnumbers; i++)
pos += sshcom_put_mpint(outblob+pos, put_mp_sshcom_from_string(outblob, numbers[i].start, numbers[i].bytes);
numbers[i].start, numbers[i].bytes);
/* Now wrap up the encrypted payload. */ /* Now wrap up the encrypted payload. */
PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8)); PUT_32BIT(outblob->s + lenpos + 4,
outblob->len - (lenpos + 8));
/* Pad encrypted blob to a multiple of cipher block size. */ /* Pad encrypted blob to a multiple of cipher block size. */
if (passphrase) { if (passphrase) {
int padding = -(pos - (lenpos+4)) & 7; int padding = -(outblob->len - (lenpos+4)) & 7;
while (padding--) while (padding--)
outblob[pos++] = random_byte(); put_byte(outblob, random_byte());
} }
ciphertext = (char *)outblob+lenpos+4; ciphertext = outblob->s + lenpos + 4;
cipherlen = pos - (lenpos+4); cipherlen = outblob->len - (lenpos + 4);
assert(!passphrase || cipherlen % 8 == 0); assert(!passphrase || cipherlen % 8 == 0);
/* Wrap up the encrypted blob string. */ /* Wrap up the encrypted blob string. */
PUT_32BIT(outblob+lenpos, cipherlen); PUT_32BIT(outblob->s + lenpos, cipherlen);
/* And finally fill in the total length field. */ /* And finally fill in the total length field. */
PUT_32BIT(outblob+4, pos); PUT_32BIT(outblob->s + 4, outblob->len);
assert(pos < outlen);
/* /*
* Encrypt the key. * Encrypt the key.
@ -2686,16 +2514,14 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
} }
fprintf(fp, "%s\"\n", c); fprintf(fp, "%s\"\n", c);
} }
base64_encode(fp, outblob, pos, 70); base64_encode(fp, outblob->u, outblob->len, 70);
fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
fclose(fp); fclose(fp);
ret = 1; ret = 1;
error: error:
if (outblob) { if (outblob)
smemclr(outblob, outlen); strbuf_free(outblob);
sfree(outblob);
}
if (privblob) if (privblob)
strbuf_free(privblob); strbuf_free(privblob);
if (pubblob) if (pubblob)

161
sshpubk.c
View File

@ -329,88 +329,64 @@ int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs,
int rsa_ssh1_savekey(const Filename *filename, struct RSAKey *key, int rsa_ssh1_savekey(const Filename *filename, struct RSAKey *key,
char *passphrase) char *passphrase)
{ {
unsigned char buf[16384]; strbuf *buf = strbuf_new();
unsigned char keybuf[16]; int estart;
struct MD5Context md5c;
unsigned char *p, *estart;
FILE *fp; FILE *fp;
/* /*
* Write the initial signature. * The public part of the key.
*/ */
p = buf; put_data(buf, rsa_signature, sizeof(rsa_signature));
memcpy(p, rsa_signature, sizeof(rsa_signature)); put_byte(buf, passphrase ? SSH_CIPHER_3DES : 0); /* encryption type */
p += sizeof(rsa_signature); put_uint32(buf, 0); /* reserved */
rsa_ssh1_public_blob(BinarySink_UPCAST(buf), key,
/* RSA_SSH1_MODULUS_FIRST);
* One byte giving encryption type, and one reserved (zero) put_stringz(buf, NULLTOEMPTY(key->comment));
* uint32.
*/
*p++ = (passphrase ? SSH_CIPHER_3DES : 0);
PUT_32BIT(p, 0);
p += 4;
/*
* An ordinary SSH-1 public key consists of: a uint32
* containing the bit count, then two bignums containing the
* modulus and exponent respectively.
*/
PUT_32BIT(p, bignum_bitcount(key->modulus));
p += 4;
p += ssh1_write_bignum(p, key->modulus);
p += ssh1_write_bignum(p, key->exponent);
/*
* A string containing the comment field.
*/
if (key->comment) {
PUT_32BIT(p, strlen(key->comment));
p += 4;
memcpy(p, key->comment, strlen(key->comment));
p += strlen(key->comment);
} else {
PUT_32BIT(p, 0);
p += 4;
}
/* /*
* The encrypted portion starts here. * The encrypted portion starts here.
*/ */
estart = p; estart = buf->len;
/* /*
* Two bytes, then the same two bytes repeated. * Two bytes, then the same two bytes repeated.
*/ */
*p++ = random_byte(); {
*p++ = random_byte(); unsigned char b0 = random_byte();
p[0] = p[-2]; unsigned char b1 = random_byte();
p[1] = p[-1]; put_byte(buf, b0);
p += 2; put_byte(buf, b1);
put_byte(buf, b0);
put_byte(buf, b1);
}
/* /*
* Four more bignums: the decryption exponent, then iqmp, then * Four more bignums: the decryption exponent, then iqmp, then
* q, then p. * q, then p.
*/ */
p += ssh1_write_bignum(p, key->private_exponent); put_mp_ssh1(buf, key->private_exponent);
p += ssh1_write_bignum(p, key->iqmp); put_mp_ssh1(buf, key->iqmp);
p += ssh1_write_bignum(p, key->q); put_mp_ssh1(buf, key->q);
p += ssh1_write_bignum(p, key->p); put_mp_ssh1(buf, key->p);
/* /*
* Now write zeros until the encrypted portion is a multiple of * Now write zeros until the encrypted portion is a multiple of
* 8 bytes. * 8 bytes.
*/ */
while ((p - estart) % 8) while ((buf->len - estart) % 8)
*p++ = '\0'; put_byte(buf, 0);
/* /*
* Now encrypt the encrypted portion. * Now encrypt the encrypted portion.
*/ */
if (passphrase) { if (passphrase) {
struct MD5Context md5c;
unsigned char keybuf[16];
MD5Init(&md5c); MD5Init(&md5c);
put_data(&md5c, passphrase, strlen(passphrase)); put_data(&md5c, passphrase, strlen(passphrase));
MD5Final(keybuf, &md5c); MD5Final(keybuf, &md5c);
des3_encrypt_pubkey(keybuf, estart, p - estart); des3_encrypt_pubkey(keybuf, buf->u + estart, buf->len - estart);
smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */
} }
@ -419,7 +395,7 @@ int rsa_ssh1_savekey(const Filename *filename, struct RSAKey *key,
*/ */
fp = f_open(filename, "wb", TRUE); fp = f_open(filename, "wb", TRUE);
if (fp) { if (fp) {
int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf)); int ret = (fwrite(buf->u, 1, buf->len, fp) == (size_t) (buf->len));
if (fclose(fp)) if (fclose(fp))
ret = 0; ret = 0;
return ret; return ret;
@ -773,35 +749,23 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
{ {
char realmac[41]; char realmac[41];
unsigned char binary[20]; unsigned char binary[20];
unsigned char *macdata; strbuf *macdata;
int maclen;
int free_macdata; int free_macdata;
if (old_fmt) { if (old_fmt) {
/* MAC (or hash) only covers the private blob. */ /* MAC (or hash) only covers the private blob. */
macdata = private_blob->u; macdata = private_blob;
maclen = private_blob->len; free_macdata = FALSE;
free_macdata = 0;
} else { } else {
unsigned char *p; macdata = strbuf_new();
int namelen = strlen(alg->name); put_stringz(macdata, alg->name);
int enclen = strlen(encryption); put_stringz(macdata, encryption);
int commlen = strlen(comment); put_stringz(macdata, comment);
maclen = (4 + namelen + put_string(macdata, public_blob->s,
4 + enclen + public_blob->len);
4 + commlen + put_string(macdata, private_blob->s,
4 + public_blob->len + private_blob->len);
4 + private_blob->len); free_macdata = TRUE;
macdata = snewn(maclen, unsigned char);
p = macdata;
#define DO_STR(s,len) PUT_32BIT(p,(len));memcpy(p+4,(s),(len));p+=4+(len)
DO_STR(alg->name, namelen);
DO_STR(encryption, enclen);
DO_STR(comment, commlen);
DO_STR(public_blob->s, public_blob->len);
DO_STR(private_blob->s, private_blob->len);
free_macdata = 1;
} }
if (is_mac) { if (is_mac) {
@ -815,18 +779,17 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
put_data(&s, passphrase, passlen); put_data(&s, passphrase, passlen);
SHA_Final(&s, mackey); SHA_Final(&s, mackey);
hmac_sha1_simple(mackey, 20, macdata, maclen, binary); hmac_sha1_simple(mackey, 20, macdata->s,
macdata->len, binary);
smemclr(mackey, sizeof(mackey)); smemclr(mackey, sizeof(mackey));
smemclr(&s, sizeof(s)); smemclr(&s, sizeof(s));
} else { } else {
SHA_Simple(macdata, maclen, binary); SHA_Simple(macdata->s, macdata->len, binary);
} }
if (free_macdata) { if (free_macdata)
smemclr(macdata, maclen); strbuf_free(macdata);
sfree(macdata);
}
for (i = 0; i < 20; i++) for (i = 0; i < 20; i++)
sprintf(realmac + 2 * i, "%02x", binary[i]); sprintf(realmac + 2 * i, "%02x", binary[i]);
@ -1333,38 +1296,26 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
/* Now create the MAC. */ /* Now create the MAC. */
{ {
unsigned char *macdata; strbuf *macdata;
int maclen;
unsigned char *p;
int namelen = strlen(key->alg->name);
int enclen = strlen(cipherstr);
int commlen = strlen(key->comment);
SHA_State s; SHA_State s;
unsigned char mackey[20]; unsigned char mackey[20];
char header[] = "putty-private-key-file-mac-key"; char header[] = "putty-private-key-file-mac-key";
maclen = (4 + namelen + macdata = strbuf_new();
4 + enclen + put_stringz(macdata, key->alg->name);
4 + commlen + put_stringz(macdata, cipherstr);
4 + pub_blob->len + put_stringz(macdata, key->comment);
4 + priv_encrypted_len); put_string(macdata, pub_blob->s, pub_blob->len);
macdata = snewn(maclen, unsigned char); put_string(macdata, priv_blob_encrypted, priv_encrypted_len);
p = macdata;
#define DO_STR(s,len) PUT_32BIT(p,(len));memcpy(p+4,(s),(len));p+=4+(len)
DO_STR(key->alg->name, namelen);
DO_STR(cipherstr, enclen);
DO_STR(key->comment, commlen);
DO_STR(pub_blob->u, pub_blob->len);
DO_STR(priv_blob_encrypted, priv_encrypted_len);
SHA_Init(&s); SHA_Init(&s);
put_data(&s, header, sizeof(header)-1); put_data(&s, header, sizeof(header)-1);
if (passphrase) if (passphrase)
put_data(&s, passphrase, strlen(passphrase)); put_data(&s, passphrase, strlen(passphrase));
SHA_Final(&s, mackey); SHA_Final(&s, mackey);
hmac_sha1_simple(mackey, 20, macdata, maclen, priv_mac); hmac_sha1_simple(mackey, 20, macdata->s,
smemclr(macdata, maclen); macdata->len, priv_mac);
sfree(macdata); strbuf_free(macdata);
smemclr(mackey, sizeof(mackey)); smemclr(mackey, sizeof(mackey));
smemclr(&s, sizeof(s)); smemclr(&s, sizeof(s));
} }