# HG changeset patch # User Matt Johnston # Date 1648543566 -28800 # Node ID 3e0aacf0a4f3aa28ebb52f88b8e62fae1828a444 # Parent 46f68ace1c3c646bd4b55def09349272ddfa4dc8 Fix ed25519 dropbear to openssh conversion This introduces buf_put_ed25519_priv_ossh and buf_get_ed25519_priv_ossh to handle OpenSSH internal private key format. Previously writing OpenSSH format keys didn't write the private part correctly. diff -r 46f68ace1c3c -r 3e0aacf0a4f3 keyimport.c --- a/keyimport.c Thu Mar 24 16:50:42 2022 +0800 +++ b/keyimport.c Tue Mar 29 16:46:06 2022 +0800 @@ -48,6 +48,9 @@ "\0\0\0\1"; /* key num */ #define OSSH_PKEY_BLOBLEN (sizeof(OSSH_PKEY_BLOB) - 1) +void buf_put_ed25519_priv_ossh(buffer *buf, const sign_key *akey); +int buf_get_ed25519_priv_ossh(buffer *buf, sign_key *akey); + #if DROPBEAR_ECDSA static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; static const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22}; @@ -369,6 +372,7 @@ int type; int encrypted; char iv[32]; + /* keyblob is publickey1 onwards (ref OpenSSH PROTOCOL.key) */ unsigned char *keyblob; unsigned int keyblob_len, keyblob_size; }; @@ -601,7 +605,7 @@ memset(&md5c, 0, sizeof(md5c)); memset(keybuf, 0, sizeof(keybuf)); -#endif +#endif } /* @@ -613,9 +617,8 @@ buf_putbytes(blobbuf, key->keyblob, key->keyblob_len); buf_setpos(blobbuf, 0); - /* limit length of private key blob */ + /* limit length of public key blob */ len = buf_getint(blobbuf); - buf_setlen(blobbuf, blobbuf->pos + len); type = DROPBEAR_SIGNKEY_ANY; if (buf_get_pub_key(blobbuf, retkey, &type) @@ -627,19 +630,20 @@ /* restore full length */ buf_setlen(blobbuf, key->keyblob_len); + /* length of private key part. we can discard it */ + buf_getint(blobbuf); + + /* discard checkkey1 */ + buf_getint(blobbuf); + /* discard checkkey2 */ + buf_getint(blobbuf); + 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_decrpos(blobbuf, SSH_SIGNKEY_ED25519_LEN+4); - if (buf_get_ed25519_priv_key(blobbuf, retkey->ed25519key) - == DROPBEAR_SUCCESS) { + if (buf_get_ed25519_priv_ossh(blobbuf, retkey) + == DROPBEAR_SUCCESS) { errmsg = NULL; retval = retkey; goto error; @@ -913,6 +917,57 @@ return retval; } +#if DROPBEAR_ED25519 +/* OpenSSH raw private ed25519 format is +string "ssh-ed25519" +uint32 32 +byte[32] pubkey +uint32 64 +byte[32] privkey +byte[32] pubkey +*/ + +void buf_put_ed25519_priv_ossh(buffer *buf, const sign_key *akey) { + const dropbear_ed25519_key *key = akey->ed25519key; + dropbear_assert(key != NULL); + buf_putstring(buf, SSH_SIGNKEY_ED25519, SSH_SIGNKEY_ED25519_LEN); + buf_putint(buf, CURVE25519_LEN); + buf_putbytes(buf, key->pub, CURVE25519_LEN); + buf_putint(buf, CURVE25519_LEN*2); + buf_putbytes(buf, key->priv, CURVE25519_LEN); + buf_putbytes(buf, key->pub, CURVE25519_LEN); +} + +int buf_get_ed25519_priv_ossh(buffer *buf, sign_key *akey) { + dropbear_ed25519_key *key = akey->ed25519key; + uint32_t len; + + dropbear_assert(key != NULL); + + /* Parse past the first string and pubkey */ + if (buf_get_ed25519_pub_key(buf, key, DROPBEAR_SIGNKEY_ED25519) + == DROPBEAR_FAILURE) { + dropbear_log(LOG_ERR, "Error parsing ed25519 key, pubkey"); + return DROPBEAR_FAILURE; + } + len = buf_getint(buf); + if (len != 2*CURVE25519_LEN) { + dropbear_log(LOG_ERR, "Error parsing ed25519 key, bad length"); + return DROPBEAR_FAILURE; + } + memcpy(key->priv, buf_getptr(buf, CURVE25519_LEN), CURVE25519_LEN); + buf_incrpos(buf, CURVE25519_LEN); + + /* Sanity check */ + if (memcmp(buf_getptr(buf, CURVE25519_LEN), key->pub, + CURVE25519_LEN) != 0) { + dropbear_log(LOG_ERR, "Error parsing ed25519 key, mismatch pubkey"); + return DROPBEAR_FAILURE; + } + return DROPBEAR_SUCCESS; +} +#endif + static int openssh_write(const char *filename, sign_key *key, const char *passphrase) { @@ -1220,15 +1275,12 @@ #if DROPBEAR_ED25519 if (key->type == DROPBEAR_SIGNKEY_ED25519) { - buffer *buf = buf_new(300); - keyblob = buf_new(100); - extrablob = buf_new(200); + buffer *buf = buf_new(1200); + keyblob = buf_new(1000); + extrablob = buf_new(1100); /* 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); + buf_put_ed25519_priv_ossh(keyblob, key); /* header */ buf_putbytes(buf, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN); @@ -1237,9 +1289,10 @@ 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); + buf_putint(extrablob, 0); /* checkint 1 */ + buf_putint(extrablob, 0); /* checkint 2 */ + /* raw openssh private key */ + buf_putbytes(extrablob, keyblob->data, keyblob->len); /* comment */ buf_putstring(extrablob, "", 0); /* padding to cipher block length */