blob: 7136f6986e6412e72b3bcab25baf4e54e2229d9c [file] [log] [blame]
djm@openbsd.orgf8df0412019-09-03 08:31:20 +00001/* $OpenBSD: sshkey.c,v 1.82 2019/09/03 08:31:20 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);
93
94/* Supported key types */
95struct keytype {
96 const char *name;
97 const char *shortname;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +000098 const char *sigalg;
Damien Miller86687062014-07-02 15:28:02 +100099 int type;
100 int nid;
101 int cert;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000102 int sigonly;
Damien Miller86687062014-07-02 15:28:02 +1000103};
104static const struct keytype keytypes[] = {
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000105 { "ssh-ed25519", "ED25519", NULL, KEY_ED25519, 0, 0, 0 },
106 { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000107 KEY_ED25519_CERT, 0, 1, 0 },
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000108#ifdef WITH_XMSS
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000109 { "ssh-xmss@openssh.com", "XMSS", NULL, KEY_XMSS, 0, 0, 0 },
110 { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", NULL,
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000111 KEY_XMSS_CERT, 0, 1, 0 },
112#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000113#ifdef WITH_OPENSSL
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000114 { "ssh-rsa", "RSA", NULL, KEY_RSA, 0, 0, 0 },
115 { "rsa-sha2-256", "RSA", NULL, KEY_RSA, 0, 0, 1 },
116 { "rsa-sha2-512", "RSA", NULL, KEY_RSA, 0, 0, 1 },
117 { "ssh-dss", "DSA", NULL, KEY_DSA, 0, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000118# ifdef OPENSSL_HAS_ECC
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000119 { "ecdsa-sha2-nistp256", "ECDSA", NULL,
120 KEY_ECDSA, NID_X9_62_prime256v1, 0, 0 },
121 { "ecdsa-sha2-nistp384", "ECDSA", NULL,
122 KEY_ECDSA, NID_secp384r1, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000123# ifdef OPENSSL_HAS_NISTP521
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000124 { "ecdsa-sha2-nistp521", "ECDSA", NULL,
125 KEY_ECDSA, NID_secp521r1, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000126# endif /* OPENSSL_HAS_NISTP521 */
127# endif /* OPENSSL_HAS_ECC */
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000128 { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL,
129 KEY_RSA_CERT, 0, 1, 0 },
130 { "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT",
djm@openbsd.orgebfafd92018-10-11 00:52:46 +0000131 "rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 },
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000132 { "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT",
djm@openbsd.orgebfafd92018-10-11 00:52:46 +0000133 "rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 },
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000134 { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL,
135 KEY_DSA_CERT, 0, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000136# ifdef OPENSSL_HAS_ECC
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000137 { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000138 KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1, 0 },
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000139 { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000140 KEY_ECDSA_CERT, NID_secp384r1, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000141# ifdef OPENSSL_HAS_NISTP521
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000142 { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", NULL,
143 KEY_ECDSA_CERT, NID_secp521r1, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000144# endif /* OPENSSL_HAS_NISTP521 */
145# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +1000146#endif /* WITH_OPENSSL */
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000147 { NULL, NULL, NULL, -1, -1, 0, 0 }
Damien Miller86687062014-07-02 15:28:02 +1000148};
149
150const char *
151sshkey_type(const struct sshkey *k)
152{
153 const struct keytype *kt;
154
155 for (kt = keytypes; kt->type != -1; kt++) {
156 if (kt->type == k->type)
157 return kt->shortname;
158 }
159 return "unknown";
160}
161
162static const char *
163sshkey_ssh_name_from_type_nid(int type, int nid)
164{
165 const struct keytype *kt;
166
167 for (kt = keytypes; kt->type != -1; kt++) {
168 if (kt->type == type && (kt->nid == 0 || kt->nid == nid))
169 return kt->name;
170 }
171 return "ssh-unknown";
172}
173
174int
175sshkey_type_is_cert(int type)
176{
177 const struct keytype *kt;
178
179 for (kt = keytypes; kt->type != -1; kt++) {
180 if (kt->type == type)
181 return kt->cert;
182 }
183 return 0;
184}
185
186const char *
187sshkey_ssh_name(const struct sshkey *k)
188{
189 return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid);
190}
191
192const char *
193sshkey_ssh_name_plain(const struct sshkey *k)
194{
195 return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type),
196 k->ecdsa_nid);
197}
198
199int
200sshkey_type_from_name(const char *name)
201{
202 const struct keytype *kt;
203
204 for (kt = keytypes; kt->type != -1; kt++) {
205 /* Only allow shortname matches for plain key types */
206 if ((kt->name != NULL && strcmp(name, kt->name) == 0) ||
207 (!kt->cert && strcasecmp(kt->shortname, name) == 0))
208 return kt->type;
209 }
210 return KEY_UNSPEC;
211}
212
213int
214sshkey_ecdsa_nid_from_name(const char *name)
215{
216 const struct keytype *kt;
217
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +0000218 for (kt = keytypes; kt->type != -1; kt++) {
219 if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT)
220 continue;
221 if (kt->name != NULL && strcmp(name, kt->name) == 0)
222 return kt->nid;
223 }
Damien Miller86687062014-07-02 15:28:02 +1000224 return -1;
225}
226
227char *
djm@openbsd.org183ba552017-03-10 04:07:20 +0000228sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
Damien Miller86687062014-07-02 15:28:02 +1000229{
230 char *tmp, *ret = NULL;
231 size_t nlen, rlen = 0;
232 const struct keytype *kt;
233
234 for (kt = keytypes; kt->type != -1; kt++) {
djm@openbsd.org183ba552017-03-10 04:07:20 +0000235 if (kt->name == NULL)
236 continue;
237 if (!include_sigonly && kt->sigonly)
Damien Miller86687062014-07-02 15:28:02 +1000238 continue;
239 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
240 continue;
241 if (ret != NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000242 ret[rlen++] = sep;
Damien Miller86687062014-07-02 15:28:02 +1000243 nlen = strlen(kt->name);
244 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
245 free(ret);
246 return NULL;
247 }
248 ret = tmp;
249 memcpy(ret + rlen, kt->name, nlen + 1);
250 rlen += nlen;
251 }
252 return ret;
253}
254
255int
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000256sshkey_names_valid2(const char *names, int allow_wildcard)
Damien Miller86687062014-07-02 15:28:02 +1000257{
258 char *s, *cp, *p;
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000259 const struct keytype *kt;
260 int type;
Damien Miller86687062014-07-02 15:28:02 +1000261
262 if (names == NULL || strcmp(names, "") == 0)
263 return 0;
264 if ((s = cp = strdup(names)) == NULL)
265 return 0;
266 for ((p = strsep(&cp, ",")); p && *p != '\0';
267 (p = strsep(&cp, ","))) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000268 type = sshkey_type_from_name(p);
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000269 if (type == KEY_UNSPEC) {
270 if (allow_wildcard) {
271 /*
272 * Try matching key types against the string.
273 * If any has a positive or negative match then
274 * the component is accepted.
275 */
276 for (kt = keytypes; kt->type != -1; kt++) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000277 if (match_pattern_list(kt->name,
djm@openbsd.orge661a862015-05-04 06:10:48 +0000278 p, 0) != 0)
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000279 break;
280 }
281 if (kt->type != -1)
282 continue;
283 }
Damien Miller86687062014-07-02 15:28:02 +1000284 free(s);
285 return 0;
286 }
287 }
288 free(s);
289 return 1;
290}
291
292u_int
293sshkey_size(const struct sshkey *k)
294{
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000295#ifdef WITH_OPENSSL
296 const BIGNUM *rsa_n, *dsa_p;
297#endif /* WITH_OPENSSL */
298
Damien Miller86687062014-07-02 15:28:02 +1000299 switch (k->type) {
300#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000301 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000302 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000303 if (k->rsa == NULL)
304 return 0;
305 RSA_get0_key(k->rsa, &rsa_n, NULL, NULL);
306 return BN_num_bits(rsa_n);
Damien Miller86687062014-07-02 15:28:02 +1000307 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000308 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000309 if (k->dsa == NULL)
310 return 0;
311 DSA_get0_pqg(k->dsa, &dsa_p, NULL, NULL);
312 return BN_num_bits(dsa_p);
Damien Miller86687062014-07-02 15:28:02 +1000313 case KEY_ECDSA:
314 case KEY_ECDSA_CERT:
315 return sshkey_curve_nid_to_bits(k->ecdsa_nid);
316#endif /* WITH_OPENSSL */
317 case KEY_ED25519:
318 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000319 case KEY_XMSS:
320 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000321 return 256; /* XXX */
322 }
323 return 0;
324}
325
Damien Miller86687062014-07-02 15:28:02 +1000326static int
327sshkey_type_is_valid_ca(int type)
328{
329 switch (type) {
330 case KEY_RSA:
331 case KEY_DSA:
332 case KEY_ECDSA:
333 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000334 case KEY_XMSS:
Damien Miller86687062014-07-02 15:28:02 +1000335 return 1;
336 default:
337 return 0;
338 }
339}
340
341int
342sshkey_is_cert(const struct sshkey *k)
343{
344 if (k == NULL)
345 return 0;
346 return sshkey_type_is_cert(k->type);
347}
348
349/* Return the cert-less equivalent to a certified key type */
350int
351sshkey_type_plain(int type)
352{
353 switch (type) {
Damien Miller86687062014-07-02 15:28:02 +1000354 case KEY_RSA_CERT:
355 return KEY_RSA;
Damien Miller86687062014-07-02 15:28:02 +1000356 case KEY_DSA_CERT:
357 return KEY_DSA;
358 case KEY_ECDSA_CERT:
359 return KEY_ECDSA;
360 case KEY_ED25519_CERT:
361 return KEY_ED25519;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000362 case KEY_XMSS_CERT:
363 return KEY_XMSS;
Damien Miller86687062014-07-02 15:28:02 +1000364 default:
365 return type;
366 }
367}
368
369#ifdef WITH_OPENSSL
370/* XXX: these are really begging for a table-driven approach */
371int
372sshkey_curve_name_to_nid(const char *name)
373{
374 if (strcmp(name, "nistp256") == 0)
375 return NID_X9_62_prime256v1;
376 else if (strcmp(name, "nistp384") == 0)
377 return NID_secp384r1;
378# ifdef OPENSSL_HAS_NISTP521
379 else if (strcmp(name, "nistp521") == 0)
380 return NID_secp521r1;
381# endif /* OPENSSL_HAS_NISTP521 */
382 else
383 return -1;
384}
385
386u_int
387sshkey_curve_nid_to_bits(int nid)
388{
389 switch (nid) {
390 case NID_X9_62_prime256v1:
391 return 256;
392 case NID_secp384r1:
393 return 384;
394# ifdef OPENSSL_HAS_NISTP521
395 case NID_secp521r1:
396 return 521;
397# endif /* OPENSSL_HAS_NISTP521 */
398 default:
399 return 0;
400 }
401}
402
403int
404sshkey_ecdsa_bits_to_nid(int bits)
405{
406 switch (bits) {
407 case 256:
408 return NID_X9_62_prime256v1;
409 case 384:
410 return NID_secp384r1;
411# ifdef OPENSSL_HAS_NISTP521
412 case 521:
413 return NID_secp521r1;
414# endif /* OPENSSL_HAS_NISTP521 */
415 default:
416 return -1;
417 }
418}
419
420const char *
421sshkey_curve_nid_to_name(int nid)
422{
423 switch (nid) {
424 case NID_X9_62_prime256v1:
425 return "nistp256";
426 case NID_secp384r1:
427 return "nistp384";
428# ifdef OPENSSL_HAS_NISTP521
429 case NID_secp521r1:
430 return "nistp521";
431# endif /* OPENSSL_HAS_NISTP521 */
432 default:
433 return NULL;
434 }
435}
436
437int
438sshkey_ec_nid_to_hash_alg(int nid)
439{
440 int kbits = sshkey_curve_nid_to_bits(nid);
441
442 if (kbits <= 0)
443 return -1;
444
445 /* RFC5656 section 6.2.1 */
446 if (kbits <= 256)
447 return SSH_DIGEST_SHA256;
448 else if (kbits <= 384)
449 return SSH_DIGEST_SHA384;
450 else
451 return SSH_DIGEST_SHA512;
452}
453#endif /* WITH_OPENSSL */
454
455static void
456cert_free(struct sshkey_cert *cert)
457{
458 u_int i;
459
460 if (cert == NULL)
461 return;
mmcc@openbsd.org52d70782015-12-11 04:21:11 +0000462 sshbuf_free(cert->certblob);
463 sshbuf_free(cert->critical);
464 sshbuf_free(cert->extensions);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000465 free(cert->key_id);
Damien Miller86687062014-07-02 15:28:02 +1000466 for (i = 0; i < cert->nprincipals; i++)
467 free(cert->principals[i]);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000468 free(cert->principals);
mmcc@openbsd.org89540b62015-12-11 02:31:47 +0000469 sshkey_free(cert->signature_key);
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +0000470 free(cert->signature_type);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000471 freezero(cert, sizeof(*cert));
Damien Miller86687062014-07-02 15:28:02 +1000472}
473
474static struct sshkey_cert *
475cert_new(void)
476{
477 struct sshkey_cert *cert;
478
479 if ((cert = calloc(1, sizeof(*cert))) == NULL)
480 return NULL;
481 if ((cert->certblob = sshbuf_new()) == NULL ||
482 (cert->critical = sshbuf_new()) == NULL ||
483 (cert->extensions = sshbuf_new()) == NULL) {
484 cert_free(cert);
485 return NULL;
486 }
487 cert->key_id = NULL;
488 cert->principals = NULL;
489 cert->signature_key = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +0000490 cert->signature_type = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000491 return cert;
492}
493
494struct sshkey *
495sshkey_new(int type)
496{
497 struct sshkey *k;
498#ifdef WITH_OPENSSL
499 RSA *rsa;
500 DSA *dsa;
501#endif /* WITH_OPENSSL */
502
503 if ((k = calloc(1, sizeof(*k))) == NULL)
504 return NULL;
505 k->type = type;
506 k->ecdsa = NULL;
507 k->ecdsa_nid = -1;
508 k->dsa = NULL;
509 k->rsa = NULL;
510 k->cert = NULL;
511 k->ed25519_sk = NULL;
512 k->ed25519_pk = NULL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000513 k->xmss_sk = NULL;
514 k->xmss_pk = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000515 switch (k->type) {
516#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000517 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000518 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000519 if ((rsa = RSA_new()) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +1000520 free(k);
521 return NULL;
522 }
523 k->rsa = rsa;
524 break;
525 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000526 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000527 if ((dsa = DSA_new()) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +1000528 free(k);
529 return NULL;
530 }
531 k->dsa = dsa;
532 break;
533 case KEY_ECDSA:
534 case KEY_ECDSA_CERT:
535 /* Cannot do anything until we know the group */
536 break;
537#endif /* WITH_OPENSSL */
538 case KEY_ED25519:
539 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000540 case KEY_XMSS:
541 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000542 /* no need to prealloc */
543 break;
544 case KEY_UNSPEC:
545 break;
546 default:
547 free(k);
548 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000549 }
550
551 if (sshkey_is_cert(k)) {
552 if ((k->cert = cert_new()) == NULL) {
553 sshkey_free(k);
554 return NULL;
555 }
556 }
557
558 return k;
559}
560
Damien Miller86687062014-07-02 15:28:02 +1000561void
562sshkey_free(struct sshkey *k)
563{
564 if (k == NULL)
565 return;
566 switch (k->type) {
567#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000568 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000569 case KEY_RSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000570 RSA_free(k->rsa);
Damien Miller86687062014-07-02 15:28:02 +1000571 k->rsa = NULL;
572 break;
573 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000574 case KEY_DSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000575 DSA_free(k->dsa);
Damien Miller86687062014-07-02 15:28:02 +1000576 k->dsa = NULL;
577 break;
578# ifdef OPENSSL_HAS_ECC
579 case KEY_ECDSA:
580 case KEY_ECDSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000581 EC_KEY_free(k->ecdsa);
Damien Miller86687062014-07-02 15:28:02 +1000582 k->ecdsa = NULL;
583 break;
584# endif /* OPENSSL_HAS_ECC */
585#endif /* WITH_OPENSSL */
586 case KEY_ED25519:
587 case KEY_ED25519_CERT:
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000588 freezero(k->ed25519_pk, ED25519_PK_SZ);
589 k->ed25519_pk = NULL;
590 freezero(k->ed25519_sk, ED25519_SK_SZ);
591 k->ed25519_sk = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000592 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000593#ifdef WITH_XMSS
594 case KEY_XMSS:
595 case KEY_XMSS_CERT:
596 freezero(k->xmss_pk, sshkey_xmss_pklen(k));
597 k->xmss_pk = NULL;
598 freezero(k->xmss_sk, sshkey_xmss_sklen(k));
599 k->xmss_sk = NULL;
600 sshkey_xmss_free_state(k);
601 free(k->xmss_name);
602 k->xmss_name = NULL;
603 free(k->xmss_filename);
604 k->xmss_filename = NULL;
605 break;
606#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000607 case KEY_UNSPEC:
608 break;
609 default:
610 break;
611 }
612 if (sshkey_is_cert(k))
613 cert_free(k->cert);
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +0000614 freezero(k->shielded_private, k->shielded_len);
615 freezero(k->shield_prekey, k->shield_prekey_len);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000616 freezero(k, sizeof(*k));
Damien Miller86687062014-07-02 15:28:02 +1000617}
618
619static int
620cert_compare(struct sshkey_cert *a, struct sshkey_cert *b)
621{
622 if (a == NULL && b == NULL)
623 return 1;
624 if (a == NULL || b == NULL)
625 return 0;
626 if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob))
627 return 0;
628 if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob),
629 sshbuf_len(a->certblob)) != 0)
630 return 0;
631 return 1;
632}
633
634/*
635 * Compare public portions of key only, allowing comparisons between
636 * certificates and plain keys too.
637 */
638int
639sshkey_equal_public(const struct sshkey *a, const struct sshkey *b)
640{
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000641#if defined(WITH_OPENSSL)
642 const BIGNUM *rsa_e_a, *rsa_n_a;
643 const BIGNUM *rsa_e_b, *rsa_n_b;
644 const BIGNUM *dsa_p_a, *dsa_q_a, *dsa_g_a, *dsa_pub_key_a;
645 const BIGNUM *dsa_p_b, *dsa_q_b, *dsa_g_b, *dsa_pub_key_b;
646# if defined(OPENSSL_HAS_ECC)
Damien Miller86687062014-07-02 15:28:02 +1000647 BN_CTX *bnctx;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000648# endif /* OPENSSL_HAS_ECC */
649#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +1000650
651 if (a == NULL || b == NULL ||
652 sshkey_type_plain(a->type) != sshkey_type_plain(b->type))
653 return 0;
654
655 switch (a->type) {
656#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000657 case KEY_RSA_CERT:
658 case KEY_RSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000659 if (a->rsa == NULL || b->rsa == NULL)
660 return 0;
661 RSA_get0_key(a->rsa, &rsa_n_a, &rsa_e_a, NULL);
662 RSA_get0_key(b->rsa, &rsa_n_b, &rsa_e_b, NULL);
663 return BN_cmp(rsa_e_a, rsa_e_b) == 0 &&
664 BN_cmp(rsa_n_a, rsa_n_b) == 0;
Damien Miller86687062014-07-02 15:28:02 +1000665 case KEY_DSA_CERT:
666 case KEY_DSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000667 if (a->dsa == NULL || b->dsa == NULL)
668 return 0;
669 DSA_get0_pqg(a->dsa, &dsa_p_a, &dsa_q_a, &dsa_g_a);
670 DSA_get0_pqg(b->dsa, &dsa_p_b, &dsa_q_b, &dsa_g_b);
671 DSA_get0_key(a->dsa, &dsa_pub_key_a, NULL);
672 DSA_get0_key(b->dsa, &dsa_pub_key_b, NULL);
673 return BN_cmp(dsa_p_a, dsa_p_b) == 0 &&
674 BN_cmp(dsa_q_a, dsa_q_b) == 0 &&
675 BN_cmp(dsa_g_a, dsa_g_b) == 0 &&
676 BN_cmp(dsa_pub_key_a, dsa_pub_key_b) == 0;
Damien Miller86687062014-07-02 15:28:02 +1000677# ifdef OPENSSL_HAS_ECC
678 case KEY_ECDSA_CERT:
679 case KEY_ECDSA:
680 if (a->ecdsa == NULL || b->ecdsa == NULL ||
681 EC_KEY_get0_public_key(a->ecdsa) == NULL ||
682 EC_KEY_get0_public_key(b->ecdsa) == NULL)
683 return 0;
684 if ((bnctx = BN_CTX_new()) == NULL)
685 return 0;
686 if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa),
687 EC_KEY_get0_group(b->ecdsa), bnctx) != 0 ||
688 EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa),
689 EC_KEY_get0_public_key(a->ecdsa),
690 EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) {
691 BN_CTX_free(bnctx);
692 return 0;
693 }
694 BN_CTX_free(bnctx);
695 return 1;
696# endif /* OPENSSL_HAS_ECC */
697#endif /* WITH_OPENSSL */
698 case KEY_ED25519:
699 case KEY_ED25519_CERT:
700 return a->ed25519_pk != NULL && b->ed25519_pk != NULL &&
701 memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000702#ifdef WITH_XMSS
703 case KEY_XMSS:
704 case KEY_XMSS_CERT:
705 return a->xmss_pk != NULL && b->xmss_pk != NULL &&
706 sshkey_xmss_pklen(a) == sshkey_xmss_pklen(b) &&
707 memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) == 0;
708#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000709 default:
710 return 0;
711 }
712 /* NOTREACHED */
713}
714
715int
716sshkey_equal(const struct sshkey *a, const struct sshkey *b)
717{
718 if (a == NULL || b == NULL || a->type != b->type)
719 return 0;
720 if (sshkey_is_cert(a)) {
721 if (!cert_compare(a->cert, b->cert))
722 return 0;
723 }
724 return sshkey_equal_public(a, b);
725}
726
727static int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000728to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain,
729 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +1000730{
731 int type, ret = SSH_ERR_INTERNAL_ERROR;
732 const char *typename;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000733#ifdef WITH_OPENSSL
734 const BIGNUM *rsa_n, *rsa_e, *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
735#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +1000736
737 if (key == NULL)
738 return SSH_ERR_INVALID_ARGUMENT;
739
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +0000740 if (sshkey_is_cert(key)) {
741 if (key->cert == NULL)
742 return SSH_ERR_EXPECTED_CERT;
743 if (sshbuf_len(key->cert->certblob) == 0)
744 return SSH_ERR_KEY_LACKS_CERTBLOB;
745 }
Damien Miller86687062014-07-02 15:28:02 +1000746 type = force_plain ? sshkey_type_plain(key->type) : key->type;
747 typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid);
748
749 switch (type) {
750#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000751 case KEY_DSA_CERT:
752 case KEY_ECDSA_CERT:
753 case KEY_RSA_CERT:
754#endif /* WITH_OPENSSL */
755 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000756#ifdef WITH_XMSS
757 case KEY_XMSS_CERT:
758#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000759 /* Use the existing blob */
760 /* XXX modified flag? */
761 if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0)
762 return ret;
763 break;
764#ifdef WITH_OPENSSL
765 case KEY_DSA:
766 if (key->dsa == NULL)
767 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000768 DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g);
769 DSA_get0_key(key->dsa, &dsa_pub_key, NULL);
Damien Miller86687062014-07-02 15:28:02 +1000770 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000771 (ret = sshbuf_put_bignum2(b, dsa_p)) != 0 ||
772 (ret = sshbuf_put_bignum2(b, dsa_q)) != 0 ||
773 (ret = sshbuf_put_bignum2(b, dsa_g)) != 0 ||
774 (ret = sshbuf_put_bignum2(b, dsa_pub_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000775 return ret;
776 break;
Darren Tuckerd1a04212014-07-19 07:23:55 +1000777# ifdef OPENSSL_HAS_ECC
Damien Miller86687062014-07-02 15:28:02 +1000778 case KEY_ECDSA:
779 if (key->ecdsa == NULL)
780 return SSH_ERR_INVALID_ARGUMENT;
781 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
782 (ret = sshbuf_put_cstring(b,
783 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
784 (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0)
785 return ret;
786 break;
Darren Tuckerd1a04212014-07-19 07:23:55 +1000787# endif
Damien Miller86687062014-07-02 15:28:02 +1000788 case KEY_RSA:
789 if (key->rsa == NULL)
790 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000791 RSA_get0_key(key->rsa, &rsa_n, &rsa_e, NULL);
Damien Miller86687062014-07-02 15:28:02 +1000792 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000793 (ret = sshbuf_put_bignum2(b, rsa_e)) != 0 ||
794 (ret = sshbuf_put_bignum2(b, rsa_n)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000795 return ret;
796 break;
797#endif /* WITH_OPENSSL */
798 case KEY_ED25519:
799 if (key->ed25519_pk == NULL)
800 return SSH_ERR_INVALID_ARGUMENT;
801 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
802 (ret = sshbuf_put_string(b,
803 key->ed25519_pk, ED25519_PK_SZ)) != 0)
804 return ret;
805 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000806#ifdef WITH_XMSS
807 case KEY_XMSS:
808 if (key->xmss_name == NULL || key->xmss_pk == NULL ||
809 sshkey_xmss_pklen(key) == 0)
810 return SSH_ERR_INVALID_ARGUMENT;
811 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
812 (ret = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
813 (ret = sshbuf_put_string(b,
814 key->xmss_pk, sshkey_xmss_pklen(key))) != 0 ||
815 (ret = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0)
816 return ret;
817 break;
818#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000819 default:
820 return SSH_ERR_KEY_TYPE_UNKNOWN;
821 }
822 return 0;
823}
824
825int
djm@openbsd.org60b18252015-01-26 02:59:11 +0000826sshkey_putb(const struct sshkey *key, struct sshbuf *b)
Damien Miller86687062014-07-02 15:28:02 +1000827{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000828 return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000829}
830
831int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000832sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b,
833 enum sshkey_serialize_rep opts)
djm@openbsd.org60b18252015-01-26 02:59:11 +0000834{
835 struct sshbuf *tmp;
836 int r;
837
838 if ((tmp = sshbuf_new()) == NULL)
839 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000840 r = to_blob_buf(key, tmp, 0, opts);
djm@openbsd.org60b18252015-01-26 02:59:11 +0000841 if (r == 0)
842 r = sshbuf_put_stringb(b, tmp);
843 sshbuf_free(tmp);
844 return r;
845}
846
847int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000848sshkey_puts(const struct sshkey *key, struct sshbuf *b)
849{
850 return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT);
851}
852
853int
djm@openbsd.org60b18252015-01-26 02:59:11 +0000854sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b)
Damien Miller86687062014-07-02 15:28:02 +1000855{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000856 return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000857}
858
859static int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000860to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain,
861 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +1000862{
863 int ret = SSH_ERR_INTERNAL_ERROR;
864 size_t len;
865 struct sshbuf *b = NULL;
866
867 if (lenp != NULL)
868 *lenp = 0;
869 if (blobp != NULL)
870 *blobp = NULL;
871 if ((b = sshbuf_new()) == NULL)
872 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000873 if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000874 goto out;
875 len = sshbuf_len(b);
876 if (lenp != NULL)
877 *lenp = len;
878 if (blobp != NULL) {
879 if ((*blobp = malloc(len)) == NULL) {
880 ret = SSH_ERR_ALLOC_FAIL;
881 goto out;
882 }
883 memcpy(*blobp, sshbuf_ptr(b), len);
884 }
885 ret = 0;
886 out:
887 sshbuf_free(b);
888 return ret;
889}
890
891int
892sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
893{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000894 return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000895}
896
897int
898sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
899{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000900 return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000901}
902
903int
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000904sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg,
Damien Miller86687062014-07-02 15:28:02 +1000905 u_char **retp, size_t *lenp)
906{
907 u_char *blob = NULL, *ret = NULL;
908 size_t blob_len = 0;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000909 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +1000910
911 if (retp != NULL)
912 *retp = NULL;
913 if (lenp != NULL)
914 *lenp = 0;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000915 if (ssh_digest_bytes(dgst_alg) == 0) {
Damien Miller86687062014-07-02 15:28:02 +1000916 r = SSH_ERR_INVALID_ARGUMENT;
917 goto out;
918 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000919 if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT))
920 != 0)
Damien Miller86687062014-07-02 15:28:02 +1000921 goto out;
922 if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) {
923 r = SSH_ERR_ALLOC_FAIL;
924 goto out;
925 }
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000926 if ((r = ssh_digest_memory(dgst_alg, blob, blob_len,
Damien Miller86687062014-07-02 15:28:02 +1000927 ret, SSH_DIGEST_MAX_LENGTH)) != 0)
928 goto out;
929 /* success */
930 if (retp != NULL) {
931 *retp = ret;
932 ret = NULL;
933 }
934 if (lenp != NULL)
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000935 *lenp = ssh_digest_bytes(dgst_alg);
Damien Miller86687062014-07-02 15:28:02 +1000936 r = 0;
937 out:
938 free(ret);
939 if (blob != NULL) {
940 explicit_bzero(blob, blob_len);
941 free(blob);
942 }
943 return r;
944}
945
946static char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000947fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
Damien Miller86687062014-07-02 15:28:02 +1000948{
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000949 char *ret;
950 size_t plen = strlen(alg) + 1;
951 size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1;
Damien Miller86687062014-07-02 15:28:02 +1000952
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000953 if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL)
Damien Miller86687062014-07-02 15:28:02 +1000954 return NULL;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000955 strlcpy(ret, alg, rlen);
956 strlcat(ret, ":", rlen);
957 if (dgst_raw_len == 0)
958 return ret;
dtucker@openbsd.org696fb422019-07-07 01:05:00 +0000959 if (b64_ntop(dgst_raw, dgst_raw_len, ret + plen, rlen - plen) == -1) {
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000960 freezero(ret, rlen);
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000961 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000962 }
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000963 /* Trim padding characters from end */
964 ret[strcspn(ret, "=")] = '\0';
965 return ret;
966}
Damien Miller86687062014-07-02 15:28:02 +1000967
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000968static char *
969fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
970{
971 char *retval, hex[5];
972 size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2;
973
974 if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL)
975 return NULL;
976 strlcpy(retval, alg, rlen);
977 strlcat(retval, ":", rlen);
978 for (i = 0; i < dgst_raw_len; i++) {
979 snprintf(hex, sizeof(hex), "%s%02x",
980 i > 0 ? ":" : "", dgst_raw[i]);
981 strlcat(retval, hex, rlen);
982 }
Damien Miller86687062014-07-02 15:28:02 +1000983 return retval;
984}
985
986static char *
987fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len)
988{
989 char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' };
990 char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
991 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' };
992 u_int i, j = 0, rounds, seed = 1;
993 char *retval;
994
995 rounds = (dgst_raw_len / 2) + 1;
996 if ((retval = calloc(rounds, 6)) == NULL)
997 return NULL;
998 retval[j++] = 'x';
999 for (i = 0; i < rounds; i++) {
1000 u_int idx0, idx1, idx2, idx3, idx4;
1001 if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) {
1002 idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) +
1003 seed) % 6;
1004 idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15;
1005 idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) +
1006 (seed / 6)) % 6;
1007 retval[j++] = vowels[idx0];
1008 retval[j++] = consonants[idx1];
1009 retval[j++] = vowels[idx2];
1010 if ((i + 1) < rounds) {
1011 idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15;
1012 idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15;
1013 retval[j++] = consonants[idx3];
1014 retval[j++] = '-';
1015 retval[j++] = consonants[idx4];
1016 seed = ((seed * 5) +
1017 ((((u_int)(dgst_raw[2 * i])) * 7) +
1018 ((u_int)(dgst_raw[(2 * i) + 1])))) % 36;
1019 }
1020 } else {
1021 idx0 = seed % 6;
1022 idx1 = 16;
1023 idx2 = seed / 6;
1024 retval[j++] = vowels[idx0];
1025 retval[j++] = consonants[idx1];
1026 retval[j++] = vowels[idx2];
1027 }
1028 }
1029 retval[j++] = 'x';
1030 retval[j++] = '\0';
1031 return retval;
1032}
1033
1034/*
1035 * Draw an ASCII-Art representing the fingerprint so human brain can
1036 * profit from its built-in pattern recognition ability.
1037 * This technique is called "random art" and can be found in some
1038 * scientific publications like this original paper:
1039 *
1040 * "Hash Visualization: a New Technique to improve Real-World Security",
1041 * Perrig A. and Song D., 1999, International Workshop on Cryptographic
1042 * Techniques and E-Commerce (CrypTEC '99)
1043 * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
1044 *
1045 * The subject came up in a talk by Dan Kaminsky, too.
1046 *
1047 * If you see the picture is different, the key is different.
1048 * If the picture looks the same, you still know nothing.
1049 *
1050 * The algorithm used here is a worm crawling over a discrete plane,
1051 * leaving a trace (augmenting the field) everywhere it goes.
1052 * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
1053 * makes the respective movement vector be ignored for this turn.
1054 * Graphs are not unambiguous, because circles in graphs can be
1055 * walked in either direction.
1056 */
1057
1058/*
1059 * Field sizes for the random art. Have to be odd, so the starting point
1060 * can be in the exact middle of the picture, and FLDBASE should be >=8 .
1061 * Else pictures would be too dense, and drawing the frame would
1062 * fail, too, because the key type would not fit in anymore.
1063 */
1064#define FLDBASE 8
1065#define FLDSIZE_Y (FLDBASE + 1)
1066#define FLDSIZE_X (FLDBASE * 2 + 1)
1067static char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001068fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len,
Damien Miller86687062014-07-02 15:28:02 +10001069 const struct sshkey *k)
1070{
1071 /*
1072 * Chars to be used after each other every time the worm
1073 * intersects with itself. Matter of taste.
1074 */
1075 char *augmentation_string = " .o+=*BOX@%&#/^SE";
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001076 char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X];
Damien Miller86687062014-07-02 15:28:02 +10001077 u_char field[FLDSIZE_X][FLDSIZE_Y];
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001078 size_t i, tlen, hlen;
Damien Miller86687062014-07-02 15:28:02 +10001079 u_int b;
Damien Miller61e28e52014-07-03 21:22:22 +10001080 int x, y, r;
Damien Miller86687062014-07-02 15:28:02 +10001081 size_t len = strlen(augmentation_string) - 1;
1082
1083 if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL)
1084 return NULL;
1085
1086 /* initialize field */
1087 memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
1088 x = FLDSIZE_X / 2;
1089 y = FLDSIZE_Y / 2;
1090
1091 /* process raw key */
1092 for (i = 0; i < dgst_raw_len; i++) {
1093 int input;
1094 /* each byte conveys four 2-bit move commands */
1095 input = dgst_raw[i];
1096 for (b = 0; b < 4; b++) {
1097 /* evaluate 2 bit, rest is shifted later */
1098 x += (input & 0x1) ? 1 : -1;
1099 y += (input & 0x2) ? 1 : -1;
1100
1101 /* assure we are still in bounds */
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001102 x = MAXIMUM(x, 0);
1103 y = MAXIMUM(y, 0);
1104 x = MINIMUM(x, FLDSIZE_X - 1);
1105 y = MINIMUM(y, FLDSIZE_Y - 1);
Damien Miller86687062014-07-02 15:28:02 +10001106
1107 /* augment the field */
1108 if (field[x][y] < len - 2)
1109 field[x][y]++;
1110 input = input >> 2;
1111 }
1112 }
1113
1114 /* mark starting point and end point*/
1115 field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
1116 field[x][y] = len;
1117
Damien Miller61e28e52014-07-03 21:22:22 +10001118 /* assemble title */
1119 r = snprintf(title, sizeof(title), "[%s %u]",
1120 sshkey_type(k), sshkey_size(k));
1121 /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */
1122 if (r < 0 || r > (int)sizeof(title))
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001123 r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k));
1124 tlen = (r <= 0) ? 0 : strlen(title);
1125
1126 /* assemble hash ID. */
1127 r = snprintf(hash, sizeof(hash), "[%s]", alg);
1128 hlen = (r <= 0) ? 0 : strlen(hash);
Damien Miller86687062014-07-02 15:28:02 +10001129
1130 /* output upper border */
Damien Miller61e28e52014-07-03 21:22:22 +10001131 p = retval;
1132 *p++ = '+';
1133 for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++)
1134 *p++ = '-';
1135 memcpy(p, title, tlen);
1136 p += tlen;
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001137 for (i += tlen; i < FLDSIZE_X; i++)
Damien Miller86687062014-07-02 15:28:02 +10001138 *p++ = '-';
1139 *p++ = '+';
1140 *p++ = '\n';
1141
1142 /* output content */
1143 for (y = 0; y < FLDSIZE_Y; y++) {
1144 *p++ = '|';
1145 for (x = 0; x < FLDSIZE_X; x++)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001146 *p++ = augmentation_string[MINIMUM(field[x][y], len)];
Damien Miller86687062014-07-02 15:28:02 +10001147 *p++ = '|';
1148 *p++ = '\n';
1149 }
1150
1151 /* output lower border */
1152 *p++ = '+';
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001153 for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++)
1154 *p++ = '-';
1155 memcpy(p, hash, hlen);
1156 p += hlen;
1157 for (i += hlen; i < FLDSIZE_X; i++)
Damien Miller86687062014-07-02 15:28:02 +10001158 *p++ = '-';
1159 *p++ = '+';
1160
1161 return retval;
1162}
1163
1164char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001165sshkey_fingerprint(const struct sshkey *k, int dgst_alg,
Damien Miller86687062014-07-02 15:28:02 +10001166 enum sshkey_fp_rep dgst_rep)
1167{
1168 char *retval = NULL;
1169 u_char *dgst_raw;
1170 size_t dgst_raw_len;
1171
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001172 if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001173 return NULL;
1174 switch (dgst_rep) {
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001175 case SSH_FP_DEFAULT:
1176 if (dgst_alg == SSH_DIGEST_MD5) {
1177 retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
1178 dgst_raw, dgst_raw_len);
1179 } else {
1180 retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
1181 dgst_raw, dgst_raw_len);
1182 }
1183 break;
Damien Miller86687062014-07-02 15:28:02 +10001184 case SSH_FP_HEX:
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001185 retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
1186 dgst_raw, dgst_raw_len);
1187 break;
1188 case SSH_FP_BASE64:
1189 retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
1190 dgst_raw, dgst_raw_len);
Damien Miller86687062014-07-02 15:28:02 +10001191 break;
1192 case SSH_FP_BUBBLEBABBLE:
1193 retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
1194 break;
1195 case SSH_FP_RANDOMART:
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001196 retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg),
1197 dgst_raw, dgst_raw_len, k);
Damien Miller86687062014-07-02 15:28:02 +10001198 break;
1199 default:
1200 explicit_bzero(dgst_raw, dgst_raw_len);
1201 free(dgst_raw);
1202 return NULL;
1203 }
1204 explicit_bzero(dgst_raw, dgst_raw_len);
1205 free(dgst_raw);
1206 return retval;
1207}
1208
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001209static int
1210peek_type_nid(const char *s, size_t l, int *nid)
1211{
1212 const struct keytype *kt;
Damien Miller86687062014-07-02 15:28:02 +10001213
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001214 for (kt = keytypes; kt->type != -1; kt++) {
1215 if (kt->name == NULL || strlen(kt->name) != l)
1216 continue;
1217 if (memcmp(s, kt->name, l) == 0) {
1218 *nid = -1;
1219 if (kt->type == KEY_ECDSA || kt->type == KEY_ECDSA_CERT)
1220 *nid = kt->nid;
1221 return kt->type;
1222 }
1223 }
1224 return KEY_UNSPEC;
1225}
1226
1227/* XXX this can now be made const char * */
Damien Miller86687062014-07-02 15:28:02 +10001228int
1229sshkey_read(struct sshkey *ret, char **cpp)
1230{
1231 struct sshkey *k;
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001232 char *cp, *blobcopy;
1233 size_t space;
Damien Miller86687062014-07-02 15:28:02 +10001234 int r, type, curve_nid = -1;
1235 struct sshbuf *blob;
Damien Miller86687062014-07-02 15:28:02 +10001236
dtucker@openbsd.org7fadbb62017-03-10 03:48:57 +00001237 if (ret == NULL)
1238 return SSH_ERR_INVALID_ARGUMENT;
1239
Damien Miller86687062014-07-02 15:28:02 +10001240 switch (ret->type) {
Damien Miller86687062014-07-02 15:28:02 +10001241 case KEY_UNSPEC:
1242 case KEY_RSA:
1243 case KEY_DSA:
1244 case KEY_ECDSA:
1245 case KEY_ED25519:
Damien Miller86687062014-07-02 15:28:02 +10001246 case KEY_DSA_CERT:
1247 case KEY_ECDSA_CERT:
1248 case KEY_RSA_CERT:
1249 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001250#ifdef WITH_XMSS
1251 case KEY_XMSS:
1252 case KEY_XMSS_CERT:
1253#endif /* WITH_XMSS */
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001254 break; /* ok */
Damien Miller86687062014-07-02 15:28:02 +10001255 default:
1256 return SSH_ERR_INVALID_ARGUMENT;
1257 }
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001258
1259 /* Decode type */
1260 cp = *cpp;
1261 space = strcspn(cp, " \t");
1262 if (space == strlen(cp))
1263 return SSH_ERR_INVALID_FORMAT;
1264 if ((type = peek_type_nid(cp, space, &curve_nid)) == KEY_UNSPEC)
1265 return SSH_ERR_INVALID_FORMAT;
1266
1267 /* skip whitespace */
1268 for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
1269 ;
1270 if (*cp == '\0')
1271 return SSH_ERR_INVALID_FORMAT;
1272 if (ret->type != KEY_UNSPEC && ret->type != type)
1273 return SSH_ERR_KEY_TYPE_MISMATCH;
1274 if ((blob = sshbuf_new()) == NULL)
1275 return SSH_ERR_ALLOC_FAIL;
1276
1277 /* find end of keyblob and decode */
1278 space = strcspn(cp, " \t");
1279 if ((blobcopy = strndup(cp, space)) == NULL) {
1280 sshbuf_free(blob);
1281 return SSH_ERR_ALLOC_FAIL;
1282 }
1283 if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) {
1284 free(blobcopy);
1285 sshbuf_free(blob);
1286 return r;
1287 }
1288 free(blobcopy);
1289 if ((r = sshkey_fromb(blob, &k)) != 0) {
1290 sshbuf_free(blob);
1291 return r;
1292 }
1293 sshbuf_free(blob);
1294
1295 /* skip whitespace and leave cp at start of comment */
1296 for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
1297 ;
1298
1299 /* ensure type of blob matches type at start of line */
1300 if (k->type != type) {
1301 sshkey_free(k);
1302 return SSH_ERR_KEY_TYPE_MISMATCH;
1303 }
1304 if (sshkey_type_plain(type) == KEY_ECDSA && curve_nid != k->ecdsa_nid) {
1305 sshkey_free(k);
1306 return SSH_ERR_EC_CURVE_MISMATCH;
1307 }
1308
1309 /* Fill in ret from parsed key */
1310 ret->type = type;
1311 if (sshkey_is_cert(ret)) {
1312 if (!sshkey_is_cert(k)) {
1313 sshkey_free(k);
1314 return SSH_ERR_EXPECTED_CERT;
1315 }
1316 if (ret->cert != NULL)
1317 cert_free(ret->cert);
1318 ret->cert = k->cert;
1319 k->cert = NULL;
1320 }
1321 switch (sshkey_type_plain(ret->type)) {
1322#ifdef WITH_OPENSSL
1323 case KEY_RSA:
1324 RSA_free(ret->rsa);
1325 ret->rsa = k->rsa;
1326 k->rsa = NULL;
1327#ifdef DEBUG_PK
1328 RSA_print_fp(stderr, ret->rsa, 8);
1329#endif
1330 break;
1331 case KEY_DSA:
1332 DSA_free(ret->dsa);
1333 ret->dsa = k->dsa;
1334 k->dsa = NULL;
1335#ifdef DEBUG_PK
1336 DSA_print_fp(stderr, ret->dsa, 8);
1337#endif
1338 break;
1339# ifdef OPENSSL_HAS_ECC
1340 case KEY_ECDSA:
1341 EC_KEY_free(ret->ecdsa);
1342 ret->ecdsa = k->ecdsa;
1343 ret->ecdsa_nid = k->ecdsa_nid;
1344 k->ecdsa = NULL;
1345 k->ecdsa_nid = -1;
1346#ifdef DEBUG_PK
1347 sshkey_dump_ec_key(ret->ecdsa);
1348#endif
1349 break;
1350# endif /* OPENSSL_HAS_ECC */
1351#endif /* WITH_OPENSSL */
1352 case KEY_ED25519:
1353 freezero(ret->ed25519_pk, ED25519_PK_SZ);
1354 ret->ed25519_pk = k->ed25519_pk;
1355 k->ed25519_pk = NULL;
1356#ifdef DEBUG_PK
1357 /* XXX */
1358#endif
1359 break;
1360#ifdef WITH_XMSS
1361 case KEY_XMSS:
1362 free(ret->xmss_pk);
1363 ret->xmss_pk = k->xmss_pk;
1364 k->xmss_pk = NULL;
1365 free(ret->xmss_state);
1366 ret->xmss_state = k->xmss_state;
1367 k->xmss_state = NULL;
1368 free(ret->xmss_name);
1369 ret->xmss_name = k->xmss_name;
1370 k->xmss_name = NULL;
1371 free(ret->xmss_filename);
1372 ret->xmss_filename = k->xmss_filename;
1373 k->xmss_filename = NULL;
1374#ifdef DEBUG_PK
1375 /* XXX */
1376#endif
1377 break;
1378#endif /* WITH_XMSS */
1379 default:
1380 sshkey_free(k);
1381 return SSH_ERR_INTERNAL_ERROR;
1382 }
1383 sshkey_free(k);
1384
1385 /* success */
1386 *cpp = cp;
1387 return 0;
Damien Miller86687062014-07-02 15:28:02 +10001388}
1389
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001390
Damien Miller86687062014-07-02 15:28:02 +10001391int
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001392sshkey_to_base64(const struct sshkey *key, char **b64p)
Damien Miller86687062014-07-02 15:28:02 +10001393{
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001394 int r = SSH_ERR_INTERNAL_ERROR;
1395 struct sshbuf *b = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001396 char *uu = NULL;
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001397
1398 if (b64p != NULL)
1399 *b64p = NULL;
1400 if ((b = sshbuf_new()) == NULL)
1401 return SSH_ERR_ALLOC_FAIL;
1402 if ((r = sshkey_putb(key, b)) != 0)
1403 goto out;
djm@openbsd.org16dd8b22019-07-16 13:18:39 +00001404 if ((uu = sshbuf_dtob64_string(b, 0)) == NULL) {
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001405 r = SSH_ERR_ALLOC_FAIL;
1406 goto out;
1407 }
1408 /* Success */
1409 if (b64p != NULL) {
1410 *b64p = uu;
1411 uu = NULL;
1412 }
1413 r = 0;
1414 out:
1415 sshbuf_free(b);
1416 free(uu);
1417 return r;
1418}
1419
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00001420int
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001421sshkey_format_text(const struct sshkey *key, struct sshbuf *b)
1422{
1423 int r = SSH_ERR_INTERNAL_ERROR;
1424 char *uu = NULL;
1425
djm@openbsd.org873d3e72017-04-30 23:18:44 +00001426 if ((r = sshkey_to_base64(key, &uu)) != 0)
1427 goto out;
1428 if ((r = sshbuf_putf(b, "%s %s",
1429 sshkey_ssh_name(key), uu)) != 0)
1430 goto out;
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001431 r = 0;
1432 out:
1433 free(uu);
1434 return r;
1435}
1436
1437int
1438sshkey_write(const struct sshkey *key, FILE *f)
1439{
1440 struct sshbuf *b = NULL;
1441 int r = SSH_ERR_INTERNAL_ERROR;
1442
1443 if ((b = sshbuf_new()) == NULL)
1444 return SSH_ERR_ALLOC_FAIL;
1445 if ((r = sshkey_format_text(key, b)) != 0)
1446 goto out;
1447 if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) {
1448 if (feof(f))
1449 errno = EPIPE;
1450 r = SSH_ERR_SYSTEM_ERROR;
1451 goto out;
1452 }
1453 /* Success */
1454 r = 0;
1455 out:
1456 sshbuf_free(b);
1457 return r;
Damien Miller86687062014-07-02 15:28:02 +10001458}
1459
1460const char *
1461sshkey_cert_type(const struct sshkey *k)
1462{
1463 switch (k->cert->type) {
1464 case SSH2_CERT_TYPE_USER:
1465 return "user";
1466 case SSH2_CERT_TYPE_HOST:
1467 return "host";
1468 default:
1469 return "unknown";
1470 }
1471}
1472
1473#ifdef WITH_OPENSSL
1474static int
1475rsa_generate_private_key(u_int bits, RSA **rsap)
1476{
1477 RSA *private = NULL;
1478 BIGNUM *f4 = NULL;
1479 int ret = SSH_ERR_INTERNAL_ERROR;
1480
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001481 if (rsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001482 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001483 if (bits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
1484 bits > SSHBUF_MAX_BIGNUM * 8)
1485 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001486 *rsap = NULL;
1487 if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) {
1488 ret = SSH_ERR_ALLOC_FAIL;
1489 goto out;
1490 }
1491 if (!BN_set_word(f4, RSA_F4) ||
1492 !RSA_generate_key_ex(private, bits, f4, NULL)) {
1493 ret = SSH_ERR_LIBCRYPTO_ERROR;
1494 goto out;
1495 }
1496 *rsap = private;
1497 private = NULL;
1498 ret = 0;
1499 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001500 RSA_free(private);
1501 BN_free(f4);
Damien Miller86687062014-07-02 15:28:02 +10001502 return ret;
1503}
1504
1505static int
1506dsa_generate_private_key(u_int bits, DSA **dsap)
1507{
1508 DSA *private;
1509 int ret = SSH_ERR_INTERNAL_ERROR;
1510
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001511 if (dsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001512 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001513 if (bits != 1024)
1514 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001515 if ((private = DSA_new()) == NULL) {
1516 ret = SSH_ERR_ALLOC_FAIL;
1517 goto out;
1518 }
1519 *dsap = NULL;
1520 if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
1521 NULL, NULL) || !DSA_generate_key(private)) {
Damien Miller86687062014-07-02 15:28:02 +10001522 ret = SSH_ERR_LIBCRYPTO_ERROR;
1523 goto out;
1524 }
1525 *dsap = private;
1526 private = NULL;
1527 ret = 0;
1528 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001529 DSA_free(private);
Damien Miller86687062014-07-02 15:28:02 +10001530 return ret;
1531}
1532
1533# ifdef OPENSSL_HAS_ECC
1534int
1535sshkey_ecdsa_key_to_nid(EC_KEY *k)
1536{
1537 EC_GROUP *eg;
1538 int nids[] = {
1539 NID_X9_62_prime256v1,
1540 NID_secp384r1,
1541# ifdef OPENSSL_HAS_NISTP521
1542 NID_secp521r1,
1543# endif /* OPENSSL_HAS_NISTP521 */
1544 -1
1545 };
1546 int nid;
1547 u_int i;
1548 BN_CTX *bnctx;
1549 const EC_GROUP *g = EC_KEY_get0_group(k);
1550
1551 /*
1552 * The group may be stored in a ASN.1 encoded private key in one of two
1553 * ways: as a "named group", which is reconstituted by ASN.1 object ID
1554 * or explicit group parameters encoded into the key blob. Only the
1555 * "named group" case sets the group NID for us, but we can figure
1556 * it out for the other case by comparing against all the groups that
1557 * are supported.
1558 */
1559 if ((nid = EC_GROUP_get_curve_name(g)) > 0)
1560 return nid;
1561 if ((bnctx = BN_CTX_new()) == NULL)
1562 return -1;
1563 for (i = 0; nids[i] != -1; i++) {
1564 if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) {
1565 BN_CTX_free(bnctx);
1566 return -1;
1567 }
1568 if (EC_GROUP_cmp(g, eg, bnctx) == 0)
1569 break;
1570 EC_GROUP_free(eg);
1571 }
1572 BN_CTX_free(bnctx);
1573 if (nids[i] != -1) {
1574 /* Use the group with the NID attached */
1575 EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
1576 if (EC_KEY_set_group(k, eg) != 1) {
1577 EC_GROUP_free(eg);
1578 return -1;
1579 }
1580 }
1581 return nids[i];
1582}
1583
1584static int
1585ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap)
1586{
1587 EC_KEY *private;
1588 int ret = SSH_ERR_INTERNAL_ERROR;
1589
djm@openbsd.org5f02bb12017-05-08 06:11:06 +00001590 if (nid == NULL || ecdsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001591 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org5f02bb12017-05-08 06:11:06 +00001592 if ((*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
1593 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001594 *ecdsap = NULL;
1595 if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) {
1596 ret = SSH_ERR_ALLOC_FAIL;
1597 goto out;
1598 }
1599 if (EC_KEY_generate_key(private) != 1) {
1600 ret = SSH_ERR_LIBCRYPTO_ERROR;
1601 goto out;
1602 }
1603 EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
1604 *ecdsap = private;
1605 private = NULL;
1606 ret = 0;
1607 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001608 EC_KEY_free(private);
Damien Miller86687062014-07-02 15:28:02 +10001609 return ret;
1610}
1611# endif /* OPENSSL_HAS_ECC */
1612#endif /* WITH_OPENSSL */
1613
1614int
1615sshkey_generate(int type, u_int bits, struct sshkey **keyp)
1616{
1617 struct sshkey *k;
1618 int ret = SSH_ERR_INTERNAL_ERROR;
1619
1620 if (keyp == NULL)
1621 return SSH_ERR_INVALID_ARGUMENT;
1622 *keyp = NULL;
1623 if ((k = sshkey_new(KEY_UNSPEC)) == NULL)
1624 return SSH_ERR_ALLOC_FAIL;
1625 switch (type) {
1626 case KEY_ED25519:
1627 if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL ||
1628 (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) {
1629 ret = SSH_ERR_ALLOC_FAIL;
1630 break;
1631 }
1632 crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
1633 ret = 0;
1634 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001635#ifdef WITH_XMSS
1636 case KEY_XMSS:
1637 ret = sshkey_xmss_generate_private_key(k, bits);
1638 break;
1639#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10001640#ifdef WITH_OPENSSL
1641 case KEY_DSA:
1642 ret = dsa_generate_private_key(bits, &k->dsa);
1643 break;
1644# ifdef OPENSSL_HAS_ECC
1645 case KEY_ECDSA:
1646 ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid,
1647 &k->ecdsa);
1648 break;
1649# endif /* OPENSSL_HAS_ECC */
1650 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10001651 ret = rsa_generate_private_key(bits, &k->rsa);
1652 break;
1653#endif /* WITH_OPENSSL */
1654 default:
1655 ret = SSH_ERR_INVALID_ARGUMENT;
1656 }
1657 if (ret == 0) {
1658 k->type = type;
1659 *keyp = k;
1660 } else
1661 sshkey_free(k);
1662 return ret;
1663}
1664
1665int
1666sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key)
1667{
1668 u_int i;
1669 const struct sshkey_cert *from;
1670 struct sshkey_cert *to;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001671 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +10001672
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001673 if (to_key == NULL || (from = from_key->cert) == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001674 return SSH_ERR_INVALID_ARGUMENT;
1675
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001676 if ((to = cert_new()) == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001677 return SSH_ERR_ALLOC_FAIL;
1678
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001679 if ((r = sshbuf_putb(to->certblob, from->certblob)) != 0 ||
1680 (r = sshbuf_putb(to->critical, from->critical)) != 0 ||
1681 (r = sshbuf_putb(to->extensions, from->extensions)) != 0)
1682 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001683
1684 to->serial = from->serial;
1685 to->type = from->type;
1686 if (from->key_id == NULL)
1687 to->key_id = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001688 else if ((to->key_id = strdup(from->key_id)) == NULL) {
1689 r = SSH_ERR_ALLOC_FAIL;
1690 goto out;
1691 }
Damien Miller86687062014-07-02 15:28:02 +10001692 to->valid_after = from->valid_after;
1693 to->valid_before = from->valid_before;
1694 if (from->signature_key == NULL)
1695 to->signature_key = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001696 else if ((r = sshkey_from_private(from->signature_key,
Damien Miller86687062014-07-02 15:28:02 +10001697 &to->signature_key)) != 0)
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001698 goto out;
1699 if (from->signature_type != NULL &&
1700 (to->signature_type = strdup(from->signature_type)) == NULL) {
1701 r = SSH_ERR_ALLOC_FAIL;
1702 goto out;
1703 }
1704 if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) {
1705 r = SSH_ERR_INVALID_ARGUMENT;
1706 goto out;
1707 }
Damien Miller86687062014-07-02 15:28:02 +10001708 if (from->nprincipals > 0) {
1709 if ((to->principals = calloc(from->nprincipals,
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001710 sizeof(*to->principals))) == NULL) {
1711 r = SSH_ERR_ALLOC_FAIL;
1712 goto out;
1713 }
Damien Miller86687062014-07-02 15:28:02 +10001714 for (i = 0; i < from->nprincipals; i++) {
1715 to->principals[i] = strdup(from->principals[i]);
1716 if (to->principals[i] == NULL) {
1717 to->nprincipals = i;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001718 r = SSH_ERR_ALLOC_FAIL;
1719 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001720 }
1721 }
1722 }
1723 to->nprincipals = from->nprincipals;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001724
1725 /* success */
1726 cert_free(to_key->cert);
1727 to_key->cert = to;
1728 to = NULL;
1729 r = 0;
1730 out:
1731 cert_free(to);
1732 return r;
Damien Miller86687062014-07-02 15:28:02 +10001733}
1734
1735int
1736sshkey_from_private(const struct sshkey *k, struct sshkey **pkp)
1737{
1738 struct sshkey *n = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001739 int r = SSH_ERR_INTERNAL_ERROR;
1740#ifdef WITH_OPENSSL
1741 const BIGNUM *rsa_n, *rsa_e;
1742 BIGNUM *rsa_n_dup = NULL, *rsa_e_dup = NULL;
1743 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
1744 BIGNUM *dsa_p_dup = NULL, *dsa_q_dup = NULL, *dsa_g_dup = NULL;
1745 BIGNUM *dsa_pub_key_dup = NULL;
1746#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10001747
djm@openbsd.org1a2663a2015-10-15 23:08:23 +00001748 *pkp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001749 switch (k->type) {
1750#ifdef WITH_OPENSSL
1751 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +10001752 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001753 if ((n = sshkey_new(k->type)) == NULL) {
1754 r = SSH_ERR_ALLOC_FAIL;
1755 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001756 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001757
1758 DSA_get0_pqg(k->dsa, &dsa_p, &dsa_q, &dsa_g);
1759 DSA_get0_key(k->dsa, &dsa_pub_key, NULL);
1760 if ((dsa_p_dup = BN_dup(dsa_p)) == NULL ||
1761 (dsa_q_dup = BN_dup(dsa_q)) == NULL ||
1762 (dsa_g_dup = BN_dup(dsa_g)) == NULL ||
1763 (dsa_pub_key_dup = BN_dup(dsa_pub_key)) == NULL) {
1764 r = SSH_ERR_ALLOC_FAIL;
1765 goto out;
1766 }
1767 if (!DSA_set0_pqg(n->dsa, dsa_p_dup, dsa_q_dup, dsa_g_dup)) {
1768 r = SSH_ERR_LIBCRYPTO_ERROR;
1769 goto out;
1770 }
1771 dsa_p_dup = dsa_q_dup = dsa_g_dup = NULL; /* transferred */
1772 if (!DSA_set0_key(n->dsa, dsa_pub_key_dup, NULL)) {
1773 r = SSH_ERR_LIBCRYPTO_ERROR;
1774 goto out;
1775 }
1776 dsa_pub_key_dup = NULL; /* transferred */
1777
Damien Miller86687062014-07-02 15:28:02 +10001778 break;
1779# ifdef OPENSSL_HAS_ECC
1780 case KEY_ECDSA:
1781 case KEY_ECDSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001782 if ((n = sshkey_new(k->type)) == NULL) {
1783 r = SSH_ERR_ALLOC_FAIL;
1784 goto out;
1785 }
Damien Miller86687062014-07-02 15:28:02 +10001786 n->ecdsa_nid = k->ecdsa_nid;
1787 n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
1788 if (n->ecdsa == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001789 r = SSH_ERR_ALLOC_FAIL;
1790 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001791 }
1792 if (EC_KEY_set_public_key(n->ecdsa,
1793 EC_KEY_get0_public_key(k->ecdsa)) != 1) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001794 r = SSH_ERR_LIBCRYPTO_ERROR;
1795 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001796 }
1797 break;
1798# endif /* OPENSSL_HAS_ECC */
1799 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10001800 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001801 if ((n = sshkey_new(k->type)) == NULL) {
1802 r = SSH_ERR_ALLOC_FAIL;
1803 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001804 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001805 RSA_get0_key(k->rsa, &rsa_n, &rsa_e, NULL);
1806 if ((rsa_n_dup = BN_dup(rsa_n)) == NULL ||
1807 (rsa_e_dup = BN_dup(rsa_e)) == NULL) {
1808 r = SSH_ERR_ALLOC_FAIL;
1809 goto out;
1810 }
1811 if (!RSA_set0_key(n->rsa, rsa_n_dup, rsa_e_dup, NULL)) {
1812 r = SSH_ERR_LIBCRYPTO_ERROR;
1813 goto out;
1814 }
1815 rsa_n_dup = rsa_e_dup = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10001816 break;
1817#endif /* WITH_OPENSSL */
1818 case KEY_ED25519:
1819 case KEY_ED25519_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001820 if ((n = sshkey_new(k->type)) == NULL) {
1821 r = SSH_ERR_ALLOC_FAIL;
1822 goto out;
1823 }
Damien Miller86687062014-07-02 15:28:02 +10001824 if (k->ed25519_pk != NULL) {
1825 if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001826 r = SSH_ERR_ALLOC_FAIL;
1827 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001828 }
1829 memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
1830 }
1831 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001832#ifdef WITH_XMSS
1833 case KEY_XMSS:
1834 case KEY_XMSS_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001835 if ((n = sshkey_new(k->type)) == NULL) {
1836 r = SSH_ERR_ALLOC_FAIL;
1837 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001838 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001839 if ((r = sshkey_xmss_init(n, k->xmss_name)) != 0)
1840 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001841 if (k->xmss_pk != NULL) {
1842 size_t pklen = sshkey_xmss_pklen(k);
1843 if (pklen == 0 || sshkey_xmss_pklen(n) != pklen) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001844 r = SSH_ERR_INTERNAL_ERROR;
1845 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001846 }
1847 if ((n->xmss_pk = malloc(pklen)) == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001848 r = SSH_ERR_ALLOC_FAIL;
1849 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001850 }
1851 memcpy(n->xmss_pk, k->xmss_pk, pklen);
1852 }
1853 break;
1854#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10001855 default:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001856 r = SSH_ERR_KEY_TYPE_UNKNOWN;
1857 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001858 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001859 if (sshkey_is_cert(k) && (r = sshkey_cert_copy(k, n)) != 0)
1860 goto out;
1861 /* success */
Damien Miller86687062014-07-02 15:28:02 +10001862 *pkp = n;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001863 n = NULL;
1864 r = 0;
1865 out:
1866 sshkey_free(n);
Darren Tuckercce8cbe2018-09-15 19:44:06 +10001867#ifdef WITH_OPENSSL
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001868 BN_clear_free(rsa_n_dup);
1869 BN_clear_free(rsa_e_dup);
1870 BN_clear_free(dsa_p_dup);
1871 BN_clear_free(dsa_q_dup);
1872 BN_clear_free(dsa_g_dup);
1873 BN_clear_free(dsa_pub_key_dup);
Darren Tuckercce8cbe2018-09-15 19:44:06 +10001874#endif
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001875
1876 return r;
Damien Miller86687062014-07-02 15:28:02 +10001877}
1878
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00001879int
1880sshkey_is_shielded(struct sshkey *k)
1881{
1882 return k != NULL && k->shielded_private != NULL;
1883}
1884
1885int
1886sshkey_shield_private(struct sshkey *k)
1887{
1888 struct sshbuf *prvbuf = NULL;
1889 u_char *prekey = NULL, *enc = NULL, keyiv[SSH_DIGEST_MAX_LENGTH];
1890 struct sshcipher_ctx *cctx = NULL;
1891 const struct sshcipher *cipher;
1892 size_t i, enclen = 0;
1893 struct sshkey *kswap = NULL, tmp;
1894 int r = SSH_ERR_INTERNAL_ERROR;
1895
1896#ifdef DEBUG_PK
1897 fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k));
1898#endif
1899 if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) {
1900 r = SSH_ERR_INVALID_ARGUMENT;
1901 goto out;
1902 }
1903 if (cipher_keylen(cipher) + cipher_ivlen(cipher) >
1904 ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) {
1905 r = SSH_ERR_INTERNAL_ERROR;
1906 goto out;
1907 }
1908
1909 /* Prepare a random pre-key, and from it an ephemeral key */
1910 if ((prekey = malloc(SSHKEY_SHIELD_PREKEY_LEN)) == NULL) {
1911 r = SSH_ERR_ALLOC_FAIL;
1912 goto out;
1913 }
1914 arc4random_buf(prekey, SSHKEY_SHIELD_PREKEY_LEN);
1915 if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH,
1916 prekey, SSHKEY_SHIELD_PREKEY_LEN,
1917 keyiv, SSH_DIGEST_MAX_LENGTH)) != 0)
1918 goto out;
1919#ifdef DEBUG_PK
1920 fprintf(stderr, "%s: key+iv\n", __func__);
1921 sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH),
1922 stderr);
1923#endif
1924 if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher),
1925 keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 1)) != 0)
1926 goto out;
1927
1928 /* Serialise and encrypt the private key using the ephemeral key */
1929 if ((prvbuf = sshbuf_new()) == NULL) {
1930 r = SSH_ERR_ALLOC_FAIL;
1931 goto out;
1932 }
1933 if (sshkey_is_shielded(k) && (r = sshkey_unshield_private(k)) != 0)
1934 goto out;
1935 if ((r = sshkey_private_serialize_opt(k, prvbuf,
1936 SSHKEY_SERIALIZE_FULL)) != 0)
1937 goto out;
1938 /* pad to cipher blocksize */
1939 i = 0;
1940 while (sshbuf_len(prvbuf) % cipher_blocksize(cipher)) {
1941 if ((r = sshbuf_put_u8(prvbuf, ++i & 0xff)) != 0)
1942 goto out;
1943 }
1944#ifdef DEBUG_PK
1945 fprintf(stderr, "%s: serialised\n", __func__);
1946 sshbuf_dump(prvbuf, stderr);
1947#endif
1948 /* encrypt */
1949 enclen = sshbuf_len(prvbuf);
1950 if ((enc = malloc(enclen)) == NULL) {
1951 r = SSH_ERR_ALLOC_FAIL;
1952 goto out;
1953 }
1954 if ((r = cipher_crypt(cctx, 0, enc,
1955 sshbuf_ptr(prvbuf), sshbuf_len(prvbuf), 0, 0)) != 0)
1956 goto out;
1957#ifdef DEBUG_PK
1958 fprintf(stderr, "%s: encrypted\n", __func__);
1959 sshbuf_dump_data(enc, enclen, stderr);
1960#endif
1961
1962 /* Make a scrubbed, public-only copy of our private key argument */
1963 if ((r = sshkey_from_private(k, &kswap)) != 0)
1964 goto out;
1965
1966 /* Swap the private key out (it will be destroyed below) */
1967 tmp = *kswap;
1968 *kswap = *k;
1969 *k = tmp;
1970
1971 /* Insert the shielded key into our argument */
1972 k->shielded_private = enc;
1973 k->shielded_len = enclen;
1974 k->shield_prekey = prekey;
1975 k->shield_prekey_len = SSHKEY_SHIELD_PREKEY_LEN;
1976 enc = prekey = NULL; /* transferred */
1977 enclen = 0;
1978
1979 /* success */
1980 r = 0;
1981
1982 out:
1983 /* XXX behaviour on error - invalidate original private key? */
1984 cipher_free(cctx);
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00001985 explicit_bzero(keyiv, sizeof(keyiv));
1986 explicit_bzero(&tmp, sizeof(tmp));
djm@openbsd.orgb2e3e572019-06-27 06:29:35 +00001987 freezero(enc, enclen);
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +00001988 freezero(prekey, SSHKEY_SHIELD_PREKEY_LEN);
1989 sshkey_free(kswap);
1990 sshbuf_free(prvbuf);
1991 return r;
1992}
1993
1994int
1995sshkey_unshield_private(struct sshkey *k)
1996{
1997 struct sshbuf *prvbuf = NULL;
1998 u_char pad, *cp, keyiv[SSH_DIGEST_MAX_LENGTH];
1999 struct sshcipher_ctx *cctx = NULL;
2000 const struct sshcipher *cipher;
2001 size_t i;
2002 struct sshkey *kswap = NULL, tmp;
2003 int r = SSH_ERR_INTERNAL_ERROR;
2004
2005#ifdef DEBUG_PK
2006 fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k));
2007#endif
2008 if (!sshkey_is_shielded(k))
2009 return 0; /* nothing to do */
2010
2011 if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) {
2012 r = SSH_ERR_INVALID_ARGUMENT;
2013 goto out;
2014 }
2015 if (cipher_keylen(cipher) + cipher_ivlen(cipher) >
2016 ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) {
2017 r = SSH_ERR_INTERNAL_ERROR;
2018 goto out;
2019 }
2020 /* check size of shielded key blob */
2021 if (k->shielded_len < cipher_blocksize(cipher) ||
2022 (k->shielded_len % cipher_blocksize(cipher)) != 0) {
2023 r = SSH_ERR_INVALID_FORMAT;
2024 goto out;
2025 }
2026
2027 /* Calculate the ephemeral key from the prekey */
2028 if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH,
2029 k->shield_prekey, k->shield_prekey_len,
2030 keyiv, SSH_DIGEST_MAX_LENGTH)) != 0)
2031 goto out;
2032 if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher),
2033 keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 0)) != 0)
2034 goto out;
2035#ifdef DEBUG_PK
2036 fprintf(stderr, "%s: key+iv\n", __func__);
2037 sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH),
2038 stderr);
2039#endif
2040
2041 /* Decrypt and parse the shielded private key using the ephemeral key */
2042 if ((prvbuf = sshbuf_new()) == NULL) {
2043 r = SSH_ERR_ALLOC_FAIL;
2044 goto out;
2045 }
2046 if ((r = sshbuf_reserve(prvbuf, k->shielded_len, &cp)) != 0)
2047 goto out;
2048 /* decrypt */
2049#ifdef DEBUG_PK
2050 fprintf(stderr, "%s: encrypted\n", __func__);
2051 sshbuf_dump_data(k->shielded_private, k->shielded_len, stderr);
2052#endif
2053 if ((r = cipher_crypt(cctx, 0, cp,
2054 k->shielded_private, k->shielded_len, 0, 0)) != 0)
2055 goto out;
2056#ifdef DEBUG_PK
2057 fprintf(stderr, "%s: serialised\n", __func__);
2058 sshbuf_dump(prvbuf, stderr);
2059#endif
2060 /* Parse private key */
2061 if ((r = sshkey_private_deserialize(prvbuf, &kswap)) != 0)
2062 goto out;
2063 /* Check deterministic padding */
2064 i = 0;
2065 while (sshbuf_len(prvbuf)) {
2066 if ((r = sshbuf_get_u8(prvbuf, &pad)) != 0)
2067 goto out;
2068 if (pad != (++i & 0xff)) {
2069 r = SSH_ERR_INVALID_FORMAT;
2070 goto out;
2071 }
2072 }
2073
2074 /* Swap the parsed key back into place */
2075 tmp = *kswap;
2076 *kswap = *k;
2077 *k = tmp;
2078
2079 /* success */
2080 r = 0;
2081
2082 out:
2083 cipher_free(cctx);
2084 explicit_bzero(keyiv, sizeof(keyiv));
2085 explicit_bzero(&tmp, sizeof(tmp));
2086 sshkey_free(kswap);
2087 sshbuf_free(prvbuf);
2088 return r;
2089}
2090
Damien Miller86687062014-07-02 15:28:02 +10002091static int
djm@openbsd.org60b18252015-01-26 02:59:11 +00002092cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
Damien Miller86687062014-07-02 15:28:02 +10002093{
djm@openbsd.org60b18252015-01-26 02:59:11 +00002094 struct sshbuf *principals = NULL, *crit = NULL;
2095 struct sshbuf *exts = NULL, *ca = NULL;
2096 u_char *sig = NULL;
2097 size_t signed_len = 0, slen = 0, kidlen = 0;
Damien Miller86687062014-07-02 15:28:02 +10002098 int ret = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +10002099
2100 /* Copy the entire key blob for verification and later serialisation */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002101 if ((ret = sshbuf_putb(key->cert->certblob, certbuf)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002102 return ret;
2103
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002104 /* Parse body of certificate up to signature */
2105 if ((ret = sshbuf_get_u64(b, &key->cert->serial)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002106 (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 ||
2107 (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 ||
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002108 (ret = sshbuf_froms(b, &principals)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002109 (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 ||
2110 (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 ||
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002111 (ret = sshbuf_froms(b, &crit)) != 0 ||
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002112 (ret = sshbuf_froms(b, &exts)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002113 (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 ||
djm@openbsd.org60b18252015-01-26 02:59:11 +00002114 (ret = sshbuf_froms(b, &ca)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002115 /* XXX debug print error for ret */
2116 ret = SSH_ERR_INVALID_FORMAT;
2117 goto out;
2118 }
2119
2120 /* Signature is left in the buffer so we can calculate this length */
2121 signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b);
2122
2123 if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) {
2124 ret = SSH_ERR_INVALID_FORMAT;
2125 goto out;
2126 }
2127
2128 if (key->cert->type != SSH2_CERT_TYPE_USER &&
2129 key->cert->type != SSH2_CERT_TYPE_HOST) {
2130 ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE;
2131 goto out;
2132 }
2133
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002134 /* Parse principals section */
2135 while (sshbuf_len(principals) > 0) {
2136 char *principal = NULL;
2137 char **oprincipals = NULL;
2138
Damien Miller86687062014-07-02 15:28:02 +10002139 if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) {
2140 ret = SSH_ERR_INVALID_FORMAT;
2141 goto out;
2142 }
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002143 if ((ret = sshbuf_get_cstring(principals, &principal,
2144 NULL)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002145 ret = SSH_ERR_INVALID_FORMAT;
2146 goto out;
2147 }
2148 oprincipals = key->cert->principals;
deraadt@openbsd.org9e509d42017-05-31 09:15:42 +00002149 key->cert->principals = recallocarray(key->cert->principals,
2150 key->cert->nprincipals, key->cert->nprincipals + 1,
2151 sizeof(*key->cert->principals));
Damien Miller86687062014-07-02 15:28:02 +10002152 if (key->cert->principals == NULL) {
2153 free(principal);
2154 key->cert->principals = oprincipals;
2155 ret = SSH_ERR_ALLOC_FAIL;
2156 goto out;
2157 }
2158 key->cert->principals[key->cert->nprincipals++] = principal;
2159 }
2160
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002161 /*
2162 * Stash a copies of the critical options and extensions sections
2163 * for later use.
2164 */
2165 if ((ret = sshbuf_putb(key->cert->critical, crit)) != 0 ||
2166 (exts != NULL &&
2167 (ret = sshbuf_putb(key->cert->extensions, exts)) != 0))
Damien Miller86687062014-07-02 15:28:02 +10002168 goto out;
2169
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002170 /*
2171 * Validate critical options and extensions sections format.
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002172 */
2173 while (sshbuf_len(crit) != 0) {
2174 if ((ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0 ||
2175 (ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0) {
2176 sshbuf_reset(key->cert->critical);
Damien Miller86687062014-07-02 15:28:02 +10002177 ret = SSH_ERR_INVALID_FORMAT;
2178 goto out;
2179 }
2180 }
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002181 while (exts != NULL && sshbuf_len(exts) != 0) {
2182 if ((ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0 ||
2183 (ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0) {
2184 sshbuf_reset(key->cert->extensions);
Damien Miller86687062014-07-02 15:28:02 +10002185 ret = SSH_ERR_INVALID_FORMAT;
2186 goto out;
2187 }
2188 }
Damien Miller86687062014-07-02 15:28:02 +10002189
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002190 /* Parse CA key and check signature */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002191 if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002192 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2193 goto out;
2194 }
2195 if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) {
2196 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2197 goto out;
2198 }
Damien Miller86687062014-07-02 15:28:02 +10002199 if ((ret = sshkey_verify(key->cert->signature_key, sig, slen,
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002200 sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002201 goto out;
djm@openbsd.orgf8df0412019-09-03 08:31:20 +00002202 if ((ret = sshkey_get_sigtype(sig, slen,
2203 &key->cert->signature_type)) != 0)
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002204 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.orgf8df0412019-09-03 08:31:20 +00002505int
2506sshkey_get_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;
djm@openbsd.orgf8df0412019-09-03 08:31:20 +00002588 if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0)
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002589 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 */
djm@openbsd.orgf8df0412019-09-03 08:31:20 +00002864 if ((ret = sshkey_get_sigtype(sig_blob, sig_len, &sigtype)) != 0)
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002865 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 */