blob: 6f2c9d44b8d9127cd063711e4a000ea2c7ca429b [file] [log] [blame]
djm@openbsd.orgba9e7882018-09-12 01:32:54 +00001/* $OpenBSD: sshkey.c,v 1.68 2018/09/12 01:32:54 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 Miller48f54b92018-09-13 12:13:50 +100063#include "openbsd-compat/openssl-compat.h"
64
Damien Miller86687062014-07-02 15:28:02 +100065/* openssh private key file format */
66#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n"
67#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n"
68#define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1)
69#define MARK_END_LEN (sizeof(MARK_END) - 1)
70#define KDFNAME "bcrypt"
71#define AUTH_MAGIC "openssh-key-v1"
72#define SALT_LEN 16
djm@openbsd.org0f345532017-08-12 06:42:52 +000073#define DEFAULT_CIPHERNAME "aes256-ctr"
Damien Miller86687062014-07-02 15:28:02 +100074#define DEFAULT_ROUNDS 16
75
76/* Version identification string for SSH v1 identity files. */
77#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n"
78
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000079int sshkey_private_serialize_opt(const struct sshkey *key,
80 struct sshbuf *buf, enum sshkey_serialize_rep);
djm@openbsd.org60b18252015-01-26 02:59:11 +000081static int sshkey_from_blob_internal(struct sshbuf *buf,
Damien Miller86687062014-07-02 15:28:02 +100082 struct sshkey **keyp, int allow_cert);
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +000083static int get_sigtype(const u_char *sig, size_t siglen, char **sigtypep);
Damien Miller86687062014-07-02 15:28:02 +100084
85/* Supported key types */
86struct keytype {
87 const char *name;
88 const char *shortname;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +000089 const char *sigalg;
Damien Miller86687062014-07-02 15:28:02 +100090 int type;
91 int nid;
92 int cert;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +000093 int sigonly;
Damien Miller86687062014-07-02 15:28:02 +100094};
95static const struct keytype keytypes[] = {
djm@openbsd.org4ba0d542018-07-03 11:39:54 +000096 { "ssh-ed25519", "ED25519", NULL, KEY_ED25519, 0, 0, 0 },
97 { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +000098 KEY_ED25519_CERT, 0, 1, 0 },
markus@openbsd.org1b11ea72018-02-23 15:58:37 +000099#ifdef WITH_XMSS
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000100 { "ssh-xmss@openssh.com", "XMSS", NULL, KEY_XMSS, 0, 0, 0 },
101 { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", NULL,
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000102 KEY_XMSS_CERT, 0, 1, 0 },
103#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000104#ifdef WITH_OPENSSL
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000105 { "ssh-rsa", "RSA", NULL, KEY_RSA, 0, 0, 0 },
106 { "rsa-sha2-256", "RSA", NULL, KEY_RSA, 0, 0, 1 },
107 { "rsa-sha2-512", "RSA", NULL, KEY_RSA, 0, 0, 1 },
108 { "ssh-dss", "DSA", NULL, KEY_DSA, 0, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000109# ifdef OPENSSL_HAS_ECC
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000110 { "ecdsa-sha2-nistp256", "ECDSA", NULL,
111 KEY_ECDSA, NID_X9_62_prime256v1, 0, 0 },
112 { "ecdsa-sha2-nistp384", "ECDSA", NULL,
113 KEY_ECDSA, NID_secp384r1, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000114# ifdef OPENSSL_HAS_NISTP521
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000115 { "ecdsa-sha2-nistp521", "ECDSA", NULL,
116 KEY_ECDSA, NID_secp521r1, 0, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000117# endif /* OPENSSL_HAS_NISTP521 */
118# endif /* OPENSSL_HAS_ECC */
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000119 { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL,
120 KEY_RSA_CERT, 0, 1, 0 },
121 { "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT",
122 "ssh-rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 },
123 { "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT",
124 "ssh-rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 },
125 { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL,
126 KEY_DSA_CERT, 0, 1, 0 },
127 { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL,
128 KEY_RSA_CERT, 0, 1, 0 },
129 { "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT",
130 "ssh-rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 },
131 { "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT",
132 "ssh-rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 },
133 { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL,
134 KEY_DSA_CERT, 0, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000135# ifdef OPENSSL_HAS_ECC
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000136 { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000137 KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1, 0 },
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000138 { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", NULL,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000139 KEY_ECDSA_CERT, NID_secp384r1, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000140# ifdef OPENSSL_HAS_NISTP521
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000141 { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", NULL,
142 KEY_ECDSA_CERT, NID_secp521r1, 1, 0 },
Damien Miller86687062014-07-02 15:28:02 +1000143# endif /* OPENSSL_HAS_NISTP521 */
144# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +1000145#endif /* WITH_OPENSSL */
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000146 { NULL, NULL, NULL, -1, -1, 0, 0 }
Damien Miller86687062014-07-02 15:28:02 +1000147};
148
149const char *
150sshkey_type(const struct sshkey *k)
151{
152 const struct keytype *kt;
153
154 for (kt = keytypes; kt->type != -1; kt++) {
155 if (kt->type == k->type)
156 return kt->shortname;
157 }
158 return "unknown";
159}
160
161static const char *
162sshkey_ssh_name_from_type_nid(int type, int nid)
163{
164 const struct keytype *kt;
165
166 for (kt = keytypes; kt->type != -1; kt++) {
167 if (kt->type == type && (kt->nid == 0 || kt->nid == nid))
168 return kt->name;
169 }
170 return "ssh-unknown";
171}
172
173int
174sshkey_type_is_cert(int type)
175{
176 const struct keytype *kt;
177
178 for (kt = keytypes; kt->type != -1; kt++) {
179 if (kt->type == type)
180 return kt->cert;
181 }
182 return 0;
183}
184
185const char *
186sshkey_ssh_name(const struct sshkey *k)
187{
188 return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid);
189}
190
191const char *
192sshkey_ssh_name_plain(const struct sshkey *k)
193{
194 return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type),
195 k->ecdsa_nid);
196}
197
198int
199sshkey_type_from_name(const char *name)
200{
201 const struct keytype *kt;
202
203 for (kt = keytypes; kt->type != -1; kt++) {
204 /* Only allow shortname matches for plain key types */
205 if ((kt->name != NULL && strcmp(name, kt->name) == 0) ||
206 (!kt->cert && strcasecmp(kt->shortname, name) == 0))
207 return kt->type;
208 }
209 return KEY_UNSPEC;
210}
211
212int
213sshkey_ecdsa_nid_from_name(const char *name)
214{
215 const struct keytype *kt;
216
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +0000217 for (kt = keytypes; kt->type != -1; kt++) {
218 if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT)
219 continue;
220 if (kt->name != NULL && strcmp(name, kt->name) == 0)
221 return kt->nid;
222 }
Damien Miller86687062014-07-02 15:28:02 +1000223 return -1;
224}
225
226char *
djm@openbsd.org183ba552017-03-10 04:07:20 +0000227sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
Damien Miller86687062014-07-02 15:28:02 +1000228{
229 char *tmp, *ret = NULL;
230 size_t nlen, rlen = 0;
231 const struct keytype *kt;
232
233 for (kt = keytypes; kt->type != -1; kt++) {
djm@openbsd.org183ba552017-03-10 04:07:20 +0000234 if (kt->name == NULL)
235 continue;
236 if (!include_sigonly && kt->sigonly)
Damien Miller86687062014-07-02 15:28:02 +1000237 continue;
238 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
239 continue;
240 if (ret != NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000241 ret[rlen++] = sep;
Damien Miller86687062014-07-02 15:28:02 +1000242 nlen = strlen(kt->name);
243 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
244 free(ret);
245 return NULL;
246 }
247 ret = tmp;
248 memcpy(ret + rlen, kt->name, nlen + 1);
249 rlen += nlen;
250 }
251 return ret;
252}
253
254int
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000255sshkey_names_valid2(const char *names, int allow_wildcard)
Damien Miller86687062014-07-02 15:28:02 +1000256{
257 char *s, *cp, *p;
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000258 const struct keytype *kt;
259 int type;
Damien Miller86687062014-07-02 15:28:02 +1000260
261 if (names == NULL || strcmp(names, "") == 0)
262 return 0;
263 if ((s = cp = strdup(names)) == NULL)
264 return 0;
265 for ((p = strsep(&cp, ",")); p && *p != '\0';
266 (p = strsep(&cp, ","))) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000267 type = sshkey_type_from_name(p);
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000268 if (type == KEY_UNSPEC) {
269 if (allow_wildcard) {
270 /*
271 * Try matching key types against the string.
272 * If any has a positive or negative match then
273 * the component is accepted.
274 */
275 for (kt = keytypes; kt->type != -1; kt++) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000276 if (match_pattern_list(kt->name,
djm@openbsd.orge661a862015-05-04 06:10:48 +0000277 p, 0) != 0)
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000278 break;
279 }
280 if (kt->type != -1)
281 continue;
282 }
Damien Miller86687062014-07-02 15:28:02 +1000283 free(s);
284 return 0;
285 }
286 }
287 free(s);
288 return 1;
289}
290
291u_int
292sshkey_size(const struct sshkey *k)
293{
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000294#ifdef WITH_OPENSSL
295 const BIGNUM *rsa_n, *dsa_p;
296#endif /* WITH_OPENSSL */
297
Damien Miller86687062014-07-02 15:28:02 +1000298 switch (k->type) {
299#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000300 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000301 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000302 if (k->rsa == NULL)
303 return 0;
304 RSA_get0_key(k->rsa, &rsa_n, NULL, NULL);
305 return BN_num_bits(rsa_n);
Damien Miller86687062014-07-02 15:28:02 +1000306 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000307 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000308 if (k->dsa == NULL)
309 return 0;
310 DSA_get0_pqg(k->dsa, &dsa_p, NULL, NULL);
311 return BN_num_bits(dsa_p);
Damien Miller86687062014-07-02 15:28:02 +1000312 case KEY_ECDSA:
313 case KEY_ECDSA_CERT:
314 return sshkey_curve_nid_to_bits(k->ecdsa_nid);
315#endif /* WITH_OPENSSL */
316 case KEY_ED25519:
317 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000318 case KEY_XMSS:
319 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000320 return 256; /* XXX */
321 }
322 return 0;
323}
324
Damien Miller86687062014-07-02 15:28:02 +1000325static int
326sshkey_type_is_valid_ca(int type)
327{
328 switch (type) {
329 case KEY_RSA:
330 case KEY_DSA:
331 case KEY_ECDSA:
332 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000333 case KEY_XMSS:
Damien Miller86687062014-07-02 15:28:02 +1000334 return 1;
335 default:
336 return 0;
337 }
338}
339
340int
341sshkey_is_cert(const struct sshkey *k)
342{
343 if (k == NULL)
344 return 0;
345 return sshkey_type_is_cert(k->type);
346}
347
348/* Return the cert-less equivalent to a certified key type */
349int
350sshkey_type_plain(int type)
351{
352 switch (type) {
Damien Miller86687062014-07-02 15:28:02 +1000353 case KEY_RSA_CERT:
354 return KEY_RSA;
Damien Miller86687062014-07-02 15:28:02 +1000355 case KEY_DSA_CERT:
356 return KEY_DSA;
357 case KEY_ECDSA_CERT:
358 return KEY_ECDSA;
359 case KEY_ED25519_CERT:
360 return KEY_ED25519;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000361 case KEY_XMSS_CERT:
362 return KEY_XMSS;
Damien Miller86687062014-07-02 15:28:02 +1000363 default:
364 return type;
365 }
366}
367
368#ifdef WITH_OPENSSL
369/* XXX: these are really begging for a table-driven approach */
370int
371sshkey_curve_name_to_nid(const char *name)
372{
373 if (strcmp(name, "nistp256") == 0)
374 return NID_X9_62_prime256v1;
375 else if (strcmp(name, "nistp384") == 0)
376 return NID_secp384r1;
377# ifdef OPENSSL_HAS_NISTP521
378 else if (strcmp(name, "nistp521") == 0)
379 return NID_secp521r1;
380# endif /* OPENSSL_HAS_NISTP521 */
381 else
382 return -1;
383}
384
385u_int
386sshkey_curve_nid_to_bits(int nid)
387{
388 switch (nid) {
389 case NID_X9_62_prime256v1:
390 return 256;
391 case NID_secp384r1:
392 return 384;
393# ifdef OPENSSL_HAS_NISTP521
394 case NID_secp521r1:
395 return 521;
396# endif /* OPENSSL_HAS_NISTP521 */
397 default:
398 return 0;
399 }
400}
401
402int
403sshkey_ecdsa_bits_to_nid(int bits)
404{
405 switch (bits) {
406 case 256:
407 return NID_X9_62_prime256v1;
408 case 384:
409 return NID_secp384r1;
410# ifdef OPENSSL_HAS_NISTP521
411 case 521:
412 return NID_secp521r1;
413# endif /* OPENSSL_HAS_NISTP521 */
414 default:
415 return -1;
416 }
417}
418
419const char *
420sshkey_curve_nid_to_name(int nid)
421{
422 switch (nid) {
423 case NID_X9_62_prime256v1:
424 return "nistp256";
425 case NID_secp384r1:
426 return "nistp384";
427# ifdef OPENSSL_HAS_NISTP521
428 case NID_secp521r1:
429 return "nistp521";
430# endif /* OPENSSL_HAS_NISTP521 */
431 default:
432 return NULL;
433 }
434}
435
436int
437sshkey_ec_nid_to_hash_alg(int nid)
438{
439 int kbits = sshkey_curve_nid_to_bits(nid);
440
441 if (kbits <= 0)
442 return -1;
443
444 /* RFC5656 section 6.2.1 */
445 if (kbits <= 256)
446 return SSH_DIGEST_SHA256;
447 else if (kbits <= 384)
448 return SSH_DIGEST_SHA384;
449 else
450 return SSH_DIGEST_SHA512;
451}
452#endif /* WITH_OPENSSL */
453
454static void
455cert_free(struct sshkey_cert *cert)
456{
457 u_int i;
458
459 if (cert == NULL)
460 return;
mmcc@openbsd.org52d70782015-12-11 04:21:11 +0000461 sshbuf_free(cert->certblob);
462 sshbuf_free(cert->critical);
463 sshbuf_free(cert->extensions);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000464 free(cert->key_id);
Damien Miller86687062014-07-02 15:28:02 +1000465 for (i = 0; i < cert->nprincipals; i++)
466 free(cert->principals[i]);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000467 free(cert->principals);
mmcc@openbsd.org89540b62015-12-11 02:31:47 +0000468 sshkey_free(cert->signature_key);
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +0000469 free(cert->signature_type);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000470 freezero(cert, sizeof(*cert));
Damien Miller86687062014-07-02 15:28:02 +1000471}
472
473static struct sshkey_cert *
474cert_new(void)
475{
476 struct sshkey_cert *cert;
477
478 if ((cert = calloc(1, sizeof(*cert))) == NULL)
479 return NULL;
480 if ((cert->certblob = sshbuf_new()) == NULL ||
481 (cert->critical = sshbuf_new()) == NULL ||
482 (cert->extensions = sshbuf_new()) == NULL) {
483 cert_free(cert);
484 return NULL;
485 }
486 cert->key_id = NULL;
487 cert->principals = NULL;
488 cert->signature_key = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +0000489 cert->signature_type = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000490 return cert;
491}
492
493struct sshkey *
494sshkey_new(int type)
495{
496 struct sshkey *k;
497#ifdef WITH_OPENSSL
498 RSA *rsa;
499 DSA *dsa;
500#endif /* WITH_OPENSSL */
501
502 if ((k = calloc(1, sizeof(*k))) == NULL)
503 return NULL;
504 k->type = type;
505 k->ecdsa = NULL;
506 k->ecdsa_nid = -1;
507 k->dsa = NULL;
508 k->rsa = NULL;
509 k->cert = NULL;
510 k->ed25519_sk = NULL;
511 k->ed25519_pk = NULL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000512 k->xmss_sk = NULL;
513 k->xmss_pk = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000514 switch (k->type) {
515#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000516 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000517 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000518 if ((rsa = RSA_new()) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +1000519 free(k);
520 return NULL;
521 }
522 k->rsa = rsa;
523 break;
524 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000525 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000526 if ((dsa = DSA_new()) == NULL) {
Damien Miller86687062014-07-02 15:28:02 +1000527 free(k);
528 return NULL;
529 }
530 k->dsa = dsa;
531 break;
532 case KEY_ECDSA:
533 case KEY_ECDSA_CERT:
534 /* Cannot do anything until we know the group */
535 break;
536#endif /* WITH_OPENSSL */
537 case KEY_ED25519:
538 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000539 case KEY_XMSS:
540 case KEY_XMSS_CERT:
Damien Miller86687062014-07-02 15:28:02 +1000541 /* no need to prealloc */
542 break;
543 case KEY_UNSPEC:
544 break;
545 default:
546 free(k);
547 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000548 }
549
550 if (sshkey_is_cert(k)) {
551 if ((k->cert = cert_new()) == NULL) {
552 sshkey_free(k);
553 return NULL;
554 }
555 }
556
557 return k;
558}
559
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000560/* XXX garbage-collect this API */
Damien Miller86687062014-07-02 15:28:02 +1000561struct sshkey *
562sshkey_new_private(int type)
563{
564 struct sshkey *k = sshkey_new(type);
565
566 if (k == NULL)
567 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000568 return k;
569}
570
571void
572sshkey_free(struct sshkey *k)
573{
574 if (k == NULL)
575 return;
576 switch (k->type) {
577#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000578 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +1000579 case KEY_RSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000580 RSA_free(k->rsa);
Damien Miller86687062014-07-02 15:28:02 +1000581 k->rsa = NULL;
582 break;
583 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +1000584 case KEY_DSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000585 DSA_free(k->dsa);
Damien Miller86687062014-07-02 15:28:02 +1000586 k->dsa = NULL;
587 break;
588# ifdef OPENSSL_HAS_ECC
589 case KEY_ECDSA:
590 case KEY_ECDSA_CERT:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000591 EC_KEY_free(k->ecdsa);
Damien Miller86687062014-07-02 15:28:02 +1000592 k->ecdsa = NULL;
593 break;
594# endif /* OPENSSL_HAS_ECC */
595#endif /* WITH_OPENSSL */
596 case KEY_ED25519:
597 case KEY_ED25519_CERT:
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000598 freezero(k->ed25519_pk, ED25519_PK_SZ);
599 k->ed25519_pk = NULL;
600 freezero(k->ed25519_sk, ED25519_SK_SZ);
601 k->ed25519_sk = NULL;
Damien Miller86687062014-07-02 15:28:02 +1000602 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000603#ifdef WITH_XMSS
604 case KEY_XMSS:
605 case KEY_XMSS_CERT:
606 freezero(k->xmss_pk, sshkey_xmss_pklen(k));
607 k->xmss_pk = NULL;
608 freezero(k->xmss_sk, sshkey_xmss_sklen(k));
609 k->xmss_sk = NULL;
610 sshkey_xmss_free_state(k);
611 free(k->xmss_name);
612 k->xmss_name = NULL;
613 free(k->xmss_filename);
614 k->xmss_filename = NULL;
615 break;
616#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000617 case KEY_UNSPEC:
618 break;
619 default:
620 break;
621 }
622 if (sshkey_is_cert(k))
623 cert_free(k->cert);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000624 freezero(k, sizeof(*k));
Damien Miller86687062014-07-02 15:28:02 +1000625}
626
627static int
628cert_compare(struct sshkey_cert *a, struct sshkey_cert *b)
629{
630 if (a == NULL && b == NULL)
631 return 1;
632 if (a == NULL || b == NULL)
633 return 0;
634 if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob))
635 return 0;
636 if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob),
637 sshbuf_len(a->certblob)) != 0)
638 return 0;
639 return 1;
640}
641
642/*
643 * Compare public portions of key only, allowing comparisons between
644 * certificates and plain keys too.
645 */
646int
647sshkey_equal_public(const struct sshkey *a, const struct sshkey *b)
648{
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000649#if defined(WITH_OPENSSL)
650 const BIGNUM *rsa_e_a, *rsa_n_a;
651 const BIGNUM *rsa_e_b, *rsa_n_b;
652 const BIGNUM *dsa_p_a, *dsa_q_a, *dsa_g_a, *dsa_pub_key_a;
653 const BIGNUM *dsa_p_b, *dsa_q_b, *dsa_g_b, *dsa_pub_key_b;
654# if defined(OPENSSL_HAS_ECC)
Damien Miller86687062014-07-02 15:28:02 +1000655 BN_CTX *bnctx;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000656# endif /* OPENSSL_HAS_ECC */
657#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +1000658
659 if (a == NULL || b == NULL ||
660 sshkey_type_plain(a->type) != sshkey_type_plain(b->type))
661 return 0;
662
663 switch (a->type) {
664#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000665 case KEY_RSA_CERT:
666 case KEY_RSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000667 if (a->rsa == NULL || b->rsa == NULL)
668 return 0;
669 RSA_get0_key(a->rsa, &rsa_n_a, &rsa_e_a, NULL);
670 RSA_get0_key(b->rsa, &rsa_n_b, &rsa_e_b, NULL);
671 return BN_cmp(rsa_e_a, rsa_e_b) == 0 &&
672 BN_cmp(rsa_n_a, rsa_n_b) == 0;
Damien Miller86687062014-07-02 15:28:02 +1000673 case KEY_DSA_CERT:
674 case KEY_DSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000675 if (a->dsa == NULL || b->dsa == NULL)
676 return 0;
677 DSA_get0_pqg(a->dsa, &dsa_p_a, &dsa_q_a, &dsa_g_a);
678 DSA_get0_pqg(b->dsa, &dsa_p_b, &dsa_q_b, &dsa_g_b);
679 DSA_get0_key(a->dsa, &dsa_pub_key_a, NULL);
680 DSA_get0_key(b->dsa, &dsa_pub_key_b, NULL);
681 return BN_cmp(dsa_p_a, dsa_p_b) == 0 &&
682 BN_cmp(dsa_q_a, dsa_q_b) == 0 &&
683 BN_cmp(dsa_g_a, dsa_g_b) == 0 &&
684 BN_cmp(dsa_pub_key_a, dsa_pub_key_b) == 0;
Damien Miller86687062014-07-02 15:28:02 +1000685# ifdef OPENSSL_HAS_ECC
686 case KEY_ECDSA_CERT:
687 case KEY_ECDSA:
688 if (a->ecdsa == NULL || b->ecdsa == NULL ||
689 EC_KEY_get0_public_key(a->ecdsa) == NULL ||
690 EC_KEY_get0_public_key(b->ecdsa) == NULL)
691 return 0;
692 if ((bnctx = BN_CTX_new()) == NULL)
693 return 0;
694 if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa),
695 EC_KEY_get0_group(b->ecdsa), bnctx) != 0 ||
696 EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa),
697 EC_KEY_get0_public_key(a->ecdsa),
698 EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) {
699 BN_CTX_free(bnctx);
700 return 0;
701 }
702 BN_CTX_free(bnctx);
703 return 1;
704# endif /* OPENSSL_HAS_ECC */
705#endif /* WITH_OPENSSL */
706 case KEY_ED25519:
707 case KEY_ED25519_CERT:
708 return a->ed25519_pk != NULL && b->ed25519_pk != NULL &&
709 memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000710#ifdef WITH_XMSS
711 case KEY_XMSS:
712 case KEY_XMSS_CERT:
713 return a->xmss_pk != NULL && b->xmss_pk != NULL &&
714 sshkey_xmss_pklen(a) == sshkey_xmss_pklen(b) &&
715 memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) == 0;
716#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000717 default:
718 return 0;
719 }
720 /* NOTREACHED */
721}
722
723int
724sshkey_equal(const struct sshkey *a, const struct sshkey *b)
725{
726 if (a == NULL || b == NULL || a->type != b->type)
727 return 0;
728 if (sshkey_is_cert(a)) {
729 if (!cert_compare(a->cert, b->cert))
730 return 0;
731 }
732 return sshkey_equal_public(a, b);
733}
734
735static int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000736to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain,
737 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +1000738{
739 int type, ret = SSH_ERR_INTERNAL_ERROR;
740 const char *typename;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000741#ifdef WITH_OPENSSL
742 const BIGNUM *rsa_n, *rsa_e, *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
743#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +1000744
745 if (key == NULL)
746 return SSH_ERR_INVALID_ARGUMENT;
747
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +0000748 if (sshkey_is_cert(key)) {
749 if (key->cert == NULL)
750 return SSH_ERR_EXPECTED_CERT;
751 if (sshbuf_len(key->cert->certblob) == 0)
752 return SSH_ERR_KEY_LACKS_CERTBLOB;
753 }
Damien Miller86687062014-07-02 15:28:02 +1000754 type = force_plain ? sshkey_type_plain(key->type) : key->type;
755 typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid);
756
757 switch (type) {
758#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +1000759 case KEY_DSA_CERT:
760 case KEY_ECDSA_CERT:
761 case KEY_RSA_CERT:
762#endif /* WITH_OPENSSL */
763 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000764#ifdef WITH_XMSS
765 case KEY_XMSS_CERT:
766#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000767 /* Use the existing blob */
768 /* XXX modified flag? */
769 if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0)
770 return ret;
771 break;
772#ifdef WITH_OPENSSL
773 case KEY_DSA:
774 if (key->dsa == NULL)
775 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000776 DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g);
777 DSA_get0_key(key->dsa, &dsa_pub_key, NULL);
Damien Miller86687062014-07-02 15:28:02 +1000778 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000779 (ret = sshbuf_put_bignum2(b, dsa_p)) != 0 ||
780 (ret = sshbuf_put_bignum2(b, dsa_q)) != 0 ||
781 (ret = sshbuf_put_bignum2(b, dsa_g)) != 0 ||
782 (ret = sshbuf_put_bignum2(b, dsa_pub_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000783 return ret;
784 break;
Darren Tuckerd1a04212014-07-19 07:23:55 +1000785# ifdef OPENSSL_HAS_ECC
Damien Miller86687062014-07-02 15:28:02 +1000786 case KEY_ECDSA:
787 if (key->ecdsa == NULL)
788 return SSH_ERR_INVALID_ARGUMENT;
789 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
790 (ret = sshbuf_put_cstring(b,
791 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
792 (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0)
793 return ret;
794 break;
Darren Tuckerd1a04212014-07-19 07:23:55 +1000795# endif
Damien Miller86687062014-07-02 15:28:02 +1000796 case KEY_RSA:
797 if (key->rsa == NULL)
798 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000799 RSA_get0_key(key->rsa, &rsa_n, &rsa_e, NULL);
Damien Miller86687062014-07-02 15:28:02 +1000800 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000801 (ret = sshbuf_put_bignum2(b, rsa_e)) != 0 ||
802 (ret = sshbuf_put_bignum2(b, rsa_n)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000803 return ret;
804 break;
805#endif /* WITH_OPENSSL */
806 case KEY_ED25519:
807 if (key->ed25519_pk == NULL)
808 return SSH_ERR_INVALID_ARGUMENT;
809 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
810 (ret = sshbuf_put_string(b,
811 key->ed25519_pk, ED25519_PK_SZ)) != 0)
812 return ret;
813 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000814#ifdef WITH_XMSS
815 case KEY_XMSS:
816 if (key->xmss_name == NULL || key->xmss_pk == NULL ||
817 sshkey_xmss_pklen(key) == 0)
818 return SSH_ERR_INVALID_ARGUMENT;
819 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
820 (ret = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
821 (ret = sshbuf_put_string(b,
822 key->xmss_pk, sshkey_xmss_pklen(key))) != 0 ||
823 (ret = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0)
824 return ret;
825 break;
826#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +1000827 default:
828 return SSH_ERR_KEY_TYPE_UNKNOWN;
829 }
830 return 0;
831}
832
833int
djm@openbsd.org60b18252015-01-26 02:59:11 +0000834sshkey_putb(const struct sshkey *key, struct sshbuf *b)
Damien Miller86687062014-07-02 15:28:02 +1000835{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000836 return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000837}
838
839int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000840sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b,
841 enum sshkey_serialize_rep opts)
djm@openbsd.org60b18252015-01-26 02:59:11 +0000842{
843 struct sshbuf *tmp;
844 int r;
845
846 if ((tmp = sshbuf_new()) == NULL)
847 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000848 r = to_blob_buf(key, tmp, 0, opts);
djm@openbsd.org60b18252015-01-26 02:59:11 +0000849 if (r == 0)
850 r = sshbuf_put_stringb(b, tmp);
851 sshbuf_free(tmp);
852 return r;
853}
854
855int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000856sshkey_puts(const struct sshkey *key, struct sshbuf *b)
857{
858 return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT);
859}
860
861int
djm@openbsd.org60b18252015-01-26 02:59:11 +0000862sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b)
Damien Miller86687062014-07-02 15:28:02 +1000863{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000864 return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000865}
866
867static int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000868to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain,
869 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +1000870{
871 int ret = SSH_ERR_INTERNAL_ERROR;
872 size_t len;
873 struct sshbuf *b = NULL;
874
875 if (lenp != NULL)
876 *lenp = 0;
877 if (blobp != NULL)
878 *blobp = NULL;
879 if ((b = sshbuf_new()) == NULL)
880 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000881 if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0)
Damien Miller86687062014-07-02 15:28:02 +1000882 goto out;
883 len = sshbuf_len(b);
884 if (lenp != NULL)
885 *lenp = len;
886 if (blobp != NULL) {
887 if ((*blobp = malloc(len)) == NULL) {
888 ret = SSH_ERR_ALLOC_FAIL;
889 goto out;
890 }
891 memcpy(*blobp, sshbuf_ptr(b), len);
892 }
893 ret = 0;
894 out:
895 sshbuf_free(b);
896 return ret;
897}
898
899int
900sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
901{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000902 return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000903}
904
905int
906sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
907{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000908 return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT);
Damien Miller86687062014-07-02 15:28:02 +1000909}
910
911int
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000912sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg,
Damien Miller86687062014-07-02 15:28:02 +1000913 u_char **retp, size_t *lenp)
914{
915 u_char *blob = NULL, *ret = NULL;
916 size_t blob_len = 0;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000917 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +1000918
919 if (retp != NULL)
920 *retp = NULL;
921 if (lenp != NULL)
922 *lenp = 0;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000923 if (ssh_digest_bytes(dgst_alg) == 0) {
Damien Miller86687062014-07-02 15:28:02 +1000924 r = SSH_ERR_INVALID_ARGUMENT;
925 goto out;
926 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000927 if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT))
928 != 0)
Damien Miller86687062014-07-02 15:28:02 +1000929 goto out;
930 if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) {
931 r = SSH_ERR_ALLOC_FAIL;
932 goto out;
933 }
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000934 if ((r = ssh_digest_memory(dgst_alg, blob, blob_len,
Damien Miller86687062014-07-02 15:28:02 +1000935 ret, SSH_DIGEST_MAX_LENGTH)) != 0)
936 goto out;
937 /* success */
938 if (retp != NULL) {
939 *retp = ret;
940 ret = NULL;
941 }
942 if (lenp != NULL)
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000943 *lenp = ssh_digest_bytes(dgst_alg);
Damien Miller86687062014-07-02 15:28:02 +1000944 r = 0;
945 out:
946 free(ret);
947 if (blob != NULL) {
948 explicit_bzero(blob, blob_len);
949 free(blob);
950 }
951 return r;
952}
953
954static char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000955fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
Damien Miller86687062014-07-02 15:28:02 +1000956{
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000957 char *ret;
958 size_t plen = strlen(alg) + 1;
959 size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1;
960 int r;
Damien Miller86687062014-07-02 15:28:02 +1000961
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000962 if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL)
Damien Miller86687062014-07-02 15:28:02 +1000963 return NULL;
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000964 strlcpy(ret, alg, rlen);
965 strlcat(ret, ":", rlen);
966 if (dgst_raw_len == 0)
967 return ret;
968 if ((r = b64_ntop(dgst_raw, dgst_raw_len,
969 ret + plen, rlen - plen)) == -1) {
jsing@openbsd.org4270efa2018-02-14 16:03:32 +0000970 freezero(ret, rlen);
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000971 return NULL;
Damien Miller86687062014-07-02 15:28:02 +1000972 }
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000973 /* Trim padding characters from end */
974 ret[strcspn(ret, "=")] = '\0';
975 return ret;
976}
Damien Miller86687062014-07-02 15:28:02 +1000977
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000978static char *
979fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
980{
981 char *retval, hex[5];
982 size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2;
983
984 if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL)
985 return NULL;
986 strlcpy(retval, alg, rlen);
987 strlcat(retval, ":", rlen);
988 for (i = 0; i < dgst_raw_len; i++) {
989 snprintf(hex, sizeof(hex), "%s%02x",
990 i > 0 ? ":" : "", dgst_raw[i]);
991 strlcat(retval, hex, rlen);
992 }
Damien Miller86687062014-07-02 15:28:02 +1000993 return retval;
994}
995
996static char *
997fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len)
998{
999 char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' };
1000 char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
1001 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' };
1002 u_int i, j = 0, rounds, seed = 1;
1003 char *retval;
1004
1005 rounds = (dgst_raw_len / 2) + 1;
1006 if ((retval = calloc(rounds, 6)) == NULL)
1007 return NULL;
1008 retval[j++] = 'x';
1009 for (i = 0; i < rounds; i++) {
1010 u_int idx0, idx1, idx2, idx3, idx4;
1011 if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) {
1012 idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) +
1013 seed) % 6;
1014 idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15;
1015 idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) +
1016 (seed / 6)) % 6;
1017 retval[j++] = vowels[idx0];
1018 retval[j++] = consonants[idx1];
1019 retval[j++] = vowels[idx2];
1020 if ((i + 1) < rounds) {
1021 idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15;
1022 idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15;
1023 retval[j++] = consonants[idx3];
1024 retval[j++] = '-';
1025 retval[j++] = consonants[idx4];
1026 seed = ((seed * 5) +
1027 ((((u_int)(dgst_raw[2 * i])) * 7) +
1028 ((u_int)(dgst_raw[(2 * i) + 1])))) % 36;
1029 }
1030 } else {
1031 idx0 = seed % 6;
1032 idx1 = 16;
1033 idx2 = seed / 6;
1034 retval[j++] = vowels[idx0];
1035 retval[j++] = consonants[idx1];
1036 retval[j++] = vowels[idx2];
1037 }
1038 }
1039 retval[j++] = 'x';
1040 retval[j++] = '\0';
1041 return retval;
1042}
1043
1044/*
1045 * Draw an ASCII-Art representing the fingerprint so human brain can
1046 * profit from its built-in pattern recognition ability.
1047 * This technique is called "random art" and can be found in some
1048 * scientific publications like this original paper:
1049 *
1050 * "Hash Visualization: a New Technique to improve Real-World Security",
1051 * Perrig A. and Song D., 1999, International Workshop on Cryptographic
1052 * Techniques and E-Commerce (CrypTEC '99)
1053 * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
1054 *
1055 * The subject came up in a talk by Dan Kaminsky, too.
1056 *
1057 * If you see the picture is different, the key is different.
1058 * If the picture looks the same, you still know nothing.
1059 *
1060 * The algorithm used here is a worm crawling over a discrete plane,
1061 * leaving a trace (augmenting the field) everywhere it goes.
1062 * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
1063 * makes the respective movement vector be ignored for this turn.
1064 * Graphs are not unambiguous, because circles in graphs can be
1065 * walked in either direction.
1066 */
1067
1068/*
1069 * Field sizes for the random art. Have to be odd, so the starting point
1070 * can be in the exact middle of the picture, and FLDBASE should be >=8 .
1071 * Else pictures would be too dense, and drawing the frame would
1072 * fail, too, because the key type would not fit in anymore.
1073 */
1074#define FLDBASE 8
1075#define FLDSIZE_Y (FLDBASE + 1)
1076#define FLDSIZE_X (FLDBASE * 2 + 1)
1077static char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001078fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len,
Damien Miller86687062014-07-02 15:28:02 +10001079 const struct sshkey *k)
1080{
1081 /*
1082 * Chars to be used after each other every time the worm
1083 * intersects with itself. Matter of taste.
1084 */
1085 char *augmentation_string = " .o+=*BOX@%&#/^SE";
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001086 char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X];
Damien Miller86687062014-07-02 15:28:02 +10001087 u_char field[FLDSIZE_X][FLDSIZE_Y];
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001088 size_t i, tlen, hlen;
Damien Miller86687062014-07-02 15:28:02 +10001089 u_int b;
Damien Miller61e28e52014-07-03 21:22:22 +10001090 int x, y, r;
Damien Miller86687062014-07-02 15:28:02 +10001091 size_t len = strlen(augmentation_string) - 1;
1092
1093 if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL)
1094 return NULL;
1095
1096 /* initialize field */
1097 memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
1098 x = FLDSIZE_X / 2;
1099 y = FLDSIZE_Y / 2;
1100
1101 /* process raw key */
1102 for (i = 0; i < dgst_raw_len; i++) {
1103 int input;
1104 /* each byte conveys four 2-bit move commands */
1105 input = dgst_raw[i];
1106 for (b = 0; b < 4; b++) {
1107 /* evaluate 2 bit, rest is shifted later */
1108 x += (input & 0x1) ? 1 : -1;
1109 y += (input & 0x2) ? 1 : -1;
1110
1111 /* assure we are still in bounds */
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001112 x = MAXIMUM(x, 0);
1113 y = MAXIMUM(y, 0);
1114 x = MINIMUM(x, FLDSIZE_X - 1);
1115 y = MINIMUM(y, FLDSIZE_Y - 1);
Damien Miller86687062014-07-02 15:28:02 +10001116
1117 /* augment the field */
1118 if (field[x][y] < len - 2)
1119 field[x][y]++;
1120 input = input >> 2;
1121 }
1122 }
1123
1124 /* mark starting point and end point*/
1125 field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
1126 field[x][y] = len;
1127
Damien Miller61e28e52014-07-03 21:22:22 +10001128 /* assemble title */
1129 r = snprintf(title, sizeof(title), "[%s %u]",
1130 sshkey_type(k), sshkey_size(k));
1131 /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */
1132 if (r < 0 || r > (int)sizeof(title))
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001133 r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k));
1134 tlen = (r <= 0) ? 0 : strlen(title);
1135
1136 /* assemble hash ID. */
1137 r = snprintf(hash, sizeof(hash), "[%s]", alg);
1138 hlen = (r <= 0) ? 0 : strlen(hash);
Damien Miller86687062014-07-02 15:28:02 +10001139
1140 /* output upper border */
Damien Miller61e28e52014-07-03 21:22:22 +10001141 p = retval;
1142 *p++ = '+';
1143 for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++)
1144 *p++ = '-';
1145 memcpy(p, title, tlen);
1146 p += tlen;
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001147 for (i += tlen; i < FLDSIZE_X; i++)
Damien Miller86687062014-07-02 15:28:02 +10001148 *p++ = '-';
1149 *p++ = '+';
1150 *p++ = '\n';
1151
1152 /* output content */
1153 for (y = 0; y < FLDSIZE_Y; y++) {
1154 *p++ = '|';
1155 for (x = 0; x < FLDSIZE_X; x++)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001156 *p++ = augmentation_string[MINIMUM(field[x][y], len)];
Damien Miller86687062014-07-02 15:28:02 +10001157 *p++ = '|';
1158 *p++ = '\n';
1159 }
1160
1161 /* output lower border */
1162 *p++ = '+';
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001163 for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++)
1164 *p++ = '-';
1165 memcpy(p, hash, hlen);
1166 p += hlen;
1167 for (i += hlen; i < FLDSIZE_X; i++)
Damien Miller86687062014-07-02 15:28:02 +10001168 *p++ = '-';
1169 *p++ = '+';
1170
1171 return retval;
1172}
1173
1174char *
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001175sshkey_fingerprint(const struct sshkey *k, int dgst_alg,
Damien Miller86687062014-07-02 15:28:02 +10001176 enum sshkey_fp_rep dgst_rep)
1177{
1178 char *retval = NULL;
1179 u_char *dgst_raw;
1180 size_t dgst_raw_len;
1181
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001182 if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001183 return NULL;
1184 switch (dgst_rep) {
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001185 case SSH_FP_DEFAULT:
1186 if (dgst_alg == SSH_DIGEST_MD5) {
1187 retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
1188 dgst_raw, dgst_raw_len);
1189 } else {
1190 retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
1191 dgst_raw, dgst_raw_len);
1192 }
1193 break;
Damien Miller86687062014-07-02 15:28:02 +10001194 case SSH_FP_HEX:
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001195 retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg),
1196 dgst_raw, dgst_raw_len);
1197 break;
1198 case SSH_FP_BASE64:
1199 retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg),
1200 dgst_raw, dgst_raw_len);
Damien Miller86687062014-07-02 15:28:02 +10001201 break;
1202 case SSH_FP_BUBBLEBABBLE:
1203 retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
1204 break;
1205 case SSH_FP_RANDOMART:
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001206 retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg),
1207 dgst_raw, dgst_raw_len, k);
Damien Miller86687062014-07-02 15:28:02 +10001208 break;
1209 default:
1210 explicit_bzero(dgst_raw, dgst_raw_len);
1211 free(dgst_raw);
1212 return NULL;
1213 }
1214 explicit_bzero(dgst_raw, dgst_raw_len);
1215 free(dgst_raw);
1216 return retval;
1217}
1218
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001219static int
1220peek_type_nid(const char *s, size_t l, int *nid)
1221{
1222 const struct keytype *kt;
Damien Miller86687062014-07-02 15:28:02 +10001223
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001224 for (kt = keytypes; kt->type != -1; kt++) {
1225 if (kt->name == NULL || strlen(kt->name) != l)
1226 continue;
1227 if (memcmp(s, kt->name, l) == 0) {
1228 *nid = -1;
1229 if (kt->type == KEY_ECDSA || kt->type == KEY_ECDSA_CERT)
1230 *nid = kt->nid;
1231 return kt->type;
1232 }
1233 }
1234 return KEY_UNSPEC;
1235}
1236
1237/* XXX this can now be made const char * */
Damien Miller86687062014-07-02 15:28:02 +10001238int
1239sshkey_read(struct sshkey *ret, char **cpp)
1240{
1241 struct sshkey *k;
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001242 char *cp, *blobcopy;
1243 size_t space;
Damien Miller86687062014-07-02 15:28:02 +10001244 int r, type, curve_nid = -1;
1245 struct sshbuf *blob;
Damien Miller86687062014-07-02 15:28:02 +10001246
dtucker@openbsd.org7fadbb62017-03-10 03:48:57 +00001247 if (ret == NULL)
1248 return SSH_ERR_INVALID_ARGUMENT;
1249
Damien Miller86687062014-07-02 15:28:02 +10001250 switch (ret->type) {
Damien Miller86687062014-07-02 15:28:02 +10001251 case KEY_UNSPEC:
1252 case KEY_RSA:
1253 case KEY_DSA:
1254 case KEY_ECDSA:
1255 case KEY_ED25519:
Damien Miller86687062014-07-02 15:28:02 +10001256 case KEY_DSA_CERT:
1257 case KEY_ECDSA_CERT:
1258 case KEY_RSA_CERT:
1259 case KEY_ED25519_CERT:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001260#ifdef WITH_XMSS
1261 case KEY_XMSS:
1262 case KEY_XMSS_CERT:
1263#endif /* WITH_XMSS */
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001264 break; /* ok */
Damien Miller86687062014-07-02 15:28:02 +10001265 default:
1266 return SSH_ERR_INVALID_ARGUMENT;
1267 }
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001268
1269 /* Decode type */
1270 cp = *cpp;
1271 space = strcspn(cp, " \t");
1272 if (space == strlen(cp))
1273 return SSH_ERR_INVALID_FORMAT;
1274 if ((type = peek_type_nid(cp, space, &curve_nid)) == KEY_UNSPEC)
1275 return SSH_ERR_INVALID_FORMAT;
1276
1277 /* skip whitespace */
1278 for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
1279 ;
1280 if (*cp == '\0')
1281 return SSH_ERR_INVALID_FORMAT;
1282 if (ret->type != KEY_UNSPEC && ret->type != type)
1283 return SSH_ERR_KEY_TYPE_MISMATCH;
1284 if ((blob = sshbuf_new()) == NULL)
1285 return SSH_ERR_ALLOC_FAIL;
1286
1287 /* find end of keyblob and decode */
1288 space = strcspn(cp, " \t");
1289 if ((blobcopy = strndup(cp, space)) == NULL) {
1290 sshbuf_free(blob);
1291 return SSH_ERR_ALLOC_FAIL;
1292 }
1293 if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) {
1294 free(blobcopy);
1295 sshbuf_free(blob);
1296 return r;
1297 }
1298 free(blobcopy);
1299 if ((r = sshkey_fromb(blob, &k)) != 0) {
1300 sshbuf_free(blob);
1301 return r;
1302 }
1303 sshbuf_free(blob);
1304
1305 /* skip whitespace and leave cp at start of comment */
1306 for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
1307 ;
1308
1309 /* ensure type of blob matches type at start of line */
1310 if (k->type != type) {
1311 sshkey_free(k);
1312 return SSH_ERR_KEY_TYPE_MISMATCH;
1313 }
1314 if (sshkey_type_plain(type) == KEY_ECDSA && curve_nid != k->ecdsa_nid) {
1315 sshkey_free(k);
1316 return SSH_ERR_EC_CURVE_MISMATCH;
1317 }
1318
1319 /* Fill in ret from parsed key */
1320 ret->type = type;
1321 if (sshkey_is_cert(ret)) {
1322 if (!sshkey_is_cert(k)) {
1323 sshkey_free(k);
1324 return SSH_ERR_EXPECTED_CERT;
1325 }
1326 if (ret->cert != NULL)
1327 cert_free(ret->cert);
1328 ret->cert = k->cert;
1329 k->cert = NULL;
1330 }
1331 switch (sshkey_type_plain(ret->type)) {
1332#ifdef WITH_OPENSSL
1333 case KEY_RSA:
1334 RSA_free(ret->rsa);
1335 ret->rsa = k->rsa;
1336 k->rsa = NULL;
1337#ifdef DEBUG_PK
1338 RSA_print_fp(stderr, ret->rsa, 8);
1339#endif
1340 break;
1341 case KEY_DSA:
1342 DSA_free(ret->dsa);
1343 ret->dsa = k->dsa;
1344 k->dsa = NULL;
1345#ifdef DEBUG_PK
1346 DSA_print_fp(stderr, ret->dsa, 8);
1347#endif
1348 break;
1349# ifdef OPENSSL_HAS_ECC
1350 case KEY_ECDSA:
1351 EC_KEY_free(ret->ecdsa);
1352 ret->ecdsa = k->ecdsa;
1353 ret->ecdsa_nid = k->ecdsa_nid;
1354 k->ecdsa = NULL;
1355 k->ecdsa_nid = -1;
1356#ifdef DEBUG_PK
1357 sshkey_dump_ec_key(ret->ecdsa);
1358#endif
1359 break;
1360# endif /* OPENSSL_HAS_ECC */
1361#endif /* WITH_OPENSSL */
1362 case KEY_ED25519:
1363 freezero(ret->ed25519_pk, ED25519_PK_SZ);
1364 ret->ed25519_pk = k->ed25519_pk;
1365 k->ed25519_pk = NULL;
1366#ifdef DEBUG_PK
1367 /* XXX */
1368#endif
1369 break;
1370#ifdef WITH_XMSS
1371 case KEY_XMSS:
1372 free(ret->xmss_pk);
1373 ret->xmss_pk = k->xmss_pk;
1374 k->xmss_pk = NULL;
1375 free(ret->xmss_state);
1376 ret->xmss_state = k->xmss_state;
1377 k->xmss_state = NULL;
1378 free(ret->xmss_name);
1379 ret->xmss_name = k->xmss_name;
1380 k->xmss_name = NULL;
1381 free(ret->xmss_filename);
1382 ret->xmss_filename = k->xmss_filename;
1383 k->xmss_filename = NULL;
1384#ifdef DEBUG_PK
1385 /* XXX */
1386#endif
1387 break;
1388#endif /* WITH_XMSS */
1389 default:
1390 sshkey_free(k);
1391 return SSH_ERR_INTERNAL_ERROR;
1392 }
1393 sshkey_free(k);
1394
1395 /* success */
1396 *cpp = cp;
1397 return 0;
Damien Miller86687062014-07-02 15:28:02 +10001398}
1399
djm@openbsd.org94b4e2d2018-03-02 02:08:03 +00001400
Damien Miller86687062014-07-02 15:28:02 +10001401int
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001402sshkey_to_base64(const struct sshkey *key, char **b64p)
Damien Miller86687062014-07-02 15:28:02 +10001403{
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001404 int r = SSH_ERR_INTERNAL_ERROR;
1405 struct sshbuf *b = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001406 char *uu = NULL;
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001407
1408 if (b64p != NULL)
1409 *b64p = NULL;
1410 if ((b = sshbuf_new()) == NULL)
1411 return SSH_ERR_ALLOC_FAIL;
1412 if ((r = sshkey_putb(key, b)) != 0)
1413 goto out;
1414 if ((uu = sshbuf_dtob64(b)) == NULL) {
1415 r = SSH_ERR_ALLOC_FAIL;
1416 goto out;
1417 }
1418 /* Success */
1419 if (b64p != NULL) {
1420 *b64p = uu;
1421 uu = NULL;
1422 }
1423 r = 0;
1424 out:
1425 sshbuf_free(b);
1426 free(uu);
1427 return r;
1428}
1429
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00001430int
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001431sshkey_format_text(const struct sshkey *key, struct sshbuf *b)
1432{
1433 int r = SSH_ERR_INTERNAL_ERROR;
1434 char *uu = NULL;
1435
djm@openbsd.org873d3e72017-04-30 23:18:44 +00001436 if ((r = sshkey_to_base64(key, &uu)) != 0)
1437 goto out;
1438 if ((r = sshbuf_putf(b, "%s %s",
1439 sshkey_ssh_name(key), uu)) != 0)
1440 goto out;
djm@openbsd.orgd80fbe42015-05-21 04:55:51 +00001441 r = 0;
1442 out:
1443 free(uu);
1444 return r;
1445}
1446
1447int
1448sshkey_write(const struct sshkey *key, FILE *f)
1449{
1450 struct sshbuf *b = NULL;
1451 int r = SSH_ERR_INTERNAL_ERROR;
1452
1453 if ((b = sshbuf_new()) == NULL)
1454 return SSH_ERR_ALLOC_FAIL;
1455 if ((r = sshkey_format_text(key, b)) != 0)
1456 goto out;
1457 if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) {
1458 if (feof(f))
1459 errno = EPIPE;
1460 r = SSH_ERR_SYSTEM_ERROR;
1461 goto out;
1462 }
1463 /* Success */
1464 r = 0;
1465 out:
1466 sshbuf_free(b);
1467 return r;
Damien Miller86687062014-07-02 15:28:02 +10001468}
1469
1470const char *
1471sshkey_cert_type(const struct sshkey *k)
1472{
1473 switch (k->cert->type) {
1474 case SSH2_CERT_TYPE_USER:
1475 return "user";
1476 case SSH2_CERT_TYPE_HOST:
1477 return "host";
1478 default:
1479 return "unknown";
1480 }
1481}
1482
1483#ifdef WITH_OPENSSL
1484static int
1485rsa_generate_private_key(u_int bits, RSA **rsap)
1486{
1487 RSA *private = NULL;
1488 BIGNUM *f4 = NULL;
1489 int ret = SSH_ERR_INTERNAL_ERROR;
1490
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001491 if (rsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001492 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001493 if (bits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
1494 bits > SSHBUF_MAX_BIGNUM * 8)
1495 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001496 *rsap = NULL;
1497 if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) {
1498 ret = SSH_ERR_ALLOC_FAIL;
1499 goto out;
1500 }
1501 if (!BN_set_word(f4, RSA_F4) ||
1502 !RSA_generate_key_ex(private, bits, f4, NULL)) {
1503 ret = SSH_ERR_LIBCRYPTO_ERROR;
1504 goto out;
1505 }
1506 *rsap = private;
1507 private = NULL;
1508 ret = 0;
1509 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001510 RSA_free(private);
1511 BN_free(f4);
Damien Miller86687062014-07-02 15:28:02 +10001512 return ret;
1513}
1514
1515static int
1516dsa_generate_private_key(u_int bits, DSA **dsap)
1517{
1518 DSA *private;
1519 int ret = SSH_ERR_INTERNAL_ERROR;
1520
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001521 if (dsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001522 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00001523 if (bits != 1024)
1524 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001525 if ((private = DSA_new()) == NULL) {
1526 ret = SSH_ERR_ALLOC_FAIL;
1527 goto out;
1528 }
1529 *dsap = NULL;
1530 if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
1531 NULL, NULL) || !DSA_generate_key(private)) {
Damien Miller86687062014-07-02 15:28:02 +10001532 ret = SSH_ERR_LIBCRYPTO_ERROR;
1533 goto out;
1534 }
1535 *dsap = private;
1536 private = NULL;
1537 ret = 0;
1538 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001539 DSA_free(private);
Damien Miller86687062014-07-02 15:28:02 +10001540 return ret;
1541}
1542
1543# ifdef OPENSSL_HAS_ECC
1544int
1545sshkey_ecdsa_key_to_nid(EC_KEY *k)
1546{
1547 EC_GROUP *eg;
1548 int nids[] = {
1549 NID_X9_62_prime256v1,
1550 NID_secp384r1,
1551# ifdef OPENSSL_HAS_NISTP521
1552 NID_secp521r1,
1553# endif /* OPENSSL_HAS_NISTP521 */
1554 -1
1555 };
1556 int nid;
1557 u_int i;
1558 BN_CTX *bnctx;
1559 const EC_GROUP *g = EC_KEY_get0_group(k);
1560
1561 /*
1562 * The group may be stored in a ASN.1 encoded private key in one of two
1563 * ways: as a "named group", which is reconstituted by ASN.1 object ID
1564 * or explicit group parameters encoded into the key blob. Only the
1565 * "named group" case sets the group NID for us, but we can figure
1566 * it out for the other case by comparing against all the groups that
1567 * are supported.
1568 */
1569 if ((nid = EC_GROUP_get_curve_name(g)) > 0)
1570 return nid;
1571 if ((bnctx = BN_CTX_new()) == NULL)
1572 return -1;
1573 for (i = 0; nids[i] != -1; i++) {
1574 if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) {
1575 BN_CTX_free(bnctx);
1576 return -1;
1577 }
1578 if (EC_GROUP_cmp(g, eg, bnctx) == 0)
1579 break;
1580 EC_GROUP_free(eg);
1581 }
1582 BN_CTX_free(bnctx);
1583 if (nids[i] != -1) {
1584 /* Use the group with the NID attached */
1585 EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
1586 if (EC_KEY_set_group(k, eg) != 1) {
1587 EC_GROUP_free(eg);
1588 return -1;
1589 }
1590 }
1591 return nids[i];
1592}
1593
1594static int
1595ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap)
1596{
1597 EC_KEY *private;
1598 int ret = SSH_ERR_INTERNAL_ERROR;
1599
djm@openbsd.org5f02bb12017-05-08 06:11:06 +00001600 if (nid == NULL || ecdsap == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001601 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org5f02bb12017-05-08 06:11:06 +00001602 if ((*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
1603 return SSH_ERR_KEY_LENGTH;
Damien Miller86687062014-07-02 15:28:02 +10001604 *ecdsap = NULL;
1605 if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) {
1606 ret = SSH_ERR_ALLOC_FAIL;
1607 goto out;
1608 }
1609 if (EC_KEY_generate_key(private) != 1) {
1610 ret = SSH_ERR_LIBCRYPTO_ERROR;
1611 goto out;
1612 }
1613 EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
1614 *ecdsap = private;
1615 private = NULL;
1616 ret = 0;
1617 out:
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00001618 EC_KEY_free(private);
Damien Miller86687062014-07-02 15:28:02 +10001619 return ret;
1620}
1621# endif /* OPENSSL_HAS_ECC */
1622#endif /* WITH_OPENSSL */
1623
1624int
1625sshkey_generate(int type, u_int bits, struct sshkey **keyp)
1626{
1627 struct sshkey *k;
1628 int ret = SSH_ERR_INTERNAL_ERROR;
1629
1630 if (keyp == NULL)
1631 return SSH_ERR_INVALID_ARGUMENT;
1632 *keyp = NULL;
1633 if ((k = sshkey_new(KEY_UNSPEC)) == NULL)
1634 return SSH_ERR_ALLOC_FAIL;
1635 switch (type) {
1636 case KEY_ED25519:
1637 if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL ||
1638 (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) {
1639 ret = SSH_ERR_ALLOC_FAIL;
1640 break;
1641 }
1642 crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
1643 ret = 0;
1644 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001645#ifdef WITH_XMSS
1646 case KEY_XMSS:
1647 ret = sshkey_xmss_generate_private_key(k, bits);
1648 break;
1649#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10001650#ifdef WITH_OPENSSL
1651 case KEY_DSA:
1652 ret = dsa_generate_private_key(bits, &k->dsa);
1653 break;
1654# ifdef OPENSSL_HAS_ECC
1655 case KEY_ECDSA:
1656 ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid,
1657 &k->ecdsa);
1658 break;
1659# endif /* OPENSSL_HAS_ECC */
1660 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10001661 ret = rsa_generate_private_key(bits, &k->rsa);
1662 break;
1663#endif /* WITH_OPENSSL */
1664 default:
1665 ret = SSH_ERR_INVALID_ARGUMENT;
1666 }
1667 if (ret == 0) {
1668 k->type = type;
1669 *keyp = k;
1670 } else
1671 sshkey_free(k);
1672 return ret;
1673}
1674
1675int
1676sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key)
1677{
1678 u_int i;
1679 const struct sshkey_cert *from;
1680 struct sshkey_cert *to;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001681 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +10001682
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001683 if (to_key == NULL || (from = from_key->cert) == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001684 return SSH_ERR_INVALID_ARGUMENT;
1685
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001686 if ((to = cert_new()) == NULL)
Damien Miller86687062014-07-02 15:28:02 +10001687 return SSH_ERR_ALLOC_FAIL;
1688
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001689 if ((r = sshbuf_putb(to->certblob, from->certblob)) != 0 ||
1690 (r = sshbuf_putb(to->critical, from->critical)) != 0 ||
1691 (r = sshbuf_putb(to->extensions, from->extensions)) != 0)
1692 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001693
1694 to->serial = from->serial;
1695 to->type = from->type;
1696 if (from->key_id == NULL)
1697 to->key_id = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001698 else if ((to->key_id = strdup(from->key_id)) == NULL) {
1699 r = SSH_ERR_ALLOC_FAIL;
1700 goto out;
1701 }
Damien Miller86687062014-07-02 15:28:02 +10001702 to->valid_after = from->valid_after;
1703 to->valid_before = from->valid_before;
1704 if (from->signature_key == NULL)
1705 to->signature_key = NULL;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001706 else if ((r = sshkey_from_private(from->signature_key,
Damien Miller86687062014-07-02 15:28:02 +10001707 &to->signature_key)) != 0)
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001708 goto out;
1709 if (from->signature_type != NULL &&
1710 (to->signature_type = strdup(from->signature_type)) == NULL) {
1711 r = SSH_ERR_ALLOC_FAIL;
1712 goto out;
1713 }
1714 if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) {
1715 r = SSH_ERR_INVALID_ARGUMENT;
1716 goto out;
1717 }
Damien Miller86687062014-07-02 15:28:02 +10001718 if (from->nprincipals > 0) {
1719 if ((to->principals = calloc(from->nprincipals,
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001720 sizeof(*to->principals))) == NULL) {
1721 r = SSH_ERR_ALLOC_FAIL;
1722 goto out;
1723 }
Damien Miller86687062014-07-02 15:28:02 +10001724 for (i = 0; i < from->nprincipals; i++) {
1725 to->principals[i] = strdup(from->principals[i]);
1726 if (to->principals[i] == NULL) {
1727 to->nprincipals = i;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001728 r = SSH_ERR_ALLOC_FAIL;
1729 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001730 }
1731 }
1732 }
1733 to->nprincipals = from->nprincipals;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001734
1735 /* success */
1736 cert_free(to_key->cert);
1737 to_key->cert = to;
1738 to = NULL;
1739 r = 0;
1740 out:
1741 cert_free(to);
1742 return r;
Damien Miller86687062014-07-02 15:28:02 +10001743}
1744
1745int
1746sshkey_from_private(const struct sshkey *k, struct sshkey **pkp)
1747{
1748 struct sshkey *n = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001749 int r = SSH_ERR_INTERNAL_ERROR;
1750#ifdef WITH_OPENSSL
1751 const BIGNUM *rsa_n, *rsa_e;
1752 BIGNUM *rsa_n_dup = NULL, *rsa_e_dup = NULL;
1753 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
1754 BIGNUM *dsa_p_dup = NULL, *dsa_q_dup = NULL, *dsa_g_dup = NULL;
1755 BIGNUM *dsa_pub_key_dup = NULL;
1756#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10001757
djm@openbsd.org1a2663a2015-10-15 23:08:23 +00001758 *pkp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10001759 switch (k->type) {
1760#ifdef WITH_OPENSSL
1761 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +10001762 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001763 if ((n = sshkey_new(k->type)) == NULL) {
1764 r = SSH_ERR_ALLOC_FAIL;
1765 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001766 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001767
1768 DSA_get0_pqg(k->dsa, &dsa_p, &dsa_q, &dsa_g);
1769 DSA_get0_key(k->dsa, &dsa_pub_key, NULL);
1770 if ((dsa_p_dup = BN_dup(dsa_p)) == NULL ||
1771 (dsa_q_dup = BN_dup(dsa_q)) == NULL ||
1772 (dsa_g_dup = BN_dup(dsa_g)) == NULL ||
1773 (dsa_pub_key_dup = BN_dup(dsa_pub_key)) == NULL) {
1774 r = SSH_ERR_ALLOC_FAIL;
1775 goto out;
1776 }
1777 if (!DSA_set0_pqg(n->dsa, dsa_p_dup, dsa_q_dup, dsa_g_dup)) {
1778 r = SSH_ERR_LIBCRYPTO_ERROR;
1779 goto out;
1780 }
1781 dsa_p_dup = dsa_q_dup = dsa_g_dup = NULL; /* transferred */
1782 if (!DSA_set0_key(n->dsa, dsa_pub_key_dup, NULL)) {
1783 r = SSH_ERR_LIBCRYPTO_ERROR;
1784 goto out;
1785 }
1786 dsa_pub_key_dup = NULL; /* transferred */
1787
Damien Miller86687062014-07-02 15:28:02 +10001788 break;
1789# ifdef OPENSSL_HAS_ECC
1790 case KEY_ECDSA:
1791 case KEY_ECDSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001792 if ((n = sshkey_new(k->type)) == NULL) {
1793 r = SSH_ERR_ALLOC_FAIL;
1794 goto out;
1795 }
Damien Miller86687062014-07-02 15:28:02 +10001796 n->ecdsa_nid = k->ecdsa_nid;
1797 n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
1798 if (n->ecdsa == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001799 r = SSH_ERR_ALLOC_FAIL;
1800 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001801 }
1802 if (EC_KEY_set_public_key(n->ecdsa,
1803 EC_KEY_get0_public_key(k->ecdsa)) != 1) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001804 r = SSH_ERR_LIBCRYPTO_ERROR;
1805 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001806 }
1807 break;
1808# endif /* OPENSSL_HAS_ECC */
1809 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10001810 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001811 if ((n = sshkey_new(k->type)) == NULL) {
1812 r = SSH_ERR_ALLOC_FAIL;
1813 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001814 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001815 RSA_get0_key(k->rsa, &rsa_n, &rsa_e, NULL);
1816 if ((rsa_n_dup = BN_dup(rsa_n)) == NULL ||
1817 (rsa_e_dup = BN_dup(rsa_e)) == NULL) {
1818 r = SSH_ERR_ALLOC_FAIL;
1819 goto out;
1820 }
1821 if (!RSA_set0_key(n->rsa, rsa_n_dup, rsa_e_dup, NULL)) {
1822 r = SSH_ERR_LIBCRYPTO_ERROR;
1823 goto out;
1824 }
1825 rsa_n_dup = rsa_e_dup = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10001826 break;
1827#endif /* WITH_OPENSSL */
1828 case KEY_ED25519:
1829 case KEY_ED25519_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001830 if ((n = sshkey_new(k->type)) == NULL) {
1831 r = SSH_ERR_ALLOC_FAIL;
1832 goto out;
1833 }
Damien Miller86687062014-07-02 15:28:02 +10001834 if (k->ed25519_pk != NULL) {
1835 if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001836 r = SSH_ERR_ALLOC_FAIL;
1837 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001838 }
1839 memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
1840 }
1841 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001842#ifdef WITH_XMSS
1843 case KEY_XMSS:
1844 case KEY_XMSS_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001845 if ((n = sshkey_new(k->type)) == NULL) {
1846 r = SSH_ERR_ALLOC_FAIL;
1847 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001848 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001849 if ((r = sshkey_xmss_init(n, k->xmss_name)) != 0)
1850 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001851 if (k->xmss_pk != NULL) {
1852 size_t pklen = sshkey_xmss_pklen(k);
1853 if (pklen == 0 || sshkey_xmss_pklen(n) != pklen) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001854 r = SSH_ERR_INTERNAL_ERROR;
1855 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001856 }
1857 if ((n->xmss_pk = malloc(pklen)) == NULL) {
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001858 r = SSH_ERR_ALLOC_FAIL;
1859 goto out;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00001860 }
1861 memcpy(n->xmss_pk, k->xmss_pk, pklen);
1862 }
1863 break;
1864#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10001865 default:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001866 r = SSH_ERR_KEY_TYPE_UNKNOWN;
1867 goto out;
Damien Miller86687062014-07-02 15:28:02 +10001868 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001869 if (sshkey_is_cert(k) && (r = sshkey_cert_copy(k, n)) != 0)
1870 goto out;
1871 /* success */
Damien Miller86687062014-07-02 15:28:02 +10001872 *pkp = n;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001873 n = NULL;
1874 r = 0;
1875 out:
1876 sshkey_free(n);
1877 BN_clear_free(rsa_n_dup);
1878 BN_clear_free(rsa_e_dup);
1879 BN_clear_free(dsa_p_dup);
1880 BN_clear_free(dsa_q_dup);
1881 BN_clear_free(dsa_g_dup);
1882 BN_clear_free(dsa_pub_key_dup);
1883
1884 return r;
Damien Miller86687062014-07-02 15:28:02 +10001885}
1886
1887static int
djm@openbsd.org60b18252015-01-26 02:59:11 +00001888cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
Damien Miller86687062014-07-02 15:28:02 +10001889{
djm@openbsd.org60b18252015-01-26 02:59:11 +00001890 struct sshbuf *principals = NULL, *crit = NULL;
1891 struct sshbuf *exts = NULL, *ca = NULL;
1892 u_char *sig = NULL;
1893 size_t signed_len = 0, slen = 0, kidlen = 0;
Damien Miller86687062014-07-02 15:28:02 +10001894 int ret = SSH_ERR_INTERNAL_ERROR;
Damien Miller86687062014-07-02 15:28:02 +10001895
1896 /* Copy the entire key blob for verification and later serialisation */
djm@openbsd.org60b18252015-01-26 02:59:11 +00001897 if ((ret = sshbuf_putb(key->cert->certblob, certbuf)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001898 return ret;
1899
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00001900 /* Parse body of certificate up to signature */
1901 if ((ret = sshbuf_get_u64(b, &key->cert->serial)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10001902 (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 ||
1903 (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 ||
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001904 (ret = sshbuf_froms(b, &principals)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10001905 (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 ||
1906 (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 ||
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001907 (ret = sshbuf_froms(b, &crit)) != 0 ||
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00001908 (ret = sshbuf_froms(b, &exts)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10001909 (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 ||
djm@openbsd.org60b18252015-01-26 02:59:11 +00001910 (ret = sshbuf_froms(b, &ca)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10001911 /* XXX debug print error for ret */
1912 ret = SSH_ERR_INVALID_FORMAT;
1913 goto out;
1914 }
1915
1916 /* Signature is left in the buffer so we can calculate this length */
1917 signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b);
1918
1919 if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) {
1920 ret = SSH_ERR_INVALID_FORMAT;
1921 goto out;
1922 }
1923
1924 if (key->cert->type != SSH2_CERT_TYPE_USER &&
1925 key->cert->type != SSH2_CERT_TYPE_HOST) {
1926 ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE;
1927 goto out;
1928 }
1929
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001930 /* Parse principals section */
1931 while (sshbuf_len(principals) > 0) {
1932 char *principal = NULL;
1933 char **oprincipals = NULL;
1934
Damien Miller86687062014-07-02 15:28:02 +10001935 if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) {
1936 ret = SSH_ERR_INVALID_FORMAT;
1937 goto out;
1938 }
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001939 if ((ret = sshbuf_get_cstring(principals, &principal,
1940 NULL)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10001941 ret = SSH_ERR_INVALID_FORMAT;
1942 goto out;
1943 }
1944 oprincipals = key->cert->principals;
deraadt@openbsd.org9e509d42017-05-31 09:15:42 +00001945 key->cert->principals = recallocarray(key->cert->principals,
1946 key->cert->nprincipals, key->cert->nprincipals + 1,
1947 sizeof(*key->cert->principals));
Damien Miller86687062014-07-02 15:28:02 +10001948 if (key->cert->principals == NULL) {
1949 free(principal);
1950 key->cert->principals = oprincipals;
1951 ret = SSH_ERR_ALLOC_FAIL;
1952 goto out;
1953 }
1954 key->cert->principals[key->cert->nprincipals++] = principal;
1955 }
1956
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001957 /*
1958 * Stash a copies of the critical options and extensions sections
1959 * for later use.
1960 */
1961 if ((ret = sshbuf_putb(key->cert->critical, crit)) != 0 ||
1962 (exts != NULL &&
1963 (ret = sshbuf_putb(key->cert->extensions, exts)) != 0))
Damien Miller86687062014-07-02 15:28:02 +10001964 goto out;
1965
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001966 /*
1967 * Validate critical options and extensions sections format.
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001968 */
1969 while (sshbuf_len(crit) != 0) {
1970 if ((ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0 ||
1971 (ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0) {
1972 sshbuf_reset(key->cert->critical);
Damien Miller86687062014-07-02 15:28:02 +10001973 ret = SSH_ERR_INVALID_FORMAT;
1974 goto out;
1975 }
1976 }
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001977 while (exts != NULL && sshbuf_len(exts) != 0) {
1978 if ((ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0 ||
1979 (ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0) {
1980 sshbuf_reset(key->cert->extensions);
Damien Miller86687062014-07-02 15:28:02 +10001981 ret = SSH_ERR_INVALID_FORMAT;
1982 goto out;
1983 }
1984 }
Damien Miller86687062014-07-02 15:28:02 +10001985
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00001986 /* Parse CA key and check signature */
djm@openbsd.org60b18252015-01-26 02:59:11 +00001987 if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10001988 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
1989 goto out;
1990 }
1991 if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) {
1992 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
1993 goto out;
1994 }
Damien Miller86687062014-07-02 15:28:02 +10001995 if ((ret = sshkey_verify(key->cert->signature_key, sig, slen,
djm@openbsd.org04c7e282017-12-18 02:25:15 +00001996 sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10001997 goto out;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00001998 if ((ret = get_sigtype(sig, slen, &key->cert->signature_type)) != 0)
1999 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002000
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002001 /* Success */
2002 ret = 0;
Damien Miller86687062014-07-02 15:28:02 +10002003 out:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002004 sshbuf_free(ca);
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00002005 sshbuf_free(crit);
2006 sshbuf_free(exts);
2007 sshbuf_free(principals);
Damien Miller86687062014-07-02 15:28:02 +10002008 free(sig);
2009 return ret;
2010}
2011
2012static int
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002013check_rsa_length(const RSA *rsa)
2014{
2015 const BIGNUM *rsa_n;
2016
2017 RSA_get0_key(rsa, &rsa_n, NULL, NULL);
2018 if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
2019 return SSH_ERR_KEY_LENGTH;
2020 return 0;
2021}
2022
2023static int
djm@openbsd.org60b18252015-01-26 02:59:11 +00002024sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
2025 int allow_cert)
Damien Miller86687062014-07-02 15:28:02 +10002026{
djm@openbsd.org54924b52015-01-14 10:46:28 +00002027 int type, ret = SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002028 char *ktype = NULL, *curve = NULL, *xmss_name = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002029 struct sshkey *key = NULL;
2030 size_t len;
2031 u_char *pk = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00002032 struct sshbuf *copy;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002033#if defined(WITH_OPENSSL)
2034 BIGNUM *rsa_n = NULL, *rsa_e = NULL;
2035 BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_pub_key = NULL;
2036# if defined(OPENSSL_HAS_ECC)
Damien Miller86687062014-07-02 15:28:02 +10002037 EC_POINT *q = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002038# endif /* OPENSSL_HAS_ECC */
2039#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10002040
2041#ifdef DEBUG_PK /* XXX */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002042 sshbuf_dump(b, stderr);
Damien Miller86687062014-07-02 15:28:02 +10002043#endif
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00002044 if (keyp != NULL)
2045 *keyp = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00002046 if ((copy = sshbuf_fromb(b)) == NULL) {
2047 ret = SSH_ERR_ALLOC_FAIL;
2048 goto out;
2049 }
Damien Miller86687062014-07-02 15:28:02 +10002050 if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
2051 ret = SSH_ERR_INVALID_FORMAT;
2052 goto out;
2053 }
2054
2055 type = sshkey_type_from_name(ktype);
Damien Miller86687062014-07-02 15:28:02 +10002056 if (!allow_cert && sshkey_type_is_cert(type)) {
2057 ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2058 goto out;
2059 }
2060 switch (type) {
2061#ifdef WITH_OPENSSL
2062 case KEY_RSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002063 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002064 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2065 ret = SSH_ERR_INVALID_FORMAT;
2066 goto out;
2067 }
2068 /* FALLTHROUGH */
2069 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10002070 if ((key = sshkey_new(type)) == NULL) {
2071 ret = SSH_ERR_ALLOC_FAIL;
2072 goto out;
2073 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002074 if ((rsa_e = BN_new()) == NULL ||
2075 (rsa_n = BN_new()) == NULL) {
2076 ret = SSH_ERR_ALLOC_FAIL;
2077 goto out;
2078 }
2079 if (sshbuf_get_bignum2(b, rsa_e) != 0 ||
2080 sshbuf_get_bignum2(b, rsa_n) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002081 ret = SSH_ERR_INVALID_FORMAT;
2082 goto out;
2083 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002084 if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, NULL)) {
2085 ret = SSH_ERR_LIBCRYPTO_ERROR;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00002086 goto out;
2087 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002088 rsa_n = rsa_e = NULL; /* transferred */
2089 if ((ret = check_rsa_length(key->rsa)) != 0)
2090 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002091#ifdef DEBUG_PK
2092 RSA_print_fp(stderr, key->rsa, 8);
2093#endif
2094 break;
2095 case KEY_DSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002096 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002097 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2098 ret = SSH_ERR_INVALID_FORMAT;
2099 goto out;
2100 }
2101 /* FALLTHROUGH */
2102 case KEY_DSA:
Damien Miller86687062014-07-02 15:28:02 +10002103 if ((key = sshkey_new(type)) == NULL) {
2104 ret = SSH_ERR_ALLOC_FAIL;
2105 goto out;
2106 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002107 if ((dsa_p = BN_new()) == NULL ||
2108 (dsa_q = BN_new()) == NULL ||
2109 (dsa_g = BN_new()) == NULL ||
2110 (dsa_pub_key = BN_new()) == NULL) {
2111 ret = SSH_ERR_ALLOC_FAIL;
2112 goto out;
2113 }
2114 if (sshbuf_get_bignum2(b, dsa_p) != 0 ||
2115 sshbuf_get_bignum2(b, dsa_q) != 0 ||
2116 sshbuf_get_bignum2(b, dsa_g) != 0 ||
2117 sshbuf_get_bignum2(b, dsa_pub_key) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10002118 ret = SSH_ERR_INVALID_FORMAT;
2119 goto out;
2120 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002121 if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) {
2122 ret = SSH_ERR_LIBCRYPTO_ERROR;
2123 goto out;
2124 }
2125 dsa_p = dsa_q = dsa_g = NULL; /* transferred */
2126 if (!DSA_set0_key(key->dsa, dsa_pub_key, NULL)) {
2127 ret = SSH_ERR_LIBCRYPTO_ERROR;
2128 goto out;
2129 }
2130 dsa_pub_key = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10002131#ifdef DEBUG_PK
2132 DSA_print_fp(stderr, key->dsa, 8);
2133#endif
2134 break;
2135 case KEY_ECDSA_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002136 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002137 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2138 ret = SSH_ERR_INVALID_FORMAT;
2139 goto out;
2140 }
2141 /* FALLTHROUGH */
2142# ifdef OPENSSL_HAS_ECC
2143 case KEY_ECDSA:
2144 if ((key = sshkey_new(type)) == NULL) {
2145 ret = SSH_ERR_ALLOC_FAIL;
2146 goto out;
2147 }
djm@openbsd.org54924b52015-01-14 10:46:28 +00002148 key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype);
Damien Miller86687062014-07-02 15:28:02 +10002149 if (sshbuf_get_cstring(b, &curve, NULL) != 0) {
2150 ret = SSH_ERR_INVALID_FORMAT;
2151 goto out;
2152 }
2153 if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
2154 ret = SSH_ERR_EC_CURVE_MISMATCH;
2155 goto out;
2156 }
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00002157 EC_KEY_free(key->ecdsa);
Damien Miller86687062014-07-02 15:28:02 +10002158 if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid))
2159 == NULL) {
2160 ret = SSH_ERR_EC_CURVE_INVALID;
2161 goto out;
2162 }
2163 if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) {
2164 ret = SSH_ERR_ALLOC_FAIL;
2165 goto out;
2166 }
2167 if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) {
2168 ret = SSH_ERR_INVALID_FORMAT;
2169 goto out;
2170 }
2171 if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
2172 q) != 0) {
2173 ret = SSH_ERR_KEY_INVALID_EC_VALUE;
2174 goto out;
2175 }
2176 if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
2177 /* XXX assume it is a allocation error */
2178 ret = SSH_ERR_ALLOC_FAIL;
2179 goto out;
2180 }
2181#ifdef DEBUG_PK
2182 sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q);
2183#endif
2184 break;
2185# endif /* OPENSSL_HAS_ECC */
2186#endif /* WITH_OPENSSL */
2187 case KEY_ED25519_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002188 /* Skip nonce */
Damien Miller86687062014-07-02 15:28:02 +10002189 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2190 ret = SSH_ERR_INVALID_FORMAT;
2191 goto out;
2192 }
2193 /* FALLTHROUGH */
2194 case KEY_ED25519:
2195 if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
2196 goto out;
2197 if (len != ED25519_PK_SZ) {
2198 ret = SSH_ERR_INVALID_FORMAT;
2199 goto out;
2200 }
2201 if ((key = sshkey_new(type)) == NULL) {
2202 ret = SSH_ERR_ALLOC_FAIL;
2203 goto out;
2204 }
2205 key->ed25519_pk = pk;
2206 pk = NULL;
2207 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002208#ifdef WITH_XMSS
2209 case KEY_XMSS_CERT:
2210 /* Skip nonce */
2211 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2212 ret = SSH_ERR_INVALID_FORMAT;
2213 goto out;
2214 }
2215 /* FALLTHROUGH */
2216 case KEY_XMSS:
2217 if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0)
2218 goto out;
2219 if ((key = sshkey_new(type)) == NULL) {
2220 ret = SSH_ERR_ALLOC_FAIL;
2221 goto out;
2222 }
2223 if ((ret = sshkey_xmss_init(key, xmss_name)) != 0)
2224 goto out;
2225 if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
2226 goto out;
2227 if (len == 0 || len != sshkey_xmss_pklen(key)) {
2228 ret = SSH_ERR_INVALID_FORMAT;
2229 goto out;
2230 }
2231 key->xmss_pk = pk;
2232 pk = NULL;
2233 if (type != KEY_XMSS_CERT &&
2234 (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0)
2235 goto out;
2236 break;
2237#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002238 case KEY_UNSPEC:
Damien Miller86687062014-07-02 15:28:02 +10002239 default:
2240 ret = SSH_ERR_KEY_TYPE_UNKNOWN;
2241 goto out;
2242 }
2243
2244 /* Parse certificate potion */
djm@openbsd.org60b18252015-01-26 02:59:11 +00002245 if (sshkey_is_cert(key) && (ret = cert_parse(b, key, copy)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002246 goto out;
2247
2248 if (key != NULL && sshbuf_len(b) != 0) {
2249 ret = SSH_ERR_INVALID_FORMAT;
2250 goto out;
2251 }
2252 ret = 0;
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00002253 if (keyp != NULL) {
2254 *keyp = key;
2255 key = NULL;
2256 }
Damien Miller86687062014-07-02 15:28:02 +10002257 out:
djm@openbsd.org60b18252015-01-26 02:59:11 +00002258 sshbuf_free(copy);
Damien Miller86687062014-07-02 15:28:02 +10002259 sshkey_free(key);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002260 free(xmss_name);
Damien Miller86687062014-07-02 15:28:02 +10002261 free(ktype);
2262 free(curve);
2263 free(pk);
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002264#if defined(WITH_OPENSSL)
2265 BN_clear_free(rsa_n);
2266 BN_clear_free(rsa_e);
2267 BN_clear_free(dsa_p);
2268 BN_clear_free(dsa_q);
2269 BN_clear_free(dsa_g);
2270 BN_clear_free(dsa_pub_key);
2271# if defined(OPENSSL_HAS_ECC)
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00002272 EC_POINT_free(q);
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002273# endif /* OPENSSL_HAS_ECC */
2274#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10002275 return ret;
2276}
2277
2278int
2279sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp)
2280{
djm@openbsd.org60b18252015-01-26 02:59:11 +00002281 struct sshbuf *b;
2282 int r;
2283
2284 if ((b = sshbuf_from(blob, blen)) == NULL)
2285 return SSH_ERR_ALLOC_FAIL;
2286 r = sshkey_from_blob_internal(b, keyp, 1);
2287 sshbuf_free(b);
2288 return r;
2289}
2290
2291int
2292sshkey_fromb(struct sshbuf *b, struct sshkey **keyp)
2293{
2294 return sshkey_from_blob_internal(b, keyp, 1);
2295}
2296
2297int
2298sshkey_froms(struct sshbuf *buf, struct sshkey **keyp)
2299{
2300 struct sshbuf *b;
2301 int r;
2302
2303 if ((r = sshbuf_froms(buf, &b)) != 0)
2304 return r;
2305 r = sshkey_from_blob_internal(b, keyp, 1);
2306 sshbuf_free(b);
2307 return r;
Damien Miller86687062014-07-02 15:28:02 +10002308}
2309
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002310static int
2311get_sigtype(const u_char *sig, size_t siglen, char **sigtypep)
djm@openbsd.org931c78d2017-12-18 02:22:29 +00002312{
2313 int r;
2314 struct sshbuf *b = NULL;
2315 char *sigtype = NULL;
2316
2317 if (sigtypep != NULL)
2318 *sigtypep = NULL;
2319 if ((b = sshbuf_from(sig, siglen)) == NULL)
2320 return SSH_ERR_ALLOC_FAIL;
2321 if ((r = sshbuf_get_cstring(b, &sigtype, NULL)) != 0)
2322 goto out;
2323 /* success */
2324 if (sigtypep != NULL) {
2325 *sigtypep = sigtype;
2326 sigtype = NULL;
2327 }
2328 r = 0;
2329 out:
2330 free(sigtype);
2331 sshbuf_free(b);
2332 return r;
2333}
2334
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002335/*
djm@openbsd.orgba9e7882018-09-12 01:32:54 +00002336 *
2337 * Checks whether a certificate's signature type is allowed.
2338 * Returns 0 (success) if the certificate signature type appears in the
2339 * "allowed" pattern-list, or the key is not a certificate to begin with.
2340 * Otherwise returns a ssherr.h code.
2341 */
2342int
2343sshkey_check_cert_sigtype(const struct sshkey *key, const char *allowed)
2344{
2345 if (key == NULL || allowed == NULL)
2346 return SSH_ERR_INVALID_ARGUMENT;
2347 if (!sshkey_type_is_cert(key->type))
2348 return 0;
2349 if (key->cert == NULL || key->cert->signature_type == NULL)
2350 return SSH_ERR_INVALID_ARGUMENT;
2351 if (match_pattern_list(key->cert->signature_type, allowed, 0) != 1)
2352 return SSH_ERR_SIGN_ALG_UNSUPPORTED;
2353 return 0;
2354}
2355
2356/*
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002357 * Returns the expected signature algorithm for a given public key algorithm.
2358 */
djm@openbsd.orgb4d4eda2018-07-03 13:20:25 +00002359const char *
2360sshkey_sigalg_by_name(const char *name)
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002361{
2362 const struct keytype *kt;
2363
2364 for (kt = keytypes; kt->type != -1; kt++) {
2365 if (strcmp(kt->name, name) != 0)
2366 continue;
2367 if (kt->sigalg != NULL)
2368 return kt->sigalg;
2369 if (!kt->cert)
2370 return kt->name;
2371 return sshkey_ssh_name_from_type_nid(
2372 sshkey_type_plain(kt->type), kt->nid);
2373 }
2374 return NULL;
2375}
2376
2377/*
2378 * Verifies that the signature algorithm appearing inside the signature blob
2379 * matches that which was requested.
2380 */
2381int
2382sshkey_check_sigtype(const u_char *sig, size_t siglen,
2383 const char *requested_alg)
2384{
2385 const char *expected_alg;
2386 char *sigtype = NULL;
2387 int r;
2388
2389 if (requested_alg == NULL)
2390 return 0;
djm@openbsd.orgb4d4eda2018-07-03 13:20:25 +00002391 if ((expected_alg = sshkey_sigalg_by_name(requested_alg)) == NULL)
djm@openbsd.org4ba0d542018-07-03 11:39:54 +00002392 return SSH_ERR_INVALID_ARGUMENT;
2393 if ((r = get_sigtype(sig, siglen, &sigtype)) != 0)
2394 return r;
2395 r = strcmp(expected_alg, sigtype) == 0;
2396 free(sigtype);
2397 return r ? 0 : SSH_ERR_SIGN_ALG_UNSUPPORTED;
2398}
2399
djm@openbsd.org931c78d2017-12-18 02:22:29 +00002400int
Damien Miller86687062014-07-02 15:28:02 +10002401sshkey_sign(const struct sshkey *key,
2402 u_char **sigp, size_t *lenp,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +00002403 const u_char *data, size_t datalen, const char *alg, u_int compat)
Damien Miller86687062014-07-02 15:28:02 +10002404{
2405 if (sigp != NULL)
2406 *sigp = NULL;
2407 if (lenp != NULL)
2408 *lenp = 0;
2409 if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
2410 return SSH_ERR_INVALID_ARGUMENT;
2411 switch (key->type) {
2412#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002413 case KEY_DSA_CERT:
2414 case KEY_DSA:
2415 return ssh_dss_sign(key, sigp, lenp, data, datalen, compat);
2416# ifdef OPENSSL_HAS_ECC
2417 case KEY_ECDSA_CERT:
2418 case KEY_ECDSA:
2419 return ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat);
2420# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002421 case KEY_RSA_CERT:
2422 case KEY_RSA:
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +00002423 return ssh_rsa_sign(key, sigp, lenp, data, datalen, alg);
Damien Miller86687062014-07-02 15:28:02 +10002424#endif /* WITH_OPENSSL */
2425 case KEY_ED25519:
2426 case KEY_ED25519_CERT:
2427 return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002428#ifdef WITH_XMSS
2429 case KEY_XMSS:
2430 case KEY_XMSS_CERT:
2431 return ssh_xmss_sign(key, sigp, lenp, data, datalen, compat);
2432#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002433 default:
2434 return SSH_ERR_KEY_TYPE_UNKNOWN;
2435 }
2436}
2437
2438/*
2439 * ssh_key_verify returns 0 for a correct signature and < 0 on error.
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002440 * If "alg" specified, then the signature must use that algorithm.
Damien Miller86687062014-07-02 15:28:02 +10002441 */
2442int
2443sshkey_verify(const struct sshkey *key,
2444 const u_char *sig, size_t siglen,
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002445 const u_char *data, size_t dlen, const char *alg, u_int compat)
Damien Miller86687062014-07-02 15:28:02 +10002446{
djm@openbsd.org4cf87f42014-12-10 01:24:09 +00002447 if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE)
Damien Miller86687062014-07-02 15:28:02 +10002448 return SSH_ERR_INVALID_ARGUMENT;
2449 switch (key->type) {
2450#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002451 case KEY_DSA_CERT:
2452 case KEY_DSA:
2453 return ssh_dss_verify(key, sig, siglen, data, dlen, compat);
2454# ifdef OPENSSL_HAS_ECC
2455 case KEY_ECDSA_CERT:
2456 case KEY_ECDSA:
2457 return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat);
2458# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002459 case KEY_RSA_CERT:
2460 case KEY_RSA:
djm@openbsd.org04c7e282017-12-18 02:25:15 +00002461 return ssh_rsa_verify(key, sig, siglen, data, dlen, alg);
Damien Miller86687062014-07-02 15:28:02 +10002462#endif /* WITH_OPENSSL */
2463 case KEY_ED25519:
2464 case KEY_ED25519_CERT:
2465 return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002466#ifdef WITH_XMSS
2467 case KEY_XMSS:
2468 case KEY_XMSS_CERT:
2469 return ssh_xmss_verify(key, sig, siglen, data, dlen, compat);
2470#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002471 default:
2472 return SSH_ERR_KEY_TYPE_UNKNOWN;
2473 }
2474}
2475
Damien Miller86687062014-07-02 15:28:02 +10002476/* Convert a plain key to their _CERT equivalent */
2477int
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002478sshkey_to_certified(struct sshkey *k)
Damien Miller86687062014-07-02 15:28:02 +10002479{
2480 int newtype;
2481
2482 switch (k->type) {
2483#ifdef WITH_OPENSSL
2484 case KEY_RSA:
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002485 newtype = KEY_RSA_CERT;
Damien Miller86687062014-07-02 15:28:02 +10002486 break;
2487 case KEY_DSA:
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002488 newtype = KEY_DSA_CERT;
Damien Miller86687062014-07-02 15:28:02 +10002489 break;
2490 case KEY_ECDSA:
Damien Miller86687062014-07-02 15:28:02 +10002491 newtype = KEY_ECDSA_CERT;
2492 break;
2493#endif /* WITH_OPENSSL */
2494 case KEY_ED25519:
Damien Miller86687062014-07-02 15:28:02 +10002495 newtype = KEY_ED25519_CERT;
2496 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002497#ifdef WITH_XMSS
2498 case KEY_XMSS:
2499 newtype = KEY_XMSS_CERT;
2500 break;
2501#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002502 default:
2503 return SSH_ERR_INVALID_ARGUMENT;
2504 }
2505 if ((k->cert = cert_new()) == NULL)
2506 return SSH_ERR_ALLOC_FAIL;
2507 k->type = newtype;
2508 return 0;
2509}
2510
2511/* Convert a certificate to its raw key equivalent */
2512int
2513sshkey_drop_cert(struct sshkey *k)
2514{
2515 if (!sshkey_type_is_cert(k->type))
2516 return SSH_ERR_KEY_TYPE_UNKNOWN;
2517 cert_free(k->cert);
2518 k->cert = NULL;
2519 k->type = sshkey_type_plain(k->type);
2520 return 0;
2521}
2522
2523/* Sign a certified key, (re-)generating the signed certblob. */
2524int
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002525sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
2526 sshkey_certify_signer *signer, void *signer_ctx)
Damien Miller86687062014-07-02 15:28:02 +10002527{
2528 struct sshbuf *principals = NULL;
2529 u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32];
2530 size_t i, ca_len, sig_len;
2531 int ret = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002532 struct sshbuf *cert = NULL;
2533 char *sigtype = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002534#ifdef WITH_OPENSSL
2535 const BIGNUM *rsa_n, *rsa_e, *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key;
2536#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10002537
2538 if (k == NULL || k->cert == NULL ||
2539 k->cert->certblob == NULL || ca == NULL)
2540 return SSH_ERR_INVALID_ARGUMENT;
2541 if (!sshkey_is_cert(k))
2542 return SSH_ERR_KEY_TYPE_UNKNOWN;
2543 if (!sshkey_type_is_valid_ca(ca->type))
2544 return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2545
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002546 /*
2547 * If no alg specified as argument but a signature_type was set,
2548 * then prefer that. If both were specified, then they must match.
2549 */
2550 if (alg == NULL)
2551 alg = k->cert->signature_type;
2552 else if (k->cert->signature_type != NULL &&
2553 strcmp(alg, k->cert->signature_type) != 0)
2554 return SSH_ERR_INVALID_ARGUMENT;
2555
Damien Miller86687062014-07-02 15:28:02 +10002556 if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0)
2557 return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
2558
2559 cert = k->cert->certblob; /* for readability */
2560 sshbuf_reset(cert);
2561 if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0)
2562 goto out;
2563
2564 /* -v01 certs put nonce first */
2565 arc4random_buf(&nonce, sizeof(nonce));
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002566 if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0)
2567 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002568
2569 /* XXX this substantially duplicates to_blob(); refactor */
2570 switch (k->type) {
2571#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10002572 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002573 DSA_get0_pqg(k->dsa, &dsa_p, &dsa_q, &dsa_g);
2574 DSA_get0_key(k->dsa, &dsa_pub_key, NULL);
2575 if ((ret = sshbuf_put_bignum2(cert, dsa_p)) != 0 ||
2576 (ret = sshbuf_put_bignum2(cert, dsa_q)) != 0 ||
2577 (ret = sshbuf_put_bignum2(cert, dsa_g)) != 0 ||
2578 (ret = sshbuf_put_bignum2(cert, dsa_pub_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002579 goto out;
2580 break;
2581# ifdef OPENSSL_HAS_ECC
2582 case KEY_ECDSA_CERT:
2583 if ((ret = sshbuf_put_cstring(cert,
2584 sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 ||
2585 (ret = sshbuf_put_ec(cert,
2586 EC_KEY_get0_public_key(k->ecdsa),
2587 EC_KEY_get0_group(k->ecdsa))) != 0)
2588 goto out;
2589 break;
2590# endif /* OPENSSL_HAS_ECC */
Damien Miller86687062014-07-02 15:28:02 +10002591 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002592 RSA_get0_key(k->rsa, &rsa_n, &rsa_e, NULL);
2593 if ((ret = sshbuf_put_bignum2(cert, rsa_e)) != 0 ||
2594 (ret = sshbuf_put_bignum2(cert, rsa_n)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002595 goto out;
2596 break;
2597#endif /* WITH_OPENSSL */
2598 case KEY_ED25519_CERT:
2599 if ((ret = sshbuf_put_string(cert,
2600 k->ed25519_pk, ED25519_PK_SZ)) != 0)
2601 goto out;
2602 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002603#ifdef WITH_XMSS
2604 case KEY_XMSS_CERT:
2605 if (k->xmss_name == NULL) {
2606 ret = SSH_ERR_INVALID_ARGUMENT;
2607 goto out;
2608 }
2609 if ((ret = sshbuf_put_cstring(cert, k->xmss_name)) ||
2610 (ret = sshbuf_put_string(cert,
2611 k->xmss_pk, sshkey_xmss_pklen(k))) != 0)
2612 goto out;
2613 break;
2614#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002615 default:
2616 ret = SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org55e5bde2015-03-06 01:40:56 +00002617 goto out;
Damien Miller86687062014-07-02 15:28:02 +10002618 }
2619
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002620 if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0 ||
2621 (ret = sshbuf_put_u32(cert, k->cert->type)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10002622 (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0)
2623 goto out;
2624
2625 if ((principals = sshbuf_new()) == NULL) {
2626 ret = SSH_ERR_ALLOC_FAIL;
2627 goto out;
2628 }
2629 for (i = 0; i < k->cert->nprincipals; i++) {
2630 if ((ret = sshbuf_put_cstring(principals,
2631 k->cert->principals[i])) != 0)
2632 goto out;
2633 }
2634 if ((ret = sshbuf_put_stringb(cert, principals)) != 0 ||
2635 (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 ||
2636 (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 ||
djm@openbsd.orgc28fc622015-07-03 03:43:18 +00002637 (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0 ||
2638 (ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0 ||
2639 (ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */
Damien Miller86687062014-07-02 15:28:02 +10002640 (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0)
2641 goto out;
2642
2643 /* Sign the whole mess */
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002644 if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert),
2645 sshbuf_len(cert), alg, 0, signer_ctx)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002646 goto out;
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002647 /* Check and update signature_type against what was actually used */
2648 if ((ret = get_sigtype(sig_blob, sig_len, &sigtype)) != 0)
2649 goto out;
2650 if (alg != NULL && strcmp(alg, sigtype) != 0) {
2651 ret = SSH_ERR_SIGN_ALG_UNSUPPORTED;
2652 goto out;
2653 }
2654 if (k->cert->signature_type == NULL) {
2655 k->cert->signature_type = sigtype;
2656 sigtype = NULL;
2657 }
Damien Miller86687062014-07-02 15:28:02 +10002658 /* Append signature and we are done */
2659 if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0)
2660 goto out;
2661 ret = 0;
2662 out:
2663 if (ret != 0)
2664 sshbuf_reset(cert);
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00002665 free(sig_blob);
2666 free(ca_blob);
djm@openbsd.orga70fd4a2018-09-12 01:31:30 +00002667 free(sigtype);
mmcc@openbsd.org52d70782015-12-11 04:21:11 +00002668 sshbuf_free(principals);
Damien Miller86687062014-07-02 15:28:02 +10002669 return ret;
2670}
2671
djm@openbsd.orga98339e2017-06-28 01:09:22 +00002672static int
2673default_key_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
2674 const u_char *data, size_t datalen,
2675 const char *alg, u_int compat, void *ctx)
2676{
2677 if (ctx != NULL)
2678 return SSH_ERR_INVALID_ARGUMENT;
2679 return sshkey_sign(key, sigp, lenp, data, datalen, alg, compat);
2680}
2681
2682int
2683sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg)
2684{
2685 return sshkey_certify_custom(k, ca, alg, default_key_sign, NULL);
2686}
2687
Damien Miller86687062014-07-02 15:28:02 +10002688int
2689sshkey_cert_check_authority(const struct sshkey *k,
2690 int want_host, int require_principal,
2691 const char *name, const char **reason)
2692{
2693 u_int i, principal_matches;
2694 time_t now = time(NULL);
2695
2696 if (reason != NULL)
2697 *reason = NULL;
2698
2699 if (want_host) {
2700 if (k->cert->type != SSH2_CERT_TYPE_HOST) {
2701 *reason = "Certificate invalid: not a host certificate";
2702 return SSH_ERR_KEY_CERT_INVALID;
2703 }
2704 } else {
2705 if (k->cert->type != SSH2_CERT_TYPE_USER) {
2706 *reason = "Certificate invalid: not a user certificate";
2707 return SSH_ERR_KEY_CERT_INVALID;
2708 }
2709 }
2710 if (now < 0) {
2711 /* yikes - system clock before epoch! */
2712 *reason = "Certificate invalid: not yet valid";
2713 return SSH_ERR_KEY_CERT_INVALID;
2714 }
2715 if ((u_int64_t)now < k->cert->valid_after) {
2716 *reason = "Certificate invalid: not yet valid";
2717 return SSH_ERR_KEY_CERT_INVALID;
2718 }
2719 if ((u_int64_t)now >= k->cert->valid_before) {
2720 *reason = "Certificate invalid: expired";
2721 return SSH_ERR_KEY_CERT_INVALID;
2722 }
2723 if (k->cert->nprincipals == 0) {
2724 if (require_principal) {
2725 *reason = "Certificate lacks principal list";
2726 return SSH_ERR_KEY_CERT_INVALID;
2727 }
2728 } else if (name != NULL) {
2729 principal_matches = 0;
2730 for (i = 0; i < k->cert->nprincipals; i++) {
2731 if (strcmp(name, k->cert->principals[i]) == 0) {
2732 principal_matches = 1;
2733 break;
2734 }
2735 }
2736 if (!principal_matches) {
2737 *reason = "Certificate invalid: name is not a listed "
2738 "principal";
2739 return SSH_ERR_KEY_CERT_INVALID;
2740 }
2741 }
2742 return 0;
2743}
2744
djm@openbsd.org499cf362015-11-19 01:08:55 +00002745size_t
2746sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l)
2747{
2748 char from[32], to[32], ret[64];
2749 time_t tt;
2750 struct tm *tm;
2751
2752 *from = *to = '\0';
2753 if (cert->valid_after == 0 &&
2754 cert->valid_before == 0xffffffffffffffffULL)
2755 return strlcpy(s, "forever", l);
2756
2757 if (cert->valid_after != 0) {
2758 /* XXX revisit INT_MAX in 2038 :) */
2759 tt = cert->valid_after > INT_MAX ?
2760 INT_MAX : cert->valid_after;
2761 tm = localtime(&tt);
2762 strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm);
2763 }
2764 if (cert->valid_before != 0xffffffffffffffffULL) {
2765 /* XXX revisit INT_MAX in 2038 :) */
2766 tt = cert->valid_before > INT_MAX ?
2767 INT_MAX : cert->valid_before;
2768 tm = localtime(&tt);
2769 strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm);
2770 }
2771
2772 if (cert->valid_after == 0)
2773 snprintf(ret, sizeof(ret), "before %s", to);
2774 else if (cert->valid_before == 0xffffffffffffffffULL)
2775 snprintf(ret, sizeof(ret), "after %s", from);
2776 else
2777 snprintf(ret, sizeof(ret), "from %s to %s", from, to);
2778
2779 return strlcpy(s, ret, l);
2780}
2781
Damien Miller86687062014-07-02 15:28:02 +10002782int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002783sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *b,
2784 enum sshkey_serialize_rep opts)
Damien Miller86687062014-07-02 15:28:02 +10002785{
2786 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002787#ifdef WITH_OPENSSL
2788 const BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_iqmp, *rsa_p, *rsa_q;
2789 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key, *dsa_priv_key;
2790#endif /* WITH_OPENSSL */
Damien Miller86687062014-07-02 15:28:02 +10002791
2792 if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0)
2793 goto out;
2794 switch (key->type) {
2795#ifdef WITH_OPENSSL
2796 case KEY_RSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002797 RSA_get0_key(key->rsa, &rsa_n, &rsa_e, &rsa_d);
2798 RSA_get0_factors(key->rsa, &rsa_p, &rsa_q);
2799 RSA_get0_crt_params(key->rsa, NULL, NULL, &rsa_iqmp);
2800 if ((r = sshbuf_put_bignum2(b, rsa_n)) != 0 ||
2801 (r = sshbuf_put_bignum2(b, rsa_e)) != 0 ||
2802 (r = sshbuf_put_bignum2(b, rsa_d)) != 0 ||
2803 (r = sshbuf_put_bignum2(b, rsa_iqmp)) != 0 ||
2804 (r = sshbuf_put_bignum2(b, rsa_p)) != 0 ||
2805 (r = sshbuf_put_bignum2(b, rsa_q)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002806 goto out;
2807 break;
Damien Miller86687062014-07-02 15:28:02 +10002808 case KEY_RSA_CERT:
2809 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
2810 r = SSH_ERR_INVALID_ARGUMENT;
2811 goto out;
2812 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002813 RSA_get0_key(key->rsa, NULL, NULL, &rsa_d);
2814 RSA_get0_factors(key->rsa, &rsa_p, &rsa_q);
2815 RSA_get0_crt_params(key->rsa, NULL, NULL, &rsa_iqmp);
Damien Miller86687062014-07-02 15:28:02 +10002816 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002817 (r = sshbuf_put_bignum2(b, rsa_d)) != 0 ||
2818 (r = sshbuf_put_bignum2(b, rsa_iqmp)) != 0 ||
2819 (r = sshbuf_put_bignum2(b, rsa_p)) != 0 ||
2820 (r = sshbuf_put_bignum2(b, rsa_q)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002821 goto out;
2822 break;
2823 case KEY_DSA:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002824 DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g);
2825 DSA_get0_key(key->dsa, &dsa_pub_key, &dsa_priv_key);
2826 if ((r = sshbuf_put_bignum2(b, dsa_p)) != 0 ||
2827 (r = sshbuf_put_bignum2(b, dsa_q)) != 0 ||
2828 (r = sshbuf_put_bignum2(b, dsa_g)) != 0 ||
2829 (r = sshbuf_put_bignum2(b, dsa_pub_key)) != 0 ||
2830 (r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002831 goto out;
2832 break;
Damien Miller86687062014-07-02 15:28:02 +10002833 case KEY_DSA_CERT:
2834 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
2835 r = SSH_ERR_INVALID_ARGUMENT;
2836 goto out;
2837 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002838 DSA_get0_key(key->dsa, NULL, &dsa_priv_key);
Damien Miller86687062014-07-02 15:28:02 +10002839 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002840 (r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10002841 goto out;
2842 break;
2843# ifdef OPENSSL_HAS_ECC
2844 case KEY_ECDSA:
2845 if ((r = sshbuf_put_cstring(b,
2846 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
2847 (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 ||
2848 (r = sshbuf_put_bignum2(b,
2849 EC_KEY_get0_private_key(key->ecdsa))) != 0)
2850 goto out;
2851 break;
2852 case KEY_ECDSA_CERT:
2853 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
2854 r = SSH_ERR_INVALID_ARGUMENT;
2855 goto out;
2856 }
2857 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2858 (r = sshbuf_put_bignum2(b,
2859 EC_KEY_get0_private_key(key->ecdsa))) != 0)
2860 goto out;
2861 break;
2862# endif /* OPENSSL_HAS_ECC */
2863#endif /* WITH_OPENSSL */
2864 case KEY_ED25519:
2865 if ((r = sshbuf_put_string(b, key->ed25519_pk,
2866 ED25519_PK_SZ)) != 0 ||
2867 (r = sshbuf_put_string(b, key->ed25519_sk,
2868 ED25519_SK_SZ)) != 0)
2869 goto out;
2870 break;
2871 case KEY_ED25519_CERT:
2872 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
2873 r = SSH_ERR_INVALID_ARGUMENT;
2874 goto out;
2875 }
2876 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2877 (r = sshbuf_put_string(b, key->ed25519_pk,
2878 ED25519_PK_SZ)) != 0 ||
2879 (r = sshbuf_put_string(b, key->ed25519_sk,
2880 ED25519_SK_SZ)) != 0)
2881 goto out;
2882 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002883#ifdef WITH_XMSS
2884 case KEY_XMSS:
2885 if (key->xmss_name == NULL) {
2886 r = SSH_ERR_INVALID_ARGUMENT;
2887 goto out;
2888 }
2889 if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
2890 (r = sshbuf_put_string(b, key->xmss_pk,
2891 sshkey_xmss_pklen(key))) != 0 ||
2892 (r = sshbuf_put_string(b, key->xmss_sk,
2893 sshkey_xmss_sklen(key))) != 0 ||
2894 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
2895 goto out;
2896 break;
2897 case KEY_XMSS_CERT:
2898 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0 ||
2899 key->xmss_name == NULL) {
2900 r = SSH_ERR_INVALID_ARGUMENT;
2901 goto out;
2902 }
2903 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2904 (r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
2905 (r = sshbuf_put_string(b, key->xmss_pk,
2906 sshkey_xmss_pklen(key))) != 0 ||
2907 (r = sshbuf_put_string(b, key->xmss_sk,
2908 sshkey_xmss_sklen(key))) != 0 ||
2909 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
2910 goto out;
2911 break;
2912#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10002913 default:
2914 r = SSH_ERR_INVALID_ARGUMENT;
2915 goto out;
2916 }
2917 /* success */
2918 r = 0;
2919 out:
2920 return r;
2921}
2922
2923int
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002924sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
2925{
2926 return sshkey_private_serialize_opt(key, b,
2927 SSHKEY_SERIALIZE_DEFAULT);
2928}
2929
2930int
Damien Miller86687062014-07-02 15:28:02 +10002931sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
2932{
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002933 char *tname = NULL, *curve = NULL, *xmss_name = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002934 struct sshkey *k = NULL;
djm@openbsd.org60b18252015-01-26 02:59:11 +00002935 size_t pklen = 0, sklen = 0;
Damien Miller86687062014-07-02 15:28:02 +10002936 int type, r = SSH_ERR_INTERNAL_ERROR;
2937 u_char *ed25519_pk = NULL, *ed25519_sk = NULL;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00002938 u_char *xmss_pk = NULL, *xmss_sk = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002939#ifdef WITH_OPENSSL
2940 BIGNUM *exponent = NULL;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002941 BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
2942 BIGNUM *rsa_iqmp = NULL, *rsa_p = NULL, *rsa_q = NULL;
2943 BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL;
2944 BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL;
Damien Miller86687062014-07-02 15:28:02 +10002945#endif /* WITH_OPENSSL */
2946
2947 if (kp != NULL)
2948 *kp = NULL;
2949 if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0)
2950 goto out;
2951 type = sshkey_type_from_name(tname);
2952 switch (type) {
2953#ifdef WITH_OPENSSL
2954 case KEY_DSA:
2955 if ((k = sshkey_new_private(type)) == NULL) {
2956 r = SSH_ERR_ALLOC_FAIL;
2957 goto out;
2958 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002959 if ((dsa_p = BN_new()) == NULL ||
2960 (dsa_q = BN_new()) == NULL ||
2961 (dsa_g = BN_new()) == NULL ||
2962 (dsa_pub_key = BN_new()) == NULL ||
2963 (dsa_priv_key = BN_new()) == NULL) {
2964 r = SSH_ERR_ALLOC_FAIL;
Damien Miller86687062014-07-02 15:28:02 +10002965 goto out;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002966 }
2967 if ((r = sshbuf_get_bignum2(buf, dsa_p)) != 0 ||
2968 (r = sshbuf_get_bignum2(buf, dsa_q)) != 0 ||
2969 (r = sshbuf_get_bignum2(buf, dsa_g)) != 0 ||
2970 (r = sshbuf_get_bignum2(buf, dsa_pub_key)) != 0 ||
2971 (r = sshbuf_get_bignum2(buf, dsa_priv_key)) != 0)
2972 goto out;
2973 if (!DSA_set0_pqg(k->dsa, dsa_p, dsa_q, dsa_g)) {
2974 r = SSH_ERR_LIBCRYPTO_ERROR;
2975 goto out;
2976 }
2977 dsa_p = dsa_q = dsa_g = NULL; /* transferred */
2978 if (!DSA_set0_key(k->dsa, dsa_pub_key, dsa_priv_key)) {
2979 r = SSH_ERR_LIBCRYPTO_ERROR;
2980 goto out;
2981 }
2982 dsa_pub_key = dsa_priv_key = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10002983 break;
Damien Miller86687062014-07-02 15:28:02 +10002984 case KEY_DSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002985 if ((dsa_priv_key = BN_new()) == NULL) {
2986 r = SSH_ERR_ALLOC_FAIL;
Damien Miller86687062014-07-02 15:28:02 +10002987 goto out;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00002988 }
2989 if ((r = sshkey_froms(buf, &k)) != 0 ||
2990 (r = sshbuf_get_bignum2(buf, dsa_priv_key)) != 0)
2991 goto out;
2992 if (!DSA_set0_key(k->dsa, NULL, dsa_priv_key)) {
2993 r = SSH_ERR_LIBCRYPTO_ERROR;
2994 goto out;
2995 }
2996 dsa_priv_key = NULL; /* transferred */
Damien Miller86687062014-07-02 15:28:02 +10002997 break;
2998# ifdef OPENSSL_HAS_ECC
2999 case KEY_ECDSA:
3000 if ((k = sshkey_new_private(type)) == NULL) {
3001 r = SSH_ERR_ALLOC_FAIL;
3002 goto out;
3003 }
3004 if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) {
3005 r = SSH_ERR_INVALID_ARGUMENT;
3006 goto out;
3007 }
3008 if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0)
3009 goto out;
3010 if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
3011 r = SSH_ERR_EC_CURVE_MISMATCH;
3012 goto out;
3013 }
3014 k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
3015 if (k->ecdsa == NULL || (exponent = BN_new()) == NULL) {
3016 r = SSH_ERR_LIBCRYPTO_ERROR;
3017 goto out;
3018 }
3019 if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 ||
3020 (r = sshbuf_get_bignum2(buf, exponent)))
3021 goto out;
3022 if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) {
3023 r = SSH_ERR_LIBCRYPTO_ERROR;
3024 goto out;
3025 }
3026 if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
jsg@openbsd.orgf3a3ea12015-09-02 07:51:12 +00003027 EC_KEY_get0_public_key(k->ecdsa))) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003028 (r = sshkey_ec_validate_private(k->ecdsa)) != 0)
3029 goto out;
3030 break;
3031 case KEY_ECDSA_CERT:
3032 if ((exponent = BN_new()) == NULL) {
3033 r = SSH_ERR_LIBCRYPTO_ERROR;
3034 goto out;
3035 }
djm@openbsd.org60b18252015-01-26 02:59:11 +00003036 if ((r = sshkey_froms(buf, &k)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003037 (r = sshbuf_get_bignum2(buf, exponent)) != 0)
3038 goto out;
3039 if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) {
3040 r = SSH_ERR_LIBCRYPTO_ERROR;
3041 goto out;
3042 }
3043 if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
jsg@openbsd.orgf3a3ea12015-09-02 07:51:12 +00003044 EC_KEY_get0_public_key(k->ecdsa))) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003045 (r = sshkey_ec_validate_private(k->ecdsa)) != 0)
3046 goto out;
3047 break;
3048# endif /* OPENSSL_HAS_ECC */
3049 case KEY_RSA:
3050 if ((k = sshkey_new_private(type)) == NULL) {
3051 r = SSH_ERR_ALLOC_FAIL;
3052 goto out;
3053 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003054 if ((rsa_n = BN_new()) == NULL ||
3055 (rsa_e = BN_new()) == NULL ||
3056 (rsa_d = BN_new()) == NULL ||
3057 (rsa_iqmp = BN_new()) == NULL ||
3058 (rsa_p = BN_new()) == NULL ||
3059 (rsa_q = BN_new()) == NULL) {
3060 r = SSH_ERR_ALLOC_FAIL;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00003061 goto out;
3062 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003063 if ((r = sshbuf_get_bignum2(buf, rsa_n)) != 0 ||
3064 (r = sshbuf_get_bignum2(buf, rsa_e)) != 0 ||
3065 (r = sshbuf_get_bignum2(buf, rsa_d)) != 0 ||
3066 (r = sshbuf_get_bignum2(buf, rsa_iqmp)) != 0 ||
3067 (r = sshbuf_get_bignum2(buf, rsa_p)) != 0 ||
3068 (r = sshbuf_get_bignum2(buf, rsa_q)) != 0)
3069 goto out;
3070 if (!RSA_set0_key(k->rsa, rsa_n, rsa_e, rsa_d)) {
3071 r = SSH_ERR_LIBCRYPTO_ERROR;
3072 goto out;
3073 }
3074 rsa_n = rsa_e = rsa_d = NULL; /* transferred */
3075 if (!RSA_set0_factors(k->rsa, rsa_p, rsa_q)) {
3076 r = SSH_ERR_LIBCRYPTO_ERROR;
3077 goto out;
3078 }
3079 rsa_p = rsa_q = NULL; /* transferred */
3080 if ((r = check_rsa_length(k->rsa)) != 0)
3081 goto out;
3082 if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0)
3083 goto out;
Damien Miller86687062014-07-02 15:28:02 +10003084 break;
Damien Miller86687062014-07-02 15:28:02 +10003085 case KEY_RSA_CERT:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003086 if ((rsa_d = BN_new()) == NULL ||
3087 (rsa_iqmp = BN_new()) == NULL ||
3088 (rsa_p = BN_new()) == NULL ||
3089 (rsa_q = BN_new()) == NULL) {
3090 r = SSH_ERR_ALLOC_FAIL;
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00003091 goto out;
3092 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003093 if ((r = sshkey_froms(buf, &k)) != 0 ||
3094 (r = sshbuf_get_bignum2(buf, rsa_d)) != 0 ||
3095 (r = sshbuf_get_bignum2(buf, rsa_iqmp)) != 0 ||
3096 (r = sshbuf_get_bignum2(buf, rsa_p)) != 0 ||
3097 (r = sshbuf_get_bignum2(buf, rsa_q)) != 0)
3098 goto out;
3099 if (!RSA_set0_key(k->rsa, NULL, NULL, rsa_d)) {
3100 r = SSH_ERR_LIBCRYPTO_ERROR;
3101 goto out;
3102 }
3103 rsa_d = NULL; /* transferred */
3104 if (!RSA_set0_factors(k->rsa, rsa_p, rsa_q)) {
3105 r = SSH_ERR_LIBCRYPTO_ERROR;
3106 goto out;
3107 }
3108 rsa_p = rsa_q = NULL; /* transferred */
3109 if ((r = check_rsa_length(k->rsa)) != 0)
3110 goto out;
3111 if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0)
3112 goto out;
Damien Miller86687062014-07-02 15:28:02 +10003113 break;
3114#endif /* WITH_OPENSSL */
3115 case KEY_ED25519:
3116 if ((k = sshkey_new_private(type)) == NULL) {
3117 r = SSH_ERR_ALLOC_FAIL;
3118 goto out;
3119 }
3120 if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 ||
3121 (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0)
3122 goto out;
3123 if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) {
3124 r = SSH_ERR_INVALID_FORMAT;
3125 goto out;
3126 }
3127 k->ed25519_pk = ed25519_pk;
3128 k->ed25519_sk = ed25519_sk;
3129 ed25519_pk = ed25519_sk = NULL;
3130 break;
3131 case KEY_ED25519_CERT:
djm@openbsd.org60b18252015-01-26 02:59:11 +00003132 if ((r = sshkey_froms(buf, &k)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003133 (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 ||
3134 (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0)
3135 goto out;
3136 if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) {
3137 r = SSH_ERR_INVALID_FORMAT;
3138 goto out;
3139 }
3140 k->ed25519_pk = ed25519_pk;
3141 k->ed25519_sk = ed25519_sk;
3142 ed25519_pk = ed25519_sk = NULL;
3143 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003144#ifdef WITH_XMSS
3145 case KEY_XMSS:
3146 if ((k = sshkey_new_private(type)) == NULL) {
3147 r = SSH_ERR_ALLOC_FAIL;
3148 goto out;
3149 }
3150 if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
3151 (r = sshkey_xmss_init(k, xmss_name)) != 0 ||
3152 (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
3153 (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
3154 goto out;
3155 if (pklen != sshkey_xmss_pklen(k) ||
3156 sklen != sshkey_xmss_sklen(k)) {
3157 r = SSH_ERR_INVALID_FORMAT;
3158 goto out;
3159 }
3160 k->xmss_pk = xmss_pk;
3161 k->xmss_sk = xmss_sk;
3162 xmss_pk = xmss_sk = NULL;
3163 /* optional internal state */
3164 if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
3165 goto out;
3166 break;
3167 case KEY_XMSS_CERT:
3168 if ((r = sshkey_froms(buf, &k)) != 0 ||
markus@openbsd.org27979da2018-03-22 07:05:48 +00003169 (r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003170 (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
3171 (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
3172 goto out;
markus@openbsd.org27979da2018-03-22 07:05:48 +00003173 if (strcmp(xmss_name, k->xmss_name)) {
3174 r = SSH_ERR_INVALID_FORMAT;
3175 goto out;
3176 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003177 if (pklen != sshkey_xmss_pklen(k) ||
3178 sklen != sshkey_xmss_sklen(k)) {
3179 r = SSH_ERR_INVALID_FORMAT;
3180 goto out;
3181 }
3182 k->xmss_pk = xmss_pk;
3183 k->xmss_sk = xmss_sk;
3184 xmss_pk = xmss_sk = NULL;
3185 /* optional internal state */
3186 if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
3187 goto out;
3188 break;
3189#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10003190 default:
3191 r = SSH_ERR_KEY_TYPE_UNKNOWN;
3192 goto out;
3193 }
3194#ifdef WITH_OPENSSL
3195 /* enable blinding */
3196 switch (k->type) {
3197 case KEY_RSA:
Damien Miller86687062014-07-02 15:28:02 +10003198 case KEY_RSA_CERT:
Damien Miller86687062014-07-02 15:28:02 +10003199 if (RSA_blinding_on(k->rsa, NULL) != 1) {
3200 r = SSH_ERR_LIBCRYPTO_ERROR;
3201 goto out;
3202 }
3203 break;
3204 }
3205#endif /* WITH_OPENSSL */
3206 /* success */
3207 r = 0;
3208 if (kp != NULL) {
3209 *kp = k;
3210 k = NULL;
3211 }
3212 out:
3213 free(tname);
3214 free(curve);
3215#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00003216 BN_clear_free(exponent);
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003217 BN_clear_free(dsa_p);
3218 BN_clear_free(dsa_q);
3219 BN_clear_free(dsa_g);
3220 BN_clear_free(dsa_pub_key);
3221 BN_clear_free(dsa_priv_key);
3222 BN_clear_free(rsa_n);
3223 BN_clear_free(rsa_e);
3224 BN_clear_free(rsa_d);
3225 BN_clear_free(rsa_p);
3226 BN_clear_free(rsa_q);
3227 BN_clear_free(rsa_iqmp);
Damien Miller86687062014-07-02 15:28:02 +10003228#endif /* WITH_OPENSSL */
3229 sshkey_free(k);
jsing@openbsd.org4270efa2018-02-14 16:03:32 +00003230 freezero(ed25519_pk, pklen);
3231 freezero(ed25519_sk, sklen);
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003232 free(xmss_name);
3233 freezero(xmss_pk, pklen);
3234 freezero(xmss_sk, sklen);
Damien Miller86687062014-07-02 15:28:02 +10003235 return r;
3236}
3237
3238#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
3239int
3240sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
3241{
3242 BN_CTX *bnctx;
3243 EC_POINT *nq = NULL;
3244 BIGNUM *order, *x, *y, *tmp;
3245 int ret = SSH_ERR_KEY_INVALID_EC_VALUE;
3246
djm@openbsd.orga571dbc2016-10-04 21:34:40 +00003247 /*
3248 * NB. This assumes OpenSSL has already verified that the public
3249 * point lies on the curve. This is done by EC_POINT_oct2point()
3250 * implicitly calling EC_POINT_is_on_curve(). If this code is ever
3251 * reachable with public points not unmarshalled using
3252 * EC_POINT_oct2point then the caller will need to explicitly check.
3253 */
3254
Damien Miller86687062014-07-02 15:28:02 +10003255 if ((bnctx = BN_CTX_new()) == NULL)
3256 return SSH_ERR_ALLOC_FAIL;
3257 BN_CTX_start(bnctx);
3258
3259 /*
3260 * We shouldn't ever hit this case because bignum_get_ecpoint()
3261 * refuses to load GF2m points.
3262 */
3263 if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
3264 NID_X9_62_prime_field)
3265 goto out;
3266
3267 /* Q != infinity */
3268 if (EC_POINT_is_at_infinity(group, public))
3269 goto out;
3270
3271 if ((x = BN_CTX_get(bnctx)) == NULL ||
3272 (y = BN_CTX_get(bnctx)) == NULL ||
3273 (order = BN_CTX_get(bnctx)) == NULL ||
3274 (tmp = BN_CTX_get(bnctx)) == NULL) {
3275 ret = SSH_ERR_ALLOC_FAIL;
3276 goto out;
3277 }
3278
3279 /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */
3280 if (EC_GROUP_get_order(group, order, bnctx) != 1 ||
3281 EC_POINT_get_affine_coordinates_GFp(group, public,
3282 x, y, bnctx) != 1) {
3283 ret = SSH_ERR_LIBCRYPTO_ERROR;
3284 goto out;
3285 }
3286 if (BN_num_bits(x) <= BN_num_bits(order) / 2 ||
3287 BN_num_bits(y) <= BN_num_bits(order) / 2)
3288 goto out;
3289
3290 /* nQ == infinity (n == order of subgroup) */
3291 if ((nq = EC_POINT_new(group)) == NULL) {
3292 ret = SSH_ERR_ALLOC_FAIL;
3293 goto out;
3294 }
3295 if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) {
3296 ret = SSH_ERR_LIBCRYPTO_ERROR;
3297 goto out;
3298 }
3299 if (EC_POINT_is_at_infinity(group, nq) != 1)
3300 goto out;
3301
3302 /* x < order - 1, y < order - 1 */
3303 if (!BN_sub(tmp, order, BN_value_one())) {
3304 ret = SSH_ERR_LIBCRYPTO_ERROR;
3305 goto out;
3306 }
3307 if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0)
3308 goto out;
3309 ret = 0;
3310 out:
3311 BN_CTX_free(bnctx);
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00003312 EC_POINT_free(nq);
Damien Miller86687062014-07-02 15:28:02 +10003313 return ret;
3314}
3315
3316int
3317sshkey_ec_validate_private(const EC_KEY *key)
3318{
3319 BN_CTX *bnctx;
3320 BIGNUM *order, *tmp;
3321 int ret = SSH_ERR_KEY_INVALID_EC_VALUE;
3322
3323 if ((bnctx = BN_CTX_new()) == NULL)
3324 return SSH_ERR_ALLOC_FAIL;
3325 BN_CTX_start(bnctx);
3326
3327 if ((order = BN_CTX_get(bnctx)) == NULL ||
3328 (tmp = BN_CTX_get(bnctx)) == NULL) {
3329 ret = SSH_ERR_ALLOC_FAIL;
3330 goto out;
3331 }
3332
3333 /* log2(private) > log2(order)/2 */
3334 if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) {
3335 ret = SSH_ERR_LIBCRYPTO_ERROR;
3336 goto out;
3337 }
3338 if (BN_num_bits(EC_KEY_get0_private_key(key)) <=
3339 BN_num_bits(order) / 2)
3340 goto out;
3341
3342 /* private < order - 1 */
3343 if (!BN_sub(tmp, order, BN_value_one())) {
3344 ret = SSH_ERR_LIBCRYPTO_ERROR;
3345 goto out;
3346 }
3347 if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0)
3348 goto out;
3349 ret = 0;
3350 out:
3351 BN_CTX_free(bnctx);
3352 return ret;
3353}
3354
3355void
3356sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point)
3357{
3358 BIGNUM *x, *y;
3359 BN_CTX *bnctx;
3360
3361 if (point == NULL) {
3362 fputs("point=(NULL)\n", stderr);
3363 return;
3364 }
3365 if ((bnctx = BN_CTX_new()) == NULL) {
3366 fprintf(stderr, "%s: BN_CTX_new failed\n", __func__);
3367 return;
3368 }
3369 BN_CTX_start(bnctx);
3370 if ((x = BN_CTX_get(bnctx)) == NULL ||
3371 (y = BN_CTX_get(bnctx)) == NULL) {
3372 fprintf(stderr, "%s: BN_CTX_get failed\n", __func__);
3373 return;
3374 }
3375 if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
3376 NID_X9_62_prime_field) {
3377 fprintf(stderr, "%s: group is not a prime field\n", __func__);
3378 return;
3379 }
3380 if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y,
3381 bnctx) != 1) {
3382 fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n",
3383 __func__);
3384 return;
3385 }
3386 fputs("x=", stderr);
3387 BN_print_fp(stderr, x);
3388 fputs("\ny=", stderr);
3389 BN_print_fp(stderr, y);
3390 fputs("\n", stderr);
3391 BN_CTX_free(bnctx);
3392}
3393
3394void
3395sshkey_dump_ec_key(const EC_KEY *key)
3396{
3397 const BIGNUM *exponent;
3398
3399 sshkey_dump_ec_point(EC_KEY_get0_group(key),
3400 EC_KEY_get0_public_key(key));
3401 fputs("exponent=", stderr);
3402 if ((exponent = EC_KEY_get0_private_key(key)) == NULL)
3403 fputs("(NULL)", stderr);
3404 else
3405 BN_print_fp(stderr, EC_KEY_get0_private_key(key));
3406 fputs("\n", stderr);
3407}
3408#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
3409
3410static int
3411sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob,
3412 const char *passphrase, const char *comment, const char *ciphername,
3413 int rounds)
3414{
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003415 u_char *cp, *key = NULL, *pubkeyblob = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003416 u_char salt[SALT_LEN];
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003417 char *b64 = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003418 size_t i, pubkeylen, keylen, ivlen, blocksize, authlen;
3419 u_int check;
3420 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003421 struct sshcipher_ctx *ciphercontext = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003422 const struct sshcipher *cipher;
3423 const char *kdfname = KDFNAME;
3424 struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL;
3425
Damien Miller86687062014-07-02 15:28:02 +10003426 if (rounds <= 0)
3427 rounds = DEFAULT_ROUNDS;
3428 if (passphrase == NULL || !strlen(passphrase)) {
3429 ciphername = "none";
3430 kdfname = "none";
3431 } else if (ciphername == NULL)
3432 ciphername = DEFAULT_CIPHERNAME;
Damien Miller86687062014-07-02 15:28:02 +10003433 if ((cipher = cipher_by_name(ciphername)) == NULL) {
djm@openbsd.orgcdccebd2017-04-30 23:15:04 +00003434 r = SSH_ERR_INVALID_ARGUMENT;
Damien Miller86687062014-07-02 15:28:02 +10003435 goto out;
3436 }
3437
3438 if ((kdf = sshbuf_new()) == NULL ||
3439 (encoded = sshbuf_new()) == NULL ||
3440 (encrypted = sshbuf_new()) == NULL) {
3441 r = SSH_ERR_ALLOC_FAIL;
3442 goto out;
3443 }
3444 blocksize = cipher_blocksize(cipher);
3445 keylen = cipher_keylen(cipher);
3446 ivlen = cipher_ivlen(cipher);
3447 authlen = cipher_authlen(cipher);
3448 if ((key = calloc(1, keylen + ivlen)) == NULL) {
3449 r = SSH_ERR_ALLOC_FAIL;
3450 goto out;
3451 }
3452 if (strcmp(kdfname, "bcrypt") == 0) {
3453 arc4random_buf(salt, SALT_LEN);
3454 if (bcrypt_pbkdf(passphrase, strlen(passphrase),
3455 salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) {
3456 r = SSH_ERR_INVALID_ARGUMENT;
3457 goto out;
3458 }
3459 if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 ||
3460 (r = sshbuf_put_u32(kdf, rounds)) != 0)
3461 goto out;
3462 } else if (strcmp(kdfname, "none") != 0) {
3463 /* Unsupported KDF type */
3464 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3465 goto out;
3466 }
3467 if ((r = cipher_init(&ciphercontext, cipher, key, keylen,
3468 key + keylen, ivlen, 1)) != 0)
3469 goto out;
3470
3471 if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 ||
3472 (r = sshbuf_put_cstring(encoded, ciphername)) != 0 ||
3473 (r = sshbuf_put_cstring(encoded, kdfname)) != 0 ||
3474 (r = sshbuf_put_stringb(encoded, kdf)) != 0 ||
3475 (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */
3476 (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 ||
3477 (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0)
3478 goto out;
3479
3480 /* set up the buffer that will be encrypted */
3481
3482 /* Random check bytes */
3483 check = arc4random();
3484 if ((r = sshbuf_put_u32(encrypted, check)) != 0 ||
3485 (r = sshbuf_put_u32(encrypted, check)) != 0)
3486 goto out;
3487
3488 /* append private key and comment*/
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003489 if ((r = sshkey_private_serialize_opt(prv, encrypted,
3490 SSHKEY_SERIALIZE_FULL)) != 0 ||
Damien Miller86687062014-07-02 15:28:02 +10003491 (r = sshbuf_put_cstring(encrypted, comment)) != 0)
3492 goto out;
3493
3494 /* padding */
3495 i = 0;
3496 while (sshbuf_len(encrypted) % blocksize) {
3497 if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0)
3498 goto out;
3499 }
3500
3501 /* length in destination buffer */
3502 if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0)
3503 goto out;
3504
3505 /* encrypt */
3506 if ((r = sshbuf_reserve(encoded,
3507 sshbuf_len(encrypted) + authlen, &cp)) != 0)
3508 goto out;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003509 if ((r = cipher_crypt(ciphercontext, 0, cp,
Damien Miller86687062014-07-02 15:28:02 +10003510 sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0)
3511 goto out;
3512
3513 /* uuencode */
3514 if ((b64 = sshbuf_dtob64(encoded)) == NULL) {
3515 r = SSH_ERR_ALLOC_FAIL;
3516 goto out;
3517 }
3518
3519 sshbuf_reset(blob);
3520 if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0)
3521 goto out;
3522 for (i = 0; i < strlen(b64); i++) {
3523 if ((r = sshbuf_put_u8(blob, b64[i])) != 0)
3524 goto out;
3525 /* insert line breaks */
3526 if (i % 70 == 69 && (r = sshbuf_put_u8(blob, '\n')) != 0)
3527 goto out;
3528 }
3529 if (i % 70 != 69 && (r = sshbuf_put_u8(blob, '\n')) != 0)
3530 goto out;
3531 if ((r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0)
3532 goto out;
3533
3534 /* success */
3535 r = 0;
3536
3537 out:
3538 sshbuf_free(kdf);
3539 sshbuf_free(encoded);
3540 sshbuf_free(encrypted);
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003541 cipher_free(ciphercontext);
Damien Miller86687062014-07-02 15:28:02 +10003542 explicit_bzero(salt, sizeof(salt));
3543 if (key != NULL) {
3544 explicit_bzero(key, keylen + ivlen);
3545 free(key);
3546 }
3547 if (pubkeyblob != NULL) {
3548 explicit_bzero(pubkeyblob, pubkeylen);
3549 free(pubkeyblob);
3550 }
3551 if (b64 != NULL) {
3552 explicit_bzero(b64, strlen(b64));
3553 free(b64);
3554 }
3555 return r;
3556}
3557
3558static int
3559sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
3560 struct sshkey **keyp, char **commentp)
3561{
3562 char *comment = NULL, *ciphername = NULL, *kdfname = NULL;
3563 const struct sshcipher *cipher = NULL;
3564 const u_char *cp;
3565 int r = SSH_ERR_INTERNAL_ERROR;
3566 size_t encoded_len;
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003567 size_t i, keylen = 0, ivlen = 0, authlen = 0, slen = 0;
Damien Miller86687062014-07-02 15:28:02 +10003568 struct sshbuf *encoded = NULL, *decoded = NULL;
3569 struct sshbuf *kdf = NULL, *decrypted = NULL;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003570 struct sshcipher_ctx *ciphercontext = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003571 struct sshkey *k = NULL;
3572 u_char *key = NULL, *salt = NULL, *dp, pad, last;
3573 u_int blocksize, rounds, nkeys, encrypted_len, check1, check2;
3574
Damien Miller86687062014-07-02 15:28:02 +10003575 if (keyp != NULL)
3576 *keyp = NULL;
3577 if (commentp != NULL)
3578 *commentp = NULL;
3579
3580 if ((encoded = sshbuf_new()) == NULL ||
3581 (decoded = sshbuf_new()) == NULL ||
3582 (decrypted = sshbuf_new()) == NULL) {
3583 r = SSH_ERR_ALLOC_FAIL;
3584 goto out;
3585 }
3586
3587 /* check preamble */
3588 cp = sshbuf_ptr(blob);
3589 encoded_len = sshbuf_len(blob);
3590 if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) ||
3591 memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) {
3592 r = SSH_ERR_INVALID_FORMAT;
3593 goto out;
3594 }
3595 cp += MARK_BEGIN_LEN;
3596 encoded_len -= MARK_BEGIN_LEN;
3597
3598 /* Look for end marker, removing whitespace as we go */
3599 while (encoded_len > 0) {
3600 if (*cp != '\n' && *cp != '\r') {
3601 if ((r = sshbuf_put_u8(encoded, *cp)) != 0)
3602 goto out;
3603 }
3604 last = *cp;
3605 encoded_len--;
3606 cp++;
3607 if (last == '\n') {
3608 if (encoded_len >= MARK_END_LEN &&
3609 memcmp(cp, MARK_END, MARK_END_LEN) == 0) {
3610 /* \0 terminate */
3611 if ((r = sshbuf_put_u8(encoded, 0)) != 0)
3612 goto out;
3613 break;
3614 }
3615 }
3616 }
3617 if (encoded_len == 0) {
3618 r = SSH_ERR_INVALID_FORMAT;
3619 goto out;
3620 }
3621
3622 /* decode base64 */
djm@openbsd.org3cc1fbb2014-10-08 21:45:48 +00003623 if ((r = sshbuf_b64tod(decoded, (char *)sshbuf_ptr(encoded))) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003624 goto out;
3625
3626 /* check magic */
3627 if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) ||
3628 memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) {
3629 r = SSH_ERR_INVALID_FORMAT;
3630 goto out;
3631 }
3632 /* parse public portion of key */
3633 if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 ||
3634 (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 ||
3635 (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 ||
3636 (r = sshbuf_froms(decoded, &kdf)) != 0 ||
3637 (r = sshbuf_get_u32(decoded, &nkeys)) != 0 ||
3638 (r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */
3639 (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0)
3640 goto out;
3641
3642 if ((cipher = cipher_by_name(ciphername)) == NULL) {
3643 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3644 goto out;
3645 }
3646 if ((passphrase == NULL || strlen(passphrase) == 0) &&
3647 strcmp(ciphername, "none") != 0) {
3648 /* passphrase required */
3649 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3650 goto out;
3651 }
3652 if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) {
3653 r = SSH_ERR_KEY_UNKNOWN_CIPHER;
3654 goto out;
3655 }
3656 if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) {
3657 r = SSH_ERR_INVALID_FORMAT;
3658 goto out;
3659 }
3660 if (nkeys != 1) {
3661 /* XXX only one key supported */
3662 r = SSH_ERR_INVALID_FORMAT;
3663 goto out;
3664 }
3665
3666 /* check size of encrypted key blob */
3667 blocksize = cipher_blocksize(cipher);
3668 if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
3669 r = SSH_ERR_INVALID_FORMAT;
3670 goto out;
3671 }
3672
3673 /* setup key */
3674 keylen = cipher_keylen(cipher);
3675 ivlen = cipher_ivlen(cipher);
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003676 authlen = cipher_authlen(cipher);
Damien Miller86687062014-07-02 15:28:02 +10003677 if ((key = calloc(1, keylen + ivlen)) == NULL) {
3678 r = SSH_ERR_ALLOC_FAIL;
3679 goto out;
3680 }
3681 if (strcmp(kdfname, "bcrypt") == 0) {
3682 if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 ||
3683 (r = sshbuf_get_u32(kdf, &rounds)) != 0)
3684 goto out;
3685 if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen,
3686 key, keylen + ivlen, rounds) < 0) {
3687 r = SSH_ERR_INVALID_FORMAT;
3688 goto out;
3689 }
3690 }
3691
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003692 /* check that an appropriate amount of auth data is present */
3693 if (sshbuf_len(decoded) < encrypted_len + authlen) {
3694 r = SSH_ERR_INVALID_FORMAT;
3695 goto out;
3696 }
3697
Damien Miller86687062014-07-02 15:28:02 +10003698 /* decrypt private portion of key */
3699 if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 ||
3700 (r = cipher_init(&ciphercontext, cipher, key, keylen,
3701 key + keylen, ivlen, 0)) != 0)
3702 goto out;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003703 if ((r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(decoded),
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003704 encrypted_len, 0, authlen)) != 0) {
Damien Miller86687062014-07-02 15:28:02 +10003705 /* an integrity error here indicates an incorrect passphrase */
3706 if (r == SSH_ERR_MAC_INVALID)
3707 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3708 goto out;
3709 }
djm@openbsd.org63ebf012015-05-08 03:17:49 +00003710 if ((r = sshbuf_consume(decoded, encrypted_len + authlen)) != 0)
Damien Miller86687062014-07-02 15:28:02 +10003711 goto out;
3712 /* there should be no trailing data */
3713 if (sshbuf_len(decoded) != 0) {
3714 r = SSH_ERR_INVALID_FORMAT;
3715 goto out;
3716 }
3717
3718 /* check check bytes */
3719 if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 ||
3720 (r = sshbuf_get_u32(decrypted, &check2)) != 0)
3721 goto out;
3722 if (check1 != check2) {
3723 r = SSH_ERR_KEY_WRONG_PASSPHRASE;
3724 goto out;
3725 }
3726
3727 /* Load the private key and comment */
3728 if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 ||
3729 (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0)
3730 goto out;
3731
3732 /* Check deterministic padding */
3733 i = 0;
3734 while (sshbuf_len(decrypted)) {
3735 if ((r = sshbuf_get_u8(decrypted, &pad)) != 0)
3736 goto out;
3737 if (pad != (++i & 0xff)) {
3738 r = SSH_ERR_INVALID_FORMAT;
3739 goto out;
3740 }
3741 }
3742
3743 /* XXX decode pubkey and check against private */
3744
3745 /* success */
3746 r = 0;
3747 if (keyp != NULL) {
3748 *keyp = k;
3749 k = NULL;
3750 }
3751 if (commentp != NULL) {
3752 *commentp = comment;
3753 comment = NULL;
3754 }
3755 out:
3756 pad = 0;
djm@openbsd.org4706c1d2016-08-03 05:41:57 +00003757 cipher_free(ciphercontext);
Damien Miller86687062014-07-02 15:28:02 +10003758 free(ciphername);
3759 free(kdfname);
3760 free(comment);
3761 if (salt != NULL) {
3762 explicit_bzero(salt, slen);
3763 free(salt);
3764 }
3765 if (key != NULL) {
3766 explicit_bzero(key, keylen + ivlen);
3767 free(key);
3768 }
3769 sshbuf_free(encoded);
3770 sshbuf_free(decoded);
3771 sshbuf_free(kdf);
3772 sshbuf_free(decrypted);
3773 sshkey_free(k);
3774 return r;
3775}
3776
Damien Miller86687062014-07-02 15:28:02 +10003777
3778#ifdef WITH_OPENSSL
3779/* convert SSH v2 key in OpenSSL PEM format */
3780static int
3781sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob,
3782 const char *_passphrase, const char *comment)
3783{
3784 int success, r;
3785 int blen, len = strlen(_passphrase);
3786 u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
Darren Tucker8fed0a52017-03-29 10:16:15 +11003787 const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;
djm@openbsd.org224f1932017-10-13 06:24:51 +00003788 char *bptr;
Damien Miller86687062014-07-02 15:28:02 +10003789 BIO *bio = NULL;
3790
3791 if (len > 0 && len <= 4)
3792 return SSH_ERR_PASSPHRASE_TOO_SHORT;
3793 if ((bio = BIO_new(BIO_s_mem())) == NULL)
3794 return SSH_ERR_ALLOC_FAIL;
3795
3796 switch (key->type) {
3797 case KEY_DSA:
3798 success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
3799 cipher, passphrase, len, NULL, NULL);
3800 break;
3801#ifdef OPENSSL_HAS_ECC
3802 case KEY_ECDSA:
3803 success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
3804 cipher, passphrase, len, NULL, NULL);
3805 break;
3806#endif
3807 case KEY_RSA:
3808 success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
3809 cipher, passphrase, len, NULL, NULL);
3810 break;
3811 default:
3812 success = 0;
3813 break;
3814 }
3815 if (success == 0) {
3816 r = SSH_ERR_LIBCRYPTO_ERROR;
3817 goto out;
3818 }
3819 if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) {
3820 r = SSH_ERR_INTERNAL_ERROR;
3821 goto out;
3822 }
3823 if ((r = sshbuf_put(blob, bptr, blen)) != 0)
3824 goto out;
3825 r = 0;
3826 out:
3827 BIO_free(bio);
3828 return r;
3829}
3830#endif /* WITH_OPENSSL */
3831
3832/* Serialise "key" to buffer "blob" */
3833int
3834sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
3835 const char *passphrase, const char *comment,
3836 int force_new_format, const char *new_format_cipher, int new_format_rounds)
3837{
3838 switch (key->type) {
markus@openbsd.orgf067cca2015-01-12 13:29:27 +00003839#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10003840 case KEY_DSA:
3841 case KEY_ECDSA:
3842 case KEY_RSA:
3843 if (force_new_format) {
3844 return sshkey_private_to_blob2(key, blob, passphrase,
3845 comment, new_format_cipher, new_format_rounds);
3846 }
3847 return sshkey_private_pem_to_blob(key, blob,
3848 passphrase, comment);
3849#endif /* WITH_OPENSSL */
3850 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00003851#ifdef WITH_XMSS
3852 case KEY_XMSS:
3853#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10003854 return sshkey_private_to_blob2(key, blob, passphrase,
3855 comment, new_format_cipher, new_format_rounds);
3856 default:
3857 return SSH_ERR_KEY_TYPE_UNKNOWN;
3858 }
3859}
3860
Damien Miller86687062014-07-02 15:28:02 +10003861
3862#ifdef WITH_OPENSSL
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00003863static int
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00003864translate_libcrypto_error(unsigned long pem_err)
3865{
3866 int pem_reason = ERR_GET_REASON(pem_err);
3867
3868 switch (ERR_GET_LIB(pem_err)) {
3869 case ERR_LIB_PEM:
3870 switch (pem_reason) {
3871 case PEM_R_BAD_PASSWORD_READ:
3872 case PEM_R_PROBLEMS_GETTING_PASSWORD:
3873 case PEM_R_BAD_DECRYPT:
3874 return SSH_ERR_KEY_WRONG_PASSPHRASE;
3875 default:
3876 return SSH_ERR_INVALID_FORMAT;
3877 }
3878 case ERR_LIB_EVP:
3879 switch (pem_reason) {
3880 case EVP_R_BAD_DECRYPT:
3881 return SSH_ERR_KEY_WRONG_PASSPHRASE;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003882#ifdef EVP_R_BN_DECODE_ERROR
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00003883 case EVP_R_BN_DECODE_ERROR:
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003884#endif
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00003885 case EVP_R_DECODE_ERROR:
3886#ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR
3887 case EVP_R_PRIVATE_KEY_DECODE_ERROR:
3888#endif
3889 return SSH_ERR_INVALID_FORMAT;
3890 default:
3891 return SSH_ERR_LIBCRYPTO_ERROR;
3892 }
3893 case ERR_LIB_ASN1:
3894 return SSH_ERR_INVALID_FORMAT;
3895 }
3896 return SSH_ERR_LIBCRYPTO_ERROR;
3897}
3898
3899static void
3900clear_libcrypto_errors(void)
3901{
3902 while (ERR_get_error() != 0)
3903 ;
3904}
3905
3906/*
3907 * Translate OpenSSL error codes to determine whether
3908 * passphrase is required/incorrect.
3909 */
3910static int
3911convert_libcrypto_error(void)
3912{
3913 /*
3914 * Some password errors are reported at the beginning
3915 * of the error queue.
3916 */
3917 if (translate_libcrypto_error(ERR_peek_error()) ==
3918 SSH_ERR_KEY_WRONG_PASSPHRASE)
3919 return SSH_ERR_KEY_WRONG_PASSPHRASE;
3920 return translate_libcrypto_error(ERR_peek_last_error());
3921}
3922
3923static int
Damien Miller86687062014-07-02 15:28:02 +10003924sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00003925 const char *passphrase, struct sshkey **keyp)
Damien Miller86687062014-07-02 15:28:02 +10003926{
3927 EVP_PKEY *pk = NULL;
3928 struct sshkey *prv = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003929 BIO *bio = NULL;
3930 int r;
3931
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00003932 if (keyp != NULL)
3933 *keyp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10003934
3935 if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX)
3936 return SSH_ERR_ALLOC_FAIL;
3937 if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) !=
3938 (int)sshbuf_len(blob)) {
3939 r = SSH_ERR_ALLOC_FAIL;
3940 goto out;
3941 }
3942
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00003943 clear_libcrypto_errors();
Damien Miller86687062014-07-02 15:28:02 +10003944 if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL,
3945 (char *)passphrase)) == NULL) {
djm@openbsd.org2076e4a2017-06-09 06:40:24 +00003946 r = convert_libcrypto_error();
Damien Miller86687062014-07-02 15:28:02 +10003947 goto out;
3948 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003949 if (EVP_PKEY_base_id(pk) == EVP_PKEY_RSA &&
Damien Miller86687062014-07-02 15:28:02 +10003950 (type == KEY_UNSPEC || type == KEY_RSA)) {
3951 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
3952 r = SSH_ERR_ALLOC_FAIL;
3953 goto out;
3954 }
3955 prv->rsa = EVP_PKEY_get1_RSA(pk);
3956 prv->type = KEY_RSA;
Damien Miller86687062014-07-02 15:28:02 +10003957#ifdef DEBUG_PK
3958 RSA_print_fp(stderr, prv->rsa, 8);
3959#endif
3960 if (RSA_blinding_on(prv->rsa, NULL) != 1) {
3961 r = SSH_ERR_LIBCRYPTO_ERROR;
3962 goto out;
3963 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003964 if ((r = check_rsa_length(prv->rsa)) != 0)
djm@openbsd.orgbd636f42017-05-07 23:15:59 +00003965 goto out;
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003966 } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_DSA &&
Damien Miller86687062014-07-02 15:28:02 +10003967 (type == KEY_UNSPEC || type == KEY_DSA)) {
3968 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
3969 r = SSH_ERR_ALLOC_FAIL;
3970 goto out;
3971 }
3972 prv->dsa = EVP_PKEY_get1_DSA(pk);
3973 prv->type = KEY_DSA;
Damien Miller86687062014-07-02 15:28:02 +10003974#ifdef DEBUG_PK
3975 DSA_print_fp(stderr, prv->dsa, 8);
3976#endif
3977#ifdef OPENSSL_HAS_ECC
djm@openbsd.org482d23b2018-09-13 02:08:33 +00003978 } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_EC &&
Damien Miller86687062014-07-02 15:28:02 +10003979 (type == KEY_UNSPEC || type == KEY_ECDSA)) {
3980 if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
3981 r = SSH_ERR_ALLOC_FAIL;
3982 goto out;
3983 }
3984 prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
3985 prv->type = KEY_ECDSA;
3986 prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa);
3987 if (prv->ecdsa_nid == -1 ||
3988 sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
3989 sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
3990 EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
3991 sshkey_ec_validate_private(prv->ecdsa) != 0) {
3992 r = SSH_ERR_INVALID_FORMAT;
3993 goto out;
3994 }
Damien Miller86687062014-07-02 15:28:02 +10003995# ifdef DEBUG_PK
3996 if (prv != NULL && prv->ecdsa != NULL)
3997 sshkey_dump_ec_key(prv->ecdsa);
3998# endif
3999#endif /* OPENSSL_HAS_ECC */
4000 } else {
4001 r = SSH_ERR_INVALID_FORMAT;
4002 goto out;
4003 }
Damien Miller86687062014-07-02 15:28:02 +10004004 r = 0;
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00004005 if (keyp != NULL) {
4006 *keyp = prv;
4007 prv = NULL;
4008 }
Damien Miller86687062014-07-02 15:28:02 +10004009 out:
4010 BIO_free(bio);
jsing@openbsd.org7cd31632018-02-07 02:06:50 +00004011 EVP_PKEY_free(pk);
mmcc@openbsd.org89540b62015-12-11 02:31:47 +00004012 sshkey_free(prv);
Damien Miller86687062014-07-02 15:28:02 +10004013 return r;
4014}
4015#endif /* WITH_OPENSSL */
4016
4017int
4018sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
4019 const char *passphrase, struct sshkey **keyp, char **commentp)
4020{
djm@openbsd.org155d5402017-02-10 04:34:50 +00004021 int r = SSH_ERR_INTERNAL_ERROR;
4022
djm@openbsd.orgdce19bf2016-04-09 12:39:30 +00004023 if (keyp != NULL)
4024 *keyp = NULL;
Damien Miller86687062014-07-02 15:28:02 +10004025 if (commentp != NULL)
4026 *commentp = NULL;
4027
4028 switch (type) {
markus@openbsd.orgf067cca2015-01-12 13:29:27 +00004029#ifdef WITH_OPENSSL
Damien Miller86687062014-07-02 15:28:02 +10004030 case KEY_DSA:
4031 case KEY_ECDSA:
4032 case KEY_RSA:
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00004033 return sshkey_parse_private_pem_fileblob(blob, type,
4034 passphrase, keyp);
Damien Miller86687062014-07-02 15:28:02 +10004035#endif /* WITH_OPENSSL */
4036 case KEY_ED25519:
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00004037#ifdef WITH_XMSS
4038 case KEY_XMSS:
4039#endif /* WITH_XMSS */
Damien Miller86687062014-07-02 15:28:02 +10004040 return sshkey_parse_private2(blob, type, passphrase,
4041 keyp, commentp);
4042 case KEY_UNSPEC:
djm@openbsd.org155d5402017-02-10 04:34:50 +00004043 r = sshkey_parse_private2(blob, type, passphrase, keyp,
4044 commentp);
4045 /* Do not fallback to PEM parser if only passphrase is wrong. */
4046 if (r == 0 || r == SSH_ERR_KEY_WRONG_PASSPHRASE)
4047 return r;
Damien Miller86687062014-07-02 15:28:02 +10004048#ifdef WITH_OPENSSL
djm@openbsd.org1195f4c2015-01-08 10:14:08 +00004049 return sshkey_parse_private_pem_fileblob(blob, type,
4050 passphrase, keyp);
Damien Miller86687062014-07-02 15:28:02 +10004051#else
4052 return SSH_ERR_INVALID_FORMAT;
4053#endif /* WITH_OPENSSL */
4054 default:
4055 return SSH_ERR_KEY_TYPE_UNKNOWN;
4056 }
4057}
4058
4059int
4060sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase,
tim@openbsd.org3c019a92015-09-13 14:39:16 +00004061 struct sshkey **keyp, char **commentp)
Damien Miller86687062014-07-02 15:28:02 +10004062{
Damien Miller86687062014-07-02 15:28:02 +10004063 if (keyp != NULL)
4064 *keyp = NULL;
4065 if (commentp != NULL)
4066 *commentp = NULL;
4067
tim@openbsd.org3c019a92015-09-13 14:39:16 +00004068 return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC,
4069 passphrase, keyp, commentp);
Damien Miller86687062014-07-02 15:28:02 +10004070}
markus@openbsd.org1b11ea72018-02-23 15:58:37 +00004071
4072#ifdef WITH_XMSS
4073/*
4074 * serialize the key with the current state and forward the state
4075 * maxsign times.
4076 */
4077int
4078sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,
4079 u_int32_t maxsign, sshkey_printfn *pr)
4080{
4081 int r, rupdate;
4082
4083 if (maxsign == 0 ||
4084 sshkey_type_plain(k->type) != KEY_XMSS)
4085 return sshkey_private_serialize_opt(k, b,
4086 SSHKEY_SERIALIZE_DEFAULT);
4087 if ((r = sshkey_xmss_get_state(k, pr)) != 0 ||
4088 (r = sshkey_private_serialize_opt(k, b,
4089 SSHKEY_SERIALIZE_STATE)) != 0 ||
4090 (r = sshkey_xmss_forward_state(k, maxsign)) != 0)
4091 goto out;
4092 r = 0;
4093out:
4094 if ((rupdate = sshkey_xmss_update_state(k, pr)) != 0) {
4095 if (r == 0)
4096 r = rupdate;
4097 }
4098 return r;
4099}
4100
4101u_int32_t
4102sshkey_signatures_left(const struct sshkey *k)
4103{
4104 if (sshkey_type_plain(k->type) == KEY_XMSS)
4105 return sshkey_xmss_signatures_left(k);
4106 return 0;
4107}
4108
4109int
4110sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
4111{
4112 if (sshkey_type_plain(k->type) != KEY_XMSS)
4113 return SSH_ERR_INVALID_ARGUMENT;
4114 return sshkey_xmss_enable_maxsign(k, maxsign);
4115}
4116
4117int
4118sshkey_set_filename(struct sshkey *k, const char *filename)
4119{
4120 if (k == NULL)
4121 return SSH_ERR_INVALID_ARGUMENT;
4122 if (sshkey_type_plain(k->type) != KEY_XMSS)
4123 return 0;
4124 if (filename == NULL)
4125 return SSH_ERR_INVALID_ARGUMENT;
4126 if ((k->xmss_filename = strdup(filename)) == NULL)
4127 return SSH_ERR_ALLOC_FAIL;
4128 return 0;
4129}
4130#else
4131int
4132sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,
4133 u_int32_t maxsign, sshkey_printfn *pr)
4134{
4135 return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT);
4136}
4137
4138u_int32_t
4139sshkey_signatures_left(const struct sshkey *k)
4140{
4141 return 0;
4142}
4143
4144int
4145sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
4146{
4147 return SSH_ERR_INVALID_ARGUMENT;
4148}
4149
4150int
4151sshkey_set_filename(struct sshkey *k, const char *filename)
4152{
4153 if (k == NULL)
4154 return SSH_ERR_INVALID_ARGUMENT;
4155 return 0;
4156}
4157#endif /* WITH_XMSS */