* Code to read and write OpenSSH private keys.
*/
-enum { OSSH_DSA, OSSH_RSA };
+enum { OSSH_DSA, OSSH_RSA, OSSH_ECDSA };
enum { OSSH_ENC_3DES, OSSH_ENC_AES };
struct openssh_key {
int type;
ret->type = OSSH_RSA;
else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----"))
ret->type = OSSH_DSA;
+ else if (!strcmp(line, "-----BEGIN EC PRIVATE KEY-----"))
+ ret->type = OSSH_ECDSA;
else {
errmsg = "unrecognised key type";
goto error;
if (0 == strncmp(line, "-----END ", 9) &&
0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) {
sfree(line);
+ line = NULL;
break; /* done */
}
if ((p = strchr(line, ':')) != NULL) {
*
* - 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
*/
p = key->keyblob;
else
num_integers = 0; /* placate compiler warnings */
- /*
- * Space to create key blob in.
- */
- blobsize = 256+key->keyblob_len;
- blob = snewn(blobsize, unsigned char);
- PUT_32BIT(blob, 7);
- if (key->type == OSSH_DSA)
- memcpy(blob+4, "ssh-dss", 7);
- else if (key->type == OSSH_RSA)
- memcpy(blob+4, "ssh-rsa", 7);
- blobptr = 4+7;
- privptr = -1;
-
- for (i = 0; i < num_integers; i++) {
+
+ if (key->type == OSSH_ECDSA)
+ {
+ /* And now for something completely different */
+ unsigned char *priv;
+ int privlen;
+ struct ec_curve *curve;
+ /* Read INTEGER 1 */
+ ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+ &id, &len, &flags);
+ p += ret;
+ if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len ||
+ len != 1 || p[0] != 1) {
+ errmsg = "ASN.1 decoding failure";
+ retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+ goto error;
+ }
+ p += 1;
+ /* Read private key OCTET STRING */
+ ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+ &id, &len, &flags);
+ p += ret;
+ if (ret < 0 || id != 4 || key->keyblob+key->keyblob_len-p < len) {
+ errmsg = "ASN.1 decoding failure";
+ retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+ goto error;
+ }
+ priv = p;
+ privlen = len;
+ p += len;
+ /* Read curve OID */
+ ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+ &id, &len, &flags);
+ p += ret;
+ if (ret < 0 || id != 0 || key->keyblob+key->keyblob_len-p < len) {
+ errmsg = "ASN.1 decoding failure";
+ retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+ goto error;
+ }
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
- if (ret < 0 || id != 2 ||
- key->keyblob+key->keyblob_len-p < len) {
+ if (ret < 0 || id != 6 || key->keyblob+key->keyblob_len-p < len) {
errmsg = "ASN.1 decoding failure";
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
+ if (len == 8 && !memcmp(p, nistp256_oid, nistp256_oid_len)) {
+ curve = ec_p256();
+ } else if (len == 5 && !memcmp(p, nistp384_oid, nistp384_oid_len)) {
+ curve = ec_p384();
+ } else if (len == 5 && !memcmp(p, nistp521_oid, nistp521_oid_len)) {
+ curve = ec_p521();
+ } else {
+ errmsg = "Unsupported ECDSA curve.";
+ retval = NULL;
+ goto error;
+ }
+ p += len;
+ /* Read BIT STRING point */
+ ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+ &id, &len, &flags);
+ p += ret;
+ if (ret < 0 || id != 1 || key->keyblob+key->keyblob_len-p < len) {
+ errmsg = "ASN.1 decoding failure";
+ retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+ goto error;
+ }
+ ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+ &id, &len, &flags);
+ p += ret;
+ if (ret < 0 || id != 3 || key->keyblob+key->keyblob_len-p < len ||
+ len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) {
+ errmsg = "ASN.1 decoding failure";
+ retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
+ goto error;
+ }
+ p += 1; len -= 1; /* Skip 0x00 before point */
- if (i == 0) {
- /*
- * The first integer should be zero always (I think
- * this is some sort of version indication).
- */
- if (len != 1 || p[0] != 0) {
- errmsg = "version number mismatch";
+ /* Construct the key */
+ retkey = snew(struct ssh2_userkey);
+ if (!retkey) {
+ errmsg = "out of memory";
+ goto error;
+ }
+ if (curve->fieldBits == 256) {
+ retkey->alg = &ssh_ecdsa_nistp256;
+ } else if (curve->fieldBits == 384) {
+ retkey->alg = &ssh_ecdsa_nistp384;
+ } else {
+ retkey->alg = &ssh_ecdsa_nistp521;
+ }
+ blob = snewn((4+19 + 4+8 + 4+len) + (4+privlen), unsigned char);
+ if (!blob) {
+ sfree(retkey);
+ errmsg = "out of memory";
+ goto error;
+ }
+ PUT_32BIT(blob, 19);
+ sprintf((char*)blob+4, "ecdsa-sha2-nistp%d", curve->fieldBits);
+ PUT_32BIT(blob+4+19, 8);
+ sprintf((char*)blob+4+19+4, "nistp%d", curve->fieldBits);
+ PUT_32BIT(blob+4+19+4+8, len);
+ memcpy(blob+4+19+4+8+4, p, len);
+ PUT_32BIT(blob+4+19+4+8+4+len, privlen);
+ memcpy(blob+4+19+4+8+4+len+4, priv, privlen);
+ retkey->data = retkey->alg->createkey(blob, 4+19+4+8+4+len,
+ blob+4+19+4+8+4+len, 4+privlen);
+ if (!retkey->data) {
+ sfree(retkey);
+ errmsg = "unable to create key data structure";
+ goto error;
+ }
+
+ } else if (key->type == OSSH_RSA || key->type == OSSH_DSA) {
+
+ /*
+ * Space to create key blob in.
+ */
+ blobsize = 256+key->keyblob_len;
+ blob = snewn(blobsize, unsigned char);
+ PUT_32BIT(blob, 7);
+ if (key->type == OSSH_DSA)
+ memcpy(blob+4, "ssh-dss", 7);
+ else if (key->type == OSSH_RSA)
+ memcpy(blob+4, "ssh-rsa", 7);
+ blobptr = 4+7;
+ privptr = -1;
+
+ for (i = 0; i < num_integers; i++) {
+ ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+ &id, &len, &flags);
+ p += ret;
+ if (ret < 0 || id != 2 ||
+ key->keyblob+key->keyblob_len-p < len) {
+ errmsg = "ASN.1 decoding failure";
+ retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
- } else if (key->type == OSSH_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. */
- modptr = (char *)p;
- modlen = len;
- } else if (i != 6 && i != 7) {
+
+ if (i == 0) {
+ /*
+ * The first integer should be zero always (I think
+ * this is some sort of version indication).
+ */
+ if (len != 1 || p[0] != 0) {
+ errmsg = "version number mismatch";
+ goto error;
+ }
+ } else if (key->type == OSSH_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. */
+ 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;
+ if (i == 2) {
+ PUT_32BIT(blob+blobptr, modlen);
+ memcpy(blob+blobptr+4, modptr, modlen);
+ blobptr += 4+modlen;
+ privptr = blobptr;
+ }
+ }
+ } else if (key->type == OSSH_DSA) {
+ /*
+ * 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;
- if (i == 2) {
- PUT_32BIT(blob+blobptr, modlen);
- memcpy(blob+blobptr+4, modptr, modlen);
- blobptr += 4+modlen;
+ if (i == 4)
privptr = blobptr;
- }
}
- } else if (key->type == OSSH_DSA) {
- /*
- * 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;
- if (i == 4)
- privptr = blobptr;
+
+ /* Skip past the number. */
+ p += len;
}
- /* Skip past the number. */
- p += len;
- }
+ /*
+ * 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(struct ssh2_userkey);
+ retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);
+ retkey->data = retkey->alg->createkey(blob, privptr,
+ blob+privptr, blobptr-privptr);
+ if (!retkey->data) {
+ sfree(retkey);
+ errmsg = "unable to create key data structure";
+ goto error;
+ }
- /*
- * 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(struct ssh2_userkey);
- retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);
- retkey->data = retkey->alg->createkey(blob, privptr,
- blob+privptr, blobptr-privptr);
- if (!retkey->data) {
- sfree(retkey);
- errmsg = "unable to create key data structure";
- goto error;
+ } else {
+ assert(0 && "Bad key type from load_openssh_key");
}
retkey->comment = dupstr("imported-openssh-key");
privblob = key->alg->private_blob(key->data, &privlen);
spareblob = outblob = NULL;
+ outblob = NULL;
+ len = 0;
+
/*
- * Find the sequence of integers to be encoded into the OpenSSH
- * key blob, and also decide on the header line.
+ * Encode the OpenSSH key blob, and also decide on the header
+ * line.
*/
- if (key->alg == &ssh_rsa) {
- int pos;
- struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;
- Bignum bd, bp, bq, bdmp1, bdmq1;
-
+ if (key->alg == &ssh_rsa || key->alg == &ssh_dss) {
/*
- * These blobs were generated from inside PuTTY, so we needn't
- * treat them as untrusted.
+ * 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.
*/
- pos = 4 + GET_32BIT(pubblob);
- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
- pos = 0;
- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);
- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);
- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);
- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);
+ if (key->alg == &ssh_rsa) {
+ int pos;
+ struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;
+ Bignum bd, bp, bq, bdmp1, bdmq1;
- assert(e.start && iqmp.start); /* can't go wrong */
+ /*
+ * These blobs were generated from inside PuTTY, so we needn't
+ * treat them as untrusted.
+ */
+ pos = 4 + GET_32BIT(pubblob);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
+ pos = 0;
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);
+
+ assert(e.start && iqmp.start); /* can't go wrong */
+
+ /* We also need d mod (p-1) and d mod (q-1). */
+ bd = bignum_from_bytes(d.start, d.bytes);
+ bp = bignum_from_bytes(p.start, p.bytes);
+ bq = bignum_from_bytes(q.start, q.bytes);
+ decbn(bp);
+ decbn(bq);
+ bdmp1 = bigmod(bd, bp);
+ bdmq1 = bigmod(bd, bq);
+ freebn(bd);
+ freebn(bp);
+ freebn(bq);
+
+ dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8;
+ dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8;
+ sparelen = dmp1.bytes + dmq1.bytes;
+ spareblob = snewn(sparelen, unsigned char);
+ dmp1.start = spareblob;
+ dmq1.start = spareblob + dmp1.bytes;
+ for (i = 0; i < dmp1.bytes; i++)
+ spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i);
+ for (i = 0; i < dmq1.bytes; i++)
+ spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i);
+ freebn(bdmp1);
+ freebn(bdmq1);
+
+ numbers[0].start = zero; numbers[0].bytes = 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 */
+ int pos;
+ struct mpint_pos p, q, g, y, x;
- /* We also need d mod (p-1) and d mod (q-1). */
- bd = bignum_from_bytes(d.start, d.bytes);
- bp = bignum_from_bytes(p.start, p.bytes);
- bq = bignum_from_bytes(q.start, q.bytes);
- decbn(bp);
- decbn(bq);
- bdmp1 = bigmod(bd, bp);
- bdmq1 = bigmod(bd, bq);
- freebn(bd);
- freebn(bp);
- freebn(bq);
-
- dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8;
- dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8;
- sparelen = dmp1.bytes + dmq1.bytes;
- spareblob = snewn(sparelen, unsigned char);
- dmp1.start = spareblob;
- dmq1.start = spareblob + dmp1.bytes;
- for (i = 0; i < dmp1.bytes; i++)
- spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i);
- for (i = 0; i < dmq1.bytes; i++)
- spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i);
- freebn(bdmp1);
- freebn(bdmq1);
-
- numbers[0].start = zero; numbers[0].bytes = 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 if (key->alg == &ssh_dss) {
- int pos;
- struct mpint_pos p, q, g, y, x;
+ /*
+ * These blobs were generated from inside PuTTY, so we needn't
+ * treat them as untrusted.
+ */
+ pos = 4 + GET_32BIT(pubblob);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);
+ pos = 0;
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);
+
+ assert(y.start && x.start); /* can't go wrong */
+
+ numbers[0].start = zero; numbers[0].bytes = 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";
+ }
/*
- * These blobs were generated from inside PuTTY, so we needn't
- * treat them as untrusted.
+ * 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++) {
+ 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 = 4 + GET_32BIT(pubblob);
- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);
- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);
- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);
pos = 0;
- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);
+ 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;
+ }
+ } else if (key->alg == &ssh_ecdsa_nistp256 ||
+ key->alg == &ssh_ecdsa_nistp384 ||
+ key->alg == &ssh_ecdsa_nistp521) {
+ unsigned char *oid;
+ int oidlen;
+ int pointlen;
- assert(y.start && x.start); /* can't go wrong */
+ /*
+ * Structure of asn1:
+ * SEQUENCE
+ * INTEGER 1
+ * OCTET STRING (private key)
+ * [0]
+ * OID (curve)
+ * [1]
+ * BIT STRING (0x00 public key point)
+ */
+ switch (((struct ec_key *)key->data)->publicKey.curve->fieldBits) {
+ case 256:
+ /* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */
+ oid = nistp256_oid;
+ oidlen = nistp256_oid_len;
+ pointlen = 32 * 2;
+ break;
+ case 384:
+ /* OID: 1.3.132.0.34 (secp384r1) */
+ oid = nistp384_oid;
+ oidlen = nistp384_oid_len;
+ pointlen = 48 * 2;
+ break;
+ case 521:
+ /* OID: 1.3.132.0.35 (secp521r1) */
+ oid = nistp521_oid;
+ oidlen = nistp521_oid_len;
+ pointlen = 66 * 2;
+ break;
+ default:
+ assert(0);
+ }
- numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
- numbers[1] = p;
- numbers[2] = q;
- numbers[3] = g;
- numbers[4] = y;
- numbers[5] = x;
+ len = ber_write_id_len(NULL, 2, 1, 0);
+ len += 1;
+ len += ber_write_id_len(NULL, 4, privlen - 4, 0);
+ len+= privlen - 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;
+ len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
+
+ outblob = snewn(len, unsigned char);
+ assert(outblob);
- nnumbers = 6;
- header = "-----BEGIN DSA PRIVATE KEY-----\n";
- footer = "-----END DSA PRIVATE KEY-----\n";
+ 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, privlen - 4, 0);
+ memcpy(outblob+pos, privblob + 4, privlen - 4);
+ pos += privlen - 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+39, 1 + pointlen);
+ pos += 1 + pointlen;
+
+ header = "-----BEGIN EC PRIVATE KEY-----\n";
+ footer = "-----END EC PRIVATE KEY-----\n";
} else {
assert(0); /* zoinks! */
exit(1); /* XXX: GCC doesn't understand assert() on some systems. */
}
- /*
- * 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++) {
- 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);
- /* Round up to the cipher block size, ensuring we have at least one
- * byte of padding (see below). */
- outlen = len;
- if (passphrase)
- outlen = (outlen+8) &~ 7;
-
- /*
- * Now we know how big outblob needs to be. Allocate it.
- */
- outblob = snewn(outlen, 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;
- }
-
- /*
- * Padding on OpenSSH keys is deterministic. The number of
- * padding bytes is always more than zero, and always at most
- * the cipher block length. The value of each padding byte is
- * equal to the number of padding bytes. So a plaintext that's
- * an exact multiple of the block size will be padded with 08
- * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a
- * plaintext one byte less than a multiple of the block size
- * will be padded with just 01.
- *
- * This enables the OpenSSL key decryption function to strip
- * off the padding algorithmically and return the unpadded
- * plaintext to the next layer: it looks at the final byte, and
- * then expects to find that many bytes at the end of the data
- * with the same value. Those are all removed and the rest is
- * returned.
- */
- assert(pos == len);
- while (pos < outlen) {
- outblob[pos++] = outlen - len;
- }
-
/*
* Encrypt the key.
*
* old-style 3DES.
*/
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;
+ }
+
+ /*
+ * Padding on OpenSSH keys is deterministic. The number of
+ * padding bytes is always more than zero, and always at most
+ * the cipher block length. The value of each padding byte is
+ * equal to the number of padding bytes. So a plaintext that's
+ * an exact multiple of the block size will be padded with 08
+ * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a
+ * plaintext one byte less than a multiple of the block size
+ * will be padded with just 01.
+ *
+ * This enables the OpenSSL key decryption function to strip
+ * off the padding algorithmically and return the unpadded
+ * plaintext to the next layer: it looks at the final byte, and
+ * then expects to find that many bytes at the end of the data
+ * with the same value. Those are all removed and the rest is
+ * returned.
+ */
+ assert(pos == len);
+ while (pos < outlen) {
+ outblob[pos++] = outlen - len;
+ }
+
/*
* Invent an iv. Then derive encryption key from passphrase
* and iv/salt:
* - block C would be MD5(B || passphrase || iv) and so on
* - encryption key is the first N bytes of A || B
*/
- struct MD5Context md5c;
- unsigned char keybuf[32];
-
for (i = 0; i < 8; i++) iv[i] = random_byte();
MD5Init(&md5c);
smemclr(&md5c, sizeof(md5c));
smemclr(keybuf, sizeof(keybuf));
+ } else {
+ /*
+ * If no encryption, the blob has exactly its original
+ * cleartext size.
+ */
+ outlen = len;
}
/*
strip_crlf(line);
if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) {
sfree(line);
+ line = NULL;
break; /* done */
}
if ((p = strchr(line, ':')) != NULL) {
answer = 1;
done:
- *comment = dupstr(key ? key->comment : "");
- smemclr(key->keyblob, key->keyblob_size);
- sfree(key->keyblob);
- smemclr(key, sizeof(*key));
- sfree(key);
+ if (key) {
+ *comment = dupstr(key->comment);
+ smemclr(key->keyblob, key->keyblob_size);
+ sfree(key->keyblob);
+ smemclr(key, sizeof(*key));
+ sfree(key);
+ } else {
+ *comment = dupstr("");
+ }
return answer;
}