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:
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
|
# 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
574
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
|
* 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
161
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,
|
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));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user