summaryrefslogtreecommitdiff
path: root/sshkey-xmss.c
diff options
context:
space:
mode:
authormarkus@openbsd.org <markus@openbsd.org>2018-02-23 15:58:37 +0000
committerDamien Miller <djm@mindrot.org>2018-02-26 11:40:41 +1100
commit1b11ea7c58cd5c59838b5fa574cd456d6047b2d4 (patch)
tree7e96cb41b5234b9d327f7c8f41392f09aed0994e /sshkey-xmss.c
parent7d330a1ac02076de98cfc8fda05353d57b603755 (diff)
upstream: Add experimental support for PQC XMSS keys (Extended
Hash-Based Signatures) The code is not compiled in by default (see WITH_XMSS in Makefile.inc) Joint work with stefan-lukas_gazdag at genua.eu See https://tools.ietf.org/html/draft-irtf-cfrg-xmss-hash-based-signatures-12 ok djm@ OpenBSD-Commit-ID: ef3eccb96762a5d6f135d7daeef608df7776a7ac
Diffstat (limited to 'sshkey-xmss.c')
-rw-r--r--sshkey-xmss.c1048
1 files changed, 1048 insertions, 0 deletions
diff --git a/sshkey-xmss.c b/sshkey-xmss.c
new file mode 100644
index 000000000..41cc1bade
--- /dev/null
+++ b/sshkey-xmss.c
@@ -0,0 +1,1048 @@
1/* $OpenBSD: sshkey-xmss.c,v 1.1 2018/02/23 15:58:38 markus Exp $ */
2/*
3 * Copyright (c) 2017 Markus Friedl. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <sys/types.h>
27#include <sys/uio.h>
28
29#include <stdio.h>
30#include <string.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <errno.h>
34
35#include "ssh2.h"
36#include "ssherr.h"
37#include "sshbuf.h"
38#include "cipher.h"
39#include "sshkey.h"
40#include "sshkey-xmss.h"
41#include "atomicio.h"
42
43#include "xmss_fast.h"
44
45/* opaque internal XMSS state */
46#define XMSS_MAGIC "xmss-state-v1"
47#define XMSS_CIPHERNAME "aes256-gcm@openssh.com"
48struct ssh_xmss_state {
49 xmss_params params;
50 u_int32_t n, w, h, k;
51
52 bds_state bds;
53 u_char *stack;
54 u_int32_t stackoffset;
55 u_char *stacklevels;
56 u_char *auth;
57 u_char *keep;
58 u_char *th_nodes;
59 u_char *retain;
60 treehash_inst *treehash;
61
62 u_int32_t idx; /* state read from file */
63 u_int32_t maxidx; /* resticted # of signatures */
64 int have_state; /* .state file exists */
65 int lockfd; /* locked in sshkey_xmss_get_state() */
66 int allow_update; /* allow sshkey_xmss_update_state() */
67 char *enc_ciphername;/* encrypt state with cipher */
68 u_char *enc_keyiv; /* encrypt state with key */
69 u_int32_t enc_keyiv_len; /* length of enc_keyiv */
70};
71
72int sshkey_xmss_init_bds_state(struct sshkey *);
73int sshkey_xmss_init_enc_key(struct sshkey *, const char *);
74void sshkey_xmss_free_bds(struct sshkey *);
75int sshkey_xmss_get_state_from_file(struct sshkey *, const char *,
76 int *, sshkey_printfn *);
77int sshkey_xmss_encrypt_state(const struct sshkey *, struct sshbuf *,
78 struct sshbuf **);
79int sshkey_xmss_decrypt_state(const struct sshkey *, struct sshbuf *,
80 struct sshbuf **);
81int sshkey_xmss_serialize_enc_key(const struct sshkey *, struct sshbuf *);
82int sshkey_xmss_deserialize_enc_key(struct sshkey *, struct sshbuf *);
83
84#define PRINT(s...) do { if (pr) pr(s); } while (0)
85
86int
87sshkey_xmss_init(struct sshkey *key, const char *name)
88{
89 struct ssh_xmss_state *state;
90
91 if (key->xmss_state != NULL)
92 return SSH_ERR_INVALID_FORMAT;
93 if (name == NULL)
94 return SSH_ERR_INVALID_FORMAT;
95 state = calloc(sizeof(struct ssh_xmss_state), 1);
96 if (state == NULL)
97 return SSH_ERR_ALLOC_FAIL;
98 if (strcmp(name, XMSS_SHA2_256_W16_H10_NAME) == 0) {
99 state->n = 32;
100 state->w = 16;
101 state->h = 10;
102 } else if (strcmp(name, XMSS_SHA2_256_W16_H16_NAME) == 0) {
103 state->n = 32;
104 state->w = 16;
105 state->h = 16;
106 } else if (strcmp(name, XMSS_SHA2_256_W16_H20_NAME) == 0) {
107 state->n = 32;
108 state->w = 16;
109 state->h = 20;
110 } else {
111 free(state);
112 return SSH_ERR_KEY_TYPE_UNKNOWN;
113 }
114 if ((key->xmss_name = strdup(name)) == NULL) {
115 free(state);
116 return SSH_ERR_ALLOC_FAIL;
117 }
118 state->k = 2; /* XXX hardcoded */
119 state->lockfd = -1;
120 if (xmss_set_params(&state->params, state->n, state->h, state->w,
121 state->k) != 0) {
122 free(state);
123 return SSH_ERR_INVALID_FORMAT;
124 }
125 key->xmss_state = state;
126 return 0;
127}
128
129void
130sshkey_xmss_free_state(struct sshkey *key)
131{
132 struct ssh_xmss_state *state = key->xmss_state;
133
134 sshkey_xmss_free_bds(key);
135 if (state) {
136 if (state->enc_keyiv) {
137 explicit_bzero(state->enc_keyiv, state->enc_keyiv_len);
138 free(state->enc_keyiv);
139 }
140 free(state->enc_ciphername);
141 free(state);
142 }
143 key->xmss_state = NULL;
144}
145
146#define SSH_XMSS_K2_MAGIC "k=2"
147#define num_stack(x) ((x->h+1)*(x->n))
148#define num_stacklevels(x) (x->h+1)
149#define num_auth(x) ((x->h)*(x->n))
150#define num_keep(x) ((x->h >> 1)*(x->n))
151#define num_th_nodes(x) ((x->h - x->k)*(x->n))
152#define num_retain(x) (((1ULL << x->k) - x->k - 1) * (x->n))
153#define num_treehash(x) ((x->h) - (x->k))
154
155int
156sshkey_xmss_init_bds_state(struct sshkey *key)
157{
158 struct ssh_xmss_state *state = key->xmss_state;
159 u_int32_t i;
160
161 state->stackoffset = 0;
162 if ((state->stack = calloc(num_stack(state), 1)) == NULL ||
163 (state->stacklevels = calloc(num_stacklevels(state), 1))== NULL ||
164 (state->auth = calloc(num_auth(state), 1)) == NULL ||
165 (state->keep = calloc(num_keep(state), 1)) == NULL ||
166 (state->th_nodes = calloc(num_th_nodes(state), 1)) == NULL ||
167 (state->retain = calloc(num_retain(state), 1)) == NULL ||
168 (state->treehash = calloc(num_treehash(state),
169 sizeof(treehash_inst))) == NULL) {
170 sshkey_xmss_free_bds(key);
171 return SSH_ERR_ALLOC_FAIL;
172 }
173 for (i = 0; i < state->h - state->k; i++)
174 state->treehash[i].node = &state->th_nodes[state->n*i];
175 xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
176 state->stacklevels, state->auth, state->keep, state->treehash,
177 state->retain, 0);
178 return 0;
179}
180
181void
182sshkey_xmss_free_bds(struct sshkey *key)
183{
184 struct ssh_xmss_state *state = key->xmss_state;
185
186 if (state == NULL)
187 return;
188 free(state->stack);
189 free(state->stacklevels);
190 free(state->auth);
191 free(state->keep);
192 free(state->th_nodes);
193 free(state->retain);
194 free(state->treehash);
195 state->stack = NULL;
196 state->stacklevels = NULL;
197 state->auth = NULL;
198 state->keep = NULL;
199 state->th_nodes = NULL;
200 state->retain = NULL;
201 state->treehash = NULL;
202}
203
204void *
205sshkey_xmss_params(const struct sshkey *key)
206{
207 struct ssh_xmss_state *state = key->xmss_state;
208
209 if (state == NULL)
210 return NULL;
211 return &state->params;
212}
213
214void *
215sshkey_xmss_bds_state(const struct sshkey *key)
216{
217 struct ssh_xmss_state *state = key->xmss_state;
218
219 if (state == NULL)
220 return NULL;
221 return &state->bds;
222}
223
224int
225sshkey_xmss_siglen(const struct sshkey *key, size_t *lenp)
226{
227 struct ssh_xmss_state *state = key->xmss_state;
228
229 if (lenp == NULL)
230 return SSH_ERR_INVALID_ARGUMENT;
231 if (state == NULL)
232 return SSH_ERR_INVALID_FORMAT;
233 *lenp = 4 + state->n +
234 state->params.wots_par.keysize +
235 state->h * state->n;
236 return 0;
237}
238
239size_t
240sshkey_xmss_pklen(const struct sshkey *key)
241{
242 struct ssh_xmss_state *state = key->xmss_state;
243
244 if (state == NULL)
245 return 0;
246 return state->n * 2;
247}
248
249size_t
250sshkey_xmss_sklen(const struct sshkey *key)
251{
252 struct ssh_xmss_state *state = key->xmss_state;
253
254 if (state == NULL)
255 return 0;
256 return state->n * 4 + 4;
257}
258
259int
260sshkey_xmss_init_enc_key(struct sshkey *k, const char *ciphername)
261{
262 struct ssh_xmss_state *state = k->xmss_state;
263 const struct sshcipher *cipher;
264 size_t keylen = 0, ivlen = 0;
265
266 if (state == NULL)
267 return SSH_ERR_INVALID_ARGUMENT;
268 if ((cipher = cipher_by_name(ciphername)) == NULL)
269 return SSH_ERR_INTERNAL_ERROR;
270 if ((state->enc_ciphername = strdup(ciphername)) == NULL)
271 return SSH_ERR_ALLOC_FAIL;
272 keylen = cipher_keylen(cipher);
273 ivlen = cipher_ivlen(cipher);
274 state->enc_keyiv_len = keylen + ivlen;
275 if ((state->enc_keyiv = calloc(state->enc_keyiv_len, 1)) == NULL) {
276 free(state->enc_ciphername);
277 state->enc_ciphername = NULL;
278 return SSH_ERR_ALLOC_FAIL;
279 }
280 arc4random_buf(state->enc_keyiv, state->enc_keyiv_len);
281 return 0;
282}
283
284int
285sshkey_xmss_serialize_enc_key(const struct sshkey *k, struct sshbuf *b)
286{
287 struct ssh_xmss_state *state = k->xmss_state;
288 int r;
289
290 if (state == NULL || state->enc_keyiv == NULL ||
291 state->enc_ciphername == NULL)
292 return SSH_ERR_INVALID_ARGUMENT;
293 if ((r = sshbuf_put_cstring(b, state->enc_ciphername)) != 0 ||
294 (r = sshbuf_put_string(b, state->enc_keyiv,
295 state->enc_keyiv_len)) != 0)
296 return r;
297 return 0;
298}
299
300int
301sshkey_xmss_deserialize_enc_key(struct sshkey *k, struct sshbuf *b)
302{
303 struct ssh_xmss_state *state = k->xmss_state;
304 size_t len;
305 int r;
306
307 if (state == NULL)
308 return SSH_ERR_INVALID_ARGUMENT;
309 if ((r = sshbuf_get_cstring(b, &state->enc_ciphername, NULL)) != 0 ||
310 (r = sshbuf_get_string(b, &state->enc_keyiv, &len)) != 0)
311 return r;
312 state->enc_keyiv_len = len;
313 return 0;
314}
315
316int
317sshkey_xmss_serialize_pk_info(const struct sshkey *k, struct sshbuf *b,
318 enum sshkey_serialize_rep opts)
319{
320 struct ssh_xmss_state *state = k->xmss_state;
321 u_char have_info = 1;
322 u_int32_t idx;
323 int r;
324
325 if (state == NULL)
326 return SSH_ERR_INVALID_ARGUMENT;
327 if (opts != SSHKEY_SERIALIZE_INFO)
328 return 0;
329 idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
330 if ((r = sshbuf_put_u8(b, have_info)) != 0 ||
331 (r = sshbuf_put_u32(b, idx)) != 0 ||
332 (r = sshbuf_put_u32(b, state->maxidx)) != 0)
333 return r;
334 return 0;
335}
336
337int
338sshkey_xmss_deserialize_pk_info(struct sshkey *k, struct sshbuf *b)
339{
340 struct ssh_xmss_state *state = k->xmss_state;
341 u_char have_info;
342 int r;
343
344 if (state == NULL)
345 return SSH_ERR_INVALID_ARGUMENT;
346 /* optional */
347 if (sshbuf_len(b) == 0)
348 return 0;
349 if ((r = sshbuf_get_u8(b, &have_info)) != 0)
350 return r;
351 if (have_info != 1)
352 return SSH_ERR_INVALID_ARGUMENT;
353 if ((r = sshbuf_get_u32(b, &state->idx)) != 0 ||
354 (r = sshbuf_get_u32(b, &state->maxidx)) != 0)
355 return r;
356 return 0;
357}
358
359int
360sshkey_xmss_generate_private_key(struct sshkey *k, u_int bits)
361{
362 int r;
363 const char *name;
364
365 if (bits == 10) {
366 name = XMSS_SHA2_256_W16_H10_NAME;
367 } else if (bits == 16) {
368 name = XMSS_SHA2_256_W16_H16_NAME;
369 } else if (bits == 20) {
370 name = XMSS_SHA2_256_W16_H20_NAME;
371 } else {
372 name = XMSS_DEFAULT_NAME;
373 }
374 if ((r = sshkey_xmss_init(k, name)) != 0 ||
375 (r = sshkey_xmss_init_bds_state(k)) != 0 ||
376 (r = sshkey_xmss_init_enc_key(k, XMSS_CIPHERNAME)) != 0)
377 return r;
378 if ((k->xmss_pk = malloc(sshkey_xmss_pklen(k))) == NULL ||
379 (k->xmss_sk = malloc(sshkey_xmss_sklen(k))) == NULL) {
380 return SSH_ERR_ALLOC_FAIL;
381 }
382 xmss_keypair(k->xmss_pk, k->xmss_sk, sshkey_xmss_bds_state(k),
383 sshkey_xmss_params(k));
384 return 0;
385}
386
387int
388sshkey_xmss_get_state_from_file(struct sshkey *k, const char *filename,
389 int *have_file, sshkey_printfn *pr)
390{
391 struct sshbuf *b = NULL, *enc = NULL;
392 int ret = SSH_ERR_SYSTEM_ERROR, r, fd = -1;
393 u_int32_t len;
394 unsigned char buf[4], *data = NULL;
395
396 *have_file = 0;
397 if ((fd = open(filename, O_RDONLY)) >= 0) {
398 *have_file = 1;
399 if (atomicio(read, fd, buf, sizeof(buf)) != sizeof(buf)) {
400 PRINT("%s: corrupt state file: %s", __func__, filename);
401 goto done;
402 }
403 len = PEEK_U32(buf);
404 if ((data = calloc(len, 1)) == NULL) {
405 ret = SSH_ERR_ALLOC_FAIL;
406 goto done;
407 }
408 if (atomicio(read, fd, data, len) != len) {
409 PRINT("%s: cannot read blob: %s", __func__, filename);
410 goto done;
411 }
412 if ((enc = sshbuf_from(data, len)) == NULL) {
413 ret = SSH_ERR_ALLOC_FAIL;
414 goto done;
415 }
416 sshkey_xmss_free_bds(k);
417 if ((r = sshkey_xmss_decrypt_state(k, enc, &b)) != 0) {
418 ret = r;
419 goto done;
420 }
421 if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) {
422 ret = r;
423 goto done;
424 }
425 ret = 0;
426 }
427done:
428 if (fd != -1)
429 close(fd);
430 free(data);
431 sshbuf_free(enc);
432 sshbuf_free(b);
433 return ret;
434}
435
436int
437sshkey_xmss_get_state(const struct sshkey *k, sshkey_printfn *pr)
438{
439 struct ssh_xmss_state *state = k->xmss_state;
440 u_int32_t idx = 0;
441 char *filename = NULL;
442 char *statefile = NULL, *ostatefile = NULL, *lockfile = NULL;
443 int lockfd = -1, have_state = 0, have_ostate, tries = 0;
444 int ret = SSH_ERR_INVALID_ARGUMENT, r;
445
446 if (state == NULL)
447 goto done;
448 /*
449 * If maxidx is set, then we are allowed a limited number
450 * of signatures, but don't need to access the disk.
451 * Otherwise we need to deal with the on-disk state.
452 */
453 if (state->maxidx) {
454 /* xmss_sk always contains the current state */
455 idx = PEEK_U32(k->xmss_sk);
456 if (idx < state->maxidx) {
457 state->allow_update = 1;
458 return 0;
459 }
460 return SSH_ERR_INVALID_ARGUMENT;
461 }
462 if ((filename = k->xmss_filename) == NULL)
463 goto done;
464 if (asprintf(&lockfile, "%s.lock", filename) < 0 ||
465 asprintf(&statefile, "%s.state", filename) < 0 ||
466 asprintf(&ostatefile, "%s.ostate", filename) < 0) {
467 ret = SSH_ERR_ALLOC_FAIL;
468 goto done;
469 }
470 if ((lockfd = open(lockfile, O_CREAT|O_RDONLY, 0600)) < 0) {
471 ret = SSH_ERR_SYSTEM_ERROR;
472 PRINT("%s: cannot open/create: %s", __func__, lockfile);
473 goto done;
474 }
475 while (flock(lockfd, LOCK_EX|LOCK_NB) < 0) {
476 if (errno != EWOULDBLOCK) {
477 ret = SSH_ERR_SYSTEM_ERROR;
478 PRINT("%s: cannot lock: %s", __func__, lockfile);
479 goto done;
480 }
481 if (++tries > 10) {
482 ret = SSH_ERR_SYSTEM_ERROR;
483 PRINT("%s: giving up on: %s", __func__, lockfile);
484 goto done;
485 }
486 usleep(1000*100*tries);
487 }
488 /* XXX no longer const */
489 if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
490 statefile, &have_state, pr)) != 0) {
491 if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
492 ostatefile, &have_ostate, pr)) == 0) {
493 state->allow_update = 1;
494 r = sshkey_xmss_forward_state(k, 1);
495 state->idx = PEEK_U32(k->xmss_sk);
496 state->allow_update = 0;
497 }
498 }
499 if (!have_state && !have_ostate) {
500 /* check that bds state is initialized */
501 if (state->bds.auth == NULL)
502 goto done;
503 PRINT("%s: start from scratch idx 0: %u", __func__, state->idx);
504 } else if (r != 0) {
505 ret = r;
506 goto done;
507 }
508 if (state->idx + 1 < state->idx) {
509 PRINT("%s: state wrap: %u", __func__, state->idx);
510 goto done;
511 }
512 state->have_state = have_state;
513 state->lockfd = lockfd;
514 state->allow_update = 1;
515 lockfd = -1;
516 ret = 0;
517done:
518 if (lockfd != -1)
519 close(lockfd);
520 free(lockfile);
521 free(statefile);
522 free(ostatefile);
523 return ret;
524}
525
526int
527sshkey_xmss_forward_state(const struct sshkey *k, u_int32_t reserve)
528{
529 struct ssh_xmss_state *state = k->xmss_state;
530 u_char *sig = NULL;
531 size_t required_siglen;
532 unsigned long long smlen;
533 u_char data;
534 int ret, r;
535
536 if (state == NULL || !state->allow_update)
537 return SSH_ERR_INVALID_ARGUMENT;
538 if (reserve == 0)
539 return SSH_ERR_INVALID_ARGUMENT;
540 if (state->idx + reserve <= state->idx)
541 return SSH_ERR_INVALID_ARGUMENT;
542 if ((r = sshkey_xmss_siglen(k, &required_siglen)) != 0)
543 return r;
544 if ((sig = malloc(required_siglen)) == NULL)
545 return SSH_ERR_ALLOC_FAIL;
546 while (reserve-- > 0) {
547 state->idx = PEEK_U32(k->xmss_sk);
548 smlen = required_siglen;
549 if ((ret = xmss_sign(k->xmss_sk, sshkey_xmss_bds_state(k),
550 sig, &smlen, &data, 0, sshkey_xmss_params(k))) != 0) {
551 r = SSH_ERR_INVALID_ARGUMENT;
552 break;
553 }
554 }
555 free(sig);
556 return r;
557}
558
559int
560sshkey_xmss_update_state(const struct sshkey *k, sshkey_printfn *pr)
561{
562 struct ssh_xmss_state *state = k->xmss_state;
563 struct sshbuf *b = NULL, *enc = NULL;
564 u_int32_t idx = 0;
565 unsigned char buf[4];
566 char *filename = NULL;
567 char *statefile = NULL, *ostatefile = NULL, *nstatefile = NULL;
568 int fd = -1;
569 int ret = SSH_ERR_INVALID_ARGUMENT;
570
571 if (state == NULL || !state->allow_update)
572 return ret;
573 if (state->maxidx) {
574 /* no update since the number of signatures is limited */
575 ret = 0;
576 goto done;
577 }
578 idx = PEEK_U32(k->xmss_sk);
579 if (idx == state->idx) {
580 /* no signature happend, no need to update */
581 ret = 0;
582 goto done;
583 } else if (idx != state->idx + 1) {
584 PRINT("%s: more than one signature happened: idx %u state %u",
585 __func__, idx, state->idx);
586 goto done;
587 }
588 state->idx = idx;
589 if ((filename = k->xmss_filename) == NULL)
590 goto done;
591 if (asprintf(&statefile, "%s.state", filename) < 0 ||
592 asprintf(&ostatefile, "%s.ostate", filename) < 0 ||
593 asprintf(&nstatefile, "%s.nstate", filename) < 0) {
594 ret = SSH_ERR_ALLOC_FAIL;
595 goto done;
596 }
597 unlink(nstatefile);
598 if ((b = sshbuf_new()) == NULL) {
599 ret = SSH_ERR_ALLOC_FAIL;
600 goto done;
601 }
602 if ((ret = sshkey_xmss_serialize_state(k, b)) != 0) {
603 PRINT("%s: SERLIALIZE FAILED: %d", __func__, ret);
604 goto done;
605 }
606 if ((ret = sshkey_xmss_encrypt_state(k, b, &enc)) != 0) {
607 PRINT("%s: ENCRYPT FAILED: %d", __func__, ret);
608 goto done;
609 }
610 if ((fd = open(nstatefile, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) {
611 ret = SSH_ERR_SYSTEM_ERROR;
612 PRINT("%s: open new state file: %s", __func__, nstatefile);
613 goto done;
614 }
615 POKE_U32(buf, sshbuf_len(enc));
616 if (atomicio(vwrite, fd, buf, sizeof(buf)) != sizeof(buf)) {
617 ret = SSH_ERR_SYSTEM_ERROR;
618 PRINT("%s: write new state file hdr: %s", __func__, nstatefile);
619 close(fd);
620 goto done;
621 }
622 if (atomicio(vwrite, fd, (void *)sshbuf_ptr(enc), sshbuf_len(enc)) !=
623 sshbuf_len(enc)) {
624 ret = SSH_ERR_SYSTEM_ERROR;
625 PRINT("%s: write new state file data: %s", __func__, nstatefile);
626 close(fd);
627 goto done;
628 }
629 if (fsync(fd) < 0) {
630 ret = SSH_ERR_SYSTEM_ERROR;
631 PRINT("%s: sync new state file: %s", __func__, nstatefile);
632 close(fd);
633 goto done;
634 }
635 if (close(fd) < 0) {
636 ret = SSH_ERR_SYSTEM_ERROR;
637 PRINT("%s: close new state file: %s", __func__, nstatefile);
638 goto done;
639 }
640 if (state->have_state) {
641 unlink(ostatefile);
642 if (link(statefile, ostatefile)) {
643 ret = SSH_ERR_SYSTEM_ERROR;
644 PRINT("%s: backup state %s to %s", __func__, statefile,
645 ostatefile);
646 goto done;
647 }
648 }
649 if (rename(nstatefile, statefile) < 0) {
650 ret = SSH_ERR_SYSTEM_ERROR;
651 PRINT("%s: rename %s to %s", __func__, nstatefile, statefile);
652 goto done;
653 }
654 ret = 0;
655done:
656 if (state->lockfd != -1) {
657 close(state->lockfd);
658 state->lockfd = -1;
659 }
660 if (nstatefile)
661 unlink(nstatefile);
662 free(statefile);
663 free(ostatefile);
664 free(nstatefile);
665 sshbuf_free(b);
666 sshbuf_free(enc);
667 return ret;
668}
669
670int
671sshkey_xmss_serialize_state(const struct sshkey *k, struct sshbuf *b)
672{
673 struct ssh_xmss_state *state = k->xmss_state;
674 treehash_inst *th;
675 u_int32_t i, node;
676 int r;
677
678 if (state == NULL)
679 return SSH_ERR_INVALID_ARGUMENT;
680 if (state->stack == NULL)
681 return SSH_ERR_INVALID_ARGUMENT;
682 state->stackoffset = state->bds.stackoffset; /* copy back */
683 if ((r = sshbuf_put_cstring(b, SSH_XMSS_K2_MAGIC)) != 0 ||
684 (r = sshbuf_put_u32(b, state->idx)) != 0 ||
685 (r = sshbuf_put_string(b, state->stack, num_stack(state))) != 0 ||
686 (r = sshbuf_put_u32(b, state->stackoffset)) != 0 ||
687 (r = sshbuf_put_string(b, state->stacklevels, num_stacklevels(state))) != 0 ||
688 (r = sshbuf_put_string(b, state->auth, num_auth(state))) != 0 ||
689 (r = sshbuf_put_string(b, state->keep, num_keep(state))) != 0 ||
690 (r = sshbuf_put_string(b, state->th_nodes, num_th_nodes(state))) != 0 ||
691 (r = sshbuf_put_string(b, state->retain, num_retain(state))) != 0 ||
692 (r = sshbuf_put_u32(b, num_treehash(state))) != 0)
693 return r;
694 for (i = 0; i < num_treehash(state); i++) {
695 th = &state->treehash[i];
696 node = th->node - state->th_nodes;
697 if ((r = sshbuf_put_u32(b, th->h)) != 0 ||
698 (r = sshbuf_put_u32(b, th->next_idx)) != 0 ||
699 (r = sshbuf_put_u32(b, th->stackusage)) != 0 ||
700 (r = sshbuf_put_u8(b, th->completed)) != 0 ||
701 (r = sshbuf_put_u32(b, node)) != 0)
702 return r;
703 }
704 return 0;
705}
706
707int
708sshkey_xmss_serialize_state_opt(const struct sshkey *k, struct sshbuf *b,
709 enum sshkey_serialize_rep opts)
710{
711 struct ssh_xmss_state *state = k->xmss_state;
712 int r = SSH_ERR_INVALID_ARGUMENT;
713
714 if (state == NULL)
715 return SSH_ERR_INVALID_ARGUMENT;
716 if ((r = sshbuf_put_u8(b, opts)) != 0)
717 return r;
718 switch (opts) {
719 case SSHKEY_SERIALIZE_STATE:
720 r = sshkey_xmss_serialize_state(k, b);
721 break;
722 case SSHKEY_SERIALIZE_FULL:
723 if ((r = sshkey_xmss_serialize_enc_key(k, b)) != 0)
724 break;
725 r = sshkey_xmss_serialize_state(k, b);
726 break;
727 case SSHKEY_SERIALIZE_DEFAULT:
728 r = 0;
729 break;
730 default:
731 r = SSH_ERR_INVALID_ARGUMENT;
732 break;
733 }
734 return r;
735}
736
737int
738sshkey_xmss_deserialize_state(struct sshkey *k, struct sshbuf *b)
739{
740 struct ssh_xmss_state *state = k->xmss_state;
741 treehash_inst *th;
742 u_int32_t i, lh, node;
743 size_t ls, lsl, la, lk, ln, lr;
744 char *magic;
745 int r;
746
747 if (state == NULL)
748 return SSH_ERR_INVALID_ARGUMENT;
749 if (k->xmss_sk == NULL)
750 return SSH_ERR_INVALID_ARGUMENT;
751 if ((state->treehash = calloc(num_treehash(state),
752 sizeof(treehash_inst))) == NULL)
753 return SSH_ERR_ALLOC_FAIL;
754 if ((r = sshbuf_get_cstring(b, &magic, NULL)) != 0 ||
755 (r = sshbuf_get_u32(b, &state->idx)) != 0 ||
756 (r = sshbuf_get_string(b, &state->stack, &ls)) != 0 ||
757 (r = sshbuf_get_u32(b, &state->stackoffset)) != 0 ||
758 (r = sshbuf_get_string(b, &state->stacklevels, &lsl)) != 0 ||
759 (r = sshbuf_get_string(b, &state->auth, &la)) != 0 ||
760 (r = sshbuf_get_string(b, &state->keep, &lk)) != 0 ||
761 (r = sshbuf_get_string(b, &state->th_nodes, &ln)) != 0 ||
762 (r = sshbuf_get_string(b, &state->retain, &lr)) != 0 ||
763 (r = sshbuf_get_u32(b, &lh)) != 0)
764 return r;
765 if (strcmp(magic, SSH_XMSS_K2_MAGIC) != 0)
766 return SSH_ERR_INVALID_ARGUMENT;
767 /* XXX check stackoffset */
768 if (ls != num_stack(state) ||
769 lsl != num_stacklevels(state) ||
770 la != num_auth(state) ||
771 lk != num_keep(state) ||
772 ln != num_th_nodes(state) ||
773 lr != num_retain(state) ||
774 lh != num_treehash(state))
775 return SSH_ERR_INVALID_ARGUMENT;
776 for (i = 0; i < num_treehash(state); i++) {
777 th = &state->treehash[i];
778 if ((r = sshbuf_get_u32(b, &th->h)) != 0 ||
779 (r = sshbuf_get_u32(b, &th->next_idx)) != 0 ||
780 (r = sshbuf_get_u32(b, &th->stackusage)) != 0 ||
781 (r = sshbuf_get_u8(b, &th->completed)) != 0 ||
782 (r = sshbuf_get_u32(b, &node)) != 0)
783 return r;
784 if (node < num_th_nodes(state))
785 th->node = &state->th_nodes[node];
786 }
787 POKE_U32(k->xmss_sk, state->idx);
788 xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
789 state->stacklevels, state->auth, state->keep, state->treehash,
790 state->retain, 0);
791 return 0;
792}
793
794int
795sshkey_xmss_deserialize_state_opt(struct sshkey *k, struct sshbuf *b)
796{
797 enum sshkey_serialize_rep opts;
798 u_char have_state;
799 int r;
800
801 if ((r = sshbuf_get_u8(b, &have_state)) != 0)
802 return r;
803
804 opts = have_state;
805 switch (opts) {
806 case SSHKEY_SERIALIZE_DEFAULT:
807 r = 0;
808 break;
809 case SSHKEY_SERIALIZE_STATE:
810 if ((r = sshkey_xmss_deserialize_state(k, b)) != 0)
811 return r;
812 break;
813 case SSHKEY_SERIALIZE_FULL:
814 if ((r = sshkey_xmss_deserialize_enc_key(k, b)) != 0 ||
815 (r = sshkey_xmss_deserialize_state(k, b)) != 0)
816 return r;
817 break;
818 default:
819 r = SSH_ERR_INVALID_FORMAT;
820 break;
821 }
822 return r;
823}
824
825int
826sshkey_xmss_encrypt_state(const struct sshkey *k, struct sshbuf *b,
827 struct sshbuf **retp)
828{
829 struct ssh_xmss_state *state = k->xmss_state;
830 struct sshbuf *encrypted = NULL, *encoded = NULL, *padded = NULL;
831 struct sshcipher_ctx *ciphercontext = NULL;
832 const struct sshcipher *cipher;
833 u_char *cp, *key, *iv = NULL;
834 size_t i, keylen, ivlen, blocksize, authlen, encrypted_len, aadlen;
835 int r = SSH_ERR_INTERNAL_ERROR;
836
837 if (retp != NULL)
838 *retp = NULL;
839 if (state == NULL ||
840 state->enc_keyiv == NULL ||
841 state->enc_ciphername == NULL)
842 return SSH_ERR_INTERNAL_ERROR;
843 if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
844 r = SSH_ERR_INTERNAL_ERROR;
845 goto out;
846 }
847 blocksize = cipher_blocksize(cipher);
848 keylen = cipher_keylen(cipher);
849 ivlen = cipher_ivlen(cipher);
850 authlen = cipher_authlen(cipher);
851 if (state->enc_keyiv_len != keylen + ivlen) {
852 r = SSH_ERR_INVALID_FORMAT;
853 goto out;
854 }
855 key = state->enc_keyiv;
856 if ((encrypted = sshbuf_new()) == NULL ||
857 (encoded = sshbuf_new()) == NULL ||
858 (padded = sshbuf_new()) == NULL ||
859 (iv = malloc(ivlen)) == NULL) {
860 r = SSH_ERR_ALLOC_FAIL;
861 goto out;
862 }
863
864 /* replace first 4 bytes of IV with index to ensure uniqueness */
865 memcpy(iv, key + keylen, ivlen);
866 POKE_U32(iv, state->idx);
867
868 if ((r = sshbuf_put(encoded, XMSS_MAGIC, sizeof(XMSS_MAGIC))) != 0 ||
869 (r = sshbuf_put_u32(encoded, state->idx)) != 0)
870 goto out;
871
872 /* padded state will be encrypted */
873 if ((r = sshbuf_putb(padded, b)) != 0)
874 goto out;
875 i = 0;
876 while (sshbuf_len(padded) % blocksize) {
877 if ((r = sshbuf_put_u8(padded, ++i & 0xff)) != 0)
878 goto out;
879 }
880 encrypted_len = sshbuf_len(padded);
881
882 /* header including the length of state is used as AAD */
883 if ((r = sshbuf_put_u32(encoded, encrypted_len)) != 0)
884 goto out;
885 aadlen = sshbuf_len(encoded);
886
887 /* concat header and state */
888 if ((r = sshbuf_putb(encoded, padded)) != 0)
889 goto out;
890
891 /* reserve space for encryption of encoded data plus auth tag */
892 /* encrypt at offset addlen */
893 if ((r = sshbuf_reserve(encrypted,
894 encrypted_len + aadlen + authlen, &cp)) != 0 ||
895 (r = cipher_init(&ciphercontext, cipher, key, keylen,
896 iv, ivlen, 1)) != 0 ||
897 (r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encoded),
898 encrypted_len, aadlen, authlen)) != 0)
899 goto out;
900
901 /* success */
902 r = 0;
903 out:
904 if (retp != NULL) {
905 *retp = encrypted;
906 encrypted = NULL;
907 }
908 sshbuf_free(padded);
909 sshbuf_free(encoded);
910 sshbuf_free(encrypted);
911 cipher_free(ciphercontext);
912 free(iv);
913 return r;
914}
915
916int
917sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded,
918 struct sshbuf **retp)
919{
920 struct ssh_xmss_state *state = k->xmss_state;
921 struct sshbuf *copy = NULL, *decrypted = NULL;
922 struct sshcipher_ctx *ciphercontext = NULL;
923 const struct sshcipher *cipher = NULL;
924 u_char *key, *iv = NULL, *dp;
925 size_t keylen, ivlen, authlen, aadlen;
926 u_int blocksize, encrypted_len, index;
927 int r = SSH_ERR_INTERNAL_ERROR;
928
929 if (retp != NULL)
930 *retp = NULL;
931 if (state == NULL ||
932 state->enc_keyiv == NULL ||
933 state->enc_ciphername == NULL)
934 return SSH_ERR_INTERNAL_ERROR;
935 if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
936 r = SSH_ERR_INVALID_FORMAT;
937 goto out;
938 }
939 blocksize = cipher_blocksize(cipher);
940 keylen = cipher_keylen(cipher);
941 ivlen = cipher_ivlen(cipher);
942 authlen = cipher_authlen(cipher);
943 if (state->enc_keyiv_len != keylen + ivlen) {
944 r = SSH_ERR_INTERNAL_ERROR;
945 goto out;
946 }
947 key = state->enc_keyiv;
948
949 if ((copy = sshbuf_fromb(encoded)) == NULL ||
950 (decrypted = sshbuf_new()) == NULL ||
951 (iv = malloc(ivlen)) == NULL) {
952 r = SSH_ERR_ALLOC_FAIL;
953 goto out;
954 }
955
956 /* check magic */
957 if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) ||
958 memcmp(sshbuf_ptr(encoded), XMSS_MAGIC, sizeof(XMSS_MAGIC))) {
959 r = SSH_ERR_INVALID_FORMAT;
960 goto out;
961 }
962 /* parse public portion */
963 if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 ||
964 (r = sshbuf_get_u32(encoded, &index)) != 0 ||
965 (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0)
966 goto out;
967
968 /* check size of encrypted key blob */
969 if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
970 r = SSH_ERR_INVALID_FORMAT;
971 goto out;
972 }
973 /* check that an appropriate amount of auth data is present */
974 if (sshbuf_len(encoded) < encrypted_len + authlen) {
975 r = SSH_ERR_INVALID_FORMAT;
976 goto out;
977 }
978
979 aadlen = sshbuf_len(copy) - sshbuf_len(encoded);
980
981 /* replace first 4 bytes of IV with index to ensure uniqueness */
982 memcpy(iv, key + keylen, ivlen);
983 POKE_U32(iv, index);
984
985 /* decrypt private state of key */
986 if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 ||
987 (r = cipher_init(&ciphercontext, cipher, key, keylen,
988 iv, ivlen, 0)) != 0 ||
989 (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy),
990 encrypted_len, aadlen, authlen)) != 0)
991 goto out;
992
993 /* there should be no trailing data */
994 if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0)
995 goto out;
996 if (sshbuf_len(encoded) != 0) {
997 r = SSH_ERR_INVALID_FORMAT;
998 goto out;
999 }
1000
1001 /* remove AAD */
1002 if ((r = sshbuf_consume(decrypted, aadlen)) != 0)
1003 goto out;
1004 /* XXX encrypted includes unchecked padding */
1005
1006 /* success */
1007 r = 0;
1008 if (retp != NULL) {
1009 *retp = decrypted;
1010 decrypted = NULL;
1011 }
1012 out:
1013 cipher_free(ciphercontext);
1014 sshbuf_free(copy);
1015 sshbuf_free(decrypted);
1016 free(iv);
1017 return r;
1018}
1019
1020u_int32_t
1021sshkey_xmss_signatures_left(const struct sshkey *k)
1022{
1023 struct ssh_xmss_state *state = k->xmss_state;
1024 u_int32_t idx;
1025
1026 if (sshkey_type_plain(k->type) == KEY_XMSS && state &&
1027 state->maxidx) {
1028 idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
1029 if (idx < state->maxidx)
1030 return state->maxidx - idx;
1031 }
1032 return 0;
1033}
1034
1035int
1036sshkey_xmss_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
1037{
1038 struct ssh_xmss_state *state = k->xmss_state;
1039
1040 if (sshkey_type_plain(k->type) != KEY_XMSS)
1041 return SSH_ERR_INVALID_ARGUMENT;
1042 if (maxsign == 0)
1043 return 0;
1044 if (state->idx + maxsign < state->idx)
1045 return SSH_ERR_INVALID_ARGUMENT;
1046 state->maxidx = state->idx + maxsign;
1047 return 0;
1048}