blob: 9956a08b987f64f84c83ac63febd587b95682e6e [file] [log] [blame]
djm@openbsd.org16dd8b22019-07-16 13:18:39 +00001/* $OpenBSD: sshkey.c,v 1.81 2019/07/16 13:18:39 djm Exp $ */
Damien Miller86687062014-07-02 15:28:02 +10002/*
3 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
4 * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
5 * Copyright (c) 2010,2011 Damien Miller. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "includes.h"
29
Damien Miller86687062014-07-02 15:28:02 +100030#include <sys/types.h>
djm@openbsd.org56d1c832014-12-21 22:27:55 +000031#include <netinet/in.h>
Damien Miller86687062014-07-02 15:28:02 +100032
djm@openbsd.org54924b52015-01-14 10:46:28 +000033#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +100034#include <openssl/evp.h>
35#include <openssl/err.h>
36#include <openssl/pem.h>
djm@openbsd.org54924b52015-01-14 10:46:28 +000037#endif
Damien Miller86687062014-07-02 15:28:02 +100038
39#include "crypto_api.h"
40
41#include <errno.h>
deraadt@openbsd.org2ae4f332015-01-16 06:40:12 +000042#include <limits.h>
Damien Miller86687062014-07-02 15:28:02 +100043#include <stdio.h>
44#include <string.h>
Damien Millerd16bdd82014-12-22 10:18:09 +110045#include <resolv.h>
Damien Miller82b24822014-07-02 17:43:41 +100046#ifdef HAVE_UTIL_H
Damien Miller86687062014-07-02 15:28:02 +100047#include <util.h>
Damien Miller82b24822014-07-02 17:43:41 +100048#endif /* HAVE_UTIL_H */
Damien Miller86687062014-07-02 15:28:02 +100049
50#include "ssh2.h"
51#include "ssherr.h"
52#include "misc.h"
53#include "sshbuf.h"
Damien Miller86687062014-07-02 15:28:02 +100054#include "cipher.h"
55#include "digest.h"
56#define SSHKEY_INTERNAL
57#include "sshkey.h"
djm@openbsd.org1f729f02015-01-13 07:39:19 +000058#include "match.h"
Damien Miller86687062014-07-02 15:28:02 +100059
dtucker@openbsd.org99043bd2019-05-03 03:25:18 +000060#ifdef WITH_XMSS
61#include "sshkey-xmss.h"
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000062#include "xmss_fast.h"
dtucker@openbsd.org99043bd2019-05-03 03:25:18 +000063#endif
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000064
Damien Miller48f54b92018-09-13 12:13:50 +100065#include "openbsd-compat/openssl-compat.h"
66
Damien Miller86687062014-07-02 15:28:02 +100067/* openssh private key file format */
68#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n"
69#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n"
70#define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1)
71#define MARK_END_LEN (sizeof(MARK_END) - 1)
72#define KDFNAME "bcrypt"
73#define AUTH_MAGIC "openssh-key-v1"
74#define SALT_LEN 16
djm@openbsd.org0f345532017-08-12 06:42:52 +000075#define DEFAULT_CIPHERNAME "aes256-ctr"
Damien Miller86687062014-07-02 15:28:02 +100076#define DEFAULT_ROUNDS 16
77
78/* Version identification string for SSH v1 identity files. */
79#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n"
80
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +000081/*
82 * Constants relating to "shielding" support; protection of keys expected
83 * to remain in memory for long durations
84 */
85#define SSHKEY_SHIELD_PREKEY_LEN (16 * 1024)
86#define SSHKEY_SHIELD_CIPHER "aes256-ctr" /* XXX want AES-EME* */
87#define SSHKEY_SHIELD_PREKEY_HASH SSH_DIGEST_SHA512
88
89int sshkey_private_serialize_opt(struct sshkey *key,
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000090 struct sshbuf *buf, enum sshkey_serialize_rep);
djm@openbsd.org60b18252015-01-26 02:59:11 +000091static int sshkey_from_blob_internal(struct sshbuf *buf,
Damien Miller86687062014-07-02 15:28:02 +100092 struct sshkey **keyp, int allow_cert);
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +000093static int get_sigtype(const u_char *sig, size_t siglen, char **sigtypep);
Damien Miller86687062014-07-02 15:28:02 +100094
95/* Supported key types */
96struct keytype {
97 const char *name;
98 const char *shortname;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +000099 const char *sigalg;
Damien Miller86687062014-07-02 15:28:02 +1000100 int type;
101 int nid;
102 int cert;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000103 int sigonly;
Damien Miller86687062014-07-02 15:28:02 +1000104};
105static const struct keytype keytypes[] = {
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000106 { "ssh-ed25519", "ED25519", NULL, KEY_ED25519, 0, 0, 0 },
107 { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000108 KEY_ED25519_CERT, 0, 1, 0 },
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000109#ifdef WITH_XMSS
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000110 { "ssh-xmss@openssh.com", "XMSS", NULL, KEY_XMSS, 0, 0, 0 },
111 { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", NULL,
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000112 KEY_XMSS_CERT, 0, 1, 0 },
113#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000114#ifdef WITH_OPENSSL
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000115 { "ssh-rsa", "RSA", NULL, KEY_RSA, 0, 0, 0 },
116 { "rsa-sha2-256", "RSA", NULL, KEY_RSA, 0, 0, 1 },
117 { "rsa-sha2-512", "RSA", NULL, KEY_RSA, 0, 0, 1 },
118 { "ssh-dss", "DSA", NULL, KEY_DSA, 0, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000119# ifdef OPENSSL_HAS_ECC
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000120 { "ecdsa-sha2-nistp256", "ECDSA", NULL,
121 KEY_ECDSA, NID_X9_62_prime256v1, 0, 0 },
122 { "ecdsa-sha2-nistp384", "ECDSA", NULL,
123 KEY_ECDSA, NID_secp384r1, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000124# ifdef OPENSSL_HAS_NISTP521
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000125 { "ecdsa-sha2-nistp521", "ECDSA", NULL,
126 KEY_ECDSA, NID_secp521r1, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000127# endif /* OPENSSL_HAS_NISTP521 */
128# endif /* OPENSSL_HAS_ECC */
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000129 { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL,
130 KEY_RSA_CERT, 0, 1, 0 },
131 { "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT",
djm@openbsd.orgebfafd92018-10-11 00:52:46 +0000132 "rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 },
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000133 { "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT",
djm@openbsd.orgebfafd92018-10-11 00:52:46 +0000134 "rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 },
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000135 { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL,
136 KEY_DSA_CERT, 0, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000137# ifdef OPENSSL_HAS_ECC
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000138 { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000139 KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1, 0 },
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000140 { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000141 KEY_ECDSA_CERT, NID_secp384r1, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000142# ifdef OPENSSL_HAS_NISTP521
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000143 { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", NULL,
144 KEY_ECDSA_CERT, NID_secp521r1, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000145# endif /* OPENSSL_HAS_NISTP521 */
146# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +1000147#endif /* WITH_OPENSSL */
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000148 { NULL, NULL, NULL, -1, -1, 0, 0 }
Damien Miller86687062014-07-02 15:28:02 +1000149};
150
151const char *
152sshkey_type(const struct sshkey *k)
153{
154 const struct keytype *kt;
155
156 for (kt = keytypes; kt->type != -1; kt++) {
157 if (kt->type == k->type)
158 return kt->shortname;
159 }
160 return "unknown";
161}
162
163static const char *
164sshkey_ssh_name_from_type_nid(int type, int nid)
165{
166 const struct keytype *kt;
167
168 for (kt = keytypes; kt->type != -1; kt++) {
169 if (kt->type == type && (kt->nid == 0 || kt->nid == nid))
170 return kt->name;
171 }
172 return "ssh-unknown";
173}
174
175int
176sshkey_type_is_cert(int type)
177{
178 const struct keytype *kt;
179
180 for (kt = keytypes; kt->type != -1; kt++) {
181 if (kt->type == type)
182 return kt->cert;
183 }
184 return 0;
185}
186
187const char *
188sshkey_ssh_name(const struct sshkey *k)
189{
190 return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid);
191}
192
193const char *
194sshkey_ssh_name_plain(const struct sshkey *k)
195{
196 return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type),
197 k->ecdsa_nid);
198}
199
200int
201sshkey_type_from_name(const char *name)
202{
203 const struct keytype *kt;
204
205 for (kt = keytypes; kt->type != -1; kt++) {
206 /* Only allow shortname matches for plain key types */
207 if ((kt->name != NULL && strcmp(name, kt->name) == 0) ||
208 (!kt->cert && strcasecmp(kt->shortname, name) == 0))
209 return kt->type;
210 }
211 return KEY_UNSPEC;
212}
213
214int
215sshkey_ecdsa_nid_from_name(const char *name)
216{
217 const struct keytype *kt;
218
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +0000219 for (kt = keytypes; kt->type != -1; kt++) {
220 if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT)
221 continue;
222 if (kt->name != NULL && strcmp(name, kt->name) == 0)
223 return kt->nid;
224 }
Damien Miller86687062014-07-02 15:28:02 +1000225 return -1;
226}
227
228char *
djm@openbsd.org183ba552017-03-10 04:07:20 +0000229sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
Damien Miller86687062014-07-02 15:28:02 +1000230{
231 char *tmp, *ret = NULL;
232 size_t nlen, rlen = 0;
233 const struct keytype *kt;
234
235 for (kt = keytypes; kt->type != -1; kt++) {
djm@openbsd.org183ba552017-03-10 04:07:20 +0000236 if (kt->name == NULL)
237 continue;
238 if (!include_sigonly && kt->sigonly)
Damien Miller86687062014-07-02 15:28:02 +1000239 continue;
240 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
241 continue;
242 if (ret != NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000243 ret[rlen++] = sep;
Damien Miller86687062014-07-02 15:28:02 +1000244 nlen = strlen(kt->name);
245 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
246 free(ret);
247 return NULL;
248 }
249 ret = tmp;
250 memcpy(ret + rlen, kt->name, nlen + 1);
251 rlen += nlen;
252 }
253 return ret;
254}
255
256int
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000257sshkey_names_valid2(const char *names, int allow_wildcard)
Damien Miller86687062014-07-02 15:28:02 +1000258{
259 char *s, *cp, *p;
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000260 const struct keytype *kt;
261 int type;
Damien Miller86687062014-07-02 15:28:02 +1000262
263 if (names == NULL || strcmp(names, "") == 0)
264 return 0;
265 if ((s = cp = strdup(names)) == NULL)
266 return 0;
267 for ((p = strsep(&cp, ",")); p && *p != '\0';
268 (p = strsep(&cp, ","))) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000269 type = sshkey_type_from_name(p);
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000270 if (type == KEY_UNSPEC) {
271 if (allow_wildcard) {
272 /*
273 * Try matching key types against the string.
274 * If any has a positive or negative match then
275 * the component is accepted.
276 */
277 for (kt = keytypes; kt->type != -1; kt++) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000278 if (match_pattern_list(kt->name,
djm@openbsd.orge661a862015-05-04 06:10:48 +0000279 p, 0) != 0)
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000280 break;
281 }
282 if (kt->type != -1)
283 continue;
284 }
Damien Miller86687062014-07-02 15:28:02 +1000285 free(s);
286 return 0;
287 }
288 }
289 free(s);
290 return 1;
291}
292
293u_int
294sshkey_size(const struct sshkey *k)
295{
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000296#ifdef WITH_OPENSSL
297 const BIGNUM *rsa_n, *dsa_p;
298#endif /* WITH_OPENSSL */
299
Damien Miller86687062014-07-02 15:28:02 +1000300 switch (k->type) {
301#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000302 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000303 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000304 if (k->rsa == NULL)
305 return 0;
306 RSA_get0_key(k->rsa, &rsa_n, NULL, NULL);
307 return BN_num_bits(rsa_n);
Damien Miller86687062014-07-02 15:28:02 +1000308 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000309 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000310 if (k->dsa == NULL)
311 return 0;
312 DSA_get0_pqg(k->dsa, &dsa_p, NULL, NULL);
313 return BN_num_bits(dsa_p);
Damien Miller86687062014-07-02 15:28:02 +1000314 case KEY_ECDSA:
315 case KEY_ECDSA_CERT:
316 return sshkey_curve_nid_to_bits(k->ecdsa_nid);
317#endif /* WITH_OPENSSL */
318 case KEY_ED25519:
319 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000320 case KEY_XMSS:
321 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000322 return 256; /* XXX */
323 }
324 return 0;
325}
326
Damien Miller86687062014-07-02 15:28:02 +1000327static int
328sshkey_type_is_valid_ca(int type)
329{
330 switch (type) {
331 case KEY_RSA:
332 case KEY_DSA:
333 case KEY_ECDSA:
334 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000335 case KEY_XMSS:
Damien Miller86687062014-07-02 15:28:02 +1000336 return 1;
337 default:
338 return 0;
339 }
340}
341
342int
343sshkey_is_cert(const struct sshkey *k)
344{
345 if (k == NULL)
346 return 0;
347 return sshkey_type_is_cert(k->type);
348}
349
350/* Return the cert-less equivalent to a certified key type */
351int
352sshkey_type_plain(int type)
353{
354 switch (type) {
Damien Miller86687062014-07-02 15:28:02 +1000355 case KEY_RSA_CERT:
356 return KEY_RSA;
Damien Miller86687062014-07-02 15:28:02 +1000357 case KEY_DSA_CERT:
358 return KEY_DSA;
359 case KEY_ECDSA_CERT:
360 return KEY_ECDSA;
361 case KEY_ED25519_CERT:
362 return KEY_ED25519;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000363 case KEY_XMSS_CERT:
364 return KEY_XMSS;
Damien Miller86687062014-07-02 15:28:02 +1000365 default:
366 return type;
367 }
368}
369
370#ifdef WITH_OPENSSL
371/* XXX: these are really begging for a table-driven approach */
372int
373sshkey_curve_name_to_nid(const char *name)
374{
375 if (strcmp(name, "nistp256") == 0)
376 return NID_X9_62_prime256v1;
377 else if (strcmp(name, "nistp384") == 0)
378 return NID_secp384r1;
379# ifdef OPENSSL_HAS_NISTP521
380 else if (strcmp(name, "nistp521") == 0)
381 return NID_secp521r1;
382# endif /* OPENSSL_HAS_NISTP521 */
383 else
384 return -1;
385}
386
387u_int
388sshkey_curve_nid_to_bits(int nid)
389{
390 switch (nid) {
391 case NID_X9_62_prime256v1:
392 return 256;
393 case NID_secp384r1:
394 return 384;
395# ifdef OPENSSL_HAS_NISTP521
396 case NID_secp521r1:
397 return 521;
398# endif /* OPENSSL_HAS_NISTP521 */
399 default:
400 return 0;
401 }
402}
403
404int
405sshkey_ecdsa_bits_to_nid(int bits)
406{
407 switch (bits) {
408 case 256:
409 return NID_X9_62_prime256v1;
410 case 384:
411 return NID_secp384r1;
412# ifdef OPENSSL_HAS_NISTP521
413 case 521:
414 return NID_secp521r1;
415# endif /* OPENSSL_HAS_NISTP521 */
416 default:
417 return -1;
418 }
419}
420
421const char *
422sshkey_curve_nid_to_name(int nid)
423{
424 switch (nid) {
425 case NID_X9_62_prime256v1:
426 return "nistp256";
427 case NID_secp384r1:
428 return "nistp384";
429# ifdef OPENSSL_HAS_NISTP521
430 case NID_secp521r1:
431 return "nistp521";
432# endif /* OPENSSL_HAS_NISTP521 */
433 default:
434 return NULL;
435 }
436}
437
438int
439sshkey_ec_nid_to_hash_alg(int nid)
440{
441 int kbits = sshkey_curve_nid_to_bits(nid);
442
443 if (kbits <= 0)
444 return -1;
445
446 /* RFC5656 section 6.2.1 */
447 if (kbits <= 256)
448 return SSH_DIGEST_SHA256;
449 else if (kbits <= 384)
450 return SSH_DIGEST_SHA384;
451 else
452 return SSH_DIGEST_SHA512;
453}
454#endif /* WITH_OPENSSL */
455
456static void
457cert_free(struct sshkey_cert *cert)
458{
459 u_int i;
460
461 if (cert == NULL)
462 return;
mmcc@openbsd.org52d70782015-12-11 04:21:11 +0000463 sshbuf_free(cert->certblob);
464 sshbuf_free(cert->critical);
465 sshbuf_free(cert->extensions);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000466 free(cert->key_id);
Damien Miller86687062014-07-02 15:28:02 +1000467 for (i = 0; i < cert->nprincipals; i++)
468 free(cert->principals[i]);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000469 free(cert->principals);
mmcc@openbsd.org89540b62015-12-11 02:31:47 +0000470 sshkey_free(cert->signature_key);
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +0000471 free(cert->signature_type);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000472 freezero(cert, sizeof(*cert));
Damien Miller86687062014-07-02 15:28:02 +1000473}
474
475static struct sshkey_cert *
476cert_new(void)
477{
478 struct sshkey_cert *cert;
479
480 if ((cert = calloc(1, sizeof(*cert))) == NULL)
481 return NULL;
482 if ((cert->certblob = sshbuf_new()) == NULL ||
483 (cert->critical = sshbuf_new()) == NULL ||
484 (cert->extensions = sshbuf_new()) == NULL) {
485 cert_free(cert);
486 return NULL;
487 }
488 cert->key_id = NULL;
489 cert->principals = NULL;
490 cert->signature_key = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +0000491 cert->signature_type = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000492 return cert;
493}
494
495struct sshkey *
496sshkey_new(int type)
497{
498 struct sshkey *k;
499#ifdef WITH_OPENSSL
500 RSA *rsa;
501 DSA *dsa;
502#endif /* WITH_OPENSSL */
503
504 if ((k = calloc(1, sizeof(*k))) == NULL)
505 return NULL;
506 k->type = type;
507 k->ecdsa = NULL;
508 k->ecdsa_nid = -1;
509 k->dsa = NULL;
510 k->rsa = NULL;
511 k->cert = NULL;
512 k->ed25519_sk = NULL;
513 k->ed25519_pk = NULL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000514 k->xmss_sk = NULL;
515 k->xmss_pk = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000516 switch (k->type) {
517#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000518 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000519 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000520 if ((rsa = RSA_new()) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +1000521 free(k);
522 return NULL;
523 }
524 k->rsa = rsa;
525 break;
526 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000527 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000528 if ((dsa = DSA_new()) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +1000529 free(k);
530 return NULL;
531 }
532 k->dsa = dsa;
533 break;
534 case KEY_ECDSA:
535 case KEY_ECDSA_CERT:
536 /* Cannot do anything until we know the group */
537 break;
538#endif /* WITH_OPENSSL */
539 case KEY_ED25519:
540 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000541 case KEY_XMSS:
542 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000543 /* no need to prealloc */
544 break;
545 case KEY_UNSPEC:
546 break;
547 default:
548 free(k);
549 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000550 }
551
552 if (sshkey_is_cert(k)) {
553 if ((k->cert = cert_new()) == NULL) {
554 sshkey_free(k);
555 return NULL;
556 }
557 }
558
559 return k;
560}
561
Damien Miller86687062014-07-02 15:28:02 +1000562void
563sshkey_free(struct sshkey *k)
564{
565 if (k == NULL)
566 return;
567 switch (k->type) {
568#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000569 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000570 case KEY_RSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000571 RSA_free(k->rsa);
Damien Miller86687062014-07-02 15:28:02 +1000572 k->rsa = NULL;
573 break;
574 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000575 case KEY_DSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000576 DSA_free(k->dsa);
Damien Miller86687062014-07-02 15:28:02 +1000577 k->dsa = NULL;
578 break;
579# ifdef OPENSSL_HAS_ECC
580 case KEY_ECDSA:
581 case KEY_ECDSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000582 EC_KEY_free(k->ecdsa);
Damien Miller86687062014-07-02 15:28:02 +1000583 k->ecdsa = NULL;
584 break;
585# endif /* OPENSSL_HAS_ECC */
586#endif /* WITH_OPENSSL */
587 case KEY_ED25519:
588 case KEY_ED25519_CERT:
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000589 freezero(k->ed25519_pk, ED25519_PK_SZ);
590 k->ed25519_pk = NULL;
591 freezero(k->ed25519_sk, ED25519_SK_SZ);
592 k->ed25519_sk = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000593 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000594#ifdef WITH_XMSS
595 case KEY_XMSS:
596 case KEY_XMSS_CERT:
597 freezero(k->xmss_pk, sshkey_xmss_pklen(k));
598 k->xmss_pk = NULL;
599 freezero(k->xmss_sk, sshkey_xmss_sklen(k));
600 k->xmss_sk = NULL;
601 sshkey_xmss_free_state(k);
602 free(k->xmss_name);
603 k->xmss_name = NULL;
604 free(k->xmss_filename);
605 k->xmss_filename = NULL;
606 break;
607#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000608 case KEY_UNSPEC:
609 break;
610 default:
611 break;
612 }
613 if (sshkey_is_cert(k))
614 cert_free(k->cert);
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +0000615 freezero(k->shielded_private, k->shielded_len);
616 freezero(k->shield_prekey, k->shield_prekey_len);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000617 freezero(k, sizeof(*k));
Damien Miller86687062014-07-02 15:28:02 +1000618}
619
620static int
621cert_compare(struct sshkey_cert *a, struct sshkey_cert *b)
622{
623 if (a == NULL && b == NULL)
624 return 1;
625 if (a == NULL || b == NULL)
626 return 0;
627 if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob))
628 return 0;
629 if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob),
630 sshbuf_len(a->certblob)) != 0)
631 return 0;
632 return 1;
633}
634
635/*
636 * Compare public portions of key only, allowing comparisons between
637 * certificates and plain keys too.
638 */
639int
640sshkey_equal_public(const struct sshkey *a, const struct sshkey *b)
641{
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000642#if defined(WITH_OPENSSL)
643 const BIGNUM *rsa_e_a, *rsa_n_a;
644 const BIGNUM *rsa_e_b, *rsa_n_b;
645 const BIGNUM *dsa_p_a, *dsa_q_a, *dsa_g_a, *dsa_pub_key_a;
646 const BIGNUM *dsa_p_b, *dsa_q_b, *dsa_g_b, *dsa_pub_key_b;
647# if defined(OPENSSL_HAS_ECC)
Damien Miller86687062014-07-02 15:28:02 +1000648 BN_CTX *bnctx;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000649# endif /* OPENSSL_HAS_ECC */
650#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +1000651
652 if (a == NULL || b == NULL ||
653 sshkey_type_plain(a->type) != sshkey_type_plain(b->type))
654 return 0;
655
656 switch (a->type) {
657#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000658 case KEY_RSA_CERT:
659 case KEY_RSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000660 if (a->rsa == NULL || b->rsa == NULL)
661 return 0;
662 RSA_get0_key(a->rsa, &rsa_n_a, &rsa_e_a, NULL);
663 RSA_get0_key(b->rsa, &rsa_n_b, &rsa_e_b, NULL);
664 return BN_cmp(rsa_e_a, rsa_e_b) == 0 &&
665 BN_cmp(rsa_n_a, rsa_n_b) == 0;
Damien Miller86687062014-07-02 15:28:02 +1000666 case KEY_DSA_CERT:
667 case KEY_DSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000668 if (a->dsa == NULL || b->dsa == NULL)
669 return 0;
670 DSA_get0_pqg(a->dsa, &dsa_p_a, &dsa_q_a, &dsa_g_a);
671 DSA_get0_pqg(b->dsa, &dsa_p_b, &dsa_q_b, &dsa_g_b);
672 DSA_get0_key(a->dsa, &dsa_pub_key_a, NULL);
673 DSA_get0_key(b->dsa, &dsa_pub_key_b, NULL);
674 return BN_cmp(dsa_p_a, dsa_p_b) == 0 &&
675 BN_cmp(dsa_q_a, dsa_q_b) == 0 &&
676 BN_cmp(dsa_g_a, dsa_g_b) == 0 &&
677 BN_cmp(dsa_pub_key_a, dsa_pub_key_b) == 0;
Damien Miller86687062014-07-02 15:28:02 +1000678# ifdef OPENSSL_HAS_ECC
679 case KEY_ECDSA_CERT:
680 case KEY_ECDSA:
681 if (a->ecdsa == NULL || b->ecdsa == NULL ||
682 EC_KEY_get0_public_key(a->ecdsa) == NULL ||
683 EC_KEY_get0_public_key(b->ecdsa) == NULL)
684 return 0;
685 if ((bnctx = BN_CTX_new()) == NULL)
686 return 0;
687 if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa),
688 EC_KEY_get0_group(b->ecdsa), bnctx) != 0 ||
689 EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa),
690 EC_KEY_get0_public_key(a->ecdsa),
691 EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) {
692 BN_CTX_free(bnctx);
693 return 0;
694 }
695 BN_CTX_free(bnctx);
696 return 1;
697# endif /* OPENSSL_HAS_ECC */
698#endif /* WITH_OPENSSL */
699 case KEY_ED25519:
700 case KEY_ED25519_CERT:
701 return a->ed25519_pk != NULL && b->ed25519_pk != NULL &&
702 memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000703#ifdef WITH_XMSS
704 case KEY_XMSS:
705 case KEY_XMSS_CERT:
706 return a->xmss_pk != NULL && b->xmss_pk != NULL &&
707 sshkey_xmss_pklen(a) == sshkey_xmss_pklen(b) &&
708 memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) == 0;
709#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000710 default:
711 return 0;
712 }
713 /* NOTREACHED */
714}
715
716int
717sshkey_equal(const struct sshkey *a, const struct sshkey *b)
718{
719 if (a == NULL || b == NULL || a->type != b->type)
720 return 0;
721 if (sshkey_is_cert(a)) {
722 if (!cert_compare(a->cert, b->cert))
723 return 0;
724 }
725 return sshkey_equal_public(a, b);
726}
727
728static int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000729to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain,
730 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +1000731{
732 int type, ret = SSH_ERR_INTERNAL_ERROR;
733 const char *typename;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000734#ifdef WITH_OPENSSL
735 const BIGNUM *rsa_n, *rsa_e, *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
736#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +1000737
738 if (key == NULL)
739 return SSH_ERR_INVALID_ARGUMENT;
740
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +0000741 if (sshkey_is_cert(key)) {
742 if (key->cert == NULL)
743 return SSH_ERR_EXPECTED_CERT;
744 if (sshbuf_len(key->cert->certblob) == 0)
745 return SSH_ERR_KEY_LACKS_CERTBLOB;
746 }
Damien Miller86687062014-07-02 15:28:02 +1000747 type = force_plain ? sshkey_type_plain(key->type) : key->type;
748 typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid);
749
750 switch (type) {
751#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000752 case KEY_DSA_CERT:
753 case KEY_ECDSA_CERT:
754 case KEY_RSA_CERT:
755#endif /* WITH_OPENSSL */
756 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000757#ifdef WITH_XMSS
758 case KEY_XMSS_CERT:
759#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000760 /* Use the existing blob */
761 /* XXX modified flag? */
762 if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0)
763 return ret;
764 break;
765#ifdef WITH_OPENSSL
766 case KEY_DSA:
767 if (key->dsa == NULL)
768 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000769 DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g);
770 DSA_get0_key(key->dsa, &dsa_pub_key, NULL);
Damien Miller86687062014-07-02 15:28:02 +1000771 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000772 (ret = sshbuf_put_bignum2(b, dsa_p)) != 0 ||
773 (ret = sshbuf_put_bignum2(b, dsa_q)) != 0 ||
774 (ret = sshbuf_put_bignum2(b, dsa_g)) != 0 ||
775 (ret = sshbuf_put_bignum2(b, dsa_pub_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000776 return ret;
777 break;
Darren Tuckerd1a04212014-07-19 07:23:55 +1000778# ifdef OPENSSL_HAS_ECC
Damien Miller86687062014-07-02 15:28:02 +1000779 case KEY_ECDSA:
780 if (key->ecdsa == NULL)
781 return SSH_ERR_INVALID_ARGUMENT;
782 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
783 (ret = sshbuf_put_cstring(b,
784 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
785 (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0)
786 return ret;
787 break;
Darren Tuckerd1a04212014-07-19 07:23:55 +1000788# endif
Damien Miller86687062014-07-02 15:28:02 +1000789 case KEY_RSA:
790 if (key->rsa == NULL)
791 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000792 RSA_get0_key(key->rsa, &rsa_n, &rsa_e, NULL);
Damien Miller86687062014-07-02 15:28:02 +1000793 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000794 (ret = sshbuf_put_bignum2(b, rsa_e)) != 0 ||
795 (ret = sshbuf_put_bignum2(b, rsa_n)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000796 return ret;
797 break;
798#endif /* WITH_OPENSSL */
799 case KEY_ED25519:
800 if (key->ed25519_pk == NULL)
801 return SSH_ERR_INVALID_ARGUMENT;
802 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
803 (ret = sshbuf_put_string(b,
804 key->ed25519_pk, ED25519_PK_SZ)) != 0)
805 return ret;
806 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000807#ifdef WITH_XMSS
808 case KEY_XMSS:
809 if (key->xmss_name == NULL || key->xmss_pk == NULL ||
810 sshkey_xmss_pklen(key) == 0)
811 return SSH_ERR_INVALID_ARGUMENT;
812 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
813 (ret = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
814 (ret = sshbuf_put_string(b,
815 key->xmss_pk, sshkey_xmss_pklen(key))) != 0 ||
816 (ret = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0)
817 return ret;
818 break;
819#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000820 default:
821 return SSH_ERR_KEY_TYPE_UNKNOWN;
822 }
823 return 0;
824}
825
826int
djm@openbsd.org60b18252015-01-26 02:59:11 +0000827sshkey_putb(const struct sshkey *key, struct sshbuf *b)
Damien Miller86687062014-07-02 15:28:02 +1000828{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000829 return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000830}
831
832int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000833sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b,
834 enum sshkey_serialize_rep opts)
djm@openbsd.org60b18252015-01-26 02:59:11 +0000835{
836 struct sshbuf *tmp;
837 int r;
838
839 if ((tmp = sshbuf_new()) == NULL)
840 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000841 r = to_blob_buf(key, tmp, 0, opts);
djm@openbsd.org60b18252015-01-26 02:59:11 +0000842 if (r == 0)
843 r = sshbuf_put_stringb(b, tmp);
844 sshbuf_free(tmp);
845 return r;
846}
847
848int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000849sshkey_puts(const struct sshkey *key, struct sshbuf *b)
850{
851 return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT);
852}
853
854int
djm@openbsd.org60b18252015-01-26 02:59:11 +0000855sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b)
Damien Miller86687062014-07-02 15:28:02 +1000856{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000857 return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000858}
859
860static int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000861to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain,
862 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +1000863{
864 int ret = SSH_ERR_INTERNAL_ERROR;
865 size_t len;
866 struct sshbuf *b = NULL;
867
868 if (lenp != NULL)
869 *lenp = 0;
870 if (blobp != NULL)
871 *blobp = NULL;
872 if ((b = sshbuf_new()) == NULL)
873 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000874 if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000875 goto out;
876 len = sshbuf_len(b);
877 if (lenp != NULL)
878 *lenp = len;
879 if (blobp != NULL) {
880 if ((*blobp = malloc(len)) == NULL) {
881 ret = SSH_ERR_ALLOC_FAIL;
882 goto out;
883 }
884 memcpy(*blobp, sshbuf_ptr(b), len);
885 }
886 ret = 0;
887 out:
888 sshbuf_free(b);
889 return ret;
890}
891
892int
893sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
894{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000895 return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000896}
897
898int
899sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
900{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000901 return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000902}
903
904int
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000905sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg,
Damien Miller86687062014-07-02 15:28:02 +1000906 u_char **retp, size_t *lenp)
907{
908 u_char *blob = NULL, *ret = NULL;
909 size_t blob_len = 0;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000910 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +1000911
912 if (retp != NULL)
913 *retp = NULL;
914 if (lenp != NULL)
915 *lenp = 0;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000916 if (ssh_digest_bytes(dgst_alg) == 0) {
Damien Miller86687062014-07-02 15:28:02 +1000917 r = SSH_ERR_INVALID_ARGUMENT;
918 goto out;
919 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000920 if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT))
921 != 0)
Damien Miller86687062014-07-02 15:28:02 +1000922 goto out;
923 if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) {
924 r = SSH_ERR_ALLOC_FAIL;
925 goto out;
926 }
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000927 if ((r = ssh_digest_memory(dgst_alg, blob, blob_len,
Damien Miller86687062014-07-02 15:28:02 +1000928 ret, SSH_DIGEST_MAX_LENGTH)) != 0)
929 goto out;
930 /* success */
931 if (retp != NULL) {
932 *retp = ret;
933 ret = NULL;
934 }
935 if (lenp != NULL)
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000936 *lenp = ssh_digest_bytes(dgst_alg);
Damien Miller86687062014-07-02 15:28:02 +1000937 r = 0;
938 out:
939 free(ret);
940 if (blob != NULL) {
941 explicit_bzero(blob, blob_len);
942 free(blob);
943 }
944 return r;
945}
946
947static char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000948fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
Damien Miller86687062014-07-02 15:28:02 +1000949{
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000950 char *ret;
951 size_t plen = strlen(alg) + 1;
952 size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1;
Damien Miller86687062014-07-02 15:28:02 +1000953
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000954 if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL)
Damien Miller86687062014-07-02 15:28:02 +1000955 return NULL;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000956 strlcpy(ret, alg, rlen);
957 strlcat(ret, ":", rlen);
958 if (dgst_raw_len == 0)
959 return ret;
dtucker@openbsd.org696fb422019-07-07 01:05:00 +0000960 if (b64_ntop(dgst_raw, dgst_raw_len, ret + plen, rlen - plen) == -1) {
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000961 freezero(ret, rlen);
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000962 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000963 }
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000964 /* Trim padding characters from end */
965 ret[strcspn(ret, "=")] = '\0';
966 return ret;
967}
Damien Miller86687062014-07-02 15:28:02 +1000968
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000969static char *
970fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
971{
972 char *retval, hex[5];
973 size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2;
974
975 if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL)
976 return NULL;
977 strlcpy(retval, alg, rlen);
978 strlcat(retval, ":", rlen);
979 for (i = 0; i < dgst_raw_len; i++) {
980 snprintf(hex, sizeof(hex), "%s%02x",
981 i > 0 ? ":" : "", dgst_raw[i]);
982 strlcat(retval, hex, rlen);
983 }
Damien Miller86687062014-07-02 15:28:02 +1000984 return retval;
985}
986
987static char *
988fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len)
989{
990 char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' };
991 char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
992 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' };
993 u_int i, j = 0, rounds, seed = 1;
994 char *retval;
995
996 rounds = (dgst_raw_len / 2) + 1;
997 if ((retval = calloc(rounds, 6)) == NULL)
998 return NULL;
999 retval[j++] = 'x';
1000 for (i = 0; i < rounds; i++) {
1001 u_int idx0, idx1, idx2, idx3, idx4;
1002 if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) {
1003 idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) +
1004 seed) % 6;
1005 idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15;
1006 idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) +
1007 (seed / 6)) % 6;
1008 retval[j++] = vowels[idx0];
1009 retval[j++] = consonants[idx1];
1010 retval[j++] = vowels[idx2];
1011 if ((i + 1) < rounds) {
1012 idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15;
1013 idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15;
1014 retval[j++] = consonants[idx3];
1015 retval[j++] = '-';
1016 retval[j++] = consonants[idx4];
1017 seed = ((seed * 5) +
1018 ((((u_int)(dgst_raw[2 * i])) * 7) +
1019 ((u_int)(dgst_raw[(2 * i) + 1])))) % 36;
1020 }
1021 } else {
1022 idx0 = seed % 6;
1023 idx1 = 16;
1024 idx2 = seed / 6;
1025 retval[j++] = vowels[idx0];
1026 retval[j++] = consonants[idx1];
1027 retval[j++] = vowels[idx2];
1028 }
1029 }
1030 retval[j++] = 'x';
1031 retval[j++] = '\0';
1032 return retval;
1033}
1034
1035/*
1036 * Draw an ASCII-Art representing the fingerprint so human brain can
1037 * profit from its built-in pattern recognition ability.
1038 * This technique is called "random art" and can be found in some
1039 * scientific publications like this original paper:
1040 *
1041 * "Hash Visualization: a New Technique to improve Real-World Security",
1042 * Perrig A. and Song D., 1999, International Workshop on Cryptographic
1043 * Techniques and E-Commerce (CrypTEC '99)
1044 * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
1045 *
1046 * The subject came up in a talk by Dan Kaminsky, too.
1047 *
1048 * If you see the picture is different, the key is different.
1049 * If the picture looks the same, you still know nothing.
1050 *
1051 * The algorithm used here is a worm crawling over a discrete plane,
1052 * leaving a trace (augmenting the field) everywhere it goes.
1053 * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
1054 * makes the respective movement vector be ignored for this turn.
1055 * Graphs are not unambiguous, because circles in graphs can be
1056 * walked in either direction.
1057 */
1058
1059/*
1060 * Field sizes for the random art. Have to be odd, so the starting point
1061 * can be in the exact middle of the picture, and FLDBASE should be >=8 .
1062 * Else pictures would be too dense, and drawing the frame would
1063 * fail, too, because the key type would not fit in anymore.
1064 */
1065#define FLDBASE 8
1066#define FLDSIZE_Y (FLDBASE + 1)
1067#define FLDSIZE_X (FLDBASE * 2 + 1)
1068static char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001069fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len,
Damien Miller86687062014-07-02 15:28:02 +10001070 const struct sshkey *k)
1071{
1072 /*
1073 * Chars to be used after each other every time the worm
1074 * intersects with itself. Matter of taste.
1075 */
1076 char *augmentation_string = " .o+=*BOX@%&#/^SE";
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001077 char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X];
Damien Miller86687062014-07-02 15:28:02 +10001078 u_char field[FLDSIZE_X][FLDSIZE_Y];
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001079 size_t i, tlen, hlen;
Damien Miller86687062014-07-02 15:28:02 +10001080 u_int b;
Damien Miller61e28e52014-07-03 21:22:22 +10001081 int x, y, r;
Damien Miller86687062014-07-02 15:28:02 +10001082 size_t len = strlen(augmentation_string) - 1;
1083
1084 if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL)
1085 return NULL;
1086
1087 /* initialize field */
1088 memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
1089 x = FLDSIZE_X / 2;
1090 y = FLDSIZE_Y / 2;
1091
1092 /* process raw key */
1093 for (i = 0; i < dgst_raw_len; i++) {
1094 int input;
1095 /* each byte conveys four 2-bit move commands */
1096 input = dgst_raw[i];
1097 for (b = 0; b < 4; b++) {
1098 /* evaluate 2 bit, rest is shifted later */
1099 x += (input & 0x1) ? 1 : -1;
1100 y += (input & 0x2) ? 1 : -1;
1101
1102 /* assure we are still in bounds */
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001103 x = MAXIMUM(x, 0);
1104 y = MAXIMUM(y, 0);
1105 x = MINIMUM(x, FLDSIZE_X - 1);
1106 y = MINIMUM(y, FLDSIZE_Y - 1);
Damien Miller86687062014-07-02 15:28:02 +10001107
1108 /* augment the field */
1109 if (field[x][y] < len - 2)
1110 field[x][y]++;
1111 input = input >> 2;
1112 }
1113 }
1114
1115 /* mark starting point and end point*/
1116 field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
1117 field[x][y] = len;
1118
Damien Miller61e28e52014-07-03 21:22:22 +10001119 /* assemble title */
1120 r = snprintf(title, sizeof(title), "[%s %u]",
1121 sshkey_type(k), sshkey_size(k));
1122 /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */
1123 if (r < 0 || r > (int)sizeof(title))
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001124 r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k));
1125 tlen = (r <= 0) ? 0 : strlen(title);
1126
1127 /* assemble hash ID. */
1128 r = snprintf(hash, sizeof(hash), "[%s]", alg);
1129 hlen = (r <= 0) ? 0 : strlen(hash);
Damien Miller86687062014-07-02 15:28:02 +10001130
1131 /* output upper border */
Damien Miller61e28e52014-07-03 21:22:22 +10001132 p = retval;
1133 *p++ = '+';
1134 for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++)
1135 *p++ = '-';
1136 memcpy(p, title, tlen);
1137 p += tlen;
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001138 for (i += tlen; i < FLDSIZE_X; i++)
Damien Miller86687062014-07-02 15:28:02 +10001139 *p++ = '-';
1140 *p++ = '+';
1141 *p++ = '\n';
1142
1143 /* output content */
1144 for (y = 0; y < FLDSIZE_Y; y++) {
1145 *p++ = '|';
1146 for (x = 0; x < FLDSIZE_X; x++)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001147 *p++ = augmentation_string[MINIMUM(field[x][y], len)];
Damien Miller86687062014-07-02 15:28:02 +10001148 *p++ = '|';
1149 *p++ = '\n';
1150 }
1151
1152 /* output lower border */
1153 *p++ = '+';
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001154 for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++)
1155 *p++ = '-';
1156 memcpy(p, hash, hlen);
1157 p += hlen;
1158 for (i += hlen; i < FLDSIZE_X; i++)
Damien Miller86687062014-07-02 15:28:02 +10001159 *p++ = '-';
1160 *p++ = '+';
1161
1162 return retval;
1163}
1164
1165char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001166sshkey_fingerprint(const struct sshkey *k, int dgst_alg,
Damien Miller86687062014-07-02 15:28:02 +10001167 enum sshkey_fp_rep dgst_rep)
1168{
1169 char *retval = NULL;
1170 u_char *dgst_raw;
1171 size_t dgst_raw_len;
1172
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001173 if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001174 return NULL;
1175 switch (dgst_rep) {
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001176 case SSH_FP_DEFAULT:
1177 if (dgst_alg == SSH_DIGEST_MD5) {
1178 retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
1179 dgst_raw, dgst_raw_len);
1180 } else {
1181 retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
1182 dgst_raw, dgst_raw_len);
1183 }
1184 break;
Damien Miller86687062014-07-02 15:28:02 +10001185 case SSH_FP_HEX:
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001186 retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
1187 dgst_raw, dgst_raw_len);
1188 break;
1189 case SSH_FP_BASE64:
1190 retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
1191 dgst_raw, dgst_raw_len);
Damien Miller86687062014-07-02 15:28:02 +10001192 break;
1193 case SSH_FP_BUBBLEBABBLE:
1194 retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
1195 break;
1196 case SSH_FP_RANDOMART:
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001197 retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg),
1198 dgst_raw, dgst_raw_len, k);
Damien Miller86687062014-07-02 15:28:02 +10001199 break;
1200 default:
1201 explicit_bzero(dgst_raw, dgst_raw_len);
1202 free(dgst_raw);
1203 return NULL;
1204 }
1205 explicit_bzero(dgst_raw, dgst_raw_len);
1206 free(dgst_raw);
1207 return retval;
1208}
1209
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001210static int
1211peek_type_nid(const char *s, size_t l, int *nid)
1212{
1213 const struct keytype *kt;
Damien Miller86687062014-07-02 15:28:02 +10001214
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001215 for (kt = keytypes; kt->type != -1; kt++) {
1216 if (kt->name == NULL || strlen(kt->name) != l)
1217 continue;
1218 if (memcmp(s, kt->name, l) == 0) {
1219 *nid = -1;
1220 if (kt->type == KEY_ECDSA || kt->type == KEY_ECDSA_CERT)
1221 *nid = kt->nid;
1222 return kt->type;
1223 }
1224 }
1225 return KEY_UNSPEC;
1226}
1227
1228/* XXX this can now be made const char * */
Damien Miller86687062014-07-02 15:28:02 +10001229int
1230sshkey_read(struct sshkey *ret, char **cpp)
1231{
1232 struct sshkey *k;
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001233 char *cp, *blobcopy;
1234 size_t space;
Damien Miller86687062014-07-02 15:28:02 +10001235 int r, type, curve_nid = -1;
1236 struct sshbuf *blob;
Damien Miller86687062014-07-02 15:28:02 +10001237
dtucker@openbsd.org7fadbb62017-03-10 03:48:57 +00001238 if (ret == NULL)
1239 return SSH_ERR_INVALID_ARGUMENT;
1240
Damien Miller86687062014-07-02 15:28:02 +10001241 switch (ret->type) {
Damien Miller86687062014-07-02 15:28:02 +10001242 case KEY_UNSPEC:
1243 case KEY_RSA:
1244 case KEY_DSA:
1245 case KEY_ECDSA:
1246 case KEY_ED25519:
Damien Miller86687062014-07-02 15:28:02 +10001247 case KEY_DSA_CERT:
1248 case KEY_ECDSA_CERT:
1249 case KEY_RSA_CERT:
1250 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001251#ifdef WITH_XMSS
1252 case KEY_XMSS:
1253 case KEY_XMSS_CERT:
1254#endif /* WITH_XMSS */
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001255 break; /* ok */
Damien Miller86687062014-07-02 15:28:02 +10001256 default:
1257 return SSH_ERR_INVALID_ARGUMENT;
1258 }
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001259
1260 /* Decode type */
1261 cp = *cpp;
1262 space = strcspn(cp, " \t");
1263 if (space == strlen(cp))
1264 return SSH_ERR_INVALID_FORMAT;
1265 if ((type = peek_type_nid(cp, space, &curve_nid)) == KEY_UNSPEC)
1266 return SSH_ERR_INVALID_FORMAT;
1267
1268 /* skip whitespace */
1269 for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
1270 ;
1271 if (*cp == '\0')
1272 return SSH_ERR_INVALID_FORMAT;
1273 if (ret->type != KEY_UNSPEC && ret->type != type)
1274 return SSH_ERR_KEY_TYPE_MISMATCH;
1275 if ((blob = sshbuf_new()) == NULL)
1276 return SSH_ERR_ALLOC_FAIL;
1277
1278 /* find end of keyblob and decode */
1279 space = strcspn(cp, " \t");
1280 if ((blobcopy = strndup(cp, space)) == NULL) {
1281 sshbuf_free(blob);
1282 return SSH_ERR_ALLOC_FAIL;
1283 }
1284 if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) {
1285 free(blobcopy);
1286 sshbuf_free(blob);
1287 return r;
1288 }
1289 free(blobcopy);
1290 if ((r = sshkey_fromb(blob, &k)) != 0) {
1291 sshbuf_free(blob);
1292 return r;
1293 }
1294 sshbuf_free(blob);
1295
1296 /* skip whitespace and leave cp at start of comment */
1297 for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
1298 ;
1299
1300 /* ensure type of blob matches type at start of line */
1301 if (k->type != type) {
1302 sshkey_free(k);
1303 return SSH_ERR_KEY_TYPE_MISMATCH;
1304 }
1305 if (sshkey_type_plain(type) == KEY_ECDSA && curve_nid != k->ecdsa_nid) {
1306 sshkey_free(k);
1307 return SSH_ERR_EC_CURVE_MISMATCH;
1308 }
1309
1310 /* Fill in ret from parsed key */
1311 ret->type = type;
1312 if (sshkey_is_cert(ret)) {
1313 if (!sshkey_is_cert(k)) {
1314 sshkey_free(k);
1315 return SSH_ERR_EXPECTED_CERT;
1316 }
1317 if (ret->cert != NULL)
1318 cert_free(ret->cert);
1319 ret->cert = k->cert;
1320 k->cert = NULL;
1321 }
1322 switch (sshkey_type_plain(ret->type)) {
1323#ifdef WITH_OPENSSL
1324 case KEY_RSA:
1325 RSA_free(ret->rsa);
1326 ret->rsa = k->rsa;
1327 k->rsa = NULL;
1328#ifdef DEBUG_PK
1329 RSA_print_fp(stderr, ret->rsa, 8);
1330#endif
1331 break;
1332 case KEY_DSA:
1333 DSA_free(ret->dsa);
1334 ret->dsa = k->dsa;
1335 k->dsa = NULL;
1336#ifdef DEBUG_PK
1337 DSA_print_fp(stderr, ret->dsa, 8);
1338#endif
1339 break;
1340# ifdef OPENSSL_HAS_ECC
1341 case KEY_ECDSA:
1342 EC_KEY_free(ret->ecdsa);
1343 ret->ecdsa = k->ecdsa;
1344 ret->ecdsa_nid = k->ecdsa_nid;
1345 k->ecdsa = NULL;
1346 k->ecdsa_nid = -1;
1347#ifdef DEBUG_PK
1348 sshkey_dump_ec_key(ret->ecdsa);
1349#endif
1350 break;
1351# endif /* OPENSSL_HAS_ECC */
1352#endif /* WITH_OPENSSL */
1353 case KEY_ED25519:
1354 freezero(ret->ed25519_pk, ED25519_PK_SZ);
1355 ret->ed25519_pk = k->ed25519_pk;
1356 k->ed25519_pk = NULL;
1357#ifdef DEBUG_PK
1358 /* XXX */
1359#endif
1360 break;
1361#ifdef WITH_XMSS
1362 case KEY_XMSS:
1363 free(ret->xmss_pk);
1364 ret->xmss_pk = k->xmss_pk;
1365 k->xmss_pk = NULL;
1366 free(ret->xmss_state);
1367 ret->xmss_state = k->xmss_state;
1368 k->xmss_state = NULL;
1369 free(ret->xmss_name);
1370 ret->xmss_name = k->xmss_name;
1371 k->xmss_name = NULL;
1372 free(ret->xmss_filename);
1373 ret->xmss_filename = k->xmss_filename;
1374 k->xmss_filename = NULL;
1375#ifdef DEBUG_PK
1376 /* XXX */
1377#endif
1378 break;
1379#endif /* WITH_XMSS */
1380 default:
1381 sshkey_free(k);
1382 return SSH_ERR_INTERNAL_ERROR;
1383 }
1384 sshkey_free(k);
1385
1386 /* success */
1387 *cpp = cp;
1388 return 0;
Damien Miller86687062014-07-02 15:28:02 +10001389}
1390
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001391
Damien Miller86687062014-07-02 15:28:02 +10001392int
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001393sshkey_to_base64(const struct sshkey *key, char **b64p)
Damien Miller86687062014-07-02 15:28:02 +10001394{
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001395 int r = SSH_ERR_INTERNAL_ERROR;
1396 struct sshbuf *b = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001397 char *uu = NULL;
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001398
1399 if (b64p != NULL)
1400 *b64p = NULL;
1401 if ((b = sshbuf_new()) == NULL)
1402 return SSH_ERR_ALLOC_FAIL;
1403 if ((r = sshkey_putb(key, b)) != 0)
1404 goto out;
djm@openbsd.org16dd8b22019-07-16 13:18:39 +00001405 if ((uu = sshbuf_dtob64_string(b, 0)) == NULL) {
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001406 r = SSH_ERR_ALLOC_FAIL;
1407 goto out;
1408 }
1409 /* Success */
1410 if (b64p != NULL) {
1411 *b64p = uu;
1412 uu = NULL;
1413 }
1414 r = 0;
1415 out:
1416 sshbuf_free(b);
1417 free(uu);
1418 return r;
1419}
1420
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00001421int
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001422sshkey_format_text(const struct sshkey *key, struct sshbuf *b)
1423{
1424 int r = SSH_ERR_INTERNAL_ERROR;
1425 char *uu = NULL;
1426
djm@openbsd.org873d3e72017-04-30 23:18:44 +00001427 if ((r = sshkey_to_base64(key, &uu)) != 0)
1428 goto out;
1429 if ((r = sshbuf_putf(b, "%s %s",
1430 sshkey_ssh_name(key), uu)) != 0)
1431 goto out;
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001432 r = 0;
1433 out:
1434 free(uu);
1435 return r;
1436}
1437
1438int
1439sshkey_write(const struct sshkey *key, FILE *f)
1440{
1441 struct sshbuf *b = NULL;
1442 int r = SSH_ERR_INTERNAL_ERROR;
1443
1444 if ((b = sshbuf_new()) == NULL)
1445 return SSH_ERR_ALLOC_FAIL;
1446 if ((r = sshkey_format_text(key, b)) != 0)
1447 goto out;
1448 if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) {
1449 if (feof(f))
1450 errno = EPIPE;
1451 r = SSH_ERR_SYSTEM_ERROR;
1452 goto out;
1453 }
1454 /* Success */
1455 r = 0;
1456 out:
1457 sshbuf_free(b);
1458 return r;
Damien Miller86687062014-07-02 15:28:02 +10001459}
1460
1461const char *
1462sshkey_cert_type(const struct sshkey *k)
1463{
1464 switch (k->cert->type) {
1465 case SSH2_CERT_TYPE_USER:
1466 return "user";
1467 case SSH2_CERT_TYPE_HOST:
1468 return "host";
1469 default:
1470 return "unknown";
1471 }
1472}
1473
1474#ifdef WITH_OPENSSL
1475static int
1476rsa_generate_private_key(u_int bits, RSA **rsap)
1477{
1478 RSA *private = NULL;
1479 BIGNUM *f4 = NULL;
1480 int ret = SSH_ERR_INTERNAL_ERROR;
1481
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001482 if (rsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001483 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001484 if (bits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
1485 bits > SSHBUF_MAX_BIGNUM * 8)
1486 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001487 *rsap = NULL;
1488 if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) {
1489 ret = SSH_ERR_ALLOC_FAIL;
1490 goto out;
1491 }
1492 if (!BN_set_word(f4, RSA_F4) ||
1493 !RSA_generate_key_ex(private, bits, f4, NULL)) {
1494 ret = SSH_ERR_LIBCRYPTO_ERROR;
1495 goto out;
1496 }
1497 *rsap = private;
1498 private = NULL;
1499 ret = 0;
1500 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001501 RSA_free(private);
1502 BN_free(f4);
Damien Miller86687062014-07-02 15:28:02 +10001503 return ret;
1504}
1505
1506static int
1507dsa_generate_private_key(u_int bits, DSA **dsap)
1508{
1509 DSA *private;
1510 int ret = SSH_ERR_INTERNAL_ERROR;
1511
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001512 if (dsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001513 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001514 if (bits != 1024)
1515 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001516 if ((private = DSA_new()) == NULL) {
1517 ret = SSH_ERR_ALLOC_FAIL;
1518 goto out;
1519 }
1520 *dsap = NULL;
1521 if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
1522 NULL, NULL) || !DSA_generate_key(private)) {
Damien Miller86687062014-07-02 15:28:02 +10001523 ret = SSH_ERR_LIBCRYPTO_ERROR;
1524 goto out;
1525 }
1526 *dsap = private;
1527 private = NULL;
1528 ret = 0;
1529 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001530 DSA_free(private);
Damien Miller86687062014-07-02 15:28:02 +10001531 return ret;
1532}
1533
1534# ifdef OPENSSL_HAS_ECC
1535int
1536sshkey_ecdsa_key_to_nid(EC_KEY *k)
1537{
1538 EC_GROUP *eg;
1539 int nids[] = {
1540 NID_X9_62_prime256v1,
1541 NID_secp384r1,
1542# ifdef OPENSSL_HAS_NISTP521
1543 NID_secp521r1,
1544# endif /* OPENSSL_HAS_NISTP521 */
1545 -1
1546 };
1547 int nid;
1548 u_int i;
1549 BN_CTX *bnctx;
1550 const EC_GROUP *g = EC_KEY_get0_group(k);
1551
1552 /*
1553 * The group may be stored in a ASN.1 encoded private key in one of two
1554 * ways: as a "named group", which is reconstituted by ASN.1 object ID
1555 * or explicit group parameters encoded into the key blob. Only the
1556 * "named group" case sets the group NID for us, but we can figure
1557 * it out for the other case by comparing against all the groups that
1558 * are supported.
1559 */
1560 if ((nid = EC_GROUP_get_curve_name(g)) > 0)
1561 return nid;
1562 if ((bnctx = BN_CTX_new()) == NULL)
1563 return -1;
1564 for (i = 0; nids[i] != -1; i++) {
1565 if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) {
1566 BN_CTX_free(bnctx);
1567 return -1;
1568 }
1569 if (EC_GROUP_cmp(g, eg, bnctx) == 0)
1570 break;
1571 EC_GROUP_free(eg);
1572 }
1573 BN_CTX_free(bnctx);
1574 if (nids[i] != -1) {
1575 /* Use the group with the NID attached */
1576 EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
1577 if (EC_KEY_set_group(k, eg) != 1) {
1578 EC_GROUP_free(eg);
1579 return -1;
1580 }
1581 }
1582 return nids[i];
1583}
1584
1585static int
1586ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap)
1587{
1588 EC_KEY *private;
1589 int ret = SSH_ERR_INTERNAL_ERROR;
1590
djm@openbsd.org5f02bb12017-05-08 06:11:06 +00001591 if (nid == NULL || ecdsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001592 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org5f02bb12017-05-08 06:11:06 +00001593 if ((*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
1594 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001595 *ecdsap = NULL;
1596 if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) {
1597 ret = SSH_ERR_ALLOC_FAIL;
1598 goto out;
1599 }
1600 if (EC_KEY_generate_key(private) != 1) {
1601 ret = SSH_ERR_LIBCRYPTO_ERROR;
1602 goto out;
1603 }
1604 EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
1605 *ecdsap = private;
1606 private = NULL;
1607 ret = 0;
1608 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001609 EC_KEY_free(private);
Damien Miller86687062014-07-02 15:28:02 +10001610 return ret;
1611}
1612# endif /* OPENSSL_HAS_ECC */
1613#endif /* WITH_OPENSSL */
1614
1615int
1616sshkey_generate(int type, u_int bits, struct sshkey **keyp)
1617{
1618 struct sshkey *k;
1619 int ret = SSH_ERR_INTERNAL_ERROR;
1620
1621 if (keyp == NULL)
1622 return SSH_ERR_INVALID_ARGUMENT;
1623 *keyp = NULL;
1624 if ((k = sshkey_new(KEY_UNSPEC)) == NULL)
1625 return SSH_ERR_ALLOC_FAIL;
1626 switch (type) {
1627 case KEY_ED25519:
1628 if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL ||
1629 (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) {
1630 ret = SSH_ERR_ALLOC_FAIL;
1631 break;
1632 }
1633 crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
1634 ret = 0;
1635 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001636#ifdef WITH_XMSS
1637 case KEY_XMSS:
1638 ret = sshkey_xmss_generate_private_key(k, bits);
1639 break;
1640#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10001641#ifdef WITH_OPENSSL
1642 case KEY_DSA:
1643 ret = dsa_generate_private_key(bits, &k->dsa);
1644 break;
1645# ifdef OPENSSL_HAS_ECC
1646 case KEY_ECDSA:
1647 ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid,
1648 &k->ecdsa);
1649 break;
1650# endif /* OPENSSL_HAS_ECC */
1651 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10001652 ret = rsa_generate_private_key(bits, &k->rsa);
1653 break;
1654#endif /* WITH_OPENSSL */
1655 default:
1656 ret = SSH_ERR_INVALID_ARGUMENT;
1657 }
1658 if (ret == 0) {
1659 k->type = type;
1660 *keyp = k;
1661 } else
1662 sshkey_free(k);
1663 return ret;
1664}
1665
1666int
1667sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key)
1668{
1669 u_int i;
1670 const struct sshkey_cert *from;
1671 struct sshkey_cert *to;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001672 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +10001673
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001674 if (to_key == NULL || (from = from_key->cert) == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001675 return SSH_ERR_INVALID_ARGUMENT;
1676
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001677 if ((to = cert_new()) == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001678 return SSH_ERR_ALLOC_FAIL;
1679
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001680 if ((r = sshbuf_putb(to->certblob, from->certblob)) != 0 ||
1681 (r = sshbuf_putb(to->critical, from->critical)) != 0 ||
1682 (r = sshbuf_putb(to->extensions, from->extensions)) != 0)
1683 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001684
1685 to->serial = from->serial;
1686 to->type = from->type;
1687 if (from->key_id == NULL)
1688 to->key_id = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001689 else if ((to->key_id = strdup(from->key_id)) == NULL) {
1690 r = SSH_ERR_ALLOC_FAIL;
1691 goto out;
1692 }
Damien Miller86687062014-07-02 15:28:02 +10001693 to->valid_after = from->valid_after;
1694 to->valid_before = from->valid_before;
1695 if (from->signature_key == NULL)
1696 to->signature_key = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001697 else if ((r = sshkey_from_private(from->signature_key,
Damien Miller86687062014-07-02 15:28:02 +10001698 &to->signature_key)) != 0)
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001699 goto out;
1700 if (from->signature_type != NULL &&
1701 (to->signature_type = strdup(from->signature_type)) == NULL) {
1702 r = SSH_ERR_ALLOC_FAIL;
1703 goto out;
1704 }
1705 if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) {
1706 r = SSH_ERR_INVALID_ARGUMENT;
1707 goto out;
1708 }
Damien Miller86687062014-07-02 15:28:02 +10001709 if (from->nprincipals > 0) {
1710 if ((to->principals = calloc(from->nprincipals,
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001711 sizeof(*to->principals))) == NULL) {
1712 r = SSH_ERR_ALLOC_FAIL;
1713 goto out;
1714 }
Damien Miller86687062014-07-02 15:28:02 +10001715 for (i = 0; i < from->nprincipals; i++) {
1716 to->principals[i] = strdup(from->principals[i]);
1717 if (to->principals[i] == NULL) {
1718 to->nprincipals = i;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001719 r = SSH_ERR_ALLOC_FAIL;
1720 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001721 }
1722 }
1723 }
1724 to->nprincipals = from->nprincipals;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001725
1726 /* success */
1727 cert_free(to_key->cert);
1728 to_key->cert = to;
1729 to = NULL;
1730 r = 0;
1731 out:
1732 cert_free(to);
1733 return r;
Damien Miller86687062014-07-02 15:28:02 +10001734}
1735
1736int
1737sshkey_from_private(const struct sshkey *k, struct sshkey **pkp)
1738{
1739 struct sshkey *n = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001740 int r = SSH_ERR_INTERNAL_ERROR;
1741#ifdef WITH_OPENSSL
1742 const BIGNUM *rsa_n, *rsa_e;
1743 BIGNUM *rsa_n_dup = NULL, *rsa_e_dup = NULL;
1744 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
1745 BIGNUM *dsa_p_dup = NULL, *dsa_q_dup = NULL, *dsa_g_dup = NULL;
1746 BIGNUM *dsa_pub_key_dup = NULL;
1747#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10001748
djm@openbsd.org1a2663a2015-10-15 23:08:23 +00001749 *pkp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001750 switch (k->type) {
1751#ifdef WITH_OPENSSL
1752 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +10001753 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001754 if ((n = sshkey_new(k->type)) == NULL) {
1755 r = SSH_ERR_ALLOC_FAIL;
1756 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001757 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001758
1759 DSA_get0_pqg(k->dsa, &dsa_p, &dsa_q, &dsa_g);
1760 DSA_get0_key(k->dsa, &dsa_pub_key, NULL);
1761 if ((dsa_p_dup = BN_dup(dsa_p)) == NULL ||
1762 (dsa_q_dup = BN_dup(dsa_q)) == NULL ||
1763 (dsa_g_dup = BN_dup(dsa_g)) == NULL ||
1764 (dsa_pub_key_dup = BN_dup(dsa_pub_key)) == NULL) {
1765 r = SSH_ERR_ALLOC_FAIL;
1766 goto out;
1767 }
1768 if (!DSA_set0_pqg(n->dsa, dsa_p_dup, dsa_q_dup, dsa_g_dup)) {
1769 r = SSH_ERR_LIBCRYPTO_ERROR;
1770 goto out;
1771 }
1772 dsa_p_dup = dsa_q_dup = dsa_g_dup = NULL; /* transferred */
1773 if (!DSA_set0_key(n->dsa, dsa_pub_key_dup, NULL)) {
1774 r = SSH_ERR_LIBCRYPTO_ERROR;
1775 goto out;
1776 }
1777 dsa_pub_key_dup = NULL; /* transferred */
1778
Damien Miller86687062014-07-02 15:28:02 +10001779 break;
1780# ifdef OPENSSL_HAS_ECC
1781 case KEY_ECDSA:
1782 case KEY_ECDSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001783 if ((n = sshkey_new(k->type)) == NULL) {
1784 r = SSH_ERR_ALLOC_FAIL;
1785 goto out;
1786 }
Damien Miller86687062014-07-02 15:28:02 +10001787 n->ecdsa_nid = k->ecdsa_nid;
1788 n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
1789 if (n->ecdsa == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001790 r = SSH_ERR_ALLOC_FAIL;
1791 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001792 }
1793 if (EC_KEY_set_public_key(n->ecdsa,
1794 EC_KEY_get0_public_key(k->ecdsa)) != 1) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001795 r = SSH_ERR_LIBCRYPTO_ERROR;
1796 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001797 }
1798 break;
1799# endif /* OPENSSL_HAS_ECC */
1800 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10001801 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001802 if ((n = sshkey_new(k->type)) == NULL) {
1803 r = SSH_ERR_ALLOC_FAIL;
1804 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001805 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001806 RSA_get0_key(k->rsa, &rsa_n, &rsa_e, NULL);
1807 if ((rsa_n_dup = BN_dup(rsa_n)) == NULL ||
1808 (rsa_e_dup = BN_dup(rsa_e)) == NULL) {
1809 r = SSH_ERR_ALLOC_FAIL;
1810 goto out;
1811 }
1812 if (!RSA_set0_key(n->rsa, rsa_n_dup, rsa_e_dup, NULL)) {
1813 r = SSH_ERR_LIBCRYPTO_ERROR;
1814 goto out;
1815 }
1816 rsa_n_dup = rsa_e_dup = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10001817 break;
1818#endif /* WITH_OPENSSL */
1819 case KEY_ED25519:
1820 case KEY_ED25519_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001821 if ((n = sshkey_new(k->type)) == NULL) {
1822 r = SSH_ERR_ALLOC_FAIL;
1823 goto out;
1824 }
Damien Miller86687062014-07-02 15:28:02 +10001825 if (k->ed25519_pk != NULL) {
1826 if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001827 r = SSH_ERR_ALLOC_FAIL;
1828 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001829 }
1830 memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
1831 }
1832 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001833#ifdef WITH_XMSS
1834 case KEY_XMSS:
1835 case KEY_XMSS_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001836 if ((n = sshkey_new(k->type)) == NULL) {
1837 r = SSH_ERR_ALLOC_FAIL;
1838 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001839 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001840 if ((r = sshkey_xmss_init(n, k->xmss_name)) != 0)
1841 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001842 if (k->xmss_pk != NULL) {
1843 size_t pklen = sshkey_xmss_pklen(k);
1844 if (pklen == 0 || sshkey_xmss_pklen(n) != pklen) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001845 r = SSH_ERR_INTERNAL_ERROR;
1846 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001847 }
1848 if ((n->xmss_pk = malloc(pklen)) == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001849 r = SSH_ERR_ALLOC_FAIL;
1850 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001851 }
1852 memcpy(n->xmss_pk, k->xmss_pk, pklen);
1853 }
1854 break;
1855#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10001856 default:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001857 r = SSH_ERR_KEY_TYPE_UNKNOWN;
1858 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001859 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001860 if (sshkey_is_cert(k) && (r = sshkey_cert_copy(k, n)) != 0)
1861 goto out;
1862 /* success */
Damien Miller86687062014-07-02 15:28:02 +10001863 *pkp = n;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001864 n = NULL;
1865 r = 0;
1866 out:
1867 sshkey_free(n);
Darren Tuckercce8cbe2018-09-15 19:44:06 +10001868#ifdef WITH_OPENSSL
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001869 BN_clear_free(rsa_n_dup);
1870 BN_clear_free(rsa_e_dup);
1871 BN_clear_free(dsa_p_dup);
1872 BN_clear_free(dsa_q_dup);
1873 BN_clear_free(dsa_g_dup);
1874 BN_clear_free(dsa_pub_key_dup);
Darren Tuckercce8cbe2018-09-15 19:44:06 +10001875#endif
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001876
1877 return r;
Damien Miller86687062014-07-02 15:28:02 +10001878}
1879
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00001880int
1881sshkey_is_shielded(struct sshkey *k)
1882{
1883 return k != NULL && k->shielded_private != NULL;
1884}
1885
1886int
1887sshkey_shield_private(struct sshkey *k)
1888{
1889 struct sshbuf *prvbuf = NULL;
1890 u_char *prekey = NULL, *enc = NULL, keyiv[SSH_DIGEST_MAX_LENGTH];
1891 struct sshcipher_ctx *cctx = NULL;
1892 const struct sshcipher *cipher;
1893 size_t i, enclen = 0;
1894 struct sshkey *kswap = NULL, tmp;
1895 int r = SSH_ERR_INTERNAL_ERROR;
1896
1897#ifdef DEBUG_PK
1898 fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k));
1899#endif
1900 if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) {
1901 r = SSH_ERR_INVALID_ARGUMENT;
1902 goto out;
1903 }
1904 if (cipher_keylen(cipher) + cipher_ivlen(cipher) >
1905 ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) {
1906 r = SSH_ERR_INTERNAL_ERROR;
1907 goto out;
1908 }
1909
1910 /* Prepare a random pre-key, and from it an ephemeral key */
1911 if ((prekey = malloc(SSHKEY_SHIELD_PREKEY_LEN)) == NULL) {
1912 r = SSH_ERR_ALLOC_FAIL;
1913 goto out;
1914 }
1915 arc4random_buf(prekey, SSHKEY_SHIELD_PREKEY_LEN);
1916 if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH,
1917 prekey, SSHKEY_SHIELD_PREKEY_LEN,
1918 keyiv, SSH_DIGEST_MAX_LENGTH)) != 0)
1919 goto out;
1920#ifdef DEBUG_PK
1921 fprintf(stderr, "%s: key+iv\n", __func__);
1922 sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH),
1923 stderr);
1924#endif
1925 if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher),
1926 keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 1)) != 0)
1927 goto out;
1928
1929 /* Serialise and encrypt the private key using the ephemeral key */
1930 if ((prvbuf = sshbuf_new()) == NULL) {
1931 r = SSH_ERR_ALLOC_FAIL;
1932 goto out;
1933 }
1934 if (sshkey_is_shielded(k) && (r = sshkey_unshield_private(k)) != 0)
1935 goto out;
1936 if ((r = sshkey_private_serialize_opt(k, prvbuf,
1937 SSHKEY_SERIALIZE_FULL)) != 0)
1938 goto out;
1939 /* pad to cipher blocksize */
1940 i = 0;
1941 while (sshbuf_len(prvbuf) % cipher_blocksize(cipher)) {
1942 if ((r = sshbuf_put_u8(prvbuf, ++i & 0xff)) != 0)
1943 goto out;
1944 }
1945#ifdef DEBUG_PK
1946 fprintf(stderr, "%s: serialised\n", __func__);
1947 sshbuf_dump(prvbuf, stderr);
1948#endif
1949 /* encrypt */
1950 enclen = sshbuf_len(prvbuf);
1951 if ((enc = malloc(enclen)) == NULL) {
1952 r = SSH_ERR_ALLOC_FAIL;
1953 goto out;
1954 }
1955 if ((r = cipher_crypt(cctx, 0, enc,
1956 sshbuf_ptr(prvbuf), sshbuf_len(prvbuf), 0, 0)) != 0)
1957 goto out;
1958#ifdef DEBUG_PK
1959 fprintf(stderr, "%s: encrypted\n", __func__);
1960 sshbuf_dump_data(enc, enclen, stderr);
1961#endif
1962
1963 /* Make a scrubbed, public-only copy of our private key argument */
1964 if ((r = sshkey_from_private(k, &kswap)) != 0)
1965 goto out;
1966
1967 /* Swap the private key out (it will be destroyed below) */
1968 tmp = *kswap;
1969 *kswap = *k;
1970 *k = tmp;
1971
1972 /* Insert the shielded key into our argument */
1973 k->shielded_private = enc;
1974 k->shielded_len = enclen;
1975 k->shield_prekey = prekey;
1976 k->shield_prekey_len = SSHKEY_SHIELD_PREKEY_LEN;
1977 enc = prekey = NULL; /* transferred */
1978 enclen = 0;
1979
1980 /* success */
1981 r = 0;
1982
1983 out:
1984 /* XXX behaviour on error - invalidate original private key? */
1985 cipher_free(cctx);
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00001986 explicit_bzero(keyiv, sizeof(keyiv));
1987 explicit_bzero(&tmp, sizeof(tmp));
djm@openbsd.orgb2e3e572019-06-27 06:29:35 +00001988 freezero(enc, enclen);
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00001989 freezero(prekey, SSHKEY_SHIELD_PREKEY_LEN);
1990 sshkey_free(kswap);
1991 sshbuf_free(prvbuf);
1992 return r;
1993}
1994
1995int
1996sshkey_unshield_private(struct sshkey *k)
1997{
1998 struct sshbuf *prvbuf = NULL;
1999 u_char pad, *cp, keyiv[SSH_DIGEST_MAX_LENGTH];
2000 struct sshcipher_ctx *cctx = NULL;
2001 const struct sshcipher *cipher;
2002 size_t i;
2003 struct sshkey *kswap = NULL, tmp;
2004 int r = SSH_ERR_INTERNAL_ERROR;
2005
2006#ifdef DEBUG_PK
2007 fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k));
2008#endif
2009 if (!sshkey_is_shielded(k))
2010 return 0; /* nothing to do */
2011
2012 if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) {
2013 r = SSH_ERR_INVALID_ARGUMENT;
2014 goto out;
2015 }
2016 if (cipher_keylen(cipher) + cipher_ivlen(cipher) >
2017 ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) {
2018 r = SSH_ERR_INTERNAL_ERROR;
2019 goto out;
2020 }
2021 /* check size of shielded key blob */
2022 if (k->shielded_len < cipher_blocksize(cipher) ||
2023 (k->shielded_len % cipher_blocksize(cipher)) != 0) {
2024 r = SSH_ERR_INVALID_FORMAT;
2025 goto out;
2026 }
2027
2028 /* Calculate the ephemeral key from the prekey */
2029 if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH,
2030 k->shield_prekey, k->shield_prekey_len,
2031 keyiv, SSH_DIGEST_MAX_LENGTH)) != 0)
2032 goto out;
2033 if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher),
2034 keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 0)) != 0)
2035 goto out;
2036#ifdef DEBUG_PK
2037 fprintf(stderr, "%s: key+iv\n", __func__);
2038 sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH),
2039 stderr);
2040#endif
2041
2042 /* Decrypt and parse the shielded private key using the ephemeral key */
2043 if ((prvbuf = sshbuf_new()) == NULL) {
2044 r = SSH_ERR_ALLOC_FAIL;
2045 goto out;
2046 }
2047 if ((r = sshbuf_reserve(prvbuf, k->shielded_len, &cp)) != 0)
2048 goto out;
2049 /* decrypt */
2050#ifdef DEBUG_PK
2051 fprintf(stderr, "%s: encrypted\n", __func__);
2052 sshbuf_dump_data(k->shielded_private, k->shielded_len, stderr);
2053#endif
2054 if ((r = cipher_crypt(cctx, 0, cp,
2055 k->shielded_private, k->shielded_len, 0, 0)) != 0)
2056 goto out;
2057#ifdef DEBUG_PK
2058 fprintf(stderr, "%s: serialised\n", __func__);
2059 sshbuf_dump(prvbuf, stderr);
2060#endif
2061 /* Parse private key */
2062 if ((r = sshkey_private_deserialize(prvbuf, &kswap)) != 0)
2063 goto out;
2064 /* Check deterministic padding */
2065 i = 0;
2066 while (sshbuf_len(prvbuf)) {
2067 if ((r = sshbuf_get_u8(prvbuf, &pad)) != 0)
2068 goto out;
2069 if (pad != (++i & 0xff)) {
2070 r = SSH_ERR_INVALID_FORMAT;
2071 goto out;
2072 }
2073 }
2074
2075 /* Swap the parsed key back into place */
2076 tmp = *kswap;
2077 *kswap = *k;
2078 *k = tmp;
2079
2080 /* success */
2081 r = 0;
2082
2083 out:
2084 cipher_free(cctx);
2085 explicit_bzero(keyiv, sizeof(keyiv));
2086 explicit_bzero(&tmp, sizeof(tmp));
2087 sshkey_free(kswap);
2088 sshbuf_free(prvbuf);
2089 return r;
2090}
2091
Damien Miller86687062014-07-02 15:28:02 +10002092static int
djm@openbsd.org60b18252015-01-26 02:59:11 +00002093cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
Damien Miller86687062014-07-02 15:28:02 +10002094{
djm@openbsd.org60b18252015-01-26 02:59:11 +00002095 struct sshbuf *principals = NULL, *crit = NULL;
2096 struct sshbuf *exts = NULL, *ca = NULL;
2097 u_char *sig = NULL;
2098 size_t signed_len = 0, slen = 0, kidlen = 0;
Damien Miller86687062014-07-02 15:28:02 +10002099 int ret = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +10002100
2101 /* Copy the entire key blob for verification and later serialisation */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002102 if ((ret = sshbuf_putb(key->cert->certblob, certbuf)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002103 return ret;
2104
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002105 /* Parse body of certificate up to signature */
2106 if ((ret = sshbuf_get_u64(b, &key->cert->serial)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002107 (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 ||
2108 (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 ||
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002109 (ret = sshbuf_froms(b, &principals)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002110 (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 ||
2111 (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 ||
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002112 (ret = sshbuf_froms(b, &crit)) != 0 ||
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002113 (ret = sshbuf_froms(b, &exts)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002114 (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 ||
djm@openbsd.org60b18252015-01-26 02:59:11 +00002115 (ret = sshbuf_froms(b, &ca)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002116 /* XXX debug print error for ret */
2117 ret = SSH_ERR_INVALID_FORMAT;
2118 goto out;
2119 }
2120
2121 /* Signature is left in the buffer so we can calculate this length */
2122 signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b);
2123
2124 if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) {
2125 ret = SSH_ERR_INVALID_FORMAT;
2126 goto out;
2127 }
2128
2129 if (key->cert->type != SSH2_CERT_TYPE_USER &&
2130 key->cert->type != SSH2_CERT_TYPE_HOST) {
2131 ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE;
2132 goto out;
2133 }
2134
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002135 /* Parse principals section */
2136 while (sshbuf_len(principals) > 0) {
2137 char *principal = NULL;
2138 char **oprincipals = NULL;
2139
Damien Miller86687062014-07-02 15:28:02 +10002140 if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) {
2141 ret = SSH_ERR_INVALID_FORMAT;
2142 goto out;
2143 }
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002144 if ((ret = sshbuf_get_cstring(principals, &principal,
2145 NULL)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002146 ret = SSH_ERR_INVALID_FORMAT;
2147 goto out;
2148 }
2149 oprincipals = key->cert->principals;
deraadt@openbsd.org9e509d42017-05-31 09:15:42 +00002150 key->cert->principals = recallocarray(key->cert->principals,
2151 key->cert->nprincipals, key->cert->nprincipals + 1,
2152 sizeof(*key->cert->principals));
Damien Miller86687062014-07-02 15:28:02 +10002153 if (key->cert->principals == NULL) {
2154 free(principal);
2155 key->cert->principals = oprincipals;
2156 ret = SSH_ERR_ALLOC_FAIL;
2157 goto out;
2158 }
2159 key->cert->principals[key->cert->nprincipals++] = principal;
2160 }
2161
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002162 /*
2163 * Stash a copies of the critical options and extensions sections
2164 * for later use.
2165 */
2166 if ((ret = sshbuf_putb(key->cert->critical, crit)) != 0 ||
2167 (exts != NULL &&
2168 (ret = sshbuf_putb(key->cert->extensions, exts)) != 0))
Damien Miller86687062014-07-02 15:28:02 +10002169 goto out;
2170
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002171 /*
2172 * Validate critical options and extensions sections format.
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002173 */
2174 while (sshbuf_len(crit) != 0) {
2175 if ((ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0 ||
2176 (ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0) {
2177 sshbuf_reset(key->cert->critical);
Damien Miller86687062014-07-02 15:28:02 +10002178 ret = SSH_ERR_INVALID_FORMAT;
2179 goto out;
2180 }
2181 }
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002182 while (exts != NULL && sshbuf_len(exts) != 0) {
2183 if ((ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0 ||
2184 (ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0) {
2185 sshbuf_reset(key->cert->extensions);
Damien Miller86687062014-07-02 15:28:02 +10002186 ret = SSH_ERR_INVALID_FORMAT;
2187 goto out;
2188 }
2189 }
Damien Miller86687062014-07-02 15:28:02 +10002190
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002191 /* Parse CA key and check signature */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002192 if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002193 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2194 goto out;
2195 }
2196 if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) {
2197 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2198 goto out;
2199 }
Damien Miller86687062014-07-02 15:28:02 +10002200 if ((ret = sshkey_verify(key->cert->signature_key, sig, slen,
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002201 sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002202 goto out;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002203 if ((ret = get_sigtype(sig, slen, &key->cert->signature_type)) != 0)
2204 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002205
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002206 /* Success */
2207 ret = 0;
Damien Miller86687062014-07-02 15:28:02 +10002208 out:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002209 sshbuf_free(ca);
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002210 sshbuf_free(crit);
2211 sshbuf_free(exts);
2212 sshbuf_free(principals);
Damien Miller86687062014-07-02 15:28:02 +10002213 free(sig);
2214 return ret;
2215}
2216
Darren Tuckercce8cbe2018-09-15 19:44:06 +10002217#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002218static int
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002219check_rsa_length(const RSA *rsa)
2220{
2221 const BIGNUM *rsa_n;
2222
2223 RSA_get0_key(rsa, &rsa_n, NULL, NULL);
2224 if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
2225 return SSH_ERR_KEY_LENGTH;
2226 return 0;
2227}
Darren Tuckercce8cbe2018-09-15 19:44:06 +10002228#endif
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002229
2230static int
djm@openbsd.org60b18252015-01-26 02:59:11 +00002231sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
2232 int allow_cert)
Damien Miller86687062014-07-02 15:28:02 +10002233{
djm@openbsd.org54924b52015-01-14 10:46:28 +00002234 int type, ret = SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002235 char *ktype = NULL, *curve = NULL, *xmss_name = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002236 struct sshkey *key = NULL;
2237 size_t len;
2238 u_char *pk = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00002239 struct sshbuf *copy;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002240#if defined(WITH_OPENSSL)
2241 BIGNUM *rsa_n = NULL, *rsa_e = NULL;
2242 BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_pub_key = NULL;
2243# if defined(OPENSSL_HAS_ECC)
Damien Miller86687062014-07-02 15:28:02 +10002244 EC_POINT *q = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002245# endif /* OPENSSL_HAS_ECC */
2246#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10002247
2248#ifdef DEBUG_PK /* XXX */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002249 sshbuf_dump(b, stderr);
Damien Miller86687062014-07-02 15:28:02 +10002250#endif
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00002251 if (keyp != NULL)
2252 *keyp = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00002253 if ((copy = sshbuf_fromb(b)) == NULL) {
2254 ret = SSH_ERR_ALLOC_FAIL;
2255 goto out;
2256 }
Damien Miller86687062014-07-02 15:28:02 +10002257 if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
2258 ret = SSH_ERR_INVALID_FORMAT;
2259 goto out;
2260 }
2261
2262 type = sshkey_type_from_name(ktype);
Damien Miller86687062014-07-02 15:28:02 +10002263 if (!allow_cert && sshkey_type_is_cert(type)) {
2264 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2265 goto out;
2266 }
2267 switch (type) {
2268#ifdef WITH_OPENSSL
2269 case KEY_RSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002270 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002271 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2272 ret = SSH_ERR_INVALID_FORMAT;
2273 goto out;
2274 }
2275 /* FALLTHROUGH */
2276 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10002277 if ((key = sshkey_new(type)) == NULL) {
2278 ret = SSH_ERR_ALLOC_FAIL;
2279 goto out;
2280 }
djm@openbsd.org7be85722019-01-21 09:54:11 +00002281 if (sshbuf_get_bignum2(b, &rsa_e) != 0 ||
2282 sshbuf_get_bignum2(b, &rsa_n) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002283 ret = SSH_ERR_INVALID_FORMAT;
2284 goto out;
2285 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002286 if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, NULL)) {
2287 ret = SSH_ERR_LIBCRYPTO_ERROR;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00002288 goto out;
2289 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002290 rsa_n = rsa_e = NULL; /* transferred */
2291 if ((ret = check_rsa_length(key->rsa)) != 0)
2292 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002293#ifdef DEBUG_PK
2294 RSA_print_fp(stderr, key->rsa, 8);
2295#endif
2296 break;
2297 case KEY_DSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002298 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002299 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2300 ret = SSH_ERR_INVALID_FORMAT;
2301 goto out;
2302 }
2303 /* FALLTHROUGH */
2304 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +10002305 if ((key = sshkey_new(type)) == NULL) {
2306 ret = SSH_ERR_ALLOC_FAIL;
2307 goto out;
2308 }
djm@openbsd.org7be85722019-01-21 09:54:11 +00002309 if (sshbuf_get_bignum2(b, &dsa_p) != 0 ||
2310 sshbuf_get_bignum2(b, &dsa_q) != 0 ||
2311 sshbuf_get_bignum2(b, &dsa_g) != 0 ||
2312 sshbuf_get_bignum2(b, &dsa_pub_key) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002313 ret = SSH_ERR_INVALID_FORMAT;
2314 goto out;
2315 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002316 if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) {
2317 ret = SSH_ERR_LIBCRYPTO_ERROR;
2318 goto out;
2319 }
2320 dsa_p = dsa_q = dsa_g = NULL; /* transferred */
2321 if (!DSA_set0_key(key->dsa, dsa_pub_key, NULL)) {
2322 ret = SSH_ERR_LIBCRYPTO_ERROR;
2323 goto out;
2324 }
2325 dsa_pub_key = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10002326#ifdef DEBUG_PK
2327 DSA_print_fp(stderr, key->dsa, 8);
2328#endif
2329 break;
2330 case KEY_ECDSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002331 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002332 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2333 ret = SSH_ERR_INVALID_FORMAT;
2334 goto out;
2335 }
2336 /* FALLTHROUGH */
2337# ifdef OPENSSL_HAS_ECC
2338 case KEY_ECDSA:
2339 if ((key = sshkey_new(type)) == NULL) {
2340 ret = SSH_ERR_ALLOC_FAIL;
2341 goto out;
2342 }
djm@openbsd.org54924b52015-01-14 10:46:28 +00002343 key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype);
Damien Miller86687062014-07-02 15:28:02 +10002344 if (sshbuf_get_cstring(b, &curve, NULL) != 0) {
2345 ret = SSH_ERR_INVALID_FORMAT;
2346 goto out;
2347 }
2348 if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
2349 ret = SSH_ERR_EC_CURVE_MISMATCH;
2350 goto out;
2351 }
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00002352 EC_KEY_free(key->ecdsa);
Damien Miller86687062014-07-02 15:28:02 +10002353 if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid))
2354 == NULL) {
2355 ret = SSH_ERR_EC_CURVE_INVALID;
2356 goto out;
2357 }
2358 if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) {
2359 ret = SSH_ERR_ALLOC_FAIL;
2360 goto out;
2361 }
2362 if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) {
2363 ret = SSH_ERR_INVALID_FORMAT;
2364 goto out;
2365 }
2366 if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
2367 q) != 0) {
2368 ret = SSH_ERR_KEY_INVALID_EC_VALUE;
2369 goto out;
2370 }
2371 if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
2372 /* XXX assume it is a allocation error */
2373 ret = SSH_ERR_ALLOC_FAIL;
2374 goto out;
2375 }
2376#ifdef DEBUG_PK
2377 sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q);
2378#endif
2379 break;
2380# endif /* OPENSSL_HAS_ECC */
2381#endif /* WITH_OPENSSL */
2382 case KEY_ED25519_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002383 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002384 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2385 ret = SSH_ERR_INVALID_FORMAT;
2386 goto out;
2387 }
2388 /* FALLTHROUGH */
2389 case KEY_ED25519:
2390 if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
2391 goto out;
2392 if (len != ED25519_PK_SZ) {
2393 ret = SSH_ERR_INVALID_FORMAT;
2394 goto out;
2395 }
2396 if ((key = sshkey_new(type)) == NULL) {
2397 ret = SSH_ERR_ALLOC_FAIL;
2398 goto out;
2399 }
2400 key->ed25519_pk = pk;
2401 pk = NULL;
2402 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002403#ifdef WITH_XMSS
2404 case KEY_XMSS_CERT:
2405 /* Skip nonce */
2406 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2407 ret = SSH_ERR_INVALID_FORMAT;
2408 goto out;
2409 }
2410 /* FALLTHROUGH */
2411 case KEY_XMSS:
2412 if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0)
2413 goto out;
2414 if ((key = sshkey_new(type)) == NULL) {
2415 ret = SSH_ERR_ALLOC_FAIL;
2416 goto out;
2417 }
2418 if ((ret = sshkey_xmss_init(key, xmss_name)) != 0)
2419 goto out;
2420 if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
2421 goto out;
2422 if (len == 0 || len != sshkey_xmss_pklen(key)) {
2423 ret = SSH_ERR_INVALID_FORMAT;
2424 goto out;
2425 }
2426 key->xmss_pk = pk;
2427 pk = NULL;
2428 if (type != KEY_XMSS_CERT &&
2429 (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0)
2430 goto out;
2431 break;
2432#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002433 case KEY_UNSPEC:
Damien Miller86687062014-07-02 15:28:02 +10002434 default:
2435 ret = SSH_ERR_KEY_TYPE_UNKNOWN;
2436 goto out;
2437 }
2438
2439 /* Parse certificate potion */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002440 if (sshkey_is_cert(key) && (ret = cert_parse(b, key, copy)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002441 goto out;
2442
2443 if (key != NULL && sshbuf_len(b) != 0) {
2444 ret = SSH_ERR_INVALID_FORMAT;
2445 goto out;
2446 }
2447 ret = 0;
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00002448 if (keyp != NULL) {
2449 *keyp = key;
2450 key = NULL;
2451 }
Damien Miller86687062014-07-02 15:28:02 +10002452 out:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002453 sshbuf_free(copy);
Damien Miller86687062014-07-02 15:28:02 +10002454 sshkey_free(key);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002455 free(xmss_name);
Damien Miller86687062014-07-02 15:28:02 +10002456 free(ktype);
2457 free(curve);
2458 free(pk);
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002459#if defined(WITH_OPENSSL)
2460 BN_clear_free(rsa_n);
2461 BN_clear_free(rsa_e);
2462 BN_clear_free(dsa_p);
2463 BN_clear_free(dsa_q);
2464 BN_clear_free(dsa_g);
2465 BN_clear_free(dsa_pub_key);
2466# if defined(OPENSSL_HAS_ECC)
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00002467 EC_POINT_free(q);
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002468# endif /* OPENSSL_HAS_ECC */
2469#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10002470 return ret;
2471}
2472
2473int
2474sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp)
2475{
djm@openbsd.org60b18252015-01-26 02:59:11 +00002476 struct sshbuf *b;
2477 int r;
2478
2479 if ((b = sshbuf_from(blob, blen)) == NULL)
2480 return SSH_ERR_ALLOC_FAIL;
2481 r = sshkey_from_blob_internal(b, keyp, 1);
2482 sshbuf_free(b);
2483 return r;
2484}
2485
2486int
2487sshkey_fromb(struct sshbuf *b, struct sshkey **keyp)
2488{
2489 return sshkey_from_blob_internal(b, keyp, 1);
2490}
2491
2492int
2493sshkey_froms(struct sshbuf *buf, struct sshkey **keyp)
2494{
2495 struct sshbuf *b;
2496 int r;
2497
2498 if ((r = sshbuf_froms(buf, &b)) != 0)
2499 return r;
2500 r = sshkey_from_blob_internal(b, keyp, 1);
2501 sshbuf_free(b);
2502 return r;
Damien Miller86687062014-07-02 15:28:02 +10002503}
2504
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002505static int
2506get_sigtype(const u_char *sig, size_t siglen, char **sigtypep)
djm@openbsd.org931c78d2017-12-18 02:22:29 +00002507{
2508 int r;
2509 struct sshbuf *b = NULL;
2510 char *sigtype = NULL;
2511
2512 if (sigtypep != NULL)
2513 *sigtypep = NULL;
2514 if ((b = sshbuf_from(sig, siglen)) == NULL)
2515 return SSH_ERR_ALLOC_FAIL;
2516 if ((r = sshbuf_get_cstring(b, &sigtype, NULL)) != 0)
2517 goto out;
2518 /* success */
2519 if (sigtypep != NULL) {
2520 *sigtypep = sigtype;
2521 sigtype = NULL;
2522 }
2523 r = 0;
2524 out:
2525 free(sigtype);
2526 sshbuf_free(b);
2527 return r;
2528}
2529
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002530/*
djm@openbsd.orgba9e7882018-09-12 01:32:54 +00002531 *
2532 * Checks whether a certificate's signature type is allowed.
2533 * Returns 0 (success) if the certificate signature type appears in the
2534 * "allowed" pattern-list, or the key is not a certificate to begin with.
2535 * Otherwise returns a ssherr.h code.
2536 */
2537int
2538sshkey_check_cert_sigtype(const struct sshkey *key, const char *allowed)
2539{
2540 if (key == NULL || allowed == NULL)
2541 return SSH_ERR_INVALID_ARGUMENT;
2542 if (!sshkey_type_is_cert(key->type))
2543 return 0;
2544 if (key->cert == NULL || key->cert->signature_type == NULL)
2545 return SSH_ERR_INVALID_ARGUMENT;
2546 if (match_pattern_list(key->cert->signature_type, allowed, 0) != 1)
2547 return SSH_ERR_SIGN_ALG_UNSUPPORTED;
2548 return 0;
2549}
2550
2551/*
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002552 * Returns the expected signature algorithm for a given public key algorithm.
2553 */
djm@openbsd.orgb4d4eda2018-07-03 13:20:25 +00002554const char *
2555sshkey_sigalg_by_name(const char *name)
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002556{
2557 const struct keytype *kt;
2558
2559 for (kt = keytypes; kt->type != -1; kt++) {
2560 if (strcmp(kt->name, name) != 0)
2561 continue;
2562 if (kt->sigalg != NULL)
2563 return kt->sigalg;
2564 if (!kt->cert)
2565 return kt->name;
2566 return sshkey_ssh_name_from_type_nid(
2567 sshkey_type_plain(kt->type), kt->nid);
2568 }
2569 return NULL;
2570}
2571
2572/*
2573 * Verifies that the signature algorithm appearing inside the signature blob
2574 * matches that which was requested.
2575 */
2576int
2577sshkey_check_sigtype(const u_char *sig, size_t siglen,
2578 const char *requested_alg)
2579{
2580 const char *expected_alg;
2581 char *sigtype = NULL;
2582 int r;
2583
2584 if (requested_alg == NULL)
2585 return 0;
djm@openbsd.orgb4d4eda2018-07-03 13:20:25 +00002586 if ((expected_alg = sshkey_sigalg_by_name(requested_alg)) == NULL)
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002587 return SSH_ERR_INVALID_ARGUMENT;
2588 if ((r = get_sigtype(sig, siglen, &sigtype)) != 0)
2589 return r;
2590 r = strcmp(expected_alg, sigtype) == 0;
2591 free(sigtype);
2592 return r ? 0 : SSH_ERR_SIGN_ALG_UNSUPPORTED;
2593}
2594
djm@openbsd.org931c78d2017-12-18 02:22:29 +00002595int
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002596sshkey_sign(struct sshkey *key,
Damien Miller86687062014-07-02 15:28:02 +10002597 u_char **sigp, size_t *lenp,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +00002598 const u_char *data, size_t datalen, const char *alg, u_int compat)
Damien Miller86687062014-07-02 15:28:02 +10002599{
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002600 int was_shielded = sshkey_is_shielded(key);
2601 int r2, r = SSH_ERR_INTERNAL_ERROR;
2602
Damien Miller86687062014-07-02 15:28:02 +10002603 if (sigp != NULL)
2604 *sigp = NULL;
2605 if (lenp != NULL)
2606 *lenp = 0;
2607 if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
2608 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002609 if ((r = sshkey_unshield_private(key)) != 0)
2610 return r;
Damien Miller86687062014-07-02 15:28:02 +10002611 switch (key->type) {
2612#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002613 case KEY_DSA_CERT:
2614 case KEY_DSA:
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002615 r = ssh_dss_sign(key, sigp, lenp, data, datalen, compat);
2616 break;
Damien Miller86687062014-07-02 15:28:02 +10002617# ifdef OPENSSL_HAS_ECC
2618 case KEY_ECDSA_CERT:
2619 case KEY_ECDSA:
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002620 r = ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat);
2621 break;
Damien Miller86687062014-07-02 15:28:02 +10002622# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002623 case KEY_RSA_CERT:
2624 case KEY_RSA:
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002625 r = ssh_rsa_sign(key, sigp, lenp, data, datalen, alg);
2626 break;
Damien Miller86687062014-07-02 15:28:02 +10002627#endif /* WITH_OPENSSL */
2628 case KEY_ED25519:
2629 case KEY_ED25519_CERT:
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002630 r = ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat);
2631 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002632#ifdef WITH_XMSS
2633 case KEY_XMSS:
2634 case KEY_XMSS_CERT:
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002635 r = ssh_xmss_sign(key, sigp, lenp, data, datalen, compat);
2636 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002637#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002638 default:
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002639 r = SSH_ERR_KEY_TYPE_UNKNOWN;
2640 break;
Damien Miller86687062014-07-02 15:28:02 +10002641 }
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002642 if (was_shielded && (r2 = sshkey_shield_private(key)) != 0)
2643 return r2;
2644 return r;
Damien Miller86687062014-07-02 15:28:02 +10002645}
2646
2647/*
2648 * ssh_key_verify returns 0 for a correct signature and < 0 on error.
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002649 * If "alg" specified, then the signature must use that algorithm.
Damien Miller86687062014-07-02 15:28:02 +10002650 */
2651int
2652sshkey_verify(const struct sshkey *key,
2653 const u_char *sig, size_t siglen,
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002654 const u_char *data, size_t dlen, const char *alg, u_int compat)
Damien Miller86687062014-07-02 15:28:02 +10002655{
djm@openbsd.org4cf87f42014-12-10 01:24:09 +00002656 if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE)
Damien Miller86687062014-07-02 15:28:02 +10002657 return SSH_ERR_INVALID_ARGUMENT;
2658 switch (key->type) {
2659#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002660 case KEY_DSA_CERT:
2661 case KEY_DSA:
2662 return ssh_dss_verify(key, sig, siglen, data, dlen, compat);
2663# ifdef OPENSSL_HAS_ECC
2664 case KEY_ECDSA_CERT:
2665 case KEY_ECDSA:
2666 return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat);
2667# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002668 case KEY_RSA_CERT:
2669 case KEY_RSA:
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002670 return ssh_rsa_verify(key, sig, siglen, data, dlen, alg);
Damien Miller86687062014-07-02 15:28:02 +10002671#endif /* WITH_OPENSSL */
2672 case KEY_ED25519:
2673 case KEY_ED25519_CERT:
2674 return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002675#ifdef WITH_XMSS
2676 case KEY_XMSS:
2677 case KEY_XMSS_CERT:
2678 return ssh_xmss_verify(key, sig, siglen, data, dlen, compat);
2679#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002680 default:
2681 return SSH_ERR_KEY_TYPE_UNKNOWN;
2682 }
2683}
2684
Damien Miller86687062014-07-02 15:28:02 +10002685/* Convert a plain key to their _CERT equivalent */
2686int
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002687sshkey_to_certified(struct sshkey *k)
Damien Miller86687062014-07-02 15:28:02 +10002688{
2689 int newtype;
2690
2691 switch (k->type) {
2692#ifdef WITH_OPENSSL
2693 case KEY_RSA:
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002694 newtype = KEY_RSA_CERT;
Damien Miller86687062014-07-02 15:28:02 +10002695 break;
2696 case KEY_DSA:
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002697 newtype = KEY_DSA_CERT;
Damien Miller86687062014-07-02 15:28:02 +10002698 break;
2699 case KEY_ECDSA:
Damien Miller86687062014-07-02 15:28:02 +10002700 newtype = KEY_ECDSA_CERT;
2701 break;
2702#endif /* WITH_OPENSSL */
2703 case KEY_ED25519:
Damien Miller86687062014-07-02 15:28:02 +10002704 newtype = KEY_ED25519_CERT;
2705 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002706#ifdef WITH_XMSS
2707 case KEY_XMSS:
2708 newtype = KEY_XMSS_CERT;
2709 break;
2710#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002711 default:
2712 return SSH_ERR_INVALID_ARGUMENT;
2713 }
2714 if ((k->cert = cert_new()) == NULL)
2715 return SSH_ERR_ALLOC_FAIL;
2716 k->type = newtype;
2717 return 0;
2718}
2719
2720/* Convert a certificate to its raw key equivalent */
2721int
2722sshkey_drop_cert(struct sshkey *k)
2723{
2724 if (!sshkey_type_is_cert(k->type))
2725 return SSH_ERR_KEY_TYPE_UNKNOWN;
2726 cert_free(k->cert);
2727 k->cert = NULL;
2728 k->type = sshkey_type_plain(k->type);
2729 return 0;
2730}
2731
2732/* Sign a certified key, (re-)generating the signed certblob. */
2733int
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002734sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
2735 sshkey_certify_signer *signer, void *signer_ctx)
Damien Miller86687062014-07-02 15:28:02 +10002736{
2737 struct sshbuf *principals = NULL;
2738 u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32];
2739 size_t i, ca_len, sig_len;
2740 int ret = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002741 struct sshbuf *cert = NULL;
2742 char *sigtype = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002743#ifdef WITH_OPENSSL
2744 const BIGNUM *rsa_n, *rsa_e, *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
2745#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10002746
2747 if (k == NULL || k->cert == NULL ||
2748 k->cert->certblob == NULL || ca == NULL)
2749 return SSH_ERR_INVALID_ARGUMENT;
2750 if (!sshkey_is_cert(k))
2751 return SSH_ERR_KEY_TYPE_UNKNOWN;
2752 if (!sshkey_type_is_valid_ca(ca->type))
2753 return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2754
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002755 /*
2756 * If no alg specified as argument but a signature_type was set,
2757 * then prefer that. If both were specified, then they must match.
2758 */
2759 if (alg == NULL)
2760 alg = k->cert->signature_type;
2761 else if (k->cert->signature_type != NULL &&
2762 strcmp(alg, k->cert->signature_type) != 0)
2763 return SSH_ERR_INVALID_ARGUMENT;
2764
djm@openbsd.org476e3552019-05-20 00:20:35 +00002765 /*
2766 * If no signing algorithm or signature_type was specified and we're
2767 * using a RSA key, then default to a good signature algorithm.
2768 */
2769 if (alg == NULL && ca->type == KEY_RSA)
2770 alg = "rsa-sha2-512";
2771
Damien Miller86687062014-07-02 15:28:02 +10002772 if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0)
2773 return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2774
2775 cert = k->cert->certblob; /* for readability */
2776 sshbuf_reset(cert);
2777 if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0)
2778 goto out;
2779
2780 /* -v01 certs put nonce first */
2781 arc4random_buf(&nonce, sizeof(nonce));
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002782 if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0)
2783 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002784
2785 /* XXX this substantially duplicates to_blob(); refactor */
2786 switch (k->type) {
2787#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002788 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002789 DSA_get0_pqg(k->dsa, &dsa_p, &dsa_q, &dsa_g);
2790 DSA_get0_key(k->dsa, &dsa_pub_key, NULL);
2791 if ((ret = sshbuf_put_bignum2(cert, dsa_p)) != 0 ||
2792 (ret = sshbuf_put_bignum2(cert, dsa_q)) != 0 ||
2793 (ret = sshbuf_put_bignum2(cert, dsa_g)) != 0 ||
2794 (ret = sshbuf_put_bignum2(cert, dsa_pub_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002795 goto out;
2796 break;
2797# ifdef OPENSSL_HAS_ECC
2798 case KEY_ECDSA_CERT:
2799 if ((ret = sshbuf_put_cstring(cert,
2800 sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 ||
2801 (ret = sshbuf_put_ec(cert,
2802 EC_KEY_get0_public_key(k->ecdsa),
2803 EC_KEY_get0_group(k->ecdsa))) != 0)
2804 goto out;
2805 break;
2806# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002807 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002808 RSA_get0_key(k->rsa, &rsa_n, &rsa_e, NULL);
2809 if ((ret = sshbuf_put_bignum2(cert, rsa_e)) != 0 ||
2810 (ret = sshbuf_put_bignum2(cert, rsa_n)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002811 goto out;
2812 break;
2813#endif /* WITH_OPENSSL */
2814 case KEY_ED25519_CERT:
2815 if ((ret = sshbuf_put_string(cert,
2816 k->ed25519_pk, ED25519_PK_SZ)) != 0)
2817 goto out;
2818 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002819#ifdef WITH_XMSS
2820 case KEY_XMSS_CERT:
2821 if (k->xmss_name == NULL) {
2822 ret = SSH_ERR_INVALID_ARGUMENT;
2823 goto out;
2824 }
2825 if ((ret = sshbuf_put_cstring(cert, k->xmss_name)) ||
2826 (ret = sshbuf_put_string(cert,
2827 k->xmss_pk, sshkey_xmss_pklen(k))) != 0)
2828 goto out;
2829 break;
2830#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002831 default:
2832 ret = SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org55e5bde2015-03-06 01:40:56 +00002833 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002834 }
2835
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002836 if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0 ||
2837 (ret = sshbuf_put_u32(cert, k->cert->type)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002838 (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0)
2839 goto out;
2840
2841 if ((principals = sshbuf_new()) == NULL) {
2842 ret = SSH_ERR_ALLOC_FAIL;
2843 goto out;
2844 }
2845 for (i = 0; i < k->cert->nprincipals; i++) {
2846 if ((ret = sshbuf_put_cstring(principals,
2847 k->cert->principals[i])) != 0)
2848 goto out;
2849 }
2850 if ((ret = sshbuf_put_stringb(cert, principals)) != 0 ||
2851 (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 ||
2852 (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 ||
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002853 (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0 ||
2854 (ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0 ||
2855 (ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */
Damien Miller86687062014-07-02 15:28:02 +10002856 (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0)
2857 goto out;
2858
2859 /* Sign the whole mess */
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002860 if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert),
2861 sshbuf_len(cert), alg, 0, signer_ctx)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002862 goto out;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002863 /* Check and update signature_type against what was actually used */
2864 if ((ret = get_sigtype(sig_blob, sig_len, &sigtype)) != 0)
2865 goto out;
2866 if (alg != NULL && strcmp(alg, sigtype) != 0) {
2867 ret = SSH_ERR_SIGN_ALG_UNSUPPORTED;
2868 goto out;
2869 }
2870 if (k->cert->signature_type == NULL) {
2871 k->cert->signature_type = sigtype;
2872 sigtype = NULL;
2873 }
Damien Miller86687062014-07-02 15:28:02 +10002874 /* Append signature and we are done */
2875 if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0)
2876 goto out;
2877 ret = 0;
2878 out:
2879 if (ret != 0)
2880 sshbuf_reset(cert);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00002881 free(sig_blob);
2882 free(ca_blob);
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002883 free(sigtype);
mmcc@openbsd.org52d70782015-12-11 04:21:11 +00002884 sshbuf_free(principals);
Damien Miller86687062014-07-02 15:28:02 +10002885 return ret;
2886}
2887
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002888static int
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002889default_key_sign(struct sshkey *key, u_char **sigp, size_t *lenp,
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002890 const u_char *data, size_t datalen,
2891 const char *alg, u_int compat, void *ctx)
2892{
2893 if (ctx != NULL)
2894 return SSH_ERR_INVALID_ARGUMENT;
2895 return sshkey_sign(key, sigp, lenp, data, datalen, alg, compat);
2896}
2897
2898int
2899sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg)
2900{
2901 return sshkey_certify_custom(k, ca, alg, default_key_sign, NULL);
2902}
2903
Damien Miller86687062014-07-02 15:28:02 +10002904int
2905sshkey_cert_check_authority(const struct sshkey *k,
2906 int want_host, int require_principal,
2907 const char *name, const char **reason)
2908{
2909 u_int i, principal_matches;
2910 time_t now = time(NULL);
2911
2912 if (reason != NULL)
2913 *reason = NULL;
2914
2915 if (want_host) {
2916 if (k->cert->type != SSH2_CERT_TYPE_HOST) {
2917 *reason = "Certificate invalid: not a host certificate";
2918 return SSH_ERR_KEY_CERT_INVALID;
2919 }
2920 } else {
2921 if (k->cert->type != SSH2_CERT_TYPE_USER) {
2922 *reason = "Certificate invalid: not a user certificate";
2923 return SSH_ERR_KEY_CERT_INVALID;
2924 }
2925 }
2926 if (now < 0) {
2927 /* yikes - system clock before epoch! */
2928 *reason = "Certificate invalid: not yet valid";
2929 return SSH_ERR_KEY_CERT_INVALID;
2930 }
2931 if ((u_int64_t)now < k->cert->valid_after) {
2932 *reason = "Certificate invalid: not yet valid";
2933 return SSH_ERR_KEY_CERT_INVALID;
2934 }
2935 if ((u_int64_t)now >= k->cert->valid_before) {
2936 *reason = "Certificate invalid: expired";
2937 return SSH_ERR_KEY_CERT_INVALID;
2938 }
2939 if (k->cert->nprincipals == 0) {
2940 if (require_principal) {
2941 *reason = "Certificate lacks principal list";
2942 return SSH_ERR_KEY_CERT_INVALID;
2943 }
2944 } else if (name != NULL) {
2945 principal_matches = 0;
2946 for (i = 0; i < k->cert->nprincipals; i++) {
2947 if (strcmp(name, k->cert->principals[i]) == 0) {
2948 principal_matches = 1;
2949 break;
2950 }
2951 }
2952 if (!principal_matches) {
2953 *reason = "Certificate invalid: name is not a listed "
2954 "principal";
2955 return SSH_ERR_KEY_CERT_INVALID;
2956 }
2957 }
2958 return 0;
2959}
2960
djm@openbsd.org499cf362015-11-19 01:08:55 +00002961size_t
2962sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l)
2963{
2964 char from[32], to[32], ret[64];
2965 time_t tt;
2966 struct tm *tm;
2967
2968 *from = *to = '\0';
2969 if (cert->valid_after == 0 &&
2970 cert->valid_before == 0xffffffffffffffffULL)
2971 return strlcpy(s, "forever", l);
2972
2973 if (cert->valid_after != 0) {
2974 /* XXX revisit INT_MAX in 2038 :) */
2975 tt = cert->valid_after > INT_MAX ?
2976 INT_MAX : cert->valid_after;
2977 tm = localtime(&tt);
2978 strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm);
2979 }
2980 if (cert->valid_before != 0xffffffffffffffffULL) {
2981 /* XXX revisit INT_MAX in 2038 :) */
2982 tt = cert->valid_before > INT_MAX ?
2983 INT_MAX : cert->valid_before;
2984 tm = localtime(&tt);
2985 strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm);
2986 }
2987
2988 if (cert->valid_after == 0)
2989 snprintf(ret, sizeof(ret), "before %s", to);
2990 else if (cert->valid_before == 0xffffffffffffffffULL)
2991 snprintf(ret, sizeof(ret), "after %s", from);
2992 else
2993 snprintf(ret, sizeof(ret), "from %s to %s", from, to);
2994
2995 return strlcpy(s, ret, l);
2996}
2997
Damien Miller86687062014-07-02 15:28:02 +10002998int
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00002999sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf,
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003000 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +10003001{
3002 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003003 int was_shielded = sshkey_is_shielded(key);
3004 struct sshbuf *b = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003005#ifdef WITH_OPENSSL
3006 const BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_iqmp, *rsa_p, *rsa_q;
3007 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key, *dsa_priv_key;
3008#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10003009
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003010 if ((r = sshkey_unshield_private(key)) != 0)
3011 return r;
3012 if ((b = sshbuf_new()) == NULL)
3013 return SSH_ERR_ALLOC_FAIL;
Damien Miller86687062014-07-02 15:28:02 +10003014 if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0)
3015 goto out;
3016 switch (key->type) {
3017#ifdef WITH_OPENSSL
3018 case KEY_RSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003019 RSA_get0_key(key->rsa, &rsa_n, &rsa_e, &rsa_d);
3020 RSA_get0_factors(key->rsa, &rsa_p, &rsa_q);
3021 RSA_get0_crt_params(key->rsa, NULL, NULL, &rsa_iqmp);
3022 if ((r = sshbuf_put_bignum2(b, rsa_n)) != 0 ||
3023 (r = sshbuf_put_bignum2(b, rsa_e)) != 0 ||
3024 (r = sshbuf_put_bignum2(b, rsa_d)) != 0 ||
3025 (r = sshbuf_put_bignum2(b, rsa_iqmp)) != 0 ||
3026 (r = sshbuf_put_bignum2(b, rsa_p)) != 0 ||
3027 (r = sshbuf_put_bignum2(b, rsa_q)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003028 goto out;
3029 break;
Damien Miller86687062014-07-02 15:28:02 +10003030 case KEY_RSA_CERT:
3031 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
3032 r = SSH_ERR_INVALID_ARGUMENT;
3033 goto out;
3034 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003035 RSA_get0_key(key->rsa, NULL, NULL, &rsa_d);
3036 RSA_get0_factors(key->rsa, &rsa_p, &rsa_q);
3037 RSA_get0_crt_params(key->rsa, NULL, NULL, &rsa_iqmp);
Damien Miller86687062014-07-02 15:28:02 +10003038 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003039 (r = sshbuf_put_bignum2(b, rsa_d)) != 0 ||
3040 (r = sshbuf_put_bignum2(b, rsa_iqmp)) != 0 ||
3041 (r = sshbuf_put_bignum2(b, rsa_p)) != 0 ||
3042 (r = sshbuf_put_bignum2(b, rsa_q)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003043 goto out;
3044 break;
3045 case KEY_DSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003046 DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g);
3047 DSA_get0_key(key->dsa, &dsa_pub_key, &dsa_priv_key);
3048 if ((r = sshbuf_put_bignum2(b, dsa_p)) != 0 ||
3049 (r = sshbuf_put_bignum2(b, dsa_q)) != 0 ||
3050 (r = sshbuf_put_bignum2(b, dsa_g)) != 0 ||
3051 (r = sshbuf_put_bignum2(b, dsa_pub_key)) != 0 ||
3052 (r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003053 goto out;
3054 break;
Damien Miller86687062014-07-02 15:28:02 +10003055 case KEY_DSA_CERT:
3056 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
3057 r = SSH_ERR_INVALID_ARGUMENT;
3058 goto out;
3059 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003060 DSA_get0_key(key->dsa, NULL, &dsa_priv_key);
Damien Miller86687062014-07-02 15:28:02 +10003061 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003062 (r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003063 goto out;
3064 break;
3065# ifdef OPENSSL_HAS_ECC
3066 case KEY_ECDSA:
3067 if ((r = sshbuf_put_cstring(b,
3068 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
3069 (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 ||
3070 (r = sshbuf_put_bignum2(b,
3071 EC_KEY_get0_private_key(key->ecdsa))) != 0)
3072 goto out;
3073 break;
3074 case KEY_ECDSA_CERT:
3075 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
3076 r = SSH_ERR_INVALID_ARGUMENT;
3077 goto out;
3078 }
3079 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
3080 (r = sshbuf_put_bignum2(b,
3081 EC_KEY_get0_private_key(key->ecdsa))) != 0)
3082 goto out;
3083 break;
3084# endif /* OPENSSL_HAS_ECC */
3085#endif /* WITH_OPENSSL */
3086 case KEY_ED25519:
3087 if ((r = sshbuf_put_string(b, key->ed25519_pk,
3088 ED25519_PK_SZ)) != 0 ||
3089 (r = sshbuf_put_string(b, key->ed25519_sk,
3090 ED25519_SK_SZ)) != 0)
3091 goto out;
3092 break;
3093 case KEY_ED25519_CERT:
3094 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
3095 r = SSH_ERR_INVALID_ARGUMENT;
3096 goto out;
3097 }
3098 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
3099 (r = sshbuf_put_string(b, key->ed25519_pk,
3100 ED25519_PK_SZ)) != 0 ||
3101 (r = sshbuf_put_string(b, key->ed25519_sk,
3102 ED25519_SK_SZ)) != 0)
3103 goto out;
3104 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003105#ifdef WITH_XMSS
3106 case KEY_XMSS:
3107 if (key->xmss_name == NULL) {
3108 r = SSH_ERR_INVALID_ARGUMENT;
3109 goto out;
3110 }
3111 if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
3112 (r = sshbuf_put_string(b, key->xmss_pk,
3113 sshkey_xmss_pklen(key))) != 0 ||
3114 (r = sshbuf_put_string(b, key->xmss_sk,
3115 sshkey_xmss_sklen(key))) != 0 ||
3116 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
3117 goto out;
3118 break;
3119 case KEY_XMSS_CERT:
3120 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0 ||
3121 key->xmss_name == NULL) {
3122 r = SSH_ERR_INVALID_ARGUMENT;
3123 goto out;
3124 }
3125 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
3126 (r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
3127 (r = sshbuf_put_string(b, key->xmss_pk,
3128 sshkey_xmss_pklen(key))) != 0 ||
3129 (r = sshbuf_put_string(b, key->xmss_sk,
3130 sshkey_xmss_sklen(key))) != 0 ||
3131 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
3132 goto out;
3133 break;
3134#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10003135 default:
3136 r = SSH_ERR_INVALID_ARGUMENT;
3137 goto out;
3138 }
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003139 /*
3140 * success (but we still need to append the output to buf after
3141 * possibly re-shielding the private key)
3142 */
Damien Miller86687062014-07-02 15:28:02 +10003143 r = 0;
3144 out:
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003145 if (was_shielded)
3146 r = sshkey_shield_private(key);
3147 if (r == 0)
3148 r = sshbuf_putb(buf, b);
3149 sshbuf_free(b);
3150
Damien Miller86687062014-07-02 15:28:02 +10003151 return r;
3152}
3153
3154int
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003155sshkey_private_serialize(struct sshkey *key, struct sshbuf *b)
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003156{
3157 return sshkey_private_serialize_opt(key, b,
3158 SSHKEY_SERIALIZE_DEFAULT);
3159}
3160
3161int
Damien Miller86687062014-07-02 15:28:02 +10003162sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
3163{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003164 char *tname = NULL, *curve = NULL, *xmss_name = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003165 struct sshkey *k = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00003166 size_t pklen = 0, sklen = 0;
Damien Miller86687062014-07-02 15:28:02 +10003167 int type, r = SSH_ERR_INTERNAL_ERROR;
3168 u_char *ed25519_pk = NULL, *ed25519_sk = NULL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003169 u_char *xmss_pk = NULL, *xmss_sk = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003170#ifdef WITH_OPENSSL
3171 BIGNUM *exponent = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003172 BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
3173 BIGNUM *rsa_iqmp = NULL, *rsa_p = NULL, *rsa_q = NULL;
3174 BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL;
3175 BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003176#endif /* WITH_OPENSSL */
3177
3178 if (kp != NULL)
3179 *kp = NULL;
3180 if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0)
3181 goto out;
3182 type = sshkey_type_from_name(tname);
3183 switch (type) {
3184#ifdef WITH_OPENSSL
3185 case KEY_DSA:
djm@openbsd.org6da046f2018-09-14 04:17:44 +00003186 if ((k = sshkey_new(type)) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +10003187 r = SSH_ERR_ALLOC_FAIL;
3188 goto out;
3189 }
djm@openbsd.org7be85722019-01-21 09:54:11 +00003190 if ((r = sshbuf_get_bignum2(buf, &dsa_p)) != 0 ||
3191 (r = sshbuf_get_bignum2(buf, &dsa_q)) != 0 ||
3192 (r = sshbuf_get_bignum2(buf, &dsa_g)) != 0 ||
3193 (r = sshbuf_get_bignum2(buf, &dsa_pub_key)) != 0 ||
3194 (r = sshbuf_get_bignum2(buf, &dsa_priv_key)) != 0)
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003195 goto out;
3196 if (!DSA_set0_pqg(k->dsa, dsa_p, dsa_q, dsa_g)) {
3197 r = SSH_ERR_LIBCRYPTO_ERROR;
3198 goto out;
3199 }
3200 dsa_p = dsa_q = dsa_g = NULL; /* transferred */
3201 if (!DSA_set0_key(k->dsa, dsa_pub_key, dsa_priv_key)) {
3202 r = SSH_ERR_LIBCRYPTO_ERROR;
3203 goto out;
3204 }
3205 dsa_pub_key = dsa_priv_key = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10003206 break;
Damien Miller86687062014-07-02 15:28:02 +10003207 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003208 if ((r = sshkey_froms(buf, &k)) != 0 ||
djm@openbsd.org7be85722019-01-21 09:54:11 +00003209 (r = sshbuf_get_bignum2(buf, &dsa_priv_key)) != 0)
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003210 goto out;
3211 if (!DSA_set0_key(k->dsa, NULL, dsa_priv_key)) {
3212 r = SSH_ERR_LIBCRYPTO_ERROR;
3213 goto out;
3214 }
3215 dsa_priv_key = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10003216 break;
3217# ifdef OPENSSL_HAS_ECC
3218 case KEY_ECDSA:
djm@openbsd.org6da046f2018-09-14 04:17:44 +00003219 if ((k = sshkey_new(type)) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +10003220 r = SSH_ERR_ALLOC_FAIL;
3221 goto out;
3222 }
3223 if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) {
3224 r = SSH_ERR_INVALID_ARGUMENT;
3225 goto out;
3226 }
3227 if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0)
3228 goto out;
3229 if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
3230 r = SSH_ERR_EC_CURVE_MISMATCH;
3231 goto out;
3232 }
3233 k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
djm@openbsd.org7be85722019-01-21 09:54:11 +00003234 if (k->ecdsa == NULL) {
Damien Miller86687062014-07-02 15:28:02 +10003235 r = SSH_ERR_LIBCRYPTO_ERROR;
3236 goto out;
3237 }
3238 if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 ||
djm@openbsd.org7be85722019-01-21 09:54:11 +00003239 (r = sshbuf_get_bignum2(buf, &exponent)))
Damien Miller86687062014-07-02 15:28:02 +10003240 goto out;
3241 if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) {
3242 r = SSH_ERR_LIBCRYPTO_ERROR;
3243 goto out;
3244 }
3245 if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
jsg@openbsd.orgf3a3ea12015-09-02 07:51:12 +00003246 EC_KEY_get0_public_key(k->ecdsa))) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003247 (r = sshkey_ec_validate_private(k->ecdsa)) != 0)
3248 goto out;
3249 break;
3250 case KEY_ECDSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00003251 if ((r = sshkey_froms(buf, &k)) != 0 ||
djm@openbsd.org7be85722019-01-21 09:54:11 +00003252 (r = sshbuf_get_bignum2(buf, &exponent)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003253 goto out;
3254 if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) {
3255 r = SSH_ERR_LIBCRYPTO_ERROR;
3256 goto out;
3257 }
3258 if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
jsg@openbsd.orgf3a3ea12015-09-02 07:51:12 +00003259 EC_KEY_get0_public_key(k->ecdsa))) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003260 (r = sshkey_ec_validate_private(k->ecdsa)) != 0)
3261 goto out;
3262 break;
3263# endif /* OPENSSL_HAS_ECC */
3264 case KEY_RSA:
djm@openbsd.org6da046f2018-09-14 04:17:44 +00003265 if ((k = sshkey_new(type)) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +10003266 r = SSH_ERR_ALLOC_FAIL;
3267 goto out;
3268 }
djm@openbsd.org7be85722019-01-21 09:54:11 +00003269 if ((r = sshbuf_get_bignum2(buf, &rsa_n)) != 0 ||
3270 (r = sshbuf_get_bignum2(buf, &rsa_e)) != 0 ||
3271 (r = sshbuf_get_bignum2(buf, &rsa_d)) != 0 ||
3272 (r = sshbuf_get_bignum2(buf, &rsa_iqmp)) != 0 ||
3273 (r = sshbuf_get_bignum2(buf, &rsa_p)) != 0 ||
3274 (r = sshbuf_get_bignum2(buf, &rsa_q)) != 0)
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003275 goto out;
3276 if (!RSA_set0_key(k->rsa, rsa_n, rsa_e, rsa_d)) {
3277 r = SSH_ERR_LIBCRYPTO_ERROR;
3278 goto out;
3279 }
3280 rsa_n = rsa_e = rsa_d = NULL; /* transferred */
3281 if (!RSA_set0_factors(k->rsa, rsa_p, rsa_q)) {
3282 r = SSH_ERR_LIBCRYPTO_ERROR;
3283 goto out;
3284 }
3285 rsa_p = rsa_q = NULL; /* transferred */
3286 if ((r = check_rsa_length(k->rsa)) != 0)
3287 goto out;
3288 if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0)
3289 goto out;
Damien Miller86687062014-07-02 15:28:02 +10003290 break;
Damien Miller86687062014-07-02 15:28:02 +10003291 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003292 if ((r = sshkey_froms(buf, &k)) != 0 ||
djm@openbsd.org7be85722019-01-21 09:54:11 +00003293 (r = sshbuf_get_bignum2(buf, &rsa_d)) != 0 ||
3294 (r = sshbuf_get_bignum2(buf, &rsa_iqmp)) != 0 ||
3295 (r = sshbuf_get_bignum2(buf, &rsa_p)) != 0 ||
3296 (r = sshbuf_get_bignum2(buf, &rsa_q)) != 0)
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003297 goto out;
3298 if (!RSA_set0_key(k->rsa, NULL, NULL, rsa_d)) {
3299 r = SSH_ERR_LIBCRYPTO_ERROR;
3300 goto out;
3301 }
3302 rsa_d = NULL; /* transferred */
3303 if (!RSA_set0_factors(k->rsa, rsa_p, rsa_q)) {
3304 r = SSH_ERR_LIBCRYPTO_ERROR;
3305 goto out;
3306 }
3307 rsa_p = rsa_q = NULL; /* transferred */
3308 if ((r = check_rsa_length(k->rsa)) != 0)
3309 goto out;
3310 if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0)
3311 goto out;
Damien Miller86687062014-07-02 15:28:02 +10003312 break;
3313#endif /* WITH_OPENSSL */
3314 case KEY_ED25519:
djm@openbsd.org6da046f2018-09-14 04:17:44 +00003315 if ((k = sshkey_new(type)) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +10003316 r = SSH_ERR_ALLOC_FAIL;
3317 goto out;
3318 }
3319 if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 ||
3320 (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0)
3321 goto out;
3322 if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) {
3323 r = SSH_ERR_INVALID_FORMAT;
3324 goto out;
3325 }
3326 k->ed25519_pk = ed25519_pk;
3327 k->ed25519_sk = ed25519_sk;
3328 ed25519_pk = ed25519_sk = NULL;
3329 break;
3330 case KEY_ED25519_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00003331 if ((r = sshkey_froms(buf, &k)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003332 (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 ||
3333 (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0)
3334 goto out;
3335 if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) {
3336 r = SSH_ERR_INVALID_FORMAT;
3337 goto out;
3338 }
3339 k->ed25519_pk = ed25519_pk;
3340 k->ed25519_sk = ed25519_sk;
3341 ed25519_pk = ed25519_sk = NULL;
3342 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003343#ifdef WITH_XMSS
3344 case KEY_XMSS:
djm@openbsd.org6da046f2018-09-14 04:17:44 +00003345 if ((k = sshkey_new(type)) == NULL) {
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003346 r = SSH_ERR_ALLOC_FAIL;
3347 goto out;
3348 }
3349 if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
3350 (r = sshkey_xmss_init(k, xmss_name)) != 0 ||
3351 (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
3352 (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
3353 goto out;
3354 if (pklen != sshkey_xmss_pklen(k) ||
3355 sklen != sshkey_xmss_sklen(k)) {
3356 r = SSH_ERR_INVALID_FORMAT;
3357 goto out;
3358 }
3359 k->xmss_pk = xmss_pk;
3360 k->xmss_sk = xmss_sk;
3361 xmss_pk = xmss_sk = NULL;
3362 /* optional internal state */
3363 if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
3364 goto out;
3365 break;
3366 case KEY_XMSS_CERT:
3367 if ((r = sshkey_froms(buf, &k)) != 0 ||
markus@openbsd.org27979da2018-03-22 07:05:48 +00003368 (r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003369 (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
3370 (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
3371 goto out;
markus@openbsd.org27979da2018-03-22 07:05:48 +00003372 if (strcmp(xmss_name, k->xmss_name)) {
3373 r = SSH_ERR_INVALID_FORMAT;
3374 goto out;
3375 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003376 if (pklen != sshkey_xmss_pklen(k) ||
3377 sklen != sshkey_xmss_sklen(k)) {
3378 r = SSH_ERR_INVALID_FORMAT;
3379 goto out;
3380 }
3381 k->xmss_pk = xmss_pk;
3382 k->xmss_sk = xmss_sk;
3383 xmss_pk = xmss_sk = NULL;
3384 /* optional internal state */
3385 if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
3386 goto out;
3387 break;
3388#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10003389 default:
3390 r = SSH_ERR_KEY_TYPE_UNKNOWN;
3391 goto out;
3392 }
3393#ifdef WITH_OPENSSL
3394 /* enable blinding */
3395 switch (k->type) {
3396 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10003397 case KEY_RSA_CERT:
Damien Miller86687062014-07-02 15:28:02 +10003398 if (RSA_blinding_on(k->rsa, NULL) != 1) {
3399 r = SSH_ERR_LIBCRYPTO_ERROR;
3400 goto out;
3401 }
3402 break;
3403 }
3404#endif /* WITH_OPENSSL */
3405 /* success */
3406 r = 0;
3407 if (kp != NULL) {
3408 *kp = k;
3409 k = NULL;
3410 }
3411 out:
3412 free(tname);
3413 free(curve);
3414#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00003415 BN_clear_free(exponent);
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003416 BN_clear_free(dsa_p);
3417 BN_clear_free(dsa_q);
3418 BN_clear_free(dsa_g);
3419 BN_clear_free(dsa_pub_key);
3420 BN_clear_free(dsa_priv_key);
3421 BN_clear_free(rsa_n);
3422 BN_clear_free(rsa_e);
3423 BN_clear_free(rsa_d);
3424 BN_clear_free(rsa_p);
3425 BN_clear_free(rsa_q);
3426 BN_clear_free(rsa_iqmp);
Damien Miller86687062014-07-02 15:28:02 +10003427#endif /* WITH_OPENSSL */
3428 sshkey_free(k);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +00003429 freezero(ed25519_pk, pklen);
3430 freezero(ed25519_sk, sklen);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003431 free(xmss_name);
3432 freezero(xmss_pk, pklen);
3433 freezero(xmss_sk, sklen);
Damien Miller86687062014-07-02 15:28:02 +10003434 return r;
3435}
3436
3437#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
3438int
3439sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
3440{
3441 BN_CTX *bnctx;
3442 EC_POINT *nq = NULL;
3443 BIGNUM *order, *x, *y, *tmp;
3444 int ret = SSH_ERR_KEY_INVALID_EC_VALUE;
3445
djm@openbsd.orga571dbc2016-10-04 21:34:40 +00003446 /*
3447 * NB. This assumes OpenSSL has already verified that the public
3448 * point lies on the curve. This is done by EC_POINT_oct2point()
3449 * implicitly calling EC_POINT_is_on_curve(). If this code is ever
3450 * reachable with public points not unmarshalled using
3451 * EC_POINT_oct2point then the caller will need to explicitly check.
3452 */
3453
Damien Miller86687062014-07-02 15:28:02 +10003454 if ((bnctx = BN_CTX_new()) == NULL)
3455 return SSH_ERR_ALLOC_FAIL;
3456 BN_CTX_start(bnctx);
3457
3458 /*
3459 * We shouldn't ever hit this case because bignum_get_ecpoint()
3460 * refuses to load GF2m points.
3461 */
3462 if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
3463 NID_X9_62_prime_field)
3464 goto out;
3465
3466 /* Q != infinity */
3467 if (EC_POINT_is_at_infinity(group, public))
3468 goto out;
3469
3470 if ((x = BN_CTX_get(bnctx)) == NULL ||
3471 (y = BN_CTX_get(bnctx)) == NULL ||
3472 (order = BN_CTX_get(bnctx)) == NULL ||
3473 (tmp = BN_CTX_get(bnctx)) == NULL) {
3474 ret = SSH_ERR_ALLOC_FAIL;
3475 goto out;
3476 }
3477
3478 /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */
3479 if (EC_GROUP_get_order(group, order, bnctx) != 1 ||
3480 EC_POINT_get_affine_coordinates_GFp(group, public,
3481 x, y, bnctx) != 1) {
3482 ret = SSH_ERR_LIBCRYPTO_ERROR;
3483 goto out;
3484 }
3485 if (BN_num_bits(x) <= BN_num_bits(order) / 2 ||
3486 BN_num_bits(y) <= BN_num_bits(order) / 2)
3487 goto out;
3488
3489 /* nQ == infinity (n == order of subgroup) */
3490 if ((nq = EC_POINT_new(group)) == NULL) {
3491 ret = SSH_ERR_ALLOC_FAIL;
3492 goto out;
3493 }
3494 if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) {
3495 ret = SSH_ERR_LIBCRYPTO_ERROR;
3496 goto out;
3497 }
3498 if (EC_POINT_is_at_infinity(group, nq) != 1)
3499 goto out;
3500
3501 /* x < order - 1, y < order - 1 */
3502 if (!BN_sub(tmp, order, BN_value_one())) {
3503 ret = SSH_ERR_LIBCRYPTO_ERROR;
3504 goto out;
3505 }
3506 if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0)
3507 goto out;
3508 ret = 0;
3509 out:
3510 BN_CTX_free(bnctx);
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00003511 EC_POINT_free(nq);
Damien Miller86687062014-07-02 15:28:02 +10003512 return ret;
3513}
3514
3515int
3516sshkey_ec_validate_private(const EC_KEY *key)
3517{
3518 BN_CTX *bnctx;
3519 BIGNUM *order, *tmp;
3520 int ret = SSH_ERR_KEY_INVALID_EC_VALUE;
3521
3522 if ((bnctx = BN_CTX_new()) == NULL)
3523 return SSH_ERR_ALLOC_FAIL;
3524 BN_CTX_start(bnctx);
3525
3526 if ((order = BN_CTX_get(bnctx)) == NULL ||
3527 (tmp = BN_CTX_get(bnctx)) == NULL) {
3528 ret = SSH_ERR_ALLOC_FAIL;
3529 goto out;
3530 }
3531
3532 /* log2(private) > log2(order)/2 */
3533 if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) {
3534 ret = SSH_ERR_LIBCRYPTO_ERROR;
3535 goto out;
3536 }
3537 if (BN_num_bits(EC_KEY_get0_private_key(key)) <=
3538 BN_num_bits(order) / 2)
3539 goto out;
3540
3541 /* private < order - 1 */
3542 if (!BN_sub(tmp, order, BN_value_one())) {
3543 ret = SSH_ERR_LIBCRYPTO_ERROR;
3544 goto out;
3545 }
3546 if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0)
3547 goto out;
3548 ret = 0;
3549 out:
3550 BN_CTX_free(bnctx);
3551 return ret;
3552}
3553
3554void
3555sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point)
3556{
3557 BIGNUM *x, *y;
3558 BN_CTX *bnctx;
3559
3560 if (point == NULL) {
3561 fputs("point=(NULL)\n", stderr);
3562 return;
3563 }
3564 if ((bnctx = BN_CTX_new()) == NULL) {
3565 fprintf(stderr, "%s: BN_CTX_new failed\n", __func__);
3566 return;
3567 }
3568 BN_CTX_start(bnctx);
3569 if ((x = BN_CTX_get(bnctx)) == NULL ||
3570 (y = BN_CTX_get(bnctx)) == NULL) {
3571 fprintf(stderr, "%s: BN_CTX_get failed\n", __func__);
3572 return;
3573 }
3574 if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
3575 NID_X9_62_prime_field) {
3576 fprintf(stderr, "%s: group is not a prime field\n", __func__);
3577 return;
3578 }
3579 if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y,
3580 bnctx) != 1) {
3581 fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n",
3582 __func__);
3583 return;
3584 }
3585 fputs("x=", stderr);
3586 BN_print_fp(stderr, x);
3587 fputs("\ny=", stderr);
3588 BN_print_fp(stderr, y);
3589 fputs("\n", stderr);
3590 BN_CTX_free(bnctx);
3591}
3592
3593void
3594sshkey_dump_ec_key(const EC_KEY *key)
3595{
3596 const BIGNUM *exponent;
3597
3598 sshkey_dump_ec_point(EC_KEY_get0_group(key),
3599 EC_KEY_get0_public_key(key));
3600 fputs("exponent=", stderr);
3601 if ((exponent = EC_KEY_get0_private_key(key)) == NULL)
3602 fputs("(NULL)", stderr);
3603 else
3604 BN_print_fp(stderr, EC_KEY_get0_private_key(key));
3605 fputs("\n", stderr);
3606}
3607#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
3608
3609static int
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003610sshkey_private_to_blob2(struct sshkey *prv, struct sshbuf *blob,
Damien Miller86687062014-07-02 15:28:02 +10003611 const char *passphrase, const char *comment, const char *ciphername,
3612 int rounds)
3613{
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003614 u_char *cp, *key = NULL, *pubkeyblob = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003615 u_char salt[SALT_LEN];
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003616 char *b64 = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003617 size_t i, pubkeylen, keylen, ivlen, blocksize, authlen;
3618 u_int check;
3619 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003620 struct sshcipher_ctx *ciphercontext = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003621 const struct sshcipher *cipher;
3622 const char *kdfname = KDFNAME;
3623 struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL;
3624
Damien Miller86687062014-07-02 15:28:02 +10003625 if (rounds <= 0)
3626 rounds = DEFAULT_ROUNDS;
3627 if (passphrase == NULL || !strlen(passphrase)) {
3628 ciphername = "none";
3629 kdfname = "none";
3630 } else if (ciphername == NULL)
3631 ciphername = DEFAULT_CIPHERNAME;
Damien Miller86687062014-07-02 15:28:02 +10003632 if ((cipher = cipher_by_name(ciphername)) == NULL) {
djm@openbsd.orgcdccebd2017-04-30 23:15:04 +00003633 r = SSH_ERR_INVALID_ARGUMENT;
Damien Miller86687062014-07-02 15:28:02 +10003634 goto out;
3635 }
3636
3637 if ((kdf = sshbuf_new()) == NULL ||
3638 (encoded = sshbuf_new()) == NULL ||
3639 (encrypted = sshbuf_new()) == NULL) {
3640 r = SSH_ERR_ALLOC_FAIL;
3641 goto out;
3642 }
3643 blocksize = cipher_blocksize(cipher);
3644 keylen = cipher_keylen(cipher);
3645 ivlen = cipher_ivlen(cipher);
3646 authlen = cipher_authlen(cipher);
3647 if ((key = calloc(1, keylen + ivlen)) == NULL) {
3648 r = SSH_ERR_ALLOC_FAIL;
3649 goto out;
3650 }
3651 if (strcmp(kdfname, "bcrypt") == 0) {
3652 arc4random_buf(salt, SALT_LEN);
3653 if (bcrypt_pbkdf(passphrase, strlen(passphrase),
3654 salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) {
3655 r = SSH_ERR_INVALID_ARGUMENT;
3656 goto out;
3657 }
3658 if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 ||
3659 (r = sshbuf_put_u32(kdf, rounds)) != 0)
3660 goto out;
3661 } else if (strcmp(kdfname, "none") != 0) {
3662 /* Unsupported KDF type */
3663 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3664 goto out;
3665 }
3666 if ((r = cipher_init(&ciphercontext, cipher, key, keylen,
3667 key + keylen, ivlen, 1)) != 0)
3668 goto out;
3669
3670 if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 ||
3671 (r = sshbuf_put_cstring(encoded, ciphername)) != 0 ||
3672 (r = sshbuf_put_cstring(encoded, kdfname)) != 0 ||
3673 (r = sshbuf_put_stringb(encoded, kdf)) != 0 ||
3674 (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */
3675 (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 ||
3676 (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0)
3677 goto out;
3678
3679 /* set up the buffer that will be encrypted */
3680
3681 /* Random check bytes */
3682 check = arc4random();
3683 if ((r = sshbuf_put_u32(encrypted, check)) != 0 ||
3684 (r = sshbuf_put_u32(encrypted, check)) != 0)
3685 goto out;
3686
3687 /* append private key and comment*/
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003688 if ((r = sshkey_private_serialize_opt(prv, encrypted,
3689 SSHKEY_SERIALIZE_FULL)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003690 (r = sshbuf_put_cstring(encrypted, comment)) != 0)
3691 goto out;
3692
3693 /* padding */
3694 i = 0;
3695 while (sshbuf_len(encrypted) % blocksize) {
3696 if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0)
3697 goto out;
3698 }
3699
3700 /* length in destination buffer */
3701 if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0)
3702 goto out;
3703
3704 /* encrypt */
3705 if ((r = sshbuf_reserve(encoded,
3706 sshbuf_len(encrypted) + authlen, &cp)) != 0)
3707 goto out;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003708 if ((r = cipher_crypt(ciphercontext, 0, cp,
Damien Miller86687062014-07-02 15:28:02 +10003709 sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0)
3710 goto out;
3711
Damien Miller86687062014-07-02 15:28:02 +10003712 sshbuf_reset(blob);
djm@openbsd.org16dd8b22019-07-16 13:18:39 +00003713
3714 /* assemble uuencoded key */
3715 if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0 ||
3716 (r = sshbuf_dtob64(encoded, blob, 1)) != 0 ||
3717 (r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003718 goto out;
3719
3720 /* success */
3721 r = 0;
3722
3723 out:
3724 sshbuf_free(kdf);
3725 sshbuf_free(encoded);
3726 sshbuf_free(encrypted);
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003727 cipher_free(ciphercontext);
Damien Miller86687062014-07-02 15:28:02 +10003728 explicit_bzero(salt, sizeof(salt));
3729 if (key != NULL) {
3730 explicit_bzero(key, keylen + ivlen);
3731 free(key);
3732 }
3733 if (pubkeyblob != NULL) {
3734 explicit_bzero(pubkeyblob, pubkeylen);
3735 free(pubkeyblob);
3736 }
3737 if (b64 != NULL) {
3738 explicit_bzero(b64, strlen(b64));
3739 free(b64);
3740 }
3741 return r;
3742}
3743
3744static int
3745sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
3746 struct sshkey **keyp, char **commentp)
3747{
3748 char *comment = NULL, *ciphername = NULL, *kdfname = NULL;
3749 const struct sshcipher *cipher = NULL;
3750 const u_char *cp;
3751 int r = SSH_ERR_INTERNAL_ERROR;
3752 size_t encoded_len;
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003753 size_t i, keylen = 0, ivlen = 0, authlen = 0, slen = 0;
Damien Miller86687062014-07-02 15:28:02 +10003754 struct sshbuf *encoded = NULL, *decoded = NULL;
3755 struct sshbuf *kdf = NULL, *decrypted = NULL;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003756 struct sshcipher_ctx *ciphercontext = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003757 struct sshkey *k = NULL;
3758 u_char *key = NULL, *salt = NULL, *dp, pad, last;
3759 u_int blocksize, rounds, nkeys, encrypted_len, check1, check2;
3760
Damien Miller86687062014-07-02 15:28:02 +10003761 if (keyp != NULL)
3762 *keyp = NULL;
3763 if (commentp != NULL)
3764 *commentp = NULL;
3765
3766 if ((encoded = sshbuf_new()) == NULL ||
3767 (decoded = sshbuf_new()) == NULL ||
3768 (decrypted = sshbuf_new()) == NULL) {
3769 r = SSH_ERR_ALLOC_FAIL;
3770 goto out;
3771 }
3772
3773 /* check preamble */
3774 cp = sshbuf_ptr(blob);
3775 encoded_len = sshbuf_len(blob);
3776 if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) ||
3777 memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) {
3778 r = SSH_ERR_INVALID_FORMAT;
3779 goto out;
3780 }
3781 cp += MARK_BEGIN_LEN;
3782 encoded_len -= MARK_BEGIN_LEN;
3783
3784 /* Look for end marker, removing whitespace as we go */
3785 while (encoded_len > 0) {
3786 if (*cp != '\n' && *cp != '\r') {
3787 if ((r = sshbuf_put_u8(encoded, *cp)) != 0)
3788 goto out;
3789 }
3790 last = *cp;
3791 encoded_len--;
3792 cp++;
3793 if (last == '\n') {
3794 if (encoded_len >= MARK_END_LEN &&
3795 memcmp(cp, MARK_END, MARK_END_LEN) == 0) {
3796 /* \0 terminate */
3797 if ((r = sshbuf_put_u8(encoded, 0)) != 0)
3798 goto out;
3799 break;
3800 }
3801 }
3802 }
3803 if (encoded_len == 0) {
3804 r = SSH_ERR_INVALID_FORMAT;
3805 goto out;
3806 }
3807
3808 /* decode base64 */
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003809 if ((r = sshbuf_b64tod(decoded, (char *)sshbuf_ptr(encoded))) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003810 goto out;
3811
3812 /* check magic */
3813 if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) ||
3814 memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) {
3815 r = SSH_ERR_INVALID_FORMAT;
3816 goto out;
3817 }
3818 /* parse public portion of key */
3819 if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 ||
3820 (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 ||
3821 (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 ||
3822 (r = sshbuf_froms(decoded, &kdf)) != 0 ||
3823 (r = sshbuf_get_u32(decoded, &nkeys)) != 0 ||
3824 (r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */
3825 (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0)
3826 goto out;
3827
3828 if ((cipher = cipher_by_name(ciphername)) == NULL) {
3829 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3830 goto out;
3831 }
3832 if ((passphrase == NULL || strlen(passphrase) == 0) &&
3833 strcmp(ciphername, "none") != 0) {
3834 /* passphrase required */
3835 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3836 goto out;
3837 }
3838 if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) {
3839 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3840 goto out;
3841 }
3842 if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) {
3843 r = SSH_ERR_INVALID_FORMAT;
3844 goto out;
3845 }
3846 if (nkeys != 1) {
3847 /* XXX only one key supported */
3848 r = SSH_ERR_INVALID_FORMAT;
3849 goto out;
3850 }
3851
3852 /* check size of encrypted key blob */
3853 blocksize = cipher_blocksize(cipher);
3854 if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
3855 r = SSH_ERR_INVALID_FORMAT;
3856 goto out;
3857 }
3858
3859 /* setup key */
3860 keylen = cipher_keylen(cipher);
3861 ivlen = cipher_ivlen(cipher);
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003862 authlen = cipher_authlen(cipher);
Damien Miller86687062014-07-02 15:28:02 +10003863 if ((key = calloc(1, keylen + ivlen)) == NULL) {
3864 r = SSH_ERR_ALLOC_FAIL;
3865 goto out;
3866 }
3867 if (strcmp(kdfname, "bcrypt") == 0) {
3868 if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 ||
3869 (r = sshbuf_get_u32(kdf, &rounds)) != 0)
3870 goto out;
3871 if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen,
3872 key, keylen + ivlen, rounds) < 0) {
3873 r = SSH_ERR_INVALID_FORMAT;
3874 goto out;
3875 }
3876 }
3877
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003878 /* check that an appropriate amount of auth data is present */
3879 if (sshbuf_len(decoded) < encrypted_len + authlen) {
3880 r = SSH_ERR_INVALID_FORMAT;
3881 goto out;
3882 }
3883
Damien Miller86687062014-07-02 15:28:02 +10003884 /* decrypt private portion of key */
3885 if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 ||
3886 (r = cipher_init(&ciphercontext, cipher, key, keylen,
3887 key + keylen, ivlen, 0)) != 0)
3888 goto out;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003889 if ((r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(decoded),
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003890 encrypted_len, 0, authlen)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10003891 /* an integrity error here indicates an incorrect passphrase */
3892 if (r == SSH_ERR_MAC_INVALID)
3893 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3894 goto out;
3895 }
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003896 if ((r = sshbuf_consume(decoded, encrypted_len + authlen)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003897 goto out;
3898 /* there should be no trailing data */
3899 if (sshbuf_len(decoded) != 0) {
3900 r = SSH_ERR_INVALID_FORMAT;
3901 goto out;
3902 }
3903
3904 /* check check bytes */
3905 if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 ||
3906 (r = sshbuf_get_u32(decrypted, &check2)) != 0)
3907 goto out;
3908 if (check1 != check2) {
3909 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3910 goto out;
3911 }
3912
3913 /* Load the private key and comment */
3914 if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 ||
3915 (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0)
3916 goto out;
3917
3918 /* Check deterministic padding */
3919 i = 0;
3920 while (sshbuf_len(decrypted)) {
3921 if ((r = sshbuf_get_u8(decrypted, &pad)) != 0)
3922 goto out;
3923 if (pad != (++i & 0xff)) {
3924 r = SSH_ERR_INVALID_FORMAT;
3925 goto out;
3926 }
3927 }
3928
3929 /* XXX decode pubkey and check against private */
3930
3931 /* success */
3932 r = 0;
3933 if (keyp != NULL) {
3934 *keyp = k;
3935 k = NULL;
3936 }
3937 if (commentp != NULL) {
3938 *commentp = comment;
3939 comment = NULL;
3940 }
3941 out:
3942 pad = 0;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003943 cipher_free(ciphercontext);
Damien Miller86687062014-07-02 15:28:02 +10003944 free(ciphername);
3945 free(kdfname);
3946 free(comment);
3947 if (salt != NULL) {
3948 explicit_bzero(salt, slen);
3949 free(salt);
3950 }
3951 if (key != NULL) {
3952 explicit_bzero(key, keylen + ivlen);
3953 free(key);
3954 }
3955 sshbuf_free(encoded);
3956 sshbuf_free(decoded);
3957 sshbuf_free(kdf);
3958 sshbuf_free(decrypted);
3959 sshkey_free(k);
3960 return r;
3961}
3962
Damien Miller86687062014-07-02 15:28:02 +10003963
3964#ifdef WITH_OPENSSL
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00003965/* convert SSH v2 key to PEM or PKCS#8 format */
Damien Miller86687062014-07-02 15:28:02 +10003966static int
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00003967sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf,
3968 int format, const char *_passphrase, const char *comment)
Damien Miller86687062014-07-02 15:28:02 +10003969{
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003970 int was_shielded = sshkey_is_shielded(key);
Damien Miller86687062014-07-02 15:28:02 +10003971 int success, r;
3972 int blen, len = strlen(_passphrase);
3973 u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
Darren Tucker8fed0a52017-03-29 10:16:15 +11003974 const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;
djm@openbsd.org224f1932017-10-13 06:24:51 +00003975 char *bptr;
Damien Miller86687062014-07-02 15:28:02 +10003976 BIO *bio = NULL;
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003977 struct sshbuf *blob;
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00003978 EVP_PKEY *pkey = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003979
3980 if (len > 0 && len <= 4)
3981 return SSH_ERR_PASSPHRASE_TOO_SHORT;
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003982 if ((blob = sshbuf_new()) == NULL)
Damien Miller86687062014-07-02 15:28:02 +10003983 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00003984 if ((bio = BIO_new(BIO_s_mem())) == NULL) {
3985 r = SSH_ERR_ALLOC_FAIL;
3986 goto out;
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003987 }
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00003988 if (format == SSHKEY_PRIVATE_PKCS8 && (pkey = EVP_PKEY_new()) == NULL) {
3989 r = SSH_ERR_ALLOC_FAIL;
3990 goto out;
3991 }
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00003992 if ((r = sshkey_unshield_private(key)) != 0)
3993 goto out;
Damien Miller86687062014-07-02 15:28:02 +10003994
3995 switch (key->type) {
3996 case KEY_DSA:
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00003997 if (format == SSHKEY_PRIVATE_PEM) {
3998 success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
3999 cipher, passphrase, len, NULL, NULL);
4000 } else {
4001 success = EVP_PKEY_set1_DSA(pkey, key->dsa);
4002 }
Damien Miller86687062014-07-02 15:28:02 +10004003 break;
4004#ifdef OPENSSL_HAS_ECC
4005 case KEY_ECDSA:
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00004006 if (format == SSHKEY_PRIVATE_PEM) {
4007 success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
4008 cipher, passphrase, len, NULL, NULL);
4009 } else {
4010 success = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa);
4011 }
Damien Miller86687062014-07-02 15:28:02 +10004012 break;
4013#endif
4014 case KEY_RSA:
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00004015 if (format == SSHKEY_PRIVATE_PEM) {
4016 success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
4017 cipher, passphrase, len, NULL, NULL);
4018 } else {
4019 success = EVP_PKEY_set1_RSA(pkey, key->rsa);
4020 }
Damien Miller86687062014-07-02 15:28:02 +10004021 break;
4022 default:
4023 success = 0;
4024 break;
4025 }
4026 if (success == 0) {
4027 r = SSH_ERR_LIBCRYPTO_ERROR;
4028 goto out;
4029 }
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00004030 if (format == SSHKEY_PRIVATE_PKCS8) {
4031 if ((success = PEM_write_bio_PrivateKey(bio, pkey, cipher,
4032 passphrase, len, NULL, NULL)) == 0) {
4033 r = SSH_ERR_LIBCRYPTO_ERROR;
4034 goto out;
4035 }
4036 }
Damien Miller86687062014-07-02 15:28:02 +10004037 if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) {
4038 r = SSH_ERR_INTERNAL_ERROR;
4039 goto out;
4040 }
4041 if ((r = sshbuf_put(blob, bptr, blen)) != 0)
4042 goto out;
4043 r = 0;
4044 out:
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00004045 if (was_shielded)
4046 r = sshkey_shield_private(key);
4047 if (r == 0)
4048 r = sshbuf_putb(buf, blob);
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00004049
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00004050 EVP_PKEY_free(pkey);
4051 sshbuf_free(blob);
Damien Miller86687062014-07-02 15:28:02 +10004052 BIO_free(bio);
4053 return r;
4054}
4055#endif /* WITH_OPENSSL */
4056
4057/* Serialise "key" to buffer "blob" */
4058int
4059sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
4060 const char *passphrase, const char *comment,
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00004061 int format, const char *openssh_format_cipher, int openssh_format_rounds)
Damien Miller86687062014-07-02 15:28:02 +10004062{
4063 switch (key->type) {
markus@openbsd.orgf067cca2015-01-12 13:29:27 +00004064#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10004065 case KEY_DSA:
4066 case KEY_ECDSA:
4067 case KEY_RSA:
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00004068 break; /* see below */
Damien Miller86687062014-07-02 15:28:02 +10004069#endif /* WITH_OPENSSL */
4070 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00004071#ifdef WITH_XMSS
4072 case KEY_XMSS:
4073#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10004074 return sshkey_private_to_blob2(key, blob, passphrase,
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00004075 comment, openssh_format_cipher, openssh_format_rounds);
Damien Miller86687062014-07-02 15:28:02 +10004076 default:
4077 return SSH_ERR_KEY_TYPE_UNKNOWN;
4078 }
djm@openbsd.orgeb0d8e72019-07-15 13:16:29 +00004079
4080#ifdef WITH_OPENSSL
4081 switch (format) {
4082 case SSHKEY_PRIVATE_OPENSSH:
4083 return sshkey_private_to_blob2(key, blob, passphrase,
4084 comment, openssh_format_cipher, openssh_format_rounds);
4085 case SSHKEY_PRIVATE_PEM:
4086 case SSHKEY_PRIVATE_PKCS8:
4087 return sshkey_private_to_blob_pem_pkcs8(key, blob,
4088 format, passphrase, comment);
4089 default:
4090 return SSH_ERR_INVALID_ARGUMENT;
4091 }
4092#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10004093}
4094
Damien Miller86687062014-07-02 15:28:02 +10004095
4096#ifdef WITH_OPENSSL
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00004097static int
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00004098translate_libcrypto_error(unsigned long pem_err)
4099{
4100 int pem_reason = ERR_GET_REASON(pem_err);
4101
4102 switch (ERR_GET_LIB(pem_err)) {
4103 case ERR_LIB_PEM:
4104 switch (pem_reason) {
4105 case PEM_R_BAD_PASSWORD_READ:
4106 case PEM_R_PROBLEMS_GETTING_PASSWORD:
4107 case PEM_R_BAD_DECRYPT:
4108 return SSH_ERR_KEY_WRONG_PASSPHRASE;
4109 default:
4110 return SSH_ERR_INVALID_FORMAT;
4111 }
4112 case ERR_LIB_EVP:
4113 switch (pem_reason) {
4114 case EVP_R_BAD_DECRYPT:
4115 return SSH_ERR_KEY_WRONG_PASSPHRASE;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00004116#ifdef EVP_R_BN_DECODE_ERROR
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00004117 case EVP_R_BN_DECODE_ERROR:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00004118#endif
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00004119 case EVP_R_DECODE_ERROR:
4120#ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR
4121 case EVP_R_PRIVATE_KEY_DECODE_ERROR:
4122#endif
4123 return SSH_ERR_INVALID_FORMAT;
4124 default:
4125 return SSH_ERR_LIBCRYPTO_ERROR;
4126 }
4127 case ERR_LIB_ASN1:
4128 return SSH_ERR_INVALID_FORMAT;
4129 }
4130 return SSH_ERR_LIBCRYPTO_ERROR;
4131}
4132
4133static void
4134clear_libcrypto_errors(void)
4135{
4136 while (ERR_get_error() != 0)
4137 ;
4138}
4139
4140/*
4141 * Translate OpenSSL error codes to determine whether
4142 * passphrase is required/incorrect.
4143 */
4144static int
4145convert_libcrypto_error(void)
4146{
4147 /*
4148 * Some password errors are reported at the beginning
4149 * of the error queue.
4150 */
4151 if (translate_libcrypto_error(ERR_peek_error()) ==
4152 SSH_ERR_KEY_WRONG_PASSPHRASE)
4153 return SSH_ERR_KEY_WRONG_PASSPHRASE;
4154 return translate_libcrypto_error(ERR_peek_last_error());
4155}
4156
4157static int
Damien Miller12731152018-10-11 10:29:29 +11004158pem_passphrase_cb(char *buf, int size, int rwflag, void *u)
4159{
4160 char *p = (char *)u;
4161 size_t len;
4162
4163 if (p == NULL || (len = strlen(p)) == 0)
4164 return -1;
4165 if (size < 0 || len > (size_t)size)
4166 return -1;
4167 memcpy(buf, p, len);
4168 return (int)len;
4169}
4170
4171static int
Damien Miller86687062014-07-02 15:28:02 +10004172sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00004173 const char *passphrase, struct sshkey **keyp)
Damien Miller86687062014-07-02 15:28:02 +10004174{
4175 EVP_PKEY *pk = NULL;
4176 struct sshkey *prv = NULL;
Damien Miller86687062014-07-02 15:28:02 +10004177 BIO *bio = NULL;
4178 int r;
4179
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00004180 if (keyp != NULL)
4181 *keyp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10004182
4183 if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX)
4184 return SSH_ERR_ALLOC_FAIL;
4185 if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) !=
4186 (int)sshbuf_len(blob)) {
4187 r = SSH_ERR_ALLOC_FAIL;
4188 goto out;
4189 }
4190
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00004191 clear_libcrypto_errors();
Damien Miller12731152018-10-11 10:29:29 +11004192 if ((pk = PEM_read_bio_PrivateKey(bio, NULL, pem_passphrase_cb,
Damien Miller86687062014-07-02 15:28:02 +10004193 (char *)passphrase)) == NULL) {
djm@openbsd.orgedbb6fe2018-10-09 05:42:23 +00004194 /*
4195 * libcrypto may return various ASN.1 errors when attempting
4196 * to parse a key with an incorrect passphrase.
4197 * Treat all format errors as "incorrect passphrase" if a
4198 * passphrase was supplied.
4199 */
4200 if (passphrase != NULL && *passphrase != '\0')
4201 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
4202 else
4203 r = convert_libcrypto_error();
Damien Miller86687062014-07-02 15:28:02 +10004204 goto out;
4205 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00004206 if (EVP_PKEY_base_id(pk) == EVP_PKEY_RSA &&
Damien Miller86687062014-07-02 15:28:02 +10004207 (type == KEY_UNSPEC || type == KEY_RSA)) {
4208 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
4209 r = SSH_ERR_ALLOC_FAIL;
4210 goto out;
4211 }
4212 prv->rsa = EVP_PKEY_get1_RSA(pk);
4213 prv->type = KEY_RSA;
Damien Miller86687062014-07-02 15:28:02 +10004214#ifdef DEBUG_PK
4215 RSA_print_fp(stderr, prv->rsa, 8);
4216#endif
4217 if (RSA_blinding_on(prv->rsa, NULL) != 1) {
4218 r = SSH_ERR_LIBCRYPTO_ERROR;
4219 goto out;
4220 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00004221 if ((r = check_rsa_length(prv->rsa)) != 0)
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00004222 goto out;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00004223 } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_DSA &&
Damien Miller86687062014-07-02 15:28:02 +10004224 (type == KEY_UNSPEC || type == KEY_DSA)) {
4225 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
4226 r = SSH_ERR_ALLOC_FAIL;
4227 goto out;
4228 }
4229 prv->dsa = EVP_PKEY_get1_DSA(pk);
4230 prv->type = KEY_DSA;
Damien Miller86687062014-07-02 15:28:02 +10004231#ifdef DEBUG_PK
4232 DSA_print_fp(stderr, prv->dsa, 8);
4233#endif
4234#ifdef OPENSSL_HAS_ECC
djm@openbsd.org482d23b2018-09-13 02:08:33 +00004235 } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_EC &&
Damien Miller86687062014-07-02 15:28:02 +10004236 (type == KEY_UNSPEC || type == KEY_ECDSA)) {
4237 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
4238 r = SSH_ERR_ALLOC_FAIL;
4239 goto out;
4240 }
4241 prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
4242 prv->type = KEY_ECDSA;
4243 prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa);
4244 if (prv->ecdsa_nid == -1 ||
4245 sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
4246 sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
4247 EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
4248 sshkey_ec_validate_private(prv->ecdsa) != 0) {
4249 r = SSH_ERR_INVALID_FORMAT;
4250 goto out;
4251 }
Damien Miller86687062014-07-02 15:28:02 +10004252# ifdef DEBUG_PK
4253 if (prv != NULL && prv->ecdsa != NULL)
4254 sshkey_dump_ec_key(prv->ecdsa);
4255# endif
4256#endif /* OPENSSL_HAS_ECC */
4257 } else {
4258 r = SSH_ERR_INVALID_FORMAT;
4259 goto out;
4260 }
Damien Miller86687062014-07-02 15:28:02 +10004261 r = 0;
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00004262 if (keyp != NULL) {
4263 *keyp = prv;
4264 prv = NULL;
4265 }
Damien Miller86687062014-07-02 15:28:02 +10004266 out:
4267 BIO_free(bio);
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00004268 EVP_PKEY_free(pk);
mmcc@openbsd.org89540b62015-12-11 02:31:47 +00004269 sshkey_free(prv);
Damien Miller86687062014-07-02 15:28:02 +10004270 return r;
4271}
4272#endif /* WITH_OPENSSL */
4273
4274int
4275sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
4276 const char *passphrase, struct sshkey **keyp, char **commentp)
4277{
djm@openbsd.org155d5402017-02-10 04:34:50 +00004278 int r = SSH_ERR_INTERNAL_ERROR;
4279
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00004280 if (keyp != NULL)
4281 *keyp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10004282 if (commentp != NULL)
4283 *commentp = NULL;
4284
4285 switch (type) {
markus@openbsd.orgf067cca2015-01-12 13:29:27 +00004286#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10004287 case KEY_DSA:
4288 case KEY_ECDSA:
4289 case KEY_RSA:
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00004290 return sshkey_parse_private_pem_fileblob(blob, type,
4291 passphrase, keyp);
Damien Miller86687062014-07-02 15:28:02 +10004292#endif /* WITH_OPENSSL */
4293 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00004294#ifdef WITH_XMSS
4295 case KEY_XMSS:
4296#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10004297 return sshkey_parse_private2(blob, type, passphrase,
4298 keyp, commentp);
4299 case KEY_UNSPEC:
djm@openbsd.org155d5402017-02-10 04:34:50 +00004300 r = sshkey_parse_private2(blob, type, passphrase, keyp,
4301 commentp);
4302 /* Do not fallback to PEM parser if only passphrase is wrong. */
4303 if (r == 0 || r == SSH_ERR_KEY_WRONG_PASSPHRASE)
4304 return r;
Damien Miller86687062014-07-02 15:28:02 +10004305#ifdef WITH_OPENSSL
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00004306 return sshkey_parse_private_pem_fileblob(blob, type,
4307 passphrase, keyp);
Damien Miller86687062014-07-02 15:28:02 +10004308#else
4309 return SSH_ERR_INVALID_FORMAT;
4310#endif /* WITH_OPENSSL */
4311 default:
4312 return SSH_ERR_KEY_TYPE_UNKNOWN;
4313 }
4314}
4315
4316int
4317sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase,
tim@openbsd.org3c019a92015-09-13 14:39:16 +00004318 struct sshkey **keyp, char **commentp)
Damien Miller86687062014-07-02 15:28:02 +10004319{
Damien Miller86687062014-07-02 15:28:02 +10004320 if (keyp != NULL)
4321 *keyp = NULL;
4322 if (commentp != NULL)
4323 *commentp = NULL;
4324
tim@openbsd.org3c019a92015-09-13 14:39:16 +00004325 return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC,
4326 passphrase, keyp, commentp);
Damien Miller86687062014-07-02 15:28:02 +10004327}
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00004328
4329#ifdef WITH_XMSS
4330/*
4331 * serialize the key with the current state and forward the state
4332 * maxsign times.
4333 */
4334int
djm@openbsd.org8de52eb2019-06-23 12:21:46 +00004335sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b,
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00004336 u_int32_t maxsign, sshkey_printfn *pr)
4337{
4338 int r, rupdate;
4339
4340 if (maxsign == 0 ||
4341 sshkey_type_plain(k->type) != KEY_XMSS)
4342 return sshkey_private_serialize_opt(k, b,
4343 SSHKEY_SERIALIZE_DEFAULT);
4344 if ((r = sshkey_xmss_get_state(k, pr)) != 0 ||
4345 (r = sshkey_private_serialize_opt(k, b,
4346 SSHKEY_SERIALIZE_STATE)) != 0 ||
4347 (r = sshkey_xmss_forward_state(k, maxsign)) != 0)
4348 goto out;
4349 r = 0;
4350out:
4351 if ((rupdate = sshkey_xmss_update_state(k, pr)) != 0) {
4352 if (r == 0)
4353 r = rupdate;
4354 }
4355 return r;
4356}
4357
4358u_int32_t
4359sshkey_signatures_left(const struct sshkey *k)
4360{
4361 if (sshkey_type_plain(k->type) == KEY_XMSS)
4362 return sshkey_xmss_signatures_left(k);
4363 return 0;
4364}
4365
4366int
4367sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
4368{
4369 if (sshkey_type_plain(k->type) != KEY_XMSS)
4370 return SSH_ERR_INVALID_ARGUMENT;
4371 return sshkey_xmss_enable_maxsign(k, maxsign);
4372}
4373
4374int
4375sshkey_set_filename(struct sshkey *k, const char *filename)
4376{
4377 if (k == NULL)
4378 return SSH_ERR_INVALID_ARGUMENT;
4379 if (sshkey_type_plain(k->type) != KEY_XMSS)
4380 return 0;
4381 if (filename == NULL)
4382 return SSH_ERR_INVALID_ARGUMENT;
4383 if ((k->xmss_filename = strdup(filename)) == NULL)
4384 return SSH_ERR_ALLOC_FAIL;
4385 return 0;
4386}
4387#else
4388int
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00004389sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b,
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00004390 u_int32_t maxsign, sshkey_printfn *pr)
4391{
4392 return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT);
4393}
4394
4395u_int32_t
4396sshkey_signatures_left(const struct sshkey *k)
4397{
4398 return 0;
4399}
4400
4401int
4402sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
4403{
4404 return SSH_ERR_INVALID_ARGUMENT;
4405}
4406
4407int
4408sshkey_set_filename(struct sshkey *k, const char *filename)
4409{
4410 if (k == NULL)
4411 return SSH_ERR_INVALID_ARGUMENT;
4412 return 0;
4413}
4414#endif /* WITH_XMSS */