1*773a7280Stb /* $OpenBSD: sshkey.c,v 1.148 2024/12/03 15:53:51 tb Exp $ */ 2811ca2d4Sdjm /* 3811ca2d4Sdjm * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 4811ca2d4Sdjm * Copyright (c) 2008 Alexander von Gernler. All rights reserved. 5811ca2d4Sdjm * Copyright (c) 2010,2011 Damien Miller. All rights reserved. 6811ca2d4Sdjm * 7811ca2d4Sdjm * Redistribution and use in source and binary forms, with or without 8811ca2d4Sdjm * modification, are permitted provided that the following conditions 9811ca2d4Sdjm * are met: 10811ca2d4Sdjm * 1. Redistributions of source code must retain the above copyright 11811ca2d4Sdjm * notice, this list of conditions and the following disclaimer. 12811ca2d4Sdjm * 2. Redistributions in binary form must reproduce the above copyright 13811ca2d4Sdjm * notice, this list of conditions and the following disclaimer in the 14811ca2d4Sdjm * documentation and/or other materials provided with the distribution. 15811ca2d4Sdjm * 16811ca2d4Sdjm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17811ca2d4Sdjm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18811ca2d4Sdjm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19811ca2d4Sdjm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20811ca2d4Sdjm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21811ca2d4Sdjm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22811ca2d4Sdjm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23811ca2d4Sdjm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24811ca2d4Sdjm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25811ca2d4Sdjm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26811ca2d4Sdjm */ 27811ca2d4Sdjm 28811ca2d4Sdjm #include <sys/types.h> 29502d8771Sdjm #include <sys/mman.h> 303dbedef4Sdjm #include <netinet/in.h> 31811ca2d4Sdjm 32b1aa73bdSdjm #ifdef WITH_OPENSSL 33811ca2d4Sdjm #include <openssl/evp.h> 34811ca2d4Sdjm #include <openssl/err.h> 35811ca2d4Sdjm #include <openssl/pem.h> 36b1aa73bdSdjm #endif 37811ca2d4Sdjm 38811ca2d4Sdjm #include "crypto_api.h" 39811ca2d4Sdjm 40811ca2d4Sdjm #include <errno.h> 41811ca2d4Sdjm #include <stdio.h> 4238ab26deSdjm #include <stdlib.h> 43811ca2d4Sdjm #include <string.h> 44811ca2d4Sdjm #include <util.h> 45b9fc9a72Sderaadt #include <limits.h> 463dbedef4Sdjm #include <resolv.h> 47811ca2d4Sdjm 48811ca2d4Sdjm #include "ssh2.h" 49811ca2d4Sdjm #include "ssherr.h" 50811ca2d4Sdjm #include "misc.h" 51811ca2d4Sdjm #include "sshbuf.h" 52811ca2d4Sdjm #include "cipher.h" 53811ca2d4Sdjm #include "digest.h" 54811ca2d4Sdjm #define SSHKEY_INTERNAL 55811ca2d4Sdjm #include "sshkey.h" 56dc9cac76Sdjm #include "match.h" 57e3a62e69Sdjm #include "ssh-sk.h" 58811ca2d4Sdjm 5951991f64Sdtucker #ifdef WITH_XMSS 6051991f64Sdtucker #include "sshkey-xmss.h" 61a6be8e7cSmarkus #include "xmss_fast.h" 6251991f64Sdtucker #endif 63a6be8e7cSmarkus 64811ca2d4Sdjm /* openssh private key file format */ 65811ca2d4Sdjm #define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" 66811ca2d4Sdjm #define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" 67811ca2d4Sdjm #define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1) 68811ca2d4Sdjm #define MARK_END_LEN (sizeof(MARK_END) - 1) 69811ca2d4Sdjm #define KDFNAME "bcrypt" 70811ca2d4Sdjm #define AUTH_MAGIC "openssh-key-v1" 71811ca2d4Sdjm #define SALT_LEN 16 7203bf114aSdjm #define DEFAULT_CIPHERNAME "aes256-ctr" 7349771933Sdjm #define DEFAULT_ROUNDS 24 74811ca2d4Sdjm 75707316f9Sdjm /* 76707316f9Sdjm * Constants relating to "shielding" support; protection of keys expected 77707316f9Sdjm * to remain in memory for long durations 78707316f9Sdjm */ 79707316f9Sdjm #define SSHKEY_SHIELD_PREKEY_LEN (16 * 1024) 80707316f9Sdjm #define SSHKEY_SHIELD_CIPHER "aes256-ctr" /* XXX want AES-EME* */ 81707316f9Sdjm #define SSHKEY_SHIELD_PREKEY_HASH SSH_DIGEST_SHA512 82707316f9Sdjm 83707316f9Sdjm int sshkey_private_serialize_opt(struct sshkey *key, 84a6be8e7cSmarkus struct sshbuf *buf, enum sshkey_serialize_rep); 854736d833Sdjm static int sshkey_from_blob_internal(struct sshbuf *buf, 86811ca2d4Sdjm struct sshkey **keyp, int allow_cert); 87811ca2d4Sdjm 88811ca2d4Sdjm /* Supported key types */ 899c1667dbSdjm extern const struct sshkey_impl sshkey_ed25519_impl; 909c1667dbSdjm extern const struct sshkey_impl sshkey_ed25519_cert_impl; 919c1667dbSdjm extern const struct sshkey_impl sshkey_ed25519_sk_impl; 929c1667dbSdjm extern const struct sshkey_impl sshkey_ed25519_sk_cert_impl; 93811ca2d4Sdjm #ifdef WITH_OPENSSL 949c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_sk_impl; 959c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_sk_cert_impl; 969c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_sk_webauthn_impl; 979c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_nistp256_impl; 989c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl; 999c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_nistp384_impl; 1009c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl; 1019c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_nistp521_impl; 1029c1667dbSdjm extern const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl; 1039c1667dbSdjm extern const struct sshkey_impl sshkey_rsa_impl; 1049c1667dbSdjm extern const struct sshkey_impl sshkey_rsa_cert_impl; 1059c1667dbSdjm extern const struct sshkey_impl sshkey_rsa_sha256_impl; 1069c1667dbSdjm extern const struct sshkey_impl sshkey_rsa_sha256_cert_impl; 1079c1667dbSdjm extern const struct sshkey_impl sshkey_rsa_sha512_impl; 1089c1667dbSdjm extern const struct sshkey_impl sshkey_rsa_sha512_cert_impl; 10952113de9Sdjm # ifdef WITH_DSA 1109c1667dbSdjm extern const struct sshkey_impl sshkey_dss_impl; 1119c1667dbSdjm extern const struct sshkey_impl sshkey_dsa_cert_impl; 11252113de9Sdjm # endif 113811ca2d4Sdjm #endif /* WITH_OPENSSL */ 1149c1667dbSdjm #ifdef WITH_XMSS 1159c1667dbSdjm extern const struct sshkey_impl sshkey_xmss_impl; 1169c1667dbSdjm extern const struct sshkey_impl sshkey_xmss_cert_impl; 1179c1667dbSdjm #endif 1189c1667dbSdjm 1199c1667dbSdjm const struct sshkey_impl * const keyimpls[] = { 1209c1667dbSdjm &sshkey_ed25519_impl, 1219c1667dbSdjm &sshkey_ed25519_cert_impl, 1229c1667dbSdjm &sshkey_ed25519_sk_impl, 1239c1667dbSdjm &sshkey_ed25519_sk_cert_impl, 1249c1667dbSdjm #ifdef WITH_OPENSSL 1259c1667dbSdjm &sshkey_ecdsa_nistp256_impl, 1269c1667dbSdjm &sshkey_ecdsa_nistp256_cert_impl, 1279c1667dbSdjm &sshkey_ecdsa_nistp384_impl, 1289c1667dbSdjm &sshkey_ecdsa_nistp384_cert_impl, 1299c1667dbSdjm &sshkey_ecdsa_nistp521_impl, 1309c1667dbSdjm &sshkey_ecdsa_nistp521_cert_impl, 1319c1667dbSdjm &sshkey_ecdsa_sk_impl, 1329c1667dbSdjm &sshkey_ecdsa_sk_cert_impl, 1339c1667dbSdjm &sshkey_ecdsa_sk_webauthn_impl, 13452113de9Sdjm # ifdef WITH_DSA 1359c1667dbSdjm &sshkey_dss_impl, 1369c1667dbSdjm &sshkey_dsa_cert_impl, 13752113de9Sdjm # endif 1389c1667dbSdjm &sshkey_rsa_impl, 1399c1667dbSdjm &sshkey_rsa_cert_impl, 1409c1667dbSdjm &sshkey_rsa_sha256_impl, 1419c1667dbSdjm &sshkey_rsa_sha256_cert_impl, 1429c1667dbSdjm &sshkey_rsa_sha512_impl, 1439c1667dbSdjm &sshkey_rsa_sha512_cert_impl, 1449c1667dbSdjm #endif /* WITH_OPENSSL */ 1459c1667dbSdjm #ifdef WITH_XMSS 1469c1667dbSdjm &sshkey_xmss_impl, 1479c1667dbSdjm &sshkey_xmss_cert_impl, 1489c1667dbSdjm #endif 1499c1667dbSdjm NULL 150811ca2d4Sdjm }; 151811ca2d4Sdjm 1529c1667dbSdjm static const struct sshkey_impl * 1539c1667dbSdjm sshkey_impl_from_type(int type) 1549c1667dbSdjm { 1559c1667dbSdjm int i; 1569c1667dbSdjm 1579c1667dbSdjm for (i = 0; keyimpls[i] != NULL; i++) { 1589c1667dbSdjm if (keyimpls[i]->type == type) 1599c1667dbSdjm return keyimpls[i]; 1609c1667dbSdjm } 1619c1667dbSdjm return NULL; 1629c1667dbSdjm } 1639c1667dbSdjm 1649c1667dbSdjm static const struct sshkey_impl * 1659c1667dbSdjm sshkey_impl_from_type_nid(int type, int nid) 1669c1667dbSdjm { 1679c1667dbSdjm int i; 1689c1667dbSdjm 1699c1667dbSdjm for (i = 0; keyimpls[i] != NULL; i++) { 1709c1667dbSdjm if (keyimpls[i]->type == type && 1719c1667dbSdjm (keyimpls[i]->nid == 0 || keyimpls[i]->nid == nid)) 1729c1667dbSdjm return keyimpls[i]; 1739c1667dbSdjm } 1749c1667dbSdjm return NULL; 1759c1667dbSdjm } 1769c1667dbSdjm 177a53002e7Sdjm static const struct sshkey_impl * 178a53002e7Sdjm sshkey_impl_from_key(const struct sshkey *k) 179a53002e7Sdjm { 180a53002e7Sdjm if (k == NULL) 181a53002e7Sdjm return NULL; 182a53002e7Sdjm return sshkey_impl_from_type_nid(k->type, k->ecdsa_nid); 183a53002e7Sdjm } 184a53002e7Sdjm 185811ca2d4Sdjm const char * 186811ca2d4Sdjm sshkey_type(const struct sshkey *k) 187811ca2d4Sdjm { 1889c1667dbSdjm const struct sshkey_impl *impl; 189811ca2d4Sdjm 190a53002e7Sdjm if ((impl = sshkey_impl_from_key(k)) == NULL) 191811ca2d4Sdjm return "unknown"; 1929c1667dbSdjm return impl->shortname; 193811ca2d4Sdjm } 194811ca2d4Sdjm 195811ca2d4Sdjm static const char * 196811ca2d4Sdjm sshkey_ssh_name_from_type_nid(int type, int nid) 197811ca2d4Sdjm { 1989c1667dbSdjm const struct sshkey_impl *impl; 199811ca2d4Sdjm 2009c1667dbSdjm if ((impl = sshkey_impl_from_type_nid(type, nid)) == NULL) 201811ca2d4Sdjm return "ssh-unknown"; 2029c1667dbSdjm return impl->name; 203811ca2d4Sdjm } 204811ca2d4Sdjm 205811ca2d4Sdjm int 206811ca2d4Sdjm sshkey_type_is_cert(int type) 207811ca2d4Sdjm { 2089c1667dbSdjm const struct sshkey_impl *impl; 209811ca2d4Sdjm 2109c1667dbSdjm if ((impl = sshkey_impl_from_type(type)) == NULL) 211811ca2d4Sdjm return 0; 2129c1667dbSdjm return impl->cert; 213811ca2d4Sdjm } 214811ca2d4Sdjm 215811ca2d4Sdjm const char * 216811ca2d4Sdjm sshkey_ssh_name(const struct sshkey *k) 217811ca2d4Sdjm { 218811ca2d4Sdjm return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid); 219811ca2d4Sdjm } 220811ca2d4Sdjm 221811ca2d4Sdjm const char * 222811ca2d4Sdjm sshkey_ssh_name_plain(const struct sshkey *k) 223811ca2d4Sdjm { 224811ca2d4Sdjm return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type), 225811ca2d4Sdjm k->ecdsa_nid); 226811ca2d4Sdjm } 227811ca2d4Sdjm 228bf801ff5Sdjm static int 229bf801ff5Sdjm type_from_name(const char *name, int allow_short) 230811ca2d4Sdjm { 2319c1667dbSdjm int i; 2329c1667dbSdjm const struct sshkey_impl *impl; 233811ca2d4Sdjm 2349c1667dbSdjm for (i = 0; keyimpls[i] != NULL; i++) { 2359c1667dbSdjm impl = keyimpls[i]; 236bf801ff5Sdjm if (impl->name != NULL && strcmp(name, impl->name) == 0) 237bf801ff5Sdjm return impl->type; 238811ca2d4Sdjm /* Only allow shortname matches for plain key types */ 239bf801ff5Sdjm if (allow_short && !impl->cert && impl->shortname != NULL && 240bf801ff5Sdjm strcasecmp(impl->shortname, name) == 0) 2419c1667dbSdjm return impl->type; 242811ca2d4Sdjm } 243811ca2d4Sdjm return KEY_UNSPEC; 244811ca2d4Sdjm } 245811ca2d4Sdjm 246bf801ff5Sdjm int 247bf801ff5Sdjm sshkey_type_from_name(const char *name) 248bf801ff5Sdjm { 249bf801ff5Sdjm return type_from_name(name, 0); 250bf801ff5Sdjm } 251bf801ff5Sdjm 252bf801ff5Sdjm int 253bf801ff5Sdjm sshkey_type_from_shortname(const char *name) 254bf801ff5Sdjm { 255bf801ff5Sdjm return type_from_name(name, 1); 256bf801ff5Sdjm } 257bf801ff5Sdjm 2584f5eb3ebSdjm static int 2594f5eb3ebSdjm key_type_is_ecdsa_variant(int type) 2604f5eb3ebSdjm { 2614f5eb3ebSdjm switch (type) { 2624f5eb3ebSdjm case KEY_ECDSA: 2634f5eb3ebSdjm case KEY_ECDSA_CERT: 2644f5eb3ebSdjm case KEY_ECDSA_SK: 2654f5eb3ebSdjm case KEY_ECDSA_SK_CERT: 2664f5eb3ebSdjm return 1; 2674f5eb3ebSdjm } 2684f5eb3ebSdjm return 0; 2694f5eb3ebSdjm } 2704f5eb3ebSdjm 271811ca2d4Sdjm int 272811ca2d4Sdjm sshkey_ecdsa_nid_from_name(const char *name) 273811ca2d4Sdjm { 2749c1667dbSdjm int i; 275811ca2d4Sdjm 2769c1667dbSdjm for (i = 0; keyimpls[i] != NULL; i++) { 2779c1667dbSdjm if (!key_type_is_ecdsa_variant(keyimpls[i]->type)) 278811ca2d4Sdjm continue; 2799c1667dbSdjm if (keyimpls[i]->name != NULL && 2809c1667dbSdjm strcmp(name, keyimpls[i]->name) == 0) 2819c1667dbSdjm return keyimpls[i]->nid; 282811ca2d4Sdjm } 283811ca2d4Sdjm return -1; 284811ca2d4Sdjm } 285811ca2d4Sdjm 286661a795cSdjm int 287661a795cSdjm sshkey_match_keyname_to_sigalgs(const char *keyname, const char *sigalgs) 288661a795cSdjm { 289661a795cSdjm int ktype; 290661a795cSdjm 291661a795cSdjm if (sigalgs == NULL || *sigalgs == '\0' || 292661a795cSdjm (ktype = sshkey_type_from_name(keyname)) == KEY_UNSPEC) 293661a795cSdjm return 0; 294661a795cSdjm else if (ktype == KEY_RSA) { 295661a795cSdjm return match_pattern_list("ssh-rsa", sigalgs, 0) == 1 || 296661a795cSdjm match_pattern_list("rsa-sha2-256", sigalgs, 0) == 1 || 297661a795cSdjm match_pattern_list("rsa-sha2-512", sigalgs, 0) == 1; 298661a795cSdjm } else if (ktype == KEY_RSA_CERT) { 299661a795cSdjm return match_pattern_list("ssh-rsa-cert-v01@openssh.com", 300661a795cSdjm sigalgs, 0) == 1 || 301661a795cSdjm match_pattern_list("rsa-sha2-256-cert-v01@openssh.com", 302661a795cSdjm sigalgs, 0) == 1 || 303661a795cSdjm match_pattern_list("rsa-sha2-512-cert-v01@openssh.com", 304661a795cSdjm sigalgs, 0) == 1; 305661a795cSdjm } else 306661a795cSdjm return match_pattern_list(keyname, sigalgs, 0) == 1; 307661a795cSdjm } 308661a795cSdjm 309811ca2d4Sdjm char * 31046f45969Sdjm sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep) 311811ca2d4Sdjm { 312811ca2d4Sdjm char *tmp, *ret = NULL; 3139c1667dbSdjm size_t i, nlen, rlen = 0; 3149c1667dbSdjm const struct sshkey_impl *impl; 315811ca2d4Sdjm 3169c1667dbSdjm for (i = 0; keyimpls[i] != NULL; i++) { 3179c1667dbSdjm impl = keyimpls[i]; 3189c1667dbSdjm if (impl->name == NULL) 31946f45969Sdjm continue; 3209c1667dbSdjm if (!include_sigonly && impl->sigonly) 321811ca2d4Sdjm continue; 3229c1667dbSdjm if ((certs_only && !impl->cert) || (plain_only && impl->cert)) 323811ca2d4Sdjm continue; 324811ca2d4Sdjm if (ret != NULL) 32581a19b16Sdjm ret[rlen++] = sep; 3269c1667dbSdjm nlen = strlen(impl->name); 327811ca2d4Sdjm if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { 328811ca2d4Sdjm free(ret); 329811ca2d4Sdjm return NULL; 330811ca2d4Sdjm } 331811ca2d4Sdjm ret = tmp; 3329c1667dbSdjm memcpy(ret + rlen, impl->name, nlen + 1); 333811ca2d4Sdjm rlen += nlen; 334811ca2d4Sdjm } 335811ca2d4Sdjm return ret; 336811ca2d4Sdjm } 337811ca2d4Sdjm 338811ca2d4Sdjm int 339fd6d8a57Sdjm sshkey_names_valid2(const char *names, int allow_wildcard, int plain_only) 340811ca2d4Sdjm { 341811ca2d4Sdjm char *s, *cp, *p; 3429c1667dbSdjm const struct sshkey_impl *impl; 3439c1667dbSdjm int i, type; 344811ca2d4Sdjm 345811ca2d4Sdjm if (names == NULL || strcmp(names, "") == 0) 346811ca2d4Sdjm return 0; 347811ca2d4Sdjm if ((s = cp = strdup(names)) == NULL) 348811ca2d4Sdjm return 0; 349811ca2d4Sdjm for ((p = strsep(&cp, ",")); p && *p != '\0'; 350811ca2d4Sdjm (p = strsep(&cp, ","))) { 351dc9cac76Sdjm type = sshkey_type_from_name(p); 352dc9cac76Sdjm if (type == KEY_UNSPEC) { 353dc9cac76Sdjm if (allow_wildcard) { 354dc9cac76Sdjm /* 355dc9cac76Sdjm * Try matching key types against the string. 356dc9cac76Sdjm * If any has a positive or negative match then 357dc9cac76Sdjm * the component is accepted. 358dc9cac76Sdjm */ 3599c1667dbSdjm impl = NULL; 3609c1667dbSdjm for (i = 0; keyimpls[i] != NULL; i++) { 3619c1667dbSdjm if (match_pattern_list( 3629c1667dbSdjm keyimpls[i]->name, p, 0) != 0) { 3639c1667dbSdjm impl = keyimpls[i]; 364dc9cac76Sdjm break; 365dc9cac76Sdjm } 3669c1667dbSdjm } 3679c1667dbSdjm if (impl != NULL) 368dc9cac76Sdjm continue; 369dc9cac76Sdjm } 370811ca2d4Sdjm free(s); 371811ca2d4Sdjm return 0; 372fd6d8a57Sdjm } else if (plain_only && sshkey_type_is_cert(type)) { 373fd6d8a57Sdjm free(s); 374fd6d8a57Sdjm return 0; 375811ca2d4Sdjm } 376811ca2d4Sdjm } 377811ca2d4Sdjm free(s); 378811ca2d4Sdjm return 1; 379811ca2d4Sdjm } 380811ca2d4Sdjm 381811ca2d4Sdjm u_int 382811ca2d4Sdjm sshkey_size(const struct sshkey *k) 383811ca2d4Sdjm { 3849c1667dbSdjm const struct sshkey_impl *impl; 3857c94020aSdjm 386a53002e7Sdjm if ((impl = sshkey_impl_from_key(k)) == NULL) 3877c94020aSdjm return 0; 3889c1667dbSdjm if (impl->funcs->size != NULL) 3899c1667dbSdjm return impl->funcs->size(k); 3909c1667dbSdjm return impl->keybits; 391811ca2d4Sdjm } 392811ca2d4Sdjm 393811ca2d4Sdjm static int 394811ca2d4Sdjm sshkey_type_is_valid_ca(int type) 395811ca2d4Sdjm { 3969c1667dbSdjm const struct sshkey_impl *impl; 3979c1667dbSdjm 3989c1667dbSdjm if ((impl = sshkey_impl_from_type(type)) == NULL) 399811ca2d4Sdjm return 0; 4009c1667dbSdjm /* All non-certificate types may act as CAs */ 4019c1667dbSdjm return !impl->cert; 402811ca2d4Sdjm } 403811ca2d4Sdjm 404811ca2d4Sdjm int 405811ca2d4Sdjm sshkey_is_cert(const struct sshkey *k) 406811ca2d4Sdjm { 407811ca2d4Sdjm if (k == NULL) 408811ca2d4Sdjm return 0; 409811ca2d4Sdjm return sshkey_type_is_cert(k->type); 410811ca2d4Sdjm } 411811ca2d4Sdjm 41227c8f7c6Smarkus int 41327c8f7c6Smarkus sshkey_is_sk(const struct sshkey *k) 41427c8f7c6Smarkus { 41527c8f7c6Smarkus if (k == NULL) 41627c8f7c6Smarkus return 0; 41727c8f7c6Smarkus switch (sshkey_type_plain(k->type)) { 41827c8f7c6Smarkus case KEY_ECDSA_SK: 41927c8f7c6Smarkus case KEY_ED25519_SK: 42027c8f7c6Smarkus return 1; 42127c8f7c6Smarkus default: 42227c8f7c6Smarkus return 0; 42327c8f7c6Smarkus } 42427c8f7c6Smarkus } 42527c8f7c6Smarkus 426811ca2d4Sdjm /* Return the cert-less equivalent to a certified key type */ 427811ca2d4Sdjm int 428811ca2d4Sdjm sshkey_type_plain(int type) 429811ca2d4Sdjm { 430811ca2d4Sdjm switch (type) { 431811ca2d4Sdjm case KEY_RSA_CERT: 432811ca2d4Sdjm return KEY_RSA; 433811ca2d4Sdjm case KEY_DSA_CERT: 434811ca2d4Sdjm return KEY_DSA; 435811ca2d4Sdjm case KEY_ECDSA_CERT: 436811ca2d4Sdjm return KEY_ECDSA; 4374f5eb3ebSdjm case KEY_ECDSA_SK_CERT: 4384f5eb3ebSdjm return KEY_ECDSA_SK; 439811ca2d4Sdjm case KEY_ED25519_CERT: 440811ca2d4Sdjm return KEY_ED25519; 44127c8f7c6Smarkus case KEY_ED25519_SK_CERT: 44227c8f7c6Smarkus return KEY_ED25519_SK; 443a6be8e7cSmarkus case KEY_XMSS_CERT: 444a6be8e7cSmarkus return KEY_XMSS; 445811ca2d4Sdjm default: 446811ca2d4Sdjm return type; 447811ca2d4Sdjm } 448811ca2d4Sdjm } 449811ca2d4Sdjm 45087f32bb5Sdjm /* Return the cert equivalent to a plain key type */ 45187f32bb5Sdjm static int 45287f32bb5Sdjm sshkey_type_certified(int type) 45387f32bb5Sdjm { 45487f32bb5Sdjm switch (type) { 45587f32bb5Sdjm case KEY_RSA: 45687f32bb5Sdjm return KEY_RSA_CERT; 45787f32bb5Sdjm case KEY_DSA: 45887f32bb5Sdjm return KEY_DSA_CERT; 45987f32bb5Sdjm case KEY_ECDSA: 46087f32bb5Sdjm return KEY_ECDSA_CERT; 46187f32bb5Sdjm case KEY_ECDSA_SK: 46287f32bb5Sdjm return KEY_ECDSA_SK_CERT; 46387f32bb5Sdjm case KEY_ED25519: 46487f32bb5Sdjm return KEY_ED25519_CERT; 46587f32bb5Sdjm case KEY_ED25519_SK: 46687f32bb5Sdjm return KEY_ED25519_SK_CERT; 46787f32bb5Sdjm case KEY_XMSS: 46887f32bb5Sdjm return KEY_XMSS_CERT; 46987f32bb5Sdjm default: 47087f32bb5Sdjm return -1; 47187f32bb5Sdjm } 47287f32bb5Sdjm } 47387f32bb5Sdjm 474811ca2d4Sdjm #ifdef WITH_OPENSSL 4755411e769Sdjm static const EVP_MD * 4765411e769Sdjm ssh_digest_to_md(int hash_alg) 4775411e769Sdjm { 4785411e769Sdjm switch (hash_alg) { 4795411e769Sdjm case SSH_DIGEST_SHA1: 4805411e769Sdjm return EVP_sha1(); 4815411e769Sdjm case SSH_DIGEST_SHA256: 4825411e769Sdjm return EVP_sha256(); 4835411e769Sdjm case SSH_DIGEST_SHA384: 4845411e769Sdjm return EVP_sha384(); 4855411e769Sdjm case SSH_DIGEST_SHA512: 4865411e769Sdjm return EVP_sha512(); 4875411e769Sdjm } 4885411e769Sdjm return NULL; 4895411e769Sdjm } 4905411e769Sdjm 4915411e769Sdjm int 4925411e769Sdjm sshkey_pkey_digest_sign(EVP_PKEY *pkey, int hash_alg, u_char **sigp, 4935411e769Sdjm size_t *lenp, const u_char *data, size_t datalen) 4945411e769Sdjm { 4955411e769Sdjm EVP_MD_CTX *ctx = NULL; 4965411e769Sdjm u_char *sig = NULL; 4975411e769Sdjm int ret; 4985411e769Sdjm size_t slen; 4995411e769Sdjm const EVP_MD *evpmd; 5005411e769Sdjm 5015411e769Sdjm *sigp = NULL; 5025411e769Sdjm *lenp = 0; 5035411e769Sdjm 5045411e769Sdjm slen = EVP_PKEY_size(pkey); 5055411e769Sdjm if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM || 5065411e769Sdjm (evpmd = ssh_digest_to_md(hash_alg)) == NULL) 5075411e769Sdjm return SSH_ERR_INVALID_ARGUMENT; 5085411e769Sdjm 5095411e769Sdjm if ((sig = malloc(slen)) == NULL) 5105411e769Sdjm return SSH_ERR_ALLOC_FAIL; 5115411e769Sdjm 5125411e769Sdjm if ((ctx = EVP_MD_CTX_new()) == NULL) { 5135411e769Sdjm ret = SSH_ERR_ALLOC_FAIL; 5145411e769Sdjm goto out; 5155411e769Sdjm } 5165411e769Sdjm if (EVP_DigestSignInit(ctx, NULL, evpmd, NULL, pkey) != 1 || 5175411e769Sdjm EVP_DigestSign(ctx, sig, &slen, data, datalen) != 1) { 5185411e769Sdjm ret = SSH_ERR_LIBCRYPTO_ERROR; 5195411e769Sdjm goto out; 5205411e769Sdjm } 5215411e769Sdjm 5225411e769Sdjm *sigp = sig; 5235411e769Sdjm *lenp = slen; 5245411e769Sdjm /* Now owned by the caller */ 5255411e769Sdjm sig = NULL; 5265411e769Sdjm ret = 0; 5275411e769Sdjm 5285411e769Sdjm out: 5295411e769Sdjm EVP_MD_CTX_free(ctx); 5305411e769Sdjm free(sig); 5315411e769Sdjm return ret; 5325411e769Sdjm } 5335411e769Sdjm 5345411e769Sdjm int 5355411e769Sdjm sshkey_pkey_digest_verify(EVP_PKEY *pkey, int hash_alg, const u_char *data, 5365411e769Sdjm size_t datalen, u_char *sigbuf, size_t siglen) 5375411e769Sdjm { 5385411e769Sdjm EVP_MD_CTX *ctx = NULL; 5395411e769Sdjm int ret = SSH_ERR_INTERNAL_ERROR; 5405411e769Sdjm const EVP_MD *evpmd; 5415411e769Sdjm 5425411e769Sdjm if ((evpmd = ssh_digest_to_md(hash_alg)) == NULL) 5435411e769Sdjm return SSH_ERR_INVALID_ARGUMENT; 5445411e769Sdjm if ((ctx = EVP_MD_CTX_new()) == NULL) 5455411e769Sdjm return SSH_ERR_ALLOC_FAIL; 5465411e769Sdjm if (EVP_DigestVerifyInit(ctx, NULL, evpmd, NULL, pkey) != 1) { 5475411e769Sdjm ret = SSH_ERR_LIBCRYPTO_ERROR; 5485411e769Sdjm goto out; 5495411e769Sdjm } 5505411e769Sdjm switch (EVP_DigestVerify(ctx, sigbuf, siglen, data, datalen)) { 5515411e769Sdjm case 1: 5525411e769Sdjm ret = 0; 5535411e769Sdjm break; 5545411e769Sdjm case 0: 5555411e769Sdjm ret = SSH_ERR_SIGNATURE_INVALID; 5565411e769Sdjm break; 5575411e769Sdjm default: 5585411e769Sdjm ret = SSH_ERR_LIBCRYPTO_ERROR; 5595411e769Sdjm break; 5605411e769Sdjm } 5615411e769Sdjm 5625411e769Sdjm out: 5635411e769Sdjm EVP_MD_CTX_free(ctx); 5645411e769Sdjm return ret; 5655411e769Sdjm } 5665411e769Sdjm 567811ca2d4Sdjm /* XXX: these are really begging for a table-driven approach */ 568811ca2d4Sdjm int 569811ca2d4Sdjm sshkey_curve_name_to_nid(const char *name) 570811ca2d4Sdjm { 571811ca2d4Sdjm if (strcmp(name, "nistp256") == 0) 572811ca2d4Sdjm return NID_X9_62_prime256v1; 573811ca2d4Sdjm else if (strcmp(name, "nistp384") == 0) 574811ca2d4Sdjm return NID_secp384r1; 575811ca2d4Sdjm else if (strcmp(name, "nistp521") == 0) 576811ca2d4Sdjm return NID_secp521r1; 577811ca2d4Sdjm else 578811ca2d4Sdjm return -1; 579811ca2d4Sdjm } 580811ca2d4Sdjm 581811ca2d4Sdjm u_int 582811ca2d4Sdjm sshkey_curve_nid_to_bits(int nid) 583811ca2d4Sdjm { 584811ca2d4Sdjm switch (nid) { 585811ca2d4Sdjm case NID_X9_62_prime256v1: 586811ca2d4Sdjm return 256; 587811ca2d4Sdjm case NID_secp384r1: 588811ca2d4Sdjm return 384; 589811ca2d4Sdjm case NID_secp521r1: 590811ca2d4Sdjm return 521; 591811ca2d4Sdjm default: 592811ca2d4Sdjm return 0; 593811ca2d4Sdjm } 594811ca2d4Sdjm } 595811ca2d4Sdjm 596811ca2d4Sdjm int 597811ca2d4Sdjm sshkey_ecdsa_bits_to_nid(int bits) 598811ca2d4Sdjm { 599811ca2d4Sdjm switch (bits) { 600811ca2d4Sdjm case 256: 601811ca2d4Sdjm return NID_X9_62_prime256v1; 602811ca2d4Sdjm case 384: 603811ca2d4Sdjm return NID_secp384r1; 604811ca2d4Sdjm case 521: 605811ca2d4Sdjm return NID_secp521r1; 606811ca2d4Sdjm default: 607811ca2d4Sdjm return -1; 608811ca2d4Sdjm } 609811ca2d4Sdjm } 610811ca2d4Sdjm 611811ca2d4Sdjm const char * 612811ca2d4Sdjm sshkey_curve_nid_to_name(int nid) 613811ca2d4Sdjm { 614811ca2d4Sdjm switch (nid) { 615811ca2d4Sdjm case NID_X9_62_prime256v1: 616811ca2d4Sdjm return "nistp256"; 617811ca2d4Sdjm case NID_secp384r1: 618811ca2d4Sdjm return "nistp384"; 619811ca2d4Sdjm case NID_secp521r1: 620811ca2d4Sdjm return "nistp521"; 621811ca2d4Sdjm default: 622811ca2d4Sdjm return NULL; 623811ca2d4Sdjm } 624811ca2d4Sdjm } 625811ca2d4Sdjm 626811ca2d4Sdjm int 627811ca2d4Sdjm sshkey_ec_nid_to_hash_alg(int nid) 628811ca2d4Sdjm { 629811ca2d4Sdjm int kbits = sshkey_curve_nid_to_bits(nid); 630811ca2d4Sdjm 631811ca2d4Sdjm if (kbits <= 0) 632811ca2d4Sdjm return -1; 633811ca2d4Sdjm 634811ca2d4Sdjm /* RFC5656 section 6.2.1 */ 635811ca2d4Sdjm if (kbits <= 256) 636811ca2d4Sdjm return SSH_DIGEST_SHA256; 637811ca2d4Sdjm else if (kbits <= 384) 638811ca2d4Sdjm return SSH_DIGEST_SHA384; 639811ca2d4Sdjm else 640811ca2d4Sdjm return SSH_DIGEST_SHA512; 641811ca2d4Sdjm } 642811ca2d4Sdjm #endif /* WITH_OPENSSL */ 643811ca2d4Sdjm 644811ca2d4Sdjm static void 645811ca2d4Sdjm cert_free(struct sshkey_cert *cert) 646811ca2d4Sdjm { 647811ca2d4Sdjm u_int i; 648811ca2d4Sdjm 649811ca2d4Sdjm if (cert == NULL) 650811ca2d4Sdjm return; 651811ca2d4Sdjm sshbuf_free(cert->certblob); 652811ca2d4Sdjm sshbuf_free(cert->critical); 653811ca2d4Sdjm sshbuf_free(cert->extensions); 654811ca2d4Sdjm free(cert->key_id); 655811ca2d4Sdjm for (i = 0; i < cert->nprincipals; i++) 656811ca2d4Sdjm free(cert->principals[i]); 657811ca2d4Sdjm free(cert->principals); 658811ca2d4Sdjm sshkey_free(cert->signature_key); 6590ff10372Sdjm free(cert->signature_type); 6602e320434Sjsing freezero(cert, sizeof(*cert)); 661811ca2d4Sdjm } 662811ca2d4Sdjm 663811ca2d4Sdjm static struct sshkey_cert * 664811ca2d4Sdjm cert_new(void) 665811ca2d4Sdjm { 666811ca2d4Sdjm struct sshkey_cert *cert; 667811ca2d4Sdjm 668811ca2d4Sdjm if ((cert = calloc(1, sizeof(*cert))) == NULL) 669811ca2d4Sdjm return NULL; 670811ca2d4Sdjm if ((cert->certblob = sshbuf_new()) == NULL || 671811ca2d4Sdjm (cert->critical = sshbuf_new()) == NULL || 672811ca2d4Sdjm (cert->extensions = sshbuf_new()) == NULL) { 673811ca2d4Sdjm cert_free(cert); 674811ca2d4Sdjm return NULL; 675811ca2d4Sdjm } 676811ca2d4Sdjm cert->key_id = NULL; 677811ca2d4Sdjm cert->principals = NULL; 678811ca2d4Sdjm cert->signature_key = NULL; 6790ff10372Sdjm cert->signature_type = NULL; 680811ca2d4Sdjm return cert; 681811ca2d4Sdjm } 682811ca2d4Sdjm 683811ca2d4Sdjm struct sshkey * 684811ca2d4Sdjm sshkey_new(int type) 685811ca2d4Sdjm { 686811ca2d4Sdjm struct sshkey *k; 6879c1667dbSdjm const struct sshkey_impl *impl = NULL; 688811ca2d4Sdjm 6899c1667dbSdjm if (type != KEY_UNSPEC && 6909c1667dbSdjm (impl = sshkey_impl_from_type(type)) == NULL) 6919c1667dbSdjm return NULL; 6929c1667dbSdjm 6939c1667dbSdjm /* All non-certificate types may act as CAs */ 694811ca2d4Sdjm if ((k = calloc(1, sizeof(*k))) == NULL) 695811ca2d4Sdjm return NULL; 696811ca2d4Sdjm k->type = type; 697811ca2d4Sdjm k->ecdsa_nid = -1; 6989c1667dbSdjm if (impl != NULL && impl->funcs->alloc != NULL) { 6999c1667dbSdjm if (impl->funcs->alloc(k) != 0) { 700811ca2d4Sdjm free(k); 701811ca2d4Sdjm return NULL; 702811ca2d4Sdjm } 703811ca2d4Sdjm } 704811ca2d4Sdjm if (sshkey_is_cert(k)) { 705811ca2d4Sdjm if ((k->cert = cert_new()) == NULL) { 706811ca2d4Sdjm sshkey_free(k); 707811ca2d4Sdjm return NULL; 708811ca2d4Sdjm } 709811ca2d4Sdjm } 710811ca2d4Sdjm 711811ca2d4Sdjm return k; 712811ca2d4Sdjm } 713811ca2d4Sdjm 714712f5ecfSdjm /* Frees common FIDO fields */ 715712f5ecfSdjm void 716712f5ecfSdjm sshkey_sk_cleanup(struct sshkey *k) 717712f5ecfSdjm { 718712f5ecfSdjm free(k->sk_application); 719712f5ecfSdjm sshbuf_free(k->sk_key_handle); 720712f5ecfSdjm sshbuf_free(k->sk_reserved); 721712f5ecfSdjm k->sk_application = NULL; 722712f5ecfSdjm k->sk_key_handle = k->sk_reserved = NULL; 723712f5ecfSdjm } 724712f5ecfSdjm 725502d8771Sdjm static int 726502d8771Sdjm sshkey_prekey_alloc(u_char **prekeyp, size_t len) 727502d8771Sdjm { 728502d8771Sdjm u_char *prekey; 729502d8771Sdjm 730502d8771Sdjm *prekeyp = NULL; 731a45ce29eSdjm if ((prekey = mmap(NULL, len, PROT_READ|PROT_WRITE, 732502d8771Sdjm MAP_ANON|MAP_PRIVATE|MAP_CONCEAL, -1, 0)) == MAP_FAILED) 733502d8771Sdjm return SSH_ERR_SYSTEM_ERROR; 734502d8771Sdjm *prekeyp = prekey; 735502d8771Sdjm return 0; 736502d8771Sdjm } 737502d8771Sdjm 738502d8771Sdjm static void 739502d8771Sdjm sshkey_prekey_free(void *prekey, size_t len) 740502d8771Sdjm { 741502d8771Sdjm if (prekey == NULL) 742502d8771Sdjm return; 743502d8771Sdjm munmap(prekey, len); 744502d8771Sdjm } 745502d8771Sdjm 746a53002e7Sdjm static void 747a53002e7Sdjm sshkey_free_contents(struct sshkey *k) 748811ca2d4Sdjm { 7499c1667dbSdjm const struct sshkey_impl *impl; 7509c1667dbSdjm 751811ca2d4Sdjm if (k == NULL) 752811ca2d4Sdjm return; 7539c1667dbSdjm if ((impl = sshkey_impl_from_type(k->type)) != NULL && 7549c1667dbSdjm impl->funcs->cleanup != NULL) 7559c1667dbSdjm impl->funcs->cleanup(k); 756811ca2d4Sdjm if (sshkey_is_cert(k)) 757811ca2d4Sdjm cert_free(k->cert); 758707316f9Sdjm freezero(k->shielded_private, k->shielded_len); 759502d8771Sdjm sshkey_prekey_free(k->shield_prekey, k->shield_prekey_len); 760a53002e7Sdjm } 761a53002e7Sdjm 762a53002e7Sdjm void 763a53002e7Sdjm sshkey_free(struct sshkey *k) 764a53002e7Sdjm { 765a53002e7Sdjm sshkey_free_contents(k); 7662e320434Sjsing freezero(k, sizeof(*k)); 767811ca2d4Sdjm } 768811ca2d4Sdjm 769811ca2d4Sdjm static int 770811ca2d4Sdjm cert_compare(struct sshkey_cert *a, struct sshkey_cert *b) 771811ca2d4Sdjm { 772811ca2d4Sdjm if (a == NULL && b == NULL) 773811ca2d4Sdjm return 1; 774811ca2d4Sdjm if (a == NULL || b == NULL) 775811ca2d4Sdjm return 0; 776811ca2d4Sdjm if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob)) 777811ca2d4Sdjm return 0; 778811ca2d4Sdjm if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob), 779811ca2d4Sdjm sshbuf_len(a->certblob)) != 0) 780811ca2d4Sdjm return 0; 781811ca2d4Sdjm return 1; 782811ca2d4Sdjm } 783811ca2d4Sdjm 784712f5ecfSdjm /* Compares FIDO-specific pubkey fields only */ 785712f5ecfSdjm int 786712f5ecfSdjm sshkey_sk_fields_equal(const struct sshkey *a, const struct sshkey *b) 787712f5ecfSdjm { 788712f5ecfSdjm if (a->sk_application == NULL || b->sk_application == NULL) 789712f5ecfSdjm return 0; 790712f5ecfSdjm if (strcmp(a->sk_application, b->sk_application) != 0) 791712f5ecfSdjm return 0; 792712f5ecfSdjm return 1; 793712f5ecfSdjm } 794712f5ecfSdjm 795811ca2d4Sdjm /* 796811ca2d4Sdjm * Compare public portions of key only, allowing comparisons between 797811ca2d4Sdjm * certificates and plain keys too. 798811ca2d4Sdjm */ 799811ca2d4Sdjm int 800811ca2d4Sdjm sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) 801811ca2d4Sdjm { 802712f5ecfSdjm const struct sshkey_impl *impl; 803811ca2d4Sdjm 804811ca2d4Sdjm if (a == NULL || b == NULL || 805811ca2d4Sdjm sshkey_type_plain(a->type) != sshkey_type_plain(b->type)) 806811ca2d4Sdjm return 0; 807712f5ecfSdjm if ((impl = sshkey_impl_from_type(a->type)) == NULL) 8087c94020aSdjm return 0; 809712f5ecfSdjm return impl->funcs->equal(a, b); 810811ca2d4Sdjm } 811811ca2d4Sdjm 812811ca2d4Sdjm int 813811ca2d4Sdjm sshkey_equal(const struct sshkey *a, const struct sshkey *b) 814811ca2d4Sdjm { 815811ca2d4Sdjm if (a == NULL || b == NULL || a->type != b->type) 816811ca2d4Sdjm return 0; 817811ca2d4Sdjm if (sshkey_is_cert(a)) { 818811ca2d4Sdjm if (!cert_compare(a->cert, b->cert)) 819811ca2d4Sdjm return 0; 820811ca2d4Sdjm } 821811ca2d4Sdjm return sshkey_equal_public(a, b); 822811ca2d4Sdjm } 823811ca2d4Sdjm 824eefcf659Sdjm 825eefcf659Sdjm /* Serialise common FIDO key parts */ 826eefcf659Sdjm int 827eefcf659Sdjm sshkey_serialize_sk(const struct sshkey *key, struct sshbuf *b) 828eefcf659Sdjm { 829eefcf659Sdjm int r; 830eefcf659Sdjm 831eefcf659Sdjm if ((r = sshbuf_put_cstring(b, key->sk_application)) != 0) 832eefcf659Sdjm return r; 833eefcf659Sdjm 834eefcf659Sdjm return 0; 835eefcf659Sdjm } 836eefcf659Sdjm 837811ca2d4Sdjm static int 838a6be8e7cSmarkus to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain, 839a6be8e7cSmarkus enum sshkey_serialize_rep opts) 840811ca2d4Sdjm { 841811ca2d4Sdjm int type, ret = SSH_ERR_INTERNAL_ERROR; 842811ca2d4Sdjm const char *typename; 843eefcf659Sdjm const struct sshkey_impl *impl; 844811ca2d4Sdjm 845811ca2d4Sdjm if (key == NULL) 846811ca2d4Sdjm return SSH_ERR_INVALID_ARGUMENT; 847811ca2d4Sdjm 848eefcf659Sdjm type = force_plain ? sshkey_type_plain(key->type) : key->type; 849eefcf659Sdjm 850eefcf659Sdjm if (sshkey_type_is_cert(type)) { 8513f5e6877Sdjm if (key->cert == NULL) 8523f5e6877Sdjm return SSH_ERR_EXPECTED_CERT; 8533f5e6877Sdjm if (sshbuf_len(key->cert->certblob) == 0) 8543f5e6877Sdjm return SSH_ERR_KEY_LACKS_CERTBLOB; 855811ca2d4Sdjm /* Use the existing blob */ 856811ca2d4Sdjm if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0) 857811ca2d4Sdjm return ret; 858811ca2d4Sdjm return 0; 859811ca2d4Sdjm } 860eefcf659Sdjm if ((impl = sshkey_impl_from_type(type)) == NULL) 861eefcf659Sdjm return SSH_ERR_KEY_TYPE_UNKNOWN; 862eefcf659Sdjm 863eefcf659Sdjm typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid); 864c8d92406Sdjm if ((ret = sshbuf_put_cstring(b, typename)) != 0) 865c8d92406Sdjm return ret; 866c8d92406Sdjm return impl->funcs->serialize_public(key, b, opts); 867eefcf659Sdjm } 868811ca2d4Sdjm 869811ca2d4Sdjm int 8704736d833Sdjm sshkey_putb(const struct sshkey *key, struct sshbuf *b) 871811ca2d4Sdjm { 872a6be8e7cSmarkus return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT); 873811ca2d4Sdjm } 874811ca2d4Sdjm 875811ca2d4Sdjm int 876a6be8e7cSmarkus sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b, 877a6be8e7cSmarkus enum sshkey_serialize_rep opts) 8784736d833Sdjm { 8794736d833Sdjm struct sshbuf *tmp; 8804736d833Sdjm int r; 8814736d833Sdjm 8824736d833Sdjm if ((tmp = sshbuf_new()) == NULL) 8834736d833Sdjm return SSH_ERR_ALLOC_FAIL; 884a6be8e7cSmarkus r = to_blob_buf(key, tmp, 0, opts); 8854736d833Sdjm if (r == 0) 8864736d833Sdjm r = sshbuf_put_stringb(b, tmp); 8874736d833Sdjm sshbuf_free(tmp); 8884736d833Sdjm return r; 8894736d833Sdjm } 8904736d833Sdjm 8914736d833Sdjm int 892a6be8e7cSmarkus sshkey_puts(const struct sshkey *key, struct sshbuf *b) 893a6be8e7cSmarkus { 894a6be8e7cSmarkus return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT); 895a6be8e7cSmarkus } 896a6be8e7cSmarkus 897a6be8e7cSmarkus int 8984736d833Sdjm sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b) 899811ca2d4Sdjm { 900a6be8e7cSmarkus return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT); 901811ca2d4Sdjm } 902811ca2d4Sdjm 903811ca2d4Sdjm static int 904a6be8e7cSmarkus to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain, 905a6be8e7cSmarkus enum sshkey_serialize_rep opts) 906811ca2d4Sdjm { 907811ca2d4Sdjm int ret = SSH_ERR_INTERNAL_ERROR; 908811ca2d4Sdjm size_t len; 909811ca2d4Sdjm struct sshbuf *b = NULL; 910811ca2d4Sdjm 911811ca2d4Sdjm if (lenp != NULL) 912811ca2d4Sdjm *lenp = 0; 913811ca2d4Sdjm if (blobp != NULL) 914811ca2d4Sdjm *blobp = NULL; 915811ca2d4Sdjm if ((b = sshbuf_new()) == NULL) 916811ca2d4Sdjm return SSH_ERR_ALLOC_FAIL; 917a6be8e7cSmarkus if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0) 918811ca2d4Sdjm goto out; 919811ca2d4Sdjm len = sshbuf_len(b); 920811ca2d4Sdjm if (lenp != NULL) 921811ca2d4Sdjm *lenp = len; 922811ca2d4Sdjm if (blobp != NULL) { 923811ca2d4Sdjm if ((*blobp = malloc(len)) == NULL) { 924811ca2d4Sdjm ret = SSH_ERR_ALLOC_FAIL; 925811ca2d4Sdjm goto out; 926811ca2d4Sdjm } 927811ca2d4Sdjm memcpy(*blobp, sshbuf_ptr(b), len); 928811ca2d4Sdjm } 929811ca2d4Sdjm ret = 0; 930811ca2d4Sdjm out: 931811ca2d4Sdjm sshbuf_free(b); 932811ca2d4Sdjm return ret; 933811ca2d4Sdjm } 934811ca2d4Sdjm 935811ca2d4Sdjm int 936811ca2d4Sdjm sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) 937811ca2d4Sdjm { 938a6be8e7cSmarkus return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT); 939811ca2d4Sdjm } 940811ca2d4Sdjm 941811ca2d4Sdjm int 942811ca2d4Sdjm sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) 943811ca2d4Sdjm { 944a6be8e7cSmarkus return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT); 945811ca2d4Sdjm } 946811ca2d4Sdjm 947811ca2d4Sdjm int 9483dbedef4Sdjm sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg, 949811ca2d4Sdjm u_char **retp, size_t *lenp) 950811ca2d4Sdjm { 951811ca2d4Sdjm u_char *blob = NULL, *ret = NULL; 952811ca2d4Sdjm size_t blob_len = 0; 9533dbedef4Sdjm int r = SSH_ERR_INTERNAL_ERROR; 954811ca2d4Sdjm 955811ca2d4Sdjm if (retp != NULL) 956811ca2d4Sdjm *retp = NULL; 957811ca2d4Sdjm if (lenp != NULL) 958811ca2d4Sdjm *lenp = 0; 9593dbedef4Sdjm if (ssh_digest_bytes(dgst_alg) == 0) { 960811ca2d4Sdjm r = SSH_ERR_INVALID_ARGUMENT; 961811ca2d4Sdjm goto out; 962811ca2d4Sdjm } 963a6be8e7cSmarkus if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT)) 964a6be8e7cSmarkus != 0) 965811ca2d4Sdjm goto out; 966811ca2d4Sdjm if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) { 967811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 968811ca2d4Sdjm goto out; 969811ca2d4Sdjm } 9703dbedef4Sdjm if ((r = ssh_digest_memory(dgst_alg, blob, blob_len, 971811ca2d4Sdjm ret, SSH_DIGEST_MAX_LENGTH)) != 0) 972811ca2d4Sdjm goto out; 973811ca2d4Sdjm /* success */ 974811ca2d4Sdjm if (retp != NULL) { 975811ca2d4Sdjm *retp = ret; 976811ca2d4Sdjm ret = NULL; 977811ca2d4Sdjm } 978811ca2d4Sdjm if (lenp != NULL) 9793dbedef4Sdjm *lenp = ssh_digest_bytes(dgst_alg); 980811ca2d4Sdjm r = 0; 981811ca2d4Sdjm out: 982811ca2d4Sdjm free(ret); 983c9831b39Sjsg if (blob != NULL) 984c9831b39Sjsg freezero(blob, blob_len); 985811ca2d4Sdjm return r; 986811ca2d4Sdjm } 987811ca2d4Sdjm 988811ca2d4Sdjm static char * 9893dbedef4Sdjm fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) 990811ca2d4Sdjm { 9913dbedef4Sdjm char *ret; 9923dbedef4Sdjm size_t plen = strlen(alg) + 1; 9933dbedef4Sdjm size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1; 994811ca2d4Sdjm 9953dbedef4Sdjm if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL) 996811ca2d4Sdjm return NULL; 9973dbedef4Sdjm strlcpy(ret, alg, rlen); 9983dbedef4Sdjm strlcat(ret, ":", rlen); 9993dbedef4Sdjm if (dgst_raw_len == 0) 10003dbedef4Sdjm return ret; 10014db014ecSdtucker if (b64_ntop(dgst_raw, dgst_raw_len, ret + plen, rlen - plen) == -1) { 10022e320434Sjsing freezero(ret, rlen); 10033dbedef4Sdjm return NULL; 10043dbedef4Sdjm } 10053dbedef4Sdjm /* Trim padding characters from end */ 10063dbedef4Sdjm ret[strcspn(ret, "=")] = '\0'; 10073dbedef4Sdjm return ret; 1008811ca2d4Sdjm } 1009811ca2d4Sdjm 10103dbedef4Sdjm static char * 10113dbedef4Sdjm fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) 10123dbedef4Sdjm { 10133dbedef4Sdjm char *retval, hex[5]; 10143dbedef4Sdjm size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2; 10153dbedef4Sdjm 10163dbedef4Sdjm if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL) 10173dbedef4Sdjm return NULL; 10183dbedef4Sdjm strlcpy(retval, alg, rlen); 10193dbedef4Sdjm strlcat(retval, ":", rlen); 10203dbedef4Sdjm for (i = 0; i < dgst_raw_len; i++) { 10213dbedef4Sdjm snprintf(hex, sizeof(hex), "%s%02x", 10223dbedef4Sdjm i > 0 ? ":" : "", dgst_raw[i]); 10233dbedef4Sdjm strlcat(retval, hex, rlen); 10243dbedef4Sdjm } 1025811ca2d4Sdjm return retval; 1026811ca2d4Sdjm } 1027811ca2d4Sdjm 1028811ca2d4Sdjm static char * 1029811ca2d4Sdjm fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len) 1030811ca2d4Sdjm { 1031811ca2d4Sdjm char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; 1032811ca2d4Sdjm char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', 1033811ca2d4Sdjm 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' }; 1034811ca2d4Sdjm u_int i, j = 0, rounds, seed = 1; 1035811ca2d4Sdjm char *retval; 1036811ca2d4Sdjm 1037811ca2d4Sdjm rounds = (dgst_raw_len / 2) + 1; 1038811ca2d4Sdjm if ((retval = calloc(rounds, 6)) == NULL) 1039811ca2d4Sdjm return NULL; 1040811ca2d4Sdjm retval[j++] = 'x'; 1041811ca2d4Sdjm for (i = 0; i < rounds; i++) { 1042811ca2d4Sdjm u_int idx0, idx1, idx2, idx3, idx4; 1043811ca2d4Sdjm if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) { 1044811ca2d4Sdjm idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) + 1045811ca2d4Sdjm seed) % 6; 1046811ca2d4Sdjm idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15; 1047811ca2d4Sdjm idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) + 1048811ca2d4Sdjm (seed / 6)) % 6; 1049811ca2d4Sdjm retval[j++] = vowels[idx0]; 1050811ca2d4Sdjm retval[j++] = consonants[idx1]; 1051811ca2d4Sdjm retval[j++] = vowels[idx2]; 1052811ca2d4Sdjm if ((i + 1) < rounds) { 1053811ca2d4Sdjm idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15; 1054811ca2d4Sdjm idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15; 1055811ca2d4Sdjm retval[j++] = consonants[idx3]; 1056811ca2d4Sdjm retval[j++] = '-'; 1057811ca2d4Sdjm retval[j++] = consonants[idx4]; 1058811ca2d4Sdjm seed = ((seed * 5) + 1059811ca2d4Sdjm ((((u_int)(dgst_raw[2 * i])) * 7) + 1060811ca2d4Sdjm ((u_int)(dgst_raw[(2 * i) + 1])))) % 36; 1061811ca2d4Sdjm } 1062811ca2d4Sdjm } else { 1063811ca2d4Sdjm idx0 = seed % 6; 1064811ca2d4Sdjm idx1 = 16; 1065811ca2d4Sdjm idx2 = seed / 6; 1066811ca2d4Sdjm retval[j++] = vowels[idx0]; 1067811ca2d4Sdjm retval[j++] = consonants[idx1]; 1068811ca2d4Sdjm retval[j++] = vowels[idx2]; 1069811ca2d4Sdjm } 1070811ca2d4Sdjm } 1071811ca2d4Sdjm retval[j++] = 'x'; 1072811ca2d4Sdjm retval[j++] = '\0'; 1073811ca2d4Sdjm return retval; 1074811ca2d4Sdjm } 1075811ca2d4Sdjm 1076811ca2d4Sdjm /* 1077811ca2d4Sdjm * Draw an ASCII-Art representing the fingerprint so human brain can 1078811ca2d4Sdjm * profit from its built-in pattern recognition ability. 1079811ca2d4Sdjm * This technique is called "random art" and can be found in some 1080811ca2d4Sdjm * scientific publications like this original paper: 1081811ca2d4Sdjm * 1082811ca2d4Sdjm * "Hash Visualization: a New Technique to improve Real-World Security", 1083811ca2d4Sdjm * Perrig A. and Song D., 1999, International Workshop on Cryptographic 1084811ca2d4Sdjm * Techniques and E-Commerce (CrypTEC '99) 1085811ca2d4Sdjm * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf 1086811ca2d4Sdjm * 1087811ca2d4Sdjm * The subject came up in a talk by Dan Kaminsky, too. 1088811ca2d4Sdjm * 1089811ca2d4Sdjm * If you see the picture is different, the key is different. 1090811ca2d4Sdjm * If the picture looks the same, you still know nothing. 1091811ca2d4Sdjm * 1092811ca2d4Sdjm * The algorithm used here is a worm crawling over a discrete plane, 1093811ca2d4Sdjm * leaving a trace (augmenting the field) everywhere it goes. 1094811ca2d4Sdjm * Movement is taken from dgst_raw 2bit-wise. Bumping into walls 1095811ca2d4Sdjm * makes the respective movement vector be ignored for this turn. 1096811ca2d4Sdjm * Graphs are not unambiguous, because circles in graphs can be 1097811ca2d4Sdjm * walked in either direction. 1098811ca2d4Sdjm */ 1099811ca2d4Sdjm 1100811ca2d4Sdjm /* 1101811ca2d4Sdjm * Field sizes for the random art. Have to be odd, so the starting point 1102811ca2d4Sdjm * can be in the exact middle of the picture, and FLDBASE should be >=8 . 1103811ca2d4Sdjm * Else pictures would be too dense, and drawing the frame would 1104811ca2d4Sdjm * fail, too, because the key type would not fit in anymore. 1105811ca2d4Sdjm */ 1106811ca2d4Sdjm #define FLDBASE 8 1107811ca2d4Sdjm #define FLDSIZE_Y (FLDBASE + 1) 1108811ca2d4Sdjm #define FLDSIZE_X (FLDBASE * 2 + 1) 1109811ca2d4Sdjm static char * 11103dbedef4Sdjm fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, 1111811ca2d4Sdjm const struct sshkey *k) 1112811ca2d4Sdjm { 1113811ca2d4Sdjm /* 1114811ca2d4Sdjm * Chars to be used after each other every time the worm 1115811ca2d4Sdjm * intersects with itself. Matter of taste. 1116811ca2d4Sdjm */ 1117811ca2d4Sdjm char *augmentation_string = " .o+=*BOX@%&#/^SE"; 11183dbedef4Sdjm char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X]; 1119811ca2d4Sdjm u_char field[FLDSIZE_X][FLDSIZE_Y]; 11203dbedef4Sdjm size_t i, tlen, hlen; 1121811ca2d4Sdjm u_int b; 11223ad76a24Sdjm int x, y, r; 1123811ca2d4Sdjm size_t len = strlen(augmentation_string) - 1; 1124811ca2d4Sdjm 1125811ca2d4Sdjm if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) 1126811ca2d4Sdjm return NULL; 1127811ca2d4Sdjm 1128811ca2d4Sdjm /* initialize field */ 1129811ca2d4Sdjm memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); 1130811ca2d4Sdjm x = FLDSIZE_X / 2; 1131811ca2d4Sdjm y = FLDSIZE_Y / 2; 1132811ca2d4Sdjm 1133811ca2d4Sdjm /* process raw key */ 1134811ca2d4Sdjm for (i = 0; i < dgst_raw_len; i++) { 1135811ca2d4Sdjm int input; 1136811ca2d4Sdjm /* each byte conveys four 2-bit move commands */ 1137811ca2d4Sdjm input = dgst_raw[i]; 1138811ca2d4Sdjm for (b = 0; b < 4; b++) { 1139811ca2d4Sdjm /* evaluate 2 bit, rest is shifted later */ 1140811ca2d4Sdjm x += (input & 0x1) ? 1 : -1; 1141811ca2d4Sdjm y += (input & 0x2) ? 1 : -1; 1142811ca2d4Sdjm 1143811ca2d4Sdjm /* assure we are still in bounds */ 114403db5a1fSderaadt x = MAXIMUM(x, 0); 114503db5a1fSderaadt y = MAXIMUM(y, 0); 114603db5a1fSderaadt x = MINIMUM(x, FLDSIZE_X - 1); 114703db5a1fSderaadt y = MINIMUM(y, FLDSIZE_Y - 1); 1148811ca2d4Sdjm 1149811ca2d4Sdjm /* augment the field */ 1150811ca2d4Sdjm if (field[x][y] < len - 2) 1151811ca2d4Sdjm field[x][y]++; 1152811ca2d4Sdjm input = input >> 2; 1153811ca2d4Sdjm } 1154811ca2d4Sdjm } 1155811ca2d4Sdjm 1156811ca2d4Sdjm /* mark starting point and end point*/ 1157811ca2d4Sdjm field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; 1158811ca2d4Sdjm field[x][y] = len; 1159811ca2d4Sdjm 11603ad76a24Sdjm /* assemble title */ 11613ad76a24Sdjm r = snprintf(title, sizeof(title), "[%s %u]", 1162811ca2d4Sdjm sshkey_type(k), sshkey_size(k)); 11633ad76a24Sdjm /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */ 11643ad76a24Sdjm if (r < 0 || r > (int)sizeof(title)) 11653dbedef4Sdjm r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k)); 11663dbedef4Sdjm tlen = (r <= 0) ? 0 : strlen(title); 11673dbedef4Sdjm 11683dbedef4Sdjm /* assemble hash ID. */ 11693dbedef4Sdjm r = snprintf(hash, sizeof(hash), "[%s]", alg); 11703dbedef4Sdjm hlen = (r <= 0) ? 0 : strlen(hash); 1171811ca2d4Sdjm 1172811ca2d4Sdjm /* output upper border */ 11733ad76a24Sdjm p = retval; 11743ad76a24Sdjm *p++ = '+'; 11753ad76a24Sdjm for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++) 11763ad76a24Sdjm *p++ = '-'; 11773ad76a24Sdjm memcpy(p, title, tlen); 11783ad76a24Sdjm p += tlen; 11793dbedef4Sdjm for (i += tlen; i < FLDSIZE_X; i++) 1180811ca2d4Sdjm *p++ = '-'; 1181811ca2d4Sdjm *p++ = '+'; 1182811ca2d4Sdjm *p++ = '\n'; 1183811ca2d4Sdjm 1184811ca2d4Sdjm /* output content */ 1185811ca2d4Sdjm for (y = 0; y < FLDSIZE_Y; y++) { 1186811ca2d4Sdjm *p++ = '|'; 1187811ca2d4Sdjm for (x = 0; x < FLDSIZE_X; x++) 118803db5a1fSderaadt *p++ = augmentation_string[MINIMUM(field[x][y], len)]; 1189811ca2d4Sdjm *p++ = '|'; 1190811ca2d4Sdjm *p++ = '\n'; 1191811ca2d4Sdjm } 1192811ca2d4Sdjm 1193811ca2d4Sdjm /* output lower border */ 1194811ca2d4Sdjm *p++ = '+'; 11953dbedef4Sdjm for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++) 11963dbedef4Sdjm *p++ = '-'; 11973dbedef4Sdjm memcpy(p, hash, hlen); 11983dbedef4Sdjm p += hlen; 11993dbedef4Sdjm for (i += hlen; i < FLDSIZE_X; i++) 1200811ca2d4Sdjm *p++ = '-'; 1201811ca2d4Sdjm *p++ = '+'; 1202811ca2d4Sdjm 1203811ca2d4Sdjm return retval; 1204811ca2d4Sdjm } 1205811ca2d4Sdjm 1206811ca2d4Sdjm char * 12073dbedef4Sdjm sshkey_fingerprint(const struct sshkey *k, int dgst_alg, 1208811ca2d4Sdjm enum sshkey_fp_rep dgst_rep) 1209811ca2d4Sdjm { 1210811ca2d4Sdjm char *retval = NULL; 1211811ca2d4Sdjm u_char *dgst_raw; 1212811ca2d4Sdjm size_t dgst_raw_len; 1213811ca2d4Sdjm 12143dbedef4Sdjm if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0) 1215811ca2d4Sdjm return NULL; 1216811ca2d4Sdjm switch (dgst_rep) { 12173dbedef4Sdjm case SSH_FP_DEFAULT: 12183dbedef4Sdjm if (dgst_alg == SSH_DIGEST_MD5) { 12193dbedef4Sdjm retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), 12203dbedef4Sdjm dgst_raw, dgst_raw_len); 12213dbedef4Sdjm } else { 12223dbedef4Sdjm retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), 12233dbedef4Sdjm dgst_raw, dgst_raw_len); 12243dbedef4Sdjm } 12253dbedef4Sdjm break; 1226811ca2d4Sdjm case SSH_FP_HEX: 12273dbedef4Sdjm retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), 12283dbedef4Sdjm dgst_raw, dgst_raw_len); 12293dbedef4Sdjm break; 12303dbedef4Sdjm case SSH_FP_BASE64: 12313dbedef4Sdjm retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), 12323dbedef4Sdjm dgst_raw, dgst_raw_len); 1233811ca2d4Sdjm break; 1234811ca2d4Sdjm case SSH_FP_BUBBLEBABBLE: 1235811ca2d4Sdjm retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len); 1236811ca2d4Sdjm break; 1237811ca2d4Sdjm case SSH_FP_RANDOMART: 12383dbedef4Sdjm retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg), 12393dbedef4Sdjm dgst_raw, dgst_raw_len, k); 1240811ca2d4Sdjm break; 1241811ca2d4Sdjm default: 1242c9831b39Sjsg freezero(dgst_raw, dgst_raw_len); 1243811ca2d4Sdjm return NULL; 1244811ca2d4Sdjm } 1245c9831b39Sjsg freezero(dgst_raw, dgst_raw_len); 1246811ca2d4Sdjm return retval; 1247811ca2d4Sdjm } 1248811ca2d4Sdjm 1249963d8732Sdjm static int 1250963d8732Sdjm peek_type_nid(const char *s, size_t l, int *nid) 1251963d8732Sdjm { 12529c1667dbSdjm const struct sshkey_impl *impl; 12539c1667dbSdjm int i; 1254811ca2d4Sdjm 12559c1667dbSdjm for (i = 0; keyimpls[i] != NULL; i++) { 12569c1667dbSdjm impl = keyimpls[i]; 12579c1667dbSdjm if (impl->name == NULL || strlen(impl->name) != l) 1258963d8732Sdjm continue; 12599c1667dbSdjm if (memcmp(s, impl->name, l) == 0) { 1260963d8732Sdjm *nid = -1; 12619c1667dbSdjm if (key_type_is_ecdsa_variant(impl->type)) 12629c1667dbSdjm *nid = impl->nid; 12639c1667dbSdjm return impl->type; 1264963d8732Sdjm } 1265963d8732Sdjm } 1266963d8732Sdjm return KEY_UNSPEC; 1267963d8732Sdjm } 1268963d8732Sdjm 1269963d8732Sdjm /* XXX this can now be made const char * */ 1270811ca2d4Sdjm int 1271811ca2d4Sdjm sshkey_read(struct sshkey *ret, char **cpp) 1272811ca2d4Sdjm { 1273811ca2d4Sdjm struct sshkey *k; 1274963d8732Sdjm char *cp, *blobcopy; 1275963d8732Sdjm size_t space; 1276811ca2d4Sdjm int r, type, curve_nid = -1; 1277811ca2d4Sdjm struct sshbuf *blob; 1278811ca2d4Sdjm 1279e5eb81a0Sdtucker if (ret == NULL) 1280e5eb81a0Sdtucker return SSH_ERR_INVALID_ARGUMENT; 1281a53002e7Sdjm if (ret->type != KEY_UNSPEC && sshkey_impl_from_type(ret->type) == NULL) 1282963d8732Sdjm return SSH_ERR_INVALID_ARGUMENT; 1283963d8732Sdjm 1284963d8732Sdjm /* Decode type */ 1285963d8732Sdjm cp = *cpp; 1286963d8732Sdjm space = strcspn(cp, " \t"); 1287963d8732Sdjm if (space == strlen(cp)) 1288811ca2d4Sdjm return SSH_ERR_INVALID_FORMAT; 1289963d8732Sdjm if ((type = peek_type_nid(cp, space, &curve_nid)) == KEY_UNSPEC) 1290811ca2d4Sdjm return SSH_ERR_INVALID_FORMAT; 1291963d8732Sdjm 1292963d8732Sdjm /* skip whitespace */ 1293963d8732Sdjm for (cp += space; *cp == ' ' || *cp == '\t'; cp++) 1294963d8732Sdjm ; 1295811ca2d4Sdjm if (*cp == '\0') 1296811ca2d4Sdjm return SSH_ERR_INVALID_FORMAT; 12972cec05c0Sdjm if (ret->type != KEY_UNSPEC && ret->type != type) 1298811ca2d4Sdjm return SSH_ERR_KEY_TYPE_MISMATCH; 1299811ca2d4Sdjm if ((blob = sshbuf_new()) == NULL) 1300811ca2d4Sdjm return SSH_ERR_ALLOC_FAIL; 1301963d8732Sdjm 1302963d8732Sdjm /* find end of keyblob and decode */ 1303963d8732Sdjm space = strcspn(cp, " \t"); 1304963d8732Sdjm if ((blobcopy = strndup(cp, space)) == NULL) { 1305963d8732Sdjm sshbuf_free(blob); 1306963d8732Sdjm return SSH_ERR_ALLOC_FAIL; 1307963d8732Sdjm } 1308963d8732Sdjm if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) { 1309963d8732Sdjm free(blobcopy); 1310811ca2d4Sdjm sshbuf_free(blob); 1311811ca2d4Sdjm return r; 1312811ca2d4Sdjm } 1313963d8732Sdjm free(blobcopy); 1314963d8732Sdjm if ((r = sshkey_fromb(blob, &k)) != 0) { 1315811ca2d4Sdjm sshbuf_free(blob); 1316811ca2d4Sdjm return r; 1317811ca2d4Sdjm } 1318811ca2d4Sdjm sshbuf_free(blob); 1319963d8732Sdjm 1320963d8732Sdjm /* skip whitespace and leave cp at start of comment */ 1321963d8732Sdjm for (cp += space; *cp == ' ' || *cp == '\t'; cp++) 1322963d8732Sdjm ; 1323963d8732Sdjm 1324963d8732Sdjm /* ensure type of blob matches type at start of line */ 1325811ca2d4Sdjm if (k->type != type) { 1326811ca2d4Sdjm sshkey_free(k); 1327811ca2d4Sdjm return SSH_ERR_KEY_TYPE_MISMATCH; 1328811ca2d4Sdjm } 13294f5eb3ebSdjm if (key_type_is_ecdsa_variant(type) && curve_nid != k->ecdsa_nid) { 1330811ca2d4Sdjm sshkey_free(k); 1331811ca2d4Sdjm return SSH_ERR_EC_CURVE_MISMATCH; 1332811ca2d4Sdjm } 1333963d8732Sdjm 1334963d8732Sdjm /* Fill in ret from parsed key */ 1335a53002e7Sdjm sshkey_free_contents(ret); 1336a53002e7Sdjm *ret = *k; 1337a53002e7Sdjm freezero(k, sizeof(*k)); 1338963d8732Sdjm 1339963d8732Sdjm /* success */ 1340963d8732Sdjm *cpp = cp; 1341963d8732Sdjm return 0; 1342811ca2d4Sdjm } 1343811ca2d4Sdjm 1344811ca2d4Sdjm int 13453f5e6877Sdjm sshkey_to_base64(const struct sshkey *key, char **b64p) 1346811ca2d4Sdjm { 13473f5e6877Sdjm int r = SSH_ERR_INTERNAL_ERROR; 13483f5e6877Sdjm struct sshbuf *b = NULL; 1349811ca2d4Sdjm char *uu = NULL; 13503f5e6877Sdjm 13513f5e6877Sdjm if (b64p != NULL) 13523f5e6877Sdjm *b64p = NULL; 13533f5e6877Sdjm if ((b = sshbuf_new()) == NULL) 13543f5e6877Sdjm return SSH_ERR_ALLOC_FAIL; 13553f5e6877Sdjm if ((r = sshkey_putb(key, b)) != 0) 13563f5e6877Sdjm goto out; 1357bbb0e5b6Sdjm if ((uu = sshbuf_dtob64_string(b, 0)) == NULL) { 13583f5e6877Sdjm r = SSH_ERR_ALLOC_FAIL; 13593f5e6877Sdjm goto out; 13603f5e6877Sdjm } 13613f5e6877Sdjm /* Success */ 13623f5e6877Sdjm if (b64p != NULL) { 13633f5e6877Sdjm *b64p = uu; 13643f5e6877Sdjm uu = NULL; 13653f5e6877Sdjm } 13663f5e6877Sdjm r = 0; 13673f5e6877Sdjm out: 13683f5e6877Sdjm sshbuf_free(b); 13693f5e6877Sdjm free(uu); 13703f5e6877Sdjm return r; 13713f5e6877Sdjm } 13723f5e6877Sdjm 1373355c336cSdjm int 13743f5e6877Sdjm sshkey_format_text(const struct sshkey *key, struct sshbuf *b) 13753f5e6877Sdjm { 13763f5e6877Sdjm int r = SSH_ERR_INTERNAL_ERROR; 13773f5e6877Sdjm char *uu = NULL; 13783f5e6877Sdjm 13793f5e6877Sdjm if ((r = sshkey_to_base64(key, &uu)) != 0) 13803f5e6877Sdjm goto out; 13813f5e6877Sdjm if ((r = sshbuf_putf(b, "%s %s", 13823f5e6877Sdjm sshkey_ssh_name(key), uu)) != 0) 13833f5e6877Sdjm goto out; 13843f5e6877Sdjm r = 0; 13853f5e6877Sdjm out: 13863f5e6877Sdjm free(uu); 13873f5e6877Sdjm return r; 13883f5e6877Sdjm } 13893f5e6877Sdjm 13903f5e6877Sdjm int 13913f5e6877Sdjm sshkey_write(const struct sshkey *key, FILE *f) 13923f5e6877Sdjm { 13933f5e6877Sdjm struct sshbuf *b = NULL; 13943f5e6877Sdjm int r = SSH_ERR_INTERNAL_ERROR; 13953f5e6877Sdjm 13963f5e6877Sdjm if ((b = sshbuf_new()) == NULL) 13973f5e6877Sdjm return SSH_ERR_ALLOC_FAIL; 13983f5e6877Sdjm if ((r = sshkey_format_text(key, b)) != 0) 13993f5e6877Sdjm goto out; 14003f5e6877Sdjm if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) { 14013f5e6877Sdjm if (feof(f)) 14023f5e6877Sdjm errno = EPIPE; 14033f5e6877Sdjm r = SSH_ERR_SYSTEM_ERROR; 14043f5e6877Sdjm goto out; 14053f5e6877Sdjm } 14063f5e6877Sdjm /* Success */ 14073f5e6877Sdjm r = 0; 14083f5e6877Sdjm out: 14093f5e6877Sdjm sshbuf_free(b); 14103f5e6877Sdjm return r; 1411811ca2d4Sdjm } 1412811ca2d4Sdjm 1413811ca2d4Sdjm const char * 1414811ca2d4Sdjm sshkey_cert_type(const struct sshkey *k) 1415811ca2d4Sdjm { 1416811ca2d4Sdjm switch (k->cert->type) { 1417811ca2d4Sdjm case SSH2_CERT_TYPE_USER: 1418811ca2d4Sdjm return "user"; 1419811ca2d4Sdjm case SSH2_CERT_TYPE_HOST: 1420811ca2d4Sdjm return "host"; 1421811ca2d4Sdjm default: 1422811ca2d4Sdjm return "unknown"; 1423811ca2d4Sdjm } 1424811ca2d4Sdjm } 1425811ca2d4Sdjm 14261eb54458Sdjm int 14271eb54458Sdjm sshkey_check_rsa_length(const struct sshkey *k, int min_size) 14281eb54458Sdjm { 14291eb54458Sdjm #ifdef WITH_OPENSSL 14301eb54458Sdjm int nbits; 14311eb54458Sdjm 14325411e769Sdjm if (k == NULL || k->pkey == NULL || 14331eb54458Sdjm (k->type != KEY_RSA && k->type != KEY_RSA_CERT)) 14341eb54458Sdjm return 0; 14355411e769Sdjm nbits = EVP_PKEY_bits(k->pkey); 14361eb54458Sdjm if (nbits < SSH_RSA_MINIMUM_MODULUS_SIZE || 14371eb54458Sdjm (min_size > 0 && nbits < min_size)) 14381eb54458Sdjm return SSH_ERR_KEY_LENGTH; 14391eb54458Sdjm #endif /* WITH_OPENSSL */ 14401eb54458Sdjm return 0; 14411eb54458Sdjm } 14421eb54458Sdjm 1443811ca2d4Sdjm #ifdef WITH_OPENSSL 1444811ca2d4Sdjm int 14455411e769Sdjm sshkey_ecdsa_key_to_nid(const EC_KEY *k) 1446811ca2d4Sdjm { 14475411e769Sdjm const EC_GROUP *g; 1448811ca2d4Sdjm int nid; 1449811ca2d4Sdjm 14505411e769Sdjm if (k == NULL || (g = EC_KEY_get0_group(k)) == NULL) 14515411e769Sdjm return -1; 14525411e769Sdjm if ((nid = EC_GROUP_get_curve_name(g)) <= 0) 14535411e769Sdjm return -1; 1454811ca2d4Sdjm return nid; 1455811ca2d4Sdjm } 14565411e769Sdjm 14575411e769Sdjm int 14585411e769Sdjm sshkey_ecdsa_pkey_to_nid(EVP_PKEY *pkey) 14595411e769Sdjm { 14605411e769Sdjm return sshkey_ecdsa_key_to_nid(EVP_PKEY_get0_EC_KEY(pkey)); 1461811ca2d4Sdjm } 1462811ca2d4Sdjm #endif /* WITH_OPENSSL */ 1463811ca2d4Sdjm 1464811ca2d4Sdjm int 1465811ca2d4Sdjm sshkey_generate(int type, u_int bits, struct sshkey **keyp) 1466811ca2d4Sdjm { 1467811ca2d4Sdjm struct sshkey *k; 1468811ca2d4Sdjm int ret = SSH_ERR_INTERNAL_ERROR; 1469b6025febSdjm const struct sshkey_impl *impl; 1470811ca2d4Sdjm 1471b6025febSdjm if (keyp == NULL || sshkey_type_is_cert(type)) 1472811ca2d4Sdjm return SSH_ERR_INVALID_ARGUMENT; 1473811ca2d4Sdjm *keyp = NULL; 1474b6025febSdjm if ((impl = sshkey_impl_from_type(type)) == NULL) 1475b6025febSdjm return SSH_ERR_KEY_TYPE_UNKNOWN; 1476b6025febSdjm if (impl->funcs->generate == NULL) 1477b6025febSdjm return SSH_ERR_FEATURE_UNSUPPORTED; 1478811ca2d4Sdjm if ((k = sshkey_new(KEY_UNSPEC)) == NULL) 1479811ca2d4Sdjm return SSH_ERR_ALLOC_FAIL; 1480811ca2d4Sdjm k->type = type; 1481b6025febSdjm if ((ret = impl->funcs->generate(k, bits)) != 0) { 1482811ca2d4Sdjm sshkey_free(k); 1483811ca2d4Sdjm return ret; 1484811ca2d4Sdjm } 1485b6025febSdjm /* success */ 1486b6025febSdjm *keyp = k; 1487b6025febSdjm return 0; 1488b6025febSdjm } 1489811ca2d4Sdjm 1490811ca2d4Sdjm int 1491811ca2d4Sdjm sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key) 1492811ca2d4Sdjm { 1493811ca2d4Sdjm u_int i; 1494811ca2d4Sdjm const struct sshkey_cert *from; 1495811ca2d4Sdjm struct sshkey_cert *to; 14960ff10372Sdjm int r = SSH_ERR_INTERNAL_ERROR; 1497811ca2d4Sdjm 14980ff10372Sdjm if (to_key == NULL || (from = from_key->cert) == NULL) 1499811ca2d4Sdjm return SSH_ERR_INVALID_ARGUMENT; 1500811ca2d4Sdjm 15010ff10372Sdjm if ((to = cert_new()) == NULL) 1502811ca2d4Sdjm return SSH_ERR_ALLOC_FAIL; 1503811ca2d4Sdjm 15040ff10372Sdjm if ((r = sshbuf_putb(to->certblob, from->certblob)) != 0 || 15050ff10372Sdjm (r = sshbuf_putb(to->critical, from->critical)) != 0 || 15060ff10372Sdjm (r = sshbuf_putb(to->extensions, from->extensions)) != 0) 15070ff10372Sdjm goto out; 1508811ca2d4Sdjm 1509811ca2d4Sdjm to->serial = from->serial; 1510811ca2d4Sdjm to->type = from->type; 1511811ca2d4Sdjm if (from->key_id == NULL) 1512811ca2d4Sdjm to->key_id = NULL; 15130ff10372Sdjm else if ((to->key_id = strdup(from->key_id)) == NULL) { 15140ff10372Sdjm r = SSH_ERR_ALLOC_FAIL; 15150ff10372Sdjm goto out; 15160ff10372Sdjm } 1517811ca2d4Sdjm to->valid_after = from->valid_after; 1518811ca2d4Sdjm to->valid_before = from->valid_before; 1519811ca2d4Sdjm if (from->signature_key == NULL) 1520811ca2d4Sdjm to->signature_key = NULL; 15210ff10372Sdjm else if ((r = sshkey_from_private(from->signature_key, 1522811ca2d4Sdjm &to->signature_key)) != 0) 15230ff10372Sdjm goto out; 15240ff10372Sdjm if (from->signature_type != NULL && 15250ff10372Sdjm (to->signature_type = strdup(from->signature_type)) == NULL) { 15260ff10372Sdjm r = SSH_ERR_ALLOC_FAIL; 15270ff10372Sdjm goto out; 15280ff10372Sdjm } 15290ff10372Sdjm if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) { 15300ff10372Sdjm r = SSH_ERR_INVALID_ARGUMENT; 15310ff10372Sdjm goto out; 15320ff10372Sdjm } 1533811ca2d4Sdjm if (from->nprincipals > 0) { 1534811ca2d4Sdjm if ((to->principals = calloc(from->nprincipals, 15350ff10372Sdjm sizeof(*to->principals))) == NULL) { 15360ff10372Sdjm r = SSH_ERR_ALLOC_FAIL; 15370ff10372Sdjm goto out; 15380ff10372Sdjm } 1539811ca2d4Sdjm for (i = 0; i < from->nprincipals; i++) { 1540811ca2d4Sdjm to->principals[i] = strdup(from->principals[i]); 1541811ca2d4Sdjm if (to->principals[i] == NULL) { 1542811ca2d4Sdjm to->nprincipals = i; 15430ff10372Sdjm r = SSH_ERR_ALLOC_FAIL; 15440ff10372Sdjm goto out; 1545811ca2d4Sdjm } 1546811ca2d4Sdjm } 1547811ca2d4Sdjm } 1548811ca2d4Sdjm to->nprincipals = from->nprincipals; 15490ff10372Sdjm 15500ff10372Sdjm /* success */ 15510ff10372Sdjm cert_free(to_key->cert); 15520ff10372Sdjm to_key->cert = to; 15530ff10372Sdjm to = NULL; 15540ff10372Sdjm r = 0; 15550ff10372Sdjm out: 15560ff10372Sdjm cert_free(to); 15570ff10372Sdjm return r; 1558811ca2d4Sdjm } 1559811ca2d4Sdjm 1560811ca2d4Sdjm int 15610d39f001Sdjm sshkey_copy_public_sk(const struct sshkey *from, struct sshkey *to) 15620d39f001Sdjm { 15630d39f001Sdjm /* Append security-key application string */ 15640d39f001Sdjm if ((to->sk_application = strdup(from->sk_application)) == NULL) 15650d39f001Sdjm return SSH_ERR_ALLOC_FAIL; 15660d39f001Sdjm return 0; 15670d39f001Sdjm } 15680d39f001Sdjm 15690d39f001Sdjm int 1570811ca2d4Sdjm sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) 1571811ca2d4Sdjm { 1572811ca2d4Sdjm struct sshkey *n = NULL; 15737c94020aSdjm int r = SSH_ERR_INTERNAL_ERROR; 15740d39f001Sdjm const struct sshkey_impl *impl; 1575811ca2d4Sdjm 1576811ca2d4Sdjm *pkp = NULL; 15770d39f001Sdjm if ((impl = sshkey_impl_from_key(k)) == NULL) 15780d39f001Sdjm return SSH_ERR_KEY_TYPE_UNKNOWN; 15797c94020aSdjm if ((n = sshkey_new(k->type)) == NULL) { 15807c94020aSdjm r = SSH_ERR_ALLOC_FAIL; 15817c94020aSdjm goto out; 1582811ca2d4Sdjm } 15830d39f001Sdjm if ((r = impl->funcs->copy_public(k, n)) != 0) 15847c94020aSdjm goto out; 15857c94020aSdjm if (sshkey_is_cert(k) && (r = sshkey_cert_copy(k, n)) != 0) 15867c94020aSdjm goto out; 15877c94020aSdjm /* success */ 1588811ca2d4Sdjm *pkp = n; 15897c94020aSdjm n = NULL; 15907c94020aSdjm r = 0; 15917c94020aSdjm out: 15927c94020aSdjm sshkey_free(n); 15937c94020aSdjm return r; 1594811ca2d4Sdjm } 1595811ca2d4Sdjm 1596707316f9Sdjm int 1597707316f9Sdjm sshkey_is_shielded(struct sshkey *k) 1598707316f9Sdjm { 1599707316f9Sdjm return k != NULL && k->shielded_private != NULL; 1600707316f9Sdjm } 1601707316f9Sdjm 1602707316f9Sdjm int 1603707316f9Sdjm sshkey_shield_private(struct sshkey *k) 1604707316f9Sdjm { 1605707316f9Sdjm struct sshbuf *prvbuf = NULL; 1606707316f9Sdjm u_char *prekey = NULL, *enc = NULL, keyiv[SSH_DIGEST_MAX_LENGTH]; 1607707316f9Sdjm struct sshcipher_ctx *cctx = NULL; 1608707316f9Sdjm const struct sshcipher *cipher; 1609707316f9Sdjm size_t i, enclen = 0; 1610707316f9Sdjm struct sshkey *kswap = NULL, tmp; 1611707316f9Sdjm int r = SSH_ERR_INTERNAL_ERROR; 1612707316f9Sdjm 1613707316f9Sdjm #ifdef DEBUG_PK 1614707316f9Sdjm fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k)); 1615707316f9Sdjm #endif 1616707316f9Sdjm if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) { 1617707316f9Sdjm r = SSH_ERR_INVALID_ARGUMENT; 1618707316f9Sdjm goto out; 1619707316f9Sdjm } 1620707316f9Sdjm if (cipher_keylen(cipher) + cipher_ivlen(cipher) > 1621707316f9Sdjm ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) { 1622707316f9Sdjm r = SSH_ERR_INTERNAL_ERROR; 1623707316f9Sdjm goto out; 1624707316f9Sdjm } 1625707316f9Sdjm 1626707316f9Sdjm /* Prepare a random pre-key, and from it an ephemeral key */ 1627502d8771Sdjm if ((r = sshkey_prekey_alloc(&prekey, SSHKEY_SHIELD_PREKEY_LEN)) != 0) 1628707316f9Sdjm goto out; 1629707316f9Sdjm arc4random_buf(prekey, SSHKEY_SHIELD_PREKEY_LEN); 1630707316f9Sdjm if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH, 1631707316f9Sdjm prekey, SSHKEY_SHIELD_PREKEY_LEN, 1632707316f9Sdjm keyiv, SSH_DIGEST_MAX_LENGTH)) != 0) 1633707316f9Sdjm goto out; 1634707316f9Sdjm #ifdef DEBUG_PK 1635707316f9Sdjm fprintf(stderr, "%s: key+iv\n", __func__); 1636707316f9Sdjm sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH), 1637707316f9Sdjm stderr); 1638707316f9Sdjm #endif 1639707316f9Sdjm if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher), 1640707316f9Sdjm keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 1)) != 0) 1641707316f9Sdjm goto out; 1642707316f9Sdjm 1643707316f9Sdjm /* Serialise and encrypt the private key using the ephemeral key */ 1644707316f9Sdjm if ((prvbuf = sshbuf_new()) == NULL) { 1645707316f9Sdjm r = SSH_ERR_ALLOC_FAIL; 1646707316f9Sdjm goto out; 1647707316f9Sdjm } 1648707316f9Sdjm if (sshkey_is_shielded(k) && (r = sshkey_unshield_private(k)) != 0) 1649707316f9Sdjm goto out; 1650707316f9Sdjm if ((r = sshkey_private_serialize_opt(k, prvbuf, 1651d3c68393Smarkus SSHKEY_SERIALIZE_SHIELD)) != 0) 1652707316f9Sdjm goto out; 1653707316f9Sdjm /* pad to cipher blocksize */ 1654707316f9Sdjm i = 0; 1655707316f9Sdjm while (sshbuf_len(prvbuf) % cipher_blocksize(cipher)) { 1656707316f9Sdjm if ((r = sshbuf_put_u8(prvbuf, ++i & 0xff)) != 0) 1657707316f9Sdjm goto out; 1658707316f9Sdjm } 1659707316f9Sdjm #ifdef DEBUG_PK 1660707316f9Sdjm fprintf(stderr, "%s: serialised\n", __func__); 1661707316f9Sdjm sshbuf_dump(prvbuf, stderr); 1662707316f9Sdjm #endif 1663707316f9Sdjm /* encrypt */ 1664707316f9Sdjm enclen = sshbuf_len(prvbuf); 1665707316f9Sdjm if ((enc = malloc(enclen)) == NULL) { 1666707316f9Sdjm r = SSH_ERR_ALLOC_FAIL; 1667707316f9Sdjm goto out; 1668707316f9Sdjm } 1669707316f9Sdjm if ((r = cipher_crypt(cctx, 0, enc, 1670707316f9Sdjm sshbuf_ptr(prvbuf), sshbuf_len(prvbuf), 0, 0)) != 0) 1671707316f9Sdjm goto out; 1672707316f9Sdjm #ifdef DEBUG_PK 1673707316f9Sdjm fprintf(stderr, "%s: encrypted\n", __func__); 1674707316f9Sdjm sshbuf_dump_data(enc, enclen, stderr); 1675707316f9Sdjm #endif 1676707316f9Sdjm 1677707316f9Sdjm /* Make a scrubbed, public-only copy of our private key argument */ 1678707316f9Sdjm if ((r = sshkey_from_private(k, &kswap)) != 0) 1679707316f9Sdjm goto out; 1680707316f9Sdjm 1681707316f9Sdjm /* Swap the private key out (it will be destroyed below) */ 1682707316f9Sdjm tmp = *kswap; 1683707316f9Sdjm *kswap = *k; 1684707316f9Sdjm *k = tmp; 1685707316f9Sdjm 1686707316f9Sdjm /* Insert the shielded key into our argument */ 1687707316f9Sdjm k->shielded_private = enc; 1688707316f9Sdjm k->shielded_len = enclen; 1689707316f9Sdjm k->shield_prekey = prekey; 1690707316f9Sdjm k->shield_prekey_len = SSHKEY_SHIELD_PREKEY_LEN; 1691707316f9Sdjm enc = prekey = NULL; /* transferred */ 1692707316f9Sdjm enclen = 0; 1693707316f9Sdjm 169456ca7242Sdjm /* preserve key fields that are required for correct operation */ 169556ca7242Sdjm k->sk_flags = kswap->sk_flags; 169656ca7242Sdjm 1697707316f9Sdjm /* success */ 1698707316f9Sdjm r = 0; 1699707316f9Sdjm 1700707316f9Sdjm out: 1701707316f9Sdjm /* XXX behaviour on error - invalidate original private key? */ 1702707316f9Sdjm cipher_free(cctx); 1703707316f9Sdjm explicit_bzero(keyiv, sizeof(keyiv)); 1704707316f9Sdjm explicit_bzero(&tmp, sizeof(tmp)); 17058aa78e84Sdjm freezero(enc, enclen); 1706502d8771Sdjm sshkey_prekey_free(prekey, SSHKEY_SHIELD_PREKEY_LEN); 1707707316f9Sdjm sshkey_free(kswap); 1708707316f9Sdjm sshbuf_free(prvbuf); 1709707316f9Sdjm return r; 1710707316f9Sdjm } 1711707316f9Sdjm 1712c655ee50Sdjm /* Check deterministic padding after private key */ 1713c655ee50Sdjm static int 1714c655ee50Sdjm private2_check_padding(struct sshbuf *decrypted) 1715c655ee50Sdjm { 1716c655ee50Sdjm u_char pad; 1717c655ee50Sdjm size_t i; 1718c655ee50Sdjm int r; 1719c655ee50Sdjm 1720c655ee50Sdjm i = 0; 1721c655ee50Sdjm while (sshbuf_len(decrypted)) { 1722c655ee50Sdjm if ((r = sshbuf_get_u8(decrypted, &pad)) != 0) 1723c655ee50Sdjm goto out; 1724c655ee50Sdjm if (pad != (++i & 0xff)) { 1725c655ee50Sdjm r = SSH_ERR_INVALID_FORMAT; 1726c655ee50Sdjm goto out; 1727c655ee50Sdjm } 1728c655ee50Sdjm } 1729c655ee50Sdjm /* success */ 1730c655ee50Sdjm r = 0; 1731c655ee50Sdjm out: 1732c655ee50Sdjm explicit_bzero(&pad, sizeof(pad)); 1733c655ee50Sdjm explicit_bzero(&i, sizeof(i)); 1734c655ee50Sdjm return r; 1735c655ee50Sdjm } 1736c655ee50Sdjm 1737707316f9Sdjm int 1738707316f9Sdjm sshkey_unshield_private(struct sshkey *k) 1739707316f9Sdjm { 1740707316f9Sdjm struct sshbuf *prvbuf = NULL; 1741c655ee50Sdjm u_char *cp, keyiv[SSH_DIGEST_MAX_LENGTH]; 1742707316f9Sdjm struct sshcipher_ctx *cctx = NULL; 1743707316f9Sdjm const struct sshcipher *cipher; 1744707316f9Sdjm struct sshkey *kswap = NULL, tmp; 1745707316f9Sdjm int r = SSH_ERR_INTERNAL_ERROR; 1746707316f9Sdjm 1747707316f9Sdjm #ifdef DEBUG_PK 1748707316f9Sdjm fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k)); 1749707316f9Sdjm #endif 1750707316f9Sdjm if (!sshkey_is_shielded(k)) 1751707316f9Sdjm return 0; /* nothing to do */ 1752707316f9Sdjm 1753707316f9Sdjm if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) { 1754707316f9Sdjm r = SSH_ERR_INVALID_ARGUMENT; 1755707316f9Sdjm goto out; 1756707316f9Sdjm } 1757707316f9Sdjm if (cipher_keylen(cipher) + cipher_ivlen(cipher) > 1758707316f9Sdjm ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) { 1759707316f9Sdjm r = SSH_ERR_INTERNAL_ERROR; 1760707316f9Sdjm goto out; 1761707316f9Sdjm } 1762707316f9Sdjm /* check size of shielded key blob */ 1763707316f9Sdjm if (k->shielded_len < cipher_blocksize(cipher) || 1764707316f9Sdjm (k->shielded_len % cipher_blocksize(cipher)) != 0) { 1765707316f9Sdjm r = SSH_ERR_INVALID_FORMAT; 1766707316f9Sdjm goto out; 1767707316f9Sdjm } 1768707316f9Sdjm 1769707316f9Sdjm /* Calculate the ephemeral key from the prekey */ 1770707316f9Sdjm if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH, 1771707316f9Sdjm k->shield_prekey, k->shield_prekey_len, 1772707316f9Sdjm keyiv, SSH_DIGEST_MAX_LENGTH)) != 0) 1773707316f9Sdjm goto out; 1774707316f9Sdjm if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher), 1775707316f9Sdjm keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 0)) != 0) 1776707316f9Sdjm goto out; 1777707316f9Sdjm #ifdef DEBUG_PK 1778707316f9Sdjm fprintf(stderr, "%s: key+iv\n", __func__); 1779707316f9Sdjm sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH), 1780707316f9Sdjm stderr); 1781707316f9Sdjm #endif 1782707316f9Sdjm 1783707316f9Sdjm /* Decrypt and parse the shielded private key using the ephemeral key */ 1784707316f9Sdjm if ((prvbuf = sshbuf_new()) == NULL) { 1785707316f9Sdjm r = SSH_ERR_ALLOC_FAIL; 1786707316f9Sdjm goto out; 1787707316f9Sdjm } 1788707316f9Sdjm if ((r = sshbuf_reserve(prvbuf, k->shielded_len, &cp)) != 0) 1789707316f9Sdjm goto out; 1790707316f9Sdjm /* decrypt */ 1791707316f9Sdjm #ifdef DEBUG_PK 1792707316f9Sdjm fprintf(stderr, "%s: encrypted\n", __func__); 1793707316f9Sdjm sshbuf_dump_data(k->shielded_private, k->shielded_len, stderr); 1794707316f9Sdjm #endif 1795707316f9Sdjm if ((r = cipher_crypt(cctx, 0, cp, 1796707316f9Sdjm k->shielded_private, k->shielded_len, 0, 0)) != 0) 1797707316f9Sdjm goto out; 1798707316f9Sdjm #ifdef DEBUG_PK 1799707316f9Sdjm fprintf(stderr, "%s: serialised\n", __func__); 1800707316f9Sdjm sshbuf_dump(prvbuf, stderr); 1801707316f9Sdjm #endif 1802707316f9Sdjm /* Parse private key */ 1803707316f9Sdjm if ((r = sshkey_private_deserialize(prvbuf, &kswap)) != 0) 1804707316f9Sdjm goto out; 1805c655ee50Sdjm 1806c655ee50Sdjm if ((r = private2_check_padding(prvbuf)) != 0) 1807707316f9Sdjm goto out; 1808707316f9Sdjm 1809707316f9Sdjm /* Swap the parsed key back into place */ 1810707316f9Sdjm tmp = *kswap; 1811707316f9Sdjm *kswap = *k; 1812707316f9Sdjm *k = tmp; 1813707316f9Sdjm 1814707316f9Sdjm /* success */ 1815707316f9Sdjm r = 0; 1816707316f9Sdjm 1817707316f9Sdjm out: 1818707316f9Sdjm cipher_free(cctx); 1819707316f9Sdjm explicit_bzero(keyiv, sizeof(keyiv)); 1820707316f9Sdjm explicit_bzero(&tmp, sizeof(tmp)); 1821707316f9Sdjm sshkey_free(kswap); 1822707316f9Sdjm sshbuf_free(prvbuf); 1823707316f9Sdjm return r; 1824707316f9Sdjm } 1825707316f9Sdjm 1826811ca2d4Sdjm static int 18274736d833Sdjm cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf) 1828811ca2d4Sdjm { 18294736d833Sdjm struct sshbuf *principals = NULL, *crit = NULL; 18304736d833Sdjm struct sshbuf *exts = NULL, *ca = NULL; 18314736d833Sdjm u_char *sig = NULL; 18324736d833Sdjm size_t signed_len = 0, slen = 0, kidlen = 0; 1833811ca2d4Sdjm int ret = SSH_ERR_INTERNAL_ERROR; 1834811ca2d4Sdjm 1835811ca2d4Sdjm /* Copy the entire key blob for verification and later serialisation */ 18364736d833Sdjm if ((ret = sshbuf_putb(key->cert->certblob, certbuf)) != 0) 1837811ca2d4Sdjm return ret; 1838811ca2d4Sdjm 18395e456000Sdjm /* Parse body of certificate up to signature */ 18405e456000Sdjm if ((ret = sshbuf_get_u64(b, &key->cert->serial)) != 0 || 1841811ca2d4Sdjm (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 || 1842811ca2d4Sdjm (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 || 184326066096Sdjm (ret = sshbuf_froms(b, &principals)) != 0 || 1844811ca2d4Sdjm (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 || 1845811ca2d4Sdjm (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 || 184626066096Sdjm (ret = sshbuf_froms(b, &crit)) != 0 || 18475e456000Sdjm (ret = sshbuf_froms(b, &exts)) != 0 || 1848811ca2d4Sdjm (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || 18494736d833Sdjm (ret = sshbuf_froms(b, &ca)) != 0) { 1850811ca2d4Sdjm /* XXX debug print error for ret */ 1851811ca2d4Sdjm ret = SSH_ERR_INVALID_FORMAT; 1852811ca2d4Sdjm goto out; 1853811ca2d4Sdjm } 1854811ca2d4Sdjm 1855811ca2d4Sdjm /* Signature is left in the buffer so we can calculate this length */ 1856811ca2d4Sdjm signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b); 1857811ca2d4Sdjm 1858811ca2d4Sdjm if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) { 1859811ca2d4Sdjm ret = SSH_ERR_INVALID_FORMAT; 1860811ca2d4Sdjm goto out; 1861811ca2d4Sdjm } 1862811ca2d4Sdjm 1863811ca2d4Sdjm if (key->cert->type != SSH2_CERT_TYPE_USER && 1864811ca2d4Sdjm key->cert->type != SSH2_CERT_TYPE_HOST) { 1865811ca2d4Sdjm ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE; 1866811ca2d4Sdjm goto out; 1867811ca2d4Sdjm } 1868811ca2d4Sdjm 186926066096Sdjm /* Parse principals section */ 187026066096Sdjm while (sshbuf_len(principals) > 0) { 187126066096Sdjm char *principal = NULL; 187226066096Sdjm char **oprincipals = NULL; 187326066096Sdjm 1874811ca2d4Sdjm if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) { 1875811ca2d4Sdjm ret = SSH_ERR_INVALID_FORMAT; 1876811ca2d4Sdjm goto out; 1877811ca2d4Sdjm } 187826066096Sdjm if ((ret = sshbuf_get_cstring(principals, &principal, 187926066096Sdjm NULL)) != 0) { 1880811ca2d4Sdjm ret = SSH_ERR_INVALID_FORMAT; 1881811ca2d4Sdjm goto out; 1882811ca2d4Sdjm } 1883811ca2d4Sdjm oprincipals = key->cert->principals; 1884eaf8e3f6Sderaadt key->cert->principals = recallocarray(key->cert->principals, 1885eaf8e3f6Sderaadt key->cert->nprincipals, key->cert->nprincipals + 1, 1886eaf8e3f6Sderaadt sizeof(*key->cert->principals)); 1887811ca2d4Sdjm if (key->cert->principals == NULL) { 1888811ca2d4Sdjm free(principal); 1889811ca2d4Sdjm key->cert->principals = oprincipals; 1890811ca2d4Sdjm ret = SSH_ERR_ALLOC_FAIL; 1891811ca2d4Sdjm goto out; 1892811ca2d4Sdjm } 1893811ca2d4Sdjm key->cert->principals[key->cert->nprincipals++] = principal; 1894811ca2d4Sdjm } 1895811ca2d4Sdjm 189626066096Sdjm /* 189726066096Sdjm * Stash a copies of the critical options and extensions sections 189826066096Sdjm * for later use. 189926066096Sdjm */ 190026066096Sdjm if ((ret = sshbuf_putb(key->cert->critical, crit)) != 0 || 190126066096Sdjm (exts != NULL && 190226066096Sdjm (ret = sshbuf_putb(key->cert->extensions, exts)) != 0)) 1903811ca2d4Sdjm goto out; 1904811ca2d4Sdjm 190526066096Sdjm /* 190626066096Sdjm * Validate critical options and extensions sections format. 190726066096Sdjm */ 190826066096Sdjm while (sshbuf_len(crit) != 0) { 190926066096Sdjm if ((ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0 || 191026066096Sdjm (ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0) { 191126066096Sdjm sshbuf_reset(key->cert->critical); 1912811ca2d4Sdjm ret = SSH_ERR_INVALID_FORMAT; 1913811ca2d4Sdjm goto out; 1914811ca2d4Sdjm } 1915811ca2d4Sdjm } 191626066096Sdjm while (exts != NULL && sshbuf_len(exts) != 0) { 191726066096Sdjm if ((ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0 || 191826066096Sdjm (ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0) { 191926066096Sdjm sshbuf_reset(key->cert->extensions); 1920811ca2d4Sdjm ret = SSH_ERR_INVALID_FORMAT; 1921811ca2d4Sdjm goto out; 1922811ca2d4Sdjm } 1923811ca2d4Sdjm } 1924811ca2d4Sdjm 192526066096Sdjm /* Parse CA key and check signature */ 19264736d833Sdjm if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) { 1927811ca2d4Sdjm ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; 1928811ca2d4Sdjm goto out; 1929811ca2d4Sdjm } 1930811ca2d4Sdjm if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) { 1931811ca2d4Sdjm ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; 1932811ca2d4Sdjm goto out; 1933811ca2d4Sdjm } 1934811ca2d4Sdjm if ((ret = sshkey_verify(key->cert->signature_key, sig, slen, 1935493ad5b0Sdjm sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0, NULL)) != 0) 1936811ca2d4Sdjm goto out; 193759ec76efSdjm if ((ret = sshkey_get_sigtype(sig, slen, 193859ec76efSdjm &key->cert->signature_type)) != 0) 19390ff10372Sdjm goto out; 1940811ca2d4Sdjm 194126066096Sdjm /* Success */ 194226066096Sdjm ret = 0; 1943811ca2d4Sdjm out: 19444736d833Sdjm sshbuf_free(ca); 194526066096Sdjm sshbuf_free(crit); 194626066096Sdjm sshbuf_free(exts); 194726066096Sdjm sshbuf_free(principals); 1948811ca2d4Sdjm free(sig); 1949811ca2d4Sdjm return ret; 1950811ca2d4Sdjm } 1951811ca2d4Sdjm 19525958b96bSdjm int 1953c8d92406Sdjm sshkey_deserialize_sk(struct sshbuf *b, struct sshkey *key) 19547c94020aSdjm { 1955c8d92406Sdjm /* Parse additional security-key application string */ 1956c8d92406Sdjm if (sshbuf_get_cstring(b, &key->sk_application, NULL) != 0) 1957c8d92406Sdjm return SSH_ERR_INVALID_FORMAT; 19587c94020aSdjm return 0; 19597c94020aSdjm } 19607c94020aSdjm 19617c94020aSdjm static int 19624736d833Sdjm sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, 19634736d833Sdjm int allow_cert) 1964811ca2d4Sdjm { 1965b1aa73bdSdjm int type, ret = SSH_ERR_INTERNAL_ERROR; 1966c8d92406Sdjm char *ktype = NULL; 1967811ca2d4Sdjm struct sshkey *key = NULL; 19684736d833Sdjm struct sshbuf *copy; 1969c8d92406Sdjm const struct sshkey_impl *impl; 1970811ca2d4Sdjm 1971811ca2d4Sdjm #ifdef DEBUG_PK /* XXX */ 19724736d833Sdjm sshbuf_dump(b, stderr); 1973811ca2d4Sdjm #endif 1974d9171e4cSdjm if (keyp != NULL) 1975811ca2d4Sdjm *keyp = NULL; 19764736d833Sdjm if ((copy = sshbuf_fromb(b)) == NULL) { 19774736d833Sdjm ret = SSH_ERR_ALLOC_FAIL; 19784736d833Sdjm goto out; 19794736d833Sdjm } 1980811ca2d4Sdjm if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { 1981811ca2d4Sdjm ret = SSH_ERR_INVALID_FORMAT; 1982811ca2d4Sdjm goto out; 1983811ca2d4Sdjm } 1984811ca2d4Sdjm 1985811ca2d4Sdjm type = sshkey_type_from_name(ktype); 1986811ca2d4Sdjm if (!allow_cert && sshkey_type_is_cert(type)) { 1987811ca2d4Sdjm ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; 1988811ca2d4Sdjm goto out; 1989811ca2d4Sdjm } 1990c8d92406Sdjm if ((impl = sshkey_impl_from_type(type)) == NULL) { 1991811ca2d4Sdjm ret = SSH_ERR_KEY_TYPE_UNKNOWN; 1992811ca2d4Sdjm goto out; 1993811ca2d4Sdjm } 1994c8d92406Sdjm if ((key = sshkey_new(type)) == NULL) { 1995c8d92406Sdjm ret = SSH_ERR_ALLOC_FAIL; 1996c8d92406Sdjm goto out; 1997c8d92406Sdjm } 1998c8d92406Sdjm if (sshkey_type_is_cert(type)) { 19990a3ea9a3Sjsg /* Skip nonce that precedes all certificates */ 2000c8d92406Sdjm if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { 2001c8d92406Sdjm ret = SSH_ERR_INVALID_FORMAT; 2002c8d92406Sdjm goto out; 2003c8d92406Sdjm } 2004c8d92406Sdjm } 2005c8d92406Sdjm if ((ret = impl->funcs->deserialize_public(ktype, b, key)) != 0) 2006c8d92406Sdjm goto out; 2007811ca2d4Sdjm 2008811ca2d4Sdjm /* Parse certificate potion */ 20094736d833Sdjm if (sshkey_is_cert(key) && (ret = cert_parse(b, key, copy)) != 0) 2010811ca2d4Sdjm goto out; 2011811ca2d4Sdjm 2012811ca2d4Sdjm if (key != NULL && sshbuf_len(b) != 0) { 2013811ca2d4Sdjm ret = SSH_ERR_INVALID_FORMAT; 2014811ca2d4Sdjm goto out; 2015811ca2d4Sdjm } 2016811ca2d4Sdjm ret = 0; 2017d9171e4cSdjm if (keyp != NULL) { 2018811ca2d4Sdjm *keyp = key; 2019811ca2d4Sdjm key = NULL; 2020d9171e4cSdjm } 2021811ca2d4Sdjm out: 20224736d833Sdjm sshbuf_free(copy); 2023811ca2d4Sdjm sshkey_free(key); 2024811ca2d4Sdjm free(ktype); 2025811ca2d4Sdjm return ret; 2026811ca2d4Sdjm } 2027811ca2d4Sdjm 2028811ca2d4Sdjm int 2029811ca2d4Sdjm sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp) 2030811ca2d4Sdjm { 20314736d833Sdjm struct sshbuf *b; 20324736d833Sdjm int r; 20334736d833Sdjm 20344736d833Sdjm if ((b = sshbuf_from(blob, blen)) == NULL) 20354736d833Sdjm return SSH_ERR_ALLOC_FAIL; 20364736d833Sdjm r = sshkey_from_blob_internal(b, keyp, 1); 20374736d833Sdjm sshbuf_free(b); 20384736d833Sdjm return r; 20394736d833Sdjm } 20404736d833Sdjm 20414736d833Sdjm int 20424736d833Sdjm sshkey_fromb(struct sshbuf *b, struct sshkey **keyp) 20434736d833Sdjm { 20444736d833Sdjm return sshkey_from_blob_internal(b, keyp, 1); 20454736d833Sdjm } 20464736d833Sdjm 20474736d833Sdjm int 20484736d833Sdjm sshkey_froms(struct sshbuf *buf, struct sshkey **keyp) 20494736d833Sdjm { 20504736d833Sdjm struct sshbuf *b; 20514736d833Sdjm int r; 20524736d833Sdjm 20534736d833Sdjm if ((r = sshbuf_froms(buf, &b)) != 0) 20544736d833Sdjm return r; 20554736d833Sdjm r = sshkey_from_blob_internal(b, keyp, 1); 20564736d833Sdjm sshbuf_free(b); 20574736d833Sdjm return r; 2058811ca2d4Sdjm } 2059811ca2d4Sdjm 206059ec76efSdjm int 206159ec76efSdjm sshkey_get_sigtype(const u_char *sig, size_t siglen, char **sigtypep) 2062ee642ea0Sdjm { 2063ee642ea0Sdjm int r; 2064ee642ea0Sdjm struct sshbuf *b = NULL; 2065ee642ea0Sdjm char *sigtype = NULL; 2066ee642ea0Sdjm 2067ee642ea0Sdjm if (sigtypep != NULL) 2068ee642ea0Sdjm *sigtypep = NULL; 2069ee642ea0Sdjm if ((b = sshbuf_from(sig, siglen)) == NULL) 2070ee642ea0Sdjm return SSH_ERR_ALLOC_FAIL; 2071ee642ea0Sdjm if ((r = sshbuf_get_cstring(b, &sigtype, NULL)) != 0) 2072ee642ea0Sdjm goto out; 2073ee642ea0Sdjm /* success */ 2074ee642ea0Sdjm if (sigtypep != NULL) { 2075ee642ea0Sdjm *sigtypep = sigtype; 2076ee642ea0Sdjm sigtype = NULL; 2077ee642ea0Sdjm } 2078ee642ea0Sdjm r = 0; 2079ee642ea0Sdjm out: 2080ee642ea0Sdjm free(sigtype); 2081ee642ea0Sdjm sshbuf_free(b); 2082ee642ea0Sdjm return r; 2083ee642ea0Sdjm } 2084ee642ea0Sdjm 208538a44c4dSdjm /* 20864668e1f3Sdjm * 20874668e1f3Sdjm * Checks whether a certificate's signature type is allowed. 20884668e1f3Sdjm * Returns 0 (success) if the certificate signature type appears in the 20894668e1f3Sdjm * "allowed" pattern-list, or the key is not a certificate to begin with. 20904668e1f3Sdjm * Otherwise returns a ssherr.h code. 20914668e1f3Sdjm */ 20924668e1f3Sdjm int 20934668e1f3Sdjm sshkey_check_cert_sigtype(const struct sshkey *key, const char *allowed) 20944668e1f3Sdjm { 20954668e1f3Sdjm if (key == NULL || allowed == NULL) 20964668e1f3Sdjm return SSH_ERR_INVALID_ARGUMENT; 20974668e1f3Sdjm if (!sshkey_type_is_cert(key->type)) 20984668e1f3Sdjm return 0; 20994668e1f3Sdjm if (key->cert == NULL || key->cert->signature_type == NULL) 21004668e1f3Sdjm return SSH_ERR_INVALID_ARGUMENT; 21014668e1f3Sdjm if (match_pattern_list(key->cert->signature_type, allowed, 0) != 1) 21024668e1f3Sdjm return SSH_ERR_SIGN_ALG_UNSUPPORTED; 21034668e1f3Sdjm return 0; 21044668e1f3Sdjm } 21054668e1f3Sdjm 21064668e1f3Sdjm /* 210738a44c4dSdjm * Returns the expected signature algorithm for a given public key algorithm. 210838a44c4dSdjm */ 2109d443285fSdjm const char * 2110d443285fSdjm sshkey_sigalg_by_name(const char *name) 211138a44c4dSdjm { 21129c1667dbSdjm const struct sshkey_impl *impl; 21139c1667dbSdjm int i; 211438a44c4dSdjm 21159c1667dbSdjm for (i = 0; keyimpls[i] != NULL; i++) { 21169c1667dbSdjm impl = keyimpls[i]; 21179c1667dbSdjm if (strcmp(impl->name, name) != 0) 211838a44c4dSdjm continue; 21199c1667dbSdjm if (impl->sigalg != NULL) 21209c1667dbSdjm return impl->sigalg; 21219c1667dbSdjm if (!impl->cert) 21229c1667dbSdjm return impl->name; 212338a44c4dSdjm return sshkey_ssh_name_from_type_nid( 21249c1667dbSdjm sshkey_type_plain(impl->type), impl->nid); 212538a44c4dSdjm } 212638a44c4dSdjm return NULL; 212738a44c4dSdjm } 212838a44c4dSdjm 212938a44c4dSdjm /* 213038a44c4dSdjm * Verifies that the signature algorithm appearing inside the signature blob 213138a44c4dSdjm * matches that which was requested. 213238a44c4dSdjm */ 213338a44c4dSdjm int 213438a44c4dSdjm sshkey_check_sigtype(const u_char *sig, size_t siglen, 213538a44c4dSdjm const char *requested_alg) 213638a44c4dSdjm { 213738a44c4dSdjm const char *expected_alg; 213838a44c4dSdjm char *sigtype = NULL; 213938a44c4dSdjm int r; 214038a44c4dSdjm 214138a44c4dSdjm if (requested_alg == NULL) 214238a44c4dSdjm return 0; 2143d443285fSdjm if ((expected_alg = sshkey_sigalg_by_name(requested_alg)) == NULL) 214438a44c4dSdjm return SSH_ERR_INVALID_ARGUMENT; 214559ec76efSdjm if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) 214638a44c4dSdjm return r; 214738a44c4dSdjm r = strcmp(expected_alg, sigtype) == 0; 214838a44c4dSdjm free(sigtype); 214938a44c4dSdjm return r ? 0 : SSH_ERR_SIGN_ALG_UNSUPPORTED; 215038a44c4dSdjm } 215138a44c4dSdjm 2152ee642ea0Sdjm int 2153707316f9Sdjm sshkey_sign(struct sshkey *key, 2154811ca2d4Sdjm u_char **sigp, size_t *lenp, 2155e3a62e69Sdjm const u_char *data, size_t datalen, 21561f63d3c4Sdjm const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) 2157811ca2d4Sdjm { 2158707316f9Sdjm int was_shielded = sshkey_is_shielded(key); 2159707316f9Sdjm int r2, r = SSH_ERR_INTERNAL_ERROR; 2160c5c174faSdjm const struct sshkey_impl *impl; 2161707316f9Sdjm 2162811ca2d4Sdjm if (sigp != NULL) 2163811ca2d4Sdjm *sigp = NULL; 2164811ca2d4Sdjm if (lenp != NULL) 2165811ca2d4Sdjm *lenp = 0; 2166811ca2d4Sdjm if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) 2167811ca2d4Sdjm return SSH_ERR_INVALID_ARGUMENT; 2168c5c174faSdjm if ((impl = sshkey_impl_from_key(key)) == NULL) 2169c5c174faSdjm return SSH_ERR_KEY_TYPE_UNKNOWN; 2170707316f9Sdjm if ((r = sshkey_unshield_private(key)) != 0) 2171707316f9Sdjm return r; 2172c5c174faSdjm if (sshkey_is_sk(key)) { 21734852100aSdjm r = sshsk_sign(sk_provider, key, sigp, lenp, data, 21741f63d3c4Sdjm datalen, compat, sk_pin); 2175c5c174faSdjm } else { 2176c5c174faSdjm if (impl->funcs->sign == NULL) 2177c5c174faSdjm r = SSH_ERR_SIGN_ALG_UNSUPPORTED; 2178c5c174faSdjm else { 2179c5c174faSdjm r = impl->funcs->sign(key, sigp, lenp, data, datalen, 2180c5c174faSdjm alg, sk_provider, sk_pin, compat); 2181c5c174faSdjm } 2182811ca2d4Sdjm } 2183707316f9Sdjm if (was_shielded && (r2 = sshkey_shield_private(key)) != 0) 2184707316f9Sdjm return r2; 2185707316f9Sdjm return r; 2186811ca2d4Sdjm } 2187811ca2d4Sdjm 2188811ca2d4Sdjm /* 2189811ca2d4Sdjm * ssh_key_verify returns 0 for a correct signature and < 0 on error. 219028eeb9e1Sdjm * If "alg" specified, then the signature must use that algorithm. 2191811ca2d4Sdjm */ 2192811ca2d4Sdjm int 2193811ca2d4Sdjm sshkey_verify(const struct sshkey *key, 2194811ca2d4Sdjm const u_char *sig, size_t siglen, 2195493ad5b0Sdjm const u_char *data, size_t dlen, const char *alg, u_int compat, 2196493ad5b0Sdjm struct sshkey_sig_details **detailsp) 2197811ca2d4Sdjm { 2198c5c174faSdjm const struct sshkey_impl *impl; 2199c5c174faSdjm 2200493ad5b0Sdjm if (detailsp != NULL) 2201493ad5b0Sdjm *detailsp = NULL; 2202d4faa3dfSdjm if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE) 2203811ca2d4Sdjm return SSH_ERR_INVALID_ARGUMENT; 2204c5c174faSdjm if ((impl = sshkey_impl_from_key(key)) == NULL) 2205811ca2d4Sdjm return SSH_ERR_KEY_TYPE_UNKNOWN; 2206c5c174faSdjm return impl->funcs->verify(key, sig, siglen, data, dlen, 2207c5c174faSdjm alg, compat, detailsp); 2208811ca2d4Sdjm } 2209811ca2d4Sdjm 2210811ca2d4Sdjm /* Convert a plain key to their _CERT equivalent */ 2211811ca2d4Sdjm int 22125e456000Sdjm sshkey_to_certified(struct sshkey *k) 2213811ca2d4Sdjm { 2214811ca2d4Sdjm int newtype; 2215811ca2d4Sdjm 221687f32bb5Sdjm if ((newtype = sshkey_type_certified(k->type)) == -1) 2217811ca2d4Sdjm return SSH_ERR_INVALID_ARGUMENT; 2218811ca2d4Sdjm if ((k->cert = cert_new()) == NULL) 2219811ca2d4Sdjm return SSH_ERR_ALLOC_FAIL; 2220811ca2d4Sdjm k->type = newtype; 2221811ca2d4Sdjm return 0; 2222811ca2d4Sdjm } 2223811ca2d4Sdjm 2224811ca2d4Sdjm /* Convert a certificate to its raw key equivalent */ 2225811ca2d4Sdjm int 2226811ca2d4Sdjm sshkey_drop_cert(struct sshkey *k) 2227811ca2d4Sdjm { 2228811ca2d4Sdjm if (!sshkey_type_is_cert(k->type)) 2229811ca2d4Sdjm return SSH_ERR_KEY_TYPE_UNKNOWN; 2230811ca2d4Sdjm cert_free(k->cert); 2231811ca2d4Sdjm k->cert = NULL; 2232811ca2d4Sdjm k->type = sshkey_type_plain(k->type); 2233811ca2d4Sdjm return 0; 2234811ca2d4Sdjm } 2235811ca2d4Sdjm 2236811ca2d4Sdjm /* Sign a certified key, (re-)generating the signed certblob. */ 2237811ca2d4Sdjm int 22388705e2e1Sdjm sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, 22391f63d3c4Sdjm const char *sk_provider, const char *sk_pin, 22401f63d3c4Sdjm sshkey_certify_signer *signer, void *signer_ctx) 2241811ca2d4Sdjm { 224287f32bb5Sdjm const struct sshkey_impl *impl; 2243811ca2d4Sdjm struct sshbuf *principals = NULL; 2244811ca2d4Sdjm u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; 2245811ca2d4Sdjm size_t i, ca_len, sig_len; 2246811ca2d4Sdjm int ret = SSH_ERR_INTERNAL_ERROR; 22470ff10372Sdjm struct sshbuf *cert = NULL; 22480ff10372Sdjm char *sigtype = NULL; 2249811ca2d4Sdjm 2250811ca2d4Sdjm if (k == NULL || k->cert == NULL || 2251811ca2d4Sdjm k->cert->certblob == NULL || ca == NULL) 2252811ca2d4Sdjm return SSH_ERR_INVALID_ARGUMENT; 2253811ca2d4Sdjm if (!sshkey_is_cert(k)) 2254811ca2d4Sdjm return SSH_ERR_KEY_TYPE_UNKNOWN; 2255811ca2d4Sdjm if (!sshkey_type_is_valid_ca(ca->type)) 2256811ca2d4Sdjm return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; 225787f32bb5Sdjm if ((impl = sshkey_impl_from_key(k)) == NULL) 225887f32bb5Sdjm return SSH_ERR_INTERNAL_ERROR; 2259811ca2d4Sdjm 22600ff10372Sdjm /* 22610ff10372Sdjm * If no alg specified as argument but a signature_type was set, 22620ff10372Sdjm * then prefer that. If both were specified, then they must match. 22630ff10372Sdjm */ 22640ff10372Sdjm if (alg == NULL) 22650ff10372Sdjm alg = k->cert->signature_type; 22660ff10372Sdjm else if (k->cert->signature_type != NULL && 22670ff10372Sdjm strcmp(alg, k->cert->signature_type) != 0) 22680ff10372Sdjm return SSH_ERR_INVALID_ARGUMENT; 22690ff10372Sdjm 227082dade67Sdjm /* 227182dade67Sdjm * If no signing algorithm or signature_type was specified and we're 227282dade67Sdjm * using a RSA key, then default to a good signature algorithm. 227382dade67Sdjm */ 227482dade67Sdjm if (alg == NULL && ca->type == KEY_RSA) 227582dade67Sdjm alg = "rsa-sha2-512"; 227682dade67Sdjm 2277811ca2d4Sdjm if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0) 2278811ca2d4Sdjm return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; 2279811ca2d4Sdjm 2280811ca2d4Sdjm cert = k->cert->certblob; /* for readability */ 2281811ca2d4Sdjm sshbuf_reset(cert); 2282811ca2d4Sdjm if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0) 2283811ca2d4Sdjm goto out; 2284811ca2d4Sdjm 2285811ca2d4Sdjm /* -v01 certs put nonce first */ 2286811ca2d4Sdjm arc4random_buf(&nonce, sizeof(nonce)); 2287811ca2d4Sdjm if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0) 2288811ca2d4Sdjm goto out; 2289811ca2d4Sdjm 229087f32bb5Sdjm /* Public key next */ 229187f32bb5Sdjm if ((ret = impl->funcs->serialize_public(k, cert, 229287f32bb5Sdjm SSHKEY_SERIALIZE_DEFAULT)) != 0) 2293811ca2d4Sdjm goto out; 2294811ca2d4Sdjm 229587f32bb5Sdjm /* Then remaining cert fields */ 22965e456000Sdjm if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0 || 22975e456000Sdjm (ret = sshbuf_put_u32(cert, k->cert->type)) != 0 || 2298811ca2d4Sdjm (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0) 2299811ca2d4Sdjm goto out; 2300811ca2d4Sdjm 2301811ca2d4Sdjm if ((principals = sshbuf_new()) == NULL) { 2302811ca2d4Sdjm ret = SSH_ERR_ALLOC_FAIL; 2303811ca2d4Sdjm goto out; 2304811ca2d4Sdjm } 2305811ca2d4Sdjm for (i = 0; i < k->cert->nprincipals; i++) { 2306811ca2d4Sdjm if ((ret = sshbuf_put_cstring(principals, 2307811ca2d4Sdjm k->cert->principals[i])) != 0) 2308811ca2d4Sdjm goto out; 2309811ca2d4Sdjm } 2310811ca2d4Sdjm if ((ret = sshbuf_put_stringb(cert, principals)) != 0 || 2311811ca2d4Sdjm (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 || 2312811ca2d4Sdjm (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 || 23135e456000Sdjm (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0 || 23145e456000Sdjm (ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0 || 23155e456000Sdjm (ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */ 2316811ca2d4Sdjm (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0) 2317811ca2d4Sdjm goto out; 2318811ca2d4Sdjm 2319811ca2d4Sdjm /* Sign the whole mess */ 23208705e2e1Sdjm if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), 23211f63d3c4Sdjm sshbuf_len(cert), alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) 2322811ca2d4Sdjm goto out; 23230ff10372Sdjm /* Check and update signature_type against what was actually used */ 232459ec76efSdjm if ((ret = sshkey_get_sigtype(sig_blob, sig_len, &sigtype)) != 0) 23250ff10372Sdjm goto out; 23260ff10372Sdjm if (alg != NULL && strcmp(alg, sigtype) != 0) { 23270ff10372Sdjm ret = SSH_ERR_SIGN_ALG_UNSUPPORTED; 23280ff10372Sdjm goto out; 23290ff10372Sdjm } 23300ff10372Sdjm if (k->cert->signature_type == NULL) { 23310ff10372Sdjm k->cert->signature_type = sigtype; 23320ff10372Sdjm sigtype = NULL; 23330ff10372Sdjm } 2334811ca2d4Sdjm /* Append signature and we are done */ 2335811ca2d4Sdjm if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0) 2336811ca2d4Sdjm goto out; 2337811ca2d4Sdjm ret = 0; 2338811ca2d4Sdjm out: 2339811ca2d4Sdjm if (ret != 0) 2340811ca2d4Sdjm sshbuf_reset(cert); 2341811ca2d4Sdjm free(sig_blob); 2342811ca2d4Sdjm free(ca_blob); 23430ff10372Sdjm free(sigtype); 2344811ca2d4Sdjm sshbuf_free(principals); 2345811ca2d4Sdjm return ret; 2346811ca2d4Sdjm } 2347811ca2d4Sdjm 23488705e2e1Sdjm static int 2349707316f9Sdjm default_key_sign(struct sshkey *key, u_char **sigp, size_t *lenp, 23508705e2e1Sdjm const u_char *data, size_t datalen, 23511f63d3c4Sdjm const char *alg, const char *sk_provider, const char *sk_pin, 23521f63d3c4Sdjm u_int compat, void *ctx) 23538705e2e1Sdjm { 23548705e2e1Sdjm if (ctx != NULL) 23558705e2e1Sdjm return SSH_ERR_INVALID_ARGUMENT; 2356e3a62e69Sdjm return sshkey_sign(key, sigp, lenp, data, datalen, alg, 23571f63d3c4Sdjm sk_provider, sk_pin, compat); 23588705e2e1Sdjm } 23598705e2e1Sdjm 23608705e2e1Sdjm int 2361e3a62e69Sdjm sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg, 23621f63d3c4Sdjm const char *sk_provider, const char *sk_pin) 23638705e2e1Sdjm { 23641f63d3c4Sdjm return sshkey_certify_custom(k, ca, alg, sk_provider, sk_pin, 2365e3a62e69Sdjm default_key_sign, NULL); 23668705e2e1Sdjm } 23678705e2e1Sdjm 2368811ca2d4Sdjm int 2369811ca2d4Sdjm sshkey_cert_check_authority(const struct sshkey *k, 2370fb196569Sdjm int want_host, int require_principal, int wildcard_pattern, 2371c31299abSdjm uint64_t verify_time, const char *name, const char **reason) 2372811ca2d4Sdjm { 2373811ca2d4Sdjm u_int i, principal_matches; 2374811ca2d4Sdjm 23754e349e12Smarkus if (reason == NULL) 23764e349e12Smarkus return SSH_ERR_INVALID_ARGUMENT; 2377fb196569Sdjm if (!sshkey_is_cert(k)) { 2378fb196569Sdjm *reason = "Key is not a certificate"; 2379fb196569Sdjm return SSH_ERR_KEY_CERT_INVALID; 2380fb196569Sdjm } 2381811ca2d4Sdjm if (want_host) { 2382811ca2d4Sdjm if (k->cert->type != SSH2_CERT_TYPE_HOST) { 2383811ca2d4Sdjm *reason = "Certificate invalid: not a host certificate"; 2384811ca2d4Sdjm return SSH_ERR_KEY_CERT_INVALID; 2385811ca2d4Sdjm } 2386811ca2d4Sdjm } else { 2387811ca2d4Sdjm if (k->cert->type != SSH2_CERT_TYPE_USER) { 2388811ca2d4Sdjm *reason = "Certificate invalid: not a user certificate"; 2389811ca2d4Sdjm return SSH_ERR_KEY_CERT_INVALID; 2390811ca2d4Sdjm } 2391811ca2d4Sdjm } 2392c31299abSdjm if (verify_time < k->cert->valid_after) { 2393811ca2d4Sdjm *reason = "Certificate invalid: not yet valid"; 2394811ca2d4Sdjm return SSH_ERR_KEY_CERT_INVALID; 2395811ca2d4Sdjm } 2396c31299abSdjm if (verify_time >= k->cert->valid_before) { 2397811ca2d4Sdjm *reason = "Certificate invalid: expired"; 2398811ca2d4Sdjm return SSH_ERR_KEY_CERT_INVALID; 2399811ca2d4Sdjm } 2400811ca2d4Sdjm if (k->cert->nprincipals == 0) { 2401811ca2d4Sdjm if (require_principal) { 2402811ca2d4Sdjm *reason = "Certificate lacks principal list"; 2403811ca2d4Sdjm return SSH_ERR_KEY_CERT_INVALID; 2404811ca2d4Sdjm } 2405811ca2d4Sdjm } else if (name != NULL) { 2406811ca2d4Sdjm principal_matches = 0; 2407811ca2d4Sdjm for (i = 0; i < k->cert->nprincipals; i++) { 2408fb196569Sdjm if (wildcard_pattern) { 2409fb196569Sdjm if (match_pattern(k->cert->principals[i], 2410fb196569Sdjm name)) { 2411fb196569Sdjm principal_matches = 1; 2412fb196569Sdjm break; 2413fb196569Sdjm } 2414fb196569Sdjm } else if (strcmp(name, k->cert->principals[i]) == 0) { 2415811ca2d4Sdjm principal_matches = 1; 2416811ca2d4Sdjm break; 2417811ca2d4Sdjm } 2418811ca2d4Sdjm } 2419811ca2d4Sdjm if (!principal_matches) { 2420811ca2d4Sdjm *reason = "Certificate invalid: name is not a listed " 2421811ca2d4Sdjm "principal"; 2422811ca2d4Sdjm return SSH_ERR_KEY_CERT_INVALID; 2423811ca2d4Sdjm } 2424811ca2d4Sdjm } 2425811ca2d4Sdjm return 0; 2426811ca2d4Sdjm } 2427811ca2d4Sdjm 2428fb196569Sdjm int 2429c31299abSdjm sshkey_cert_check_authority_now(const struct sshkey *k, 2430c31299abSdjm int want_host, int require_principal, int wildcard_pattern, 2431c31299abSdjm const char *name, const char **reason) 2432c31299abSdjm { 2433c31299abSdjm time_t now; 2434c31299abSdjm 2435c31299abSdjm if ((now = time(NULL)) < 0) { 2436c31299abSdjm /* yikes - system clock before epoch! */ 2437c31299abSdjm *reason = "Certificate invalid: not yet valid"; 2438c31299abSdjm return SSH_ERR_KEY_CERT_INVALID; 2439c31299abSdjm } 2440c31299abSdjm return sshkey_cert_check_authority(k, want_host, require_principal, 2441c31299abSdjm wildcard_pattern, (uint64_t)now, name, reason); 2442c31299abSdjm } 2443c31299abSdjm 2444c31299abSdjm int 2445fb196569Sdjm sshkey_cert_check_host(const struct sshkey *key, const char *host, 2446fb196569Sdjm int wildcard_principals, const char *ca_sign_algorithms, 2447fb196569Sdjm const char **reason) 2448fb196569Sdjm { 2449fb196569Sdjm int r; 2450fb196569Sdjm 2451c31299abSdjm if ((r = sshkey_cert_check_authority_now(key, 1, 0, wildcard_principals, 2452fb196569Sdjm host, reason)) != 0) 2453fb196569Sdjm return r; 2454fb196569Sdjm if (sshbuf_len(key->cert->critical) != 0) { 2455fb196569Sdjm *reason = "Certificate contains unsupported critical options"; 2456fb196569Sdjm return SSH_ERR_KEY_CERT_INVALID; 2457fb196569Sdjm } 2458fb196569Sdjm if (ca_sign_algorithms != NULL && 2459fb196569Sdjm (r = sshkey_check_cert_sigtype(key, ca_sign_algorithms)) != 0) { 2460fb196569Sdjm *reason = "Certificate signed with disallowed algorithm"; 2461fb196569Sdjm return SSH_ERR_KEY_CERT_INVALID; 2462fb196569Sdjm } 2463fb196569Sdjm return 0; 2464fb196569Sdjm } 2465fb196569Sdjm 2466e0afdfdeSdjm size_t 2467e0afdfdeSdjm sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l) 2468e0afdfdeSdjm { 2469c36ec5d6Sdtucker char from[32], to[32], ret[128]; 2470e0afdfdeSdjm 2471e0afdfdeSdjm *from = *to = '\0'; 2472e0afdfdeSdjm if (cert->valid_after == 0 && 2473e0afdfdeSdjm cert->valid_before == 0xffffffffffffffffULL) 2474e0afdfdeSdjm return strlcpy(s, "forever", l); 2475e0afdfdeSdjm 24768bf4cf37Sdtucker if (cert->valid_after != 0) 24778bf4cf37Sdtucker format_absolute_time(cert->valid_after, from, sizeof(from)); 24788bf4cf37Sdtucker if (cert->valid_before != 0xffffffffffffffffULL) 24798bf4cf37Sdtucker format_absolute_time(cert->valid_before, to, sizeof(to)); 2480e0afdfdeSdjm 2481e0afdfdeSdjm if (cert->valid_after == 0) 2482e0afdfdeSdjm snprintf(ret, sizeof(ret), "before %s", to); 2483e0afdfdeSdjm else if (cert->valid_before == 0xffffffffffffffffULL) 2484e0afdfdeSdjm snprintf(ret, sizeof(ret), "after %s", from); 2485e0afdfdeSdjm else 2486e0afdfdeSdjm snprintf(ret, sizeof(ret), "from %s to %s", from, to); 2487e0afdfdeSdjm 2488e0afdfdeSdjm return strlcpy(s, ret, l); 2489e0afdfdeSdjm } 2490e0afdfdeSdjm 2491d03db38bSdjm /* Common serialization for FIDO private keys */ 2492d03db38bSdjm int 2493d03db38bSdjm sshkey_serialize_private_sk(const struct sshkey *key, struct sshbuf *b) 2494d03db38bSdjm { 2495d03db38bSdjm int r; 2496d03db38bSdjm 2497d03db38bSdjm if ((r = sshbuf_put_cstring(b, key->sk_application)) != 0 || 2498d03db38bSdjm (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || 2499d03db38bSdjm (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || 2500d03db38bSdjm (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) 2501d03db38bSdjm return r; 2502d03db38bSdjm 2503d03db38bSdjm return 0; 2504d03db38bSdjm } 2505d03db38bSdjm 2506811ca2d4Sdjm int 2507707316f9Sdjm sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, 2508a6be8e7cSmarkus enum sshkey_serialize_rep opts) 2509811ca2d4Sdjm { 2510811ca2d4Sdjm int r = SSH_ERR_INTERNAL_ERROR; 2511707316f9Sdjm int was_shielded = sshkey_is_shielded(key); 2512707316f9Sdjm struct sshbuf *b = NULL; 2513d03db38bSdjm const struct sshkey_impl *impl; 2514811ca2d4Sdjm 2515d03db38bSdjm if ((impl = sshkey_impl_from_key(key)) == NULL) 2516d03db38bSdjm return SSH_ERR_INTERNAL_ERROR; 2517707316f9Sdjm if ((r = sshkey_unshield_private(key)) != 0) 2518707316f9Sdjm return r; 2519707316f9Sdjm if ((b = sshbuf_new()) == NULL) 2520707316f9Sdjm return SSH_ERR_ALLOC_FAIL; 2521811ca2d4Sdjm if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0) 2522811ca2d4Sdjm goto out; 2523d03db38bSdjm if (sshkey_is_cert(key)) { 2524d03db38bSdjm if (key->cert == NULL || 2525d03db38bSdjm sshbuf_len(key->cert->certblob) == 0) { 2526811ca2d4Sdjm r = SSH_ERR_INVALID_ARGUMENT; 2527811ca2d4Sdjm goto out; 2528811ca2d4Sdjm } 2529d03db38bSdjm if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0) 2530811ca2d4Sdjm goto out; 2531811ca2d4Sdjm } 2532d03db38bSdjm if ((r = impl->funcs->serialize_private(key, b, opts)) != 0) 2533811ca2d4Sdjm goto out; 2534d03db38bSdjm 2535707316f9Sdjm /* 2536707316f9Sdjm * success (but we still need to append the output to buf after 2537707316f9Sdjm * possibly re-shielding the private key) 2538707316f9Sdjm */ 2539811ca2d4Sdjm r = 0; 2540811ca2d4Sdjm out: 2541707316f9Sdjm if (was_shielded) 2542707316f9Sdjm r = sshkey_shield_private(key); 2543707316f9Sdjm if (r == 0) 2544707316f9Sdjm r = sshbuf_putb(buf, b); 2545707316f9Sdjm sshbuf_free(b); 2546707316f9Sdjm 2547811ca2d4Sdjm return r; 2548811ca2d4Sdjm } 2549811ca2d4Sdjm 2550811ca2d4Sdjm int 2551707316f9Sdjm sshkey_private_serialize(struct sshkey *key, struct sshbuf *b) 2552a6be8e7cSmarkus { 2553a6be8e7cSmarkus return sshkey_private_serialize_opt(key, b, 2554a6be8e7cSmarkus SSHKEY_SERIALIZE_DEFAULT); 2555a6be8e7cSmarkus } 2556a6be8e7cSmarkus 2557a2c931d9Sdjm /* Shared deserialization of FIDO private key components */ 2558a2c931d9Sdjm int 2559a2c931d9Sdjm sshkey_private_deserialize_sk(struct sshbuf *buf, struct sshkey *k) 2560a2c931d9Sdjm { 2561a2c931d9Sdjm int r; 2562a2c931d9Sdjm 2563a2c931d9Sdjm if ((k->sk_key_handle = sshbuf_new()) == NULL || 2564a2c931d9Sdjm (k->sk_reserved = sshbuf_new()) == NULL) 2565a2c931d9Sdjm return SSH_ERR_ALLOC_FAIL; 2566a2c931d9Sdjm if ((r = sshbuf_get_cstring(buf, &k->sk_application, NULL)) != 0 || 2567a2c931d9Sdjm (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || 2568a2c931d9Sdjm (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || 2569a2c931d9Sdjm (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) 2570a2c931d9Sdjm return r; 2571a2c931d9Sdjm 2572a2c931d9Sdjm return 0; 2573a2c931d9Sdjm } 2574a2c931d9Sdjm 2575a6be8e7cSmarkus int 2576811ca2d4Sdjm sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) 2577811ca2d4Sdjm { 2578a2c931d9Sdjm const struct sshkey_impl *impl; 2579a2c931d9Sdjm char *tname = NULL; 25800ba312d8Sdjm char *expect_sk_application = NULL; 25810ba312d8Sdjm u_char *expect_ed25519_pk = NULL; 2582a2c931d9Sdjm struct sshkey *k = NULL; 2583a2c931d9Sdjm int type, r = SSH_ERR_INTERNAL_ERROR; 2584811ca2d4Sdjm 2585811ca2d4Sdjm if (kp != NULL) 2586811ca2d4Sdjm *kp = NULL; 2587811ca2d4Sdjm if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0) 2588811ca2d4Sdjm goto out; 2589811ca2d4Sdjm type = sshkey_type_from_name(tname); 2590dd305755Sdjm if (sshkey_type_is_cert(type)) { 2591dd305755Sdjm /* 2592dd305755Sdjm * Certificate key private keys begin with the certificate 2593dd305755Sdjm * itself. Make sure this matches the type of the enclosing 2594dd305755Sdjm * private key. 2595dd305755Sdjm */ 2596dd305755Sdjm if ((r = sshkey_froms(buf, &k)) != 0) 2597dd305755Sdjm goto out; 2598dd305755Sdjm if (k->type != type) { 2599dd305755Sdjm r = SSH_ERR_KEY_CERT_MISMATCH; 2600dd305755Sdjm goto out; 2601dd305755Sdjm } 2602dd305755Sdjm /* For ECDSA keys, the group must match too */ 2603dd305755Sdjm if (k->type == KEY_ECDSA && 2604dd305755Sdjm k->ecdsa_nid != sshkey_ecdsa_nid_from_name(tname)) { 2605dd305755Sdjm r = SSH_ERR_KEY_CERT_MISMATCH; 2606dd305755Sdjm goto out; 2607dd305755Sdjm } 26080ba312d8Sdjm /* 26090ba312d8Sdjm * Several fields are redundant between certificate and 26100ba312d8Sdjm * private key body, we require these to match. 26110ba312d8Sdjm */ 26120ba312d8Sdjm expect_sk_application = k->sk_application; 26130ba312d8Sdjm expect_ed25519_pk = k->ed25519_pk; 26140ba312d8Sdjm k->sk_application = NULL; 26150ba312d8Sdjm k->ed25519_pk = NULL; 2616a2c931d9Sdjm /* XXX xmss too or refactor */ 2617dd305755Sdjm } else { 2618253be06fSdjm if ((k = sshkey_new(type)) == NULL) { 2619811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 2620811ca2d4Sdjm goto out; 2621811ca2d4Sdjm } 2622dd305755Sdjm } 2623a2c931d9Sdjm if ((impl = sshkey_impl_from_type(type)) == NULL) { 2624a2c931d9Sdjm r = SSH_ERR_INTERNAL_ERROR; 26257c94020aSdjm goto out; 26267c94020aSdjm } 2627a2c931d9Sdjm if ((r = impl->funcs->deserialize_private(tname, buf, k)) != 0) 26287c94020aSdjm goto out; 2629a2c931d9Sdjm 2630a2c931d9Sdjm /* XXX xmss too or refactor */ 26310ba312d8Sdjm if ((expect_sk_application != NULL && (k->sk_application == NULL || 26320ba312d8Sdjm strcmp(expect_sk_application, k->sk_application) != 0)) || 26330ba312d8Sdjm (expect_ed25519_pk != NULL && (k->ed25519_pk == NULL || 26340ba312d8Sdjm memcmp(expect_ed25519_pk, k->ed25519_pk, ED25519_PK_SZ) != 0))) { 26350ba312d8Sdjm r = SSH_ERR_KEY_CERT_MISMATCH; 26360ba312d8Sdjm goto out; 26370ba312d8Sdjm } 2638811ca2d4Sdjm /* success */ 2639811ca2d4Sdjm r = 0; 2640811ca2d4Sdjm if (kp != NULL) { 2641811ca2d4Sdjm *kp = k; 2642811ca2d4Sdjm k = NULL; 2643811ca2d4Sdjm } 2644811ca2d4Sdjm out: 2645811ca2d4Sdjm free(tname); 2646811ca2d4Sdjm sshkey_free(k); 26470ba312d8Sdjm free(expect_sk_application); 26480ba312d8Sdjm free(expect_ed25519_pk); 2649811ca2d4Sdjm return r; 2650811ca2d4Sdjm } 2651811ca2d4Sdjm 2652811ca2d4Sdjm #ifdef WITH_OPENSSL 2653811ca2d4Sdjm int 2654811ca2d4Sdjm sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) 2655811ca2d4Sdjm { 2656811ca2d4Sdjm EC_POINT *nq = NULL; 265707b718edSdjm BIGNUM *order = NULL, *x = NULL, *y = NULL, *tmp = NULL; 2658811ca2d4Sdjm int ret = SSH_ERR_KEY_INVALID_EC_VALUE; 2659811ca2d4Sdjm 26602959d888Sdjm /* 26612959d888Sdjm * NB. This assumes OpenSSL has already verified that the public 26622959d888Sdjm * point lies on the curve. This is done by EC_POINT_oct2point() 26632959d888Sdjm * implicitly calling EC_POINT_is_on_curve(). If this code is ever 26642959d888Sdjm * reachable with public points not unmarshalled using 26652959d888Sdjm * EC_POINT_oct2point then the caller will need to explicitly check. 26662959d888Sdjm */ 26672959d888Sdjm 2668811ca2d4Sdjm /* Q != infinity */ 2669811ca2d4Sdjm if (EC_POINT_is_at_infinity(group, public)) 2670811ca2d4Sdjm goto out; 2671811ca2d4Sdjm 267207b718edSdjm if ((x = BN_new()) == NULL || 267307b718edSdjm (y = BN_new()) == NULL || 267407b718edSdjm (order = BN_new()) == NULL || 267507b718edSdjm (tmp = BN_new()) == NULL) { 2676811ca2d4Sdjm ret = SSH_ERR_ALLOC_FAIL; 2677811ca2d4Sdjm goto out; 2678811ca2d4Sdjm } 2679811ca2d4Sdjm 2680811ca2d4Sdjm /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */ 268107b718edSdjm if (EC_GROUP_get_order(group, order, NULL) != 1 || 2682811ca2d4Sdjm EC_POINT_get_affine_coordinates_GFp(group, public, 268307b718edSdjm x, y, NULL) != 1) { 2684811ca2d4Sdjm ret = SSH_ERR_LIBCRYPTO_ERROR; 2685811ca2d4Sdjm goto out; 2686811ca2d4Sdjm } 2687811ca2d4Sdjm if (BN_num_bits(x) <= BN_num_bits(order) / 2 || 2688811ca2d4Sdjm BN_num_bits(y) <= BN_num_bits(order) / 2) 2689811ca2d4Sdjm goto out; 2690811ca2d4Sdjm 2691811ca2d4Sdjm /* nQ == infinity (n == order of subgroup) */ 2692811ca2d4Sdjm if ((nq = EC_POINT_new(group)) == NULL) { 2693811ca2d4Sdjm ret = SSH_ERR_ALLOC_FAIL; 2694811ca2d4Sdjm goto out; 2695811ca2d4Sdjm } 269607b718edSdjm if (EC_POINT_mul(group, nq, NULL, public, order, NULL) != 1) { 2697811ca2d4Sdjm ret = SSH_ERR_LIBCRYPTO_ERROR; 2698811ca2d4Sdjm goto out; 2699811ca2d4Sdjm } 2700811ca2d4Sdjm if (EC_POINT_is_at_infinity(group, nq) != 1) 2701811ca2d4Sdjm goto out; 2702811ca2d4Sdjm 2703811ca2d4Sdjm /* x < order - 1, y < order - 1 */ 2704811ca2d4Sdjm if (!BN_sub(tmp, order, BN_value_one())) { 2705811ca2d4Sdjm ret = SSH_ERR_LIBCRYPTO_ERROR; 2706811ca2d4Sdjm goto out; 2707811ca2d4Sdjm } 2708811ca2d4Sdjm if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0) 2709811ca2d4Sdjm goto out; 2710811ca2d4Sdjm ret = 0; 2711811ca2d4Sdjm out: 271207b718edSdjm BN_clear_free(x); 271307b718edSdjm BN_clear_free(y); 271407b718edSdjm BN_clear_free(order); 271507b718edSdjm BN_clear_free(tmp); 2716811ca2d4Sdjm EC_POINT_free(nq); 2717811ca2d4Sdjm return ret; 2718811ca2d4Sdjm } 2719811ca2d4Sdjm 2720811ca2d4Sdjm int 2721811ca2d4Sdjm sshkey_ec_validate_private(const EC_KEY *key) 2722811ca2d4Sdjm { 272307b718edSdjm BIGNUM *order = NULL, *tmp = NULL; 2724811ca2d4Sdjm int ret = SSH_ERR_KEY_INVALID_EC_VALUE; 2725811ca2d4Sdjm 272607b718edSdjm if ((order = BN_new()) == NULL || (tmp = BN_new()) == NULL) { 2727811ca2d4Sdjm ret = SSH_ERR_ALLOC_FAIL; 2728811ca2d4Sdjm goto out; 2729811ca2d4Sdjm } 2730811ca2d4Sdjm 2731811ca2d4Sdjm /* log2(private) > log2(order)/2 */ 273207b718edSdjm if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, NULL) != 1) { 2733811ca2d4Sdjm ret = SSH_ERR_LIBCRYPTO_ERROR; 2734811ca2d4Sdjm goto out; 2735811ca2d4Sdjm } 2736811ca2d4Sdjm if (BN_num_bits(EC_KEY_get0_private_key(key)) <= 2737811ca2d4Sdjm BN_num_bits(order) / 2) 2738811ca2d4Sdjm goto out; 2739811ca2d4Sdjm 2740811ca2d4Sdjm /* private < order - 1 */ 2741811ca2d4Sdjm if (!BN_sub(tmp, order, BN_value_one())) { 2742811ca2d4Sdjm ret = SSH_ERR_LIBCRYPTO_ERROR; 2743811ca2d4Sdjm goto out; 2744811ca2d4Sdjm } 2745811ca2d4Sdjm if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) 2746811ca2d4Sdjm goto out; 2747811ca2d4Sdjm ret = 0; 2748811ca2d4Sdjm out: 274907b718edSdjm BN_clear_free(order); 275007b718edSdjm BN_clear_free(tmp); 2751811ca2d4Sdjm return ret; 2752811ca2d4Sdjm } 2753811ca2d4Sdjm 2754811ca2d4Sdjm void 2755811ca2d4Sdjm sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) 2756811ca2d4Sdjm { 275707b718edSdjm BIGNUM *x = NULL, *y = NULL; 2758811ca2d4Sdjm 2759811ca2d4Sdjm if (point == NULL) { 2760811ca2d4Sdjm fputs("point=(NULL)\n", stderr); 2761811ca2d4Sdjm return; 2762811ca2d4Sdjm } 276307b718edSdjm if ((x = BN_new()) == NULL || (y = BN_new()) == NULL) { 276407b718edSdjm fprintf(stderr, "%s: BN_new failed\n", __func__); 276507b718edSdjm goto out; 2766811ca2d4Sdjm } 276707b718edSdjm if (EC_POINT_get_affine_coordinates_GFp(group, point, 276807b718edSdjm x, y, NULL) != 1) { 2769811ca2d4Sdjm fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n", 2770811ca2d4Sdjm __func__); 277107b718edSdjm goto out; 2772811ca2d4Sdjm } 2773811ca2d4Sdjm fputs("x=", stderr); 2774811ca2d4Sdjm BN_print_fp(stderr, x); 2775811ca2d4Sdjm fputs("\ny=", stderr); 2776811ca2d4Sdjm BN_print_fp(stderr, y); 2777811ca2d4Sdjm fputs("\n", stderr); 277807b718edSdjm out: 277907b718edSdjm BN_clear_free(x); 278007b718edSdjm BN_clear_free(y); 2781811ca2d4Sdjm } 2782811ca2d4Sdjm 2783811ca2d4Sdjm void 2784811ca2d4Sdjm sshkey_dump_ec_key(const EC_KEY *key) 2785811ca2d4Sdjm { 2786811ca2d4Sdjm const BIGNUM *exponent; 2787811ca2d4Sdjm 2788811ca2d4Sdjm sshkey_dump_ec_point(EC_KEY_get0_group(key), 2789811ca2d4Sdjm EC_KEY_get0_public_key(key)); 2790811ca2d4Sdjm fputs("exponent=", stderr); 2791811ca2d4Sdjm if ((exponent = EC_KEY_get0_private_key(key)) == NULL) 2792811ca2d4Sdjm fputs("(NULL)", stderr); 2793811ca2d4Sdjm else 2794811ca2d4Sdjm BN_print_fp(stderr, EC_KEY_get0_private_key(key)); 2795811ca2d4Sdjm fputs("\n", stderr); 2796811ca2d4Sdjm } 2797811ca2d4Sdjm #endif /* WITH_OPENSSL */ 2798811ca2d4Sdjm 2799811ca2d4Sdjm static int 2800707316f9Sdjm sshkey_private_to_blob2(struct sshkey *prv, struct sshbuf *blob, 2801811ca2d4Sdjm const char *passphrase, const char *comment, const char *ciphername, 2802811ca2d4Sdjm int rounds) 2803811ca2d4Sdjm { 280426066096Sdjm u_char *cp, *key = NULL, *pubkeyblob = NULL; 2805811ca2d4Sdjm u_char salt[SALT_LEN]; 2806811ca2d4Sdjm size_t i, pubkeylen, keylen, ivlen, blocksize, authlen; 2807811ca2d4Sdjm u_int check; 2808811ca2d4Sdjm int r = SSH_ERR_INTERNAL_ERROR; 280932a036abSdjm struct sshcipher_ctx *ciphercontext = NULL; 2810811ca2d4Sdjm const struct sshcipher *cipher; 2811811ca2d4Sdjm const char *kdfname = KDFNAME; 2812811ca2d4Sdjm struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL; 2813811ca2d4Sdjm 2814811ca2d4Sdjm if (rounds <= 0) 2815811ca2d4Sdjm rounds = DEFAULT_ROUNDS; 2816811ca2d4Sdjm if (passphrase == NULL || !strlen(passphrase)) { 2817811ca2d4Sdjm ciphername = "none"; 2818811ca2d4Sdjm kdfname = "none"; 2819811ca2d4Sdjm } else if (ciphername == NULL) 2820811ca2d4Sdjm ciphername = DEFAULT_CIPHERNAME; 2821811ca2d4Sdjm if ((cipher = cipher_by_name(ciphername)) == NULL) { 282276745bedSdjm r = SSH_ERR_INVALID_ARGUMENT; 2823811ca2d4Sdjm goto out; 2824811ca2d4Sdjm } 2825811ca2d4Sdjm 2826811ca2d4Sdjm if ((kdf = sshbuf_new()) == NULL || 2827811ca2d4Sdjm (encoded = sshbuf_new()) == NULL || 2828811ca2d4Sdjm (encrypted = sshbuf_new()) == NULL) { 2829811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 2830811ca2d4Sdjm goto out; 2831811ca2d4Sdjm } 2832811ca2d4Sdjm blocksize = cipher_blocksize(cipher); 2833811ca2d4Sdjm keylen = cipher_keylen(cipher); 2834811ca2d4Sdjm ivlen = cipher_ivlen(cipher); 2835811ca2d4Sdjm authlen = cipher_authlen(cipher); 2836811ca2d4Sdjm if ((key = calloc(1, keylen + ivlen)) == NULL) { 2837811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 2838811ca2d4Sdjm goto out; 2839811ca2d4Sdjm } 2840811ca2d4Sdjm if (strcmp(kdfname, "bcrypt") == 0) { 2841811ca2d4Sdjm arc4random_buf(salt, SALT_LEN); 2842811ca2d4Sdjm if (bcrypt_pbkdf(passphrase, strlen(passphrase), 2843811ca2d4Sdjm salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) { 2844811ca2d4Sdjm r = SSH_ERR_INVALID_ARGUMENT; 2845811ca2d4Sdjm goto out; 2846811ca2d4Sdjm } 2847811ca2d4Sdjm if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 || 2848811ca2d4Sdjm (r = sshbuf_put_u32(kdf, rounds)) != 0) 2849811ca2d4Sdjm goto out; 2850811ca2d4Sdjm } else if (strcmp(kdfname, "none") != 0) { 2851811ca2d4Sdjm /* Unsupported KDF type */ 2852811ca2d4Sdjm r = SSH_ERR_KEY_UNKNOWN_CIPHER; 2853811ca2d4Sdjm goto out; 2854811ca2d4Sdjm } 2855811ca2d4Sdjm if ((r = cipher_init(&ciphercontext, cipher, key, keylen, 2856811ca2d4Sdjm key + keylen, ivlen, 1)) != 0) 2857811ca2d4Sdjm goto out; 2858811ca2d4Sdjm 2859811ca2d4Sdjm if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 || 2860811ca2d4Sdjm (r = sshbuf_put_cstring(encoded, ciphername)) != 0 || 2861811ca2d4Sdjm (r = sshbuf_put_cstring(encoded, kdfname)) != 0 || 2862811ca2d4Sdjm (r = sshbuf_put_stringb(encoded, kdf)) != 0 || 2863811ca2d4Sdjm (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */ 2864811ca2d4Sdjm (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 || 2865811ca2d4Sdjm (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0) 2866811ca2d4Sdjm goto out; 2867811ca2d4Sdjm 2868811ca2d4Sdjm /* set up the buffer that will be encrypted */ 2869811ca2d4Sdjm 2870811ca2d4Sdjm /* Random check bytes */ 2871811ca2d4Sdjm check = arc4random(); 2872811ca2d4Sdjm if ((r = sshbuf_put_u32(encrypted, check)) != 0 || 2873811ca2d4Sdjm (r = sshbuf_put_u32(encrypted, check)) != 0) 2874811ca2d4Sdjm goto out; 2875811ca2d4Sdjm 2876811ca2d4Sdjm /* append private key and comment*/ 2877a6be8e7cSmarkus if ((r = sshkey_private_serialize_opt(prv, encrypted, 2878a6be8e7cSmarkus SSHKEY_SERIALIZE_FULL)) != 0 || 2879811ca2d4Sdjm (r = sshbuf_put_cstring(encrypted, comment)) != 0) 2880811ca2d4Sdjm goto out; 2881811ca2d4Sdjm 2882811ca2d4Sdjm /* padding */ 2883811ca2d4Sdjm i = 0; 2884811ca2d4Sdjm while (sshbuf_len(encrypted) % blocksize) { 2885811ca2d4Sdjm if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0) 2886811ca2d4Sdjm goto out; 2887811ca2d4Sdjm } 2888811ca2d4Sdjm 2889811ca2d4Sdjm /* length in destination buffer */ 2890811ca2d4Sdjm if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0) 2891811ca2d4Sdjm goto out; 2892811ca2d4Sdjm 2893811ca2d4Sdjm /* encrypt */ 2894811ca2d4Sdjm if ((r = sshbuf_reserve(encoded, 2895811ca2d4Sdjm sshbuf_len(encrypted) + authlen, &cp)) != 0) 2896811ca2d4Sdjm goto out; 289732a036abSdjm if ((r = cipher_crypt(ciphercontext, 0, cp, 2898811ca2d4Sdjm sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0) 2899811ca2d4Sdjm goto out; 2900811ca2d4Sdjm 2901811ca2d4Sdjm sshbuf_reset(blob); 2902bbb0e5b6Sdjm 2903bbb0e5b6Sdjm /* assemble uuencoded key */ 2904bbb0e5b6Sdjm if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0 || 2905bbb0e5b6Sdjm (r = sshbuf_dtob64(encoded, blob, 1)) != 0 || 2906bbb0e5b6Sdjm (r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0) 2907811ca2d4Sdjm goto out; 2908811ca2d4Sdjm 2909811ca2d4Sdjm /* success */ 2910811ca2d4Sdjm r = 0; 2911811ca2d4Sdjm 2912811ca2d4Sdjm out: 2913811ca2d4Sdjm sshbuf_free(kdf); 2914811ca2d4Sdjm sshbuf_free(encoded); 2915811ca2d4Sdjm sshbuf_free(encrypted); 291632a036abSdjm cipher_free(ciphercontext); 2917811ca2d4Sdjm explicit_bzero(salt, sizeof(salt)); 2918c9831b39Sjsg if (key != NULL) 2919c9831b39Sjsg freezero(key, keylen + ivlen); 2920c9831b39Sjsg if (pubkeyblob != NULL) 2921c9831b39Sjsg freezero(pubkeyblob, pubkeylen); 2922811ca2d4Sdjm return r; 2923811ca2d4Sdjm } 2924811ca2d4Sdjm 2925811ca2d4Sdjm static int 2926a8b00297Sdjm private2_uudecode(struct sshbuf *blob, struct sshbuf **decodedp) 2927811ca2d4Sdjm { 2928811ca2d4Sdjm const u_char *cp; 2929811ca2d4Sdjm size_t encoded_len; 2930a8b00297Sdjm int r; 2931a8b00297Sdjm u_char last; 2932811ca2d4Sdjm struct sshbuf *encoded = NULL, *decoded = NULL; 2933811ca2d4Sdjm 2934a8b00297Sdjm if (blob == NULL || decodedp == NULL) 2935a8b00297Sdjm return SSH_ERR_INVALID_ARGUMENT; 2936a8b00297Sdjm 2937a8b00297Sdjm *decodedp = NULL; 2938811ca2d4Sdjm 2939811ca2d4Sdjm if ((encoded = sshbuf_new()) == NULL || 2940a8b00297Sdjm (decoded = sshbuf_new()) == NULL) { 2941811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 2942811ca2d4Sdjm goto out; 2943811ca2d4Sdjm } 2944811ca2d4Sdjm 2945811ca2d4Sdjm /* check preamble */ 2946811ca2d4Sdjm cp = sshbuf_ptr(blob); 2947811ca2d4Sdjm encoded_len = sshbuf_len(blob); 2948811ca2d4Sdjm if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) || 2949811ca2d4Sdjm memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) { 2950811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 2951811ca2d4Sdjm goto out; 2952811ca2d4Sdjm } 2953811ca2d4Sdjm cp += MARK_BEGIN_LEN; 2954811ca2d4Sdjm encoded_len -= MARK_BEGIN_LEN; 2955811ca2d4Sdjm 2956811ca2d4Sdjm /* Look for end marker, removing whitespace as we go */ 2957811ca2d4Sdjm while (encoded_len > 0) { 2958811ca2d4Sdjm if (*cp != '\n' && *cp != '\r') { 2959811ca2d4Sdjm if ((r = sshbuf_put_u8(encoded, *cp)) != 0) 2960811ca2d4Sdjm goto out; 2961811ca2d4Sdjm } 2962811ca2d4Sdjm last = *cp; 2963811ca2d4Sdjm encoded_len--; 2964811ca2d4Sdjm cp++; 2965811ca2d4Sdjm if (last == '\n') { 2966811ca2d4Sdjm if (encoded_len >= MARK_END_LEN && 2967811ca2d4Sdjm memcmp(cp, MARK_END, MARK_END_LEN) == 0) { 2968811ca2d4Sdjm /* \0 terminate */ 2969811ca2d4Sdjm if ((r = sshbuf_put_u8(encoded, 0)) != 0) 2970811ca2d4Sdjm goto out; 2971811ca2d4Sdjm break; 2972811ca2d4Sdjm } 2973811ca2d4Sdjm } 2974811ca2d4Sdjm } 2975811ca2d4Sdjm if (encoded_len == 0) { 2976811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 2977811ca2d4Sdjm goto out; 2978811ca2d4Sdjm } 2979811ca2d4Sdjm 2980811ca2d4Sdjm /* decode base64 */ 298126066096Sdjm if ((r = sshbuf_b64tod(decoded, (char *)sshbuf_ptr(encoded))) != 0) 2982811ca2d4Sdjm goto out; 2983811ca2d4Sdjm 2984811ca2d4Sdjm /* check magic */ 2985811ca2d4Sdjm if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) || 2986811ca2d4Sdjm memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) { 2987811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 2988811ca2d4Sdjm goto out; 2989811ca2d4Sdjm } 2990a8b00297Sdjm /* success */ 2991a8b00297Sdjm *decodedp = decoded; 2992a8b00297Sdjm decoded = NULL; 2993a8b00297Sdjm r = 0; 2994a8b00297Sdjm out: 2995a8b00297Sdjm sshbuf_free(encoded); 2996a8b00297Sdjm sshbuf_free(decoded); 2997a8b00297Sdjm return r; 2998a8b00297Sdjm } 2999a8b00297Sdjm 3000a8b00297Sdjm static int 3001c3be6246Sdjm private2_decrypt(struct sshbuf *decoded, const char *passphrase, 3002c3be6246Sdjm struct sshbuf **decryptedp, struct sshkey **pubkeyp) 3003a8b00297Sdjm { 3004a8b00297Sdjm char *ciphername = NULL, *kdfname = NULL; 3005a8b00297Sdjm const struct sshcipher *cipher = NULL; 3006a8b00297Sdjm int r = SSH_ERR_INTERNAL_ERROR; 3007a8b00297Sdjm size_t keylen = 0, ivlen = 0, authlen = 0, slen = 0; 3008a8b00297Sdjm struct sshbuf *kdf = NULL, *decrypted = NULL; 3009a8b00297Sdjm struct sshcipher_ctx *ciphercontext = NULL; 3010c3be6246Sdjm struct sshkey *pubkey = NULL; 3011a8b00297Sdjm u_char *key = NULL, *salt = NULL, *dp; 3012a8b00297Sdjm u_int blocksize, rounds, nkeys, encrypted_len, check1, check2; 3013a8b00297Sdjm 3014c3be6246Sdjm if (decoded == NULL || decryptedp == NULL || pubkeyp == NULL) 3015a8b00297Sdjm return SSH_ERR_INVALID_ARGUMENT; 3016a8b00297Sdjm 3017a8b00297Sdjm *decryptedp = NULL; 3018c3be6246Sdjm *pubkeyp = NULL; 3019a8b00297Sdjm 3020a8b00297Sdjm if ((decrypted = sshbuf_new()) == NULL) { 3021a8b00297Sdjm r = SSH_ERR_ALLOC_FAIL; 3022a8b00297Sdjm goto out; 3023a8b00297Sdjm } 3024a8b00297Sdjm 3025811ca2d4Sdjm /* parse public portion of key */ 3026811ca2d4Sdjm if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || 3027811ca2d4Sdjm (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 || 3028811ca2d4Sdjm (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 || 3029811ca2d4Sdjm (r = sshbuf_froms(decoded, &kdf)) != 0 || 3030a8b00297Sdjm (r = sshbuf_get_u32(decoded, &nkeys)) != 0) 3031a8b00297Sdjm goto out; 3032a8b00297Sdjm 3033a8b00297Sdjm if (nkeys != 1) { 3034a8b00297Sdjm /* XXX only one key supported at present */ 3035a8b00297Sdjm r = SSH_ERR_INVALID_FORMAT; 3036a8b00297Sdjm goto out; 3037a8b00297Sdjm } 3038a8b00297Sdjm 3039c3be6246Sdjm if ((r = sshkey_froms(decoded, &pubkey)) != 0 || 3040811ca2d4Sdjm (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0) 3041811ca2d4Sdjm goto out; 3042811ca2d4Sdjm 3043811ca2d4Sdjm if ((cipher = cipher_by_name(ciphername)) == NULL) { 3044811ca2d4Sdjm r = SSH_ERR_KEY_UNKNOWN_CIPHER; 3045811ca2d4Sdjm goto out; 3046811ca2d4Sdjm } 3047811ca2d4Sdjm if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) { 3048811ca2d4Sdjm r = SSH_ERR_KEY_UNKNOWN_CIPHER; 3049811ca2d4Sdjm goto out; 3050811ca2d4Sdjm } 30515496609aSmarkus if (strcmp(kdfname, "none") == 0 && strcmp(ciphername, "none") != 0) { 3052811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 3053811ca2d4Sdjm goto out; 3054811ca2d4Sdjm } 30555496609aSmarkus if ((passphrase == NULL || strlen(passphrase) == 0) && 30565496609aSmarkus strcmp(kdfname, "none") != 0) { 30575496609aSmarkus /* passphrase required */ 30585496609aSmarkus r = SSH_ERR_KEY_WRONG_PASSPHRASE; 30595496609aSmarkus goto out; 30605496609aSmarkus } 3061811ca2d4Sdjm 3062811ca2d4Sdjm /* check size of encrypted key blob */ 3063811ca2d4Sdjm blocksize = cipher_blocksize(cipher); 3064811ca2d4Sdjm if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) { 3065811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 3066811ca2d4Sdjm goto out; 3067811ca2d4Sdjm } 3068811ca2d4Sdjm 3069811ca2d4Sdjm /* setup key */ 3070811ca2d4Sdjm keylen = cipher_keylen(cipher); 3071811ca2d4Sdjm ivlen = cipher_ivlen(cipher); 3072100a083eSdjm authlen = cipher_authlen(cipher); 3073811ca2d4Sdjm if ((key = calloc(1, keylen + ivlen)) == NULL) { 3074811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 3075811ca2d4Sdjm goto out; 3076811ca2d4Sdjm } 3077811ca2d4Sdjm if (strcmp(kdfname, "bcrypt") == 0) { 3078811ca2d4Sdjm if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 || 3079811ca2d4Sdjm (r = sshbuf_get_u32(kdf, &rounds)) != 0) 3080811ca2d4Sdjm goto out; 3081811ca2d4Sdjm if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, 3082811ca2d4Sdjm key, keylen + ivlen, rounds) < 0) { 3083811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 3084811ca2d4Sdjm goto out; 3085811ca2d4Sdjm } 3086811ca2d4Sdjm } 3087811ca2d4Sdjm 3088100a083eSdjm /* check that an appropriate amount of auth data is present */ 30894a1c78abSdjm if (sshbuf_len(decoded) < authlen || 30904a1c78abSdjm sshbuf_len(decoded) - authlen < encrypted_len) { 3091100a083eSdjm r = SSH_ERR_INVALID_FORMAT; 3092100a083eSdjm goto out; 3093100a083eSdjm } 3094100a083eSdjm 3095811ca2d4Sdjm /* decrypt private portion of key */ 3096811ca2d4Sdjm if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 || 3097811ca2d4Sdjm (r = cipher_init(&ciphercontext, cipher, key, keylen, 3098811ca2d4Sdjm key + keylen, ivlen, 0)) != 0) 3099811ca2d4Sdjm goto out; 310032a036abSdjm if ((r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(decoded), 3101100a083eSdjm encrypted_len, 0, authlen)) != 0) { 3102811ca2d4Sdjm /* an integrity error here indicates an incorrect passphrase */ 3103811ca2d4Sdjm if (r == SSH_ERR_MAC_INVALID) 3104811ca2d4Sdjm r = SSH_ERR_KEY_WRONG_PASSPHRASE; 3105811ca2d4Sdjm goto out; 3106811ca2d4Sdjm } 3107100a083eSdjm if ((r = sshbuf_consume(decoded, encrypted_len + authlen)) != 0) 3108811ca2d4Sdjm goto out; 3109811ca2d4Sdjm /* there should be no trailing data */ 3110811ca2d4Sdjm if (sshbuf_len(decoded) != 0) { 3111811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 3112811ca2d4Sdjm goto out; 3113811ca2d4Sdjm } 3114811ca2d4Sdjm 3115811ca2d4Sdjm /* check check bytes */ 3116811ca2d4Sdjm if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 || 3117811ca2d4Sdjm (r = sshbuf_get_u32(decrypted, &check2)) != 0) 3118811ca2d4Sdjm goto out; 3119811ca2d4Sdjm if (check1 != check2) { 3120811ca2d4Sdjm r = SSH_ERR_KEY_WRONG_PASSPHRASE; 3121811ca2d4Sdjm goto out; 3122811ca2d4Sdjm } 3123a8b00297Sdjm /* success */ 3124a8b00297Sdjm *decryptedp = decrypted; 3125a8b00297Sdjm decrypted = NULL; 3126c3be6246Sdjm *pubkeyp = pubkey; 3127c3be6246Sdjm pubkey = NULL; 3128a8b00297Sdjm r = 0; 3129a8b00297Sdjm out: 3130a8b00297Sdjm cipher_free(ciphercontext); 3131a8b00297Sdjm free(ciphername); 3132a8b00297Sdjm free(kdfname); 3133c3be6246Sdjm sshkey_free(pubkey); 3134a8b00297Sdjm if (salt != NULL) { 3135a8b00297Sdjm explicit_bzero(salt, slen); 3136a8b00297Sdjm free(salt); 3137a8b00297Sdjm } 3138a8b00297Sdjm if (key != NULL) { 3139a8b00297Sdjm explicit_bzero(key, keylen + ivlen); 3140a8b00297Sdjm free(key); 3141a8b00297Sdjm } 3142a8b00297Sdjm sshbuf_free(kdf); 3143a8b00297Sdjm sshbuf_free(decrypted); 3144a8b00297Sdjm return r; 3145a8b00297Sdjm } 3146811ca2d4Sdjm 3147a8b00297Sdjm static int 3148a8b00297Sdjm sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, 3149a8b00297Sdjm struct sshkey **keyp, char **commentp) 3150a8b00297Sdjm { 3151a8b00297Sdjm char *comment = NULL; 3152a8b00297Sdjm int r = SSH_ERR_INTERNAL_ERROR; 3153a8b00297Sdjm struct sshbuf *decoded = NULL, *decrypted = NULL; 3154c3be6246Sdjm struct sshkey *k = NULL, *pubkey = NULL; 3155a8b00297Sdjm 3156a8b00297Sdjm if (keyp != NULL) 3157a8b00297Sdjm *keyp = NULL; 3158a8b00297Sdjm if (commentp != NULL) 3159a8b00297Sdjm *commentp = NULL; 3160a8b00297Sdjm 3161a8b00297Sdjm /* Undo base64 encoding and decrypt the private section */ 3162a8b00297Sdjm if ((r = private2_uudecode(blob, &decoded)) != 0 || 3163c3be6246Sdjm (r = private2_decrypt(decoded, passphrase, 3164c3be6246Sdjm &decrypted, &pubkey)) != 0) 3165a8b00297Sdjm goto out; 3166a8b00297Sdjm 3167c96249d0Sdjm if (type != KEY_UNSPEC && 3168c96249d0Sdjm sshkey_type_plain(type) != sshkey_type_plain(pubkey->type)) { 3169c96249d0Sdjm r = SSH_ERR_KEY_TYPE_MISMATCH; 3170c96249d0Sdjm goto out; 3171c96249d0Sdjm } 3172c96249d0Sdjm 3173a8b00297Sdjm /* Load the private key and comment */ 3174a8b00297Sdjm if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 || 3175a8b00297Sdjm (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0) 3176a8b00297Sdjm goto out; 3177a8b00297Sdjm 3178a8b00297Sdjm /* Check deterministic padding after private section */ 3179a8b00297Sdjm if ((r = private2_check_padding(decrypted)) != 0) 3180a8b00297Sdjm goto out; 3181811ca2d4Sdjm 3182c3be6246Sdjm /* Check that the public key in the envelope matches the private key */ 3183c3be6246Sdjm if (!sshkey_equal(pubkey, k)) { 3184c3be6246Sdjm r = SSH_ERR_INVALID_FORMAT; 3185c3be6246Sdjm goto out; 3186c3be6246Sdjm } 3187811ca2d4Sdjm 3188811ca2d4Sdjm /* success */ 3189811ca2d4Sdjm r = 0; 3190811ca2d4Sdjm if (keyp != NULL) { 3191811ca2d4Sdjm *keyp = k; 3192811ca2d4Sdjm k = NULL; 3193811ca2d4Sdjm } 3194811ca2d4Sdjm if (commentp != NULL) { 3195811ca2d4Sdjm *commentp = comment; 3196811ca2d4Sdjm comment = NULL; 3197811ca2d4Sdjm } 3198811ca2d4Sdjm out: 3199811ca2d4Sdjm free(comment); 3200811ca2d4Sdjm sshbuf_free(decoded); 3201811ca2d4Sdjm sshbuf_free(decrypted); 3202811ca2d4Sdjm sshkey_free(k); 3203c3be6246Sdjm sshkey_free(pubkey); 3204811ca2d4Sdjm return r; 3205811ca2d4Sdjm } 3206811ca2d4Sdjm 3207117d1613Sdjm static int 3208117d1613Sdjm sshkey_parse_private2_pubkey(struct sshbuf *blob, int type, 3209117d1613Sdjm struct sshkey **keyp) 3210117d1613Sdjm { 3211117d1613Sdjm int r = SSH_ERR_INTERNAL_ERROR; 3212117d1613Sdjm struct sshbuf *decoded = NULL; 3213117d1613Sdjm struct sshkey *pubkey = NULL; 3214117d1613Sdjm u_int nkeys = 0; 3215117d1613Sdjm 3216117d1613Sdjm if (keyp != NULL) 3217117d1613Sdjm *keyp = NULL; 3218117d1613Sdjm 3219117d1613Sdjm if ((r = private2_uudecode(blob, &decoded)) != 0) 3220117d1613Sdjm goto out; 3221117d1613Sdjm /* parse public key from unencrypted envelope */ 3222117d1613Sdjm if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || 3223117d1613Sdjm (r = sshbuf_skip_string(decoded)) != 0 || /* cipher */ 3224117d1613Sdjm (r = sshbuf_skip_string(decoded)) != 0 || /* KDF alg */ 3225117d1613Sdjm (r = sshbuf_skip_string(decoded)) != 0 || /* KDF hint */ 3226117d1613Sdjm (r = sshbuf_get_u32(decoded, &nkeys)) != 0) 3227117d1613Sdjm goto out; 3228117d1613Sdjm 3229117d1613Sdjm if (nkeys != 1) { 3230117d1613Sdjm /* XXX only one key supported at present */ 3231117d1613Sdjm r = SSH_ERR_INVALID_FORMAT; 3232117d1613Sdjm goto out; 3233117d1613Sdjm } 3234117d1613Sdjm 3235117d1613Sdjm /* Parse the public key */ 3236117d1613Sdjm if ((r = sshkey_froms(decoded, &pubkey)) != 0) 3237117d1613Sdjm goto out; 3238117d1613Sdjm 3239117d1613Sdjm if (type != KEY_UNSPEC && 3240117d1613Sdjm sshkey_type_plain(type) != sshkey_type_plain(pubkey->type)) { 3241117d1613Sdjm r = SSH_ERR_KEY_TYPE_MISMATCH; 3242117d1613Sdjm goto out; 3243117d1613Sdjm } 3244117d1613Sdjm 3245117d1613Sdjm /* success */ 3246117d1613Sdjm r = 0; 3247117d1613Sdjm if (keyp != NULL) { 3248117d1613Sdjm *keyp = pubkey; 3249117d1613Sdjm pubkey = NULL; 3250117d1613Sdjm } 3251117d1613Sdjm out: 3252117d1613Sdjm sshbuf_free(decoded); 3253117d1613Sdjm sshkey_free(pubkey); 3254117d1613Sdjm return r; 3255117d1613Sdjm } 3256117d1613Sdjm 3257811ca2d4Sdjm #ifdef WITH_OPENSSL 325846250577Sdjm /* convert SSH v2 key to PEM or PKCS#8 format */ 3259811ca2d4Sdjm static int 326046250577Sdjm sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf, 326146250577Sdjm int format, const char *_passphrase, const char *comment) 3262811ca2d4Sdjm { 3263707316f9Sdjm int was_shielded = sshkey_is_shielded(key); 3264811ca2d4Sdjm int success, r; 3265811ca2d4Sdjm int blen, len = strlen(_passphrase); 3266811ca2d4Sdjm u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; 3267811ca2d4Sdjm const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; 3268d5da3e95Sdjm char *bptr; 3269811ca2d4Sdjm BIO *bio = NULL; 3270707316f9Sdjm struct sshbuf *blob; 327146250577Sdjm EVP_PKEY *pkey = NULL; 3272811ca2d4Sdjm 3273811ca2d4Sdjm if (len > 0 && len <= 4) 3274811ca2d4Sdjm return SSH_ERR_PASSPHRASE_TOO_SHORT; 3275707316f9Sdjm if ((blob = sshbuf_new()) == NULL) 3276811ca2d4Sdjm return SSH_ERR_ALLOC_FAIL; 3277707316f9Sdjm if ((bio = BIO_new(BIO_s_mem())) == NULL) { 327846250577Sdjm r = SSH_ERR_ALLOC_FAIL; 327946250577Sdjm goto out; 328046250577Sdjm } 3281707316f9Sdjm if ((r = sshkey_unshield_private(key)) != 0) 3282707316f9Sdjm goto out; 3283811ca2d4Sdjm 3284811ca2d4Sdjm switch (key->type) { 328552113de9Sdjm #ifdef WITH_DSA 3286811ca2d4Sdjm case KEY_DSA: 328746250577Sdjm if (format == SSHKEY_PRIVATE_PEM) { 3288811ca2d4Sdjm success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, 3289811ca2d4Sdjm cipher, passphrase, len, NULL, NULL); 329046250577Sdjm } else { 32915411e769Sdjm if ((pkey = EVP_PKEY_new()) == NULL) { 32925411e769Sdjm r = SSH_ERR_ALLOC_FAIL; 32935411e769Sdjm goto out; 32945411e769Sdjm } 329546250577Sdjm success = EVP_PKEY_set1_DSA(pkey, key->dsa); 329646250577Sdjm } 3297811ca2d4Sdjm break; 329852113de9Sdjm #endif 3299811ca2d4Sdjm case KEY_ECDSA: 330046250577Sdjm if (format == SSHKEY_PRIVATE_PEM) { 33015411e769Sdjm success = PEM_write_bio_ECPrivateKey(bio, 33025411e769Sdjm EVP_PKEY_get0_EC_KEY(key->pkey), 3303811ca2d4Sdjm cipher, passphrase, len, NULL, NULL); 330446250577Sdjm } else { 33055411e769Sdjm pkey = key->pkey; 33065411e769Sdjm EVP_PKEY_up_ref(key->pkey); 33075411e769Sdjm success = 1; 330846250577Sdjm } 3309811ca2d4Sdjm break; 3310811ca2d4Sdjm case KEY_RSA: 331146250577Sdjm if (format == SSHKEY_PRIVATE_PEM) { 33125411e769Sdjm success = PEM_write_bio_RSAPrivateKey(bio, 33135411e769Sdjm EVP_PKEY_get0_RSA(key->pkey), 3314811ca2d4Sdjm cipher, passphrase, len, NULL, NULL); 331546250577Sdjm } else { 33165411e769Sdjm pkey = key->pkey; 33175411e769Sdjm EVP_PKEY_up_ref(key->pkey); 33185411e769Sdjm success = 1; 331946250577Sdjm } 3320811ca2d4Sdjm break; 3321811ca2d4Sdjm default: 3322811ca2d4Sdjm success = 0; 3323811ca2d4Sdjm break; 3324811ca2d4Sdjm } 3325811ca2d4Sdjm if (success == 0) { 3326811ca2d4Sdjm r = SSH_ERR_LIBCRYPTO_ERROR; 3327811ca2d4Sdjm goto out; 3328811ca2d4Sdjm } 332946250577Sdjm if (format == SSHKEY_PRIVATE_PKCS8) { 333046250577Sdjm if ((success = PEM_write_bio_PrivateKey(bio, pkey, cipher, 333146250577Sdjm passphrase, len, NULL, NULL)) == 0) { 333246250577Sdjm r = SSH_ERR_LIBCRYPTO_ERROR; 333346250577Sdjm goto out; 333446250577Sdjm } 333546250577Sdjm } 3336811ca2d4Sdjm if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) { 3337811ca2d4Sdjm r = SSH_ERR_INTERNAL_ERROR; 3338811ca2d4Sdjm goto out; 3339811ca2d4Sdjm } 3340811ca2d4Sdjm if ((r = sshbuf_put(blob, bptr, blen)) != 0) 3341811ca2d4Sdjm goto out; 3342811ca2d4Sdjm r = 0; 3343811ca2d4Sdjm out: 3344707316f9Sdjm if (was_shielded) 3345707316f9Sdjm r = sshkey_shield_private(key); 3346707316f9Sdjm if (r == 0) 3347707316f9Sdjm r = sshbuf_putb(buf, blob); 3348707316f9Sdjm 334946250577Sdjm EVP_PKEY_free(pkey); 335046250577Sdjm sshbuf_free(blob); 3351811ca2d4Sdjm BIO_free(bio); 3352811ca2d4Sdjm return r; 3353811ca2d4Sdjm } 3354811ca2d4Sdjm #endif /* WITH_OPENSSL */ 3355811ca2d4Sdjm 3356811ca2d4Sdjm /* Serialise "key" to buffer "blob" */ 3357811ca2d4Sdjm int 3358811ca2d4Sdjm sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, 3359811ca2d4Sdjm const char *passphrase, const char *comment, 336046250577Sdjm int format, const char *openssh_format_cipher, int openssh_format_rounds) 3361811ca2d4Sdjm { 3362811ca2d4Sdjm switch (key->type) { 33635b2e2a49Smarkus #ifdef WITH_OPENSSL 3364811ca2d4Sdjm case KEY_DSA: 3365811ca2d4Sdjm case KEY_ECDSA: 3366811ca2d4Sdjm case KEY_RSA: 336746250577Sdjm break; /* see below */ 3368811ca2d4Sdjm #endif /* WITH_OPENSSL */ 3369811ca2d4Sdjm case KEY_ED25519: 337027c8f7c6Smarkus case KEY_ED25519_SK: 3371a6be8e7cSmarkus #ifdef WITH_XMSS 3372a6be8e7cSmarkus case KEY_XMSS: 3373a6be8e7cSmarkus #endif /* WITH_XMSS */ 33744f5eb3ebSdjm #ifdef WITH_OPENSSL 33754f5eb3ebSdjm case KEY_ECDSA_SK: 33764f5eb3ebSdjm #endif /* WITH_OPENSSL */ 3377811ca2d4Sdjm return sshkey_private_to_blob2(key, blob, passphrase, 337846250577Sdjm comment, openssh_format_cipher, openssh_format_rounds); 3379811ca2d4Sdjm default: 3380811ca2d4Sdjm return SSH_ERR_KEY_TYPE_UNKNOWN; 3381811ca2d4Sdjm } 338246250577Sdjm 338346250577Sdjm #ifdef WITH_OPENSSL 338446250577Sdjm switch (format) { 338546250577Sdjm case SSHKEY_PRIVATE_OPENSSH: 338646250577Sdjm return sshkey_private_to_blob2(key, blob, passphrase, 338746250577Sdjm comment, openssh_format_cipher, openssh_format_rounds); 338846250577Sdjm case SSHKEY_PRIVATE_PEM: 338946250577Sdjm case SSHKEY_PRIVATE_PKCS8: 339046250577Sdjm return sshkey_private_to_blob_pem_pkcs8(key, blob, 339146250577Sdjm format, passphrase, comment); 339246250577Sdjm default: 339346250577Sdjm return SSH_ERR_INVALID_ARGUMENT; 339446250577Sdjm } 339546250577Sdjm #endif /* WITH_OPENSSL */ 3396811ca2d4Sdjm } 3397811ca2d4Sdjm 3398811ca2d4Sdjm #ifdef WITH_OPENSSL 339903b8b379Sdjm static int 3400355c336cSdjm translate_libcrypto_error(unsigned long pem_err) 3401355c336cSdjm { 3402355c336cSdjm int pem_reason = ERR_GET_REASON(pem_err); 3403355c336cSdjm 3404355c336cSdjm switch (ERR_GET_LIB(pem_err)) { 3405355c336cSdjm case ERR_LIB_PEM: 3406355c336cSdjm switch (pem_reason) { 3407355c336cSdjm case PEM_R_BAD_PASSWORD_READ: 3408355c336cSdjm case PEM_R_PROBLEMS_GETTING_PASSWORD: 3409355c336cSdjm case PEM_R_BAD_DECRYPT: 3410355c336cSdjm return SSH_ERR_KEY_WRONG_PASSPHRASE; 3411355c336cSdjm default: 3412355c336cSdjm return SSH_ERR_INVALID_FORMAT; 3413355c336cSdjm } 3414355c336cSdjm case ERR_LIB_EVP: 3415355c336cSdjm switch (pem_reason) { 3416355c336cSdjm case EVP_R_BAD_DECRYPT: 3417355c336cSdjm return SSH_ERR_KEY_WRONG_PASSPHRASE; 34187c94020aSdjm #ifdef EVP_R_BN_DECODE_ERROR 3419355c336cSdjm case EVP_R_BN_DECODE_ERROR: 34207c94020aSdjm #endif 3421355c336cSdjm case EVP_R_DECODE_ERROR: 3422355c336cSdjm #ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR 3423355c336cSdjm case EVP_R_PRIVATE_KEY_DECODE_ERROR: 3424355c336cSdjm #endif 3425355c336cSdjm return SSH_ERR_INVALID_FORMAT; 3426355c336cSdjm default: 3427355c336cSdjm return SSH_ERR_LIBCRYPTO_ERROR; 3428355c336cSdjm } 3429355c336cSdjm case ERR_LIB_ASN1: 3430355c336cSdjm return SSH_ERR_INVALID_FORMAT; 3431355c336cSdjm } 3432355c336cSdjm return SSH_ERR_LIBCRYPTO_ERROR; 3433355c336cSdjm } 3434355c336cSdjm 3435355c336cSdjm static void 3436355c336cSdjm clear_libcrypto_errors(void) 3437355c336cSdjm { 3438355c336cSdjm while (ERR_get_error() != 0) 3439355c336cSdjm ; 3440355c336cSdjm } 3441355c336cSdjm 3442355c336cSdjm /* 3443355c336cSdjm * Translate OpenSSL error codes to determine whether 3444355c336cSdjm * passphrase is required/incorrect. 3445355c336cSdjm */ 3446355c336cSdjm static int 3447355c336cSdjm convert_libcrypto_error(void) 3448355c336cSdjm { 3449355c336cSdjm /* 3450355c336cSdjm * Some password errors are reported at the beginning 3451355c336cSdjm * of the error queue. 3452355c336cSdjm */ 3453355c336cSdjm if (translate_libcrypto_error(ERR_peek_error()) == 3454355c336cSdjm SSH_ERR_KEY_WRONG_PASSPHRASE) 3455355c336cSdjm return SSH_ERR_KEY_WRONG_PASSPHRASE; 3456355c336cSdjm return translate_libcrypto_error(ERR_peek_last_error()); 3457355c336cSdjm } 3458355c336cSdjm 3459355c336cSdjm static int 3460811ca2d4Sdjm sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, 346103b8b379Sdjm const char *passphrase, struct sshkey **keyp) 3462811ca2d4Sdjm { 3463811ca2d4Sdjm EVP_PKEY *pk = NULL; 3464811ca2d4Sdjm struct sshkey *prv = NULL; 3465811ca2d4Sdjm BIO *bio = NULL; 3466811ca2d4Sdjm int r; 34675411e769Sdjm RSA *rsa = NULL; 34685411e769Sdjm EC_KEY *ecdsa = NULL; 3469811ca2d4Sdjm 3470d9171e4cSdjm if (keyp != NULL) 3471811ca2d4Sdjm *keyp = NULL; 3472811ca2d4Sdjm 3473811ca2d4Sdjm if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX) 3474811ca2d4Sdjm return SSH_ERR_ALLOC_FAIL; 3475811ca2d4Sdjm if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) != 3476811ca2d4Sdjm (int)sshbuf_len(blob)) { 3477811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 3478811ca2d4Sdjm goto out; 3479811ca2d4Sdjm } 3480811ca2d4Sdjm 3481355c336cSdjm clear_libcrypto_errors(); 3482811ca2d4Sdjm if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, 3483811ca2d4Sdjm (char *)passphrase)) == NULL) { 3484c0e5e802Sdjm /* 3485c0e5e802Sdjm * libcrypto may return various ASN.1 errors when attempting 3486c0e5e802Sdjm * to parse a key with an incorrect passphrase. 3487c0e5e802Sdjm * Treat all format errors as "incorrect passphrase" if a 3488c0e5e802Sdjm * passphrase was supplied. 3489c0e5e802Sdjm */ 3490c0e5e802Sdjm if (passphrase != NULL && *passphrase != '\0') 3491c0e5e802Sdjm r = SSH_ERR_KEY_WRONG_PASSPHRASE; 3492c0e5e802Sdjm else 3493355c336cSdjm r = convert_libcrypto_error(); 3494f3162c4aSdjm goto out; 3495811ca2d4Sdjm } 34967c94020aSdjm if (EVP_PKEY_base_id(pk) == EVP_PKEY_RSA && 3497811ca2d4Sdjm (type == KEY_UNSPEC || type == KEY_RSA)) { 3498811ca2d4Sdjm if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { 3499811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 3500811ca2d4Sdjm goto out; 3501811ca2d4Sdjm } 35025411e769Sdjm if ((rsa = EVP_PKEY_get1_RSA(pk)) == NULL) { 3503811ca2d4Sdjm r = SSH_ERR_LIBCRYPTO_ERROR; 3504811ca2d4Sdjm goto out; 3505811ca2d4Sdjm } 35065411e769Sdjm prv->type = KEY_RSA; 35075411e769Sdjm #ifdef DEBUG_PK 35085411e769Sdjm RSA_print_fp(stderr, rsa, 8); 35095411e769Sdjm #endif 35105411e769Sdjm if (RSA_blinding_on(rsa, NULL) != 1 || 35115411e769Sdjm EVP_PKEY_set1_RSA(pk, rsa) != 1) { 35125411e769Sdjm r = SSH_ERR_LIBCRYPTO_ERROR; 35135411e769Sdjm goto out; 35145411e769Sdjm } 35155411e769Sdjm EVP_PKEY_up_ref(pk); 35165411e769Sdjm prv->pkey = pk; 35175958b96bSdjm if ((r = sshkey_check_rsa_length(prv, 0)) != 0) 3518654a3af3Sdjm goto out; 351952113de9Sdjm #ifdef WITH_DSA 35207c94020aSdjm } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_DSA && 3521811ca2d4Sdjm (type == KEY_UNSPEC || type == KEY_DSA)) { 3522811ca2d4Sdjm if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { 3523811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 3524811ca2d4Sdjm goto out; 3525811ca2d4Sdjm } 3526811ca2d4Sdjm prv->dsa = EVP_PKEY_get1_DSA(pk); 3527811ca2d4Sdjm prv->type = KEY_DSA; 3528811ca2d4Sdjm #ifdef DEBUG_PK 3529811ca2d4Sdjm DSA_print_fp(stderr, prv->dsa, 8); 3530811ca2d4Sdjm #endif 353152113de9Sdjm #endif 35327c94020aSdjm } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_EC && 3533811ca2d4Sdjm (type == KEY_UNSPEC || type == KEY_ECDSA)) { 3534811ca2d4Sdjm if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { 3535811ca2d4Sdjm r = SSH_ERR_ALLOC_FAIL; 3536811ca2d4Sdjm goto out; 3537811ca2d4Sdjm } 35385411e769Sdjm if ((prv->ecdsa_nid = sshkey_ecdsa_fixup_group(pk)) == -1 || 35395411e769Sdjm (ecdsa = EVP_PKEY_get1_EC_KEY(pk)) == NULL) { 35405411e769Sdjm r = SSH_ERR_LIBCRYPTO_ERROR; 35415411e769Sdjm goto out; 35425411e769Sdjm } 3543811ca2d4Sdjm prv->type = KEY_ECDSA; 35445411e769Sdjm if (sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL || 35455411e769Sdjm sshkey_ec_validate_public(EC_KEY_get0_group(ecdsa), 35465411e769Sdjm EC_KEY_get0_public_key(ecdsa)) != 0 || 35475411e769Sdjm sshkey_ec_validate_private(ecdsa) != 0) { 3548811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 3549811ca2d4Sdjm goto out; 3550811ca2d4Sdjm } 35515411e769Sdjm EVP_PKEY_up_ref(pk); 35525411e769Sdjm prv->pkey = pk; 3553811ca2d4Sdjm #ifdef DEBUG_PK 35545411e769Sdjm if (prv != NULL && prv->pkey != NULL) 35555411e769Sdjm sshkey_dump_ec_key(EVP_PKEY_get0_EC_KEY(prv->pkey)); 3556811ca2d4Sdjm #endif 355771ad7c65Sdjm } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_ED25519 && 355871ad7c65Sdjm (type == KEY_UNSPEC || type == KEY_ED25519)) { 3559e8d392efSdtucker size_t len; 3560e8d392efSdtucker 356171ad7c65Sdjm if ((prv = sshkey_new(KEY_UNSPEC)) == NULL || 356271ad7c65Sdjm (prv->ed25519_sk = calloc(1, ED25519_SK_SZ)) == NULL || 356371ad7c65Sdjm (prv->ed25519_pk = calloc(1, ED25519_PK_SZ)) == NULL) { 356471ad7c65Sdjm r = SSH_ERR_ALLOC_FAIL; 356571ad7c65Sdjm goto out; 356671ad7c65Sdjm } 356771ad7c65Sdjm prv->type = KEY_ED25519; 356871ad7c65Sdjm len = ED25519_PK_SZ; 356971ad7c65Sdjm if (!EVP_PKEY_get_raw_public_key(pk, prv->ed25519_pk, &len)) { 357071ad7c65Sdjm r = SSH_ERR_LIBCRYPTO_ERROR; 357171ad7c65Sdjm goto out; 357271ad7c65Sdjm } 357371ad7c65Sdjm if (len != ED25519_PK_SZ) { 357471ad7c65Sdjm r = SSH_ERR_INVALID_FORMAT; 357571ad7c65Sdjm goto out; 357671ad7c65Sdjm } 357771ad7c65Sdjm len = ED25519_SK_SZ - ED25519_PK_SZ; 357871ad7c65Sdjm if (!EVP_PKEY_get_raw_private_key(pk, prv->ed25519_sk, &len)) { 357971ad7c65Sdjm r = SSH_ERR_LIBCRYPTO_ERROR; 358071ad7c65Sdjm goto out; 358171ad7c65Sdjm } 358271ad7c65Sdjm if (len != ED25519_SK_SZ - ED25519_PK_SZ) { 358371ad7c65Sdjm r = SSH_ERR_INVALID_FORMAT; 358471ad7c65Sdjm goto out; 358571ad7c65Sdjm } 358671ad7c65Sdjm /* Append the public key to our private key */ 358771ad7c65Sdjm memcpy(prv->ed25519_sk + (ED25519_SK_SZ - ED25519_PK_SZ), 358871ad7c65Sdjm prv->ed25519_pk, ED25519_PK_SZ); 358971ad7c65Sdjm #ifdef DEBUG_PK 359071ad7c65Sdjm sshbuf_dump_data(prv->ed25519_sk, ED25519_SK_SZ, stderr); 359171ad7c65Sdjm #endif 3592811ca2d4Sdjm } else { 3593811ca2d4Sdjm r = SSH_ERR_INVALID_FORMAT; 3594811ca2d4Sdjm goto out; 3595811ca2d4Sdjm } 3596811ca2d4Sdjm r = 0; 3597d9171e4cSdjm if (keyp != NULL) { 3598811ca2d4Sdjm *keyp = prv; 3599811ca2d4Sdjm prv = NULL; 3600d9171e4cSdjm } 3601811ca2d4Sdjm out: 3602811ca2d4Sdjm BIO_free(bio); 3603811ca2d4Sdjm EVP_PKEY_free(pk); 36045411e769Sdjm RSA_free(rsa); 36055411e769Sdjm EC_KEY_free(ecdsa); 3606811ca2d4Sdjm sshkey_free(prv); 3607811ca2d4Sdjm return r; 3608811ca2d4Sdjm } 3609811ca2d4Sdjm #endif /* WITH_OPENSSL */ 3610811ca2d4Sdjm 3611811ca2d4Sdjm int 3612811ca2d4Sdjm sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, 3613811ca2d4Sdjm const char *passphrase, struct sshkey **keyp, char **commentp) 3614811ca2d4Sdjm { 3615f3162c4aSdjm int r = SSH_ERR_INTERNAL_ERROR; 3616f3162c4aSdjm 3617d9171e4cSdjm if (keyp != NULL) 3618811ca2d4Sdjm *keyp = NULL; 3619811ca2d4Sdjm if (commentp != NULL) 3620811ca2d4Sdjm *commentp = NULL; 3621811ca2d4Sdjm 3622811ca2d4Sdjm switch (type) { 3623a6be8e7cSmarkus case KEY_XMSS: 3624d49d52ceSdjm /* No fallback for new-format-only keys */ 3625811ca2d4Sdjm return sshkey_parse_private2(blob, type, passphrase, 3626811ca2d4Sdjm keyp, commentp); 3627d49d52ceSdjm default: 3628f3162c4aSdjm r = sshkey_parse_private2(blob, type, passphrase, keyp, 3629f3162c4aSdjm commentp); 3630d49d52ceSdjm /* Only fallback to PEM parser if a format error occurred. */ 3631d49d52ceSdjm if (r != SSH_ERR_INVALID_FORMAT) 3632f3162c4aSdjm return r; 3633811ca2d4Sdjm #ifdef WITH_OPENSSL 363403b8b379Sdjm return sshkey_parse_private_pem_fileblob(blob, type, 363503b8b379Sdjm passphrase, keyp); 3636811ca2d4Sdjm #else 3637811ca2d4Sdjm return SSH_ERR_INVALID_FORMAT; 3638811ca2d4Sdjm #endif /* WITH_OPENSSL */ 3639811ca2d4Sdjm } 3640811ca2d4Sdjm } 3641811ca2d4Sdjm 3642811ca2d4Sdjm int 3643811ca2d4Sdjm sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, 36440768ca3dStim struct sshkey **keyp, char **commentp) 3645811ca2d4Sdjm { 3646811ca2d4Sdjm if (keyp != NULL) 3647811ca2d4Sdjm *keyp = NULL; 3648811ca2d4Sdjm if (commentp != NULL) 3649811ca2d4Sdjm *commentp = NULL; 3650811ca2d4Sdjm 36510768ca3dStim return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC, 36520768ca3dStim passphrase, keyp, commentp); 3653811ca2d4Sdjm } 3654a6be8e7cSmarkus 3655493ad5b0Sdjm void 3656493ad5b0Sdjm sshkey_sig_details_free(struct sshkey_sig_details *details) 3657493ad5b0Sdjm { 3658493ad5b0Sdjm freezero(details, sizeof(*details)); 3659493ad5b0Sdjm } 3660493ad5b0Sdjm 3661117d1613Sdjm int 3662117d1613Sdjm sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob, int type, 3663117d1613Sdjm struct sshkey **pubkeyp) 3664117d1613Sdjm { 3665117d1613Sdjm int r = SSH_ERR_INTERNAL_ERROR; 3666117d1613Sdjm 3667117d1613Sdjm if (pubkeyp != NULL) 3668117d1613Sdjm *pubkeyp = NULL; 3669117d1613Sdjm /* only new-format private keys bundle a public key inside */ 3670117d1613Sdjm if ((r = sshkey_parse_private2_pubkey(blob, type, pubkeyp)) != 0) 3671117d1613Sdjm return r; 3672117d1613Sdjm return 0; 3673117d1613Sdjm } 3674117d1613Sdjm 3675a6be8e7cSmarkus #ifdef WITH_XMSS 3676a6be8e7cSmarkus /* 3677a6be8e7cSmarkus * serialize the key with the current state and forward the state 3678a6be8e7cSmarkus * maxsign times. 3679a6be8e7cSmarkus */ 3680a6be8e7cSmarkus int 368102eeb258Sdjm sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b, 368279e62715Sdtucker u_int32_t maxsign, int printerror) 3683a6be8e7cSmarkus { 3684a6be8e7cSmarkus int r, rupdate; 3685a6be8e7cSmarkus 3686a6be8e7cSmarkus if (maxsign == 0 || 3687a6be8e7cSmarkus sshkey_type_plain(k->type) != KEY_XMSS) 3688a6be8e7cSmarkus return sshkey_private_serialize_opt(k, b, 3689a6be8e7cSmarkus SSHKEY_SERIALIZE_DEFAULT); 369079e62715Sdtucker if ((r = sshkey_xmss_get_state(k, printerror)) != 0 || 3691a6be8e7cSmarkus (r = sshkey_private_serialize_opt(k, b, 3692a6be8e7cSmarkus SSHKEY_SERIALIZE_STATE)) != 0 || 3693a6be8e7cSmarkus (r = sshkey_xmss_forward_state(k, maxsign)) != 0) 3694a6be8e7cSmarkus goto out; 3695a6be8e7cSmarkus r = 0; 3696a6be8e7cSmarkus out: 369779e62715Sdtucker if ((rupdate = sshkey_xmss_update_state(k, printerror)) != 0) { 3698a6be8e7cSmarkus if (r == 0) 3699a6be8e7cSmarkus r = rupdate; 3700a6be8e7cSmarkus } 3701a6be8e7cSmarkus return r; 3702a6be8e7cSmarkus } 3703a6be8e7cSmarkus 3704a6be8e7cSmarkus u_int32_t 3705a6be8e7cSmarkus sshkey_signatures_left(const struct sshkey *k) 3706a6be8e7cSmarkus { 3707a6be8e7cSmarkus if (sshkey_type_plain(k->type) == KEY_XMSS) 3708a6be8e7cSmarkus return sshkey_xmss_signatures_left(k); 3709a6be8e7cSmarkus return 0; 3710a6be8e7cSmarkus } 3711a6be8e7cSmarkus 3712a6be8e7cSmarkus int 3713a6be8e7cSmarkus sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) 3714a6be8e7cSmarkus { 3715a6be8e7cSmarkus if (sshkey_type_plain(k->type) != KEY_XMSS) 3716a6be8e7cSmarkus return SSH_ERR_INVALID_ARGUMENT; 3717a6be8e7cSmarkus return sshkey_xmss_enable_maxsign(k, maxsign); 3718a6be8e7cSmarkus } 3719a6be8e7cSmarkus 3720a6be8e7cSmarkus int 3721a6be8e7cSmarkus sshkey_set_filename(struct sshkey *k, const char *filename) 3722a6be8e7cSmarkus { 3723a6be8e7cSmarkus if (k == NULL) 3724a6be8e7cSmarkus return SSH_ERR_INVALID_ARGUMENT; 3725a6be8e7cSmarkus if (sshkey_type_plain(k->type) != KEY_XMSS) 3726a6be8e7cSmarkus return 0; 3727a6be8e7cSmarkus if (filename == NULL) 3728a6be8e7cSmarkus return SSH_ERR_INVALID_ARGUMENT; 3729a6be8e7cSmarkus if ((k->xmss_filename = strdup(filename)) == NULL) 3730a6be8e7cSmarkus return SSH_ERR_ALLOC_FAIL; 3731a6be8e7cSmarkus return 0; 3732a6be8e7cSmarkus } 3733a6be8e7cSmarkus #else 3734a6be8e7cSmarkus int 3735707316f9Sdjm sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b, 373679e62715Sdtucker u_int32_t maxsign, int printerror) 3737a6be8e7cSmarkus { 3738a6be8e7cSmarkus return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT); 3739a6be8e7cSmarkus } 3740a6be8e7cSmarkus 3741a6be8e7cSmarkus u_int32_t 3742a6be8e7cSmarkus sshkey_signatures_left(const struct sshkey *k) 3743a6be8e7cSmarkus { 3744a6be8e7cSmarkus return 0; 3745a6be8e7cSmarkus } 3746a6be8e7cSmarkus 3747a6be8e7cSmarkus int 3748a6be8e7cSmarkus sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) 3749a6be8e7cSmarkus { 3750a6be8e7cSmarkus return SSH_ERR_INVALID_ARGUMENT; 3751a6be8e7cSmarkus } 3752a6be8e7cSmarkus 3753a6be8e7cSmarkus int 3754a6be8e7cSmarkus sshkey_set_filename(struct sshkey *k, const char *filename) 3755a6be8e7cSmarkus { 3756a6be8e7cSmarkus if (k == NULL) 3757a6be8e7cSmarkus return SSH_ERR_INVALID_ARGUMENT; 3758a6be8e7cSmarkus return 0; 3759a6be8e7cSmarkus } 3760a6be8e7cSmarkus #endif /* WITH_XMSS */ 3761