summaryrefslogtreecommitdiff
path: root/keyimport.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyimport.c')
-rw-r--r--keyimport.c172
1 files changed, 153 insertions, 19 deletions
diff --git a/keyimport.c b/keyimport.c
index 7304e58..2b80770 100644
--- a/keyimport.c
+++ b/keyimport.c
@@ -35,6 +35,18 @@
#include "buffer.h"
#include "dbutil.h"
#include "ecc.h"
+#include "ssh.h"
+#include "rsa.h"
+#include "dss.h"
+#include "ed25519.h"
+
+static const unsigned char OSSH_PKEY_BLOB[] =
+ "openssh-key-v1\0" /* AUTH_MAGIC */
+ "\0\0\0\4none" /* cipher name*/
+ "\0\0\0\4none" /* kdf name */
+ "\0\0\0\0" /* kdf */
+ "\0\0\0\1"; /* key num */
+#define OSSH_PKEY_BLOBLEN (sizeof(OSSH_PKEY_BLOB) - 1)
#if DROPBEAR_ECDSA
static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07};
@@ -352,7 +364,7 @@ struct mpint_pos { void *start; int bytes; };
* Code to read and write OpenSSH private keys.
*/
-enum { OSSH_DSA, OSSH_RSA, OSSH_EC };
+enum { OSSH_DSA, OSSH_RSA, OSSH_EC, OSSH_PKEY };
struct openssh_key {
int type;
int encrypted;
@@ -364,11 +376,12 @@ struct openssh_key {
static struct openssh_key *load_openssh_key(const char *filename)
{
struct openssh_key *ret;
+ buffer *buf = NULL;
FILE *fp = NULL;
char buffer[256];
char *errmsg = NULL, *p = NULL;
int headers_done;
- unsigned long len, outlen;
+ unsigned long len;
ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key));
ret->keyblob = NULL;
@@ -397,12 +410,15 @@ static struct openssh_key *load_openssh_key(const char *filename)
ret->type = OSSH_DSA;
else if (!strcmp(buffer, "-----BEGIN EC PRIVATE KEY-----\n"))
ret->type = OSSH_EC;
+ else if (!strcmp(buffer, "-----BEGIN OPENSSH PRIVATE KEY-----\n"))
+ ret->type = OSSH_PKEY;
else {
errmsg = "Unrecognised key type";
goto error;
}
headers_done = 0;
+ buf = buf_new(0);
while (1) {
if (!fgets(buffer, sizeof(buffer), fp)) {
errmsg = "Unexpected end of file";
@@ -448,20 +464,31 @@ static struct openssh_key *load_openssh_key(const char *filename)
} else {
headers_done = 1;
len = strlen(buffer);
- outlen = len*4/3;
- if (ret->keyblob_len + outlen > ret->keyblob_size) {
- ret->keyblob_size = ret->keyblob_len + outlen + 256;
- ret->keyblob = (unsigned char*)m_realloc(ret->keyblob,
- ret->keyblob_size);
- }
- outlen = ret->keyblob_size - ret->keyblob_len;
- if (base64_decode((const unsigned char *)buffer, len,
- ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){
- errmsg = "Error decoding base64";
- goto error;
- }
- ret->keyblob_len += outlen;
+ buf = buf_resize(buf, buf->size + len);
+ buf_putbytes(buf, buffer, len);
+ }
+ }
+
+ if (buf && buf->len) {
+ ret->keyblob_size = ret->keyblob_len + buf->len*4/3 + 256;
+ ret->keyblob = (unsigned char*)m_realloc(ret->keyblob, ret->keyblob_size);
+ len = ret->keyblob_size;
+ if (base64_decode((const unsigned char *)buf->data, buf->len,
+ ret->keyblob, &len) != CRYPT_OK){
+ errmsg = "Error decoding base64";
+ goto error;
+ }
+ ret->keyblob_len = len;
+ }
+
+ if (ret->type == OSSH_PKEY) {
+ if (ret->keyblob_len < OSSH_PKEY_BLOBLEN ||
+ memcmp(ret->keyblob, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN)) {
+ errmsg = "Error decoding OpenSSH key";
+ goto error;
}
+ ret->keyblob_len -= OSSH_PKEY_BLOBLEN;
+ memmove(ret->keyblob, ret->keyblob + OSSH_PKEY_BLOBLEN, ret->keyblob_len);
}
if (ret->keyblob_len == 0 || !ret->keyblob) {
@@ -474,10 +501,18 @@ static struct openssh_key *load_openssh_key(const char *filename)
goto error;
}
+ if (buf) {
+ buf_burn(buf);
+ buf_free(buf);
+ }
m_burn(buffer, sizeof(buffer));
return ret;
error:
+ if (buf) {
+ buf_burn(buf);
+ buf_free(buf);
+ }
m_burn(buffer, sizeof(buffer));
if (ret) {
if (ret->keyblob) {
@@ -570,6 +605,57 @@ static sign_key *openssh_read(const char *filename, const char * UNUSED(passphra
}
/*
+ * Now we have a decrypted key blob, which contains OpenSSH
+ * encoded private key. We must now untangle the OpenSSH format.
+ */
+ if (key->type == OSSH_PKEY) {
+ blobbuf = buf_new(key->keyblob_len);
+ buf_putbytes(blobbuf, key->keyblob, key->keyblob_len);
+ buf_setpos(blobbuf, 0);
+
+ /* limit length of private key blob */
+ len = buf_getint(blobbuf);
+ buf_setlen(blobbuf, blobbuf->pos + len);
+
+ type = DROPBEAR_SIGNKEY_ANY;
+ if (buf_get_pub_key(blobbuf, retkey, &type)
+ != DROPBEAR_SUCCESS) {
+ errmsg = "Error parsing OpenSSH key";
+ goto ossh_error;
+ }
+
+ /* restore full length */
+ buf_setlen(blobbuf, key->keyblob_len);
+
+ if (type != DROPBEAR_SIGNKEY_NONE) {
+ retkey->type = type;
+ /* limit length of private key blob */
+ len = buf_getint(blobbuf);
+ buf_setlen(blobbuf, blobbuf->pos + len);
+#if DROPBEAR_ED25519
+ if (type == DROPBEAR_SIGNKEY_ED25519) {
+ buf_incrpos(blobbuf, 8);
+ buf_eatstring(blobbuf);
+ buf_eatstring(blobbuf);
+ buf_incrpos(blobbuf, -SSH_SIGNKEY_ED25519_LEN-4);
+ if (buf_get_ed25519_priv_key(blobbuf, retkey->ed25519key)
+ == DROPBEAR_SUCCESS) {
+ errmsg = NULL;
+ retval = retkey;
+ goto error;
+ }
+ }
+#endif
+ }
+
+ errmsg = "Unsupported OpenSSH key type";
+ ossh_error:
+ sign_key_free(retkey);
+ retkey = NULL;
+ goto error;
+ }
+
+ /*
* Now we have a decrypted key blob, which contains an ASN.1
* encoded private key. We must now untangle the ASN.1.
*
@@ -781,7 +867,7 @@ static sign_key *openssh_read(const char *filename, const char * UNUSED(passphra
goto error;
}
m_mp_alloc_init_multi((mp_int**)&ecc->k, NULL);
- if (mp_read_unsigned_bin(ecc->k, private_key_bytes, private_key_len)
+ if (mp_from_ubin(ecc->k, private_key_bytes, private_key_len)
!= MP_OKAY) {
errmsg = "Error parsing ECC key";
goto error;
@@ -1056,6 +1142,7 @@ static int openssh_write(const char *filename, sign_key *key,
unsigned long pubkey_size = 2*curve_size+1;
int k_size;
int err = 0;
+ size_t written;
/* version. less than 10 bytes */
buf_incrwritepos(seq_buf,
@@ -1063,12 +1150,14 @@ static int openssh_write(const char *filename, sign_key *key,
buf_putbyte(seq_buf, 1);
/* privateKey */
- k_size = mp_unsigned_bin_size((*eck)->k);
+ k_size = mp_ubin_size((*eck)->k);
dropbear_assert(k_size <= curve_size);
buf_incrwritepos(seq_buf,
ber_write_id_len(buf_getwriteptr(seq_buf, 10), 4, k_size, 0));
- mp_to_unsigned_bin((*eck)->k, buf_getwriteptr(seq_buf, k_size));
- buf_incrwritepos(seq_buf, k_size);
+ if (mp_to_ubin((*eck)->k, buf_getwriteptr(seq_buf, k_size), k_size, &written) != MP_OKAY) {
+ dropbear_exit("ECC error");
+ }
+ buf_incrwritepos(seq_buf, written);
/* SECGCurveNames */
switch (key->type)
@@ -1129,6 +1218,51 @@ static int openssh_write(const char *filename, sign_key *key,
}
#endif
+#if DROPBEAR_ED25519
+ if (key->type == DROPBEAR_SIGNKEY_ED25519) {
+ buffer *buf = buf_new(300);
+ keyblob = buf_new(100);
+ extrablob = buf_new(200);
+
+ /* private key blob w/o header */
+ buf_put_priv_key(keyblob, key, key->type);
+ buf_setpos(keyblob, 0);
+ buf_incrpos(keyblob, buf_getint(keyblob));
+ len = buf_getint(keyblob);
+
+ /* header */
+ buf_putbytes(buf, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN);
+
+ /* public key */
+ buf_put_pub_key(buf, key, key->type);
+
+ /* private key */
+ buf_incrwritepos(extrablob, 4);
+ buf_put_pub_key(extrablob, key, key->type);
+ buf_putstring(extrablob, buf_getptr(keyblob, len), len);
+ /* comment */
+ buf_putstring(extrablob, "", 0);
+ /* padding to cipher block length */
+ len = (extrablob->len+8) & ~7;
+ for (i = 1; len - extrablob->len > 0; i++)
+ buf_putbyte(extrablob, i);
+ buf_setpos(extrablob, 0);
+ buf_putbytes(extrablob, "\0\0\0\0\0\0\0\0", 8);
+ buf_putbufstring(buf, extrablob);
+
+ outlen = len = pos = buf->len;
+ outblob = (unsigned char*)m_malloc(outlen);
+ memcpy(outblob, buf->data, buf->len);
+
+ buf_burn(buf);
+ buf_free(buf);
+ buf = NULL;
+
+ header = "-----BEGIN OPENSSH PRIVATE KEY-----\n";
+ footer = "-----END OPENSSH PRIVATE KEY-----\n";
+ }
+#endif
+
/*
* Padding on OpenSSH keys is deterministic. The number of
* padding bytes is always more than zero, and always at most