Newer
Older
/*
* Code for PuTTY to import and export private key files in other
* SSH clients' formats.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include "putty.h"
#include "ssh.h"
#include "misc.h"
static bool openssh_pem_encrypted(BinarySource *src);
static bool openssh_new_encrypted(BinarySource *src);
static ssh2_userkey *openssh_pem_read(
BinarySource *src, const char *passphrase, const char **errmsg_p);
static ssh2_userkey *openssh_new_read(
BinarySource *src, const char *passphrase, const char **errmsg_p);
static bool openssh_auto_write(
const Filename *file, ssh2_userkey *key, const char *passphrase);
static bool openssh_pem_write(
const Filename *file, ssh2_userkey *key, const char *passphrase);
static bool openssh_new_write(
const Filename *file, ssh2_userkey *key, const char *passphrase);
static bool sshcom_encrypted(BinarySource *src, char **comment);
static ssh2_userkey *sshcom_read(
BinarySource *src, const char *passphrase, const char **errmsg_p);
static bool sshcom_write(
const Filename *file, ssh2_userkey *key, const char *passphrase);
/*
* Given a key type, determine whether we know how to import it.
*/
bool import_possible(int type)
if (type == SSH_KEYTYPE_OPENSSH_PEM)
if (type == SSH_KEYTYPE_OPENSSH_NEW)
}
/*
* Given a key type, determine what native key type
* (SSH_KEYTYPE_SSH1 or SSH_KEYTYPE_SSH2) it will come out as once
* we've imported it.
*/
int import_target_type(int type)
{
/*
* There are no known foreign SSH-1 key formats.
*/
return SSH_KEYTYPE_SSH2;
}
static inline char *bsgetline(BinarySource *src)
{
ptrlen line = get_chomped_line(src);
if (get_err(src))
return NULL;
return mkstr(line);
}
/*
* Determine whether a foreign key is encrypted.
*/
bool import_encrypted_s(const Filename *filename, BinarySource *src,
int type, char **comment)
if (type == SSH_KEYTYPE_OPENSSH_PEM) {
/* OpenSSH PEM format doesn't contain a key comment at all */
*comment = dupstr(filename_to_str(filename));
return openssh_pem_encrypted(src);
} else if (type == SSH_KEYTYPE_OPENSSH_NEW) {
/* OpenSSH new format does, but it's inside the encrypted
* section for some reason */
*comment = dupstr(filename_to_str(filename));
return openssh_new_encrypted(src);
} else if (type == SSH_KEYTYPE_SSHCOM) {
return sshcom_encrypted(src, comment);
bool import_encrypted(const Filename *filename, int type, char **comment)
{
LoadedFile *lf = lf_load_keyfile(filename, NULL);
if (!lf)
return false; /* couldn't even open the file */
bool toret = import_encrypted_s(filename, BinarySource_UPCAST(lf),
type, comment);
lf_free(lf);
return toret;
}
* Import an SSH-1 key.
int import_ssh1_s(BinarySource *src, int type,
RSAKey *key, char *passphrase, const char **errmsg_p)
{
return 0;
}
int import_ssh1(const Filename *filename, int type,
RSAKey *key, char *passphrase, const char **errmsg_p)
LoadedFile *lf = lf_load_keyfile(filename, errmsg_p);
if (!lf)
return false;
int toret = import_ssh1_s(BinarySource_UPCAST(lf),
type, key, passphrase, errmsg_p);
lf_free(lf);
return toret;
}
/*
* Import an SSH-2 key.
ssh2_userkey *import_ssh2_s(BinarySource *src, int type,
char *passphrase, const char **errmsg_p)
if (type == SSH_KEYTYPE_OPENSSH_PEM)
return openssh_pem_read(src, passphrase, errmsg_p);
else if (type == SSH_KEYTYPE_OPENSSH_NEW)
return openssh_new_read(src, passphrase, errmsg_p);
return sshcom_read(src, passphrase, errmsg_p);
return NULL;
}
ssh2_userkey *import_ssh2(const Filename *filename, int type,
char *passphrase, const char **errmsg_p)
{
LoadedFile *lf = lf_load_keyfile(filename, errmsg_p);
if (!lf)
return false;
ssh2_userkey *toret = import_ssh2_s(BinarySource_UPCAST(lf),
type, passphrase, errmsg_p);
lf_free(lf);
return toret;
}
* Export an SSH-1 key.
bool export_ssh1(const Filename *filename, int type, RSAKey *key,
}
/*
* Export an SSH-2 key.
bool export_ssh2(const Filename *filename, int type,
ssh2_userkey *key, char *passphrase)
if (type == SSH_KEYTYPE_OPENSSH_AUTO)
return openssh_auto_write(filename, key, passphrase);
if (type == SSH_KEYTYPE_OPENSSH_NEW)
return openssh_new_write(filename, key, passphrase);
if (type == SSH_KEYTYPE_SSHCOM)
return sshcom_write(filename, key, passphrase);
/*
* Strip trailing CRs and LFs at the end of a line of text.
*/
void strip_crlf(char *str)
{
char *p = str + strlen(str);
while (p > str && (p[-1] == '\r' || p[-1] == '\n'))
}
/* ----------------------------------------------------------------------
* Helper routines. (The base64 ones are defined in sshpubk.c.)
*/
#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \
((c) >= 'a' && (c) <= 'z') || \
((c) >= '0' && (c) <= '9') || \
(c) == '+' || (c) == '/' || (c) == '=' \
)
/*
* Read an ASN.1/BER identifier and length pair.
* Flags are a combination of the #defines listed below.
* Returns -1 if unsuccessful; otherwise returns the number of
* bytes used out of the source data.
*/
/* ASN.1 tag classes. */
#define ASN1_CLASS_UNIVERSAL (0 << 6)
#define ASN1_CLASS_APPLICATION (1 << 6)
#define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6)
#define ASN1_CLASS_PRIVATE (3 << 6)
#define ASN1_CLASS_MASK (3 << 6)
/* Primitive versus constructed bit. */
#define ASN1_CONSTRUCTED (1 << 5)
/*
* Write an ASN.1/BER identifier and length pair. Returns the
* number of bytes consumed. Assumes dest contains enough space.
* Will avoid writing anything if dest is NULL, but still return
* amount of space required.
*/
static void BinarySink_put_ber_id_len(BinarySink *bs,
int id, int length, int flags)
/*
* Identifier is one byte.
*/
put_byte(bs, id | flags);
int n;
/*
* Identifier is multiple bytes: the first byte is 11111
* plus the flags, and subsequent bytes encode the value of
* the identifier, 7 bits at a time, with the top bit of
* each byte 1 except the last one which is 0.
*/
put_byte(bs, 0x1F | flags);
for (n = 1; (id >> (7*n)) > 0; n++)
continue; /* count the bytes */
while (n--)
put_byte(bs, (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F));
/*
* Length is one byte.
*/
int n;
/*
* Length is multiple bytes. The first is 0x80 plus the
* number of subsequent bytes, and the subsequent bytes
* encode the actual length.
*/
for (n = 1; (length >> (8*n)) > 0; n++)
continue; /* count the bytes */
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)
typedef struct ber_item {
int id;
int flags;
ptrlen data;
} ber_item;
static ber_item BinarySource_get_ber(BinarySource *src)
ber_item toret;
unsigned char leadbyte, lenbyte;
size_t length;
leadbyte = get_byte(src);
toret.flags = (leadbyte & 0xE0);
if ((leadbyte & 0x1F) == 0x1F) {
unsigned char idbyte;
do {
idbyte = get_byte(src);
toret.id = (toret.id << 7) | (idbyte & 0x7F);
} while (idbyte & 0x80);
} else {
toret.id = leadbyte & 0x1F;
lenbyte = get_byte(src);
if (lenbyte & 0x80) {
int nbytes = lenbyte & 0x7F;
length = 0;
while (nbytes-- > 0)
length = (length << 8) | get_byte(src);
}
toret.data = get_data(src, length);
return toret;
#define get_ber(bs) BinarySource_get_ber(BinarySource_UPCAST(bs))
/* ----------------------------------------------------------------------
* Code to read and write OpenSSH private keys, in the old-style PEM
* format.
*/
OP_DSA, OP_RSA, OP_ECDSA
} openssh_pem_keytype;
OP_E_3DES, OP_E_AES
} openssh_pem_enc;
struct openssh_pem_key {
openssh_pem_keytype keytype;
openssh_pem_enc encryption;
char iv[32];
};
void BinarySink_put_mp_ssh2_from_string(BinarySink *bs, ptrlen str)
const unsigned char *bytes = (const unsigned char *)str.ptr;
size_t nbytes = str.len;
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, str) \
BinarySink_put_mp_ssh2_from_string(BinarySink_UPCAST(bs), str)
static struct openssh_pem_key *load_openssh_pem_key(BinarySource *src,
const char **errmsg_p)
struct openssh_pem_key *ret;
char *line = NULL;
char base64_bit[4];
int base64_chars = 0;
ret = snew(struct openssh_pem_key);
ret->keyblob = strbuf_new_nm();
if (!(line = bsgetline(src))) {
errmsg = "unexpected end of file";
goto error;
if (!strstartswith(line, "-----BEGIN ") ||
!strendswith(line, "PRIVATE KEY-----")) {
errmsg = "file does not begin with OpenSSH key header";
goto error;
/*
* Parse the BEGIN line. For old-format keys, this tells us the
* type of the key; for new-format keys, all it tells us is the
* format, and we'll find out the key type once we parse the
* base64.
*/
if (!strcmp(line, "-----BEGIN RSA PRIVATE KEY-----")) {
} else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----")) {
} else if (!strcmp(line, "-----BEGIN EC PRIVATE KEY-----")) {
ret->keytype = OP_ECDSA;
} else if (!strcmp(line, "-----BEGIN OPENSSH PRIVATE KEY-----")) {
errmsg = "this is a new-style OpenSSH key";
goto error;
errmsg = "unrecognised key type";
goto error;
smemclr(line, strlen(line));
sfree(line);
line = NULL;
memset(ret->iv, 0, sizeof(ret->iv));
while (1) {
if (!(line = bsgetline(src))) {
errmsg = "unexpected end of file";
goto error;
}
if (strstartswith(line, "-----END ") &&
strendswith(line, "PRIVATE KEY-----")) {
sfree(line);
line = NULL;
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
if ((p = strchr(line, ':')) != NULL) {
if (headers_done) {
errmsg = "header found in body of key data";
goto error;
}
*p++ = '\0';
while (*p && isspace((unsigned char)*p)) p++;
if (!strcmp(line, "Proc-Type")) {
if (p[0] != '4' || p[1] != ',') {
errmsg = "Proc-Type is not 4 (only 4 is supported)";
goto error;
}
p += 2;
if (!strcmp(p, "ENCRYPTED"))
ret->encrypted = true;
} else if (!strcmp(line, "DEK-Info")) {
int i, ivlen;
if (!strncmp(p, "DES-EDE3-CBC,", 13)) {
ret->encryption = OP_E_3DES;
ivlen = 8;
} else if (!strncmp(p, "AES-128-CBC,", 12)) {
ret->encryption = OP_E_AES;
ivlen = 16;
} else {
errmsg = "unsupported cipher";
goto error;
}
p = strchr(p, ',') + 1;/* always non-NULL, by above checks */
for (i = 0; i < ivlen; i++) {
if (1 != sscanf(p, "%2x", &j)) {
errmsg = "expected more iv data in DEK-Info";
goto error;
}
ret->iv[i] = j;
p += 2;
}
if (*p) {
errmsg = "more iv data than expected in DEK-Info";
goto error;
}
}
} else {
headers_done = true;
p = line;
while (isbase64(*p)) {
base64_bit[base64_chars++] = *p;
if (base64_chars == 4) {
unsigned char out[3];
int len;
base64_chars = 0;
len = base64_decode_atom(base64_bit, out);
if (len <= 0) {
errmsg = "invalid base64 encoding";
goto error;
}
put_data(ret->keyblob, out, len);
smemclr(out, sizeof(out));
p++;
}
}
smemclr(line, strlen(line));
sfree(line);
line = NULL;
}
if (!ret->keyblob || ret->keyblob->len == 0) {
errmsg = "key body not present";
goto error;
}
if (ret->encrypted && ret->keyblob->len % 8 != 0) {
errmsg = "encrypted key blob is not a multiple of "
"cipher block size";
goto error;
}
smemclr(base64_bit, sizeof(base64_bit));
if (errmsg_p) *errmsg_p = NULL;
return ret;
error:
if (line) {
smemclr(line, strlen(line));
sfree(line);
line = NULL;
smemclr(base64_bit, sizeof(base64_bit));
if (ret) {
strbuf_free(ret->keyblob);
smemclr(ret, sizeof(*ret));
if (errmsg_p) *errmsg_p = errmsg;
return NULL;
}
static bool openssh_pem_encrypted(BinarySource *src)
struct openssh_pem_key *key = load_openssh_pem_key(src, NULL);
if (!key)
ret = key->encrypted;
strbuf_free(key->keyblob);
smemclr(key, sizeof(*key));
sfree(key);
return ret;
}
static void openssh_pem_derivekey(
ptrlen passphrase, const void *iv, uint8_t *keybuf)
{
/*
* Derive the encryption key for a PEM key file from the
* passphrase and iv/salt:
*
* - let block A equal MD5(passphrase || iv)
* - let block B equal MD5(A || passphrase || iv)
* - block C would be MD5(B || passphrase || iv) and so on
* - encryption key is the first N bytes of A || B
*
* (Note that only 8 bytes of the iv are used for key
* derivation, even when the key is encrypted with AES and
* hence there are 16 bytes available.)
*/
ssh_hash *h;
h = ssh_hash_new(&ssh_md5);
put_datapl(h, passphrase);
put_data(h, iv, 8);
ssh_hash_digest(h, keybuf);
ssh_hash_reset(h);
put_data(h, keybuf, 16);
put_datapl(h, passphrase);
put_data(h, iv, 8);
ssh_hash_final(h, keybuf + 16);
}
static ssh2_userkey *openssh_pem_read(
BinarySource *filesrc, const char *passphrase, const char **errmsg_p)
struct openssh_pem_key *key = load_openssh_pem_key(filesrc, errmsg_p);
int i, num_integers;
ssh2_userkey *retval = NULL;
strbuf *blob = strbuf_new_nm();
if (key->encrypted) {
unsigned char keybuf[32];
openssh_pem_derivekey(ptrlen_from_asciz(passphrase), key->iv, keybuf);
* Decrypt the key blob.
if (key->encryption == OP_E_3DES)
des3_decrypt_pubkey_ossh(keybuf, key->iv,
key->keyblob->u, key->keyblob->len);
ssh_cipher *cipher = ssh_cipher_new(&ssh_aes128_cbc);
ssh_cipher_setkey(cipher, keybuf);
ssh_cipher_setiv(cipher, key->iv);
ssh_cipher_decrypt(cipher, key->keyblob->u, key->keyblob->len);
ssh_cipher_free(cipher);
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
}
smemclr(keybuf, sizeof(keybuf));
}
/*
* Now we have a decrypted key blob, which contains an ASN.1
* encoded private key. We must now untangle the ASN.1.
*
* We expect the whole key blob to be formatted as a SEQUENCE
* (0x30 followed by a length code indicating that the rest of
* the blob is part of the sequence). Within that SEQUENCE we
* expect to see a bunch of INTEGERs. What those integers mean
* depends on the key type:
*
* - For RSA, we expect the integers to be 0, n, e, d, p, q,
* dmp1, dmq1, iqmp in that order. (The last three are d mod
* (p-1), d mod (q-1), inverse of q mod p respectively.)
*
* - For DSA, we expect them to be 0, p, q, g, y, x in that
* order.
*
* - In ECDSA the format is totally different: we see the
* SEQUENCE, but beneath is an INTEGER 1, OCTET STRING priv
* EXPLICIT [0] OID curve, EXPLICIT [1] BIT STRING pubPoint
*/
BinarySource_BARE_INIT(src, key->keyblob->u, key->keyblob->len);
{
/* Expect the SEQUENCE header. Take its absence as a failure to
* decrypt, if the key was encrypted. */
ber_item seq = get_ber(src);
if (get_err(src) || seq.id != 16) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
/* Reinitialise our BinarySource to parse just the inside of that
* SEQUENCE. */
BinarySource_BARE_INIT_PL(src, seq.data);
/* Expect a load of INTEGERs. */
if (key->keytype == OP_RSA)
num_integers = 9;
else if (key->keytype == OP_DSA)
num_integers = 6;
else
num_integers = 0; /* placate compiler warnings */
if (key->keytype == OP_ECDSA) {
/* And now for something completely different */
ber_item integer, privkey, sub0, sub1, oid, pubkey;
const ssh_keyalg *alg;
const struct ec_curve *curve;
/* Parse the outer layer of things inside the containing SEQUENCE */
integer = get_ber(src);
privkey = get_ber(src);
sub0 = get_ber(src);
sub1 = get_ber(src);
/* Now look inside sub0 for the curve OID */
BinarySource_BARE_INIT_PL(src, sub0.data);
oid = get_ber(src);
/* And inside sub1 for the public-key BIT STRING */
BinarySource_BARE_INIT_PL(src, sub1.data);
pubkey = get_ber(src);
if (get_err(src) ||
integer.id != 2 ||
integer.data.len != 1 ||
((const unsigned char *)integer.data.ptr)[0] != 1 ||
privkey.id != 4 ||
sub0.id != 0 ||
sub1.id != 1 ||
oid.id != 6 ||
pubkey.id != 3) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
alg = ec_alg_by_oid(oid.data.len, oid.data.ptr, &curve);
errmsg = "Unsupported ECDSA curve.";
retval = NULL;
goto error;
}
if (pubkey.data.len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
/* Skip 0x00 before point */
pubkey.data.ptr = (const char *)pubkey.data.ptr + 1;
pubkey.data.len -= 1;
/* Construct the key */
retkey = snew(ssh2_userkey);
put_stringz(blob, alg->ssh_id);
put_stringz(blob, curve->name);
put_stringpl(blob, pubkey.data);
put_mp_ssh2_from_string(blob, privkey.data);
retkey->key = ssh_key_new_priv(
alg, make_ptrlen(blob->u, publen),
make_ptrlen(blob->u + publen, blob->len - publen));
sfree(retkey);
errmsg = "unable to create key data structure";
goto error;
}
} else if (key->keytype == OP_RSA || key->keytype == OP_DSA) {
put_stringz(blob, key->keytype == OP_DSA ? "ssh-dss" : "ssh-rsa");
ptrlen rsa_modulus = PTRLEN_LITERAL("");
for (i = 0; i < num_integers; i++) {
ber_item integer = get_ber(src);
if (get_err(src) || integer.id != 2) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
if (i == 0) {
/*
* The first integer should be zero always (I think
* this is some sort of version indication).
*/
if (integer.data.len != 1 ||
((const unsigned char *)integer.data.ptr)[0] != 0) {
errmsg = "version number mismatch";
goto error;
}
} else if (key->keytype == OP_RSA) {
/*
* Integers 1 and 2 go into the public blob but in the
* opposite order; integers 3, 4, 5 and 8 go into the
* private blob. The other two (6 and 7) are ignored.
*/
if (i == 1) {
/* Save the details for after we deal with number 2. */
} else if (i != 6 && i != 7) {
put_mp_ssh2_from_string(blob, integer.data);
put_mp_ssh2_from_string(blob, rsa_modulus);
}
}
} else if (key->keytype == OP_DSA) {
/*
* Integers 1-4 go into the public blob; integer 5 goes
* into the private blob.
*/
put_mp_ssh2_from_string(blob, integer.data);
* Now put together the actual key. Simplest way to do this is
* to assemble our own key blobs and feed them to the createkey
* functions; this is a bit faffy but it does mean we get all
* the sanity checks for free.
assert(privptr > 0); /* should have bombed by now if not */
retkey = snew(ssh2_userkey);
alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
retkey->key = ssh_key_new_priv(
alg, make_ptrlen(blob->u, privptr),
make_ptrlen(blob->u+privptr, blob->len-privptr));
sfree(retkey);
errmsg = "unable to create key data structure";
unreachable("Bad key type from load_openssh_pem_key");
errmsg = "Bad key type from load_openssh_pem_key";
goto error;
}
/*
* The old key format doesn't include a comment in the private
* key file.
*/
retkey->comment = dupstr("imported-openssh-key");
errmsg = NULL; /* no error */
retval = retkey;
error:
strbuf_free(key->keyblob);
smemclr(key, sizeof(*key));
sfree(key);
if (errmsg_p) *errmsg_p = errmsg;
return retval;
}
static bool openssh_pem_write(
const Filename *filename, ssh2_userkey *key, const char *passphrase)
strbuf *pubblob, *privblob, *outblob;
unsigned char *spareblob;
int sparelen = 0;
char zero[1];
unsigned char iv[8];
/*
* Fetch the key blobs.
*/
pubblob = strbuf_new();
ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob));
privblob = strbuf_new_nm();
ssh_key_private_blob(key->key, BinarySink_UPCAST(privblob));
outblob = strbuf_new_nm();
* Encode the OpenSSH key blob, and also decide on the header
* line.
if (ssh_key_alg(key->key) == &ssh_rsa ||
ssh_key_alg(key->key) == &ssh_dss) {
* The RSA and DSS handlers share some code because the two
* key types have very similar ASN.1 representations, as a
* plain SEQUENCE of big integers. So we set up a list of
* bignums per key type and then construct the actual blob in
* common code after that.
if (ssh_key_alg(key->key) == &ssh_rsa) {
ptrlen n, e, d, p, q, iqmp, dmp1, dmq1;
mp_int *bd, *bp, *bq, *bdmp1, *bdmq1;
/*
* These blobs were generated from inside PuTTY, so we needn't
* treat them as untrusted.
*/
BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
get_string(src); /* skip algorithm name */
e = get_string(src);
n = get_string(src);
BinarySource_BARE_INIT(src, privblob->u, privblob->len);
d = get_string(src);
p = get_string(src);
q = get_string(src);
iqmp = get_string(src);
assert(!get_err(src)); /* can't go wrong */
/* We also need d mod (p-1) and d mod (q-1). */
bd = mp_from_bytes_be(d);
bp = mp_from_bytes_be(p);
bq = mp_from_bytes_be(q);
mp_sub_integer_into(bp, bp, 1);
mp_sub_integer_into(bq, bq, 1);
bdmp1 = mp_mod(bd, bp);
bdmq1 = mp_mod(bd, bq);
mp_free(bd);
mp_free(bp);
mp_free(bq);
dmp1.len = (mp_get_nbits(bdmp1)+8)/8;
dmq1.len = (mp_get_nbits(bdmq1)+8)/8;
sparelen = dmp1.len + dmq1.len;
spareblob = snewn(sparelen, unsigned char);
dmp1.ptr = spareblob;
dmq1.ptr = spareblob + dmp1.len;
for (i = 0; i < dmp1.len; i++)
spareblob[i] = mp_get_byte(bdmp1, dmp1.len-1 - i);
for (i = 0; i < dmq1.len; i++)
spareblob[i+dmp1.len] = mp_get_byte(bdmq1, dmq1.len-1 - i);
mp_free(bdmp1);
mp_free(bdmq1);
numbers[0] = make_ptrlen(zero, 1); zero[0] = '\0';
numbers[1] = n;
numbers[2] = e;
numbers[3] = d;
numbers[4] = p;
numbers[5] = q;
numbers[6] = dmp1;
numbers[7] = dmq1;
numbers[8] = iqmp;
nnumbers = 9;
header = "-----BEGIN RSA PRIVATE KEY-----\n";
footer = "-----END RSA PRIVATE KEY-----\n";
} else { /* ssh-dss */
/*
* These blobs were generated from inside PuTTY, so we needn't
* treat them as untrusted.
*/
BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
get_string(src); /* skip algorithm name */
p = get_string(src);
q = get_string(src);
g = get_string(src);
y = get_string(src);
BinarySource_BARE_INIT(src, privblob->u, privblob->len);
x = get_string(src);
assert(!get_err(src)); /* can't go wrong */
numbers[0].ptr = zero; numbers[0].len = 1; zero[0] = '\0';
numbers[1] = p;
numbers[2] = q;
numbers[3] = g;
numbers[4] = y;
numbers[5] = x;
nnumbers = 6;
header = "-----BEGIN DSA PRIVATE KEY-----\n";
footer = "-----END DSA PRIVATE KEY-----\n";
}
seq = strbuf_new_nm();
for (i = 0; i < nnumbers; i++) {
put_ber_id_len(seq, 2, numbers[i].len, 0);
put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED);
put_data(outblob, seq->s, seq->len);
strbuf_free(seq);
} else if (ssh_key_alg(key->key) == &ssh_ecdsa_nistp256 ||
ssh_key_alg(key->key) == &ssh_ecdsa_nistp384 ||
ssh_key_alg(key->key) == &ssh_ecdsa_nistp521) {
struct ecdsa_key *ec = container_of(key->key, struct ecdsa_key, sshk);
/*
* Structure of asn1:
* SEQUENCE
* INTEGER 1
* OCTET STRING (private key)
* [0]
* OID (curve)
* [1]
* BIT STRING (0x00 public key point)
*/
oid = ec_alg_oid(ssh_key_alg(key->key), &oidlen);
pointlen = (ec->curve->fieldBits + 7) / 8 * 2;
seq = strbuf_new_nm();
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* INTEGER 1 */
put_ber_id_len(seq, 2, 1, 0);
put_byte(seq, 1);
/* OCTET STRING private key */
put_ber_id_len(seq, 4, privblob->len - 4, 0);
put_data(seq, privblob->s + 4, privblob->len - 4);
/* 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,