mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +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:
parent
e27ddf6d28
commit
8ce0a67028
2
Recipe
2
Recipe
@ -271,7 +271,7 @@ UXMISC = MISCNET uxstore uxsel uxnet uxpeer uxmisc uxproxy time
|
||||
|
||||
# import.c and dependencies, for PuTTYgen-like utilities that have to
|
||||
# load foreign key files.
|
||||
IMPORT = import sshbcrypt sshblowf
|
||||
IMPORT = import sshbcrypt sshblowf marshal
|
||||
|
||||
# Character set library, for use in pterm.
|
||||
CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
|
||||
|
626
import.c
626
import.c
@ -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
|
||||
* 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) {
|
||||
/*
|
||||
* Identifier is one byte.
|
||||
*/
|
||||
len++;
|
||||
if (d) *d++ = id | flags;
|
||||
put_byte(bs, id | flags);
|
||||
} else {
|
||||
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
|
||||
* each byte 1 except the last one which is 0.
|
||||
*/
|
||||
len++;
|
||||
if (d) *d++ = 0x1F | flags;
|
||||
put_byte(bs, 0x1F | flags);
|
||||
for (n = 1; (id >> (7*n)) > 0; n++)
|
||||
continue; /* count the bytes */
|
||||
while (n--) {
|
||||
len++;
|
||||
if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F);
|
||||
}
|
||||
while (n--)
|
||||
put_byte(bs, (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F));
|
||||
}
|
||||
|
||||
if (length < 128) {
|
||||
/*
|
||||
* Length is one byte.
|
||||
*/
|
||||
len++;
|
||||
if (d) *d++ = length;
|
||||
put_byte(bs, length);
|
||||
} else {
|
||||
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++)
|
||||
continue; /* count the bytes */
|
||||
len++;
|
||||
if (d) *d++ = 0x80 | n;
|
||||
while (n--) {
|
||||
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;
|
||||
put_byte(bs, 0x80 | n);
|
||||
while (n--)
|
||||
put_byte(bs, (length >> (8*n)) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
#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. */
|
||||
struct mpint_pos { void *start; int bytes; };
|
||||
|
||||
@ -358,6 +310,25 @@ struct openssh_pem_key {
|
||||
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,
|
||||
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 ssh2_userkey *retkey;
|
||||
unsigned char *p, *q;
|
||||
unsigned char *p;
|
||||
int ret, id, len, flags;
|
||||
int i, num_integers;
|
||||
struct ssh2_userkey *retval = NULL;
|
||||
const char *errmsg;
|
||||
unsigned char *blob;
|
||||
int blobsize = 0, blobptr, privptr;
|
||||
strbuf *blob = strbuf_new();
|
||||
int privptr = 0, publen;
|
||||
char *modptr = NULL;
|
||||
int modlen = 0;
|
||||
|
||||
blob = NULL;
|
||||
|
||||
if (!key)
|
||||
return NULL;
|
||||
|
||||
@ -682,7 +651,6 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
|
||||
int privlen;
|
||||
const struct ssh_signkey *alg;
|
||||
const struct ec_curve *curve;
|
||||
int algnamelen, curvenamelen;
|
||||
/* Read INTEGER 1 */
|
||||
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
|
||||
&id, &len, &flags);
|
||||
@ -762,40 +730,16 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
|
||||
goto error;
|
||||
}
|
||||
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);
|
||||
PUT_32BIT(q, algnamelen); q += 4;
|
||||
memcpy(q, alg->name, algnamelen); q += algnamelen;
|
||||
|
||||
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);
|
||||
retkey->data = retkey->alg->createkey(
|
||||
retkey->alg, blob->u, publen,
|
||||
blob->u + publen, blob->len - publen);
|
||||
|
||||
if (!retkey->data) {
|
||||
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) {
|
||||
|
||||
/*
|
||||
* 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;
|
||||
put_stringz(blob, key->keytype == OP_DSA ? "ssh-dss" : "ssh-rsa");
|
||||
|
||||
for (i = 0; i < num_integers; i++) {
|
||||
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;
|
||||
modlen = len;
|
||||
} else if (i != 6 && i != 7) {
|
||||
PUT_32BIT(blob+blobptr, len);
|
||||
memcpy(blob+blobptr+4, p, len);
|
||||
blobptr += 4+len;
|
||||
put_mp_ssh2_from_string(blob, p, len);
|
||||
if (i == 2) {
|
||||
PUT_32BIT(blob+blobptr, modlen);
|
||||
memcpy(blob+blobptr+4, modptr, modlen);
|
||||
blobptr += 4+modlen;
|
||||
privptr = blobptr;
|
||||
put_mp_ssh2_from_string(blob, modptr, modlen);
|
||||
privptr = blob->len;
|
||||
}
|
||||
}
|
||||
} 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
|
||||
* into the private blob.
|
||||
*/
|
||||
PUT_32BIT(blob+blobptr, len);
|
||||
memcpy(blob+blobptr+4, p, len);
|
||||
blobptr += 4+len;
|
||||
put_mp_ssh2_from_string(blob, p, len);
|
||||
if (i == 4)
|
||||
privptr = blobptr;
|
||||
privptr = blob->len;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
retkey = snew(struct ssh2_userkey);
|
||||
retkey->alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
|
||||
retkey->data = retkey->alg->createkey(retkey->alg, blob, privptr,
|
||||
blob+privptr,
|
||||
blobptr-privptr);
|
||||
retkey->data = retkey->alg->createkey(
|
||||
retkey->alg, blob->u, privptr, blob->u+privptr, blob->len-privptr);
|
||||
|
||||
if (!retkey->data) {
|
||||
sfree(retkey);
|
||||
errmsg = "unable to create key data structure";
|
||||
@ -909,10 +836,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
|
||||
retval = retkey;
|
||||
|
||||
error:
|
||||
if (blob) {
|
||||
smemclr(blob, blobsize);
|
||||
sfree(blob);
|
||||
}
|
||||
strbuf_free(blob);
|
||||
smemclr(key->keyblob, key->keyblob_size);
|
||||
sfree(key->keyblob);
|
||||
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,
|
||||
char *passphrase)
|
||||
{
|
||||
strbuf *pubblob, *privblob;
|
||||
strbuf *pubblob, *privblob, *outblob;
|
||||
unsigned char *spareblob;
|
||||
int sparelen = 0;
|
||||
unsigned char *outblob;
|
||||
int outlen;
|
||||
struct mpint_pos numbers[9];
|
||||
int nnumbers, pos, len, seqlen, i;
|
||||
int nnumbers, i;
|
||||
const char *header, *footer;
|
||||
char zero[1];
|
||||
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));
|
||||
privblob = strbuf_new();
|
||||
key->alg->private_blob(key->data, BinarySink_UPCAST(privblob));
|
||||
spareblob = outblob = NULL;
|
||||
spareblob = NULL;
|
||||
|
||||
outblob = NULL;
|
||||
len = 0;
|
||||
outblob = strbuf_new();
|
||||
|
||||
/*
|
||||
* Encode the OpenSSH key blob, and also decide on the header
|
||||
* line.
|
||||
*/
|
||||
if (key->alg == &ssh_rsa || key->alg == &ssh_dss) {
|
||||
strbuf *seq;
|
||||
|
||||
/*
|
||||
* The RSA and DSS handlers share some code because the two
|
||||
* 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";
|
||||
}
|
||||
|
||||
/*
|
||||
* Now count up the total size of the ASN.1 encoded integers,
|
||||
* so as to determine the length of the containing SEQUENCE.
|
||||
*/
|
||||
len = 0;
|
||||
seq = strbuf_new();
|
||||
for (i = 0; i < nnumbers; i++) {
|
||||
len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);
|
||||
len += 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(seq, 2, numbers[i].bytes, 0);
|
||||
put_data(seq, numbers[i].start, 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 ||
|
||||
key->alg == &ssh_ecdsa_nistp384 ||
|
||||
key->alg == &ssh_ecdsa_nistp521) {
|
||||
const unsigned char *oid;
|
||||
int oidlen;
|
||||
int pointlen;
|
||||
strbuf *seq, *sub;
|
||||
|
||||
/*
|
||||
* 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
|
||||
+ 7) / 8 * 2;
|
||||
|
||||
len = ber_write_id_len(NULL, 2, 1, 0);
|
||||
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;
|
||||
seq = strbuf_new();
|
||||
|
||||
seqlen = len;
|
||||
len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
|
||||
/* INTEGER 1 */
|
||||
put_ber_id_len(seq, 2, 1, 0);
|
||||
put_byte(seq, 1);
|
||||
|
||||
outblob = snewn(len, unsigned char);
|
||||
assert(outblob);
|
||||
/* OCTET STRING private key */
|
||||
put_ber_id_len(seq, 4, privblob->len - 4, 0);
|
||||
put_data(seq, privblob->s + 4, privblob->len - 4);
|
||||
|
||||
pos = 0;
|
||||
pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
|
||||
pos += ber_write_id_len(outblob+pos, 2, 1, 0);
|
||||
outblob[pos++] = 1;
|
||||
pos += ber_write_id_len(outblob+pos, 4, privblob->len - 4, 0);
|
||||
memcpy(outblob+pos, privblob->u + 4, privblob->len - 4);
|
||||
pos += privblob->len - 4;
|
||||
pos += ber_write_id_len(outblob+pos, 0, oidlen +
|
||||
ber_write_id_len(NULL, 6, oidlen, 0),
|
||||
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
|
||||
pos += ber_write_id_len(outblob+pos, 6, oidlen, 0);
|
||||
memcpy(outblob+pos, oid, oidlen);
|
||||
pos += oidlen;
|
||||
pos += ber_write_id_len(outblob+pos, 1, 2 + pointlen +
|
||||
ber_write_id_len(NULL, 3, 2 + pointlen, 0),
|
||||
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
|
||||
pos += ber_write_id_len(outblob+pos, 3, 2 + pointlen, 0);
|
||||
outblob[pos++] = 0;
|
||||
memcpy(outblob+pos, pubblob->u+39, 1 + pointlen);
|
||||
pos += 1 + pointlen;
|
||||
/* Subsidiary OID */
|
||||
sub = strbuf_new();
|
||||
put_ber_id_len(sub, 6, oidlen, 0);
|
||||
put_data(sub, oid, oidlen);
|
||||
|
||||
/* Append the OID to the sequence */
|
||||
put_ber_id_len(seq, 0, sub->len,
|
||||
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
|
||||
put_data(seq, sub->s, sub->len);
|
||||
strbuf_free(sub);
|
||||
|
||||
/* Subsidiary BIT STRING */
|
||||
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);
|
||||
put_data(seq, sub->s, sub->len);
|
||||
strbuf_free(sub);
|
||||
|
||||
/* 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";
|
||||
footer = "-----END EC PRIVATE KEY-----\n";
|
||||
@ -1156,19 +1056,7 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
|
||||
if (passphrase) {
|
||||
struct MD5Context md5c;
|
||||
unsigned char keybuf[32];
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
int origlen, outlen, pad, i;
|
||||
|
||||
/*
|
||||
* 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
|
||||
* returned.
|
||||
*/
|
||||
assert(pos == len);
|
||||
while (pos < outlen) {
|
||||
outblob[pos++] = outlen - len;
|
||||
}
|
||||
origlen = outblob->len;
|
||||
outlen = (origlen + 8) &~ 7;
|
||||
pad = outlen - origlen;
|
||||
for (i = 0; i < pad; i++)
|
||||
put_byte(outblob, pad);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen);
|
||||
des3_encrypt_pubkey_ossh(keybuf, iv,
|
||||
outblob->u, outlen);
|
||||
|
||||
smemclr(&md5c, sizeof(md5c));
|
||||
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, "\n\n");
|
||||
}
|
||||
base64_encode(fp, outblob, outlen, 64);
|
||||
base64_encode(fp, outblob->u, outblob->len, 64);
|
||||
fputs(footer, fp);
|
||||
fclose(fp);
|
||||
ret = 1;
|
||||
|
||||
error:
|
||||
if (outblob) {
|
||||
smemclr(outblob, outlen);
|
||||
sfree(outblob);
|
||||
}
|
||||
if (outblob)
|
||||
strbuf_free(outblob);
|
||||
if (spareblob) {
|
||||
smemclr(spareblob, sparelen);
|
||||
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,
|
||||
char *passphrase)
|
||||
{
|
||||
strbuf *pubblob, *privblob;
|
||||
unsigned char *outblob, *p;
|
||||
unsigned char *private_section_start, *private_section_length_field;
|
||||
int commentlen, maxsize, padvalue, i;
|
||||
strbuf *pubblob, *privblob, *cblob;
|
||||
int padvalue, i;
|
||||
unsigned checkint;
|
||||
int ret = 0;
|
||||
unsigned char bcrypt_salt[16];
|
||||
@ -1763,113 +1643,88 @@ int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
|
||||
key->alg->public_blob(key->data, BinarySink_UPCAST(pubblob));
|
||||
privblob = strbuf_new();
|
||||
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.
|
||||
*/
|
||||
p = outblob;
|
||||
cblob = strbuf_new();
|
||||
|
||||
/* Magic number. */
|
||||
memcpy(p, "openssh-key-v1\0", 15);
|
||||
p += 15;
|
||||
put_asciz(cblob, "openssh-key-v1");
|
||||
|
||||
/* Cipher and kdf names, and kdf options. */
|
||||
if (!passphrase) {
|
||||
memset(bcrypt_salt, 0, sizeof(bcrypt_salt)); /* prevent warnings */
|
||||
p += write_string_z(p, "none");
|
||||
p += write_string_z(p, "none");
|
||||
p += write_string_z(p, "");
|
||||
put_stringz(cblob, "none");
|
||||
put_stringz(cblob, "none");
|
||||
put_stringz(cblob, "");
|
||||
} else {
|
||||
unsigned char *q;
|
||||
strbuf *substr;
|
||||
|
||||
for (i = 0; i < (int)sizeof(bcrypt_salt); i++)
|
||||
bcrypt_salt[i] = random_byte();
|
||||
p += write_string_z(p, "aes256-ctr");
|
||||
p += write_string_z(p, "bcrypt");
|
||||
q = p;
|
||||
p += 4;
|
||||
p += write_string(p, bcrypt_salt, sizeof(bcrypt_salt));
|
||||
p += write_uint32(p, bcrypt_rounds);
|
||||
PUT_32BIT_MSB_FIRST(q, (unsigned)(p - (q+4)));
|
||||
put_stringz(cblob, "aes256-ctr");
|
||||
put_stringz(cblob, "bcrypt");
|
||||
substr = strbuf_new();
|
||||
put_string(substr, bcrypt_salt, sizeof(bcrypt_salt));
|
||||
put_uint32(substr, bcrypt_rounds);
|
||||
put_stringsb(cblob, substr);
|
||||
}
|
||||
|
||||
/* Number of keys. */
|
||||
p += write_uint32(p, 1);
|
||||
put_uint32(cblob, 1);
|
||||
|
||||
/* Public blob. */
|
||||
p += write_string(p, pubblob->u, pubblob->len);
|
||||
put_string(cblob, pubblob->s, pubblob->len);
|
||||
|
||||
/* Begin private section. */
|
||||
private_section_length_field = p;
|
||||
p += 4;
|
||||
private_section_start = p;
|
||||
/* Private section. */
|
||||
{
|
||||
strbuf *cpblob = strbuf_new();
|
||||
|
||||
/* checkint. */
|
||||
checkint = 0;
|
||||
for (i = 0; i < 4; i++)
|
||||
checkint = (checkint << 8) + random_byte();
|
||||
p += write_uint32(p, checkint);
|
||||
p += write_uint32(p, checkint);
|
||||
/* checkint. */
|
||||
checkint = 0;
|
||||
for (i = 0; i < 4; i++)
|
||||
checkint = (checkint << 8) + random_byte();
|
||||
put_uint32(cpblob, checkint);
|
||||
put_uint32(cpblob, checkint);
|
||||
|
||||
/* Private key. The main private blob goes inline, with no string
|
||||
* wrapper. */
|
||||
p += write_string_z(p, key->alg->name);
|
||||
memcpy(p, privblob->u, privblob->len);
|
||||
p += privblob->len;
|
||||
/* Private key. The main private blob goes inline, with no string
|
||||
* wrapper. */
|
||||
put_stringz(cpblob, key->alg->name);
|
||||
put_data(cpblob, privblob->s, privblob->len);
|
||||
|
||||
/* Comment. */
|
||||
p += write_string_z(p, key->comment);
|
||||
/* Comment. */
|
||||
put_stringz(cpblob, key->comment);
|
||||
|
||||
/* Pad out the encrypted section. */
|
||||
padvalue = 1;
|
||||
do {
|
||||
*p++ = padvalue++;
|
||||
} while ((p - private_section_start) & 15);
|
||||
/* Pad out the encrypted section. */
|
||||
padvalue = 1;
|
||||
do {
|
||||
put_byte(cpblob, padvalue++);
|
||||
} while (cpblob->len & 15);
|
||||
|
||||
assert(p - outblob < maxsize);
|
||||
if (passphrase) {
|
||||
/*
|
||||
* Encrypt the private section. We need 48 bytes of key
|
||||
* material: 32 bytes AES key + 16 bytes iv.
|
||||
*/
|
||||
unsigned char keybuf[48];
|
||||
void *ctx;
|
||||
|
||||
/* Go back and fill in the length field for the private section. */
|
||||
PUT_32BIT_MSB_FIRST(private_section_length_field,
|
||||
p - private_section_start);
|
||||
openssh_bcrypt(passphrase,
|
||||
bcrypt_salt, sizeof(bcrypt_salt), bcrypt_rounds,
|
||||
keybuf, sizeof(keybuf));
|
||||
|
||||
if (passphrase) {
|
||||
/*
|
||||
* Encrypt the private section. We need 48 bytes of key
|
||||
* material: 32 bytes AES key + 16 bytes iv.
|
||||
*/
|
||||
unsigned char keybuf[48];
|
||||
void *ctx;
|
||||
ctx = aes_make_context();
|
||||
aes256_key(ctx, keybuf);
|
||||
aes_iv(ctx, keybuf + 32);
|
||||
aes_ssh2_sdctr(ctx, cpblob->u,
|
||||
cpblob->len);
|
||||
aes_free_context(ctx);
|
||||
|
||||
openssh_bcrypt(passphrase,
|
||||
bcrypt_salt, sizeof(bcrypt_salt), bcrypt_rounds,
|
||||
keybuf, sizeof(keybuf));
|
||||
smemclr(keybuf, sizeof(keybuf));
|
||||
}
|
||||
|
||||
ctx = aes_make_context();
|
||||
aes256_key(ctx, keybuf);
|
||||
aes_iv(ctx, keybuf + 32);
|
||||
aes_ssh2_sdctr(ctx, private_section_start,
|
||||
p - private_section_start);
|
||||
aes_free_context(ctx);
|
||||
|
||||
smemclr(keybuf, sizeof(keybuf));
|
||||
put_stringsb(cblob, cpblob);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1880,16 +1735,14 @@ int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
|
||||
if (!fp)
|
||||
goto error;
|
||||
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);
|
||||
fclose(fp);
|
||||
ret = 1;
|
||||
|
||||
error:
|
||||
if (outblob) {
|
||||
smemclr(outblob, maxsize);
|
||||
sfree(outblob);
|
||||
}
|
||||
if (cblob)
|
||||
strbuf_free(cblob);
|
||||
if (privblob)
|
||||
strbuf_free(privblob);
|
||||
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 */
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned char *i = (unsigned char *)data;
|
||||
int bits = len * 8 - 1;
|
||||
const unsigned char *bytes = (const unsigned char *)bytesv;
|
||||
int bits = nbytes * 8 - 1;
|
||||
|
||||
while (bits > 0) {
|
||||
if (*i & (1 << (bits & 7)))
|
||||
if (*bytes & (1 << (bits & 7)))
|
||||
break;
|
||||
if (!(bits-- & 7))
|
||||
i++, len--;
|
||||
bytes++, nbytes--;
|
||||
}
|
||||
|
||||
PUT_32BIT(d, bits+1);
|
||||
memcpy(d+4, i, len);
|
||||
return len+4;
|
||||
put_uint32(bs, bits+1);
|
||||
put_data(bs, bytes, nbytes);
|
||||
}
|
||||
|
||||
#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,
|
||||
const char **errmsg_p)
|
||||
{
|
||||
struct sshcom_key *key = load_sshcom_key(filename, errmsg_p);
|
||||
const char *errmsg;
|
||||
int pos, len;
|
||||
int pos, len, publen;
|
||||
const char prefix_rsa[] = "if-modn{sign{rsa";
|
||||
const char prefix_dsa[] = "dl-modp{sign{dsa";
|
||||
enum { RSA, DSA } type;
|
||||
@ -2264,8 +2119,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
|
||||
int cipherlen;
|
||||
struct ssh2_userkey *ret = NULL, *retkey;
|
||||
const struct ssh_signkey *alg;
|
||||
unsigned char *blob = NULL;
|
||||
int blobsize = 0, publen, privlen;
|
||||
strbuf *blob = NULL;
|
||||
|
||||
if (!key)
|
||||
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
|
||||
* end up feeding them to alg->createkey().
|
||||
*/
|
||||
blobsize = cipherlen + 256;
|
||||
blob = snewn(blobsize, unsigned char);
|
||||
privlen = 0;
|
||||
blob = strbuf_new();
|
||||
if (type == RSA) {
|
||||
struct mpint_pos n, e, d, u, p, q;
|
||||
int pos = 0;
|
||||
@ -2419,16 +2271,14 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
|
||||
}
|
||||
|
||||
alg = &ssh_rsa;
|
||||
pos = 0;
|
||||
pos += write_string(blob+pos, "ssh-rsa", 7);
|
||||
pos += put_mp(blob+pos, e.start, e.bytes);
|
||||
pos += put_mp(blob+pos, n.start, n.bytes);
|
||||
publen = pos;
|
||||
pos += write_string(blob+pos, d.start, d.bytes);
|
||||
pos += put_mp(blob+pos, q.start, q.bytes);
|
||||
pos += put_mp(blob+pos, p.start, p.bytes);
|
||||
pos += put_mp(blob+pos, u.start, u.bytes);
|
||||
privlen = pos - publen;
|
||||
put_stringz(blob, "ssh-rsa");
|
||||
put_mp_ssh2_from_string(blob, e.start, e.bytes);
|
||||
put_mp_ssh2_from_string(blob, n.start, n.bytes);
|
||||
publen = blob->len;
|
||||
put_string(blob, d.start, d.bytes);
|
||||
put_mp_ssh2_from_string(blob, q.start, q.bytes);
|
||||
put_mp_ssh2_from_string(blob, p.start, p.bytes);
|
||||
put_mp_ssh2_from_string(blob, u.start, u.bytes);
|
||||
} else {
|
||||
struct mpint_pos p, q, g, x, y;
|
||||
int pos = 4;
|
||||
@ -2450,22 +2300,19 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
|
||||
}
|
||||
|
||||
alg = &ssh_dss;
|
||||
pos = 0;
|
||||
pos += write_string(blob+pos, "ssh-dss", 7);
|
||||
pos += put_mp(blob+pos, p.start, p.bytes);
|
||||
pos += put_mp(blob+pos, q.start, q.bytes);
|
||||
pos += put_mp(blob+pos, g.start, g.bytes);
|
||||
pos += put_mp(blob+pos, y.start, y.bytes);
|
||||
publen = pos;
|
||||
pos += put_mp(blob+pos, x.start, x.bytes);
|
||||
privlen = pos - publen;
|
||||
put_stringz(blob, "ssh-dss");
|
||||
put_mp_ssh2_from_string(blob, p.start, p.bytes);
|
||||
put_mp_ssh2_from_string(blob, q.start, q.bytes);
|
||||
put_mp_ssh2_from_string(blob, g.start, g.bytes);
|
||||
put_mp_ssh2_from_string(blob, y.start, y.bytes);
|
||||
publen = blob->len;
|
||||
put_mp_ssh2_from_string(blob, x.start, x.bytes);
|
||||
}
|
||||
|
||||
assert(privlen > 0); /* should have bombed by now if not */
|
||||
|
||||
retkey = snew(struct ssh2_userkey);
|
||||
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) {
|
||||
sfree(retkey);
|
||||
errmsg = "unable to create key data structure";
|
||||
@ -2478,8 +2325,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
|
||||
|
||||
error:
|
||||
if (blob) {
|
||||
smemclr(blob, blobsize);
|
||||
sfree(blob);
|
||||
strbuf_free(blob);
|
||||
}
|
||||
smemclr(key->keyblob, key->keyblob_size);
|
||||
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,
|
||||
char *passphrase)
|
||||
{
|
||||
strbuf *pubblob, *privblob;
|
||||
unsigned char *outblob;
|
||||
int outlen;
|
||||
strbuf *pubblob, *privblob, *outblob;
|
||||
struct mpint_pos numbers[6];
|
||||
int nnumbers, initial_zero, pos, lenpos, i;
|
||||
int nnumbers, initial_zero, lenpos, i;
|
||||
const char *type;
|
||||
char *ciphertext;
|
||||
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. */
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
outblob = strbuf_new();
|
||||
|
||||
/*
|
||||
* Create the unencrypted key blob.
|
||||
*/
|
||||
pos = 0;
|
||||
PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4;
|
||||
pos += 4; /* length field, fill in later */
|
||||
pos += write_string(outblob+pos, type, strlen(type));
|
||||
{
|
||||
const char *ciphertype = passphrase ? "3des-cbc" : "none";
|
||||
pos += write_string(outblob+pos, ciphertype, strlen(ciphertype));
|
||||
}
|
||||
lenpos = pos; /* remember this position */
|
||||
pos += 4; /* encrypted-blob size */
|
||||
pos += 4; /* encrypted-payload size */
|
||||
if (initial_zero) {
|
||||
PUT_32BIT(outblob+pos, 0);
|
||||
pos += 4;
|
||||
}
|
||||
put_uint32(outblob, SSHCOM_MAGIC_NUMBER);
|
||||
put_uint32(outblob, 0); /* length field, fill in later */
|
||||
put_stringz(outblob, type);
|
||||
put_stringz(outblob, passphrase ? "3des-cbc" : "none");
|
||||
lenpos = outblob->len; /* remember this position */
|
||||
put_uint32(outblob, 0); /* encrypted-blob size */
|
||||
put_uint32(outblob, 0); /* encrypted-payload size */
|
||||
if (initial_zero)
|
||||
put_uint32(outblob, 0);
|
||||
for (i = 0; i < nnumbers; i++)
|
||||
pos += sshcom_put_mpint(outblob+pos,
|
||||
numbers[i].start, numbers[i].bytes);
|
||||
put_mp_sshcom_from_string(outblob, numbers[i].start, numbers[i].bytes);
|
||||
/* 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. */
|
||||
if (passphrase) {
|
||||
int padding = -(pos - (lenpos+4)) & 7;
|
||||
int padding = -(outblob->len - (lenpos+4)) & 7;
|
||||
while (padding--)
|
||||
outblob[pos++] = random_byte();
|
||||
put_byte(outblob, random_byte());
|
||||
}
|
||||
ciphertext = (char *)outblob+lenpos+4;
|
||||
cipherlen = pos - (lenpos+4);
|
||||
ciphertext = outblob->s + lenpos + 4;
|
||||
cipherlen = outblob->len - (lenpos + 4);
|
||||
assert(!passphrase || cipherlen % 8 == 0);
|
||||
/* 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. */
|
||||
PUT_32BIT(outblob+4, pos);
|
||||
|
||||
assert(pos < outlen);
|
||||
PUT_32BIT(outblob->s + 4, outblob->len);
|
||||
|
||||
/*
|
||||
* Encrypt the key.
|
||||
@ -2686,16 +2514,14 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
|
||||
}
|
||||
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);
|
||||
fclose(fp);
|
||||
ret = 1;
|
||||
|
||||
error:
|
||||
if (outblob) {
|
||||
smemclr(outblob, outlen);
|
||||
sfree(outblob);
|
||||
}
|
||||
if (outblob)
|
||||
strbuf_free(outblob);
|
||||
if (privblob)
|
||||
strbuf_free(privblob);
|
||||
if (pubblob)
|
||||
|
163
sshpubk.c
163
sshpubk.c
@ -329,88 +329,64 @@ int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs,
|
||||
int rsa_ssh1_savekey(const Filename *filename, struct RSAKey *key,
|
||||
char *passphrase)
|
||||
{
|
||||
unsigned char buf[16384];
|
||||
unsigned char keybuf[16];
|
||||
struct MD5Context md5c;
|
||||
unsigned char *p, *estart;
|
||||
strbuf *buf = strbuf_new();
|
||||
int estart;
|
||||
FILE *fp;
|
||||
|
||||
/*
|
||||
* Write the initial signature.
|
||||
* The public part of the key.
|
||||
*/
|
||||
p = buf;
|
||||
memcpy(p, rsa_signature, sizeof(rsa_signature));
|
||||
p += sizeof(rsa_signature);
|
||||
|
||||
/*
|
||||
* One byte giving encryption type, and one reserved (zero)
|
||||
* 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;
|
||||
}
|
||||
put_data(buf, rsa_signature, sizeof(rsa_signature));
|
||||
put_byte(buf, passphrase ? SSH_CIPHER_3DES : 0); /* encryption type */
|
||||
put_uint32(buf, 0); /* reserved */
|
||||
rsa_ssh1_public_blob(BinarySink_UPCAST(buf), key,
|
||||
RSA_SSH1_MODULUS_FIRST);
|
||||
put_stringz(buf, NULLTOEMPTY(key->comment));
|
||||
|
||||
/*
|
||||
* The encrypted portion starts here.
|
||||
*/
|
||||
estart = p;
|
||||
estart = buf->len;
|
||||
|
||||
/*
|
||||
* Two bytes, then the same two bytes repeated.
|
||||
*/
|
||||
*p++ = random_byte();
|
||||
*p++ = random_byte();
|
||||
p[0] = p[-2];
|
||||
p[1] = p[-1];
|
||||
p += 2;
|
||||
{
|
||||
unsigned char b0 = random_byte();
|
||||
unsigned char b1 = random_byte();
|
||||
put_byte(buf, b0);
|
||||
put_byte(buf, b1);
|
||||
put_byte(buf, b0);
|
||||
put_byte(buf, b1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Four more bignums: the decryption exponent, then iqmp, then
|
||||
* q, then p.
|
||||
*/
|
||||
p += ssh1_write_bignum(p, key->private_exponent);
|
||||
p += ssh1_write_bignum(p, key->iqmp);
|
||||
p += ssh1_write_bignum(p, key->q);
|
||||
p += ssh1_write_bignum(p, key->p);
|
||||
put_mp_ssh1(buf, key->private_exponent);
|
||||
put_mp_ssh1(buf, key->iqmp);
|
||||
put_mp_ssh1(buf, key->q);
|
||||
put_mp_ssh1(buf, key->p);
|
||||
|
||||
/*
|
||||
* Now write zeros until the encrypted portion is a multiple of
|
||||
* 8 bytes.
|
||||
*/
|
||||
while ((p - estart) % 8)
|
||||
*p++ = '\0';
|
||||
while ((buf->len - estart) % 8)
|
||||
put_byte(buf, 0);
|
||||
|
||||
/*
|
||||
* Now encrypt the encrypted portion.
|
||||
*/
|
||||
if (passphrase) {
|
||||
struct MD5Context md5c;
|
||||
unsigned char keybuf[16];
|
||||
|
||||
MD5Init(&md5c);
|
||||
put_data(&md5c, passphrase, strlen(passphrase));
|
||||
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 */
|
||||
}
|
||||
|
||||
@ -419,7 +395,7 @@ int rsa_ssh1_savekey(const Filename *filename, struct RSAKey *key,
|
||||
*/
|
||||
fp = f_open(filename, "wb", TRUE);
|
||||
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))
|
||||
ret = 0;
|
||||
return ret;
|
||||
@ -773,35 +749,23 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
|
||||
{
|
||||
char realmac[41];
|
||||
unsigned char binary[20];
|
||||
unsigned char *macdata;
|
||||
int maclen;
|
||||
int free_macdata;
|
||||
strbuf *macdata;
|
||||
int free_macdata;
|
||||
|
||||
if (old_fmt) {
|
||||
/* MAC (or hash) only covers the private blob. */
|
||||
macdata = private_blob->u;
|
||||
maclen = private_blob->len;
|
||||
free_macdata = 0;
|
||||
macdata = private_blob;
|
||||
free_macdata = FALSE;
|
||||
} else {
|
||||
unsigned char *p;
|
||||
int namelen = strlen(alg->name);
|
||||
int enclen = strlen(encryption);
|
||||
int commlen = strlen(comment);
|
||||
maclen = (4 + namelen +
|
||||
4 + enclen +
|
||||
4 + commlen +
|
||||
4 + public_blob->len +
|
||||
4 + private_blob->len);
|
||||
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;
|
||||
macdata = strbuf_new();
|
||||
put_stringz(macdata, alg->name);
|
||||
put_stringz(macdata, encryption);
|
||||
put_stringz(macdata, comment);
|
||||
put_string(macdata, public_blob->s,
|
||||
public_blob->len);
|
||||
put_string(macdata, private_blob->s,
|
||||
private_blob->len);
|
||||
free_macdata = TRUE;
|
||||
}
|
||||
|
||||
if (is_mac) {
|
||||
@ -815,18 +779,17 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
|
||||
put_data(&s, passphrase, passlen);
|
||||
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(&s, sizeof(s));
|
||||
} else {
|
||||
SHA_Simple(macdata, maclen, binary);
|
||||
SHA_Simple(macdata->s, macdata->len, binary);
|
||||
}
|
||||
|
||||
if (free_macdata) {
|
||||
smemclr(macdata, maclen);
|
||||
sfree(macdata);
|
||||
}
|
||||
if (free_macdata)
|
||||
strbuf_free(macdata);
|
||||
|
||||
for (i = 0; i < 20; 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. */
|
||||
{
|
||||
unsigned char *macdata;
|
||||
int maclen;
|
||||
unsigned char *p;
|
||||
int namelen = strlen(key->alg->name);
|
||||
int enclen = strlen(cipherstr);
|
||||
int commlen = strlen(key->comment);
|
||||
strbuf *macdata;
|
||||
SHA_State s;
|
||||
unsigned char mackey[20];
|
||||
char header[] = "putty-private-key-file-mac-key";
|
||||
|
||||
maclen = (4 + namelen +
|
||||
4 + enclen +
|
||||
4 + commlen +
|
||||
4 + pub_blob->len +
|
||||
4 + priv_encrypted_len);
|
||||
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(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);
|
||||
macdata = strbuf_new();
|
||||
put_stringz(macdata, key->alg->name);
|
||||
put_stringz(macdata, cipherstr);
|
||||
put_stringz(macdata, key->comment);
|
||||
put_string(macdata, pub_blob->s, pub_blob->len);
|
||||
put_string(macdata, priv_blob_encrypted, priv_encrypted_len);
|
||||
|
||||
SHA_Init(&s);
|
||||
put_data(&s, header, sizeof(header)-1);
|
||||
if (passphrase)
|
||||
put_data(&s, passphrase, strlen(passphrase));
|
||||
SHA_Final(&s, mackey);
|
||||
hmac_sha1_simple(mackey, 20, macdata, maclen, priv_mac);
|
||||
smemclr(macdata, maclen);
|
||||
sfree(macdata);
|
||||
hmac_sha1_simple(mackey, 20, macdata->s,
|
||||
macdata->len, priv_mac);
|
||||
strbuf_free(macdata);
|
||||
smemclr(mackey, sizeof(mackey));
|
||||
smemclr(&s, sizeof(s));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user