1 /* $OpenBSD: sshkey-xmss.c,v 1.12 2022/10/28 00:39:29 djm 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"
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
{
61 u_int32_t stackoffset
;
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 *,
84 int sshkey_xmss_encrypt_state(const struct sshkey
*, struct sshbuf
*,
86 int sshkey_xmss_decrypt_state(const struct sshkey
*, 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)
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
;
102 return SSH_ERR_INVALID_FORMAT
;
103 state
= calloc(sizeof(struct ssh_xmss_state
), 1);
105 return SSH_ERR_ALLOC_FAIL
;
106 if (strcmp(name
, XMSS_SHA2_256_W16_H10_NAME
) == 0) {
110 } else if (strcmp(name
, XMSS_SHA2_256_W16_H16_NAME
) == 0) {
114 } else if (strcmp(name
, XMSS_SHA2_256_W16_H20_NAME
) == 0) {
120 return SSH_ERR_KEY_TYPE_UNKNOWN
;
122 if ((key
->xmss_name
= strdup(name
)) == NULL
) {
124 return SSH_ERR_ALLOC_FAIL
;
126 state
->k
= 2; /* XXX hardcoded */
128 if (xmss_set_params(&state
->params
, state
->n
, state
->h
, state
->w
,
131 return SSH_ERR_INVALID_FORMAT
;
133 key
->xmss_state
= state
;
138 sshkey_xmss_free_state(struct sshkey
*key
)
140 struct ssh_xmss_state
*state
= key
->xmss_state
;
142 sshkey_xmss_free_bds(key
);
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
);
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
;
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
,
190 sshkey_xmss_free_bds(struct sshkey
*key
)
192 struct ssh_xmss_state
*state
= key
->xmss_state
;
197 free(state
->stacklevels
);
200 free(state
->th_nodes
);
202 free(state
->treehash
);
204 state
->stacklevels
= NULL
;
207 state
->th_nodes
= NULL
;
208 state
->retain
= NULL
;
209 state
->treehash
= NULL
;
213 sshkey_xmss_params(const struct sshkey
*key
)
215 struct ssh_xmss_state
*state
= key
->xmss_state
;
219 return &state
->params
;
223 sshkey_xmss_bds_state(const struct sshkey
*key
)
225 struct ssh_xmss_state
*state
= key
->xmss_state
;
233 sshkey_xmss_siglen(const struct sshkey
*key
, size_t *lenp
)
235 struct ssh_xmss_state
*state
= key
->xmss_state
;
238 return SSH_ERR_INVALID_ARGUMENT
;
240 return SSH_ERR_INVALID_FORMAT
;
241 *lenp
= 4 + state
->n
+
242 state
->params
.wots_par
.keysize
+
248 sshkey_xmss_pklen(const struct sshkey
*key
)
250 struct ssh_xmss_state
*state
= key
->xmss_state
;
258 sshkey_xmss_sklen(const struct sshkey
*key
)
260 struct ssh_xmss_state
*state
= key
->xmss_state
;
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;
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
);
293 sshkey_xmss_serialize_enc_key(const struct sshkey
*k
, struct sshbuf
*b
)
295 struct ssh_xmss_state
*state
= k
->xmss_state
;
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)
309 sshkey_xmss_deserialize_enc_key(struct sshkey
*k
, struct sshbuf
*b
)
311 struct ssh_xmss_state
*state
= k
->xmss_state
;
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)
320 state
->enc_keyiv_len
= len
;
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;
334 return SSH_ERR_INVALID_ARGUMENT
;
335 if (opts
!= SSHKEY_SERIALIZE_INFO
)
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)
346 sshkey_xmss_deserialize_pk_info(struct sshkey
*k
, struct sshbuf
*b
)
348 struct ssh_xmss_state
*state
= k
->xmss_state
;
353 return SSH_ERR_INVALID_ARGUMENT
;
355 if (sshbuf_len(b
) == 0)
357 if ((r
= sshbuf_get_u8(b
, &have_info
)) != 0)
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)
368 sshkey_xmss_generate_private_key(struct sshkey
*k
, int bits
)
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
;
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)
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
));
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;
402 unsigned char buf
[4], *data
= NULL
;
405 if ((fd
= open(filename
, O_RDONLY
)) >= 0) {
407 if (atomicio(read
, fd
, buf
, sizeof(buf
)) != sizeof(buf
)) {
408 PRINT("corrupt state file: %s", filename
);
412 if ((data
= calloc(len
, 1)) == NULL
) {
413 ret
= SSH_ERR_ALLOC_FAIL
;
416 if (atomicio(read
, fd
, data
, len
) != len
) {
417 PRINT("cannot read blob: %s", filename
);
420 if ((enc
= sshbuf_from(data
, len
)) == NULL
) {
421 ret
= SSH_ERR_ALLOC_FAIL
;
424 sshkey_xmss_free_bds(k
);
425 if ((r
= sshkey_xmss_decrypt_state(k
, enc
, &b
)) != 0) {
429 if ((r
= sshkey_xmss_deserialize_state(k
, b
)) != 0) {
445 sshkey_xmss_get_state(const struct sshkey
*k
, int printerror
)
447 struct ssh_xmss_state
*state
= k
->xmss_state
;
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
;
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.
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;
468 return SSH_ERR_INVALID_ARGUMENT
;
470 if ((filename
= k
->xmss_filename
) == NULL
)
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
;
478 if ((lockfd
= open(lockfile
, O_CREAT
|O_RDONLY
, 0600)) == -1) {
479 ret
= SSH_ERR_SYSTEM_ERROR
;
480 PRINT("cannot open/create: %s", lockfile
);
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
);
490 ret
= SSH_ERR_SYSTEM_ERROR
;
491 PRINT("giving up on: %s", lockfile
);
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
)
511 PRINT("start from scratch idx 0: %u", state
->idx
);
516 if (state
->idx
+ 1 < state
->idx
) {
517 PRINT("state wrap: %u", state
->idx
);
520 state
->have_state
= have_state
;
521 state
->lockfd
= lockfd
;
522 state
->allow_update
= 1;
535 sshkey_xmss_forward_state(const struct sshkey
*k
, u_int32_t reserve
)
537 struct ssh_xmss_state
*state
= k
->xmss_state
;
539 size_t required_siglen
;
540 unsigned long long smlen
;
544 if (state
== NULL
|| !state
->allow_update
)
545 return SSH_ERR_INVALID_ARGUMENT
;
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)
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
;
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
;
573 unsigned char buf
[4];
574 char *filename
= NULL
;
575 char *statefile
= NULL
, *ostatefile
= NULL
, *nstatefile
= NULL
;
577 int ret
= SSH_ERR_INVALID_ARGUMENT
;
579 if (state
== NULL
|| !state
->allow_update
)
582 /* no update since the number of signatures is limited */
586 idx
= PEEK_U32(k
->xmss_sk
);
587 if (idx
== state
->idx
) {
588 /* no signature happened, no need to update */
591 } else if (idx
!= state
->idx
+ 1) {
592 PRINT("more than one signature happened: idx %u state %u",
597 if ((filename
= k
->xmss_filename
) == NULL
)
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
;
606 if ((b
= sshbuf_new()) == NULL
) {
607 ret
= SSH_ERR_ALLOC_FAIL
;
610 if ((ret
= sshkey_xmss_serialize_state(k
, b
)) != 0) {
611 PRINT("SERLIALIZE FAILED: %d", ret
);
614 if ((ret
= sshkey_xmss_encrypt_state(k
, b
, &enc
)) != 0) {
615 PRINT("ENCRYPT FAILED: %d", ret
);
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
);
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
);
630 if (atomicio(vwrite
, fd
, sshbuf_mutable_ptr(enc
), sshbuf_len(enc
)) !=
632 ret
= SSH_ERR_SYSTEM_ERROR
;
633 PRINT("write new state file data: %s", nstatefile
);
637 if (fsync(fd
) == -1) {
638 ret
= SSH_ERR_SYSTEM_ERROR
;
639 PRINT("sync new state file: %s", nstatefile
);
643 if (close(fd
) == -1) {
644 ret
= SSH_ERR_SYSTEM_ERROR
;
645 PRINT("close new state file: %s", nstatefile
);
648 if (state
->have_state
) {
650 if (link(statefile
, ostatefile
)) {
651 ret
= SSH_ERR_SYSTEM_ERROR
;
652 PRINT("backup state %s to %s", statefile
, ostatefile
);
656 if (rename(nstatefile
, statefile
) == -1) {
657 ret
= SSH_ERR_SYSTEM_ERROR
;
658 PRINT("rename %s to %s", nstatefile
, statefile
);
663 if (state
->lockfd
!= -1) {
664 close(state
->lockfd
);
678 sshkey_xmss_serialize_state(const struct sshkey
*k
, struct sshbuf
*b
)
680 struct ssh_xmss_state
*state
= k
->xmss_state
;
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)
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)
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
;
723 return SSH_ERR_INVALID_ARGUMENT
;
724 if ((r
= sshbuf_put_u8(b
, opts
)) != 0)
727 case SSHKEY_SERIALIZE_STATE
:
728 r
= sshkey_xmss_serialize_state(k
, b
);
730 case SSHKEY_SERIALIZE_FULL
:
731 if ((r
= sshkey_xmss_serialize_enc_key(k
, b
)) != 0)
733 r
= sshkey_xmss_serialize_state(k
, b
);
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)
741 state
->idx
= PEEK_U32(k
->xmss_sk
); /* update */
742 if ((r
= sshkey_xmss_serialize_state(k
, b
)) != 0)
745 have_filename
= k
->xmss_filename
!= NULL
;
746 if ((r
= sshbuf_put_u8(b
, have_filename
)) != 0)
749 (r
= sshbuf_put_cstring(b
, k
->xmss_filename
)) != 0)
751 have_enc
= state
->enc_keyiv
!= NULL
;
752 if ((r
= sshbuf_put_u8(b
, have_enc
)) != 0)
755 (r
= sshkey_xmss_serialize_enc_key(k
, b
)) != 0)
757 if ((r
= sshbuf_put_u32(b
, state
->maxidx
)) != 0 ||
758 (r
= sshbuf_put_u8(b
, state
->allow_update
)) != 0)
761 case SSHKEY_SERIALIZE_DEFAULT
:
765 r
= SSH_ERR_INVALID_ARGUMENT
;
772 sshkey_xmss_deserialize_state(struct sshkey
*k
, struct sshbuf
*b
)
774 struct ssh_xmss_state
*state
= k
->xmss_state
;
776 u_int32_t i
, lh
, node
;
777 size_t ls
, lsl
, la
, lk
, ln
, lr
;
779 int r
= SSH_ERR_INTERNAL_ERROR
;
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)
799 if (strcmp(magic
, SSH_XMSS_K2_MAGIC
) != 0) {
800 r
= SSH_ERR_INVALID_ARGUMENT
;
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
;
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)
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
,
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
;
844 if ((r
= sshbuf_get_u8(b
, &have_state
)) != 0)
849 case SSHKEY_SERIALIZE_DEFAULT
:
852 case SSHKEY_SERIALIZE_SHIELD
:
853 if ((r
= sshbuf_get_u8(b
, &have_stack
)) != 0)
856 (r
= sshkey_xmss_deserialize_state(k
, b
)) != 0)
858 if ((r
= sshbuf_get_u8(b
, &have_filename
)) != 0)
861 (r
= sshbuf_get_cstring(b
, &k
->xmss_filename
, NULL
)) != 0)
863 if ((r
= sshbuf_get_u8(b
, &have_enc
)) != 0)
866 (r
= sshkey_xmss_deserialize_enc_key(k
, b
)) != 0)
868 if ((r
= sshbuf_get_u32(b
, &state
->maxidx
)) != 0 ||
869 (r
= sshbuf_get_u8(b
, &state
->allow_update
)) != 0)
872 case SSHKEY_SERIALIZE_STATE
:
873 if ((r
= sshkey_xmss_deserialize_state(k
, b
)) != 0)
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)
882 r
= SSH_ERR_INVALID_FORMAT
;
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
;
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
;
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
;
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
;
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)
935 /* padded state will be encrypted */
936 if ((r
= sshbuf_putb(padded
, b
)) != 0)
939 while (sshbuf_len(padded
) % blocksize
) {
940 if ((r
= sshbuf_put_u8(padded
, ++i
& 0xff)) != 0)
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)
948 aadlen
= sshbuf_len(encoded
);
950 /* concat header and state */
951 if ((r
= sshbuf_putb(encoded
, padded
)) != 0)
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)
972 sshbuf_free(encoded
);
973 sshbuf_free(encrypted
);
974 cipher_free(ciphercontext
);
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
;
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
;
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
;
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
;
1020 if (sshbuf_len(encoded
) < sizeof(XMSS_MAGIC
) ||
1021 memcmp(sshbuf_ptr(encoded
), XMSS_MAGIC
, sizeof(XMSS_MAGIC
))) {
1022 r
= SSH_ERR_INVALID_FORMAT
;
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)
1031 /* check size of encrypted key blob */
1032 if (encrypted_len
< blocksize
|| (encrypted_len
% blocksize
) != 0) {
1033 r
= SSH_ERR_INVALID_FORMAT
;
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
;
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)
1057 /* there should be no trailing data */
1058 if ((r
= sshbuf_consume(encoded
, encrypted_len
+ authlen
)) != 0)
1060 if (sshbuf_len(encoded
) != 0) {
1061 r
= SSH_ERR_INVALID_FORMAT
;
1066 if ((r
= sshbuf_consume(decrypted
, aadlen
)) != 0)
1068 /* XXX encrypted includes unchecked padding */
1077 cipher_free(ciphercontext
);
1079 sshbuf_free(decrypted
);
1085 sshkey_xmss_signatures_left(const struct sshkey
*k
)
1087 struct ssh_xmss_state
*state
= k
->xmss_state
;
1090 if (sshkey_type_plain(k
->type
) == KEY_XMSS
&& state
&&
1092 idx
= k
->xmss_sk
? PEEK_U32(k
->xmss_sk
) : state
->idx
;
1093 if (idx
< state
->maxidx
)
1094 return state
->maxidx
- idx
;
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
;
1108 if (state
->idx
+ maxsign
< state
->idx
)
1109 return SSH_ERR_INVALID_ARGUMENT
;
1110 state
->maxidx
= state
->idx
+ maxsign
;
1113 #endif /* WITH_XMSS */