xref: /openbsd-src/usr.bin/ssh/sshkey.c (revision 773a72803dd603236f43cbc6986189c9c8b294a7)
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