diff --git a/Recipe b/Recipe index 77dd2bc5..f0183b88 100644 --- a/Recipe +++ b/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 diff --git a/import.c b/import.c index 5bbfe2dc..36d29539 100644 --- a/import.c +++ b/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) diff --git a/sshpubk.c b/sshpubk.c index f9b6e974..eca362b0 100644 --- a/sshpubk.c +++ b/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)); }