usr.bin/bc: remove OpenBSD derived bc and dc commands
[freebsd/src.git] / crypto / openssh / sshkey-xmss.c
blob818aba9059df9d77cca5ebf1043e505631ede19b
1 /* $OpenBSD: sshkey-xmss.c,v 1.12 2022/10/28 00:39:29 djm Exp $ */
2 /*
3 * Copyright (c) 2017 Markus Friedl. All rights reserved.
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.
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.
26 #include "includes.h"
27 #ifdef WITH_XMSS
29 #include <sys/types.h>
30 #include <sys/uio.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #ifdef HAVE_SYS_FILE_H
38 # include <sys/file.h>
39 #endif
41 #include "ssh2.h"
42 #include "ssherr.h"
43 #include "sshbuf.h"
44 #include "cipher.h"
45 #include "sshkey.h"
46 #include "sshkey-xmss.h"
47 #include "atomicio.h"
48 #include "log.h"
50 #include "xmss_fast.h"
52 /* opaque internal XMSS state */
53 #define XMSS_MAGIC "xmss-state-v1"
54 #define XMSS_CIPHERNAME "aes256-gcm@openssh.com"
55 struct ssh_xmss_state {
56 xmss_params params;
57 u_int32_t n, w, h, k;
59 bds_state bds;
60 u_char *stack;
61 u_int32_t stackoffset;
62 u_char *stacklevels;
63 u_char *auth;
64 u_char *keep;
65 u_char *th_nodes;
66 u_char *retain;
67 treehash_inst *treehash;
69 u_int32_t idx; /* state read from file */
70 u_int32_t maxidx; /* restricted # of signatures */
71 int have_state; /* .state file exists */
72 int lockfd; /* locked in sshkey_xmss_get_state() */
73 u_char allow_update; /* allow sshkey_xmss_update_state() */
74 char *enc_ciphername;/* encrypt state with cipher */
75 u_char *enc_keyiv; /* encrypt state with key */
76 u_int32_t enc_keyiv_len; /* length of enc_keyiv */
79 int sshkey_xmss_init_bds_state(struct sshkey *);
80 int sshkey_xmss_init_enc_key(struct sshkey *, const char *);
81 void sshkey_xmss_free_bds(struct sshkey *);
82 int sshkey_xmss_get_state_from_file(struct sshkey *, const char *,
83 int *, int);
84 int sshkey_xmss_encrypt_state(const struct sshkey *, struct sshbuf *,
85 struct sshbuf **);
86 int sshkey_xmss_decrypt_state(const struct sshkey *, struct sshbuf *,
87 struct sshbuf **);
88 int sshkey_xmss_serialize_enc_key(const struct sshkey *, struct sshbuf *);
89 int sshkey_xmss_deserialize_enc_key(struct sshkey *, struct sshbuf *);
91 #define PRINT(...) do { if (printerror) sshlog(__FILE__, __func__, __LINE__, \
92 0, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__); } while (0)
94 int
95 sshkey_xmss_init(struct sshkey *key, const char *name)
97 struct ssh_xmss_state *state;
99 if (key->xmss_state != NULL)
100 return SSH_ERR_INVALID_FORMAT;
101 if (name == NULL)
102 return SSH_ERR_INVALID_FORMAT;
103 state = calloc(sizeof(struct ssh_xmss_state), 1);
104 if (state == NULL)
105 return SSH_ERR_ALLOC_FAIL;
106 if (strcmp(name, XMSS_SHA2_256_W16_H10_NAME) == 0) {
107 state->n = 32;
108 state->w = 16;
109 state->h = 10;
110 } else if (strcmp(name, XMSS_SHA2_256_W16_H16_NAME) == 0) {
111 state->n = 32;
112 state->w = 16;
113 state->h = 16;
114 } else if (strcmp(name, XMSS_SHA2_256_W16_H20_NAME) == 0) {
115 state->n = 32;
116 state->w = 16;
117 state->h = 20;
118 } else {
119 free(state);
120 return SSH_ERR_KEY_TYPE_UNKNOWN;
122 if ((key->xmss_name = strdup(name)) == NULL) {
123 free(state);
124 return SSH_ERR_ALLOC_FAIL;
126 state->k = 2; /* XXX hardcoded */
127 state->lockfd = -1;
128 if (xmss_set_params(&state->params, state->n, state->h, state->w,
129 state->k) != 0) {
130 free(state);
131 return SSH_ERR_INVALID_FORMAT;
133 key->xmss_state = state;
134 return 0;
137 void
138 sshkey_xmss_free_state(struct sshkey *key)
140 struct ssh_xmss_state *state = key->xmss_state;
142 sshkey_xmss_free_bds(key);
143 if (state) {
144 if (state->enc_keyiv) {
145 explicit_bzero(state->enc_keyiv, state->enc_keyiv_len);
146 free(state->enc_keyiv);
148 free(state->enc_ciphername);
149 free(state);
151 key->xmss_state = NULL;
154 #define SSH_XMSS_K2_MAGIC "k=2"
155 #define num_stack(x) ((x->h+1)*(x->n))
156 #define num_stacklevels(x) (x->h+1)
157 #define num_auth(x) ((x->h)*(x->n))
158 #define num_keep(x) ((x->h >> 1)*(x->n))
159 #define num_th_nodes(x) ((x->h - x->k)*(x->n))
160 #define num_retain(x) (((1ULL << x->k) - x->k - 1) * (x->n))
161 #define num_treehash(x) ((x->h) - (x->k))
164 sshkey_xmss_init_bds_state(struct sshkey *key)
166 struct ssh_xmss_state *state = key->xmss_state;
167 u_int32_t i;
169 state->stackoffset = 0;
170 if ((state->stack = calloc(num_stack(state), 1)) == NULL ||
171 (state->stacklevels = calloc(num_stacklevels(state), 1))== NULL ||
172 (state->auth = calloc(num_auth(state), 1)) == NULL ||
173 (state->keep = calloc(num_keep(state), 1)) == NULL ||
174 (state->th_nodes = calloc(num_th_nodes(state), 1)) == NULL ||
175 (state->retain = calloc(num_retain(state), 1)) == NULL ||
176 (state->treehash = calloc(num_treehash(state),
177 sizeof(treehash_inst))) == NULL) {
178 sshkey_xmss_free_bds(key);
179 return SSH_ERR_ALLOC_FAIL;
181 for (i = 0; i < state->h - state->k; i++)
182 state->treehash[i].node = &state->th_nodes[state->n*i];
183 xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
184 state->stacklevels, state->auth, state->keep, state->treehash,
185 state->retain, 0);
186 return 0;
189 void
190 sshkey_xmss_free_bds(struct sshkey *key)
192 struct ssh_xmss_state *state = key->xmss_state;
194 if (state == NULL)
195 return;
196 free(state->stack);
197 free(state->stacklevels);
198 free(state->auth);
199 free(state->keep);
200 free(state->th_nodes);
201 free(state->retain);
202 free(state->treehash);
203 state->stack = NULL;
204 state->stacklevels = NULL;
205 state->auth = NULL;
206 state->keep = NULL;
207 state->th_nodes = NULL;
208 state->retain = NULL;
209 state->treehash = NULL;
212 void *
213 sshkey_xmss_params(const struct sshkey *key)
215 struct ssh_xmss_state *state = key->xmss_state;
217 if (state == NULL)
218 return NULL;
219 return &state->params;
222 void *
223 sshkey_xmss_bds_state(const struct sshkey *key)
225 struct ssh_xmss_state *state = key->xmss_state;
227 if (state == NULL)
228 return NULL;
229 return &state->bds;
233 sshkey_xmss_siglen(const struct sshkey *key, size_t *lenp)
235 struct ssh_xmss_state *state = key->xmss_state;
237 if (lenp == NULL)
238 return SSH_ERR_INVALID_ARGUMENT;
239 if (state == NULL)
240 return SSH_ERR_INVALID_FORMAT;
241 *lenp = 4 + state->n +
242 state->params.wots_par.keysize +
243 state->h * state->n;
244 return 0;
247 size_t
248 sshkey_xmss_pklen(const struct sshkey *key)
250 struct ssh_xmss_state *state = key->xmss_state;
252 if (state == NULL)
253 return 0;
254 return state->n * 2;
257 size_t
258 sshkey_xmss_sklen(const struct sshkey *key)
260 struct ssh_xmss_state *state = key->xmss_state;
262 if (state == NULL)
263 return 0;
264 return state->n * 4 + 4;
268 sshkey_xmss_init_enc_key(struct sshkey *k, const char *ciphername)
270 struct ssh_xmss_state *state = k->xmss_state;
271 const struct sshcipher *cipher;
272 size_t keylen = 0, ivlen = 0;
274 if (state == NULL)
275 return SSH_ERR_INVALID_ARGUMENT;
276 if ((cipher = cipher_by_name(ciphername)) == NULL)
277 return SSH_ERR_INTERNAL_ERROR;
278 if ((state->enc_ciphername = strdup(ciphername)) == NULL)
279 return SSH_ERR_ALLOC_FAIL;
280 keylen = cipher_keylen(cipher);
281 ivlen = cipher_ivlen(cipher);
282 state->enc_keyiv_len = keylen + ivlen;
283 if ((state->enc_keyiv = calloc(state->enc_keyiv_len, 1)) == NULL) {
284 free(state->enc_ciphername);
285 state->enc_ciphername = NULL;
286 return SSH_ERR_ALLOC_FAIL;
288 arc4random_buf(state->enc_keyiv, state->enc_keyiv_len);
289 return 0;
293 sshkey_xmss_serialize_enc_key(const struct sshkey *k, struct sshbuf *b)
295 struct ssh_xmss_state *state = k->xmss_state;
296 int r;
298 if (state == NULL || state->enc_keyiv == NULL ||
299 state->enc_ciphername == NULL)
300 return SSH_ERR_INVALID_ARGUMENT;
301 if ((r = sshbuf_put_cstring(b, state->enc_ciphername)) != 0 ||
302 (r = sshbuf_put_string(b, state->enc_keyiv,
303 state->enc_keyiv_len)) != 0)
304 return r;
305 return 0;
309 sshkey_xmss_deserialize_enc_key(struct sshkey *k, struct sshbuf *b)
311 struct ssh_xmss_state *state = k->xmss_state;
312 size_t len;
313 int r;
315 if (state == NULL)
316 return SSH_ERR_INVALID_ARGUMENT;
317 if ((r = sshbuf_get_cstring(b, &state->enc_ciphername, NULL)) != 0 ||
318 (r = sshbuf_get_string(b, &state->enc_keyiv, &len)) != 0)
319 return r;
320 state->enc_keyiv_len = len;
321 return 0;
325 sshkey_xmss_serialize_pk_info(const struct sshkey *k, struct sshbuf *b,
326 enum sshkey_serialize_rep opts)
328 struct ssh_xmss_state *state = k->xmss_state;
329 u_char have_info = 1;
330 u_int32_t idx;
331 int r;
333 if (state == NULL)
334 return SSH_ERR_INVALID_ARGUMENT;
335 if (opts != SSHKEY_SERIALIZE_INFO)
336 return 0;
337 idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
338 if ((r = sshbuf_put_u8(b, have_info)) != 0 ||
339 (r = sshbuf_put_u32(b, idx)) != 0 ||
340 (r = sshbuf_put_u32(b, state->maxidx)) != 0)
341 return r;
342 return 0;
346 sshkey_xmss_deserialize_pk_info(struct sshkey *k, struct sshbuf *b)
348 struct ssh_xmss_state *state = k->xmss_state;
349 u_char have_info;
350 int r;
352 if (state == NULL)
353 return SSH_ERR_INVALID_ARGUMENT;
354 /* optional */
355 if (sshbuf_len(b) == 0)
356 return 0;
357 if ((r = sshbuf_get_u8(b, &have_info)) != 0)
358 return r;
359 if (have_info != 1)
360 return SSH_ERR_INVALID_ARGUMENT;
361 if ((r = sshbuf_get_u32(b, &state->idx)) != 0 ||
362 (r = sshbuf_get_u32(b, &state->maxidx)) != 0)
363 return r;
364 return 0;
368 sshkey_xmss_generate_private_key(struct sshkey *k, int bits)
370 int r;
371 const char *name;
373 if (bits == 10) {
374 name = XMSS_SHA2_256_W16_H10_NAME;
375 } else if (bits == 16) {
376 name = XMSS_SHA2_256_W16_H16_NAME;
377 } else if (bits == 20) {
378 name = XMSS_SHA2_256_W16_H20_NAME;
379 } else {
380 name = XMSS_DEFAULT_NAME;
382 if ((r = sshkey_xmss_init(k, name)) != 0 ||
383 (r = sshkey_xmss_init_bds_state(k)) != 0 ||
384 (r = sshkey_xmss_init_enc_key(k, XMSS_CIPHERNAME)) != 0)
385 return r;
386 if ((k->xmss_pk = malloc(sshkey_xmss_pklen(k))) == NULL ||
387 (k->xmss_sk = malloc(sshkey_xmss_sklen(k))) == NULL) {
388 return SSH_ERR_ALLOC_FAIL;
390 xmss_keypair(k->xmss_pk, k->xmss_sk, sshkey_xmss_bds_state(k),
391 sshkey_xmss_params(k));
392 return 0;
396 sshkey_xmss_get_state_from_file(struct sshkey *k, const char *filename,
397 int *have_file, int printerror)
399 struct sshbuf *b = NULL, *enc = NULL;
400 int ret = SSH_ERR_SYSTEM_ERROR, r, fd = -1;
401 u_int32_t len;
402 unsigned char buf[4], *data = NULL;
404 *have_file = 0;
405 if ((fd = open(filename, O_RDONLY)) >= 0) {
406 *have_file = 1;
407 if (atomicio(read, fd, buf, sizeof(buf)) != sizeof(buf)) {
408 PRINT("corrupt state file: %s", filename);
409 goto done;
411 len = PEEK_U32(buf);
412 if ((data = calloc(len, 1)) == NULL) {
413 ret = SSH_ERR_ALLOC_FAIL;
414 goto done;
416 if (atomicio(read, fd, data, len) != len) {
417 PRINT("cannot read blob: %s", filename);
418 goto done;
420 if ((enc = sshbuf_from(data, len)) == NULL) {
421 ret = SSH_ERR_ALLOC_FAIL;
422 goto done;
424 sshkey_xmss_free_bds(k);
425 if ((r = sshkey_xmss_decrypt_state(k, enc, &b)) != 0) {
426 ret = r;
427 goto done;
429 if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) {
430 ret = r;
431 goto done;
433 ret = 0;
435 done:
436 if (fd != -1)
437 close(fd);
438 free(data);
439 sshbuf_free(enc);
440 sshbuf_free(b);
441 return ret;
445 sshkey_xmss_get_state(const struct sshkey *k, int printerror)
447 struct ssh_xmss_state *state = k->xmss_state;
448 u_int32_t idx = 0;
449 char *filename = NULL;
450 char *statefile = NULL, *ostatefile = NULL, *lockfile = NULL;
451 int lockfd = -1, have_state = 0, have_ostate, tries = 0;
452 int ret = SSH_ERR_INVALID_ARGUMENT, r;
454 if (state == NULL)
455 goto done;
457 * If maxidx is set, then we are allowed a limited number
458 * of signatures, but don't need to access the disk.
459 * Otherwise we need to deal with the on-disk state.
461 if (state->maxidx) {
462 /* xmss_sk always contains the current state */
463 idx = PEEK_U32(k->xmss_sk);
464 if (idx < state->maxidx) {
465 state->allow_update = 1;
466 return 0;
468 return SSH_ERR_INVALID_ARGUMENT;
470 if ((filename = k->xmss_filename) == NULL)
471 goto done;
472 if (asprintf(&lockfile, "%s.lock", filename) == -1 ||
473 asprintf(&statefile, "%s.state", filename) == -1 ||
474 asprintf(&ostatefile, "%s.ostate", filename) == -1) {
475 ret = SSH_ERR_ALLOC_FAIL;
476 goto done;
478 if ((lockfd = open(lockfile, O_CREAT|O_RDONLY, 0600)) == -1) {
479 ret = SSH_ERR_SYSTEM_ERROR;
480 PRINT("cannot open/create: %s", lockfile);
481 goto done;
483 while (flock(lockfd, LOCK_EX|LOCK_NB) == -1) {
484 if (errno != EWOULDBLOCK) {
485 ret = SSH_ERR_SYSTEM_ERROR;
486 PRINT("cannot lock: %s", lockfile);
487 goto done;
489 if (++tries > 10) {
490 ret = SSH_ERR_SYSTEM_ERROR;
491 PRINT("giving up on: %s", lockfile);
492 goto done;
494 usleep(1000*100*tries);
496 /* XXX no longer const */
497 if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
498 statefile, &have_state, printerror)) != 0) {
499 if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
500 ostatefile, &have_ostate, printerror)) == 0) {
501 state->allow_update = 1;
502 r = sshkey_xmss_forward_state(k, 1);
503 state->idx = PEEK_U32(k->xmss_sk);
504 state->allow_update = 0;
507 if (!have_state && !have_ostate) {
508 /* check that bds state is initialized */
509 if (state->bds.auth == NULL)
510 goto done;
511 PRINT("start from scratch idx 0: %u", state->idx);
512 } else if (r != 0) {
513 ret = r;
514 goto done;
516 if (state->idx + 1 < state->idx) {
517 PRINT("state wrap: %u", state->idx);
518 goto done;
520 state->have_state = have_state;
521 state->lockfd = lockfd;
522 state->allow_update = 1;
523 lockfd = -1;
524 ret = 0;
525 done:
526 if (lockfd != -1)
527 close(lockfd);
528 free(lockfile);
529 free(statefile);
530 free(ostatefile);
531 return ret;
535 sshkey_xmss_forward_state(const struct sshkey *k, u_int32_t reserve)
537 struct ssh_xmss_state *state = k->xmss_state;
538 u_char *sig = NULL;
539 size_t required_siglen;
540 unsigned long long smlen;
541 u_char data;
542 int ret, r;
544 if (state == NULL || !state->allow_update)
545 return SSH_ERR_INVALID_ARGUMENT;
546 if (reserve == 0)
547 return SSH_ERR_INVALID_ARGUMENT;
548 if (state->idx + reserve <= state->idx)
549 return SSH_ERR_INVALID_ARGUMENT;
550 if ((r = sshkey_xmss_siglen(k, &required_siglen)) != 0)
551 return r;
552 if ((sig = malloc(required_siglen)) == NULL)
553 return SSH_ERR_ALLOC_FAIL;
554 while (reserve-- > 0) {
555 state->idx = PEEK_U32(k->xmss_sk);
556 smlen = required_siglen;
557 if ((ret = xmss_sign(k->xmss_sk, sshkey_xmss_bds_state(k),
558 sig, &smlen, &data, 0, sshkey_xmss_params(k))) != 0) {
559 r = SSH_ERR_INVALID_ARGUMENT;
560 break;
563 free(sig);
564 return r;
568 sshkey_xmss_update_state(const struct sshkey *k, int printerror)
570 struct ssh_xmss_state *state = k->xmss_state;
571 struct sshbuf *b = NULL, *enc = NULL;
572 u_int32_t idx = 0;
573 unsigned char buf[4];
574 char *filename = NULL;
575 char *statefile = NULL, *ostatefile = NULL, *nstatefile = NULL;
576 int fd = -1;
577 int ret = SSH_ERR_INVALID_ARGUMENT;
579 if (state == NULL || !state->allow_update)
580 return ret;
581 if (state->maxidx) {
582 /* no update since the number of signatures is limited */
583 ret = 0;
584 goto done;
586 idx = PEEK_U32(k->xmss_sk);
587 if (idx == state->idx) {
588 /* no signature happened, no need to update */
589 ret = 0;
590 goto done;
591 } else if (idx != state->idx + 1) {
592 PRINT("more than one signature happened: idx %u state %u",
593 idx, state->idx);
594 goto done;
596 state->idx = idx;
597 if ((filename = k->xmss_filename) == NULL)
598 goto done;
599 if (asprintf(&statefile, "%s.state", filename) == -1 ||
600 asprintf(&ostatefile, "%s.ostate", filename) == -1 ||
601 asprintf(&nstatefile, "%s.nstate", filename) == -1) {
602 ret = SSH_ERR_ALLOC_FAIL;
603 goto done;
605 unlink(nstatefile);
606 if ((b = sshbuf_new()) == NULL) {
607 ret = SSH_ERR_ALLOC_FAIL;
608 goto done;
610 if ((ret = sshkey_xmss_serialize_state(k, b)) != 0) {
611 PRINT("SERLIALIZE FAILED: %d", ret);
612 goto done;
614 if ((ret = sshkey_xmss_encrypt_state(k, b, &enc)) != 0) {
615 PRINT("ENCRYPT FAILED: %d", ret);
616 goto done;
618 if ((fd = open(nstatefile, O_CREAT|O_WRONLY|O_EXCL, 0600)) == -1) {
619 ret = SSH_ERR_SYSTEM_ERROR;
620 PRINT("open new state file: %s", nstatefile);
621 goto done;
623 POKE_U32(buf, sshbuf_len(enc));
624 if (atomicio(vwrite, fd, buf, sizeof(buf)) != sizeof(buf)) {
625 ret = SSH_ERR_SYSTEM_ERROR;
626 PRINT("write new state file hdr: %s", nstatefile);
627 close(fd);
628 goto done;
630 if (atomicio(vwrite, fd, sshbuf_mutable_ptr(enc), sshbuf_len(enc)) !=
631 sshbuf_len(enc)) {
632 ret = SSH_ERR_SYSTEM_ERROR;
633 PRINT("write new state file data: %s", nstatefile);
634 close(fd);
635 goto done;
637 if (fsync(fd) == -1) {
638 ret = SSH_ERR_SYSTEM_ERROR;
639 PRINT("sync new state file: %s", nstatefile);
640 close(fd);
641 goto done;
643 if (close(fd) == -1) {
644 ret = SSH_ERR_SYSTEM_ERROR;
645 PRINT("close new state file: %s", nstatefile);
646 goto done;
648 if (state->have_state) {
649 unlink(ostatefile);
650 if (link(statefile, ostatefile)) {
651 ret = SSH_ERR_SYSTEM_ERROR;
652 PRINT("backup state %s to %s", statefile, ostatefile);
653 goto done;
656 if (rename(nstatefile, statefile) == -1) {
657 ret = SSH_ERR_SYSTEM_ERROR;
658 PRINT("rename %s to %s", nstatefile, statefile);
659 goto done;
661 ret = 0;
662 done:
663 if (state->lockfd != -1) {
664 close(state->lockfd);
665 state->lockfd = -1;
667 if (nstatefile)
668 unlink(nstatefile);
669 free(statefile);
670 free(ostatefile);
671 free(nstatefile);
672 sshbuf_free(b);
673 sshbuf_free(enc);
674 return ret;
678 sshkey_xmss_serialize_state(const struct sshkey *k, struct sshbuf *b)
680 struct ssh_xmss_state *state = k->xmss_state;
681 treehash_inst *th;
682 u_int32_t i, node;
683 int r;
685 if (state == NULL)
686 return SSH_ERR_INVALID_ARGUMENT;
687 if (state->stack == NULL)
688 return SSH_ERR_INVALID_ARGUMENT;
689 state->stackoffset = state->bds.stackoffset; /* copy back */
690 if ((r = sshbuf_put_cstring(b, SSH_XMSS_K2_MAGIC)) != 0 ||
691 (r = sshbuf_put_u32(b, state->idx)) != 0 ||
692 (r = sshbuf_put_string(b, state->stack, num_stack(state))) != 0 ||
693 (r = sshbuf_put_u32(b, state->stackoffset)) != 0 ||
694 (r = sshbuf_put_string(b, state->stacklevels, num_stacklevels(state))) != 0 ||
695 (r = sshbuf_put_string(b, state->auth, num_auth(state))) != 0 ||
696 (r = sshbuf_put_string(b, state->keep, num_keep(state))) != 0 ||
697 (r = sshbuf_put_string(b, state->th_nodes, num_th_nodes(state))) != 0 ||
698 (r = sshbuf_put_string(b, state->retain, num_retain(state))) != 0 ||
699 (r = sshbuf_put_u32(b, num_treehash(state))) != 0)
700 return r;
701 for (i = 0; i < num_treehash(state); i++) {
702 th = &state->treehash[i];
703 node = th->node - state->th_nodes;
704 if ((r = sshbuf_put_u32(b, th->h)) != 0 ||
705 (r = sshbuf_put_u32(b, th->next_idx)) != 0 ||
706 (r = sshbuf_put_u32(b, th->stackusage)) != 0 ||
707 (r = sshbuf_put_u8(b, th->completed)) != 0 ||
708 (r = sshbuf_put_u32(b, node)) != 0)
709 return r;
711 return 0;
715 sshkey_xmss_serialize_state_opt(const struct sshkey *k, struct sshbuf *b,
716 enum sshkey_serialize_rep opts)
718 struct ssh_xmss_state *state = k->xmss_state;
719 int r = SSH_ERR_INVALID_ARGUMENT;
720 u_char have_stack, have_filename, have_enc;
722 if (state == NULL)
723 return SSH_ERR_INVALID_ARGUMENT;
724 if ((r = sshbuf_put_u8(b, opts)) != 0)
725 return r;
726 switch (opts) {
727 case SSHKEY_SERIALIZE_STATE:
728 r = sshkey_xmss_serialize_state(k, b);
729 break;
730 case SSHKEY_SERIALIZE_FULL:
731 if ((r = sshkey_xmss_serialize_enc_key(k, b)) != 0)
732 return r;
733 r = sshkey_xmss_serialize_state(k, b);
734 break;
735 case SSHKEY_SERIALIZE_SHIELD:
736 /* all of stack/filename/enc are optional */
737 have_stack = state->stack != NULL;
738 if ((r = sshbuf_put_u8(b, have_stack)) != 0)
739 return r;
740 if (have_stack) {
741 state->idx = PEEK_U32(k->xmss_sk); /* update */
742 if ((r = sshkey_xmss_serialize_state(k, b)) != 0)
743 return r;
745 have_filename = k->xmss_filename != NULL;
746 if ((r = sshbuf_put_u8(b, have_filename)) != 0)
747 return r;
748 if (have_filename &&
749 (r = sshbuf_put_cstring(b, k->xmss_filename)) != 0)
750 return r;
751 have_enc = state->enc_keyiv != NULL;
752 if ((r = sshbuf_put_u8(b, have_enc)) != 0)
753 return r;
754 if (have_enc &&
755 (r = sshkey_xmss_serialize_enc_key(k, b)) != 0)
756 return r;
757 if ((r = sshbuf_put_u32(b, state->maxidx)) != 0 ||
758 (r = sshbuf_put_u8(b, state->allow_update)) != 0)
759 return r;
760 break;
761 case SSHKEY_SERIALIZE_DEFAULT:
762 r = 0;
763 break;
764 default:
765 r = SSH_ERR_INVALID_ARGUMENT;
766 break;
768 return r;
772 sshkey_xmss_deserialize_state(struct sshkey *k, struct sshbuf *b)
774 struct ssh_xmss_state *state = k->xmss_state;
775 treehash_inst *th;
776 u_int32_t i, lh, node;
777 size_t ls, lsl, la, lk, ln, lr;
778 char *magic;
779 int r = SSH_ERR_INTERNAL_ERROR;
781 if (state == NULL)
782 return SSH_ERR_INVALID_ARGUMENT;
783 if (k->xmss_sk == NULL)
784 return SSH_ERR_INVALID_ARGUMENT;
785 if ((state->treehash = calloc(num_treehash(state),
786 sizeof(treehash_inst))) == NULL)
787 return SSH_ERR_ALLOC_FAIL;
788 if ((r = sshbuf_get_cstring(b, &magic, NULL)) != 0 ||
789 (r = sshbuf_get_u32(b, &state->idx)) != 0 ||
790 (r = sshbuf_get_string(b, &state->stack, &ls)) != 0 ||
791 (r = sshbuf_get_u32(b, &state->stackoffset)) != 0 ||
792 (r = sshbuf_get_string(b, &state->stacklevels, &lsl)) != 0 ||
793 (r = sshbuf_get_string(b, &state->auth, &la)) != 0 ||
794 (r = sshbuf_get_string(b, &state->keep, &lk)) != 0 ||
795 (r = sshbuf_get_string(b, &state->th_nodes, &ln)) != 0 ||
796 (r = sshbuf_get_string(b, &state->retain, &lr)) != 0 ||
797 (r = sshbuf_get_u32(b, &lh)) != 0)
798 goto out;
799 if (strcmp(magic, SSH_XMSS_K2_MAGIC) != 0) {
800 r = SSH_ERR_INVALID_ARGUMENT;
801 goto out;
803 /* XXX check stackoffset */
804 if (ls != num_stack(state) ||
805 lsl != num_stacklevels(state) ||
806 la != num_auth(state) ||
807 lk != num_keep(state) ||
808 ln != num_th_nodes(state) ||
809 lr != num_retain(state) ||
810 lh != num_treehash(state)) {
811 r = SSH_ERR_INVALID_ARGUMENT;
812 goto out;
814 for (i = 0; i < num_treehash(state); i++) {
815 th = &state->treehash[i];
816 if ((r = sshbuf_get_u32(b, &th->h)) != 0 ||
817 (r = sshbuf_get_u32(b, &th->next_idx)) != 0 ||
818 (r = sshbuf_get_u32(b, &th->stackusage)) != 0 ||
819 (r = sshbuf_get_u8(b, &th->completed)) != 0 ||
820 (r = sshbuf_get_u32(b, &node)) != 0)
821 goto out;
822 if (node < num_th_nodes(state))
823 th->node = &state->th_nodes[node];
825 POKE_U32(k->xmss_sk, state->idx);
826 xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
827 state->stacklevels, state->auth, state->keep, state->treehash,
828 state->retain, 0);
829 /* success */
830 r = 0;
831 out:
832 free(magic);
833 return r;
837 sshkey_xmss_deserialize_state_opt(struct sshkey *k, struct sshbuf *b)
839 struct ssh_xmss_state *state = k->xmss_state;
840 enum sshkey_serialize_rep opts;
841 u_char have_state, have_stack, have_filename, have_enc;
842 int r;
844 if ((r = sshbuf_get_u8(b, &have_state)) != 0)
845 return r;
847 opts = have_state;
848 switch (opts) {
849 case SSHKEY_SERIALIZE_DEFAULT:
850 r = 0;
851 break;
852 case SSHKEY_SERIALIZE_SHIELD:
853 if ((r = sshbuf_get_u8(b, &have_stack)) != 0)
854 return r;
855 if (have_stack &&
856 (r = sshkey_xmss_deserialize_state(k, b)) != 0)
857 return r;
858 if ((r = sshbuf_get_u8(b, &have_filename)) != 0)
859 return r;
860 if (have_filename &&
861 (r = sshbuf_get_cstring(b, &k->xmss_filename, NULL)) != 0)
862 return r;
863 if ((r = sshbuf_get_u8(b, &have_enc)) != 0)
864 return r;
865 if (have_enc &&
866 (r = sshkey_xmss_deserialize_enc_key(k, b)) != 0)
867 return r;
868 if ((r = sshbuf_get_u32(b, &state->maxidx)) != 0 ||
869 (r = sshbuf_get_u8(b, &state->allow_update)) != 0)
870 return r;
871 break;
872 case SSHKEY_SERIALIZE_STATE:
873 if ((r = sshkey_xmss_deserialize_state(k, b)) != 0)
874 return r;
875 break;
876 case SSHKEY_SERIALIZE_FULL:
877 if ((r = sshkey_xmss_deserialize_enc_key(k, b)) != 0 ||
878 (r = sshkey_xmss_deserialize_state(k, b)) != 0)
879 return r;
880 break;
881 default:
882 r = SSH_ERR_INVALID_FORMAT;
883 break;
885 return r;
889 sshkey_xmss_encrypt_state(const struct sshkey *k, struct sshbuf *b,
890 struct sshbuf **retp)
892 struct ssh_xmss_state *state = k->xmss_state;
893 struct sshbuf *encrypted = NULL, *encoded = NULL, *padded = NULL;
894 struct sshcipher_ctx *ciphercontext = NULL;
895 const struct sshcipher *cipher;
896 u_char *cp, *key, *iv = NULL;
897 size_t i, keylen, ivlen, blocksize, authlen, encrypted_len, aadlen;
898 int r = SSH_ERR_INTERNAL_ERROR;
900 if (retp != NULL)
901 *retp = NULL;
902 if (state == NULL ||
903 state->enc_keyiv == NULL ||
904 state->enc_ciphername == NULL)
905 return SSH_ERR_INTERNAL_ERROR;
906 if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
907 r = SSH_ERR_INTERNAL_ERROR;
908 goto out;
910 blocksize = cipher_blocksize(cipher);
911 keylen = cipher_keylen(cipher);
912 ivlen = cipher_ivlen(cipher);
913 authlen = cipher_authlen(cipher);
914 if (state->enc_keyiv_len != keylen + ivlen) {
915 r = SSH_ERR_INVALID_FORMAT;
916 goto out;
918 key = state->enc_keyiv;
919 if ((encrypted = sshbuf_new()) == NULL ||
920 (encoded = sshbuf_new()) == NULL ||
921 (padded = sshbuf_new()) == NULL ||
922 (iv = malloc(ivlen)) == NULL) {
923 r = SSH_ERR_ALLOC_FAIL;
924 goto out;
927 /* replace first 4 bytes of IV with index to ensure uniqueness */
928 memcpy(iv, key + keylen, ivlen);
929 POKE_U32(iv, state->idx);
931 if ((r = sshbuf_put(encoded, XMSS_MAGIC, sizeof(XMSS_MAGIC))) != 0 ||
932 (r = sshbuf_put_u32(encoded, state->idx)) != 0)
933 goto out;
935 /* padded state will be encrypted */
936 if ((r = sshbuf_putb(padded, b)) != 0)
937 goto out;
938 i = 0;
939 while (sshbuf_len(padded) % blocksize) {
940 if ((r = sshbuf_put_u8(padded, ++i & 0xff)) != 0)
941 goto out;
943 encrypted_len = sshbuf_len(padded);
945 /* header including the length of state is used as AAD */
946 if ((r = sshbuf_put_u32(encoded, encrypted_len)) != 0)
947 goto out;
948 aadlen = sshbuf_len(encoded);
950 /* concat header and state */
951 if ((r = sshbuf_putb(encoded, padded)) != 0)
952 goto out;
954 /* reserve space for encryption of encoded data plus auth tag */
955 /* encrypt at offset addlen */
956 if ((r = sshbuf_reserve(encrypted,
957 encrypted_len + aadlen + authlen, &cp)) != 0 ||
958 (r = cipher_init(&ciphercontext, cipher, key, keylen,
959 iv, ivlen, 1)) != 0 ||
960 (r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encoded),
961 encrypted_len, aadlen, authlen)) != 0)
962 goto out;
964 /* success */
965 r = 0;
966 out:
967 if (retp != NULL) {
968 *retp = encrypted;
969 encrypted = NULL;
971 sshbuf_free(padded);
972 sshbuf_free(encoded);
973 sshbuf_free(encrypted);
974 cipher_free(ciphercontext);
975 free(iv);
976 return r;
980 sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded,
981 struct sshbuf **retp)
983 struct ssh_xmss_state *state = k->xmss_state;
984 struct sshbuf *copy = NULL, *decrypted = NULL;
985 struct sshcipher_ctx *ciphercontext = NULL;
986 const struct sshcipher *cipher = NULL;
987 u_char *key, *iv = NULL, *dp;
988 size_t keylen, ivlen, authlen, aadlen;
989 u_int blocksize, encrypted_len, index;
990 int r = SSH_ERR_INTERNAL_ERROR;
992 if (retp != NULL)
993 *retp = NULL;
994 if (state == NULL ||
995 state->enc_keyiv == NULL ||
996 state->enc_ciphername == NULL)
997 return SSH_ERR_INTERNAL_ERROR;
998 if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
999 r = SSH_ERR_INVALID_FORMAT;
1000 goto out;
1002 blocksize = cipher_blocksize(cipher);
1003 keylen = cipher_keylen(cipher);
1004 ivlen = cipher_ivlen(cipher);
1005 authlen = cipher_authlen(cipher);
1006 if (state->enc_keyiv_len != keylen + ivlen) {
1007 r = SSH_ERR_INTERNAL_ERROR;
1008 goto out;
1010 key = state->enc_keyiv;
1012 if ((copy = sshbuf_fromb(encoded)) == NULL ||
1013 (decrypted = sshbuf_new()) == NULL ||
1014 (iv = malloc(ivlen)) == NULL) {
1015 r = SSH_ERR_ALLOC_FAIL;
1016 goto out;
1019 /* check magic */
1020 if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) ||
1021 memcmp(sshbuf_ptr(encoded), XMSS_MAGIC, sizeof(XMSS_MAGIC))) {
1022 r = SSH_ERR_INVALID_FORMAT;
1023 goto out;
1025 /* parse public portion */
1026 if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 ||
1027 (r = sshbuf_get_u32(encoded, &index)) != 0 ||
1028 (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0)
1029 goto out;
1031 /* check size of encrypted key blob */
1032 if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
1033 r = SSH_ERR_INVALID_FORMAT;
1034 goto out;
1036 /* check that an appropriate amount of auth data is present */
1037 if (sshbuf_len(encoded) < authlen ||
1038 sshbuf_len(encoded) - authlen < encrypted_len) {
1039 r = SSH_ERR_INVALID_FORMAT;
1040 goto out;
1043 aadlen = sshbuf_len(copy) - sshbuf_len(encoded);
1045 /* replace first 4 bytes of IV with index to ensure uniqueness */
1046 memcpy(iv, key + keylen, ivlen);
1047 POKE_U32(iv, index);
1049 /* decrypt private state of key */
1050 if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 ||
1051 (r = cipher_init(&ciphercontext, cipher, key, keylen,
1052 iv, ivlen, 0)) != 0 ||
1053 (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy),
1054 encrypted_len, aadlen, authlen)) != 0)
1055 goto out;
1057 /* there should be no trailing data */
1058 if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0)
1059 goto out;
1060 if (sshbuf_len(encoded) != 0) {
1061 r = SSH_ERR_INVALID_FORMAT;
1062 goto out;
1065 /* remove AAD */
1066 if ((r = sshbuf_consume(decrypted, aadlen)) != 0)
1067 goto out;
1068 /* XXX encrypted includes unchecked padding */
1070 /* success */
1071 r = 0;
1072 if (retp != NULL) {
1073 *retp = decrypted;
1074 decrypted = NULL;
1076 out:
1077 cipher_free(ciphercontext);
1078 sshbuf_free(copy);
1079 sshbuf_free(decrypted);
1080 free(iv);
1081 return r;
1084 u_int32_t
1085 sshkey_xmss_signatures_left(const struct sshkey *k)
1087 struct ssh_xmss_state *state = k->xmss_state;
1088 u_int32_t idx;
1090 if (sshkey_type_plain(k->type) == KEY_XMSS && state &&
1091 state->maxidx) {
1092 idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
1093 if (idx < state->maxidx)
1094 return state->maxidx - idx;
1096 return 0;
1100 sshkey_xmss_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
1102 struct ssh_xmss_state *state = k->xmss_state;
1104 if (sshkey_type_plain(k->type) != KEY_XMSS)
1105 return SSH_ERR_INVALID_ARGUMENT;
1106 if (maxsign == 0)
1107 return 0;
1108 if (state->idx + maxsign < state->idx)
1109 return SSH_ERR_INVALID_ARGUMENT;
1110 state->maxidx = state->idx + maxsign;
1111 return 0;
1113 #endif /* WITH_XMSS */