blob: 72c08c7e0227423fb65d83e6c37aba2973ceec30 [file] [log] [blame]
djm@openbsd.orgb4d4eda2018-07-03 13:20:25 +00001/* $OpenBSD: sshkey.c,v 1.66 2018/07/03 13:20:25 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"
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000058#include "sshkey-xmss.h"
djm@openbsd.org1f729f02015-01-13 07:39:19 +000059#include "match.h"
Damien Miller86687062014-07-02 15:28:02 +100060
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000061#include "xmss_fast.h"
62
Damien Miller86687062014-07-02 15:28:02 +100063/* openssh private key file format */
64#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n"
65#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n"
66#define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1)
67#define MARK_END_LEN (sizeof(MARK_END) - 1)
68#define KDFNAME "bcrypt"
69#define AUTH_MAGIC "openssh-key-v1"
70#define SALT_LEN 16
djm@openbsd.org0f345532017-08-12 06:42:52 +000071#define DEFAULT_CIPHERNAME "aes256-ctr"
Damien Miller86687062014-07-02 15:28:02 +100072#define DEFAULT_ROUNDS 16
73
74/* Version identification string for SSH v1 identity files. */
75#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n"
76
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000077int sshkey_private_serialize_opt(const struct sshkey *key,
78 struct sshbuf *buf, enum sshkey_serialize_rep);
djm@openbsd.org60b18252015-01-26 02:59:11 +000079static int sshkey_from_blob_internal(struct sshbuf *buf,
Damien Miller86687062014-07-02 15:28:02 +100080 struct sshkey **keyp, int allow_cert);
81
82/* Supported key types */
83struct keytype {
84 const char *name;
85 const char *shortname;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +000086 const char *sigalg;
Damien Miller86687062014-07-02 15:28:02 +100087 int type;
88 int nid;
89 int cert;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +000090 int sigonly;
Damien Miller86687062014-07-02 15:28:02 +100091};
92static const struct keytype keytypes[] = {
djm@openbsd.org4ba0d542018-07-03 11:39:54 +000093 { "ssh-ed25519", "ED25519", NULL, KEY_ED25519, 0, 0, 0 },
94 { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +000095 KEY_ED25519_CERT, 0, 1, 0 },
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000096#ifdef WITH_XMSS
djm@openbsd.org4ba0d542018-07-03 11:39:54 +000097 { "ssh-xmss@openssh.com", "XMSS", NULL, KEY_XMSS, 0, 0, 0 },
98 { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", NULL,
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000099 KEY_XMSS_CERT, 0, 1, 0 },
100#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000101#ifdef WITH_OPENSSL
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000102 { "ssh-rsa", "RSA", NULL, KEY_RSA, 0, 0, 0 },
103 { "rsa-sha2-256", "RSA", NULL, KEY_RSA, 0, 0, 1 },
104 { "rsa-sha2-512", "RSA", NULL, KEY_RSA, 0, 0, 1 },
105 { "ssh-dss", "DSA", NULL, KEY_DSA, 0, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000106# ifdef OPENSSL_HAS_ECC
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000107 { "ecdsa-sha2-nistp256", "ECDSA", NULL,
108 KEY_ECDSA, NID_X9_62_prime256v1, 0, 0 },
109 { "ecdsa-sha2-nistp384", "ECDSA", NULL,
110 KEY_ECDSA, NID_secp384r1, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000111# ifdef OPENSSL_HAS_NISTP521
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000112 { "ecdsa-sha2-nistp521", "ECDSA", NULL,
113 KEY_ECDSA, NID_secp521r1, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000114# endif /* OPENSSL_HAS_NISTP521 */
115# endif /* OPENSSL_HAS_ECC */
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000116 { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL,
117 KEY_RSA_CERT, 0, 1, 0 },
118 { "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT",
119 "ssh-rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 },
120 { "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT",
121 "ssh-rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 },
122 { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL,
123 KEY_DSA_CERT, 0, 1, 0 },
124 { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL,
125 KEY_RSA_CERT, 0, 1, 0 },
126 { "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT",
127 "ssh-rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 },
128 { "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT",
129 "ssh-rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 },
130 { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL,
131 KEY_DSA_CERT, 0, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000132# ifdef OPENSSL_HAS_ECC
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000133 { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000134 KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1, 0 },
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000135 { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000136 KEY_ECDSA_CERT, NID_secp384r1, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000137# ifdef OPENSSL_HAS_NISTP521
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000138 { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", NULL,
139 KEY_ECDSA_CERT, NID_secp521r1, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000140# endif /* OPENSSL_HAS_NISTP521 */
141# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +1000142#endif /* WITH_OPENSSL */
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000143 { NULL, NULL, NULL, -1, -1, 0, 0 }
Damien Miller86687062014-07-02 15:28:02 +1000144};
145
146const char *
147sshkey_type(const struct sshkey *k)
148{
149 const struct keytype *kt;
150
151 for (kt = keytypes; kt->type != -1; kt++) {
152 if (kt->type == k->type)
153 return kt->shortname;
154 }
155 return "unknown";
156}
157
158static const char *
159sshkey_ssh_name_from_type_nid(int type, int nid)
160{
161 const struct keytype *kt;
162
163 for (kt = keytypes; kt->type != -1; kt++) {
164 if (kt->type == type && (kt->nid == 0 || kt->nid == nid))
165 return kt->name;
166 }
167 return "ssh-unknown";
168}
169
170int
171sshkey_type_is_cert(int type)
172{
173 const struct keytype *kt;
174
175 for (kt = keytypes; kt->type != -1; kt++) {
176 if (kt->type == type)
177 return kt->cert;
178 }
179 return 0;
180}
181
182const char *
183sshkey_ssh_name(const struct sshkey *k)
184{
185 return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid);
186}
187
188const char *
189sshkey_ssh_name_plain(const struct sshkey *k)
190{
191 return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type),
192 k->ecdsa_nid);
193}
194
195int
196sshkey_type_from_name(const char *name)
197{
198 const struct keytype *kt;
199
200 for (kt = keytypes; kt->type != -1; kt++) {
201 /* Only allow shortname matches for plain key types */
202 if ((kt->name != NULL && strcmp(name, kt->name) == 0) ||
203 (!kt->cert && strcasecmp(kt->shortname, name) == 0))
204 return kt->type;
205 }
206 return KEY_UNSPEC;
207}
208
209int
210sshkey_ecdsa_nid_from_name(const char *name)
211{
212 const struct keytype *kt;
213
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +0000214 for (kt = keytypes; kt->type != -1; kt++) {
215 if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT)
216 continue;
217 if (kt->name != NULL && strcmp(name, kt->name) == 0)
218 return kt->nid;
219 }
Damien Miller86687062014-07-02 15:28:02 +1000220 return -1;
221}
222
223char *
djm@openbsd.org183ba552017-03-10 04:07:20 +0000224sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
Damien Miller86687062014-07-02 15:28:02 +1000225{
226 char *tmp, *ret = NULL;
227 size_t nlen, rlen = 0;
228 const struct keytype *kt;
229
230 for (kt = keytypes; kt->type != -1; kt++) {
djm@openbsd.org183ba552017-03-10 04:07:20 +0000231 if (kt->name == NULL)
232 continue;
233 if (!include_sigonly && kt->sigonly)
Damien Miller86687062014-07-02 15:28:02 +1000234 continue;
235 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
236 continue;
237 if (ret != NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000238 ret[rlen++] = sep;
Damien Miller86687062014-07-02 15:28:02 +1000239 nlen = strlen(kt->name);
240 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
241 free(ret);
242 return NULL;
243 }
244 ret = tmp;
245 memcpy(ret + rlen, kt->name, nlen + 1);
246 rlen += nlen;
247 }
248 return ret;
249}
250
251int
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000252sshkey_names_valid2(const char *names, int allow_wildcard)
Damien Miller86687062014-07-02 15:28:02 +1000253{
254 char *s, *cp, *p;
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000255 const struct keytype *kt;
256 int type;
Damien Miller86687062014-07-02 15:28:02 +1000257
258 if (names == NULL || strcmp(names, "") == 0)
259 return 0;
260 if ((s = cp = strdup(names)) == NULL)
261 return 0;
262 for ((p = strsep(&cp, ",")); p && *p != '\0';
263 (p = strsep(&cp, ","))) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000264 type = sshkey_type_from_name(p);
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000265 if (type == KEY_UNSPEC) {
266 if (allow_wildcard) {
267 /*
268 * Try matching key types against the string.
269 * If any has a positive or negative match then
270 * the component is accepted.
271 */
272 for (kt = keytypes; kt->type != -1; kt++) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000273 if (match_pattern_list(kt->name,
djm@openbsd.orge661a862015-05-04 06:10:48 +0000274 p, 0) != 0)
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000275 break;
276 }
277 if (kt->type != -1)
278 continue;
279 }
Damien Miller86687062014-07-02 15:28:02 +1000280 free(s);
281 return 0;
282 }
283 }
284 free(s);
285 return 1;
286}
287
288u_int
289sshkey_size(const struct sshkey *k)
290{
291 switch (k->type) {
292#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000293 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000294 case KEY_RSA_CERT:
295 return BN_num_bits(k->rsa->n);
296 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000297 case KEY_DSA_CERT:
298 return BN_num_bits(k->dsa->p);
299 case KEY_ECDSA:
300 case KEY_ECDSA_CERT:
301 return sshkey_curve_nid_to_bits(k->ecdsa_nid);
302#endif /* WITH_OPENSSL */
303 case KEY_ED25519:
304 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000305 case KEY_XMSS:
306 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000307 return 256; /* XXX */
308 }
309 return 0;
310}
311
Damien Miller86687062014-07-02 15:28:02 +1000312static int
313sshkey_type_is_valid_ca(int type)
314{
315 switch (type) {
316 case KEY_RSA:
317 case KEY_DSA:
318 case KEY_ECDSA:
319 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000320 case KEY_XMSS:
Damien Miller86687062014-07-02 15:28:02 +1000321 return 1;
322 default:
323 return 0;
324 }
325}
326
327int
328sshkey_is_cert(const struct sshkey *k)
329{
330 if (k == NULL)
331 return 0;
332 return sshkey_type_is_cert(k->type);
333}
334
335/* Return the cert-less equivalent to a certified key type */
336int
337sshkey_type_plain(int type)
338{
339 switch (type) {
Damien Miller86687062014-07-02 15:28:02 +1000340 case KEY_RSA_CERT:
341 return KEY_RSA;
Damien Miller86687062014-07-02 15:28:02 +1000342 case KEY_DSA_CERT:
343 return KEY_DSA;
344 case KEY_ECDSA_CERT:
345 return KEY_ECDSA;
346 case KEY_ED25519_CERT:
347 return KEY_ED25519;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000348 case KEY_XMSS_CERT:
349 return KEY_XMSS;
Damien Miller86687062014-07-02 15:28:02 +1000350 default:
351 return type;
352 }
353}
354
355#ifdef WITH_OPENSSL
356/* XXX: these are really begging for a table-driven approach */
357int
358sshkey_curve_name_to_nid(const char *name)
359{
360 if (strcmp(name, "nistp256") == 0)
361 return NID_X9_62_prime256v1;
362 else if (strcmp(name, "nistp384") == 0)
363 return NID_secp384r1;
364# ifdef OPENSSL_HAS_NISTP521
365 else if (strcmp(name, "nistp521") == 0)
366 return NID_secp521r1;
367# endif /* OPENSSL_HAS_NISTP521 */
368 else
369 return -1;
370}
371
372u_int
373sshkey_curve_nid_to_bits(int nid)
374{
375 switch (nid) {
376 case NID_X9_62_prime256v1:
377 return 256;
378 case NID_secp384r1:
379 return 384;
380# ifdef OPENSSL_HAS_NISTP521
381 case NID_secp521r1:
382 return 521;
383# endif /* OPENSSL_HAS_NISTP521 */
384 default:
385 return 0;
386 }
387}
388
389int
390sshkey_ecdsa_bits_to_nid(int bits)
391{
392 switch (bits) {
393 case 256:
394 return NID_X9_62_prime256v1;
395 case 384:
396 return NID_secp384r1;
397# ifdef OPENSSL_HAS_NISTP521
398 case 521:
399 return NID_secp521r1;
400# endif /* OPENSSL_HAS_NISTP521 */
401 default:
402 return -1;
403 }
404}
405
406const char *
407sshkey_curve_nid_to_name(int nid)
408{
409 switch (nid) {
410 case NID_X9_62_prime256v1:
411 return "nistp256";
412 case NID_secp384r1:
413 return "nistp384";
414# ifdef OPENSSL_HAS_NISTP521
415 case NID_secp521r1:
416 return "nistp521";
417# endif /* OPENSSL_HAS_NISTP521 */
418 default:
419 return NULL;
420 }
421}
422
423int
424sshkey_ec_nid_to_hash_alg(int nid)
425{
426 int kbits = sshkey_curve_nid_to_bits(nid);
427
428 if (kbits <= 0)
429 return -1;
430
431 /* RFC5656 section 6.2.1 */
432 if (kbits <= 256)
433 return SSH_DIGEST_SHA256;
434 else if (kbits <= 384)
435 return SSH_DIGEST_SHA384;
436 else
437 return SSH_DIGEST_SHA512;
438}
439#endif /* WITH_OPENSSL */
440
441static void
442cert_free(struct sshkey_cert *cert)
443{
444 u_int i;
445
446 if (cert == NULL)
447 return;
mmcc@openbsd.org52d70782015-12-11 04:21:11 +0000448 sshbuf_free(cert->certblob);
449 sshbuf_free(cert->critical);
450 sshbuf_free(cert->extensions);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000451 free(cert->key_id);
Damien Miller86687062014-07-02 15:28:02 +1000452 for (i = 0; i < cert->nprincipals; i++)
453 free(cert->principals[i]);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000454 free(cert->principals);
mmcc@openbsd.org89540b62015-12-11 02:31:47 +0000455 sshkey_free(cert->signature_key);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000456 freezero(cert, sizeof(*cert));
Damien Miller86687062014-07-02 15:28:02 +1000457}
458
459static struct sshkey_cert *
460cert_new(void)
461{
462 struct sshkey_cert *cert;
463
464 if ((cert = calloc(1, sizeof(*cert))) == NULL)
465 return NULL;
466 if ((cert->certblob = sshbuf_new()) == NULL ||
467 (cert->critical = sshbuf_new()) == NULL ||
468 (cert->extensions = sshbuf_new()) == NULL) {
469 cert_free(cert);
470 return NULL;
471 }
472 cert->key_id = NULL;
473 cert->principals = NULL;
474 cert->signature_key = NULL;
475 return cert;
476}
477
478struct sshkey *
479sshkey_new(int type)
480{
481 struct sshkey *k;
482#ifdef WITH_OPENSSL
483 RSA *rsa;
484 DSA *dsa;
485#endif /* WITH_OPENSSL */
486
487 if ((k = calloc(1, sizeof(*k))) == NULL)
488 return NULL;
489 k->type = type;
490 k->ecdsa = NULL;
491 k->ecdsa_nid = -1;
492 k->dsa = NULL;
493 k->rsa = NULL;
494 k->cert = NULL;
495 k->ed25519_sk = NULL;
496 k->ed25519_pk = NULL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000497 k->xmss_sk = NULL;
498 k->xmss_pk = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000499 switch (k->type) {
500#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000501 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000502 case KEY_RSA_CERT:
503 if ((rsa = RSA_new()) == NULL ||
504 (rsa->n = BN_new()) == NULL ||
505 (rsa->e = BN_new()) == NULL) {
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000506 RSA_free(rsa);
Damien Miller86687062014-07-02 15:28:02 +1000507 free(k);
508 return NULL;
509 }
510 k->rsa = rsa;
511 break;
512 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000513 case KEY_DSA_CERT:
514 if ((dsa = DSA_new()) == NULL ||
515 (dsa->p = BN_new()) == NULL ||
516 (dsa->q = BN_new()) == NULL ||
517 (dsa->g = BN_new()) == NULL ||
518 (dsa->pub_key = BN_new()) == NULL) {
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000519 DSA_free(dsa);
Damien Miller86687062014-07-02 15:28:02 +1000520 free(k);
521 return NULL;
522 }
523 k->dsa = dsa;
524 break;
525 case KEY_ECDSA:
526 case KEY_ECDSA_CERT:
527 /* Cannot do anything until we know the group */
528 break;
529#endif /* WITH_OPENSSL */
530 case KEY_ED25519:
531 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000532 case KEY_XMSS:
533 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000534 /* no need to prealloc */
535 break;
536 case KEY_UNSPEC:
537 break;
538 default:
539 free(k);
540 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000541 }
542
543 if (sshkey_is_cert(k)) {
544 if ((k->cert = cert_new()) == NULL) {
545 sshkey_free(k);
546 return NULL;
547 }
548 }
549
550 return k;
551}
552
553int
554sshkey_add_private(struct sshkey *k)
555{
556 switch (k->type) {
557#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000558 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000559 case KEY_RSA_CERT:
560#define bn_maybe_alloc_failed(p) (p == NULL && (p = BN_new()) == NULL)
561 if (bn_maybe_alloc_failed(k->rsa->d) ||
562 bn_maybe_alloc_failed(k->rsa->iqmp) ||
563 bn_maybe_alloc_failed(k->rsa->q) ||
564 bn_maybe_alloc_failed(k->rsa->p) ||
565 bn_maybe_alloc_failed(k->rsa->dmq1) ||
566 bn_maybe_alloc_failed(k->rsa->dmp1))
567 return SSH_ERR_ALLOC_FAIL;
568 break;
569 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000570 case KEY_DSA_CERT:
571 if (bn_maybe_alloc_failed(k->dsa->priv_key))
572 return SSH_ERR_ALLOC_FAIL;
573 break;
574#undef bn_maybe_alloc_failed
575 case KEY_ECDSA:
576 case KEY_ECDSA_CERT:
577 /* Cannot do anything until we know the group */
578 break;
579#endif /* WITH_OPENSSL */
580 case KEY_ED25519:
581 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000582 case KEY_XMSS:
583 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000584 /* no need to prealloc */
585 break;
586 case KEY_UNSPEC:
587 break;
588 default:
589 return SSH_ERR_INVALID_ARGUMENT;
590 }
591 return 0;
592}
593
594struct sshkey *
595sshkey_new_private(int type)
596{
597 struct sshkey *k = sshkey_new(type);
598
599 if (k == NULL)
600 return NULL;
601 if (sshkey_add_private(k) != 0) {
602 sshkey_free(k);
603 return NULL;
604 }
605 return k;
606}
607
608void
609sshkey_free(struct sshkey *k)
610{
611 if (k == NULL)
612 return;
613 switch (k->type) {
614#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000615 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000616 case KEY_RSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000617 RSA_free(k->rsa);
Damien Miller86687062014-07-02 15:28:02 +1000618 k->rsa = NULL;
619 break;
620 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000621 case KEY_DSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000622 DSA_free(k->dsa);
Damien Miller86687062014-07-02 15:28:02 +1000623 k->dsa = NULL;
624 break;
625# ifdef OPENSSL_HAS_ECC
626 case KEY_ECDSA:
627 case KEY_ECDSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000628 EC_KEY_free(k->ecdsa);
Damien Miller86687062014-07-02 15:28:02 +1000629 k->ecdsa = NULL;
630 break;
631# endif /* OPENSSL_HAS_ECC */
632#endif /* WITH_OPENSSL */
633 case KEY_ED25519:
634 case KEY_ED25519_CERT:
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000635 freezero(k->ed25519_pk, ED25519_PK_SZ);
636 k->ed25519_pk = NULL;
637 freezero(k->ed25519_sk, ED25519_SK_SZ);
638 k->ed25519_sk = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000639 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000640#ifdef WITH_XMSS
641 case KEY_XMSS:
642 case KEY_XMSS_CERT:
643 freezero(k->xmss_pk, sshkey_xmss_pklen(k));
644 k->xmss_pk = NULL;
645 freezero(k->xmss_sk, sshkey_xmss_sklen(k));
646 k->xmss_sk = NULL;
647 sshkey_xmss_free_state(k);
648 free(k->xmss_name);
649 k->xmss_name = NULL;
650 free(k->xmss_filename);
651 k->xmss_filename = NULL;
652 break;
653#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000654 case KEY_UNSPEC:
655 break;
656 default:
657 break;
658 }
659 if (sshkey_is_cert(k))
660 cert_free(k->cert);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000661 freezero(k, sizeof(*k));
Damien Miller86687062014-07-02 15:28:02 +1000662}
663
664static int
665cert_compare(struct sshkey_cert *a, struct sshkey_cert *b)
666{
667 if (a == NULL && b == NULL)
668 return 1;
669 if (a == NULL || b == NULL)
670 return 0;
671 if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob))
672 return 0;
673 if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob),
674 sshbuf_len(a->certblob)) != 0)
675 return 0;
676 return 1;
677}
678
679/*
680 * Compare public portions of key only, allowing comparisons between
681 * certificates and plain keys too.
682 */
683int
684sshkey_equal_public(const struct sshkey *a, const struct sshkey *b)
685{
Darren Tucker948a1772014-07-22 01:07:11 +1000686#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
Damien Miller86687062014-07-02 15:28:02 +1000687 BN_CTX *bnctx;
Darren Tucker948a1772014-07-22 01:07:11 +1000688#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +1000689
690 if (a == NULL || b == NULL ||
691 sshkey_type_plain(a->type) != sshkey_type_plain(b->type))
692 return 0;
693
694 switch (a->type) {
695#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000696 case KEY_RSA_CERT:
697 case KEY_RSA:
698 return a->rsa != NULL && b->rsa != NULL &&
699 BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
700 BN_cmp(a->rsa->n, b->rsa->n) == 0;
Damien Miller86687062014-07-02 15:28:02 +1000701 case KEY_DSA_CERT:
702 case KEY_DSA:
703 return a->dsa != NULL && b->dsa != NULL &&
704 BN_cmp(a->dsa->p, b->dsa->p) == 0 &&
705 BN_cmp(a->dsa->q, b->dsa->q) == 0 &&
706 BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
707 BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
708# ifdef OPENSSL_HAS_ECC
709 case KEY_ECDSA_CERT:
710 case KEY_ECDSA:
711 if (a->ecdsa == NULL || b->ecdsa == NULL ||
712 EC_KEY_get0_public_key(a->ecdsa) == NULL ||
713 EC_KEY_get0_public_key(b->ecdsa) == NULL)
714 return 0;
715 if ((bnctx = BN_CTX_new()) == NULL)
716 return 0;
717 if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa),
718 EC_KEY_get0_group(b->ecdsa), bnctx) != 0 ||
719 EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa),
720 EC_KEY_get0_public_key(a->ecdsa),
721 EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) {
722 BN_CTX_free(bnctx);
723 return 0;
724 }
725 BN_CTX_free(bnctx);
726 return 1;
727# endif /* OPENSSL_HAS_ECC */
728#endif /* WITH_OPENSSL */
729 case KEY_ED25519:
730 case KEY_ED25519_CERT:
731 return a->ed25519_pk != NULL && b->ed25519_pk != NULL &&
732 memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000733#ifdef WITH_XMSS
734 case KEY_XMSS:
735 case KEY_XMSS_CERT:
736 return a->xmss_pk != NULL && b->xmss_pk != NULL &&
737 sshkey_xmss_pklen(a) == sshkey_xmss_pklen(b) &&
738 memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) == 0;
739#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000740 default:
741 return 0;
742 }
743 /* NOTREACHED */
744}
745
746int
747sshkey_equal(const struct sshkey *a, const struct sshkey *b)
748{
749 if (a == NULL || b == NULL || a->type != b->type)
750 return 0;
751 if (sshkey_is_cert(a)) {
752 if (!cert_compare(a->cert, b->cert))
753 return 0;
754 }
755 return sshkey_equal_public(a, b);
756}
757
758static int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000759to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain,
760 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +1000761{
762 int type, ret = SSH_ERR_INTERNAL_ERROR;
763 const char *typename;
764
765 if (key == NULL)
766 return SSH_ERR_INVALID_ARGUMENT;
767
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +0000768 if (sshkey_is_cert(key)) {
769 if (key->cert == NULL)
770 return SSH_ERR_EXPECTED_CERT;
771 if (sshbuf_len(key->cert->certblob) == 0)
772 return SSH_ERR_KEY_LACKS_CERTBLOB;
773 }
Damien Miller86687062014-07-02 15:28:02 +1000774 type = force_plain ? sshkey_type_plain(key->type) : key->type;
775 typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid);
776
777 switch (type) {
778#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000779 case KEY_DSA_CERT:
780 case KEY_ECDSA_CERT:
781 case KEY_RSA_CERT:
782#endif /* WITH_OPENSSL */
783 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000784#ifdef WITH_XMSS
785 case KEY_XMSS_CERT:
786#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000787 /* Use the existing blob */
788 /* XXX modified flag? */
789 if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0)
790 return ret;
791 break;
792#ifdef WITH_OPENSSL
793 case KEY_DSA:
794 if (key->dsa == NULL)
795 return SSH_ERR_INVALID_ARGUMENT;
796 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
797 (ret = sshbuf_put_bignum2(b, key->dsa->p)) != 0 ||
798 (ret = sshbuf_put_bignum2(b, key->dsa->q)) != 0 ||
799 (ret = sshbuf_put_bignum2(b, key->dsa->g)) != 0 ||
800 (ret = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0)
801 return ret;
802 break;
Darren Tuckerd1a04212014-07-19 07:23:55 +1000803# ifdef OPENSSL_HAS_ECC
Damien Miller86687062014-07-02 15:28:02 +1000804 case KEY_ECDSA:
805 if (key->ecdsa == NULL)
806 return SSH_ERR_INVALID_ARGUMENT;
807 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
808 (ret = sshbuf_put_cstring(b,
809 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
810 (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0)
811 return ret;
812 break;
Darren Tuckerd1a04212014-07-19 07:23:55 +1000813# endif
Damien Miller86687062014-07-02 15:28:02 +1000814 case KEY_RSA:
815 if (key->rsa == NULL)
816 return SSH_ERR_INVALID_ARGUMENT;
817 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
818 (ret = sshbuf_put_bignum2(b, key->rsa->e)) != 0 ||
819 (ret = sshbuf_put_bignum2(b, key->rsa->n)) != 0)
820 return ret;
821 break;
822#endif /* WITH_OPENSSL */
823 case KEY_ED25519:
824 if (key->ed25519_pk == NULL)
825 return SSH_ERR_INVALID_ARGUMENT;
826 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
827 (ret = sshbuf_put_string(b,
828 key->ed25519_pk, ED25519_PK_SZ)) != 0)
829 return ret;
830 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000831#ifdef WITH_XMSS
832 case KEY_XMSS:
833 if (key->xmss_name == NULL || key->xmss_pk == NULL ||
834 sshkey_xmss_pklen(key) == 0)
835 return SSH_ERR_INVALID_ARGUMENT;
836 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
837 (ret = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
838 (ret = sshbuf_put_string(b,
839 key->xmss_pk, sshkey_xmss_pklen(key))) != 0 ||
840 (ret = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0)
841 return ret;
842 break;
843#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000844 default:
845 return SSH_ERR_KEY_TYPE_UNKNOWN;
846 }
847 return 0;
848}
849
850int
djm@openbsd.org60b18252015-01-26 02:59:11 +0000851sshkey_putb(const struct sshkey *key, struct sshbuf *b)
Damien Miller86687062014-07-02 15:28:02 +1000852{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000853 return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000854}
855
856int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000857sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b,
858 enum sshkey_serialize_rep opts)
djm@openbsd.org60b18252015-01-26 02:59:11 +0000859{
860 struct sshbuf *tmp;
861 int r;
862
863 if ((tmp = sshbuf_new()) == NULL)
864 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000865 r = to_blob_buf(key, tmp, 0, opts);
djm@openbsd.org60b18252015-01-26 02:59:11 +0000866 if (r == 0)
867 r = sshbuf_put_stringb(b, tmp);
868 sshbuf_free(tmp);
869 return r;
870}
871
872int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000873sshkey_puts(const struct sshkey *key, struct sshbuf *b)
874{
875 return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT);
876}
877
878int
djm@openbsd.org60b18252015-01-26 02:59:11 +0000879sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b)
Damien Miller86687062014-07-02 15:28:02 +1000880{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000881 return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000882}
883
884static int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000885to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain,
886 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +1000887{
888 int ret = SSH_ERR_INTERNAL_ERROR;
889 size_t len;
890 struct sshbuf *b = NULL;
891
892 if (lenp != NULL)
893 *lenp = 0;
894 if (blobp != NULL)
895 *blobp = NULL;
896 if ((b = sshbuf_new()) == NULL)
897 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000898 if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000899 goto out;
900 len = sshbuf_len(b);
901 if (lenp != NULL)
902 *lenp = len;
903 if (blobp != NULL) {
904 if ((*blobp = malloc(len)) == NULL) {
905 ret = SSH_ERR_ALLOC_FAIL;
906 goto out;
907 }
908 memcpy(*blobp, sshbuf_ptr(b), len);
909 }
910 ret = 0;
911 out:
912 sshbuf_free(b);
913 return ret;
914}
915
916int
917sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
918{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000919 return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000920}
921
922int
923sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
924{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000925 return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000926}
927
928int
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000929sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg,
Damien Miller86687062014-07-02 15:28:02 +1000930 u_char **retp, size_t *lenp)
931{
932 u_char *blob = NULL, *ret = NULL;
933 size_t blob_len = 0;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000934 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +1000935
936 if (retp != NULL)
937 *retp = NULL;
938 if (lenp != NULL)
939 *lenp = 0;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000940 if (ssh_digest_bytes(dgst_alg) == 0) {
Damien Miller86687062014-07-02 15:28:02 +1000941 r = SSH_ERR_INVALID_ARGUMENT;
942 goto out;
943 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000944 if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT))
945 != 0)
Damien Miller86687062014-07-02 15:28:02 +1000946 goto out;
947 if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) {
948 r = SSH_ERR_ALLOC_FAIL;
949 goto out;
950 }
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000951 if ((r = ssh_digest_memory(dgst_alg, blob, blob_len,
Damien Miller86687062014-07-02 15:28:02 +1000952 ret, SSH_DIGEST_MAX_LENGTH)) != 0)
953 goto out;
954 /* success */
955 if (retp != NULL) {
956 *retp = ret;
957 ret = NULL;
958 }
959 if (lenp != NULL)
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000960 *lenp = ssh_digest_bytes(dgst_alg);
Damien Miller86687062014-07-02 15:28:02 +1000961 r = 0;
962 out:
963 free(ret);
964 if (blob != NULL) {
965 explicit_bzero(blob, blob_len);
966 free(blob);
967 }
968 return r;
969}
970
971static char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000972fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
Damien Miller86687062014-07-02 15:28:02 +1000973{
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000974 char *ret;
975 size_t plen = strlen(alg) + 1;
976 size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1;
977 int r;
Damien Miller86687062014-07-02 15:28:02 +1000978
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000979 if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL)
Damien Miller86687062014-07-02 15:28:02 +1000980 return NULL;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000981 strlcpy(ret, alg, rlen);
982 strlcat(ret, ":", rlen);
983 if (dgst_raw_len == 0)
984 return ret;
985 if ((r = b64_ntop(dgst_raw, dgst_raw_len,
986 ret + plen, rlen - plen)) == -1) {
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000987 freezero(ret, rlen);
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000988 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000989 }
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000990 /* Trim padding characters from end */
991 ret[strcspn(ret, "=")] = '\0';
992 return ret;
993}
Damien Miller86687062014-07-02 15:28:02 +1000994
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000995static char *
996fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
997{
998 char *retval, hex[5];
999 size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2;
1000
1001 if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL)
1002 return NULL;
1003 strlcpy(retval, alg, rlen);
1004 strlcat(retval, ":", rlen);
1005 for (i = 0; i < dgst_raw_len; i++) {
1006 snprintf(hex, sizeof(hex), "%s%02x",
1007 i > 0 ? ":" : "", dgst_raw[i]);
1008 strlcat(retval, hex, rlen);
1009 }
Damien Miller86687062014-07-02 15:28:02 +10001010 return retval;
1011}
1012
1013static char *
1014fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len)
1015{
1016 char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' };
1017 char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
1018 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' };
1019 u_int i, j = 0, rounds, seed = 1;
1020 char *retval;
1021
1022 rounds = (dgst_raw_len / 2) + 1;
1023 if ((retval = calloc(rounds, 6)) == NULL)
1024 return NULL;
1025 retval[j++] = 'x';
1026 for (i = 0; i < rounds; i++) {
1027 u_int idx0, idx1, idx2, idx3, idx4;
1028 if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) {
1029 idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) +
1030 seed) % 6;
1031 idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15;
1032 idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) +
1033 (seed / 6)) % 6;
1034 retval[j++] = vowels[idx0];
1035 retval[j++] = consonants[idx1];
1036 retval[j++] = vowels[idx2];
1037 if ((i + 1) < rounds) {
1038 idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15;
1039 idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15;
1040 retval[j++] = consonants[idx3];
1041 retval[j++] = '-';
1042 retval[j++] = consonants[idx4];
1043 seed = ((seed * 5) +
1044 ((((u_int)(dgst_raw[2 * i])) * 7) +
1045 ((u_int)(dgst_raw[(2 * i) + 1])))) % 36;
1046 }
1047 } else {
1048 idx0 = seed % 6;
1049 idx1 = 16;
1050 idx2 = seed / 6;
1051 retval[j++] = vowels[idx0];
1052 retval[j++] = consonants[idx1];
1053 retval[j++] = vowels[idx2];
1054 }
1055 }
1056 retval[j++] = 'x';
1057 retval[j++] = '\0';
1058 return retval;
1059}
1060
1061/*
1062 * Draw an ASCII-Art representing the fingerprint so human brain can
1063 * profit from its built-in pattern recognition ability.
1064 * This technique is called "random art" and can be found in some
1065 * scientific publications like this original paper:
1066 *
1067 * "Hash Visualization: a New Technique to improve Real-World Security",
1068 * Perrig A. and Song D., 1999, International Workshop on Cryptographic
1069 * Techniques and E-Commerce (CrypTEC '99)
1070 * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
1071 *
1072 * The subject came up in a talk by Dan Kaminsky, too.
1073 *
1074 * If you see the picture is different, the key is different.
1075 * If the picture looks the same, you still know nothing.
1076 *
1077 * The algorithm used here is a worm crawling over a discrete plane,
1078 * leaving a trace (augmenting the field) everywhere it goes.
1079 * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
1080 * makes the respective movement vector be ignored for this turn.
1081 * Graphs are not unambiguous, because circles in graphs can be
1082 * walked in either direction.
1083 */
1084
1085/*
1086 * Field sizes for the random art. Have to be odd, so the starting point
1087 * can be in the exact middle of the picture, and FLDBASE should be >=8 .
1088 * Else pictures would be too dense, and drawing the frame would
1089 * fail, too, because the key type would not fit in anymore.
1090 */
1091#define FLDBASE 8
1092#define FLDSIZE_Y (FLDBASE + 1)
1093#define FLDSIZE_X (FLDBASE * 2 + 1)
1094static char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001095fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len,
Damien Miller86687062014-07-02 15:28:02 +10001096 const struct sshkey *k)
1097{
1098 /*
1099 * Chars to be used after each other every time the worm
1100 * intersects with itself. Matter of taste.
1101 */
1102 char *augmentation_string = " .o+=*BOX@%&#/^SE";
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001103 char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X];
Damien Miller86687062014-07-02 15:28:02 +10001104 u_char field[FLDSIZE_X][FLDSIZE_Y];
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001105 size_t i, tlen, hlen;
Damien Miller86687062014-07-02 15:28:02 +10001106 u_int b;
Damien Miller61e28e52014-07-03 21:22:22 +10001107 int x, y, r;
Damien Miller86687062014-07-02 15:28:02 +10001108 size_t len = strlen(augmentation_string) - 1;
1109
1110 if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL)
1111 return NULL;
1112
1113 /* initialize field */
1114 memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
1115 x = FLDSIZE_X / 2;
1116 y = FLDSIZE_Y / 2;
1117
1118 /* process raw key */
1119 for (i = 0; i < dgst_raw_len; i++) {
1120 int input;
1121 /* each byte conveys four 2-bit move commands */
1122 input = dgst_raw[i];
1123 for (b = 0; b < 4; b++) {
1124 /* evaluate 2 bit, rest is shifted later */
1125 x += (input & 0x1) ? 1 : -1;
1126 y += (input & 0x2) ? 1 : -1;
1127
1128 /* assure we are still in bounds */
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001129 x = MAXIMUM(x, 0);
1130 y = MAXIMUM(y, 0);
1131 x = MINIMUM(x, FLDSIZE_X - 1);
1132 y = MINIMUM(y, FLDSIZE_Y - 1);
Damien Miller86687062014-07-02 15:28:02 +10001133
1134 /* augment the field */
1135 if (field[x][y] < len - 2)
1136 field[x][y]++;
1137 input = input >> 2;
1138 }
1139 }
1140
1141 /* mark starting point and end point*/
1142 field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
1143 field[x][y] = len;
1144
Damien Miller61e28e52014-07-03 21:22:22 +10001145 /* assemble title */
1146 r = snprintf(title, sizeof(title), "[%s %u]",
1147 sshkey_type(k), sshkey_size(k));
1148 /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */
1149 if (r < 0 || r > (int)sizeof(title))
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001150 r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k));
1151 tlen = (r <= 0) ? 0 : strlen(title);
1152
1153 /* assemble hash ID. */
1154 r = snprintf(hash, sizeof(hash), "[%s]", alg);
1155 hlen = (r <= 0) ? 0 : strlen(hash);
Damien Miller86687062014-07-02 15:28:02 +10001156
1157 /* output upper border */
Damien Miller61e28e52014-07-03 21:22:22 +10001158 p = retval;
1159 *p++ = '+';
1160 for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++)
1161 *p++ = '-';
1162 memcpy(p, title, tlen);
1163 p += tlen;
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001164 for (i += tlen; i < FLDSIZE_X; i++)
Damien Miller86687062014-07-02 15:28:02 +10001165 *p++ = '-';
1166 *p++ = '+';
1167 *p++ = '\n';
1168
1169 /* output content */
1170 for (y = 0; y < FLDSIZE_Y; y++) {
1171 *p++ = '|';
1172 for (x = 0; x < FLDSIZE_X; x++)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001173 *p++ = augmentation_string[MINIMUM(field[x][y], len)];
Damien Miller86687062014-07-02 15:28:02 +10001174 *p++ = '|';
1175 *p++ = '\n';
1176 }
1177
1178 /* output lower border */
1179 *p++ = '+';
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001180 for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++)
1181 *p++ = '-';
1182 memcpy(p, hash, hlen);
1183 p += hlen;
1184 for (i += hlen; i < FLDSIZE_X; i++)
Damien Miller86687062014-07-02 15:28:02 +10001185 *p++ = '-';
1186 *p++ = '+';
1187
1188 return retval;
1189}
1190
1191char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001192sshkey_fingerprint(const struct sshkey *k, int dgst_alg,
Damien Miller86687062014-07-02 15:28:02 +10001193 enum sshkey_fp_rep dgst_rep)
1194{
1195 char *retval = NULL;
1196 u_char *dgst_raw;
1197 size_t dgst_raw_len;
1198
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001199 if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001200 return NULL;
1201 switch (dgst_rep) {
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001202 case SSH_FP_DEFAULT:
1203 if (dgst_alg == SSH_DIGEST_MD5) {
1204 retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
1205 dgst_raw, dgst_raw_len);
1206 } else {
1207 retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
1208 dgst_raw, dgst_raw_len);
1209 }
1210 break;
Damien Miller86687062014-07-02 15:28:02 +10001211 case SSH_FP_HEX:
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001212 retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
1213 dgst_raw, dgst_raw_len);
1214 break;
1215 case SSH_FP_BASE64:
1216 retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
1217 dgst_raw, dgst_raw_len);
Damien Miller86687062014-07-02 15:28:02 +10001218 break;
1219 case SSH_FP_BUBBLEBABBLE:
1220 retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
1221 break;
1222 case SSH_FP_RANDOMART:
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001223 retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg),
1224 dgst_raw, dgst_raw_len, k);
Damien Miller86687062014-07-02 15:28:02 +10001225 break;
1226 default:
1227 explicit_bzero(dgst_raw, dgst_raw_len);
1228 free(dgst_raw);
1229 return NULL;
1230 }
1231 explicit_bzero(dgst_raw, dgst_raw_len);
1232 free(dgst_raw);
1233 return retval;
1234}
1235
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001236static int
1237peek_type_nid(const char *s, size_t l, int *nid)
1238{
1239 const struct keytype *kt;
Damien Miller86687062014-07-02 15:28:02 +10001240
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001241 for (kt = keytypes; kt->type != -1; kt++) {
1242 if (kt->name == NULL || strlen(kt->name) != l)
1243 continue;
1244 if (memcmp(s, kt->name, l) == 0) {
1245 *nid = -1;
1246 if (kt->type == KEY_ECDSA || kt->type == KEY_ECDSA_CERT)
1247 *nid = kt->nid;
1248 return kt->type;
1249 }
1250 }
1251 return KEY_UNSPEC;
1252}
1253
1254/* XXX this can now be made const char * */
Damien Miller86687062014-07-02 15:28:02 +10001255int
1256sshkey_read(struct sshkey *ret, char **cpp)
1257{
1258 struct sshkey *k;
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001259 char *cp, *blobcopy;
1260 size_t space;
Damien Miller86687062014-07-02 15:28:02 +10001261 int r, type, curve_nid = -1;
1262 struct sshbuf *blob;
Damien Miller86687062014-07-02 15:28:02 +10001263
dtucker@openbsd.org7fadbb62017-03-10 03:48:57 +00001264 if (ret == NULL)
1265 return SSH_ERR_INVALID_ARGUMENT;
1266
Damien Miller86687062014-07-02 15:28:02 +10001267 switch (ret->type) {
Damien Miller86687062014-07-02 15:28:02 +10001268 case KEY_UNSPEC:
1269 case KEY_RSA:
1270 case KEY_DSA:
1271 case KEY_ECDSA:
1272 case KEY_ED25519:
Damien Miller86687062014-07-02 15:28:02 +10001273 case KEY_DSA_CERT:
1274 case KEY_ECDSA_CERT:
1275 case KEY_RSA_CERT:
1276 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001277#ifdef WITH_XMSS
1278 case KEY_XMSS:
1279 case KEY_XMSS_CERT:
1280#endif /* WITH_XMSS */
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001281 break; /* ok */
Damien Miller86687062014-07-02 15:28:02 +10001282 default:
1283 return SSH_ERR_INVALID_ARGUMENT;
1284 }
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001285
1286 /* Decode type */
1287 cp = *cpp;
1288 space = strcspn(cp, " \t");
1289 if (space == strlen(cp))
1290 return SSH_ERR_INVALID_FORMAT;
1291 if ((type = peek_type_nid(cp, space, &curve_nid)) == KEY_UNSPEC)
1292 return SSH_ERR_INVALID_FORMAT;
1293
1294 /* skip whitespace */
1295 for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
1296 ;
1297 if (*cp == '\0')
1298 return SSH_ERR_INVALID_FORMAT;
1299 if (ret->type != KEY_UNSPEC && ret->type != type)
1300 return SSH_ERR_KEY_TYPE_MISMATCH;
1301 if ((blob = sshbuf_new()) == NULL)
1302 return SSH_ERR_ALLOC_FAIL;
1303
1304 /* find end of keyblob and decode */
1305 space = strcspn(cp, " \t");
1306 if ((blobcopy = strndup(cp, space)) == NULL) {
1307 sshbuf_free(blob);
1308 return SSH_ERR_ALLOC_FAIL;
1309 }
1310 if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) {
1311 free(blobcopy);
1312 sshbuf_free(blob);
1313 return r;
1314 }
1315 free(blobcopy);
1316 if ((r = sshkey_fromb(blob, &k)) != 0) {
1317 sshbuf_free(blob);
1318 return r;
1319 }
1320 sshbuf_free(blob);
1321
1322 /* skip whitespace and leave cp at start of comment */
1323 for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
1324 ;
1325
1326 /* ensure type of blob matches type at start of line */
1327 if (k->type != type) {
1328 sshkey_free(k);
1329 return SSH_ERR_KEY_TYPE_MISMATCH;
1330 }
1331 if (sshkey_type_plain(type) == KEY_ECDSA && curve_nid != k->ecdsa_nid) {
1332 sshkey_free(k);
1333 return SSH_ERR_EC_CURVE_MISMATCH;
1334 }
1335
1336 /* Fill in ret from parsed key */
1337 ret->type = type;
1338 if (sshkey_is_cert(ret)) {
1339 if (!sshkey_is_cert(k)) {
1340 sshkey_free(k);
1341 return SSH_ERR_EXPECTED_CERT;
1342 }
1343 if (ret->cert != NULL)
1344 cert_free(ret->cert);
1345 ret->cert = k->cert;
1346 k->cert = NULL;
1347 }
1348 switch (sshkey_type_plain(ret->type)) {
1349#ifdef WITH_OPENSSL
1350 case KEY_RSA:
1351 RSA_free(ret->rsa);
1352 ret->rsa = k->rsa;
1353 k->rsa = NULL;
1354#ifdef DEBUG_PK
1355 RSA_print_fp(stderr, ret->rsa, 8);
1356#endif
1357 break;
1358 case KEY_DSA:
1359 DSA_free(ret->dsa);
1360 ret->dsa = k->dsa;
1361 k->dsa = NULL;
1362#ifdef DEBUG_PK
1363 DSA_print_fp(stderr, ret->dsa, 8);
1364#endif
1365 break;
1366# ifdef OPENSSL_HAS_ECC
1367 case KEY_ECDSA:
1368 EC_KEY_free(ret->ecdsa);
1369 ret->ecdsa = k->ecdsa;
1370 ret->ecdsa_nid = k->ecdsa_nid;
1371 k->ecdsa = NULL;
1372 k->ecdsa_nid = -1;
1373#ifdef DEBUG_PK
1374 sshkey_dump_ec_key(ret->ecdsa);
1375#endif
1376 break;
1377# endif /* OPENSSL_HAS_ECC */
1378#endif /* WITH_OPENSSL */
1379 case KEY_ED25519:
1380 freezero(ret->ed25519_pk, ED25519_PK_SZ);
1381 ret->ed25519_pk = k->ed25519_pk;
1382 k->ed25519_pk = NULL;
1383#ifdef DEBUG_PK
1384 /* XXX */
1385#endif
1386 break;
1387#ifdef WITH_XMSS
1388 case KEY_XMSS:
1389 free(ret->xmss_pk);
1390 ret->xmss_pk = k->xmss_pk;
1391 k->xmss_pk = NULL;
1392 free(ret->xmss_state);
1393 ret->xmss_state = k->xmss_state;
1394 k->xmss_state = NULL;
1395 free(ret->xmss_name);
1396 ret->xmss_name = k->xmss_name;
1397 k->xmss_name = NULL;
1398 free(ret->xmss_filename);
1399 ret->xmss_filename = k->xmss_filename;
1400 k->xmss_filename = NULL;
1401#ifdef DEBUG_PK
1402 /* XXX */
1403#endif
1404 break;
1405#endif /* WITH_XMSS */
1406 default:
1407 sshkey_free(k);
1408 return SSH_ERR_INTERNAL_ERROR;
1409 }
1410 sshkey_free(k);
1411
1412 /* success */
1413 *cpp = cp;
1414 return 0;
Damien Miller86687062014-07-02 15:28:02 +10001415}
1416
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001417
Damien Miller86687062014-07-02 15:28:02 +10001418int
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001419sshkey_to_base64(const struct sshkey *key, char **b64p)
Damien Miller86687062014-07-02 15:28:02 +10001420{
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001421 int r = SSH_ERR_INTERNAL_ERROR;
1422 struct sshbuf *b = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001423 char *uu = NULL;
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001424
1425 if (b64p != NULL)
1426 *b64p = NULL;
1427 if ((b = sshbuf_new()) == NULL)
1428 return SSH_ERR_ALLOC_FAIL;
1429 if ((r = sshkey_putb(key, b)) != 0)
1430 goto out;
1431 if ((uu = sshbuf_dtob64(b)) == NULL) {
1432 r = SSH_ERR_ALLOC_FAIL;
1433 goto out;
1434 }
1435 /* Success */
1436 if (b64p != NULL) {
1437 *b64p = uu;
1438 uu = NULL;
1439 }
1440 r = 0;
1441 out:
1442 sshbuf_free(b);
1443 free(uu);
1444 return r;
1445}
1446
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00001447int
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001448sshkey_format_text(const struct sshkey *key, struct sshbuf *b)
1449{
1450 int r = SSH_ERR_INTERNAL_ERROR;
1451 char *uu = NULL;
1452
djm@openbsd.org873d3e72017-04-30 23:18:44 +00001453 if ((r = sshkey_to_base64(key, &uu)) != 0)
1454 goto out;
1455 if ((r = sshbuf_putf(b, "%s %s",
1456 sshkey_ssh_name(key), uu)) != 0)
1457 goto out;
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001458 r = 0;
1459 out:
1460 free(uu);
1461 return r;
1462}
1463
1464int
1465sshkey_write(const struct sshkey *key, FILE *f)
1466{
1467 struct sshbuf *b = NULL;
1468 int r = SSH_ERR_INTERNAL_ERROR;
1469
1470 if ((b = sshbuf_new()) == NULL)
1471 return SSH_ERR_ALLOC_FAIL;
1472 if ((r = sshkey_format_text(key, b)) != 0)
1473 goto out;
1474 if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) {
1475 if (feof(f))
1476 errno = EPIPE;
1477 r = SSH_ERR_SYSTEM_ERROR;
1478 goto out;
1479 }
1480 /* Success */
1481 r = 0;
1482 out:
1483 sshbuf_free(b);
1484 return r;
Damien Miller86687062014-07-02 15:28:02 +10001485}
1486
1487const char *
1488sshkey_cert_type(const struct sshkey *k)
1489{
1490 switch (k->cert->type) {
1491 case SSH2_CERT_TYPE_USER:
1492 return "user";
1493 case SSH2_CERT_TYPE_HOST:
1494 return "host";
1495 default:
1496 return "unknown";
1497 }
1498}
1499
1500#ifdef WITH_OPENSSL
1501static int
1502rsa_generate_private_key(u_int bits, RSA **rsap)
1503{
1504 RSA *private = NULL;
1505 BIGNUM *f4 = NULL;
1506 int ret = SSH_ERR_INTERNAL_ERROR;
1507
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001508 if (rsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001509 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001510 if (bits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
1511 bits > SSHBUF_MAX_BIGNUM * 8)
1512 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001513 *rsap = NULL;
1514 if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) {
1515 ret = SSH_ERR_ALLOC_FAIL;
1516 goto out;
1517 }
1518 if (!BN_set_word(f4, RSA_F4) ||
1519 !RSA_generate_key_ex(private, bits, f4, NULL)) {
1520 ret = SSH_ERR_LIBCRYPTO_ERROR;
1521 goto out;
1522 }
1523 *rsap = private;
1524 private = NULL;
1525 ret = 0;
1526 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001527 RSA_free(private);
1528 BN_free(f4);
Damien Miller86687062014-07-02 15:28:02 +10001529 return ret;
1530}
1531
1532static int
1533dsa_generate_private_key(u_int bits, DSA **dsap)
1534{
1535 DSA *private;
1536 int ret = SSH_ERR_INTERNAL_ERROR;
1537
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001538 if (dsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001539 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001540 if (bits != 1024)
1541 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001542 if ((private = DSA_new()) == NULL) {
1543 ret = SSH_ERR_ALLOC_FAIL;
1544 goto out;
1545 }
1546 *dsap = NULL;
1547 if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
1548 NULL, NULL) || !DSA_generate_key(private)) {
Damien Miller86687062014-07-02 15:28:02 +10001549 ret = SSH_ERR_LIBCRYPTO_ERROR;
1550 goto out;
1551 }
1552 *dsap = private;
1553 private = NULL;
1554 ret = 0;
1555 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001556 DSA_free(private);
Damien Miller86687062014-07-02 15:28:02 +10001557 return ret;
1558}
1559
1560# ifdef OPENSSL_HAS_ECC
1561int
1562sshkey_ecdsa_key_to_nid(EC_KEY *k)
1563{
1564 EC_GROUP *eg;
1565 int nids[] = {
1566 NID_X9_62_prime256v1,
1567 NID_secp384r1,
1568# ifdef OPENSSL_HAS_NISTP521
1569 NID_secp521r1,
1570# endif /* OPENSSL_HAS_NISTP521 */
1571 -1
1572 };
1573 int nid;
1574 u_int i;
1575 BN_CTX *bnctx;
1576 const EC_GROUP *g = EC_KEY_get0_group(k);
1577
1578 /*
1579 * The group may be stored in a ASN.1 encoded private key in one of two
1580 * ways: as a "named group", which is reconstituted by ASN.1 object ID
1581 * or explicit group parameters encoded into the key blob. Only the
1582 * "named group" case sets the group NID for us, but we can figure
1583 * it out for the other case by comparing against all the groups that
1584 * are supported.
1585 */
1586 if ((nid = EC_GROUP_get_curve_name(g)) > 0)
1587 return nid;
1588 if ((bnctx = BN_CTX_new()) == NULL)
1589 return -1;
1590 for (i = 0; nids[i] != -1; i++) {
1591 if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) {
1592 BN_CTX_free(bnctx);
1593 return -1;
1594 }
1595 if (EC_GROUP_cmp(g, eg, bnctx) == 0)
1596 break;
1597 EC_GROUP_free(eg);
1598 }
1599 BN_CTX_free(bnctx);
1600 if (nids[i] != -1) {
1601 /* Use the group with the NID attached */
1602 EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
1603 if (EC_KEY_set_group(k, eg) != 1) {
1604 EC_GROUP_free(eg);
1605 return -1;
1606 }
1607 }
1608 return nids[i];
1609}
1610
1611static int
1612ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap)
1613{
1614 EC_KEY *private;
1615 int ret = SSH_ERR_INTERNAL_ERROR;
1616
djm@openbsd.org5f02bb12017-05-08 06:11:06 +00001617 if (nid == NULL || ecdsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001618 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org5f02bb12017-05-08 06:11:06 +00001619 if ((*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
1620 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001621 *ecdsap = NULL;
1622 if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) {
1623 ret = SSH_ERR_ALLOC_FAIL;
1624 goto out;
1625 }
1626 if (EC_KEY_generate_key(private) != 1) {
1627 ret = SSH_ERR_LIBCRYPTO_ERROR;
1628 goto out;
1629 }
1630 EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
1631 *ecdsap = private;
1632 private = NULL;
1633 ret = 0;
1634 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001635 EC_KEY_free(private);
Damien Miller86687062014-07-02 15:28:02 +10001636 return ret;
1637}
1638# endif /* OPENSSL_HAS_ECC */
1639#endif /* WITH_OPENSSL */
1640
1641int
1642sshkey_generate(int type, u_int bits, struct sshkey **keyp)
1643{
1644 struct sshkey *k;
1645 int ret = SSH_ERR_INTERNAL_ERROR;
1646
1647 if (keyp == NULL)
1648 return SSH_ERR_INVALID_ARGUMENT;
1649 *keyp = NULL;
1650 if ((k = sshkey_new(KEY_UNSPEC)) == NULL)
1651 return SSH_ERR_ALLOC_FAIL;
1652 switch (type) {
1653 case KEY_ED25519:
1654 if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL ||
1655 (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) {
1656 ret = SSH_ERR_ALLOC_FAIL;
1657 break;
1658 }
1659 crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
1660 ret = 0;
1661 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001662#ifdef WITH_XMSS
1663 case KEY_XMSS:
1664 ret = sshkey_xmss_generate_private_key(k, bits);
1665 break;
1666#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10001667#ifdef WITH_OPENSSL
1668 case KEY_DSA:
1669 ret = dsa_generate_private_key(bits, &k->dsa);
1670 break;
1671# ifdef OPENSSL_HAS_ECC
1672 case KEY_ECDSA:
1673 ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid,
1674 &k->ecdsa);
1675 break;
1676# endif /* OPENSSL_HAS_ECC */
1677 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10001678 ret = rsa_generate_private_key(bits, &k->rsa);
1679 break;
1680#endif /* WITH_OPENSSL */
1681 default:
1682 ret = SSH_ERR_INVALID_ARGUMENT;
1683 }
1684 if (ret == 0) {
1685 k->type = type;
1686 *keyp = k;
1687 } else
1688 sshkey_free(k);
1689 return ret;
1690}
1691
1692int
1693sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key)
1694{
1695 u_int i;
1696 const struct sshkey_cert *from;
1697 struct sshkey_cert *to;
1698 int ret = SSH_ERR_INTERNAL_ERROR;
1699
1700 if (to_key->cert != NULL) {
1701 cert_free(to_key->cert);
1702 to_key->cert = NULL;
1703 }
1704
1705 if ((from = from_key->cert) == NULL)
1706 return SSH_ERR_INVALID_ARGUMENT;
1707
1708 if ((to = to_key->cert = cert_new()) == NULL)
1709 return SSH_ERR_ALLOC_FAIL;
1710
1711 if ((ret = sshbuf_putb(to->certblob, from->certblob)) != 0 ||
1712 (ret = sshbuf_putb(to->critical, from->critical)) != 0 ||
jsg@openbsd.orgf3a3ea12015-09-02 07:51:12 +00001713 (ret = sshbuf_putb(to->extensions, from->extensions)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001714 return ret;
1715
1716 to->serial = from->serial;
1717 to->type = from->type;
1718 if (from->key_id == NULL)
1719 to->key_id = NULL;
1720 else if ((to->key_id = strdup(from->key_id)) == NULL)
1721 return SSH_ERR_ALLOC_FAIL;
1722 to->valid_after = from->valid_after;
1723 to->valid_before = from->valid_before;
1724 if (from->signature_key == NULL)
1725 to->signature_key = NULL;
1726 else if ((ret = sshkey_from_private(from->signature_key,
1727 &to->signature_key)) != 0)
1728 return ret;
1729
1730 if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS)
1731 return SSH_ERR_INVALID_ARGUMENT;
1732 if (from->nprincipals > 0) {
1733 if ((to->principals = calloc(from->nprincipals,
1734 sizeof(*to->principals))) == NULL)
1735 return SSH_ERR_ALLOC_FAIL;
1736 for (i = 0; i < from->nprincipals; i++) {
1737 to->principals[i] = strdup(from->principals[i]);
1738 if (to->principals[i] == NULL) {
1739 to->nprincipals = i;
1740 return SSH_ERR_ALLOC_FAIL;
1741 }
1742 }
1743 }
1744 to->nprincipals = from->nprincipals;
1745 return 0;
1746}
1747
1748int
1749sshkey_from_private(const struct sshkey *k, struct sshkey **pkp)
1750{
1751 struct sshkey *n = NULL;
1752 int ret = SSH_ERR_INTERNAL_ERROR;
1753
djm@openbsd.org1a2663a2015-10-15 23:08:23 +00001754 *pkp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001755 switch (k->type) {
1756#ifdef WITH_OPENSSL
1757 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +10001758 case KEY_DSA_CERT:
1759 if ((n = sshkey_new(k->type)) == NULL)
1760 return SSH_ERR_ALLOC_FAIL;
1761 if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) ||
1762 (BN_copy(n->dsa->q, k->dsa->q) == NULL) ||
1763 (BN_copy(n->dsa->g, k->dsa->g) == NULL) ||
1764 (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) {
1765 sshkey_free(n);
1766 return SSH_ERR_ALLOC_FAIL;
1767 }
1768 break;
1769# ifdef OPENSSL_HAS_ECC
1770 case KEY_ECDSA:
1771 case KEY_ECDSA_CERT:
1772 if ((n = sshkey_new(k->type)) == NULL)
1773 return SSH_ERR_ALLOC_FAIL;
1774 n->ecdsa_nid = k->ecdsa_nid;
1775 n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
1776 if (n->ecdsa == NULL) {
1777 sshkey_free(n);
1778 return SSH_ERR_ALLOC_FAIL;
1779 }
1780 if (EC_KEY_set_public_key(n->ecdsa,
1781 EC_KEY_get0_public_key(k->ecdsa)) != 1) {
1782 sshkey_free(n);
1783 return SSH_ERR_LIBCRYPTO_ERROR;
1784 }
1785 break;
1786# endif /* OPENSSL_HAS_ECC */
1787 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10001788 case KEY_RSA_CERT:
1789 if ((n = sshkey_new(k->type)) == NULL)
1790 return SSH_ERR_ALLOC_FAIL;
1791 if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) ||
1792 (BN_copy(n->rsa->e, k->rsa->e) == NULL)) {
1793 sshkey_free(n);
1794 return SSH_ERR_ALLOC_FAIL;
1795 }
1796 break;
1797#endif /* WITH_OPENSSL */
1798 case KEY_ED25519:
1799 case KEY_ED25519_CERT:
1800 if ((n = sshkey_new(k->type)) == NULL)
1801 return SSH_ERR_ALLOC_FAIL;
1802 if (k->ed25519_pk != NULL) {
1803 if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
1804 sshkey_free(n);
1805 return SSH_ERR_ALLOC_FAIL;
1806 }
1807 memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
1808 }
1809 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001810#ifdef WITH_XMSS
1811 case KEY_XMSS:
1812 case KEY_XMSS_CERT:
1813 if ((n = sshkey_new(k->type)) == NULL)
1814 return SSH_ERR_ALLOC_FAIL;
1815 if ((ret = sshkey_xmss_init(n, k->xmss_name)) != 0) {
1816 sshkey_free(n);
1817 return ret;
1818 }
1819 if (k->xmss_pk != NULL) {
1820 size_t pklen = sshkey_xmss_pklen(k);
1821 if (pklen == 0 || sshkey_xmss_pklen(n) != pklen) {
1822 sshkey_free(n);
1823 return SSH_ERR_INTERNAL_ERROR;
1824 }
1825 if ((n->xmss_pk = malloc(pklen)) == NULL) {
1826 sshkey_free(n);
1827 return SSH_ERR_ALLOC_FAIL;
1828 }
1829 memcpy(n->xmss_pk, k->xmss_pk, pklen);
1830 }
1831 break;
1832#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10001833 default:
1834 return SSH_ERR_KEY_TYPE_UNKNOWN;
1835 }
1836 if (sshkey_is_cert(k)) {
1837 if ((ret = sshkey_cert_copy(k, n)) != 0) {
1838 sshkey_free(n);
1839 return ret;
1840 }
1841 }
1842 *pkp = n;
1843 return 0;
1844}
1845
1846static int
djm@openbsd.org60b18252015-01-26 02:59:11 +00001847cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
Damien Miller86687062014-07-02 15:28:02 +10001848{
djm@openbsd.org60b18252015-01-26 02:59:11 +00001849 struct sshbuf *principals = NULL, *crit = NULL;
1850 struct sshbuf *exts = NULL, *ca = NULL;
1851 u_char *sig = NULL;
1852 size_t signed_len = 0, slen = 0, kidlen = 0;
Damien Miller86687062014-07-02 15:28:02 +10001853 int ret = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +10001854
1855 /* Copy the entire key blob for verification and later serialisation */
djm@openbsd.org60b18252015-01-26 02:59:11 +00001856 if ((ret = sshbuf_putb(key->cert->certblob, certbuf)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001857 return ret;
1858
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00001859 /* Parse body of certificate up to signature */
1860 if ((ret = sshbuf_get_u64(b, &key->cert->serial)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10001861 (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 ||
1862 (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 ||
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001863 (ret = sshbuf_froms(b, &principals)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10001864 (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 ||
1865 (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 ||
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001866 (ret = sshbuf_froms(b, &crit)) != 0 ||
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00001867 (ret = sshbuf_froms(b, &exts)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10001868 (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 ||
djm@openbsd.org60b18252015-01-26 02:59:11 +00001869 (ret = sshbuf_froms(b, &ca)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10001870 /* XXX debug print error for ret */
1871 ret = SSH_ERR_INVALID_FORMAT;
1872 goto out;
1873 }
1874
1875 /* Signature is left in the buffer so we can calculate this length */
1876 signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b);
1877
1878 if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) {
1879 ret = SSH_ERR_INVALID_FORMAT;
1880 goto out;
1881 }
1882
1883 if (key->cert->type != SSH2_CERT_TYPE_USER &&
1884 key->cert->type != SSH2_CERT_TYPE_HOST) {
1885 ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE;
1886 goto out;
1887 }
1888
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001889 /* Parse principals section */
1890 while (sshbuf_len(principals) > 0) {
1891 char *principal = NULL;
1892 char **oprincipals = NULL;
1893
Damien Miller86687062014-07-02 15:28:02 +10001894 if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) {
1895 ret = SSH_ERR_INVALID_FORMAT;
1896 goto out;
1897 }
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001898 if ((ret = sshbuf_get_cstring(principals, &principal,
1899 NULL)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10001900 ret = SSH_ERR_INVALID_FORMAT;
1901 goto out;
1902 }
1903 oprincipals = key->cert->principals;
deraadt@openbsd.org9e509d42017-05-31 09:15:42 +00001904 key->cert->principals = recallocarray(key->cert->principals,
1905 key->cert->nprincipals, key->cert->nprincipals + 1,
1906 sizeof(*key->cert->principals));
Damien Miller86687062014-07-02 15:28:02 +10001907 if (key->cert->principals == NULL) {
1908 free(principal);
1909 key->cert->principals = oprincipals;
1910 ret = SSH_ERR_ALLOC_FAIL;
1911 goto out;
1912 }
1913 key->cert->principals[key->cert->nprincipals++] = principal;
1914 }
1915
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001916 /*
1917 * Stash a copies of the critical options and extensions sections
1918 * for later use.
1919 */
1920 if ((ret = sshbuf_putb(key->cert->critical, crit)) != 0 ||
1921 (exts != NULL &&
1922 (ret = sshbuf_putb(key->cert->extensions, exts)) != 0))
Damien Miller86687062014-07-02 15:28:02 +10001923 goto out;
1924
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001925 /*
1926 * Validate critical options and extensions sections format.
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001927 */
1928 while (sshbuf_len(crit) != 0) {
1929 if ((ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0 ||
1930 (ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0) {
1931 sshbuf_reset(key->cert->critical);
Damien Miller86687062014-07-02 15:28:02 +10001932 ret = SSH_ERR_INVALID_FORMAT;
1933 goto out;
1934 }
1935 }
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001936 while (exts != NULL && sshbuf_len(exts) != 0) {
1937 if ((ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0 ||
1938 (ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0) {
1939 sshbuf_reset(key->cert->extensions);
Damien Miller86687062014-07-02 15:28:02 +10001940 ret = SSH_ERR_INVALID_FORMAT;
1941 goto out;
1942 }
1943 }
Damien Miller86687062014-07-02 15:28:02 +10001944
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001945 /* Parse CA key and check signature */
djm@openbsd.org60b18252015-01-26 02:59:11 +00001946 if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10001947 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
1948 goto out;
1949 }
1950 if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) {
1951 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
1952 goto out;
1953 }
Damien Miller86687062014-07-02 15:28:02 +10001954 if ((ret = sshkey_verify(key->cert->signature_key, sig, slen,
djm@openbsd.org04c7e282017-12-18 02:25:15 +00001955 sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001956 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001957
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001958 /* Success */
1959 ret = 0;
Damien Miller86687062014-07-02 15:28:02 +10001960 out:
djm@openbsd.org60b18252015-01-26 02:59:11 +00001961 sshbuf_free(ca);
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001962 sshbuf_free(crit);
1963 sshbuf_free(exts);
1964 sshbuf_free(principals);
Damien Miller86687062014-07-02 15:28:02 +10001965 free(sig);
1966 return ret;
1967}
1968
1969static int
djm@openbsd.org60b18252015-01-26 02:59:11 +00001970sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
1971 int allow_cert)
Damien Miller86687062014-07-02 15:28:02 +10001972{
djm@openbsd.org54924b52015-01-14 10:46:28 +00001973 int type, ret = SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001974 char *ktype = NULL, *curve = NULL, *xmss_name = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001975 struct sshkey *key = NULL;
1976 size_t len;
1977 u_char *pk = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00001978 struct sshbuf *copy;
Damien Miller86687062014-07-02 15:28:02 +10001979#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
1980 EC_POINT *q = NULL;
1981#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
1982
1983#ifdef DEBUG_PK /* XXX */
djm@openbsd.org60b18252015-01-26 02:59:11 +00001984 sshbuf_dump(b, stderr);
Damien Miller86687062014-07-02 15:28:02 +10001985#endif
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00001986 if (keyp != NULL)
1987 *keyp = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00001988 if ((copy = sshbuf_fromb(b)) == NULL) {
1989 ret = SSH_ERR_ALLOC_FAIL;
1990 goto out;
1991 }
Damien Miller86687062014-07-02 15:28:02 +10001992 if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
1993 ret = SSH_ERR_INVALID_FORMAT;
1994 goto out;
1995 }
1996
1997 type = sshkey_type_from_name(ktype);
Damien Miller86687062014-07-02 15:28:02 +10001998 if (!allow_cert && sshkey_type_is_cert(type)) {
1999 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2000 goto out;
2001 }
2002 switch (type) {
2003#ifdef WITH_OPENSSL
2004 case KEY_RSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002005 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002006 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2007 ret = SSH_ERR_INVALID_FORMAT;
2008 goto out;
2009 }
2010 /* FALLTHROUGH */
2011 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10002012 if ((key = sshkey_new(type)) == NULL) {
2013 ret = SSH_ERR_ALLOC_FAIL;
2014 goto out;
2015 }
djm@openbsd.org3f4ea3c2015-04-03 22:17:27 +00002016 if (sshbuf_get_bignum2(b, key->rsa->e) != 0 ||
2017 sshbuf_get_bignum2(b, key->rsa->n) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002018 ret = SSH_ERR_INVALID_FORMAT;
2019 goto out;
2020 }
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00002021 if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
2022 ret = SSH_ERR_KEY_LENGTH;
2023 goto out;
2024 }
Damien Miller86687062014-07-02 15:28:02 +10002025#ifdef DEBUG_PK
2026 RSA_print_fp(stderr, key->rsa, 8);
2027#endif
2028 break;
2029 case KEY_DSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002030 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002031 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2032 ret = SSH_ERR_INVALID_FORMAT;
2033 goto out;
2034 }
2035 /* FALLTHROUGH */
2036 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +10002037 if ((key = sshkey_new(type)) == NULL) {
2038 ret = SSH_ERR_ALLOC_FAIL;
2039 goto out;
2040 }
djm@openbsd.org3f4ea3c2015-04-03 22:17:27 +00002041 if (sshbuf_get_bignum2(b, key->dsa->p) != 0 ||
2042 sshbuf_get_bignum2(b, key->dsa->q) != 0 ||
2043 sshbuf_get_bignum2(b, key->dsa->g) != 0 ||
2044 sshbuf_get_bignum2(b, key->dsa->pub_key) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002045 ret = SSH_ERR_INVALID_FORMAT;
2046 goto out;
2047 }
2048#ifdef DEBUG_PK
2049 DSA_print_fp(stderr, key->dsa, 8);
2050#endif
2051 break;
2052 case KEY_ECDSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002053 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002054 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2055 ret = SSH_ERR_INVALID_FORMAT;
2056 goto out;
2057 }
2058 /* FALLTHROUGH */
2059# ifdef OPENSSL_HAS_ECC
2060 case KEY_ECDSA:
2061 if ((key = sshkey_new(type)) == NULL) {
2062 ret = SSH_ERR_ALLOC_FAIL;
2063 goto out;
2064 }
djm@openbsd.org54924b52015-01-14 10:46:28 +00002065 key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype);
Damien Miller86687062014-07-02 15:28:02 +10002066 if (sshbuf_get_cstring(b, &curve, NULL) != 0) {
2067 ret = SSH_ERR_INVALID_FORMAT;
2068 goto out;
2069 }
2070 if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
2071 ret = SSH_ERR_EC_CURVE_MISMATCH;
2072 goto out;
2073 }
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00002074 EC_KEY_free(key->ecdsa);
Damien Miller86687062014-07-02 15:28:02 +10002075 if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid))
2076 == NULL) {
2077 ret = SSH_ERR_EC_CURVE_INVALID;
2078 goto out;
2079 }
2080 if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) {
2081 ret = SSH_ERR_ALLOC_FAIL;
2082 goto out;
2083 }
2084 if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) {
2085 ret = SSH_ERR_INVALID_FORMAT;
2086 goto out;
2087 }
2088 if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
2089 q) != 0) {
2090 ret = SSH_ERR_KEY_INVALID_EC_VALUE;
2091 goto out;
2092 }
2093 if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
2094 /* XXX assume it is a allocation error */
2095 ret = SSH_ERR_ALLOC_FAIL;
2096 goto out;
2097 }
2098#ifdef DEBUG_PK
2099 sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q);
2100#endif
2101 break;
2102# endif /* OPENSSL_HAS_ECC */
2103#endif /* WITH_OPENSSL */
2104 case KEY_ED25519_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002105 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002106 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2107 ret = SSH_ERR_INVALID_FORMAT;
2108 goto out;
2109 }
2110 /* FALLTHROUGH */
2111 case KEY_ED25519:
2112 if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
2113 goto out;
2114 if (len != ED25519_PK_SZ) {
2115 ret = SSH_ERR_INVALID_FORMAT;
2116 goto out;
2117 }
2118 if ((key = sshkey_new(type)) == NULL) {
2119 ret = SSH_ERR_ALLOC_FAIL;
2120 goto out;
2121 }
2122 key->ed25519_pk = pk;
2123 pk = NULL;
2124 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002125#ifdef WITH_XMSS
2126 case KEY_XMSS_CERT:
2127 /* Skip nonce */
2128 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2129 ret = SSH_ERR_INVALID_FORMAT;
2130 goto out;
2131 }
2132 /* FALLTHROUGH */
2133 case KEY_XMSS:
2134 if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0)
2135 goto out;
2136 if ((key = sshkey_new(type)) == NULL) {
2137 ret = SSH_ERR_ALLOC_FAIL;
2138 goto out;
2139 }
2140 if ((ret = sshkey_xmss_init(key, xmss_name)) != 0)
2141 goto out;
2142 if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
2143 goto out;
2144 if (len == 0 || len != sshkey_xmss_pklen(key)) {
2145 ret = SSH_ERR_INVALID_FORMAT;
2146 goto out;
2147 }
2148 key->xmss_pk = pk;
2149 pk = NULL;
2150 if (type != KEY_XMSS_CERT &&
2151 (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0)
2152 goto out;
2153 break;
2154#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002155 case KEY_UNSPEC:
Damien Miller86687062014-07-02 15:28:02 +10002156 default:
2157 ret = SSH_ERR_KEY_TYPE_UNKNOWN;
2158 goto out;
2159 }
2160
2161 /* Parse certificate potion */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002162 if (sshkey_is_cert(key) && (ret = cert_parse(b, key, copy)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002163 goto out;
2164
2165 if (key != NULL && sshbuf_len(b) != 0) {
2166 ret = SSH_ERR_INVALID_FORMAT;
2167 goto out;
2168 }
2169 ret = 0;
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00002170 if (keyp != NULL) {
2171 *keyp = key;
2172 key = NULL;
2173 }
Damien Miller86687062014-07-02 15:28:02 +10002174 out:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002175 sshbuf_free(copy);
Damien Miller86687062014-07-02 15:28:02 +10002176 sshkey_free(key);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002177 free(xmss_name);
Damien Miller86687062014-07-02 15:28:02 +10002178 free(ktype);
2179 free(curve);
2180 free(pk);
2181#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00002182 EC_POINT_free(q);
Damien Miller86687062014-07-02 15:28:02 +10002183#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
2184 return ret;
2185}
2186
2187int
2188sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp)
2189{
djm@openbsd.org60b18252015-01-26 02:59:11 +00002190 struct sshbuf *b;
2191 int r;
2192
2193 if ((b = sshbuf_from(blob, blen)) == NULL)
2194 return SSH_ERR_ALLOC_FAIL;
2195 r = sshkey_from_blob_internal(b, keyp, 1);
2196 sshbuf_free(b);
2197 return r;
2198}
2199
2200int
2201sshkey_fromb(struct sshbuf *b, struct sshkey **keyp)
2202{
2203 return sshkey_from_blob_internal(b, keyp, 1);
2204}
2205
2206int
2207sshkey_froms(struct sshbuf *buf, struct sshkey **keyp)
2208{
2209 struct sshbuf *b;
2210 int r;
2211
2212 if ((r = sshbuf_froms(buf, &b)) != 0)
2213 return r;
2214 r = sshkey_from_blob_internal(b, keyp, 1);
2215 sshbuf_free(b);
2216 return r;
Damien Miller86687062014-07-02 15:28:02 +10002217}
2218
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002219static int
2220get_sigtype(const u_char *sig, size_t siglen, char **sigtypep)
djm@openbsd.org931c78d2017-12-18 02:22:29 +00002221{
2222 int r;
2223 struct sshbuf *b = NULL;
2224 char *sigtype = NULL;
2225
2226 if (sigtypep != NULL)
2227 *sigtypep = NULL;
2228 if ((b = sshbuf_from(sig, siglen)) == NULL)
2229 return SSH_ERR_ALLOC_FAIL;
2230 if ((r = sshbuf_get_cstring(b, &sigtype, NULL)) != 0)
2231 goto out;
2232 /* success */
2233 if (sigtypep != NULL) {
2234 *sigtypep = sigtype;
2235 sigtype = NULL;
2236 }
2237 r = 0;
2238 out:
2239 free(sigtype);
2240 sshbuf_free(b);
2241 return r;
2242}
2243
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002244/*
2245 * Returns the expected signature algorithm for a given public key algorithm.
2246 */
djm@openbsd.orgb4d4eda2018-07-03 13:20:25 +00002247const char *
2248sshkey_sigalg_by_name(const char *name)
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002249{
2250 const struct keytype *kt;
2251
2252 for (kt = keytypes; kt->type != -1; kt++) {
2253 if (strcmp(kt->name, name) != 0)
2254 continue;
2255 if (kt->sigalg != NULL)
2256 return kt->sigalg;
2257 if (!kt->cert)
2258 return kt->name;
2259 return sshkey_ssh_name_from_type_nid(
2260 sshkey_type_plain(kt->type), kt->nid);
2261 }
2262 return NULL;
2263}
2264
2265/*
2266 * Verifies that the signature algorithm appearing inside the signature blob
2267 * matches that which was requested.
2268 */
2269int
2270sshkey_check_sigtype(const u_char *sig, size_t siglen,
2271 const char *requested_alg)
2272{
2273 const char *expected_alg;
2274 char *sigtype = NULL;
2275 int r;
2276
2277 if (requested_alg == NULL)
2278 return 0;
djm@openbsd.orgb4d4eda2018-07-03 13:20:25 +00002279 if ((expected_alg = sshkey_sigalg_by_name(requested_alg)) == NULL)
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002280 return SSH_ERR_INVALID_ARGUMENT;
2281 if ((r = get_sigtype(sig, siglen, &sigtype)) != 0)
2282 return r;
2283 r = strcmp(expected_alg, sigtype) == 0;
2284 free(sigtype);
2285 return r ? 0 : SSH_ERR_SIGN_ALG_UNSUPPORTED;
2286}
2287
djm@openbsd.org931c78d2017-12-18 02:22:29 +00002288int
Damien Miller86687062014-07-02 15:28:02 +10002289sshkey_sign(const struct sshkey *key,
2290 u_char **sigp, size_t *lenp,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +00002291 const u_char *data, size_t datalen, const char *alg, u_int compat)
Damien Miller86687062014-07-02 15:28:02 +10002292{
2293 if (sigp != NULL)
2294 *sigp = NULL;
2295 if (lenp != NULL)
2296 *lenp = 0;
2297 if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
2298 return SSH_ERR_INVALID_ARGUMENT;
2299 switch (key->type) {
2300#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002301 case KEY_DSA_CERT:
2302 case KEY_DSA:
2303 return ssh_dss_sign(key, sigp, lenp, data, datalen, compat);
2304# ifdef OPENSSL_HAS_ECC
2305 case KEY_ECDSA_CERT:
2306 case KEY_ECDSA:
2307 return ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat);
2308# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002309 case KEY_RSA_CERT:
2310 case KEY_RSA:
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +00002311 return ssh_rsa_sign(key, sigp, lenp, data, datalen, alg);
Damien Miller86687062014-07-02 15:28:02 +10002312#endif /* WITH_OPENSSL */
2313 case KEY_ED25519:
2314 case KEY_ED25519_CERT:
2315 return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002316#ifdef WITH_XMSS
2317 case KEY_XMSS:
2318 case KEY_XMSS_CERT:
2319 return ssh_xmss_sign(key, sigp, lenp, data, datalen, compat);
2320#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002321 default:
2322 return SSH_ERR_KEY_TYPE_UNKNOWN;
2323 }
2324}
2325
2326/*
2327 * ssh_key_verify returns 0 for a correct signature and < 0 on error.
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002328 * If "alg" specified, then the signature must use that algorithm.
Damien Miller86687062014-07-02 15:28:02 +10002329 */
2330int
2331sshkey_verify(const struct sshkey *key,
2332 const u_char *sig, size_t siglen,
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002333 const u_char *data, size_t dlen, const char *alg, u_int compat)
Damien Miller86687062014-07-02 15:28:02 +10002334{
djm@openbsd.org4cf87f42014-12-10 01:24:09 +00002335 if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE)
Damien Miller86687062014-07-02 15:28:02 +10002336 return SSH_ERR_INVALID_ARGUMENT;
2337 switch (key->type) {
2338#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002339 case KEY_DSA_CERT:
2340 case KEY_DSA:
2341 return ssh_dss_verify(key, sig, siglen, data, dlen, compat);
2342# ifdef OPENSSL_HAS_ECC
2343 case KEY_ECDSA_CERT:
2344 case KEY_ECDSA:
2345 return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat);
2346# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002347 case KEY_RSA_CERT:
2348 case KEY_RSA:
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002349 return ssh_rsa_verify(key, sig, siglen, data, dlen, alg);
Damien Miller86687062014-07-02 15:28:02 +10002350#endif /* WITH_OPENSSL */
2351 case KEY_ED25519:
2352 case KEY_ED25519_CERT:
2353 return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002354#ifdef WITH_XMSS
2355 case KEY_XMSS:
2356 case KEY_XMSS_CERT:
2357 return ssh_xmss_verify(key, sig, siglen, data, dlen, compat);
2358#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002359 default:
2360 return SSH_ERR_KEY_TYPE_UNKNOWN;
2361 }
2362}
2363
2364/* Converts a private to a public key */
2365int
2366sshkey_demote(const struct sshkey *k, struct sshkey **dkp)
2367{
2368 struct sshkey *pk;
2369 int ret = SSH_ERR_INTERNAL_ERROR;
2370
djm@openbsd.org1a2663a2015-10-15 23:08:23 +00002371 *dkp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002372 if ((pk = calloc(1, sizeof(*pk))) == NULL)
2373 return SSH_ERR_ALLOC_FAIL;
2374 pk->type = k->type;
2375 pk->flags = k->flags;
2376 pk->ecdsa_nid = k->ecdsa_nid;
2377 pk->dsa = NULL;
2378 pk->ecdsa = NULL;
2379 pk->rsa = NULL;
2380 pk->ed25519_pk = NULL;
2381 pk->ed25519_sk = NULL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002382 pk->xmss_pk = NULL;
2383 pk->xmss_sk = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002384
2385 switch (k->type) {
2386#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002387 case KEY_RSA_CERT:
2388 if ((ret = sshkey_cert_copy(k, pk)) != 0)
2389 goto fail;
2390 /* FALLTHROUGH */
Damien Miller86687062014-07-02 15:28:02 +10002391 case KEY_RSA:
2392 if ((pk->rsa = RSA_new()) == NULL ||
2393 (pk->rsa->e = BN_dup(k->rsa->e)) == NULL ||
2394 (pk->rsa->n = BN_dup(k->rsa->n)) == NULL) {
2395 ret = SSH_ERR_ALLOC_FAIL;
2396 goto fail;
2397 }
2398 break;
Damien Miller86687062014-07-02 15:28:02 +10002399 case KEY_DSA_CERT:
2400 if ((ret = sshkey_cert_copy(k, pk)) != 0)
2401 goto fail;
2402 /* FALLTHROUGH */
2403 case KEY_DSA:
2404 if ((pk->dsa = DSA_new()) == NULL ||
2405 (pk->dsa->p = BN_dup(k->dsa->p)) == NULL ||
2406 (pk->dsa->q = BN_dup(k->dsa->q)) == NULL ||
2407 (pk->dsa->g = BN_dup(k->dsa->g)) == NULL ||
2408 (pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) {
2409 ret = SSH_ERR_ALLOC_FAIL;
2410 goto fail;
2411 }
2412 break;
2413 case KEY_ECDSA_CERT:
2414 if ((ret = sshkey_cert_copy(k, pk)) != 0)
2415 goto fail;
2416 /* FALLTHROUGH */
2417# ifdef OPENSSL_HAS_ECC
2418 case KEY_ECDSA:
2419 pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid);
2420 if (pk->ecdsa == NULL) {
2421 ret = SSH_ERR_ALLOC_FAIL;
2422 goto fail;
2423 }
2424 if (EC_KEY_set_public_key(pk->ecdsa,
2425 EC_KEY_get0_public_key(k->ecdsa)) != 1) {
2426 ret = SSH_ERR_LIBCRYPTO_ERROR;
2427 goto fail;
2428 }
2429 break;
2430# endif /* OPENSSL_HAS_ECC */
2431#endif /* WITH_OPENSSL */
2432 case KEY_ED25519_CERT:
2433 if ((ret = sshkey_cert_copy(k, pk)) != 0)
2434 goto fail;
2435 /* FALLTHROUGH */
2436 case KEY_ED25519:
2437 if (k->ed25519_pk != NULL) {
2438 if ((pk->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
2439 ret = SSH_ERR_ALLOC_FAIL;
2440 goto fail;
2441 }
2442 memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
2443 }
2444 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002445#ifdef WITH_XMSS
2446 case KEY_XMSS_CERT:
2447 if ((ret = sshkey_cert_copy(k, pk)) != 0)
2448 goto fail;
2449 /* FALLTHROUGH */
2450 case KEY_XMSS:
2451 if ((ret = sshkey_xmss_init(pk, k->xmss_name)) != 0)
2452 goto fail;
2453 if (k->xmss_pk != NULL) {
2454 size_t pklen = sshkey_xmss_pklen(k);
2455
2456 if (pklen == 0 || sshkey_xmss_pklen(pk) != pklen) {
2457 ret = SSH_ERR_INTERNAL_ERROR;
2458 goto fail;
2459 }
2460 if ((pk->xmss_pk = malloc(pklen)) == NULL) {
2461 ret = SSH_ERR_ALLOC_FAIL;
2462 goto fail;
2463 }
2464 memcpy(pk->xmss_pk, k->xmss_pk, pklen);
2465 }
2466 break;
2467#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002468 default:
2469 ret = SSH_ERR_KEY_TYPE_UNKNOWN;
2470 fail:
2471 sshkey_free(pk);
2472 return ret;
2473 }
2474 *dkp = pk;
2475 return 0;
2476}
2477
2478/* Convert a plain key to their _CERT equivalent */
2479int
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002480sshkey_to_certified(struct sshkey *k)
Damien Miller86687062014-07-02 15:28:02 +10002481{
2482 int newtype;
2483
2484 switch (k->type) {
2485#ifdef WITH_OPENSSL
2486 case KEY_RSA:
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002487 newtype = KEY_RSA_CERT;
Damien Miller86687062014-07-02 15:28:02 +10002488 break;
2489 case KEY_DSA:
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002490 newtype = KEY_DSA_CERT;
Damien Miller86687062014-07-02 15:28:02 +10002491 break;
2492 case KEY_ECDSA:
Damien Miller86687062014-07-02 15:28:02 +10002493 newtype = KEY_ECDSA_CERT;
2494 break;
2495#endif /* WITH_OPENSSL */
2496 case KEY_ED25519:
Damien Miller86687062014-07-02 15:28:02 +10002497 newtype = KEY_ED25519_CERT;
2498 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002499#ifdef WITH_XMSS
2500 case KEY_XMSS:
2501 newtype = KEY_XMSS_CERT;
2502 break;
2503#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002504 default:
2505 return SSH_ERR_INVALID_ARGUMENT;
2506 }
2507 if ((k->cert = cert_new()) == NULL)
2508 return SSH_ERR_ALLOC_FAIL;
2509 k->type = newtype;
2510 return 0;
2511}
2512
2513/* Convert a certificate to its raw key equivalent */
2514int
2515sshkey_drop_cert(struct sshkey *k)
2516{
2517 if (!sshkey_type_is_cert(k->type))
2518 return SSH_ERR_KEY_TYPE_UNKNOWN;
2519 cert_free(k->cert);
2520 k->cert = NULL;
2521 k->type = sshkey_type_plain(k->type);
2522 return 0;
2523}
2524
2525/* Sign a certified key, (re-)generating the signed certblob. */
2526int
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002527sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
2528 sshkey_certify_signer *signer, void *signer_ctx)
Damien Miller86687062014-07-02 15:28:02 +10002529{
2530 struct sshbuf *principals = NULL;
2531 u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32];
2532 size_t i, ca_len, sig_len;
2533 int ret = SSH_ERR_INTERNAL_ERROR;
2534 struct sshbuf *cert;
2535
2536 if (k == NULL || k->cert == NULL ||
2537 k->cert->certblob == NULL || ca == NULL)
2538 return SSH_ERR_INVALID_ARGUMENT;
2539 if (!sshkey_is_cert(k))
2540 return SSH_ERR_KEY_TYPE_UNKNOWN;
2541 if (!sshkey_type_is_valid_ca(ca->type))
2542 return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2543
2544 if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0)
2545 return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2546
2547 cert = k->cert->certblob; /* for readability */
2548 sshbuf_reset(cert);
2549 if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0)
2550 goto out;
2551
2552 /* -v01 certs put nonce first */
2553 arc4random_buf(&nonce, sizeof(nonce));
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002554 if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0)
2555 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002556
2557 /* XXX this substantially duplicates to_blob(); refactor */
2558 switch (k->type) {
2559#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002560 case KEY_DSA_CERT:
2561 if ((ret = sshbuf_put_bignum2(cert, k->dsa->p)) != 0 ||
2562 (ret = sshbuf_put_bignum2(cert, k->dsa->q)) != 0 ||
2563 (ret = sshbuf_put_bignum2(cert, k->dsa->g)) != 0 ||
2564 (ret = sshbuf_put_bignum2(cert, k->dsa->pub_key)) != 0)
2565 goto out;
2566 break;
2567# ifdef OPENSSL_HAS_ECC
2568 case KEY_ECDSA_CERT:
2569 if ((ret = sshbuf_put_cstring(cert,
2570 sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 ||
2571 (ret = sshbuf_put_ec(cert,
2572 EC_KEY_get0_public_key(k->ecdsa),
2573 EC_KEY_get0_group(k->ecdsa))) != 0)
2574 goto out;
2575 break;
2576# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002577 case KEY_RSA_CERT:
2578 if ((ret = sshbuf_put_bignum2(cert, k->rsa->e)) != 0 ||
2579 (ret = sshbuf_put_bignum2(cert, k->rsa->n)) != 0)
2580 goto out;
2581 break;
2582#endif /* WITH_OPENSSL */
2583 case KEY_ED25519_CERT:
2584 if ((ret = sshbuf_put_string(cert,
2585 k->ed25519_pk, ED25519_PK_SZ)) != 0)
2586 goto out;
2587 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002588#ifdef WITH_XMSS
2589 case KEY_XMSS_CERT:
2590 if (k->xmss_name == NULL) {
2591 ret = SSH_ERR_INVALID_ARGUMENT;
2592 goto out;
2593 }
2594 if ((ret = sshbuf_put_cstring(cert, k->xmss_name)) ||
2595 (ret = sshbuf_put_string(cert,
2596 k->xmss_pk, sshkey_xmss_pklen(k))) != 0)
2597 goto out;
2598 break;
2599#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002600 default:
2601 ret = SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org55e5bde2015-03-06 01:40:56 +00002602 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002603 }
2604
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002605 if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0 ||
2606 (ret = sshbuf_put_u32(cert, k->cert->type)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002607 (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0)
2608 goto out;
2609
2610 if ((principals = sshbuf_new()) == NULL) {
2611 ret = SSH_ERR_ALLOC_FAIL;
2612 goto out;
2613 }
2614 for (i = 0; i < k->cert->nprincipals; i++) {
2615 if ((ret = sshbuf_put_cstring(principals,
2616 k->cert->principals[i])) != 0)
2617 goto out;
2618 }
2619 if ((ret = sshbuf_put_stringb(cert, principals)) != 0 ||
2620 (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 ||
2621 (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 ||
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002622 (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0 ||
2623 (ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0 ||
2624 (ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */
Damien Miller86687062014-07-02 15:28:02 +10002625 (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0)
2626 goto out;
2627
2628 /* Sign the whole mess */
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002629 if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert),
2630 sshbuf_len(cert), alg, 0, signer_ctx)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002631 goto out;
2632
2633 /* Append signature and we are done */
2634 if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0)
2635 goto out;
2636 ret = 0;
2637 out:
2638 if (ret != 0)
2639 sshbuf_reset(cert);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00002640 free(sig_blob);
2641 free(ca_blob);
mmcc@openbsd.org52d70782015-12-11 04:21:11 +00002642 sshbuf_free(principals);
Damien Miller86687062014-07-02 15:28:02 +10002643 return ret;
2644}
2645
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002646static int
2647default_key_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
2648 const u_char *data, size_t datalen,
2649 const char *alg, u_int compat, void *ctx)
2650{
2651 if (ctx != NULL)
2652 return SSH_ERR_INVALID_ARGUMENT;
2653 return sshkey_sign(key, sigp, lenp, data, datalen, alg, compat);
2654}
2655
2656int
2657sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg)
2658{
2659 return sshkey_certify_custom(k, ca, alg, default_key_sign, NULL);
2660}
2661
Damien Miller86687062014-07-02 15:28:02 +10002662int
2663sshkey_cert_check_authority(const struct sshkey *k,
2664 int want_host, int require_principal,
2665 const char *name, const char **reason)
2666{
2667 u_int i, principal_matches;
2668 time_t now = time(NULL);
2669
2670 if (reason != NULL)
2671 *reason = NULL;
2672
2673 if (want_host) {
2674 if (k->cert->type != SSH2_CERT_TYPE_HOST) {
2675 *reason = "Certificate invalid: not a host certificate";
2676 return SSH_ERR_KEY_CERT_INVALID;
2677 }
2678 } else {
2679 if (k->cert->type != SSH2_CERT_TYPE_USER) {
2680 *reason = "Certificate invalid: not a user certificate";
2681 return SSH_ERR_KEY_CERT_INVALID;
2682 }
2683 }
2684 if (now < 0) {
2685 /* yikes - system clock before epoch! */
2686 *reason = "Certificate invalid: not yet valid";
2687 return SSH_ERR_KEY_CERT_INVALID;
2688 }
2689 if ((u_int64_t)now < k->cert->valid_after) {
2690 *reason = "Certificate invalid: not yet valid";
2691 return SSH_ERR_KEY_CERT_INVALID;
2692 }
2693 if ((u_int64_t)now >= k->cert->valid_before) {
2694 *reason = "Certificate invalid: expired";
2695 return SSH_ERR_KEY_CERT_INVALID;
2696 }
2697 if (k->cert->nprincipals == 0) {
2698 if (require_principal) {
2699 *reason = "Certificate lacks principal list";
2700 return SSH_ERR_KEY_CERT_INVALID;
2701 }
2702 } else if (name != NULL) {
2703 principal_matches = 0;
2704 for (i = 0; i < k->cert->nprincipals; i++) {
2705 if (strcmp(name, k->cert->principals[i]) == 0) {
2706 principal_matches = 1;
2707 break;
2708 }
2709 }
2710 if (!principal_matches) {
2711 *reason = "Certificate invalid: name is not a listed "
2712 "principal";
2713 return SSH_ERR_KEY_CERT_INVALID;
2714 }
2715 }
2716 return 0;
2717}
2718
djm@openbsd.org499cf362015-11-19 01:08:55 +00002719size_t
2720sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l)
2721{
2722 char from[32], to[32], ret[64];
2723 time_t tt;
2724 struct tm *tm;
2725
2726 *from = *to = '\0';
2727 if (cert->valid_after == 0 &&
2728 cert->valid_before == 0xffffffffffffffffULL)
2729 return strlcpy(s, "forever", l);
2730
2731 if (cert->valid_after != 0) {
2732 /* XXX revisit INT_MAX in 2038 :) */
2733 tt = cert->valid_after > INT_MAX ?
2734 INT_MAX : cert->valid_after;
2735 tm = localtime(&tt);
2736 strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm);
2737 }
2738 if (cert->valid_before != 0xffffffffffffffffULL) {
2739 /* XXX revisit INT_MAX in 2038 :) */
2740 tt = cert->valid_before > INT_MAX ?
2741 INT_MAX : cert->valid_before;
2742 tm = localtime(&tt);
2743 strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm);
2744 }
2745
2746 if (cert->valid_after == 0)
2747 snprintf(ret, sizeof(ret), "before %s", to);
2748 else if (cert->valid_before == 0xffffffffffffffffULL)
2749 snprintf(ret, sizeof(ret), "after %s", from);
2750 else
2751 snprintf(ret, sizeof(ret), "from %s to %s", from, to);
2752
2753 return strlcpy(s, ret, l);
2754}
2755
Damien Miller86687062014-07-02 15:28:02 +10002756int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002757sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *b,
2758 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +10002759{
2760 int r = SSH_ERR_INTERNAL_ERROR;
2761
2762 if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0)
2763 goto out;
2764 switch (key->type) {
2765#ifdef WITH_OPENSSL
2766 case KEY_RSA:
2767 if ((r = sshbuf_put_bignum2(b, key->rsa->n)) != 0 ||
2768 (r = sshbuf_put_bignum2(b, key->rsa->e)) != 0 ||
2769 (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 ||
2770 (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 ||
2771 (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 ||
2772 (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0)
2773 goto out;
2774 break;
Damien Miller86687062014-07-02 15:28:02 +10002775 case KEY_RSA_CERT:
2776 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
2777 r = SSH_ERR_INVALID_ARGUMENT;
2778 goto out;
2779 }
2780 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2781 (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 ||
2782 (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 ||
2783 (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 ||
2784 (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0)
2785 goto out;
2786 break;
2787 case KEY_DSA:
2788 if ((r = sshbuf_put_bignum2(b, key->dsa->p)) != 0 ||
2789 (r = sshbuf_put_bignum2(b, key->dsa->q)) != 0 ||
2790 (r = sshbuf_put_bignum2(b, key->dsa->g)) != 0 ||
2791 (r = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0 ||
2792 (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0)
2793 goto out;
2794 break;
Damien Miller86687062014-07-02 15:28:02 +10002795 case KEY_DSA_CERT:
2796 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
2797 r = SSH_ERR_INVALID_ARGUMENT;
2798 goto out;
2799 }
2800 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2801 (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0)
2802 goto out;
2803 break;
2804# ifdef OPENSSL_HAS_ECC
2805 case KEY_ECDSA:
2806 if ((r = sshbuf_put_cstring(b,
2807 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
2808 (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 ||
2809 (r = sshbuf_put_bignum2(b,
2810 EC_KEY_get0_private_key(key->ecdsa))) != 0)
2811 goto out;
2812 break;
2813 case KEY_ECDSA_CERT:
2814 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
2815 r = SSH_ERR_INVALID_ARGUMENT;
2816 goto out;
2817 }
2818 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2819 (r = sshbuf_put_bignum2(b,
2820 EC_KEY_get0_private_key(key->ecdsa))) != 0)
2821 goto out;
2822 break;
2823# endif /* OPENSSL_HAS_ECC */
2824#endif /* WITH_OPENSSL */
2825 case KEY_ED25519:
2826 if ((r = sshbuf_put_string(b, key->ed25519_pk,
2827 ED25519_PK_SZ)) != 0 ||
2828 (r = sshbuf_put_string(b, key->ed25519_sk,
2829 ED25519_SK_SZ)) != 0)
2830 goto out;
2831 break;
2832 case KEY_ED25519_CERT:
2833 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
2834 r = SSH_ERR_INVALID_ARGUMENT;
2835 goto out;
2836 }
2837 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2838 (r = sshbuf_put_string(b, key->ed25519_pk,
2839 ED25519_PK_SZ)) != 0 ||
2840 (r = sshbuf_put_string(b, key->ed25519_sk,
2841 ED25519_SK_SZ)) != 0)
2842 goto out;
2843 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002844#ifdef WITH_XMSS
2845 case KEY_XMSS:
2846 if (key->xmss_name == NULL) {
2847 r = SSH_ERR_INVALID_ARGUMENT;
2848 goto out;
2849 }
2850 if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
2851 (r = sshbuf_put_string(b, key->xmss_pk,
2852 sshkey_xmss_pklen(key))) != 0 ||
2853 (r = sshbuf_put_string(b, key->xmss_sk,
2854 sshkey_xmss_sklen(key))) != 0 ||
2855 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
2856 goto out;
2857 break;
2858 case KEY_XMSS_CERT:
2859 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0 ||
2860 key->xmss_name == NULL) {
2861 r = SSH_ERR_INVALID_ARGUMENT;
2862 goto out;
2863 }
2864 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2865 (r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
2866 (r = sshbuf_put_string(b, key->xmss_pk,
2867 sshkey_xmss_pklen(key))) != 0 ||
2868 (r = sshbuf_put_string(b, key->xmss_sk,
2869 sshkey_xmss_sklen(key))) != 0 ||
2870 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
2871 goto out;
2872 break;
2873#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002874 default:
2875 r = SSH_ERR_INVALID_ARGUMENT;
2876 goto out;
2877 }
2878 /* success */
2879 r = 0;
2880 out:
2881 return r;
2882}
2883
2884int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002885sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
2886{
2887 return sshkey_private_serialize_opt(key, b,
2888 SSHKEY_SERIALIZE_DEFAULT);
2889}
2890
2891int
Damien Miller86687062014-07-02 15:28:02 +10002892sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
2893{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002894 char *tname = NULL, *curve = NULL, *xmss_name = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002895 struct sshkey *k = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00002896 size_t pklen = 0, sklen = 0;
Damien Miller86687062014-07-02 15:28:02 +10002897 int type, r = SSH_ERR_INTERNAL_ERROR;
2898 u_char *ed25519_pk = NULL, *ed25519_sk = NULL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002899 u_char *xmss_pk = NULL, *xmss_sk = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002900#ifdef WITH_OPENSSL
2901 BIGNUM *exponent = NULL;
2902#endif /* WITH_OPENSSL */
2903
2904 if (kp != NULL)
2905 *kp = NULL;
2906 if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0)
2907 goto out;
2908 type = sshkey_type_from_name(tname);
2909 switch (type) {
2910#ifdef WITH_OPENSSL
2911 case KEY_DSA:
2912 if ((k = sshkey_new_private(type)) == NULL) {
2913 r = SSH_ERR_ALLOC_FAIL;
2914 goto out;
2915 }
2916 if ((r = sshbuf_get_bignum2(buf, k->dsa->p)) != 0 ||
2917 (r = sshbuf_get_bignum2(buf, k->dsa->q)) != 0 ||
2918 (r = sshbuf_get_bignum2(buf, k->dsa->g)) != 0 ||
2919 (r = sshbuf_get_bignum2(buf, k->dsa->pub_key)) != 0 ||
2920 (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0)
2921 goto out;
2922 break;
Damien Miller86687062014-07-02 15:28:02 +10002923 case KEY_DSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002924 if ((r = sshkey_froms(buf, &k)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002925 (r = sshkey_add_private(k)) != 0 ||
2926 (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0)
2927 goto out;
2928 break;
2929# ifdef OPENSSL_HAS_ECC
2930 case KEY_ECDSA:
2931 if ((k = sshkey_new_private(type)) == NULL) {
2932 r = SSH_ERR_ALLOC_FAIL;
2933 goto out;
2934 }
2935 if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) {
2936 r = SSH_ERR_INVALID_ARGUMENT;
2937 goto out;
2938 }
2939 if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0)
2940 goto out;
2941 if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
2942 r = SSH_ERR_EC_CURVE_MISMATCH;
2943 goto out;
2944 }
2945 k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
2946 if (k->ecdsa == NULL || (exponent = BN_new()) == NULL) {
2947 r = SSH_ERR_LIBCRYPTO_ERROR;
2948 goto out;
2949 }
2950 if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 ||
2951 (r = sshbuf_get_bignum2(buf, exponent)))
2952 goto out;
2953 if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) {
2954 r = SSH_ERR_LIBCRYPTO_ERROR;
2955 goto out;
2956 }
2957 if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
jsg@openbsd.orgf3a3ea12015-09-02 07:51:12 +00002958 EC_KEY_get0_public_key(k->ecdsa))) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002959 (r = sshkey_ec_validate_private(k->ecdsa)) != 0)
2960 goto out;
2961 break;
2962 case KEY_ECDSA_CERT:
2963 if ((exponent = BN_new()) == NULL) {
2964 r = SSH_ERR_LIBCRYPTO_ERROR;
2965 goto out;
2966 }
djm@openbsd.org60b18252015-01-26 02:59:11 +00002967 if ((r = sshkey_froms(buf, &k)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002968 (r = sshkey_add_private(k)) != 0 ||
2969 (r = sshbuf_get_bignum2(buf, exponent)) != 0)
2970 goto out;
2971 if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) {
2972 r = SSH_ERR_LIBCRYPTO_ERROR;
2973 goto out;
2974 }
2975 if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
jsg@openbsd.orgf3a3ea12015-09-02 07:51:12 +00002976 EC_KEY_get0_public_key(k->ecdsa))) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002977 (r = sshkey_ec_validate_private(k->ecdsa)) != 0)
2978 goto out;
2979 break;
2980# endif /* OPENSSL_HAS_ECC */
2981 case KEY_RSA:
2982 if ((k = sshkey_new_private(type)) == NULL) {
2983 r = SSH_ERR_ALLOC_FAIL;
2984 goto out;
2985 }
2986 if ((r = sshbuf_get_bignum2(buf, k->rsa->n)) != 0 ||
2987 (r = sshbuf_get_bignum2(buf, k->rsa->e)) != 0 ||
2988 (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 ||
2989 (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 ||
2990 (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 ||
2991 (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 ||
djm@openbsd.org83fa3a02017-07-01 13:50:45 +00002992 (r = ssh_rsa_generate_additional_parameters(k)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002993 goto out;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00002994 if (BN_num_bits(k->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
2995 r = SSH_ERR_KEY_LENGTH;
2996 goto out;
2997 }
Damien Miller86687062014-07-02 15:28:02 +10002998 break;
Damien Miller86687062014-07-02 15:28:02 +10002999 case KEY_RSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00003000 if ((r = sshkey_froms(buf, &k)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003001 (r = sshkey_add_private(k)) != 0 ||
jsg@openbsd.orgf3a3ea12015-09-02 07:51:12 +00003002 (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 ||
3003 (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 ||
3004 (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 ||
3005 (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 ||
djm@openbsd.org83fa3a02017-07-01 13:50:45 +00003006 (r = ssh_rsa_generate_additional_parameters(k)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003007 goto out;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00003008 if (BN_num_bits(k->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
3009 r = SSH_ERR_KEY_LENGTH;
3010 goto out;
3011 }
Damien Miller86687062014-07-02 15:28:02 +10003012 break;
3013#endif /* WITH_OPENSSL */
3014 case KEY_ED25519:
3015 if ((k = sshkey_new_private(type)) == NULL) {
3016 r = SSH_ERR_ALLOC_FAIL;
3017 goto out;
3018 }
3019 if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 ||
3020 (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0)
3021 goto out;
3022 if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) {
3023 r = SSH_ERR_INVALID_FORMAT;
3024 goto out;
3025 }
3026 k->ed25519_pk = ed25519_pk;
3027 k->ed25519_sk = ed25519_sk;
3028 ed25519_pk = ed25519_sk = NULL;
3029 break;
3030 case KEY_ED25519_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00003031 if ((r = sshkey_froms(buf, &k)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003032 (r = sshkey_add_private(k)) != 0 ||
3033 (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 ||
3034 (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0)
3035 goto out;
3036 if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) {
3037 r = SSH_ERR_INVALID_FORMAT;
3038 goto out;
3039 }
3040 k->ed25519_pk = ed25519_pk;
3041 k->ed25519_sk = ed25519_sk;
3042 ed25519_pk = ed25519_sk = NULL;
3043 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003044#ifdef WITH_XMSS
3045 case KEY_XMSS:
3046 if ((k = sshkey_new_private(type)) == NULL) {
3047 r = SSH_ERR_ALLOC_FAIL;
3048 goto out;
3049 }
3050 if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
3051 (r = sshkey_xmss_init(k, xmss_name)) != 0 ||
3052 (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
3053 (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
3054 goto out;
3055 if (pklen != sshkey_xmss_pklen(k) ||
3056 sklen != sshkey_xmss_sklen(k)) {
3057 r = SSH_ERR_INVALID_FORMAT;
3058 goto out;
3059 }
3060 k->xmss_pk = xmss_pk;
3061 k->xmss_sk = xmss_sk;
3062 xmss_pk = xmss_sk = NULL;
3063 /* optional internal state */
3064 if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
3065 goto out;
3066 break;
3067 case KEY_XMSS_CERT:
3068 if ((r = sshkey_froms(buf, &k)) != 0 ||
3069 (r = sshkey_add_private(k)) != 0 ||
markus@openbsd.org27979da2018-03-22 07:05:48 +00003070 (r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003071 (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
3072 (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
3073 goto out;
markus@openbsd.org27979da2018-03-22 07:05:48 +00003074 if (strcmp(xmss_name, k->xmss_name)) {
3075 r = SSH_ERR_INVALID_FORMAT;
3076 goto out;
3077 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003078 if (pklen != sshkey_xmss_pklen(k) ||
3079 sklen != sshkey_xmss_sklen(k)) {
3080 r = SSH_ERR_INVALID_FORMAT;
3081 goto out;
3082 }
3083 k->xmss_pk = xmss_pk;
3084 k->xmss_sk = xmss_sk;
3085 xmss_pk = xmss_sk = NULL;
3086 /* optional internal state */
3087 if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
3088 goto out;
3089 break;
3090#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10003091 default:
3092 r = SSH_ERR_KEY_TYPE_UNKNOWN;
3093 goto out;
3094 }
3095#ifdef WITH_OPENSSL
3096 /* enable blinding */
3097 switch (k->type) {
3098 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10003099 case KEY_RSA_CERT:
Damien Miller86687062014-07-02 15:28:02 +10003100 if (RSA_blinding_on(k->rsa, NULL) != 1) {
3101 r = SSH_ERR_LIBCRYPTO_ERROR;
3102 goto out;
3103 }
3104 break;
3105 }
3106#endif /* WITH_OPENSSL */
3107 /* success */
3108 r = 0;
3109 if (kp != NULL) {
3110 *kp = k;
3111 k = NULL;
3112 }
3113 out:
3114 free(tname);
3115 free(curve);
3116#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00003117 BN_clear_free(exponent);
Damien Miller86687062014-07-02 15:28:02 +10003118#endif /* WITH_OPENSSL */
3119 sshkey_free(k);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +00003120 freezero(ed25519_pk, pklen);
3121 freezero(ed25519_sk, sklen);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003122 free(xmss_name);
3123 freezero(xmss_pk, pklen);
3124 freezero(xmss_sk, sklen);
Damien Miller86687062014-07-02 15:28:02 +10003125 return r;
3126}
3127
3128#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
3129int
3130sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
3131{
3132 BN_CTX *bnctx;
3133 EC_POINT *nq = NULL;
3134 BIGNUM *order, *x, *y, *tmp;
3135 int ret = SSH_ERR_KEY_INVALID_EC_VALUE;
3136
djm@openbsd.orga571dbc2016-10-04 21:34:40 +00003137 /*
3138 * NB. This assumes OpenSSL has already verified that the public
3139 * point lies on the curve. This is done by EC_POINT_oct2point()
3140 * implicitly calling EC_POINT_is_on_curve(). If this code is ever
3141 * reachable with public points not unmarshalled using
3142 * EC_POINT_oct2point then the caller will need to explicitly check.
3143 */
3144
Damien Miller86687062014-07-02 15:28:02 +10003145 if ((bnctx = BN_CTX_new()) == NULL)
3146 return SSH_ERR_ALLOC_FAIL;
3147 BN_CTX_start(bnctx);
3148
3149 /*
3150 * We shouldn't ever hit this case because bignum_get_ecpoint()
3151 * refuses to load GF2m points.
3152 */
3153 if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
3154 NID_X9_62_prime_field)
3155 goto out;
3156
3157 /* Q != infinity */
3158 if (EC_POINT_is_at_infinity(group, public))
3159 goto out;
3160
3161 if ((x = BN_CTX_get(bnctx)) == NULL ||
3162 (y = BN_CTX_get(bnctx)) == NULL ||
3163 (order = BN_CTX_get(bnctx)) == NULL ||
3164 (tmp = BN_CTX_get(bnctx)) == NULL) {
3165 ret = SSH_ERR_ALLOC_FAIL;
3166 goto out;
3167 }
3168
3169 /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */
3170 if (EC_GROUP_get_order(group, order, bnctx) != 1 ||
3171 EC_POINT_get_affine_coordinates_GFp(group, public,
3172 x, y, bnctx) != 1) {
3173 ret = SSH_ERR_LIBCRYPTO_ERROR;
3174 goto out;
3175 }
3176 if (BN_num_bits(x) <= BN_num_bits(order) / 2 ||
3177 BN_num_bits(y) <= BN_num_bits(order) / 2)
3178 goto out;
3179
3180 /* nQ == infinity (n == order of subgroup) */
3181 if ((nq = EC_POINT_new(group)) == NULL) {
3182 ret = SSH_ERR_ALLOC_FAIL;
3183 goto out;
3184 }
3185 if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) {
3186 ret = SSH_ERR_LIBCRYPTO_ERROR;
3187 goto out;
3188 }
3189 if (EC_POINT_is_at_infinity(group, nq) != 1)
3190 goto out;
3191
3192 /* x < order - 1, y < order - 1 */
3193 if (!BN_sub(tmp, order, BN_value_one())) {
3194 ret = SSH_ERR_LIBCRYPTO_ERROR;
3195 goto out;
3196 }
3197 if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0)
3198 goto out;
3199 ret = 0;
3200 out:
3201 BN_CTX_free(bnctx);
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00003202 EC_POINT_free(nq);
Damien Miller86687062014-07-02 15:28:02 +10003203 return ret;
3204}
3205
3206int
3207sshkey_ec_validate_private(const EC_KEY *key)
3208{
3209 BN_CTX *bnctx;
3210 BIGNUM *order, *tmp;
3211 int ret = SSH_ERR_KEY_INVALID_EC_VALUE;
3212
3213 if ((bnctx = BN_CTX_new()) == NULL)
3214 return SSH_ERR_ALLOC_FAIL;
3215 BN_CTX_start(bnctx);
3216
3217 if ((order = BN_CTX_get(bnctx)) == NULL ||
3218 (tmp = BN_CTX_get(bnctx)) == NULL) {
3219 ret = SSH_ERR_ALLOC_FAIL;
3220 goto out;
3221 }
3222
3223 /* log2(private) > log2(order)/2 */
3224 if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) {
3225 ret = SSH_ERR_LIBCRYPTO_ERROR;
3226 goto out;
3227 }
3228 if (BN_num_bits(EC_KEY_get0_private_key(key)) <=
3229 BN_num_bits(order) / 2)
3230 goto out;
3231
3232 /* private < order - 1 */
3233 if (!BN_sub(tmp, order, BN_value_one())) {
3234 ret = SSH_ERR_LIBCRYPTO_ERROR;
3235 goto out;
3236 }
3237 if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0)
3238 goto out;
3239 ret = 0;
3240 out:
3241 BN_CTX_free(bnctx);
3242 return ret;
3243}
3244
3245void
3246sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point)
3247{
3248 BIGNUM *x, *y;
3249 BN_CTX *bnctx;
3250
3251 if (point == NULL) {
3252 fputs("point=(NULL)\n", stderr);
3253 return;
3254 }
3255 if ((bnctx = BN_CTX_new()) == NULL) {
3256 fprintf(stderr, "%s: BN_CTX_new failed\n", __func__);
3257 return;
3258 }
3259 BN_CTX_start(bnctx);
3260 if ((x = BN_CTX_get(bnctx)) == NULL ||
3261 (y = BN_CTX_get(bnctx)) == NULL) {
3262 fprintf(stderr, "%s: BN_CTX_get failed\n", __func__);
3263 return;
3264 }
3265 if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
3266 NID_X9_62_prime_field) {
3267 fprintf(stderr, "%s: group is not a prime field\n", __func__);
3268 return;
3269 }
3270 if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y,
3271 bnctx) != 1) {
3272 fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n",
3273 __func__);
3274 return;
3275 }
3276 fputs("x=", stderr);
3277 BN_print_fp(stderr, x);
3278 fputs("\ny=", stderr);
3279 BN_print_fp(stderr, y);
3280 fputs("\n", stderr);
3281 BN_CTX_free(bnctx);
3282}
3283
3284void
3285sshkey_dump_ec_key(const EC_KEY *key)
3286{
3287 const BIGNUM *exponent;
3288
3289 sshkey_dump_ec_point(EC_KEY_get0_group(key),
3290 EC_KEY_get0_public_key(key));
3291 fputs("exponent=", stderr);
3292 if ((exponent = EC_KEY_get0_private_key(key)) == NULL)
3293 fputs("(NULL)", stderr);
3294 else
3295 BN_print_fp(stderr, EC_KEY_get0_private_key(key));
3296 fputs("\n", stderr);
3297}
3298#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
3299
3300static int
3301sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob,
3302 const char *passphrase, const char *comment, const char *ciphername,
3303 int rounds)
3304{
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003305 u_char *cp, *key = NULL, *pubkeyblob = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003306 u_char salt[SALT_LEN];
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003307 char *b64 = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003308 size_t i, pubkeylen, keylen, ivlen, blocksize, authlen;
3309 u_int check;
3310 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003311 struct sshcipher_ctx *ciphercontext = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003312 const struct sshcipher *cipher;
3313 const char *kdfname = KDFNAME;
3314 struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL;
3315
Damien Miller86687062014-07-02 15:28:02 +10003316 if (rounds <= 0)
3317 rounds = DEFAULT_ROUNDS;
3318 if (passphrase == NULL || !strlen(passphrase)) {
3319 ciphername = "none";
3320 kdfname = "none";
3321 } else if (ciphername == NULL)
3322 ciphername = DEFAULT_CIPHERNAME;
Damien Miller86687062014-07-02 15:28:02 +10003323 if ((cipher = cipher_by_name(ciphername)) == NULL) {
djm@openbsd.orgcdccebd2017-04-30 23:15:04 +00003324 r = SSH_ERR_INVALID_ARGUMENT;
Damien Miller86687062014-07-02 15:28:02 +10003325 goto out;
3326 }
3327
3328 if ((kdf = sshbuf_new()) == NULL ||
3329 (encoded = sshbuf_new()) == NULL ||
3330 (encrypted = sshbuf_new()) == NULL) {
3331 r = SSH_ERR_ALLOC_FAIL;
3332 goto out;
3333 }
3334 blocksize = cipher_blocksize(cipher);
3335 keylen = cipher_keylen(cipher);
3336 ivlen = cipher_ivlen(cipher);
3337 authlen = cipher_authlen(cipher);
3338 if ((key = calloc(1, keylen + ivlen)) == NULL) {
3339 r = SSH_ERR_ALLOC_FAIL;
3340 goto out;
3341 }
3342 if (strcmp(kdfname, "bcrypt") == 0) {
3343 arc4random_buf(salt, SALT_LEN);
3344 if (bcrypt_pbkdf(passphrase, strlen(passphrase),
3345 salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) {
3346 r = SSH_ERR_INVALID_ARGUMENT;
3347 goto out;
3348 }
3349 if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 ||
3350 (r = sshbuf_put_u32(kdf, rounds)) != 0)
3351 goto out;
3352 } else if (strcmp(kdfname, "none") != 0) {
3353 /* Unsupported KDF type */
3354 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3355 goto out;
3356 }
3357 if ((r = cipher_init(&ciphercontext, cipher, key, keylen,
3358 key + keylen, ivlen, 1)) != 0)
3359 goto out;
3360
3361 if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 ||
3362 (r = sshbuf_put_cstring(encoded, ciphername)) != 0 ||
3363 (r = sshbuf_put_cstring(encoded, kdfname)) != 0 ||
3364 (r = sshbuf_put_stringb(encoded, kdf)) != 0 ||
3365 (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */
3366 (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 ||
3367 (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0)
3368 goto out;
3369
3370 /* set up the buffer that will be encrypted */
3371
3372 /* Random check bytes */
3373 check = arc4random();
3374 if ((r = sshbuf_put_u32(encrypted, check)) != 0 ||
3375 (r = sshbuf_put_u32(encrypted, check)) != 0)
3376 goto out;
3377
3378 /* append private key and comment*/
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003379 if ((r = sshkey_private_serialize_opt(prv, encrypted,
3380 SSHKEY_SERIALIZE_FULL)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003381 (r = sshbuf_put_cstring(encrypted, comment)) != 0)
3382 goto out;
3383
3384 /* padding */
3385 i = 0;
3386 while (sshbuf_len(encrypted) % blocksize) {
3387 if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0)
3388 goto out;
3389 }
3390
3391 /* length in destination buffer */
3392 if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0)
3393 goto out;
3394
3395 /* encrypt */
3396 if ((r = sshbuf_reserve(encoded,
3397 sshbuf_len(encrypted) + authlen, &cp)) != 0)
3398 goto out;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003399 if ((r = cipher_crypt(ciphercontext, 0, cp,
Damien Miller86687062014-07-02 15:28:02 +10003400 sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0)
3401 goto out;
3402
3403 /* uuencode */
3404 if ((b64 = sshbuf_dtob64(encoded)) == NULL) {
3405 r = SSH_ERR_ALLOC_FAIL;
3406 goto out;
3407 }
3408
3409 sshbuf_reset(blob);
3410 if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0)
3411 goto out;
3412 for (i = 0; i < strlen(b64); i++) {
3413 if ((r = sshbuf_put_u8(blob, b64[i])) != 0)
3414 goto out;
3415 /* insert line breaks */
3416 if (i % 70 == 69 && (r = sshbuf_put_u8(blob, '\n')) != 0)
3417 goto out;
3418 }
3419 if (i % 70 != 69 && (r = sshbuf_put_u8(blob, '\n')) != 0)
3420 goto out;
3421 if ((r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0)
3422 goto out;
3423
3424 /* success */
3425 r = 0;
3426
3427 out:
3428 sshbuf_free(kdf);
3429 sshbuf_free(encoded);
3430 sshbuf_free(encrypted);
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003431 cipher_free(ciphercontext);
Damien Miller86687062014-07-02 15:28:02 +10003432 explicit_bzero(salt, sizeof(salt));
3433 if (key != NULL) {
3434 explicit_bzero(key, keylen + ivlen);
3435 free(key);
3436 }
3437 if (pubkeyblob != NULL) {
3438 explicit_bzero(pubkeyblob, pubkeylen);
3439 free(pubkeyblob);
3440 }
3441 if (b64 != NULL) {
3442 explicit_bzero(b64, strlen(b64));
3443 free(b64);
3444 }
3445 return r;
3446}
3447
3448static int
3449sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
3450 struct sshkey **keyp, char **commentp)
3451{
3452 char *comment = NULL, *ciphername = NULL, *kdfname = NULL;
3453 const struct sshcipher *cipher = NULL;
3454 const u_char *cp;
3455 int r = SSH_ERR_INTERNAL_ERROR;
3456 size_t encoded_len;
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003457 size_t i, keylen = 0, ivlen = 0, authlen = 0, slen = 0;
Damien Miller86687062014-07-02 15:28:02 +10003458 struct sshbuf *encoded = NULL, *decoded = NULL;
3459 struct sshbuf *kdf = NULL, *decrypted = NULL;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003460 struct sshcipher_ctx *ciphercontext = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003461 struct sshkey *k = NULL;
3462 u_char *key = NULL, *salt = NULL, *dp, pad, last;
3463 u_int blocksize, rounds, nkeys, encrypted_len, check1, check2;
3464
Damien Miller86687062014-07-02 15:28:02 +10003465 if (keyp != NULL)
3466 *keyp = NULL;
3467 if (commentp != NULL)
3468 *commentp = NULL;
3469
3470 if ((encoded = sshbuf_new()) == NULL ||
3471 (decoded = sshbuf_new()) == NULL ||
3472 (decrypted = sshbuf_new()) == NULL) {
3473 r = SSH_ERR_ALLOC_FAIL;
3474 goto out;
3475 }
3476
3477 /* check preamble */
3478 cp = sshbuf_ptr(blob);
3479 encoded_len = sshbuf_len(blob);
3480 if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) ||
3481 memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) {
3482 r = SSH_ERR_INVALID_FORMAT;
3483 goto out;
3484 }
3485 cp += MARK_BEGIN_LEN;
3486 encoded_len -= MARK_BEGIN_LEN;
3487
3488 /* Look for end marker, removing whitespace as we go */
3489 while (encoded_len > 0) {
3490 if (*cp != '\n' && *cp != '\r') {
3491 if ((r = sshbuf_put_u8(encoded, *cp)) != 0)
3492 goto out;
3493 }
3494 last = *cp;
3495 encoded_len--;
3496 cp++;
3497 if (last == '\n') {
3498 if (encoded_len >= MARK_END_LEN &&
3499 memcmp(cp, MARK_END, MARK_END_LEN) == 0) {
3500 /* \0 terminate */
3501 if ((r = sshbuf_put_u8(encoded, 0)) != 0)
3502 goto out;
3503 break;
3504 }
3505 }
3506 }
3507 if (encoded_len == 0) {
3508 r = SSH_ERR_INVALID_FORMAT;
3509 goto out;
3510 }
3511
3512 /* decode base64 */
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003513 if ((r = sshbuf_b64tod(decoded, (char *)sshbuf_ptr(encoded))) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003514 goto out;
3515
3516 /* check magic */
3517 if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) ||
3518 memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) {
3519 r = SSH_ERR_INVALID_FORMAT;
3520 goto out;
3521 }
3522 /* parse public portion of key */
3523 if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 ||
3524 (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 ||
3525 (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 ||
3526 (r = sshbuf_froms(decoded, &kdf)) != 0 ||
3527 (r = sshbuf_get_u32(decoded, &nkeys)) != 0 ||
3528 (r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */
3529 (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0)
3530 goto out;
3531
3532 if ((cipher = cipher_by_name(ciphername)) == NULL) {
3533 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3534 goto out;
3535 }
3536 if ((passphrase == NULL || strlen(passphrase) == 0) &&
3537 strcmp(ciphername, "none") != 0) {
3538 /* passphrase required */
3539 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3540 goto out;
3541 }
3542 if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) {
3543 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3544 goto out;
3545 }
3546 if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) {
3547 r = SSH_ERR_INVALID_FORMAT;
3548 goto out;
3549 }
3550 if (nkeys != 1) {
3551 /* XXX only one key supported */
3552 r = SSH_ERR_INVALID_FORMAT;
3553 goto out;
3554 }
3555
3556 /* check size of encrypted key blob */
3557 blocksize = cipher_blocksize(cipher);
3558 if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
3559 r = SSH_ERR_INVALID_FORMAT;
3560 goto out;
3561 }
3562
3563 /* setup key */
3564 keylen = cipher_keylen(cipher);
3565 ivlen = cipher_ivlen(cipher);
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003566 authlen = cipher_authlen(cipher);
Damien Miller86687062014-07-02 15:28:02 +10003567 if ((key = calloc(1, keylen + ivlen)) == NULL) {
3568 r = SSH_ERR_ALLOC_FAIL;
3569 goto out;
3570 }
3571 if (strcmp(kdfname, "bcrypt") == 0) {
3572 if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 ||
3573 (r = sshbuf_get_u32(kdf, &rounds)) != 0)
3574 goto out;
3575 if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen,
3576 key, keylen + ivlen, rounds) < 0) {
3577 r = SSH_ERR_INVALID_FORMAT;
3578 goto out;
3579 }
3580 }
3581
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003582 /* check that an appropriate amount of auth data is present */
3583 if (sshbuf_len(decoded) < encrypted_len + authlen) {
3584 r = SSH_ERR_INVALID_FORMAT;
3585 goto out;
3586 }
3587
Damien Miller86687062014-07-02 15:28:02 +10003588 /* decrypt private portion of key */
3589 if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 ||
3590 (r = cipher_init(&ciphercontext, cipher, key, keylen,
3591 key + keylen, ivlen, 0)) != 0)
3592 goto out;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003593 if ((r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(decoded),
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003594 encrypted_len, 0, authlen)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10003595 /* an integrity error here indicates an incorrect passphrase */
3596 if (r == SSH_ERR_MAC_INVALID)
3597 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3598 goto out;
3599 }
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003600 if ((r = sshbuf_consume(decoded, encrypted_len + authlen)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003601 goto out;
3602 /* there should be no trailing data */
3603 if (sshbuf_len(decoded) != 0) {
3604 r = SSH_ERR_INVALID_FORMAT;
3605 goto out;
3606 }
3607
3608 /* check check bytes */
3609 if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 ||
3610 (r = sshbuf_get_u32(decrypted, &check2)) != 0)
3611 goto out;
3612 if (check1 != check2) {
3613 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3614 goto out;
3615 }
3616
3617 /* Load the private key and comment */
3618 if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 ||
3619 (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0)
3620 goto out;
3621
3622 /* Check deterministic padding */
3623 i = 0;
3624 while (sshbuf_len(decrypted)) {
3625 if ((r = sshbuf_get_u8(decrypted, &pad)) != 0)
3626 goto out;
3627 if (pad != (++i & 0xff)) {
3628 r = SSH_ERR_INVALID_FORMAT;
3629 goto out;
3630 }
3631 }
3632
3633 /* XXX decode pubkey and check against private */
3634
3635 /* success */
3636 r = 0;
3637 if (keyp != NULL) {
3638 *keyp = k;
3639 k = NULL;
3640 }
3641 if (commentp != NULL) {
3642 *commentp = comment;
3643 comment = NULL;
3644 }
3645 out:
3646 pad = 0;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003647 cipher_free(ciphercontext);
Damien Miller86687062014-07-02 15:28:02 +10003648 free(ciphername);
3649 free(kdfname);
3650 free(comment);
3651 if (salt != NULL) {
3652 explicit_bzero(salt, slen);
3653 free(salt);
3654 }
3655 if (key != NULL) {
3656 explicit_bzero(key, keylen + ivlen);
3657 free(key);
3658 }
3659 sshbuf_free(encoded);
3660 sshbuf_free(decoded);
3661 sshbuf_free(kdf);
3662 sshbuf_free(decrypted);
3663 sshkey_free(k);
3664 return r;
3665}
3666
Damien Miller86687062014-07-02 15:28:02 +10003667
3668#ifdef WITH_OPENSSL
3669/* convert SSH v2 key in OpenSSL PEM format */
3670static int
3671sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob,
3672 const char *_passphrase, const char *comment)
3673{
3674 int success, r;
3675 int blen, len = strlen(_passphrase);
3676 u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
Darren Tucker8fed0a52017-03-29 10:16:15 +11003677 const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;
djm@openbsd.org224f1932017-10-13 06:24:51 +00003678 char *bptr;
Damien Miller86687062014-07-02 15:28:02 +10003679 BIO *bio = NULL;
3680
3681 if (len > 0 && len <= 4)
3682 return SSH_ERR_PASSPHRASE_TOO_SHORT;
3683 if ((bio = BIO_new(BIO_s_mem())) == NULL)
3684 return SSH_ERR_ALLOC_FAIL;
3685
3686 switch (key->type) {
3687 case KEY_DSA:
3688 success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
3689 cipher, passphrase, len, NULL, NULL);
3690 break;
3691#ifdef OPENSSL_HAS_ECC
3692 case KEY_ECDSA:
3693 success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
3694 cipher, passphrase, len, NULL, NULL);
3695 break;
3696#endif
3697 case KEY_RSA:
3698 success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
3699 cipher, passphrase, len, NULL, NULL);
3700 break;
3701 default:
3702 success = 0;
3703 break;
3704 }
3705 if (success == 0) {
3706 r = SSH_ERR_LIBCRYPTO_ERROR;
3707 goto out;
3708 }
3709 if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) {
3710 r = SSH_ERR_INTERNAL_ERROR;
3711 goto out;
3712 }
3713 if ((r = sshbuf_put(blob, bptr, blen)) != 0)
3714 goto out;
3715 r = 0;
3716 out:
3717 BIO_free(bio);
3718 return r;
3719}
3720#endif /* WITH_OPENSSL */
3721
3722/* Serialise "key" to buffer "blob" */
3723int
3724sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
3725 const char *passphrase, const char *comment,
3726 int force_new_format, const char *new_format_cipher, int new_format_rounds)
3727{
3728 switch (key->type) {
markus@openbsd.orgf067cca2015-01-12 13:29:27 +00003729#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10003730 case KEY_DSA:
3731 case KEY_ECDSA:
3732 case KEY_RSA:
3733 if (force_new_format) {
3734 return sshkey_private_to_blob2(key, blob, passphrase,
3735 comment, new_format_cipher, new_format_rounds);
3736 }
3737 return sshkey_private_pem_to_blob(key, blob,
3738 passphrase, comment);
3739#endif /* WITH_OPENSSL */
3740 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003741#ifdef WITH_XMSS
3742 case KEY_XMSS:
3743#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10003744 return sshkey_private_to_blob2(key, blob, passphrase,
3745 comment, new_format_cipher, new_format_rounds);
3746 default:
3747 return SSH_ERR_KEY_TYPE_UNKNOWN;
3748 }
3749}
3750
Damien Miller86687062014-07-02 15:28:02 +10003751
3752#ifdef WITH_OPENSSL
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00003753static int
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00003754translate_libcrypto_error(unsigned long pem_err)
3755{
3756 int pem_reason = ERR_GET_REASON(pem_err);
3757
3758 switch (ERR_GET_LIB(pem_err)) {
3759 case ERR_LIB_PEM:
3760 switch (pem_reason) {
3761 case PEM_R_BAD_PASSWORD_READ:
3762 case PEM_R_PROBLEMS_GETTING_PASSWORD:
3763 case PEM_R_BAD_DECRYPT:
3764 return SSH_ERR_KEY_WRONG_PASSPHRASE;
3765 default:
3766 return SSH_ERR_INVALID_FORMAT;
3767 }
3768 case ERR_LIB_EVP:
3769 switch (pem_reason) {
3770 case EVP_R_BAD_DECRYPT:
3771 return SSH_ERR_KEY_WRONG_PASSPHRASE;
3772 case EVP_R_BN_DECODE_ERROR:
3773 case EVP_R_DECODE_ERROR:
3774#ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR
3775 case EVP_R_PRIVATE_KEY_DECODE_ERROR:
3776#endif
3777 return SSH_ERR_INVALID_FORMAT;
3778 default:
3779 return SSH_ERR_LIBCRYPTO_ERROR;
3780 }
3781 case ERR_LIB_ASN1:
3782 return SSH_ERR_INVALID_FORMAT;
3783 }
3784 return SSH_ERR_LIBCRYPTO_ERROR;
3785}
3786
3787static void
3788clear_libcrypto_errors(void)
3789{
3790 while (ERR_get_error() != 0)
3791 ;
3792}
3793
3794/*
3795 * Translate OpenSSL error codes to determine whether
3796 * passphrase is required/incorrect.
3797 */
3798static int
3799convert_libcrypto_error(void)
3800{
3801 /*
3802 * Some password errors are reported at the beginning
3803 * of the error queue.
3804 */
3805 if (translate_libcrypto_error(ERR_peek_error()) ==
3806 SSH_ERR_KEY_WRONG_PASSPHRASE)
3807 return SSH_ERR_KEY_WRONG_PASSPHRASE;
3808 return translate_libcrypto_error(ERR_peek_last_error());
3809}
3810
3811static int
Damien Miller86687062014-07-02 15:28:02 +10003812sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00003813 const char *passphrase, struct sshkey **keyp)
Damien Miller86687062014-07-02 15:28:02 +10003814{
3815 EVP_PKEY *pk = NULL;
3816 struct sshkey *prv = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003817 BIO *bio = NULL;
3818 int r;
3819
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00003820 if (keyp != NULL)
3821 *keyp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003822
3823 if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX)
3824 return SSH_ERR_ALLOC_FAIL;
3825 if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) !=
3826 (int)sshbuf_len(blob)) {
3827 r = SSH_ERR_ALLOC_FAIL;
3828 goto out;
3829 }
3830
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00003831 clear_libcrypto_errors();
Damien Miller86687062014-07-02 15:28:02 +10003832 if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL,
3833 (char *)passphrase)) == NULL) {
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00003834 r = convert_libcrypto_error();
Damien Miller86687062014-07-02 15:28:02 +10003835 goto out;
3836 }
3837 if (pk->type == EVP_PKEY_RSA &&
3838 (type == KEY_UNSPEC || type == KEY_RSA)) {
3839 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
3840 r = SSH_ERR_ALLOC_FAIL;
3841 goto out;
3842 }
3843 prv->rsa = EVP_PKEY_get1_RSA(pk);
3844 prv->type = KEY_RSA;
Damien Miller86687062014-07-02 15:28:02 +10003845#ifdef DEBUG_PK
3846 RSA_print_fp(stderr, prv->rsa, 8);
3847#endif
3848 if (RSA_blinding_on(prv->rsa, NULL) != 1) {
3849 r = SSH_ERR_LIBCRYPTO_ERROR;
3850 goto out;
3851 }
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00003852 if (BN_num_bits(prv->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
3853 r = SSH_ERR_KEY_LENGTH;
3854 goto out;
3855 }
Damien Miller86687062014-07-02 15:28:02 +10003856 } else if (pk->type == EVP_PKEY_DSA &&
3857 (type == KEY_UNSPEC || type == KEY_DSA)) {
3858 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
3859 r = SSH_ERR_ALLOC_FAIL;
3860 goto out;
3861 }
3862 prv->dsa = EVP_PKEY_get1_DSA(pk);
3863 prv->type = KEY_DSA;
Damien Miller86687062014-07-02 15:28:02 +10003864#ifdef DEBUG_PK
3865 DSA_print_fp(stderr, prv->dsa, 8);
3866#endif
3867#ifdef OPENSSL_HAS_ECC
3868 } else if (pk->type == EVP_PKEY_EC &&
3869 (type == KEY_UNSPEC || type == KEY_ECDSA)) {
3870 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
3871 r = SSH_ERR_ALLOC_FAIL;
3872 goto out;
3873 }
3874 prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
3875 prv->type = KEY_ECDSA;
3876 prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa);
3877 if (prv->ecdsa_nid == -1 ||
3878 sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
3879 sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
3880 EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
3881 sshkey_ec_validate_private(prv->ecdsa) != 0) {
3882 r = SSH_ERR_INVALID_FORMAT;
3883 goto out;
3884 }
Damien Miller86687062014-07-02 15:28:02 +10003885# ifdef DEBUG_PK
3886 if (prv != NULL && prv->ecdsa != NULL)
3887 sshkey_dump_ec_key(prv->ecdsa);
3888# endif
3889#endif /* OPENSSL_HAS_ECC */
3890 } else {
3891 r = SSH_ERR_INVALID_FORMAT;
3892 goto out;
3893 }
Damien Miller86687062014-07-02 15:28:02 +10003894 r = 0;
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00003895 if (keyp != NULL) {
3896 *keyp = prv;
3897 prv = NULL;
3898 }
Damien Miller86687062014-07-02 15:28:02 +10003899 out:
3900 BIO_free(bio);
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00003901 EVP_PKEY_free(pk);
mmcc@openbsd.org89540b62015-12-11 02:31:47 +00003902 sshkey_free(prv);
Damien Miller86687062014-07-02 15:28:02 +10003903 return r;
3904}
3905#endif /* WITH_OPENSSL */
3906
3907int
3908sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
3909 const char *passphrase, struct sshkey **keyp, char **commentp)
3910{
djm@openbsd.org155d5402017-02-10 04:34:50 +00003911 int r = SSH_ERR_INTERNAL_ERROR;
3912
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00003913 if (keyp != NULL)
3914 *keyp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003915 if (commentp != NULL)
3916 *commentp = NULL;
3917
3918 switch (type) {
markus@openbsd.orgf067cca2015-01-12 13:29:27 +00003919#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10003920 case KEY_DSA:
3921 case KEY_ECDSA:
3922 case KEY_RSA:
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00003923 return sshkey_parse_private_pem_fileblob(blob, type,
3924 passphrase, keyp);
Damien Miller86687062014-07-02 15:28:02 +10003925#endif /* WITH_OPENSSL */
3926 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003927#ifdef WITH_XMSS
3928 case KEY_XMSS:
3929#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10003930 return sshkey_parse_private2(blob, type, passphrase,
3931 keyp, commentp);
3932 case KEY_UNSPEC:
djm@openbsd.org155d5402017-02-10 04:34:50 +00003933 r = sshkey_parse_private2(blob, type, passphrase, keyp,
3934 commentp);
3935 /* Do not fallback to PEM parser if only passphrase is wrong. */
3936 if (r == 0 || r == SSH_ERR_KEY_WRONG_PASSPHRASE)
3937 return r;
Damien Miller86687062014-07-02 15:28:02 +10003938#ifdef WITH_OPENSSL
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00003939 return sshkey_parse_private_pem_fileblob(blob, type,
3940 passphrase, keyp);
Damien Miller86687062014-07-02 15:28:02 +10003941#else
3942 return SSH_ERR_INVALID_FORMAT;
3943#endif /* WITH_OPENSSL */
3944 default:
3945 return SSH_ERR_KEY_TYPE_UNKNOWN;
3946 }
3947}
3948
3949int
3950sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase,
tim@openbsd.org3c019a92015-09-13 14:39:16 +00003951 struct sshkey **keyp, char **commentp)
Damien Miller86687062014-07-02 15:28:02 +10003952{
Damien Miller86687062014-07-02 15:28:02 +10003953 if (keyp != NULL)
3954 *keyp = NULL;
3955 if (commentp != NULL)
3956 *commentp = NULL;
3957
tim@openbsd.org3c019a92015-09-13 14:39:16 +00003958 return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC,
3959 passphrase, keyp, commentp);
Damien Miller86687062014-07-02 15:28:02 +10003960}
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003961
3962#ifdef WITH_XMSS
3963/*
3964 * serialize the key with the current state and forward the state
3965 * maxsign times.
3966 */
3967int
3968sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,
3969 u_int32_t maxsign, sshkey_printfn *pr)
3970{
3971 int r, rupdate;
3972
3973 if (maxsign == 0 ||
3974 sshkey_type_plain(k->type) != KEY_XMSS)
3975 return sshkey_private_serialize_opt(k, b,
3976 SSHKEY_SERIALIZE_DEFAULT);
3977 if ((r = sshkey_xmss_get_state(k, pr)) != 0 ||
3978 (r = sshkey_private_serialize_opt(k, b,
3979 SSHKEY_SERIALIZE_STATE)) != 0 ||
3980 (r = sshkey_xmss_forward_state(k, maxsign)) != 0)
3981 goto out;
3982 r = 0;
3983out:
3984 if ((rupdate = sshkey_xmss_update_state(k, pr)) != 0) {
3985 if (r == 0)
3986 r = rupdate;
3987 }
3988 return r;
3989}
3990
3991u_int32_t
3992sshkey_signatures_left(const struct sshkey *k)
3993{
3994 if (sshkey_type_plain(k->type) == KEY_XMSS)
3995 return sshkey_xmss_signatures_left(k);
3996 return 0;
3997}
3998
3999int
4000sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
4001{
4002 if (sshkey_type_plain(k->type) != KEY_XMSS)
4003 return SSH_ERR_INVALID_ARGUMENT;
4004 return sshkey_xmss_enable_maxsign(k, maxsign);
4005}
4006
4007int
4008sshkey_set_filename(struct sshkey *k, const char *filename)
4009{
4010 if (k == NULL)
4011 return SSH_ERR_INVALID_ARGUMENT;
4012 if (sshkey_type_plain(k->type) != KEY_XMSS)
4013 return 0;
4014 if (filename == NULL)
4015 return SSH_ERR_INVALID_ARGUMENT;
4016 if ((k->xmss_filename = strdup(filename)) == NULL)
4017 return SSH_ERR_ALLOC_FAIL;
4018 return 0;
4019}
4020#else
4021int
4022sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,
4023 u_int32_t maxsign, sshkey_printfn *pr)
4024{
4025 return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT);
4026}
4027
4028u_int32_t
4029sshkey_signatures_left(const struct sshkey *k)
4030{
4031 return 0;
4032}
4033
4034int
4035sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
4036{
4037 return SSH_ERR_INVALID_ARGUMENT;
4038}
4039
4040int
4041sshkey_set_filename(struct sshkey *k, const char *filename)
4042{
4043 if (k == NULL)
4044 return SSH_ERR_INVALID_ARGUMENT;
4045 return 0;
4046}
4047#endif /* WITH_XMSS */