diff options
author | djm@openbsd.org <djm@openbsd.org> | 2019-10-31 21:15:14 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2019-11-01 09:46:08 +1100 |
commit | 02bb0768a937e50bbb236efc2bbdddb1991b1c85 (patch) | |
tree | d0a182540b8034345b20a49a09cbf90b234cad1c | |
parent | 57ecc10628b04c384cbba2fbc87d38b74cd1199d (diff) | |
download | openssh-git-02bb0768a937e50bbb236efc2bbdddb1991b1c85.tar.gz |
upstream: Initial infrastructure for U2F/FIDO support
Key library support: including allocation, marshalling public/private
keys and certificates, signature validation.
feedback & ok markus@
OpenBSD-Commit-ID: a17615ba15e0f7932ac4360cb18fc9a9544e68c7
-rw-r--r-- | Makefile.in | 3 | ||||
-rw-r--r-- | pathnames.h | 3 | ||||
-rw-r--r-- | ssh-ecdsa-sk.c | 180 | ||||
-rw-r--r-- | sshkey.c | 205 | ||||
-rw-r--r-- | sshkey.h | 19 |
5 files changed, 381 insertions, 29 deletions
diff --git a/Makefile.in b/Makefile.in index adb1977e..f22c8367 100644 --- a/Makefile.in +++ b/Makefile.in @@ -91,7 +91,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ log.o match.o moduli.o nchan.o packet.o \ readpass.o ttymodes.o xmalloc.o addrmatch.o \ atomicio.o dispatch.o mac.o misc.o utf8.o \ - monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-ecdsa-sk.o \ + ssh-rsa.o dh.o \ msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ ssh-pkcs11.o smult_curve25519_ref.o \ poly1305.o chacha.o cipher-chachapoly.o \ diff --git a/pathnames.h b/pathnames.h index cb44caa4..2e0c7b15 100644 --- a/pathnames.h +++ b/pathnames.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pathnames.h,v 1.28 2018/02/23 15:58:37 markus Exp $ */ +/* $OpenBSD: pathnames.h,v 1.29 2019/10/31 21:15:14 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -77,6 +77,7 @@ #define _PATH_SSH_CLIENT_ID_RSA _PATH_SSH_USER_DIR "/id_rsa" #define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519" #define _PATH_SSH_CLIENT_ID_XMSS _PATH_SSH_USER_DIR "/id_xmss" +#define _PATH_SSH_CLIENT_ID_ECDSA_SK _PATH_SSH_USER_DIR "/id_ecdsa_sk" /* * Configuration file in user's home directory. This file need not be diff --git a/ssh-ecdsa-sk.c b/ssh-ecdsa-sk.c new file mode 100644 index 00000000..6441cd7f --- /dev/null +++ b/ssh-ecdsa-sk.c @@ -0,0 +1,180 @@ +/* $OpenBSD: ssh-ecdsa-sk.c,v 1.1 2019/10/31 21:15:14 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2010 Damien Miller. All rights reserved. + * Copyright (c) 2019 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* #define DEBUG_SK 1 */ + +#include "includes.h" + +#include <sys/types.h> + +#include <openssl/bn.h> +#include <openssl/ec.h> +#include <openssl/ecdsa.h> +#include <openssl/evp.h> + +#include <string.h> +#include <stdio.h> /* needed for DEBUG_SK only */ + +#include "sshbuf.h" +#include "ssherr.h" +#include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" + +/* ARGSUSED */ +int +ssh_ecdsa_sk_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) +{ + ECDSA_SIG *sig = NULL; + BIGNUM *sig_r = NULL, *sig_s = NULL; + u_char sig_flags; + u_char msghash[32], apphash[32], sighash[32]; + u_int sig_counter; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *sigbuf = NULL, *original_signed = NULL; + char *ktype = NULL; +#ifdef DEBUG_SK + char *tmp = NULL; +#endif + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA_SK || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + + if (key->ecdsa_nid != NID_X9_62_prime256v1) + return SSH_ERR_INTERNAL_ERROR; + + /* fetch signature */ + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_froms(b, &sigbuf) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + + /* parse signature */ + if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 || + sshbuf_get_bignum2(sigbuf, &sig_s) != 0 || + sshbuf_get_u8(sigbuf, &sig_flags) != 0 || + sshbuf_get_u32(sigbuf, &sig_counter) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((sig = ECDSA_SIG_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (!ECDSA_SIG_set0(sig, sig_r, sig_s)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +#ifdef DEBUG_SK + fprintf(stderr, "%s: sig_r: %s\n", __func__, (tmp = BN_bn2hex(sig_r))); + free(tmp); + fprintf(stderr, "%s: sig_s: %s\n", __func__, (tmp = BN_bn2hex(sig_s))); + free(tmp); + fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", + __func__, sig_flags, sig_counter); +#endif + sig_r = sig_s = NULL; /* transferred */ + + if (sshbuf_len(sigbuf) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + + /* Reconstruct data that was supposedly signed */ + if ((original_signed = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen, + msghash, sizeof(msghash))) != 0) + goto out; + /* Application value is hashed before signature */ + if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application, + strlen(key->sk_application), apphash, sizeof(apphash))) != 0) + goto out; +#ifdef DEBUG_SK + fprintf(stderr, "%s: hashed message:\n", __func__); + sshbuf_dump_data(msghash, sizeof(msghash), stderr); +#endif + if ((ret = sshbuf_put(original_signed, + apphash, sizeof(apphash))) != 0 || + (ret = sshbuf_put_u8(original_signed, sig_flags)) != 0 || + (ret = sshbuf_put_u32(original_signed, sig_counter)) != 0 || + (ret = sshbuf_put(original_signed, msghash, sizeof(msghash))) != 0) + goto out; + /* Signature is over H(original_signed) */ + if ((ret = ssh_digest_buffer(SSH_DIGEST_SHA256, original_signed, + sighash, sizeof(sighash))) != 0) + goto out; +#ifdef DEBUG_SK + fprintf(stderr, "%s: signed buf:\n", __func__); + sshbuf_dump(original_signed, stderr); + fprintf(stderr, "%s: signed hash:\n", __func__); + sshbuf_dump_data(sighash, sizeof(sighash), stderr); +#endif + + /* Verify it */ + switch (ECDSA_do_verify(sighash, sizeof(sighash), sig, key->ecdsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + out: + explicit_bzero(&sig_flags, sizeof(sig_flags)); + explicit_bzero(&sig_counter, sizeof(sig_counter)); + explicit_bzero(msghash, sizeof(msghash)); + explicit_bzero(sighash, sizeof(msghash)); + explicit_bzero(apphash, sizeof(apphash)); + sshbuf_free(original_signed); + sshbuf_free(sigbuf); + sshbuf_free(b); + ECDSA_SIG_free(sig); + BN_clear_free(sig_r); + BN_clear_free(sig_s); + free(ktype); + return ret; +} @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.c,v 1.84 2019/10/09 00:04:42 djm Exp $ */ +/* $OpenBSD: sshkey.c,v 1.85 2019/10/31 21:15:14 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. @@ -125,6 +125,8 @@ static const struct keytype keytypes[] = { { "ecdsa-sha2-nistp521", "ECDSA", NULL, KEY_ECDSA, NID_secp521r1, 0, 0 }, # endif /* OPENSSL_HAS_NISTP521 */ + { "sk-ecdsa-sha2-nistp256@openssh.com", "ECDSA-SK", NULL, + KEY_ECDSA_SK, NID_X9_62_prime256v1, 0, 0 }, # endif /* OPENSSL_HAS_ECC */ { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL, KEY_RSA_CERT, 0, 1, 0 }, @@ -143,6 +145,8 @@ static const struct keytype keytypes[] = { { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", NULL, KEY_ECDSA_CERT, NID_secp521r1, 1, 0 }, # endif /* OPENSSL_HAS_NISTP521 */ + { "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-SK-CERT", NULL, + KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 }, # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ { NULL, NULL, NULL, -1, -1, 0, 0 } @@ -211,13 +215,26 @@ sshkey_type_from_name(const char *name) return KEY_UNSPEC; } +static int +key_type_is_ecdsa_variant(int type) +{ + switch (type) { + case KEY_ECDSA: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + return 1; + } + return 0; +} + int sshkey_ecdsa_nid_from_name(const char *name) { const struct keytype *kt; for (kt = keytypes; kt->type != -1; kt++) { - if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT) + if (!key_type_is_ecdsa_variant(kt->type)) continue; if (kt->name != NULL && strcmp(name, kt->name) == 0) return kt->nid; @@ -313,6 +330,8 @@ sshkey_size(const struct sshkey *k) return BN_num_bits(dsa_p); case KEY_ECDSA: case KEY_ECDSA_CERT: + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: return sshkey_curve_nid_to_bits(k->ecdsa_nid); #endif /* WITH_OPENSSL */ case KEY_ED25519: @@ -331,6 +350,7 @@ sshkey_type_is_valid_ca(int type) case KEY_RSA: case KEY_DSA: case KEY_ECDSA: + case KEY_ECDSA_SK: case KEY_ED25519: case KEY_XMSS: return 1; @@ -358,6 +378,8 @@ sshkey_type_plain(int type) return KEY_DSA; case KEY_ECDSA_CERT: return KEY_ECDSA; + case KEY_ECDSA_SK_CERT: + return KEY_ECDSA_SK; case KEY_ED25519_CERT: return KEY_ED25519; case KEY_XMSS_CERT: @@ -533,6 +555,8 @@ sshkey_new(int type) break; case KEY_ECDSA: case KEY_ECDSA_CERT: + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: /* Cannot do anything until we know the group */ break; #endif /* WITH_OPENSSL */ @@ -577,6 +601,12 @@ sshkey_free(struct sshkey *k) k->dsa = NULL; break; # ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + free(k->sk_application); + sshbuf_free(k->sk_key_handle); + sshbuf_free(k->sk_reserved); + /* FALLTHROUGH */ case KEY_ECDSA: case KEY_ECDSA_CERT: EC_KEY_free(k->ecdsa); @@ -676,6 +706,13 @@ sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) BN_cmp(dsa_g_a, dsa_g_b) == 0 && BN_cmp(dsa_pub_key_a, dsa_pub_key_b) == 0; # ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + if (a->sk_application == NULL || b->sk_application == NULL) + return 0; + if (strcmp(a->sk_application, b->sk_application) != 0) + return 0; + /* FALLTHROUGH */ case KEY_ECDSA_CERT: case KEY_ECDSA: if (a->ecdsa == NULL || b->ecdsa == NULL || @@ -751,6 +788,7 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain, #ifdef WITH_OPENSSL case KEY_DSA_CERT: case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: case KEY_RSA_CERT: #endif /* WITH_OPENSSL */ case KEY_ED25519_CERT: @@ -777,6 +815,7 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain, break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: + case KEY_ECDSA_SK: if (key->ecdsa == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((ret = sshbuf_put_cstring(b, typename)) != 0 || @@ -784,6 +823,11 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain, sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0) return ret; + if (type == KEY_ECDSA_SK) { + if ((ret = sshbuf_put_cstring(b, + key->sk_application)) != 0) + return ret; + } break; # endif case KEY_RSA: @@ -1217,7 +1261,7 @@ peek_type_nid(const char *s, size_t l, int *nid) continue; if (memcmp(s, kt->name, l) == 0) { *nid = -1; - if (kt->type == KEY_ECDSA || kt->type == KEY_ECDSA_CERT) + if (key_type_is_ecdsa_variant(kt->type)) *nid = kt->nid; return kt->type; } @@ -1243,9 +1287,11 @@ sshkey_read(struct sshkey *ret, char **cpp) case KEY_RSA: case KEY_DSA: case KEY_ECDSA: + case KEY_ECDSA_SK: case KEY_ED25519: case KEY_DSA_CERT: case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: case KEY_RSA_CERT: case KEY_ED25519_CERT: #ifdef WITH_XMSS @@ -1302,7 +1348,7 @@ sshkey_read(struct sshkey *ret, char **cpp) sshkey_free(k); return SSH_ERR_KEY_TYPE_MISMATCH; } - if (sshkey_type_plain(type) == KEY_ECDSA && curve_nid != k->ecdsa_nid) { + if (key_type_is_ecdsa_variant(type) && curve_nid != k->ecdsa_nid) { sshkey_free(k); return SSH_ERR_EC_CURVE_MISMATCH; } @@ -1348,6 +1394,19 @@ sshkey_read(struct sshkey *ret, char **cpp) sshkey_dump_ec_key(ret->ecdsa); #endif break; + case KEY_ECDSA_SK: + EC_KEY_free(ret->ecdsa); + ret->ecdsa = k->ecdsa; + ret->ecdsa_nid = k->ecdsa_nid; + ret->sk_application = k->sk_application; + k->ecdsa = NULL; + k->ecdsa_nid = -1; + k->sk_application = NULL; +#ifdef DEBUG_PK + sshkey_dump_ec_key(ret->ecdsa); + fprintf(stderr, "App: %s\n", ret->sk_application); +#endif + break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519: @@ -1747,15 +1806,14 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) #endif /* WITH_OPENSSL */ *pkp = NULL; + if ((n = sshkey_new(k->type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } switch (k->type) { #ifdef WITH_OPENSSL case KEY_DSA: case KEY_DSA_CERT: - if ((n = sshkey_new(k->type)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; - } - DSA_get0_pqg(k->dsa, &dsa_p, &dsa_q, &dsa_g); DSA_get0_key(k->dsa, &dsa_pub_key, NULL); if ((dsa_p_dup = BN_dup(dsa_p)) == NULL || @@ -1780,10 +1838,8 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: case KEY_ECDSA_CERT: - if ((n = sshkey_new(k->type)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; - } + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: n->ecdsa_nid = k->ecdsa_nid; n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); if (n->ecdsa == NULL) { @@ -1795,14 +1851,15 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } + if (k->type != KEY_ECDSA_SK && k->type != KEY_ECDSA_SK_CERT) + break; + /* Append security-key application string */ + if ((n->sk_application = strdup(k->sk_application)) == NULL) + goto out; break; # endif /* OPENSSL_HAS_ECC */ case KEY_RSA: case KEY_RSA_CERT: - if ((n = sshkey_new(k->type)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; - } RSA_get0_key(k->rsa, &rsa_n, &rsa_e, NULL); if ((rsa_n_dup = BN_dup(rsa_n)) == NULL || (rsa_e_dup = BN_dup(rsa_e)) == NULL) { @@ -1818,10 +1875,6 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: - if ((n = sshkey_new(k->type)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; - } if (k->ed25519_pk != NULL) { if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { r = SSH_ERR_ALLOC_FAIL; @@ -1833,10 +1886,6 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) #ifdef WITH_XMSS case KEY_XMSS: case KEY_XMSS_CERT: - if ((n = sshkey_new(k->type)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; - } if ((r = sshkey_xmss_init(n, k->xmss_name)) != 0) goto out; if (k->xmss_pk != NULL) { @@ -2329,6 +2378,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, #endif break; case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: /* Skip nonce */ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; @@ -2337,6 +2387,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, /* FALLTHROUGH */ # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: + case KEY_ECDSA_SK: if ((key = sshkey_new(type)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; @@ -2377,6 +2428,17 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, #ifdef DEBUG_PK sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q); #endif + if (type == KEY_ECDSA_SK || type == KEY_ECDSA_SK_CERT) { + /* Parse additional security-key application string */ + if (sshbuf_get_cstring(b, &key->sk_application, + NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } +#ifdef DEBUG_PK + fprintf(stderr, "App: %s\n", key->sk_application); +#endif + } break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ @@ -2665,6 +2727,10 @@ sshkey_verify(const struct sshkey *key, case KEY_ECDSA_CERT: case KEY_ECDSA: return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat); + case KEY_ECDSA_SK_CERT: + case KEY_ECDSA_SK: + return ssh_ecdsa_sk_verify(key, sig, siglen, data, dlen, + compat); # endif /* OPENSSL_HAS_ECC */ case KEY_RSA_CERT: case KEY_RSA: @@ -2700,6 +2766,9 @@ sshkey_to_certified(struct sshkey *k) case KEY_ECDSA: newtype = KEY_ECDSA_CERT; break; + case KEY_ECDSA_SK: + newtype = KEY_ECDSA_SK_CERT; + break; #endif /* WITH_OPENSSL */ case KEY_ED25519: newtype = KEY_ED25519_CERT; @@ -2797,12 +2866,18 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: if ((ret = sshbuf_put_cstring(cert, sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 || (ret = sshbuf_put_ec(cert, EC_KEY_get0_public_key(k->ecdsa), EC_KEY_get0_group(k->ecdsa))) != 0) goto out; + if (k->type == KEY_ECDSA_SK_CERT) { + if ((ret = sshbuf_put_cstring(cert, + k->sk_application)) != 0) + goto out; + } break; # endif /* OPENSSL_HAS_ECC */ case KEY_RSA_CERT: @@ -3082,6 +3157,28 @@ sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, EC_KEY_get0_private_key(key->ecdsa))) != 0) goto out; break; + case KEY_ECDSA_SK: + if ((r = sshbuf_put_cstring(b, + sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || + (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 || + (r = sshbuf_put_cstring(b, key->sk_application)) != 0 || + (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) + goto out; + break; + case KEY_ECDSA_SK_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_cstring(b, key->sk_application)) != 0 || + (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) + goto out; + break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519: @@ -3270,6 +3367,60 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) (r = sshkey_ec_validate_private(k->ecdsa)) != 0) goto out; break; + case KEY_ECDSA_SK: + if ((k = sshkey_new(type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0) + goto out; + if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { + r = SSH_ERR_EC_CURVE_MISMATCH; + goto out; + } + if ((k->sk_key_handle = sshbuf_new()) == NULL || + (k->sk_reserved = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); + if (k->ecdsa == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 || + (r = sshbuf_get_cstring(buf, &k->sk_application, + NULL)) != 0 || + (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) + goto out; + if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + EC_KEY_get0_public_key(k->ecdsa))) != 0) + goto out; + break; + case KEY_ECDSA_SK_CERT: + if ((r = sshkey_froms(buf, &k)) != 0) + goto out; + if ((k->sk_key_handle = sshbuf_new()) == NULL || + (k->sk_reserved = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &k->sk_application, + NULL)) != 0 || + (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) + goto out; + if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + EC_KEY_get0_public_key(k->ecdsa))) != 0) + goto out; + break; # endif /* OPENSSL_HAS_ECC */ case KEY_RSA: if ((k = sshkey_new(type)) == NULL) { @@ -4090,6 +4241,9 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, #ifdef WITH_XMSS case KEY_XMSS: #endif /* WITH_XMSS */ +#ifdef WITH_OPENSSL + case KEY_ECDSA_SK: +#endif /* WITH_OPENSSL */ return sshkey_private_to_blob2(key, blob, passphrase, comment, openssh_format_cipher, openssh_format_rounds); default: @@ -4111,7 +4265,6 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, #endif /* WITH_OPENSSL */ } - #ifdef WITH_OPENSSL static int translate_libcrypto_error(unsigned long pem_err) @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.34 2019/09/03 08:31:20 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.35 2019/10/31 21:15:14 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -65,6 +65,8 @@ enum sshkey_types { KEY_ED25519_CERT, KEY_XMSS, KEY_XMSS_CERT, + KEY_ECDSA_SK, + KEY_ECDSA_SK_CERT, KEY_UNSPEC }; @@ -118,18 +120,30 @@ struct sshkey_cert { struct sshkey { int type; int flags; + /* KEY_RSA */ RSA *rsa; + /* KEY_DSA */ DSA *dsa; + /* KEY_ECDSA and KEY_ECDSA_SK */ int ecdsa_nid; /* NID of curve */ EC_KEY *ecdsa; + /* KEY_ED25519 */ u_char *ed25519_sk; u_char *ed25519_pk; + /* KEY_XMSS */ char *xmss_name; char *xmss_filename; /* for state file updates */ void *xmss_state; /* depends on xmss_name, opaque */ u_char *xmss_sk; u_char *xmss_pk; + /* KEY_ECDSA_SK */ + char *sk_application; + uint8_t sk_flags; + struct sshbuf *sk_key_handle; + struct sshbuf *sk_reserved; + /* Certificates */ struct sshkey_cert *cert; + /* Private key shielding */ u_char *shielded_private; size_t shielded_len; u_char *shield_prekey; @@ -268,6 +282,9 @@ int ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, int ssh_ecdsa_verify(const struct sshkey *key, const u_char *signature, size_t signaturelen, const u_char *data, size_t datalen, u_int compat); +int ssh_ecdsa_sk_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); int ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat); int ssh_ed25519_verify(const struct sshkey *key, |