From 2ad020ff3018c5dc29be091a4dccfd3032663e85 Mon Sep 17 00:00:00 2001 From: Egor Duda Date: Sun, 16 Jan 2022 00:50:39 +0300 Subject: [PATCH] Implement server-side support for sk-ed25519 FIDO2-backed keys --- Makefile.in | 2 +- common-algo.c | 3 +++ default_options.h | 1 + ed25519.c | 8 +++++-- ed25519.h | 2 +- signkey.c | 47 +++++++++++++++++++++++++++++++----- signkey.h | 10 ++++++++ sk-ecdsa.c | 12 +++------- sk-ecdsa.h | 6 ++--- sk-ed25519.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ sk-ed25519.h | 15 ++++++++++++ svr-authpubkey.c | 22 ++++++++++++----- 12 files changed, 161 insertions(+), 28 deletions(-) create mode 100644 sk-ed25519.c create mode 100644 sk-ed25519.h diff --git a/Makefile.in b/Makefile.in index cab73d8..f53e9c1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -36,7 +36,7 @@ COMMONOBJS=dbutil.o buffer.o dbhelpers.o \ queue.o \ atomicio.o compat.o fake-rfc2553.o \ ltc_prng.o ecc.o ecdsa.o sk-ecdsa.o crypto_desc.o \ - curve25519.o ed25519.o \ + curve25519.o ed25519.o sk-ed25519.o \ dbmalloc.o \ gensignkey.o gendss.o genrsa.o gened25519.o diff --git a/common-algo.c b/common-algo.c index 511ae01..2d27893 100644 --- a/common-algo.c +++ b/common-algo.c @@ -239,6 +239,9 @@ algo_type ssh_nocompress[] = { 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 diff --git a/default_options.h b/default_options.h index 212e659..62f2a39 100644 --- a/default_options.h +++ b/default_options.h @@ -130,6 +130,7 @@ IMPORTANT: Some options will require "make clean" after changes */ /* 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 --git a/ed25519.c b/ed25519.c index 58cce1d..1ac3e3d 100644 --- a/ed25519.c +++ b/ed25519.c @@ -38,14 +38,18 @@ * 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, int sk) { unsigned int len; TRACE(("enter buf_get_ed25519_pub_key")) dropbear_assert(key != NULL); - buf_incrpos(buf, 4+SSH_SIGNKEY_ED25519_LEN); /* int + "ssh-ed25519" */ + if (sk) { + buf_incrpos(buf, 30); /* int + "sk-ssh-ed25519@openssh.com" */ + } else { + buf_incrpos(buf, 4+SSH_SIGNKEY_ED25519_LEN); /* int + "ssh-ed25519" */ + } len = buf_getint(buf); if (len != CURVE25519_LEN || buf->len - buf->pos < len) { diff --git a/ed25519.h b/ed25519.h index 622111a..f415e66 100644 --- a/ed25519.h +++ b/ed25519.h @@ -43,7 +43,7 @@ void buf_put_ed25519_sign(buffer* buf, const dropbear_ed25519_key *key, const bu #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, int sk); 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 --git a/signkey.c b/signkey.c index 2035635..253154f 100644 --- a/signkey.c +++ b/signkey.c @@ -29,6 +29,7 @@ #include "ssh.h" #include "ecdsa.h" #include "sk-ecdsa.h" +#include "sk-ed25519.h" #include "rsa.h" #include "dss.h" #include "ed25519.h" @@ -44,10 +45,15 @@ static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { "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 */ }; @@ -182,12 +188,17 @@ signkey_key_ptr(sign_key *key, enum signkey_type type) { 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 @@ -221,6 +232,7 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { unsigned int len; enum signkey_type keytype; int ret = DROPBEAR_FAILURE; + int is_sk = 0; TRACE2(("enter buf_get_pub_key")) @@ -265,7 +277,7 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { #if DROPBEAR_ECDSA if (signkey_is_ecdsa(keytype) #if DROPBEAR_SK_ECDSA - || signkey_is_sk_ecdsa(keytype) + || keytype == DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256 #endif ) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); @@ -283,10 +295,19 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { } #endif #if DROPBEAR_ED25519 - if (keytype == DROPBEAR_SIGNKEY_ED25519) { + if (keytype == DROPBEAR_SIGNKEY_ED25519 +#if DROPBEAR_SK_ED25519 + || keytype == DROPBEAR_SIGNKEY_SK_ED25519 +#endif + ) { +#if DROPBEAR_SK_ED25519 + if (keytype == DROPBEAR_SIGNKEY_SK_ED25519) { + is_sk = 1; + } +#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, is_sk); if (ret == DROPBEAR_FAILURE) { m_free(key->ed25519key); key->ed25519key = NULL; @@ -408,7 +429,11 @@ void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type) { } #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 @@ -647,7 +672,7 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, #if DROPBEAR_SIGNKEY_VERIFY -#if DROPBEAR_SK_ECDSA +#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 int sk_buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf, char* app, unsigned int applen) { @@ -669,12 +694,22 @@ int sk_buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtyp keytype = signkey_type_from_signature(sigtype); - if (signkey_is_sk_ecdsa(keytype)) { +#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, app, 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, app, applen); + } + } +#endif dropbear_exit("Non-matching signing type"); return DROPBEAR_FAILURE; } diff --git a/signkey.h b/signkey.h index b58af47..ee0ac11 100644 --- a/signkey.h +++ b/signkey.h @@ -44,10 +44,15 @@ enum signkey_type { 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 */ @@ -64,10 +69,15 @@ enum signature_type { 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) */ diff --git a/sk-ecdsa.c b/sk-ecdsa.c index 405be4b..8ca6c0e 100644 --- a/sk-ecdsa.c +++ b/sk-ecdsa.c @@ -1,16 +1,12 @@ #include "includes.h" + +#if DROPBEAR_SK_ECDSA + #include "dbutil.h" #include "ecc.h" #include "ecdsa.h" #include "sk-ecdsa.h" -#if DROPBEAR_SK_ECDSA - -int signkey_is_sk_ecdsa(enum signkey_type type) -{ - return type == DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256; -} - int buf_sk_ecdsa_verify(buffer *buf, const ecc_key *key, const buffer *data_buf, const char* app, unsigned int applen) { /* Based on libtomcrypt's ecc_verify_hash but without the asn1 */ int ret = DROPBEAR_FAILURE; @@ -176,6 +172,4 @@ out: return ret; } - - #endif /* DROPBEAR_SK_ECDSA */ diff --git a/sk-ecdsa.h b/sk-ecdsa.h index 30f3bcd..7a01305 100644 --- a/sk-ecdsa.h +++ b/sk-ecdsa.h @@ -2,13 +2,13 @@ #define DROPBEAR_SK_ECDSA_H_ #include "includes.h" -#include "buffer.h" -#include "signkey.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); -int signkey_is_sk_ecdsa(enum signkey_type type); #endif diff --git a/sk-ed25519.c b/sk-ed25519.c new file mode 100644 index 0000000..9da9606 --- /dev/null +++ b/sk-ed25519.c @@ -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 --git a/sk-ed25519.h b/sk-ed25519.h new file mode 100644 index 0000000..ad8561e --- /dev/null +++ b/sk-ed25519.h @@ -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 --git a/svr-authpubkey.c b/svr-authpubkey.c index 4182c86..f325fe8 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -64,7 +64,6 @@ #include "ssh.h" #include "packet.h" #include "algo.h" -#include "sk-ecdsa.h" #if DROPBEAR_SVR_PUBKEY_AUTH @@ -97,9 +96,10 @@ void svr_auth_pubkey(int valid_user) { enum signkey_type keytype; int auth_failure = 1; int verify_ret = DROPBEAR_FAILURE; -#if DROPBEAR_SK_ECDSA +#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 char* app = NULL; unsigned int applen; + int is_sk = 0; #endif TRACE(("enter pubkeyauth")) @@ -188,8 +188,18 @@ void svr_auth_pubkey(int valid_user) { goto out; } +#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 #if DROPBEAR_SK_ECDSA - if (signkey_is_sk_ecdsa(keytype)) { + if (keytype == DROPBEAR_SIGNKEY_SK_ECDSA_NISTP256) { + is_sk = 1; + } +#endif +#if DROPBEAR_SK_ED25519 + if (keytype == DROPBEAR_SIGNKEY_SK_ED25519) { + is_sk = 1; + } +#endif + if (is_sk) { app = buf_getstring (ses.payload, &applen); } #endif @@ -212,8 +222,8 @@ void svr_auth_pubkey(int valid_user) { /* ... and finally verify the signature */ fp = sign_key_fingerprint(keyblob, keybloblen); -#if DROPBEAR_SK_ECDSA - if (signkey_is_sk_ecdsa(keytype)) { +#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 + if (is_sk) { verify_ret = sk_buf_verify(ses.payload, key, sigtype, signbuf, app, applen); } else { verify_ret = buf_verify(ses.payload, key, sigtype, signbuf); @@ -253,7 +263,7 @@ out: sign_key_free(key); key = NULL; } -#if DROPBEAR_SK_ECDSA +#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 if (app) { m_free(app); }