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 /sshkey.c | |
parent | 57ecc10628b04c384cbba2fbc87d38b74cd1199d (diff) |
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
Diffstat (limited to 'sshkey.c')
-rw-r--r-- | sshkey.c | 205 |
1 files changed, 179 insertions, 26 deletions
@@ -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) |