# HG changeset patch # User egor-duda # Date 1642859584 -10800 # Node ID 35d504d59c05378f217485bdcfb254067e5d01a6 # Parent cba37fe1ddc8c592005e579cbc09d13b373e49f7 Implement server-side support for sk-ecdsa U2F-backed keys (#142) * Implement server-side support for sk-ecdsa U2F-backed keys * Fix out-of-bounds read on normal ecdsa-sha2-[identifier] keys * Fix one more potential out-of-bounds read * Check if nistp256 curve is used in sk-ecdsa-sha2- key It's the only allowed curve per PROTOCOL.u2f specification * Implement server-side support for sk-ed25519 FIDO2-backed keys * Keys with type sk-* make no sense as host keys, so they should be disabled * fix typo * Make sk-ecdsa call buf_ecdsa_verify This reduces code duplication, the SK code just handles the different message format. * Reduce sk specific code The application id can be stored in signkey, then we don't need to call sk-specific functions from svr-authpubkey * Remove debugging output, which causes compilation errors with DEBUG_TRACE disabled * Proper cleanup of sk_app Co-authored-by: Matt Johnston diff -r cba37fe1ddc8 -r 35d504d59c05 Makefile.in --- a/Makefile.in Sat Jan 22 12:46:08 2022 +0800 +++ b/Makefile.in Sat Jan 22 16:53:04 2022 +0300 @@ -35,8 +35,8 @@ signkey.o rsa.o dbrandom.o \ queue.o \ atomicio.o compat.o fake-rfc2553.o \ - ltc_prng.o ecc.o ecdsa.o crypto_desc.o \ - curve25519.o ed25519.o \ + ltc_prng.o ecc.o ecdsa.o sk-ecdsa.o crypto_desc.o \ + curve25519.o ed25519.o sk-ed25519.o \ dbmalloc.o \ gensignkey.o gendss.o genrsa.o gened25519.o diff -r cba37fe1ddc8 -r 35d504d59c05 common-algo.c --- a/common-algo.c Sat Jan 22 12:46:08 2022 +0800 +++ b/common-algo.c Sat Jan 22 16:53:04 2022 +0300 @@ -239,6 +239,9 @@ algo_type sigalgs[] = { #if DROPBEAR_ED25519 {"ssh-ed25519", DROPBEAR_SIGNATURE_ED25519, NULL, 1, NULL}, +#if DROPBEAR_SK_ED25519 + {"sk-ssh-ed25519@openssh.com", DROPBEAR_SIGNATURE_SK_ED25519, NULL, 1, NULL}, +#endif #endif #if DROPBEAR_ECDSA #if DROPBEAR_ECC_256 @@ -250,6 +253,9 @@ #if DROPBEAR_ECC_521 {"ecdsa-sha2-nistp521", DROPBEAR_SIGNATURE_ECDSA_NISTP521, NULL, 1, NULL}, #endif +#if DROPBEAR_SK_ECDSA + {"sk-ecdsa-sha2-nistp256@openssh.com", DROPBEAR_SIGNATURE_SK_ECDSA_NISTP256, NULL, 1, NULL}, +#endif #endif #if DROPBEAR_RSA #if DROPBEAR_RSA_SHA256 diff -r cba37fe1ddc8 -r 35d504d59c05 default_options.h --- a/default_options.h Sat Jan 22 12:46:08 2022 +0800 +++ b/default_options.h Sat Jan 22 16:53:04 2022 +0300 @@ -126,9 +126,11 @@ * code (either ECDSA or ECDH) increases binary size - around 30kB * on x86-64 */ #define DROPBEAR_ECDSA 1 +#define DROPBEAR_SK_ECDSA 1 /* Ed25519 is faster than ECDSA. Compiling in Ed25519 code increases binary size - around 7,5kB on x86-64 */ #define DROPBEAR_ED25519 1 +#define DROPBEAR_SK_ED25519 1 /* RSA must be >=1024 */ #define DROPBEAR_DEFAULT_RSA_SIZE 2048 diff -r cba37fe1ddc8 -r 35d504d59c05 ecdsa.c --- a/ecdsa.c Sat Jan 22 12:46:08 2022 +0800 +++ b/ecdsa.c Sat Jan 22 16:53:04 2022 +0300 @@ -81,18 +81,25 @@ struct dropbear_ecc_curve **curve; ecc_key *new_key = NULL; - /* string "ecdsa-sha2-[identifier]" */ + /* string "ecdsa-sha2-[identifier]" or "sk-ecdsa-sha2-nistp256@openssh.com" */ key_ident = (unsigned char*)buf_getstring(buf, &key_ident_len); /* string "[identifier]" */ identifier = (unsigned char*)buf_getstring(buf, &identifier_len); - if (key_ident_len != identifier_len + strlen("ecdsa-sha2-")) { - TRACE(("Bad identifier lengths")) - goto out; - } - if (memcmp(&key_ident[strlen("ecdsa-sha2-")], identifier, identifier_len) != 0) { - TRACE(("mismatching identifiers")) - goto out; + if (strcmp (key_ident, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) { + if (strcmp (identifier, "nistp256") != 0) { + TRACE(("mismatching identifiers")) + goto out; + } + } else { + if (key_ident_len != identifier_len + strlen ("ecdsa-sha2-")) { + TRACE(("Bad identifier lengths")) + goto out; + } + if (memcmp(&key_ident[strlen ("ecdsa-sha2-")], identifier, identifier_len) != 0) { + TRACE(("mismatching identifiers")) + goto out; + } } for (curve = dropbear_ecc_curves; *curve; curve++) { diff -r cba37fe1ddc8 -r 35d504d59c05 ed25519.c --- a/ed25519.c Sat Jan 22 12:46:08 2022 +0800 +++ b/ed25519.c Sat Jan 22 16:53:04 2022 +0300 @@ -38,14 +38,25 @@ * The key will have the same format as buf_put_ed25519_key. * These should be freed with ed25519_key_free. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_get_ed25519_pub_key(buffer *buf, dropbear_ed25519_key *key) { +int buf_get_ed25519_pub_key(buffer *buf, dropbear_ed25519_key *key, + enum signkey_type expect_keytype) { + - unsigned int len; + unsigned int len, typelen; + char *keytype = NULL; + enum signkey_type buf_keytype; TRACE(("enter buf_get_ed25519_pub_key")) dropbear_assert(key != NULL); - buf_incrpos(buf, 4+SSH_SIGNKEY_ED25519_LEN); /* int + "ssh-ed25519" */ + /* consume and check the key string */ + keytype = buf_getstring(buf, &typelen); + buf_keytype = signkey_type_from_name(keytype, typelen); + m_free(keytype); + if (buf_keytype != expect_keytype) { + TRACE(("leave buf_get_ed25519_pub_key: mismatch key type")) + return DROPBEAR_FAILURE; + } len = buf_getint(buf); if (len != CURVE25519_LEN || buf->len - buf->pos < len) { diff -r cba37fe1ddc8 -r 35d504d59c05 ed25519.h --- a/ed25519.h Sat Jan 22 12:46:08 2022 +0800 +++ b/ed25519.h Sat Jan 22 16:53:04 2022 +0300 @@ -27,6 +27,7 @@ #include "includes.h" #include "buffer.h" +#include "signkey.h" #if DROPBEAR_ED25519 @@ -43,7 +44,8 @@ #if DROPBEAR_SIGNKEY_VERIFY int buf_ed25519_verify(buffer * buf, const dropbear_ed25519_key *key, const buffer *data_buf); #endif -int buf_get_ed25519_pub_key(buffer* buf, dropbear_ed25519_key *key); +int buf_get_ed25519_pub_key(buffer *buf, dropbear_ed25519_key *key, + enum signkey_type expect_keytype); int buf_get_ed25519_priv_key(buffer* buf, dropbear_ed25519_key *key); void buf_put_ed25519_pub_key(buffer* buf, const dropbear_ed25519_key *key); void buf_put_ed25519_priv_key(buffer* buf, const dropbear_ed25519_key *key); diff -r cba37fe1ddc8 -r 35d504d59c05 signkey.c --- a/signkey.c Sat Jan 22 12:46:08 2022 +0800 +++ b/signkey.c Sat Jan 22 16:53:04 2022 +0300 @@ -28,6 +28,8 @@ #include "buffer.h" #include "ssh.h" #include "ecdsa.h" +#include "sk-ecdsa.h" +#include "sk-ed25519.h" #include "rsa.h" #include "dss.h" #include "ed25519.h" @@ -43,9 +45,15 @@ "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", +#if DROPBEAR_SK_ECDSA + "sk-ecdsa-sha2-nistp256@openssh.com", +#endif /* DROPBEAR_SK_ECDSA */ #endif /* DROPBEAR_ECDSA */ #if DROPBEAR_ED25519 "ssh-ed25519", +#if DROPBEAR_SK_ED25519 + "sk-ssh-ed25519@openssh.com", +#endif /* DROPBEAR_SK_ED25519 */ #endif /* DROPBEAR_ED25519 */ /* "rsa-sha2-256" is special-cased below since it is only a signature name, not key type */ }; @@ -180,11 +188,17 @@ switch (type) { #if DROPBEAR_ED25519 case DROPBEAR_SIGNKEY_ED25519: +#if DROPBEAR_SK_ED25519 + case DROPBEAR_SIGNKEY_SK_ED25519: +#endif return (void**)&key->ed25519key; #endif #if DROPBEAR_ECDSA #if DROPBEAR_ECC_256 case DROPBEAR_SIGNKEY_ECDSA_NISTP256: +#if DROPBEAR_SK_ECDSA + case DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256: +#endif return (void**)&key->ecckey256; #endif #if DROPBEAR_ECC_384 @@ -260,7 +274,11 @@ } #endif #if DROPBEAR_ECDSA - if (signkey_is_ecdsa(keytype)) { + if (signkey_is_ecdsa(keytype) +#if DROPBEAR_SK_ECDSA + || keytype == DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256 +#endif + ) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); if (eck) { if (*eck) { @@ -276,10 +294,14 @@ } #endif #if DROPBEAR_ED25519 - if (keytype == DROPBEAR_SIGNKEY_ED25519) { + if (keytype == DROPBEAR_SIGNKEY_ED25519 +#if DROPBEAR_SK_ED25519 + || keytype == DROPBEAR_SIGNKEY_SK_ED25519 +#endif + ) { ed25519_key_free(key->ed25519key); key->ed25519key = m_malloc(sizeof(*key->ed25519key)); - ret = buf_get_ed25519_pub_key(buf, key->ed25519key); + ret = buf_get_ed25519_pub_key(buf, key->ed25519key, keytype); if (ret == DROPBEAR_FAILURE) { m_free(key->ed25519key); key->ed25519key = NULL; @@ -287,6 +309,19 @@ } #endif +#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 + if (0 +#if DROPBEAR_SK_ED25519 + || keytype == DROPBEAR_SIGNKEY_SK_ED25519 +#endif +#if DROPBEAR_SK_ECDSA + || keytype == DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256 +#endif + ) { + key->sk_app = buf_getstring(buf, &key->sk_applen); + } +#endif + TRACE2(("leave buf_get_pub_key")) return ret; @@ -401,7 +436,11 @@ } #endif #if DROPBEAR_ED25519 - if (type == DROPBEAR_SIGNKEY_ED25519) { + if (type == DROPBEAR_SIGNKEY_ED25519 +#if DROPBEAR_SK_ED25519 + || type == DROPBEAR_SIGNKEY_SK_ED25519 +#endif + ) { buf_put_ed25519_pub_key(pubkeys, key->ed25519key); } #endif @@ -495,6 +534,11 @@ #endif m_free(key->filename); +#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 + if (key->sk_app) { + m_free(key->sk_app); + } +#endif m_free(key); TRACE2(("leave sign_key_free")) @@ -639,6 +683,7 @@ } #if DROPBEAR_SIGNKEY_VERIFY + /* Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE. * If FAILURE is returned, the position of * buf is undefined. If SUCCESS is returned, buf will be positioned after the @@ -695,6 +740,22 @@ return buf_ed25519_verify(buf, key->ed25519key, data_buf); } #endif +#if DROPBEAR_SK_ECDSA + if (keytype == DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256) { + ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); + if (eck && *eck) { + return buf_sk_ecdsa_verify(buf, *eck, data_buf, key->sk_app, key->sk_applen); + } + } +#endif +#if DROPBEAR_SK_ED25519 + if (keytype == DROPBEAR_SIGNKEY_SK_ED25519) { + dropbear_ed25519_key **eck = (dropbear_ed25519_key**)signkey_key_ptr(key, keytype); + if (eck && *eck) { + return buf_sk_ed25519_verify(buf, *eck, data_buf, key->sk_app, key->sk_applen); + } + } +#endif dropbear_exit("Non-matching signing type"); return DROPBEAR_FAILURE; diff -r cba37fe1ddc8 -r 35d504d59c05 signkey.h --- a/signkey.h Sat Jan 22 12:46:08 2022 +0800 +++ b/signkey.h Sat Jan 22 16:53:04 2022 +0300 @@ -44,9 +44,15 @@ DROPBEAR_SIGNKEY_ECDSA_NISTP256, DROPBEAR_SIGNKEY_ECDSA_NISTP384, DROPBEAR_SIGNKEY_ECDSA_NISTP521, +#if DROPBEAR_SK_ECDSA + DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256, +#endif /* DROPBEAR_SK_ECDSA */ #endif /* DROPBEAR_ECDSA */ #if DROPBEAR_ED25519 DROPBEAR_SIGNKEY_ED25519, +#if DROPBEAR_SK_ED25519 + DROPBEAR_SIGNKEY_SK_ED25519, +#endif #endif DROPBEAR_SIGNKEY_NUM_NAMED, DROPBEAR_SIGNKEY_ECDSA_KEYGEN = 70, /* just "ecdsa" for keygen */ @@ -63,9 +69,15 @@ DROPBEAR_SIGNATURE_ECDSA_NISTP256 = DROPBEAR_SIGNKEY_ECDSA_NISTP256, DROPBEAR_SIGNATURE_ECDSA_NISTP384 = DROPBEAR_SIGNKEY_ECDSA_NISTP384, DROPBEAR_SIGNATURE_ECDSA_NISTP521 = DROPBEAR_SIGNKEY_ECDSA_NISTP521, +#if DROPBEAR_SK_ECDSA + DROPBEAR_SIGNATURE_SK_ECDSA_NISTP256 = DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256, +#endif /* DROPBEAR_SK_ECDSA */ #endif /* DROPBEAR_ECDSA */ #if DROPBEAR_ED25519 DROPBEAR_SIGNATURE_ED25519 = DROPBEAR_SIGNKEY_ED25519, +#if DROPBEAR_SK_ED25519 + DROPBEAR_SIGNATURE_SK_ED25519 = DROPBEAR_SIGNKEY_SK_ED25519, +#endif #endif #if DROPBEAR_RSA_SHA1 DROPBEAR_SIGNATURE_RSA_SHA1 = 100, /* ssh-rsa signature (sha1) */ @@ -110,6 +122,12 @@ #if DROPBEAR_ED25519 struct dropbear_ED25519_Key * ed25519key; #endif + +#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 + /* application ID for U2F/FIDO key types, a malloced string */ + char * sk_app; + unsigned int sk_applen; +#endif }; typedef struct SIGN_key sign_key; @@ -130,6 +148,7 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, const buffer *data_buf); #if DROPBEAR_SIGNKEY_VERIFY int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf); +int sk_buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf, char* app, unsigned int applen); char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); #endif int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, diff -r cba37fe1ddc8 -r 35d504d59c05 sk-ecdsa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sk-ecdsa.c Sat Jan 22 16:53:04 2022 +0300 @@ -0,0 +1,47 @@ +#include "includes.h" + +#if DROPBEAR_SK_ECDSA + +#include "dbutil.h" +#include "ecc.h" +#include "ecdsa.h" +#include "sk-ecdsa.h" + +int buf_sk_ecdsa_verify(buffer *buf, const ecc_key *key, const buffer *data_buf, const char* app, unsigned int applen) { + hash_state hs; + unsigned char subhash[SHA256_HASH_SIZE]; + buffer *sk_buffer = NULL, *sig_buffer = NULL; + unsigned char flags; + unsigned int counter; + int ret; + + TRACE(("buf_sk_ecdsa_verify")) + + /* from https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.u2f */ + /* ecdsa signature to verify (r, s) */ + sig_buffer = buf_getbuf(buf); + + flags = buf_getbyte (buf); + counter = buf_getint (buf); + /* create the message to be signed */ + sk_buffer = buf_new (2*SHA256_HASH_SIZE+5); + sha256_init (&hs); + sha256_process (&hs, app, applen); + sha256_done (&hs, subhash); + buf_putbytes (sk_buffer, subhash, sizeof (subhash)); + buf_putbyte (sk_buffer, flags); + buf_putint (sk_buffer, counter); + sha256_init (&hs); + sha256_process (&hs, data_buf->data, data_buf->len); + sha256_done (&hs, subhash); + buf_putbytes (sk_buffer, subhash, sizeof (subhash)); + + ret = buf_ecdsa_verify(sig_buffer, key, sk_buffer); + buf_free(sk_buffer); + buf_free(sig_buffer); + + TRACE(("leave buf_sk_ecdsa_verify, ret=%d", ret)) + return ret; +} + +#endif /* DROPBEAR_SK_ECDSA */ diff -r cba37fe1ddc8 -r 35d504d59c05 sk-ecdsa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sk-ecdsa.h Sat Jan 22 16:53:04 2022 +0300 @@ -0,0 +1,15 @@ +#ifndef DROPBEAR_SK_ECDSA_H_ +#define DROPBEAR_SK_ECDSA_H_ + +#include "includes.h" + +#if DROPBEAR_SK_ECDSA + +#include "buffer.h" +#include "signkey.h" + +int buf_sk_ecdsa_verify(buffer *buf, const ecc_key *key, const buffer *data_buf, const char* app, unsigned int applen); + +#endif + +#endif /* DROPBEAR_SK_ECDSA_H_ */ diff -r cba37fe1ddc8 -r 35d504d59c05 sk-ed25519.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sk-ed25519.c Sat Jan 22 16:53:04 2022 +0300 @@ -0,0 +1,61 @@ +#include "includes.h" + +#if DROPBEAR_SK_ED25519 + +#include "dbutil.h" +#include "buffer.h" +#include "curve25519.h" +#include "ed25519.h" + +int buf_sk_ed25519_verify(buffer *buf, const dropbear_ed25519_key *key, const buffer *data_buf, const char* app, unsigned int applen) { + + int ret = DROPBEAR_FAILURE; + unsigned char *s; + unsigned long slen; + hash_state hs; + unsigned char hash[SHA256_HASH_SIZE]; + buffer *sk_buffer = NULL; + unsigned char flags; + unsigned int counter; + + TRACE(("enter buf_sk_ed25519_verify")) + dropbear_assert(key != NULL); + + slen = buf_getint(buf); + if (slen != 64 || buf->len - buf->pos < slen) { + TRACE(("leave buf_sk_ed25519_verify: bad size")) + goto out; + } + s = buf_getptr(buf, slen); + buf_incrpos(buf, slen); + + flags = buf_getbyte (buf); + counter = buf_getint (buf); + sk_buffer = buf_new (2*SHA256_HASH_SIZE+5); + sha256_init (&hs); + sha256_process (&hs, app, applen); + sha256_done (&hs, hash); + buf_putbytes (sk_buffer, hash, sizeof (hash)); + buf_putbyte (sk_buffer, flags); + buf_putint (sk_buffer, counter); + sha256_init (&hs); + sha256_process (&hs, data_buf->data, data_buf->len); + sha256_done (&hs, hash); + buf_putbytes (sk_buffer, hash, sizeof (hash)); + + if (dropbear_ed25519_verify(sk_buffer->data, sk_buffer->len, + s, slen, key->pub) == 0) { + /* signature is valid */ + TRACE(("leave buf_sk_ed25519_verify: success!")) + ret = DROPBEAR_SUCCESS; + } + +out: + if (sk_buffer) { + buf_free(sk_buffer); + } + TRACE(("leave buf_sk_ed25519_verify: ret %d", ret)) + return ret; +} + +#endif /* DROPBEAR_SK_ED25519 */ diff -r cba37fe1ddc8 -r 35d504d59c05 sk-ed25519.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sk-ed25519.h Sat Jan 22 16:53:04 2022 +0300 @@ -0,0 +1,15 @@ +#ifndef DROPBEAR_SK_ED25519_H_ +#define DROPBEAR_SK_ED25519_H_ + +#include "includes.h" + +#if DROPBEAR_SK_ED25519 + +#include "buffer.h" +#include "ed25519.h" + +int buf_sk_ed25519_verify(buffer *buf, const dropbear_ed25519_key *key, const buffer *data_buf, const char* app, unsigned int applen); + +#endif + +#endif /* DROPBEAR_SK_ED25519_H_ */ diff -r cba37fe1ddc8 -r 35d504d59c05 svr-runopts.c --- a/svr-runopts.c Sat Jan 22 12:46:08 2022 +0800 +++ b/svr-runopts.c Sat Jan 22 16:53:04 2022 +0300 @@ -668,6 +668,12 @@ any_keys = 1; } #endif +#if DROPBEAR_SK_ECDSA + disablekey(DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256); +#endif +#if DROPBEAR_SK_ED25519 + disablekey(DROPBEAR_SIGNKEY_SK_ED25519); +#endif if (!any_keys) { dropbear_exit("No hostkeys available. 'dropbear -R' may be useful or run dropbearkey."); diff -r cba37fe1ddc8 -r 35d504d59c05 sysoptions.h --- a/sysoptions.h Sat Jan 22 12:46:08 2022 +0800 +++ b/sysoptions.h Sat Jan 22 16:53:04 2022 +0300 @@ -95,6 +95,7 @@ #define DROPBEAR_MAX_PASSWORD_LEN 100 #define SHA1_HASH_SIZE 20 +#define SHA256_HASH_SIZE 32 #define MD5_HASH_SIZE 16 #define MAX_HASH_SIZE 64 /* sha512 */