1 /* $OpenBSD: sshkey-xmss.c,v 1.3 2018/07/09 21:59:10 markus Exp $ */
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
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.
29 #include <sys/types.h>
37 #ifdef HAVE_SYS_FILE_H
38 # include <sys/file.h>
46 #include "sshkey-xmss.h"
49 #include "xmss_fast.h"
51 /* opaque internal XMSS state */
52 #define XMSS_MAGIC "xmss-state-v1"
53 #define XMSS_CIPHERNAME "aes256-gcm@openssh.com"
54 struct ssh_xmss_state {
60 u_int32_t stackoffset;
66 treehash_inst *treehash;
68 u_int32_t idx; /* state read from file */
69 u_int32_t maxidx; /* restricted # of signatures */
70 int have_state; /* .state file exists */
71 int lockfd; /* locked in sshkey_xmss_get_state() */
72 int allow_update; /* allow sshkey_xmss_update_state() */
73 char *enc_ciphername;/* encrypt state with cipher */
74 u_char *enc_keyiv; /* encrypt state with key */
75 u_int32_t enc_keyiv_len; /* length of enc_keyiv */
78 int sshkey_xmss_init_bds_state(struct sshkey *);
79 int sshkey_xmss_init_enc_key(struct sshkey *, const char *);
80 void sshkey_xmss_free_bds(struct sshkey *);
81 int sshkey_xmss_get_state_from_file(struct sshkey *, const char *,
82 int *, sshkey_printfn *);
83 int sshkey_xmss_encrypt_state(const struct sshkey *, struct sshbuf *,
85 int sshkey_xmss_decrypt_state(const struct sshkey *, struct sshbuf *,
87 int sshkey_xmss_serialize_enc_key(const struct sshkey *, struct sshbuf *);
88 int sshkey_xmss_deserialize_enc_key(struct sshkey *, struct sshbuf *);
90 #define PRINT(s...) do { if (pr) pr(s); } while (0)
93 sshkey_xmss_init(struct sshkey *key, const char *name)
95 struct ssh_xmss_state *state;
97 if (key->xmss_state != NULL)
98 return SSH_ERR_INVALID_FORMAT;
100 return SSH_ERR_INVALID_FORMAT;
101 state = calloc(sizeof(struct ssh_xmss_state), 1);
103 return SSH_ERR_ALLOC_FAIL;
104 if (strcmp(name, XMSS_SHA2_256_W16_H10_NAME) == 0) {
108 } else if (strcmp(name, XMSS_SHA2_256_W16_H16_NAME) == 0) {
112 } else if (strcmp(name, XMSS_SHA2_256_W16_H20_NAME) == 0) {
118 return SSH_ERR_KEY_TYPE_UNKNOWN;
120 if ((key->xmss_name = strdup(name)) == NULL) {
122 return SSH_ERR_ALLOC_FAIL;
124 state->k = 2; /* XXX hardcoded */
126 if (xmss_set_params(&state->params, state->n, state->h, state->w,
129 return SSH_ERR_INVALID_FORMAT;
131 key->xmss_state = state;
136 sshkey_xmss_free_state(struct sshkey *key)
138 struct ssh_xmss_state *state = key->xmss_state;
140 sshkey_xmss_free_bds(key);
142 if (state->enc_keyiv) {
143 explicit_bzero(state->enc_keyiv, state->enc_keyiv_len);
144 free(state->enc_keyiv);
146 free(state->enc_ciphername);
149 key->xmss_state = NULL;
152 #define SSH_XMSS_K2_MAGIC "k=2"
153 #define num_stack(x) ((x->h+1)*(x->n))
154 #define num_stacklevels(x) (x->h+1)
155 #define num_auth(x) ((x->h)*(x->n))
156 #define num_keep(x) ((x->h >> 1)*(x->n))
157 #define num_th_nodes(x) ((x->h - x->k)*(x->n))
158 #define num_retain(x) (((1ULL << x->k) - x->k - 1) * (x->n))
159 #define num_treehash(x) ((x->h) - (x->k))
162 sshkey_xmss_init_bds_state(struct sshkey *key)
164 struct ssh_xmss_state *state = key->xmss_state;
167 state->stackoffset = 0;
168 if ((state->stack = calloc(num_stack(state), 1)) == NULL ||
169 (state->stacklevels = calloc(num_stacklevels(state), 1))== NULL ||
170 (state->auth = calloc(num_auth(state), 1)) == NULL ||
171 (state->keep = calloc(num_keep(state), 1)) == NULL ||
172 (state->th_nodes = calloc(num_th_nodes(state), 1)) == NULL ||
173 (state->retain = calloc(num_retain(state), 1)) == NULL ||
174 (state->treehash = calloc(num_treehash(state),
175 sizeof(treehash_inst))) == NULL) {
176 sshkey_xmss_free_bds(key);
177 return SSH_ERR_ALLOC_FAIL;
179 for (i = 0; i < state->h - state->k; i++)
180 state->treehash[i].node = &state->th_nodes[state->n*i];
181 xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
182 state->stacklevels, state->auth, state->keep, state->treehash,
188 sshkey_xmss_free_bds(struct sshkey *key)
190 struct ssh_xmss_state *state = key->xmss_state;
195 free(state->stacklevels);
198 free(state->th_nodes);
200 free(state->treehash);
202 state->stacklevels = NULL;
205 state->th_nodes = NULL;
206 state->retain = NULL;
207 state->treehash = NULL;
211 sshkey_xmss_params(const struct sshkey *key)
213 struct ssh_xmss_state *state = key->xmss_state;
217 return &state->params;
221 sshkey_xmss_bds_state(const struct sshkey *key)
223 struct ssh_xmss_state *state = key->xmss_state;
231 sshkey_xmss_siglen(const struct sshkey *key, size_t *lenp)
233 struct ssh_xmss_state *state = key->xmss_state;
236 return SSH_ERR_INVALID_ARGUMENT;
238 return SSH_ERR_INVALID_FORMAT;
239 *lenp = 4 + state->n +
240 state->params.wots_par.keysize +
246 sshkey_xmss_pklen(const struct sshkey *key)
248 struct ssh_xmss_state *state = key->xmss_state;
256 sshkey_xmss_sklen(const struct sshkey *key)
258 struct ssh_xmss_state *state = key->xmss_state;
262 return state->n * 4 + 4;
266 sshkey_xmss_init_enc_key(struct sshkey *k, const char *ciphername)
268 struct ssh_xmss_state *state = k->xmss_state;
269 const struct sshcipher *cipher;
270 size_t keylen = 0, ivlen = 0;
273 return SSH_ERR_INVALID_ARGUMENT;
274 if ((cipher = cipher_by_name(ciphername)) == NULL)
275 return SSH_ERR_INTERNAL_ERROR;
276 if ((state->enc_ciphername = strdup(ciphername)) == NULL)
277 return SSH_ERR_ALLOC_FAIL;
278 keylen = cipher_keylen(cipher);
279 ivlen = cipher_ivlen(cipher);
280 state->enc_keyiv_len = keylen + ivlen;
281 if ((state->enc_keyiv = calloc(state->enc_keyiv_len, 1)) == NULL) {
282 free(state->enc_ciphername);
283 state->enc_ciphername = NULL;
284 return SSH_ERR_ALLOC_FAIL;
286 arc4random_buf(state->enc_keyiv, state->enc_keyiv_len);
291 sshkey_xmss_serialize_enc_key(const struct sshkey *k, struct sshbuf *b)
293 struct ssh_xmss_state *state = k->xmss_state;
296 if (state == NULL || state->enc_keyiv == NULL ||
297 state->enc_ciphername == NULL)
298 return SSH_ERR_INVALID_ARGUMENT;
299 if ((r = sshbuf_put_cstring(b, state->enc_ciphername)) != 0 ||
300 (r = sshbuf_put_string(b, state->enc_keyiv,
301 state->enc_keyiv_len)) != 0)
307 sshkey_xmss_deserialize_enc_key(struct sshkey *k, struct sshbuf *b)
309 struct ssh_xmss_state *state = k->xmss_state;
314 return SSH_ERR_INVALID_ARGUMENT;
315 if ((r = sshbuf_get_cstring(b, &state->enc_ciphername, NULL)) != 0 ||
316 (r = sshbuf_get_string(b, &state->enc_keyiv, &len)) != 0)
318 state->enc_keyiv_len = len;
323 sshkey_xmss_serialize_pk_info(const struct sshkey *k, struct sshbuf *b,
324 enum sshkey_serialize_rep opts)
326 struct ssh_xmss_state *state = k->xmss_state;
327 u_char have_info = 1;
332 return SSH_ERR_INVALID_ARGUMENT;
333 if (opts != SSHKEY_SERIALIZE_INFO)
335 idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
336 if ((r = sshbuf_put_u8(b, have_info)) != 0 ||
337 (r = sshbuf_put_u32(b, idx)) != 0 ||
338 (r = sshbuf_put_u32(b, state->maxidx)) != 0)
344 sshkey_xmss_deserialize_pk_info(struct sshkey *k, struct sshbuf *b)
346 struct ssh_xmss_state *state = k->xmss_state;
351 return SSH_ERR_INVALID_ARGUMENT;
353 if (sshbuf_len(b) == 0)
355 if ((r = sshbuf_get_u8(b, &have_info)) != 0)
358 return SSH_ERR_INVALID_ARGUMENT;
359 if ((r = sshbuf_get_u32(b, &state->idx)) != 0 ||
360 (r = sshbuf_get_u32(b, &state->maxidx)) != 0)
366 sshkey_xmss_generate_private_key(struct sshkey *k, u_int bits)
372 name = XMSS_SHA2_256_W16_H10_NAME;
373 } else if (bits == 16) {
374 name = XMSS_SHA2_256_W16_H16_NAME;
375 } else if (bits == 20) {
376 name = XMSS_SHA2_256_W16_H20_NAME;
378 name = XMSS_DEFAULT_NAME;
380 if ((r = sshkey_xmss_init(k, name)) != 0 ||
381 (r = sshkey_xmss_init_bds_state(k)) != 0 ||
382 (r = sshkey_xmss_init_enc_key(k, XMSS_CIPHERNAME)) != 0)
384 if ((k->xmss_pk = malloc(sshkey_xmss_pklen(k))) == NULL ||
385 (k->xmss_sk = malloc(sshkey_xmss_sklen(k))) == NULL) {
386 return SSH_ERR_ALLOC_FAIL;
388 xmss_keypair(k->xmss_pk, k->xmss_sk, sshkey_xmss_bds_state(k),
389 sshkey_xmss_params(k));
394 sshkey_xmss_get_state_from_file(struct sshkey *k, const char *filename,
395 int *have_file, sshkey_printfn *pr)
397 struct sshbuf *b = NULL, *enc = NULL;
398 int ret = SSH_ERR_SYSTEM_ERROR, r, fd = -1;
400 unsigned char buf[4], *data = NULL;
403 if ((fd = open(filename, O_RDONLY)) >= 0) {
405 if (atomicio(read, fd, buf, sizeof(buf)) != sizeof(buf)) {
406 PRINT("%s: corrupt state file: %s", __func__, filename);
410 if ((data = calloc(len, 1)) == NULL) {
411 ret = SSH_ERR_ALLOC_FAIL;
414 if (atomicio(read, fd, data, len) != len) {
415 PRINT("%s: cannot read blob: %s", __func__, filename);
418 if ((enc = sshbuf_from(data, len)) == NULL) {
419 ret = SSH_ERR_ALLOC_FAIL;
422 sshkey_xmss_free_bds(k);
423 if ((r = sshkey_xmss_decrypt_state(k, enc, &b)) != 0) {
427 if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) {
443 sshkey_xmss_get_state(const struct sshkey *k, sshkey_printfn *pr)
445 struct ssh_xmss_state *state = k->xmss_state;
447 char *filename = NULL;
448 char *statefile = NULL, *ostatefile = NULL, *lockfile = NULL;
449 int lockfd = -1, have_state = 0, have_ostate, tries = 0;
450 int ret = SSH_ERR_INVALID_ARGUMENT, r;
455 * If maxidx is set, then we are allowed a limited number
456 * of signatures, but don't need to access the disk.
457 * Otherwise we need to deal with the on-disk state.
460 /* xmss_sk always contains the current state */
461 idx = PEEK_U32(k->xmss_sk);
462 if (idx < state->maxidx) {
463 state->allow_update = 1;
466 return SSH_ERR_INVALID_ARGUMENT;
468 if ((filename = k->xmss_filename) == NULL)
470 if (asprintf(&lockfile, "%s.lock", filename) < 0 ||
471 asprintf(&statefile, "%s.state", filename) < 0 ||
472 asprintf(&ostatefile, "%s.ostate", filename) < 0) {
473 ret = SSH_ERR_ALLOC_FAIL;
476 if ((lockfd = open(lockfile, O_CREAT|O_RDONLY, 0600)) < 0) {
477 ret = SSH_ERR_SYSTEM_ERROR;
478 PRINT("%s: cannot open/create: %s", __func__, lockfile);
481 while (flock(lockfd, LOCK_EX|LOCK_NB) < 0) {
482 if (errno != EWOULDBLOCK) {
483 ret = SSH_ERR_SYSTEM_ERROR;
484 PRINT("%s: cannot lock: %s", __func__, lockfile);
488 ret = SSH_ERR_SYSTEM_ERROR;
489 PRINT("%s: giving up on: %s", __func__, lockfile);
492 usleep(1000*100*tries);
494 /* XXX no longer const */
495 if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
496 statefile, &have_state, pr)) != 0) {
497 if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
498 ostatefile, &have_ostate, pr)) == 0) {
499 state->allow_update = 1;
500 r = sshkey_xmss_forward_state(k, 1);
501 state->idx = PEEK_U32(k->xmss_sk);
502 state->allow_update = 0;
505 if (!have_state && !have_ostate) {
506 /* check that bds state is initialized */
507 if (state->bds.auth == NULL)
509 PRINT("%s: start from scratch idx 0: %u", __func__, state->idx);
514 if (state->idx + 1 < state->idx) {
515 PRINT("%s: state wrap: %u", __func__, state->idx);
518 state->have_state = have_state;
519 state->lockfd = lockfd;
520 state->allow_update = 1;
533 sshkey_xmss_forward_state(const struct sshkey *k, u_int32_t reserve)
535 struct ssh_xmss_state *state = k->xmss_state;
537 size_t required_siglen;
538 unsigned long long smlen;
542 if (state == NULL || !state->allow_update)
543 return SSH_ERR_INVALID_ARGUMENT;
545 return SSH_ERR_INVALID_ARGUMENT;
546 if (state->idx + reserve <= state->idx)
547 return SSH_ERR_INVALID_ARGUMENT;
548 if ((r = sshkey_xmss_siglen(k, &required_siglen)) != 0)
550 if ((sig = malloc(required_siglen)) == NULL)
551 return SSH_ERR_ALLOC_FAIL;
552 while (reserve-- > 0) {
553 state->idx = PEEK_U32(k->xmss_sk);
554 smlen = required_siglen;
555 if ((ret = xmss_sign(k->xmss_sk, sshkey_xmss_bds_state(k),
556 sig, &smlen, &data, 0, sshkey_xmss_params(k))) != 0) {
557 r = SSH_ERR_INVALID_ARGUMENT;
566 sshkey_xmss_update_state(const struct sshkey *k, sshkey_printfn *pr)
568 struct ssh_xmss_state *state = k->xmss_state;
569 struct sshbuf *b = NULL, *enc = NULL;
571 unsigned char buf[4];
572 char *filename = NULL;
573 char *statefile = NULL, *ostatefile = NULL, *nstatefile = NULL;
575 int ret = SSH_ERR_INVALID_ARGUMENT;
577 if (state == NULL || !state->allow_update)
580 /* no update since the number of signatures is limited */
584 idx = PEEK_U32(k->xmss_sk);
585 if (idx == state->idx) {
586 /* no signature happened, no need to update */
589 } else if (idx != state->idx + 1) {
590 PRINT("%s: more than one signature happened: idx %u state %u",
591 __func__, idx, state->idx);
595 if ((filename = k->xmss_filename) == NULL)
597 if (asprintf(&statefile, "%s.state", filename) < 0 ||
598 asprintf(&ostatefile, "%s.ostate", filename) < 0 ||
599 asprintf(&nstatefile, "%s.nstate", filename) < 0) {
600 ret = SSH_ERR_ALLOC_FAIL;
604 if ((b = sshbuf_new()) == NULL) {
605 ret = SSH_ERR_ALLOC_FAIL;
608 if ((ret = sshkey_xmss_serialize_state(k, b)) != 0) {
609 PRINT("%s: SERLIALIZE FAILED: %d", __func__, ret);
612 if ((ret = sshkey_xmss_encrypt_state(k, b, &enc)) != 0) {
613 PRINT("%s: ENCRYPT FAILED: %d", __func__, ret);
616 if ((fd = open(nstatefile, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) {
617 ret = SSH_ERR_SYSTEM_ERROR;
618 PRINT("%s: open new state file: %s", __func__, nstatefile);
621 POKE_U32(buf, sshbuf_len(enc));
622 if (atomicio(vwrite, fd, buf, sizeof(buf)) != sizeof(buf)) {
623 ret = SSH_ERR_SYSTEM_ERROR;
624 PRINT("%s: write new state file hdr: %s", __func__, nstatefile);
628 if (atomicio(vwrite, fd, sshbuf_mutable_ptr(enc), sshbuf_len(enc)) !=
630 ret = SSH_ERR_SYSTEM_ERROR;
631 PRINT("%s: write new state file data: %s", __func__, nstatefile);
636 ret = SSH_ERR_SYSTEM_ERROR;
637 PRINT("%s: sync new state file: %s", __func__, nstatefile);
642 ret = SSH_ERR_SYSTEM_ERROR;
643 PRINT("%s: close new state file: %s", __func__, nstatefile);
646 if (state->have_state) {
648 if (link(statefile, ostatefile)) {
649 ret = SSH_ERR_SYSTEM_ERROR;
650 PRINT("%s: backup state %s to %s", __func__, statefile,
655 if (rename(nstatefile, statefile) < 0) {
656 ret = SSH_ERR_SYSTEM_ERROR;
657 PRINT("%s: rename %s to %s", __func__, nstatefile, statefile);
662 if (state->lockfd != -1) {
663 close(state->lockfd);
677 sshkey_xmss_serialize_state(const struct sshkey *k, struct sshbuf *b)
679 struct ssh_xmss_state *state = k->xmss_state;
685 return SSH_ERR_INVALID_ARGUMENT;
686 if (state->stack == NULL)
687 return SSH_ERR_INVALID_ARGUMENT;
688 state->stackoffset = state->bds.stackoffset; /* copy back */
689 if ((r = sshbuf_put_cstring(b, SSH_XMSS_K2_MAGIC)) != 0 ||
690 (r = sshbuf_put_u32(b, state->idx)) != 0 ||
691 (r = sshbuf_put_string(b, state->stack, num_stack(state))) != 0 ||
692 (r = sshbuf_put_u32(b, state->stackoffset)) != 0 ||
693 (r = sshbuf_put_string(b, state->stacklevels, num_stacklevels(state))) != 0 ||
694 (r = sshbuf_put_string(b, state->auth, num_auth(state))) != 0 ||
695 (r = sshbuf_put_string(b, state->keep, num_keep(state))) != 0 ||
696 (r = sshbuf_put_string(b, state->th_nodes, num_th_nodes(state))) != 0 ||
697 (r = sshbuf_put_string(b, state->retain, num_retain(state))) != 0 ||
698 (r = sshbuf_put_u32(b, num_treehash(state))) != 0)
700 for (i = 0; i < num_treehash(state); i++) {
701 th = &state->treehash[i];
702 node = th->node - state->th_nodes;
703 if ((r = sshbuf_put_u32(b, th->h)) != 0 ||
704 (r = sshbuf_put_u32(b, th->next_idx)) != 0 ||
705 (r = sshbuf_put_u32(b, th->stackusage)) != 0 ||
706 (r = sshbuf_put_u8(b, th->completed)) != 0 ||
707 (r = sshbuf_put_u32(b, node)) != 0)
714 sshkey_xmss_serialize_state_opt(const struct sshkey *k, struct sshbuf *b,
715 enum sshkey_serialize_rep opts)
717 struct ssh_xmss_state *state = k->xmss_state;
718 int r = SSH_ERR_INVALID_ARGUMENT;
721 return SSH_ERR_INVALID_ARGUMENT;
722 if ((r = sshbuf_put_u8(b, opts)) != 0)
725 case SSHKEY_SERIALIZE_STATE:
726 r = sshkey_xmss_serialize_state(k, b);
728 case SSHKEY_SERIALIZE_FULL:
729 if ((r = sshkey_xmss_serialize_enc_key(k, b)) != 0)
731 r = sshkey_xmss_serialize_state(k, b);
733 case SSHKEY_SERIALIZE_DEFAULT:
737 r = SSH_ERR_INVALID_ARGUMENT;
744 sshkey_xmss_deserialize_state(struct sshkey *k, struct sshbuf *b)
746 struct ssh_xmss_state *state = k->xmss_state;
748 u_int32_t i, lh, node;
749 size_t ls, lsl, la, lk, ln, lr;
754 return SSH_ERR_INVALID_ARGUMENT;
755 if (k->xmss_sk == NULL)
756 return SSH_ERR_INVALID_ARGUMENT;
757 if ((state->treehash = calloc(num_treehash(state),
758 sizeof(treehash_inst))) == NULL)
759 return SSH_ERR_ALLOC_FAIL;
760 if ((r = sshbuf_get_cstring(b, &magic, NULL)) != 0 ||
761 (r = sshbuf_get_u32(b, &state->idx)) != 0 ||
762 (r = sshbuf_get_string(b, &state->stack, &ls)) != 0 ||
763 (r = sshbuf_get_u32(b, &state->stackoffset)) != 0 ||
764 (r = sshbuf_get_string(b, &state->stacklevels, &lsl)) != 0 ||
765 (r = sshbuf_get_string(b, &state->auth, &la)) != 0 ||
766 (r = sshbuf_get_string(b, &state->keep, &lk)) != 0 ||
767 (r = sshbuf_get_string(b, &state->th_nodes, &ln)) != 0 ||
768 (r = sshbuf_get_string(b, &state->retain, &lr)) != 0 ||
769 (r = sshbuf_get_u32(b, &lh)) != 0)
771 if (strcmp(magic, SSH_XMSS_K2_MAGIC) != 0)
772 return SSH_ERR_INVALID_ARGUMENT;
773 /* XXX check stackoffset */
774 if (ls != num_stack(state) ||
775 lsl != num_stacklevels(state) ||
776 la != num_auth(state) ||
777 lk != num_keep(state) ||
778 ln != num_th_nodes(state) ||
779 lr != num_retain(state) ||
780 lh != num_treehash(state))
781 return SSH_ERR_INVALID_ARGUMENT;
782 for (i = 0; i < num_treehash(state); i++) {
783 th = &state->treehash[i];
784 if ((r = sshbuf_get_u32(b, &th->h)) != 0 ||
785 (r = sshbuf_get_u32(b, &th->next_idx)) != 0 ||
786 (r = sshbuf_get_u32(b, &th->stackusage)) != 0 ||
787 (r = sshbuf_get_u8(b, &th->completed)) != 0 ||
788 (r = sshbuf_get_u32(b, &node)) != 0)
790 if (node < num_th_nodes(state))
791 th->node = &state->th_nodes[node];
793 POKE_U32(k->xmss_sk, state->idx);
794 xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
795 state->stacklevels, state->auth, state->keep, state->treehash,
801 sshkey_xmss_deserialize_state_opt(struct sshkey *k, struct sshbuf *b)
803 enum sshkey_serialize_rep opts;
807 if ((r = sshbuf_get_u8(b, &have_state)) != 0)
812 case SSHKEY_SERIALIZE_DEFAULT:
815 case SSHKEY_SERIALIZE_STATE:
816 if ((r = sshkey_xmss_deserialize_state(k, b)) != 0)
819 case SSHKEY_SERIALIZE_FULL:
820 if ((r = sshkey_xmss_deserialize_enc_key(k, b)) != 0 ||
821 (r = sshkey_xmss_deserialize_state(k, b)) != 0)
825 r = SSH_ERR_INVALID_FORMAT;
832 sshkey_xmss_encrypt_state(const struct sshkey *k, struct sshbuf *b,
833 struct sshbuf **retp)
835 struct ssh_xmss_state *state = k->xmss_state;
836 struct sshbuf *encrypted = NULL, *encoded = NULL, *padded = NULL;
837 struct sshcipher_ctx *ciphercontext = NULL;
838 const struct sshcipher *cipher;
839 u_char *cp, *key, *iv = NULL;
840 size_t i, keylen, ivlen, blocksize, authlen, encrypted_len, aadlen;
841 int r = SSH_ERR_INTERNAL_ERROR;
846 state->enc_keyiv == NULL ||
847 state->enc_ciphername == NULL)
848 return SSH_ERR_INTERNAL_ERROR;
849 if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
850 r = SSH_ERR_INTERNAL_ERROR;
853 blocksize = cipher_blocksize(cipher);
854 keylen = cipher_keylen(cipher);
855 ivlen = cipher_ivlen(cipher);
856 authlen = cipher_authlen(cipher);
857 if (state->enc_keyiv_len != keylen + ivlen) {
858 r = SSH_ERR_INVALID_FORMAT;
861 key = state->enc_keyiv;
862 if ((encrypted = sshbuf_new()) == NULL ||
863 (encoded = sshbuf_new()) == NULL ||
864 (padded = sshbuf_new()) == NULL ||
865 (iv = malloc(ivlen)) == NULL) {
866 r = SSH_ERR_ALLOC_FAIL;
870 /* replace first 4 bytes of IV with index to ensure uniqueness */
871 memcpy(iv, key + keylen, ivlen);
872 POKE_U32(iv, state->idx);
874 if ((r = sshbuf_put(encoded, XMSS_MAGIC, sizeof(XMSS_MAGIC))) != 0 ||
875 (r = sshbuf_put_u32(encoded, state->idx)) != 0)
878 /* padded state will be encrypted */
879 if ((r = sshbuf_putb(padded, b)) != 0)
882 while (sshbuf_len(padded) % blocksize) {
883 if ((r = sshbuf_put_u8(padded, ++i & 0xff)) != 0)
886 encrypted_len = sshbuf_len(padded);
888 /* header including the length of state is used as AAD */
889 if ((r = sshbuf_put_u32(encoded, encrypted_len)) != 0)
891 aadlen = sshbuf_len(encoded);
893 /* concat header and state */
894 if ((r = sshbuf_putb(encoded, padded)) != 0)
897 /* reserve space for encryption of encoded data plus auth tag */
898 /* encrypt at offset addlen */
899 if ((r = sshbuf_reserve(encrypted,
900 encrypted_len + aadlen + authlen, &cp)) != 0 ||
901 (r = cipher_init(&ciphercontext, cipher, key, keylen,
902 iv, ivlen, 1)) != 0 ||
903 (r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encoded),
904 encrypted_len, aadlen, authlen)) != 0)
915 sshbuf_free(encoded);
916 sshbuf_free(encrypted);
917 cipher_free(ciphercontext);
923 sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded,
924 struct sshbuf **retp)
926 struct ssh_xmss_state *state = k->xmss_state;
927 struct sshbuf *copy = NULL, *decrypted = NULL;
928 struct sshcipher_ctx *ciphercontext = NULL;
929 const struct sshcipher *cipher = NULL;
930 u_char *key, *iv = NULL, *dp;
931 size_t keylen, ivlen, authlen, aadlen;
932 u_int blocksize, encrypted_len, index;
933 int r = SSH_ERR_INTERNAL_ERROR;
938 state->enc_keyiv == NULL ||
939 state->enc_ciphername == NULL)
940 return SSH_ERR_INTERNAL_ERROR;
941 if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
942 r = SSH_ERR_INVALID_FORMAT;
945 blocksize = cipher_blocksize(cipher);
946 keylen = cipher_keylen(cipher);
947 ivlen = cipher_ivlen(cipher);
948 authlen = cipher_authlen(cipher);
949 if (state->enc_keyiv_len != keylen + ivlen) {
950 r = SSH_ERR_INTERNAL_ERROR;
953 key = state->enc_keyiv;
955 if ((copy = sshbuf_fromb(encoded)) == NULL ||
956 (decrypted = sshbuf_new()) == NULL ||
957 (iv = malloc(ivlen)) == NULL) {
958 r = SSH_ERR_ALLOC_FAIL;
963 if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) ||
964 memcmp(sshbuf_ptr(encoded), XMSS_MAGIC, sizeof(XMSS_MAGIC))) {
965 r = SSH_ERR_INVALID_FORMAT;
968 /* parse public portion */
969 if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 ||
970 (r = sshbuf_get_u32(encoded, &index)) != 0 ||
971 (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0)
974 /* check size of encrypted key blob */
975 if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
976 r = SSH_ERR_INVALID_FORMAT;
979 /* check that an appropriate amount of auth data is present */
980 if (sshbuf_len(encoded) < encrypted_len + authlen) {
981 r = SSH_ERR_INVALID_FORMAT;
985 aadlen = sshbuf_len(copy) - sshbuf_len(encoded);
987 /* replace first 4 bytes of IV with index to ensure uniqueness */
988 memcpy(iv, key + keylen, ivlen);
991 /* decrypt private state of key */
992 if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 ||
993 (r = cipher_init(&ciphercontext, cipher, key, keylen,
994 iv, ivlen, 0)) != 0 ||
995 (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy),
996 encrypted_len, aadlen, authlen)) != 0)
999 /* there should be no trailing data */
1000 if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0)
1002 if (sshbuf_len(encoded) != 0) {
1003 r = SSH_ERR_INVALID_FORMAT;
1008 if ((r = sshbuf_consume(decrypted, aadlen)) != 0)
1010 /* XXX encrypted includes unchecked padding */
1019 cipher_free(ciphercontext);
1021 sshbuf_free(decrypted);
1027 sshkey_xmss_signatures_left(const struct sshkey *k)
1029 struct ssh_xmss_state *state = k->xmss_state;
1032 if (sshkey_type_plain(k->type) == KEY_XMSS && state &&
1034 idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
1035 if (idx < state->maxidx)
1036 return state->maxidx - idx;
1042 sshkey_xmss_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
1044 struct ssh_xmss_state *state = k->xmss_state;
1046 if (sshkey_type_plain(k->type) != KEY_XMSS)
1047 return SSH_ERR_INVALID_ARGUMENT;
1050 if (state->idx + maxsign < state->idx)
1051 return SSH_ERR_INVALID_ARGUMENT;
1052 state->maxidx = state->idx + maxsign;
1055 #endif /* WITH_XMSS */