xref: /freebsd-src/crypto/openssh/sshkey-xmss.c (revision f374ba41f55c1a127303d92d830dd58eef2f5243)
1*f374ba41SEd Maste /* $OpenBSD: sshkey-xmss.c,v 1.12 2022/10/28 00:39:29 djm Exp $ */
247dd1d1bSDag-Erling Smørgrav /*
347dd1d1bSDag-Erling Smørgrav  * Copyright (c) 2017 Markus Friedl.  All rights reserved.
447dd1d1bSDag-Erling Smørgrav  *
547dd1d1bSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
647dd1d1bSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
747dd1d1bSDag-Erling Smørgrav  * are met:
847dd1d1bSDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
947dd1d1bSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer.
1047dd1d1bSDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
1147dd1d1bSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
1247dd1d1bSDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
1347dd1d1bSDag-Erling Smørgrav  *
1447dd1d1bSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1547dd1d1bSDag-Erling Smørgrav  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1647dd1d1bSDag-Erling Smørgrav  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1747dd1d1bSDag-Erling Smørgrav  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1847dd1d1bSDag-Erling Smørgrav  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1947dd1d1bSDag-Erling Smørgrav  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2047dd1d1bSDag-Erling Smørgrav  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2147dd1d1bSDag-Erling Smørgrav  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2247dd1d1bSDag-Erling Smørgrav  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2347dd1d1bSDag-Erling Smørgrav  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2447dd1d1bSDag-Erling Smørgrav  */
2547dd1d1bSDag-Erling Smørgrav 
2647dd1d1bSDag-Erling Smørgrav #include "includes.h"
2747dd1d1bSDag-Erling Smørgrav #ifdef WITH_XMSS
2847dd1d1bSDag-Erling Smørgrav 
2947dd1d1bSDag-Erling Smørgrav #include <sys/types.h>
3047dd1d1bSDag-Erling Smørgrav #include <sys/uio.h>
3147dd1d1bSDag-Erling Smørgrav 
3247dd1d1bSDag-Erling Smørgrav #include <stdio.h>
3347dd1d1bSDag-Erling Smørgrav #include <string.h>
3447dd1d1bSDag-Erling Smørgrav #include <unistd.h>
3547dd1d1bSDag-Erling Smørgrav #include <fcntl.h>
3647dd1d1bSDag-Erling Smørgrav #include <errno.h>
3747dd1d1bSDag-Erling Smørgrav #ifdef HAVE_SYS_FILE_H
3847dd1d1bSDag-Erling Smørgrav # include <sys/file.h>
3947dd1d1bSDag-Erling Smørgrav #endif
4047dd1d1bSDag-Erling Smørgrav 
4147dd1d1bSDag-Erling Smørgrav #include "ssh2.h"
4247dd1d1bSDag-Erling Smørgrav #include "ssherr.h"
4347dd1d1bSDag-Erling Smørgrav #include "sshbuf.h"
4447dd1d1bSDag-Erling Smørgrav #include "cipher.h"
4547dd1d1bSDag-Erling Smørgrav #include "sshkey.h"
4647dd1d1bSDag-Erling Smørgrav #include "sshkey-xmss.h"
4747dd1d1bSDag-Erling Smørgrav #include "atomicio.h"
4819261079SEd Maste #include "log.h"
4947dd1d1bSDag-Erling Smørgrav 
5047dd1d1bSDag-Erling Smørgrav #include "xmss_fast.h"
5147dd1d1bSDag-Erling Smørgrav 
5247dd1d1bSDag-Erling Smørgrav /* opaque internal XMSS state */
5347dd1d1bSDag-Erling Smørgrav #define XMSS_MAGIC		"xmss-state-v1"
5447dd1d1bSDag-Erling Smørgrav #define XMSS_CIPHERNAME		"aes256-gcm@openssh.com"
5547dd1d1bSDag-Erling Smørgrav struct ssh_xmss_state {
5647dd1d1bSDag-Erling Smørgrav 	xmss_params	params;
5747dd1d1bSDag-Erling Smørgrav 	u_int32_t	n, w, h, k;
5847dd1d1bSDag-Erling Smørgrav 
5947dd1d1bSDag-Erling Smørgrav 	bds_state	bds;
6047dd1d1bSDag-Erling Smørgrav 	u_char		*stack;
6147dd1d1bSDag-Erling Smørgrav 	u_int32_t	stackoffset;
6247dd1d1bSDag-Erling Smørgrav 	u_char		*stacklevels;
6347dd1d1bSDag-Erling Smørgrav 	u_char		*auth;
6447dd1d1bSDag-Erling Smørgrav 	u_char		*keep;
6547dd1d1bSDag-Erling Smørgrav 	u_char		*th_nodes;
6647dd1d1bSDag-Erling Smørgrav 	u_char		*retain;
6747dd1d1bSDag-Erling Smørgrav 	treehash_inst	*treehash;
6847dd1d1bSDag-Erling Smørgrav 
6947dd1d1bSDag-Erling Smørgrav 	u_int32_t	idx;		/* state read from file */
70190cef3dSDag-Erling Smørgrav 	u_int32_t	maxidx;		/* restricted # of signatures */
7147dd1d1bSDag-Erling Smørgrav 	int		have_state;	/* .state file exists */
7247dd1d1bSDag-Erling Smørgrav 	int		lockfd;		/* locked in sshkey_xmss_get_state() */
7319261079SEd Maste 	u_char		allow_update;	/* allow sshkey_xmss_update_state() */
7447dd1d1bSDag-Erling Smørgrav 	char		*enc_ciphername;/* encrypt state with cipher */
7547dd1d1bSDag-Erling Smørgrav 	u_char		*enc_keyiv;	/* encrypt state with key */
7647dd1d1bSDag-Erling Smørgrav 	u_int32_t	enc_keyiv_len;	/* length of enc_keyiv */
7747dd1d1bSDag-Erling Smørgrav };
7847dd1d1bSDag-Erling Smørgrav 
7947dd1d1bSDag-Erling Smørgrav int	 sshkey_xmss_init_bds_state(struct sshkey *);
8047dd1d1bSDag-Erling Smørgrav int	 sshkey_xmss_init_enc_key(struct sshkey *, const char *);
8147dd1d1bSDag-Erling Smørgrav void	 sshkey_xmss_free_bds(struct sshkey *);
8247dd1d1bSDag-Erling Smørgrav int	 sshkey_xmss_get_state_from_file(struct sshkey *, const char *,
8319261079SEd Maste 	    int *, int);
8447dd1d1bSDag-Erling Smørgrav int	 sshkey_xmss_encrypt_state(const struct sshkey *, struct sshbuf *,
8547dd1d1bSDag-Erling Smørgrav 	    struct sshbuf **);
8647dd1d1bSDag-Erling Smørgrav int	 sshkey_xmss_decrypt_state(const struct sshkey *, struct sshbuf *,
8747dd1d1bSDag-Erling Smørgrav 	    struct sshbuf **);
8847dd1d1bSDag-Erling Smørgrav int	 sshkey_xmss_serialize_enc_key(const struct sshkey *, struct sshbuf *);
8947dd1d1bSDag-Erling Smørgrav int	 sshkey_xmss_deserialize_enc_key(struct sshkey *, struct sshbuf *);
9047dd1d1bSDag-Erling Smørgrav 
9119261079SEd Maste #define PRINT(...) do { if (printerror) sshlog(__FILE__, __func__, __LINE__, \
9219261079SEd Maste     0, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__); } while (0)
9347dd1d1bSDag-Erling Smørgrav 
9447dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_init(struct sshkey * key,const char * name)9547dd1d1bSDag-Erling Smørgrav sshkey_xmss_init(struct sshkey *key, const char *name)
9647dd1d1bSDag-Erling Smørgrav {
9747dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state;
9847dd1d1bSDag-Erling Smørgrav 
9947dd1d1bSDag-Erling Smørgrav 	if (key->xmss_state != NULL)
10047dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_FORMAT;
10147dd1d1bSDag-Erling Smørgrav 	if (name == NULL)
10247dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_FORMAT;
10347dd1d1bSDag-Erling Smørgrav 	state = calloc(sizeof(struct ssh_xmss_state), 1);
10447dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
10547dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
10647dd1d1bSDag-Erling Smørgrav 	if (strcmp(name, XMSS_SHA2_256_W16_H10_NAME) == 0) {
10747dd1d1bSDag-Erling Smørgrav 		state->n = 32;
10847dd1d1bSDag-Erling Smørgrav 		state->w = 16;
10947dd1d1bSDag-Erling Smørgrav 		state->h = 10;
11047dd1d1bSDag-Erling Smørgrav 	} else if (strcmp(name, XMSS_SHA2_256_W16_H16_NAME) == 0) {
11147dd1d1bSDag-Erling Smørgrav 		state->n = 32;
11247dd1d1bSDag-Erling Smørgrav 		state->w = 16;
11347dd1d1bSDag-Erling Smørgrav 		state->h = 16;
11447dd1d1bSDag-Erling Smørgrav 	} else if (strcmp(name, XMSS_SHA2_256_W16_H20_NAME) == 0) {
11547dd1d1bSDag-Erling Smørgrav 		state->n = 32;
11647dd1d1bSDag-Erling Smørgrav 		state->w = 16;
11747dd1d1bSDag-Erling Smørgrav 		state->h = 20;
11847dd1d1bSDag-Erling Smørgrav 	} else {
11947dd1d1bSDag-Erling Smørgrav 		free(state);
12047dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_KEY_TYPE_UNKNOWN;
12147dd1d1bSDag-Erling Smørgrav 	}
12247dd1d1bSDag-Erling Smørgrav 	if ((key->xmss_name = strdup(name)) == NULL) {
12347dd1d1bSDag-Erling Smørgrav 		free(state);
12447dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
12547dd1d1bSDag-Erling Smørgrav 	}
12647dd1d1bSDag-Erling Smørgrav 	state->k = 2;	/* XXX hardcoded */
12747dd1d1bSDag-Erling Smørgrav 	state->lockfd = -1;
12847dd1d1bSDag-Erling Smørgrav 	if (xmss_set_params(&state->params, state->n, state->h, state->w,
12947dd1d1bSDag-Erling Smørgrav 	    state->k) != 0) {
13047dd1d1bSDag-Erling Smørgrav 		free(state);
13147dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_FORMAT;
13247dd1d1bSDag-Erling Smørgrav 	}
13347dd1d1bSDag-Erling Smørgrav 	key->xmss_state = state;
13447dd1d1bSDag-Erling Smørgrav 	return 0;
13547dd1d1bSDag-Erling Smørgrav }
13647dd1d1bSDag-Erling Smørgrav 
13747dd1d1bSDag-Erling Smørgrav void
sshkey_xmss_free_state(struct sshkey * key)13847dd1d1bSDag-Erling Smørgrav sshkey_xmss_free_state(struct sshkey *key)
13947dd1d1bSDag-Erling Smørgrav {
14047dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = key->xmss_state;
14147dd1d1bSDag-Erling Smørgrav 
14247dd1d1bSDag-Erling Smørgrav 	sshkey_xmss_free_bds(key);
14347dd1d1bSDag-Erling Smørgrav 	if (state) {
14447dd1d1bSDag-Erling Smørgrav 		if (state->enc_keyiv) {
14547dd1d1bSDag-Erling Smørgrav 			explicit_bzero(state->enc_keyiv, state->enc_keyiv_len);
14647dd1d1bSDag-Erling Smørgrav 			free(state->enc_keyiv);
14747dd1d1bSDag-Erling Smørgrav 		}
14847dd1d1bSDag-Erling Smørgrav 		free(state->enc_ciphername);
14947dd1d1bSDag-Erling Smørgrav 		free(state);
15047dd1d1bSDag-Erling Smørgrav 	}
15147dd1d1bSDag-Erling Smørgrav 	key->xmss_state = NULL;
15247dd1d1bSDag-Erling Smørgrav }
15347dd1d1bSDag-Erling Smørgrav 
15447dd1d1bSDag-Erling Smørgrav #define SSH_XMSS_K2_MAGIC	"k=2"
15547dd1d1bSDag-Erling Smørgrav #define num_stack(x)		((x->h+1)*(x->n))
15647dd1d1bSDag-Erling Smørgrav #define num_stacklevels(x)	(x->h+1)
15747dd1d1bSDag-Erling Smørgrav #define num_auth(x)		((x->h)*(x->n))
15847dd1d1bSDag-Erling Smørgrav #define num_keep(x)		((x->h >> 1)*(x->n))
15947dd1d1bSDag-Erling Smørgrav #define num_th_nodes(x)		((x->h - x->k)*(x->n))
16047dd1d1bSDag-Erling Smørgrav #define num_retain(x)		(((1ULL << x->k) - x->k - 1) * (x->n))
16147dd1d1bSDag-Erling Smørgrav #define num_treehash(x)		((x->h) - (x->k))
16247dd1d1bSDag-Erling Smørgrav 
16347dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_init_bds_state(struct sshkey * key)16447dd1d1bSDag-Erling Smørgrav sshkey_xmss_init_bds_state(struct sshkey *key)
16547dd1d1bSDag-Erling Smørgrav {
16647dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = key->xmss_state;
16747dd1d1bSDag-Erling Smørgrav 	u_int32_t i;
16847dd1d1bSDag-Erling Smørgrav 
16947dd1d1bSDag-Erling Smørgrav 	state->stackoffset = 0;
17047dd1d1bSDag-Erling Smørgrav 	if ((state->stack = calloc(num_stack(state), 1)) == NULL ||
17147dd1d1bSDag-Erling Smørgrav 	    (state->stacklevels = calloc(num_stacklevels(state), 1))== NULL ||
17247dd1d1bSDag-Erling Smørgrav 	    (state->auth = calloc(num_auth(state), 1)) == NULL ||
17347dd1d1bSDag-Erling Smørgrav 	    (state->keep = calloc(num_keep(state), 1)) == NULL ||
17447dd1d1bSDag-Erling Smørgrav 	    (state->th_nodes = calloc(num_th_nodes(state), 1)) == NULL ||
17547dd1d1bSDag-Erling Smørgrav 	    (state->retain = calloc(num_retain(state), 1)) == NULL ||
17647dd1d1bSDag-Erling Smørgrav 	    (state->treehash = calloc(num_treehash(state),
17747dd1d1bSDag-Erling Smørgrav 	    sizeof(treehash_inst))) == NULL) {
17847dd1d1bSDag-Erling Smørgrav 		sshkey_xmss_free_bds(key);
17947dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
18047dd1d1bSDag-Erling Smørgrav 	}
18147dd1d1bSDag-Erling Smørgrav 	for (i = 0; i < state->h - state->k; i++)
18247dd1d1bSDag-Erling Smørgrav 		state->treehash[i].node = &state->th_nodes[state->n*i];
18347dd1d1bSDag-Erling Smørgrav 	xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
18447dd1d1bSDag-Erling Smørgrav 	    state->stacklevels, state->auth, state->keep, state->treehash,
18547dd1d1bSDag-Erling Smørgrav 	    state->retain, 0);
18647dd1d1bSDag-Erling Smørgrav 	return 0;
18747dd1d1bSDag-Erling Smørgrav }
18847dd1d1bSDag-Erling Smørgrav 
18947dd1d1bSDag-Erling Smørgrav void
sshkey_xmss_free_bds(struct sshkey * key)19047dd1d1bSDag-Erling Smørgrav sshkey_xmss_free_bds(struct sshkey *key)
19147dd1d1bSDag-Erling Smørgrav {
19247dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = key->xmss_state;
19347dd1d1bSDag-Erling Smørgrav 
19447dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
19547dd1d1bSDag-Erling Smørgrav 		return;
19647dd1d1bSDag-Erling Smørgrav 	free(state->stack);
19747dd1d1bSDag-Erling Smørgrav 	free(state->stacklevels);
19847dd1d1bSDag-Erling Smørgrav 	free(state->auth);
19947dd1d1bSDag-Erling Smørgrav 	free(state->keep);
20047dd1d1bSDag-Erling Smørgrav 	free(state->th_nodes);
20147dd1d1bSDag-Erling Smørgrav 	free(state->retain);
20247dd1d1bSDag-Erling Smørgrav 	free(state->treehash);
20347dd1d1bSDag-Erling Smørgrav 	state->stack = NULL;
20447dd1d1bSDag-Erling Smørgrav 	state->stacklevels = NULL;
20547dd1d1bSDag-Erling Smørgrav 	state->auth = NULL;
20647dd1d1bSDag-Erling Smørgrav 	state->keep = NULL;
20747dd1d1bSDag-Erling Smørgrav 	state->th_nodes = NULL;
20847dd1d1bSDag-Erling Smørgrav 	state->retain = NULL;
20947dd1d1bSDag-Erling Smørgrav 	state->treehash = NULL;
21047dd1d1bSDag-Erling Smørgrav }
21147dd1d1bSDag-Erling Smørgrav 
21247dd1d1bSDag-Erling Smørgrav void *
sshkey_xmss_params(const struct sshkey * key)21347dd1d1bSDag-Erling Smørgrav sshkey_xmss_params(const struct sshkey *key)
21447dd1d1bSDag-Erling Smørgrav {
21547dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = key->xmss_state;
21647dd1d1bSDag-Erling Smørgrav 
21747dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
21847dd1d1bSDag-Erling Smørgrav 		return NULL;
21947dd1d1bSDag-Erling Smørgrav 	return &state->params;
22047dd1d1bSDag-Erling Smørgrav }
22147dd1d1bSDag-Erling Smørgrav 
22247dd1d1bSDag-Erling Smørgrav void *
sshkey_xmss_bds_state(const struct sshkey * key)22347dd1d1bSDag-Erling Smørgrav sshkey_xmss_bds_state(const struct sshkey *key)
22447dd1d1bSDag-Erling Smørgrav {
22547dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = key->xmss_state;
22647dd1d1bSDag-Erling Smørgrav 
22747dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
22847dd1d1bSDag-Erling Smørgrav 		return NULL;
22947dd1d1bSDag-Erling Smørgrav 	return &state->bds;
23047dd1d1bSDag-Erling Smørgrav }
23147dd1d1bSDag-Erling Smørgrav 
23247dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_siglen(const struct sshkey * key,size_t * lenp)23347dd1d1bSDag-Erling Smørgrav sshkey_xmss_siglen(const struct sshkey *key, size_t *lenp)
23447dd1d1bSDag-Erling Smørgrav {
23547dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = key->xmss_state;
23647dd1d1bSDag-Erling Smørgrav 
23747dd1d1bSDag-Erling Smørgrav 	if (lenp == NULL)
23847dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
23947dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
24047dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_FORMAT;
24147dd1d1bSDag-Erling Smørgrav 	*lenp = 4 + state->n +
24247dd1d1bSDag-Erling Smørgrav 	    state->params.wots_par.keysize +
24347dd1d1bSDag-Erling Smørgrav 	    state->h * state->n;
24447dd1d1bSDag-Erling Smørgrav 	return 0;
24547dd1d1bSDag-Erling Smørgrav }
24647dd1d1bSDag-Erling Smørgrav 
24747dd1d1bSDag-Erling Smørgrav size_t
sshkey_xmss_pklen(const struct sshkey * key)24847dd1d1bSDag-Erling Smørgrav sshkey_xmss_pklen(const struct sshkey *key)
24947dd1d1bSDag-Erling Smørgrav {
25047dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = key->xmss_state;
25147dd1d1bSDag-Erling Smørgrav 
25247dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
25347dd1d1bSDag-Erling Smørgrav 		return 0;
25447dd1d1bSDag-Erling Smørgrav 	return state->n * 2;
25547dd1d1bSDag-Erling Smørgrav }
25647dd1d1bSDag-Erling Smørgrav 
25747dd1d1bSDag-Erling Smørgrav size_t
sshkey_xmss_sklen(const struct sshkey * key)25847dd1d1bSDag-Erling Smørgrav sshkey_xmss_sklen(const struct sshkey *key)
25947dd1d1bSDag-Erling Smørgrav {
26047dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = key->xmss_state;
26147dd1d1bSDag-Erling Smørgrav 
26247dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
26347dd1d1bSDag-Erling Smørgrav 		return 0;
26447dd1d1bSDag-Erling Smørgrav 	return state->n * 4 + 4;
26547dd1d1bSDag-Erling Smørgrav }
26647dd1d1bSDag-Erling Smørgrav 
26747dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_init_enc_key(struct sshkey * k,const char * ciphername)26847dd1d1bSDag-Erling Smørgrav sshkey_xmss_init_enc_key(struct sshkey *k, const char *ciphername)
26947dd1d1bSDag-Erling Smørgrav {
27047dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
27147dd1d1bSDag-Erling Smørgrav 	const struct sshcipher *cipher;
27247dd1d1bSDag-Erling Smørgrav 	size_t keylen = 0, ivlen = 0;
27347dd1d1bSDag-Erling Smørgrav 
27447dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
27547dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
27647dd1d1bSDag-Erling Smørgrav 	if ((cipher = cipher_by_name(ciphername)) == NULL)
27747dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
27847dd1d1bSDag-Erling Smørgrav 	if ((state->enc_ciphername = strdup(ciphername)) == NULL)
27947dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
28047dd1d1bSDag-Erling Smørgrav 	keylen = cipher_keylen(cipher);
28147dd1d1bSDag-Erling Smørgrav 	ivlen = cipher_ivlen(cipher);
28247dd1d1bSDag-Erling Smørgrav 	state->enc_keyiv_len = keylen + ivlen;
28347dd1d1bSDag-Erling Smørgrav 	if ((state->enc_keyiv = calloc(state->enc_keyiv_len, 1)) == NULL) {
28447dd1d1bSDag-Erling Smørgrav 		free(state->enc_ciphername);
28547dd1d1bSDag-Erling Smørgrav 		state->enc_ciphername = NULL;
28647dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
28747dd1d1bSDag-Erling Smørgrav 	}
28847dd1d1bSDag-Erling Smørgrav 	arc4random_buf(state->enc_keyiv, state->enc_keyiv_len);
28947dd1d1bSDag-Erling Smørgrav 	return 0;
29047dd1d1bSDag-Erling Smørgrav }
29147dd1d1bSDag-Erling Smørgrav 
29247dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_serialize_enc_key(const struct sshkey * k,struct sshbuf * b)29347dd1d1bSDag-Erling Smørgrav sshkey_xmss_serialize_enc_key(const struct sshkey *k, struct sshbuf *b)
29447dd1d1bSDag-Erling Smørgrav {
29547dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
29647dd1d1bSDag-Erling Smørgrav 	int r;
29747dd1d1bSDag-Erling Smørgrav 
29847dd1d1bSDag-Erling Smørgrav 	if (state == NULL || state->enc_keyiv == NULL ||
29947dd1d1bSDag-Erling Smørgrav 	    state->enc_ciphername == NULL)
30047dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
30147dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_put_cstring(b, state->enc_ciphername)) != 0 ||
30247dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_string(b, state->enc_keyiv,
30347dd1d1bSDag-Erling Smørgrav 	    state->enc_keyiv_len)) != 0)
30447dd1d1bSDag-Erling Smørgrav 		return r;
30547dd1d1bSDag-Erling Smørgrav 	return 0;
30647dd1d1bSDag-Erling Smørgrav }
30747dd1d1bSDag-Erling Smørgrav 
30847dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_deserialize_enc_key(struct sshkey * k,struct sshbuf * b)30947dd1d1bSDag-Erling Smørgrav sshkey_xmss_deserialize_enc_key(struct sshkey *k, struct sshbuf *b)
31047dd1d1bSDag-Erling Smørgrav {
31147dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
31247dd1d1bSDag-Erling Smørgrav 	size_t len;
31347dd1d1bSDag-Erling Smørgrav 	int r;
31447dd1d1bSDag-Erling Smørgrav 
31547dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
31647dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
31747dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_get_cstring(b, &state->enc_ciphername, NULL)) != 0 ||
31847dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_string(b, &state->enc_keyiv, &len)) != 0)
31947dd1d1bSDag-Erling Smørgrav 		return r;
32047dd1d1bSDag-Erling Smørgrav 	state->enc_keyiv_len = len;
32147dd1d1bSDag-Erling Smørgrav 	return 0;
32247dd1d1bSDag-Erling Smørgrav }
32347dd1d1bSDag-Erling Smørgrav 
32447dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_serialize_pk_info(const struct sshkey * k,struct sshbuf * b,enum sshkey_serialize_rep opts)32547dd1d1bSDag-Erling Smørgrav sshkey_xmss_serialize_pk_info(const struct sshkey *k, struct sshbuf *b,
32647dd1d1bSDag-Erling Smørgrav     enum sshkey_serialize_rep opts)
32747dd1d1bSDag-Erling Smørgrav {
32847dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
32947dd1d1bSDag-Erling Smørgrav 	u_char have_info = 1;
33047dd1d1bSDag-Erling Smørgrav 	u_int32_t idx;
33147dd1d1bSDag-Erling Smørgrav 	int r;
33247dd1d1bSDag-Erling Smørgrav 
33347dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
33447dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
33547dd1d1bSDag-Erling Smørgrav 	if (opts != SSHKEY_SERIALIZE_INFO)
33647dd1d1bSDag-Erling Smørgrav 		return 0;
33747dd1d1bSDag-Erling Smørgrav 	idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
33847dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(b, have_info)) != 0 ||
33947dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, idx)) != 0 ||
34047dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, state->maxidx)) != 0)
34147dd1d1bSDag-Erling Smørgrav 		return r;
34247dd1d1bSDag-Erling Smørgrav 	return 0;
34347dd1d1bSDag-Erling Smørgrav }
34447dd1d1bSDag-Erling Smørgrav 
34547dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_deserialize_pk_info(struct sshkey * k,struct sshbuf * b)34647dd1d1bSDag-Erling Smørgrav sshkey_xmss_deserialize_pk_info(struct sshkey *k, struct sshbuf *b)
34747dd1d1bSDag-Erling Smørgrav {
34847dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
34947dd1d1bSDag-Erling Smørgrav 	u_char have_info;
35047dd1d1bSDag-Erling Smørgrav 	int r;
35147dd1d1bSDag-Erling Smørgrav 
35247dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
35347dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
35447dd1d1bSDag-Erling Smørgrav 	/* optional */
35547dd1d1bSDag-Erling Smørgrav 	if (sshbuf_len(b) == 0)
35647dd1d1bSDag-Erling Smørgrav 		return 0;
35747dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_get_u8(b, &have_info)) != 0)
35847dd1d1bSDag-Erling Smørgrav 		return r;
35947dd1d1bSDag-Erling Smørgrav 	if (have_info != 1)
36047dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
36147dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_get_u32(b, &state->idx)) != 0 ||
36247dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(b, &state->maxidx)) != 0)
36347dd1d1bSDag-Erling Smørgrav 		return r;
36447dd1d1bSDag-Erling Smørgrav 	return 0;
36547dd1d1bSDag-Erling Smørgrav }
36647dd1d1bSDag-Erling Smørgrav 
36747dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_generate_private_key(struct sshkey * k,int bits)368*f374ba41SEd Maste sshkey_xmss_generate_private_key(struct sshkey *k, int bits)
36947dd1d1bSDag-Erling Smørgrav {
37047dd1d1bSDag-Erling Smørgrav 	int r;
37147dd1d1bSDag-Erling Smørgrav 	const char *name;
37247dd1d1bSDag-Erling Smørgrav 
37347dd1d1bSDag-Erling Smørgrav 	if (bits == 10) {
37447dd1d1bSDag-Erling Smørgrav 		name = XMSS_SHA2_256_W16_H10_NAME;
37547dd1d1bSDag-Erling Smørgrav 	} else if (bits == 16) {
37647dd1d1bSDag-Erling Smørgrav 		name = XMSS_SHA2_256_W16_H16_NAME;
37747dd1d1bSDag-Erling Smørgrav 	} else if (bits == 20) {
37847dd1d1bSDag-Erling Smørgrav 		name = XMSS_SHA2_256_W16_H20_NAME;
37947dd1d1bSDag-Erling Smørgrav 	} else {
38047dd1d1bSDag-Erling Smørgrav 		name = XMSS_DEFAULT_NAME;
38147dd1d1bSDag-Erling Smørgrav 	}
38247dd1d1bSDag-Erling Smørgrav 	if ((r = sshkey_xmss_init(k, name)) != 0 ||
38347dd1d1bSDag-Erling Smørgrav 	    (r = sshkey_xmss_init_bds_state(k)) != 0 ||
38447dd1d1bSDag-Erling Smørgrav 	    (r = sshkey_xmss_init_enc_key(k, XMSS_CIPHERNAME)) != 0)
38547dd1d1bSDag-Erling Smørgrav 		return r;
38647dd1d1bSDag-Erling Smørgrav 	if ((k->xmss_pk = malloc(sshkey_xmss_pklen(k))) == NULL ||
38747dd1d1bSDag-Erling Smørgrav 	    (k->xmss_sk = malloc(sshkey_xmss_sklen(k))) == NULL) {
38847dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
38947dd1d1bSDag-Erling Smørgrav 	}
39047dd1d1bSDag-Erling Smørgrav 	xmss_keypair(k->xmss_pk, k->xmss_sk, sshkey_xmss_bds_state(k),
39147dd1d1bSDag-Erling Smørgrav 	    sshkey_xmss_params(k));
39247dd1d1bSDag-Erling Smørgrav 	return 0;
39347dd1d1bSDag-Erling Smørgrav }
39447dd1d1bSDag-Erling Smørgrav 
39547dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_get_state_from_file(struct sshkey * k,const char * filename,int * have_file,int printerror)39647dd1d1bSDag-Erling Smørgrav sshkey_xmss_get_state_from_file(struct sshkey *k, const char *filename,
39719261079SEd Maste     int *have_file, int printerror)
39847dd1d1bSDag-Erling Smørgrav {
39947dd1d1bSDag-Erling Smørgrav 	struct sshbuf *b = NULL, *enc = NULL;
40047dd1d1bSDag-Erling Smørgrav 	int ret = SSH_ERR_SYSTEM_ERROR, r, fd = -1;
40147dd1d1bSDag-Erling Smørgrav 	u_int32_t len;
40247dd1d1bSDag-Erling Smørgrav 	unsigned char buf[4], *data = NULL;
40347dd1d1bSDag-Erling Smørgrav 
40447dd1d1bSDag-Erling Smørgrav 	*have_file = 0;
40547dd1d1bSDag-Erling Smørgrav 	if ((fd = open(filename, O_RDONLY)) >= 0) {
40647dd1d1bSDag-Erling Smørgrav 		*have_file = 1;
40747dd1d1bSDag-Erling Smørgrav 		if (atomicio(read, fd, buf, sizeof(buf)) != sizeof(buf)) {
40819261079SEd Maste 			PRINT("corrupt state file: %s", filename);
40947dd1d1bSDag-Erling Smørgrav 			goto done;
41047dd1d1bSDag-Erling Smørgrav 		}
41147dd1d1bSDag-Erling Smørgrav 		len = PEEK_U32(buf);
41247dd1d1bSDag-Erling Smørgrav 		if ((data = calloc(len, 1)) == NULL) {
41347dd1d1bSDag-Erling Smørgrav 			ret = SSH_ERR_ALLOC_FAIL;
41447dd1d1bSDag-Erling Smørgrav 			goto done;
41547dd1d1bSDag-Erling Smørgrav 		}
41647dd1d1bSDag-Erling Smørgrav 		if (atomicio(read, fd, data, len) != len) {
41719261079SEd Maste 			PRINT("cannot read blob: %s", filename);
41847dd1d1bSDag-Erling Smørgrav 			goto done;
41947dd1d1bSDag-Erling Smørgrav 		}
42047dd1d1bSDag-Erling Smørgrav 		if ((enc = sshbuf_from(data, len)) == NULL) {
42147dd1d1bSDag-Erling Smørgrav 			ret = SSH_ERR_ALLOC_FAIL;
42247dd1d1bSDag-Erling Smørgrav 			goto done;
42347dd1d1bSDag-Erling Smørgrav 		}
42447dd1d1bSDag-Erling Smørgrav 		sshkey_xmss_free_bds(k);
42547dd1d1bSDag-Erling Smørgrav 		if ((r = sshkey_xmss_decrypt_state(k, enc, &b)) != 0) {
42647dd1d1bSDag-Erling Smørgrav 			ret = r;
42747dd1d1bSDag-Erling Smørgrav 			goto done;
42847dd1d1bSDag-Erling Smørgrav 		}
42947dd1d1bSDag-Erling Smørgrav 		if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) {
43047dd1d1bSDag-Erling Smørgrav 			ret = r;
43147dd1d1bSDag-Erling Smørgrav 			goto done;
43247dd1d1bSDag-Erling Smørgrav 		}
43347dd1d1bSDag-Erling Smørgrav 		ret = 0;
43447dd1d1bSDag-Erling Smørgrav 	}
43547dd1d1bSDag-Erling Smørgrav done:
43647dd1d1bSDag-Erling Smørgrav 	if (fd != -1)
43747dd1d1bSDag-Erling Smørgrav 		close(fd);
43847dd1d1bSDag-Erling Smørgrav 	free(data);
43947dd1d1bSDag-Erling Smørgrav 	sshbuf_free(enc);
44047dd1d1bSDag-Erling Smørgrav 	sshbuf_free(b);
44147dd1d1bSDag-Erling Smørgrav 	return ret;
44247dd1d1bSDag-Erling Smørgrav }
44347dd1d1bSDag-Erling Smørgrav 
44447dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_get_state(const struct sshkey * k,int printerror)44519261079SEd Maste sshkey_xmss_get_state(const struct sshkey *k, int printerror)
44647dd1d1bSDag-Erling Smørgrav {
44747dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
44847dd1d1bSDag-Erling Smørgrav 	u_int32_t idx = 0;
44947dd1d1bSDag-Erling Smørgrav 	char *filename = NULL;
45047dd1d1bSDag-Erling Smørgrav 	char *statefile = NULL, *ostatefile = NULL, *lockfile = NULL;
45147dd1d1bSDag-Erling Smørgrav 	int lockfd = -1, have_state = 0, have_ostate, tries = 0;
45247dd1d1bSDag-Erling Smørgrav 	int ret = SSH_ERR_INVALID_ARGUMENT, r;
45347dd1d1bSDag-Erling Smørgrav 
45447dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
45547dd1d1bSDag-Erling Smørgrav 		goto done;
45647dd1d1bSDag-Erling Smørgrav 	/*
45747dd1d1bSDag-Erling Smørgrav 	 * If maxidx is set, then we are allowed a limited number
45847dd1d1bSDag-Erling Smørgrav 	 * of signatures, but don't need to access the disk.
45947dd1d1bSDag-Erling Smørgrav 	 * Otherwise we need to deal with the on-disk state.
46047dd1d1bSDag-Erling Smørgrav 	 */
46147dd1d1bSDag-Erling Smørgrav 	if (state->maxidx) {
46247dd1d1bSDag-Erling Smørgrav 		/* xmss_sk always contains the current state */
46347dd1d1bSDag-Erling Smørgrav 		idx = PEEK_U32(k->xmss_sk);
46447dd1d1bSDag-Erling Smørgrav 		if (idx < state->maxidx) {
46547dd1d1bSDag-Erling Smørgrav 			state->allow_update = 1;
46647dd1d1bSDag-Erling Smørgrav 			return 0;
46747dd1d1bSDag-Erling Smørgrav 		}
46847dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
46947dd1d1bSDag-Erling Smørgrav 	}
47047dd1d1bSDag-Erling Smørgrav 	if ((filename = k->xmss_filename) == NULL)
47147dd1d1bSDag-Erling Smørgrav 		goto done;
47219261079SEd Maste 	if (asprintf(&lockfile, "%s.lock", filename) == -1 ||
47319261079SEd Maste 	    asprintf(&statefile, "%s.state", filename) == -1 ||
47419261079SEd Maste 	    asprintf(&ostatefile, "%s.ostate", filename) == -1) {
47547dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_ALLOC_FAIL;
47647dd1d1bSDag-Erling Smørgrav 		goto done;
47747dd1d1bSDag-Erling Smørgrav 	}
47819261079SEd Maste 	if ((lockfd = open(lockfile, O_CREAT|O_RDONLY, 0600)) == -1) {
47947dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_SYSTEM_ERROR;
48019261079SEd Maste 		PRINT("cannot open/create: %s", lockfile);
48147dd1d1bSDag-Erling Smørgrav 		goto done;
48247dd1d1bSDag-Erling Smørgrav 	}
48319261079SEd Maste 	while (flock(lockfd, LOCK_EX|LOCK_NB) == -1) {
48447dd1d1bSDag-Erling Smørgrav 		if (errno != EWOULDBLOCK) {
48547dd1d1bSDag-Erling Smørgrav 			ret = SSH_ERR_SYSTEM_ERROR;
48619261079SEd Maste 			PRINT("cannot lock: %s", lockfile);
48747dd1d1bSDag-Erling Smørgrav 			goto done;
48847dd1d1bSDag-Erling Smørgrav 		}
48947dd1d1bSDag-Erling Smørgrav 		if (++tries > 10) {
49047dd1d1bSDag-Erling Smørgrav 			ret = SSH_ERR_SYSTEM_ERROR;
49119261079SEd Maste 			PRINT("giving up on: %s", lockfile);
49247dd1d1bSDag-Erling Smørgrav 			goto done;
49347dd1d1bSDag-Erling Smørgrav 		}
49447dd1d1bSDag-Erling Smørgrav 		usleep(1000*100*tries);
49547dd1d1bSDag-Erling Smørgrav 	}
49647dd1d1bSDag-Erling Smørgrav 	/* XXX no longer const */
49747dd1d1bSDag-Erling Smørgrav 	if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
49819261079SEd Maste 	    statefile, &have_state, printerror)) != 0) {
49947dd1d1bSDag-Erling Smørgrav 		if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
50019261079SEd Maste 		    ostatefile, &have_ostate, printerror)) == 0) {
50147dd1d1bSDag-Erling Smørgrav 			state->allow_update = 1;
50247dd1d1bSDag-Erling Smørgrav 			r = sshkey_xmss_forward_state(k, 1);
50347dd1d1bSDag-Erling Smørgrav 			state->idx = PEEK_U32(k->xmss_sk);
50447dd1d1bSDag-Erling Smørgrav 			state->allow_update = 0;
50547dd1d1bSDag-Erling Smørgrav 		}
50647dd1d1bSDag-Erling Smørgrav 	}
50747dd1d1bSDag-Erling Smørgrav 	if (!have_state && !have_ostate) {
50847dd1d1bSDag-Erling Smørgrav 		/* check that bds state is initialized */
50947dd1d1bSDag-Erling Smørgrav 		if (state->bds.auth == NULL)
51047dd1d1bSDag-Erling Smørgrav 			goto done;
51119261079SEd Maste 		PRINT("start from scratch idx 0: %u", state->idx);
51247dd1d1bSDag-Erling Smørgrav 	} else if (r != 0) {
51347dd1d1bSDag-Erling Smørgrav 		ret = r;
51447dd1d1bSDag-Erling Smørgrav 		goto done;
51547dd1d1bSDag-Erling Smørgrav 	}
51647dd1d1bSDag-Erling Smørgrav 	if (state->idx + 1 < state->idx) {
51719261079SEd Maste 		PRINT("state wrap: %u", state->idx);
51847dd1d1bSDag-Erling Smørgrav 		goto done;
51947dd1d1bSDag-Erling Smørgrav 	}
52047dd1d1bSDag-Erling Smørgrav 	state->have_state = have_state;
52147dd1d1bSDag-Erling Smørgrav 	state->lockfd = lockfd;
52247dd1d1bSDag-Erling Smørgrav 	state->allow_update = 1;
52347dd1d1bSDag-Erling Smørgrav 	lockfd = -1;
52447dd1d1bSDag-Erling Smørgrav 	ret = 0;
52547dd1d1bSDag-Erling Smørgrav done:
52647dd1d1bSDag-Erling Smørgrav 	if (lockfd != -1)
52747dd1d1bSDag-Erling Smørgrav 		close(lockfd);
52847dd1d1bSDag-Erling Smørgrav 	free(lockfile);
52947dd1d1bSDag-Erling Smørgrav 	free(statefile);
53047dd1d1bSDag-Erling Smørgrav 	free(ostatefile);
53147dd1d1bSDag-Erling Smørgrav 	return ret;
53247dd1d1bSDag-Erling Smørgrav }
53347dd1d1bSDag-Erling Smørgrav 
53447dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_forward_state(const struct sshkey * k,u_int32_t reserve)53547dd1d1bSDag-Erling Smørgrav sshkey_xmss_forward_state(const struct sshkey *k, u_int32_t reserve)
53647dd1d1bSDag-Erling Smørgrav {
53747dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
53847dd1d1bSDag-Erling Smørgrav 	u_char *sig = NULL;
53947dd1d1bSDag-Erling Smørgrav 	size_t required_siglen;
54047dd1d1bSDag-Erling Smørgrav 	unsigned long long smlen;
54147dd1d1bSDag-Erling Smørgrav 	u_char data;
54247dd1d1bSDag-Erling Smørgrav 	int ret, r;
54347dd1d1bSDag-Erling Smørgrav 
54447dd1d1bSDag-Erling Smørgrav 	if (state == NULL || !state->allow_update)
54547dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
54647dd1d1bSDag-Erling Smørgrav 	if (reserve == 0)
54747dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
54847dd1d1bSDag-Erling Smørgrav 	if (state->idx + reserve <= state->idx)
54947dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
55047dd1d1bSDag-Erling Smørgrav 	if ((r = sshkey_xmss_siglen(k, &required_siglen)) != 0)
55147dd1d1bSDag-Erling Smørgrav 		return r;
55247dd1d1bSDag-Erling Smørgrav 	if ((sig = malloc(required_siglen)) == NULL)
55347dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
55447dd1d1bSDag-Erling Smørgrav 	while (reserve-- > 0) {
55547dd1d1bSDag-Erling Smørgrav 		state->idx = PEEK_U32(k->xmss_sk);
55647dd1d1bSDag-Erling Smørgrav 		smlen = required_siglen;
55747dd1d1bSDag-Erling Smørgrav 		if ((ret = xmss_sign(k->xmss_sk, sshkey_xmss_bds_state(k),
55847dd1d1bSDag-Erling Smørgrav 		    sig, &smlen, &data, 0, sshkey_xmss_params(k))) != 0) {
55947dd1d1bSDag-Erling Smørgrav 			r = SSH_ERR_INVALID_ARGUMENT;
56047dd1d1bSDag-Erling Smørgrav 			break;
56147dd1d1bSDag-Erling Smørgrav 		}
56247dd1d1bSDag-Erling Smørgrav 	}
56347dd1d1bSDag-Erling Smørgrav 	free(sig);
56447dd1d1bSDag-Erling Smørgrav 	return r;
56547dd1d1bSDag-Erling Smørgrav }
56647dd1d1bSDag-Erling Smørgrav 
56747dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_update_state(const struct sshkey * k,int printerror)56819261079SEd Maste sshkey_xmss_update_state(const struct sshkey *k, int printerror)
56947dd1d1bSDag-Erling Smørgrav {
57047dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
57147dd1d1bSDag-Erling Smørgrav 	struct sshbuf *b = NULL, *enc = NULL;
57247dd1d1bSDag-Erling Smørgrav 	u_int32_t idx = 0;
57347dd1d1bSDag-Erling Smørgrav 	unsigned char buf[4];
57447dd1d1bSDag-Erling Smørgrav 	char *filename = NULL;
57547dd1d1bSDag-Erling Smørgrav 	char *statefile = NULL, *ostatefile = NULL, *nstatefile = NULL;
57647dd1d1bSDag-Erling Smørgrav 	int fd = -1;
57747dd1d1bSDag-Erling Smørgrav 	int ret = SSH_ERR_INVALID_ARGUMENT;
57847dd1d1bSDag-Erling Smørgrav 
57947dd1d1bSDag-Erling Smørgrav 	if (state == NULL || !state->allow_update)
58047dd1d1bSDag-Erling Smørgrav 		return ret;
58147dd1d1bSDag-Erling Smørgrav 	if (state->maxidx) {
58247dd1d1bSDag-Erling Smørgrav 		/* no update since the number of signatures is limited */
58347dd1d1bSDag-Erling Smørgrav 		ret = 0;
58447dd1d1bSDag-Erling Smørgrav 		goto done;
58547dd1d1bSDag-Erling Smørgrav 	}
58647dd1d1bSDag-Erling Smørgrav 	idx = PEEK_U32(k->xmss_sk);
58747dd1d1bSDag-Erling Smørgrav 	if (idx == state->idx) {
588190cef3dSDag-Erling Smørgrav 		/* no signature happened, no need to update */
58947dd1d1bSDag-Erling Smørgrav 		ret = 0;
59047dd1d1bSDag-Erling Smørgrav 		goto done;
59147dd1d1bSDag-Erling Smørgrav 	} else if (idx != state->idx + 1) {
59219261079SEd Maste 		PRINT("more than one signature happened: idx %u state %u",
59319261079SEd Maste 		    idx, state->idx);
59447dd1d1bSDag-Erling Smørgrav 		goto done;
59547dd1d1bSDag-Erling Smørgrav 	}
59647dd1d1bSDag-Erling Smørgrav 	state->idx = idx;
59747dd1d1bSDag-Erling Smørgrav 	if ((filename = k->xmss_filename) == NULL)
59847dd1d1bSDag-Erling Smørgrav 		goto done;
59919261079SEd Maste 	if (asprintf(&statefile, "%s.state", filename) == -1 ||
60019261079SEd Maste 	    asprintf(&ostatefile, "%s.ostate", filename) == -1 ||
60119261079SEd Maste 	    asprintf(&nstatefile, "%s.nstate", filename) == -1) {
60247dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_ALLOC_FAIL;
60347dd1d1bSDag-Erling Smørgrav 		goto done;
60447dd1d1bSDag-Erling Smørgrav 	}
60547dd1d1bSDag-Erling Smørgrav 	unlink(nstatefile);
60647dd1d1bSDag-Erling Smørgrav 	if ((b = sshbuf_new()) == NULL) {
60747dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_ALLOC_FAIL;
60847dd1d1bSDag-Erling Smørgrav 		goto done;
60947dd1d1bSDag-Erling Smørgrav 	}
61047dd1d1bSDag-Erling Smørgrav 	if ((ret = sshkey_xmss_serialize_state(k, b)) != 0) {
61119261079SEd Maste 		PRINT("SERLIALIZE FAILED: %d", ret);
61247dd1d1bSDag-Erling Smørgrav 		goto done;
61347dd1d1bSDag-Erling Smørgrav 	}
61447dd1d1bSDag-Erling Smørgrav 	if ((ret = sshkey_xmss_encrypt_state(k, b, &enc)) != 0) {
61519261079SEd Maste 		PRINT("ENCRYPT FAILED: %d", ret);
61647dd1d1bSDag-Erling Smørgrav 		goto done;
61747dd1d1bSDag-Erling Smørgrav 	}
61819261079SEd Maste 	if ((fd = open(nstatefile, O_CREAT|O_WRONLY|O_EXCL, 0600)) == -1) {
61947dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_SYSTEM_ERROR;
62019261079SEd Maste 		PRINT("open new state file: %s", nstatefile);
62147dd1d1bSDag-Erling Smørgrav 		goto done;
62247dd1d1bSDag-Erling Smørgrav 	}
62347dd1d1bSDag-Erling Smørgrav 	POKE_U32(buf, sshbuf_len(enc));
62447dd1d1bSDag-Erling Smørgrav 	if (atomicio(vwrite, fd, buf, sizeof(buf)) != sizeof(buf)) {
62547dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_SYSTEM_ERROR;
62619261079SEd Maste 		PRINT("write new state file hdr: %s", nstatefile);
62747dd1d1bSDag-Erling Smørgrav 		close(fd);
62847dd1d1bSDag-Erling Smørgrav 		goto done;
62947dd1d1bSDag-Erling Smørgrav 	}
630190cef3dSDag-Erling Smørgrav 	if (atomicio(vwrite, fd, sshbuf_mutable_ptr(enc), sshbuf_len(enc)) !=
63147dd1d1bSDag-Erling Smørgrav 	    sshbuf_len(enc)) {
63247dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_SYSTEM_ERROR;
63319261079SEd Maste 		PRINT("write new state file data: %s", nstatefile);
63447dd1d1bSDag-Erling Smørgrav 		close(fd);
63547dd1d1bSDag-Erling Smørgrav 		goto done;
63647dd1d1bSDag-Erling Smørgrav 	}
63719261079SEd Maste 	if (fsync(fd) == -1) {
63847dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_SYSTEM_ERROR;
63919261079SEd Maste 		PRINT("sync new state file: %s", nstatefile);
64047dd1d1bSDag-Erling Smørgrav 		close(fd);
64147dd1d1bSDag-Erling Smørgrav 		goto done;
64247dd1d1bSDag-Erling Smørgrav 	}
64319261079SEd Maste 	if (close(fd) == -1) {
64447dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_SYSTEM_ERROR;
64519261079SEd Maste 		PRINT("close new state file: %s", nstatefile);
64647dd1d1bSDag-Erling Smørgrav 		goto done;
64747dd1d1bSDag-Erling Smørgrav 	}
64847dd1d1bSDag-Erling Smørgrav 	if (state->have_state) {
64947dd1d1bSDag-Erling Smørgrav 		unlink(ostatefile);
65047dd1d1bSDag-Erling Smørgrav 		if (link(statefile, ostatefile)) {
65147dd1d1bSDag-Erling Smørgrav 			ret = SSH_ERR_SYSTEM_ERROR;
65219261079SEd Maste 			PRINT("backup state %s to %s", statefile, ostatefile);
65347dd1d1bSDag-Erling Smørgrav 			goto done;
65447dd1d1bSDag-Erling Smørgrav 		}
65547dd1d1bSDag-Erling Smørgrav 	}
65619261079SEd Maste 	if (rename(nstatefile, statefile) == -1) {
65747dd1d1bSDag-Erling Smørgrav 		ret = SSH_ERR_SYSTEM_ERROR;
65819261079SEd Maste 		PRINT("rename %s to %s", nstatefile, statefile);
65947dd1d1bSDag-Erling Smørgrav 		goto done;
66047dd1d1bSDag-Erling Smørgrav 	}
66147dd1d1bSDag-Erling Smørgrav 	ret = 0;
66247dd1d1bSDag-Erling Smørgrav done:
66347dd1d1bSDag-Erling Smørgrav 	if (state->lockfd != -1) {
66447dd1d1bSDag-Erling Smørgrav 		close(state->lockfd);
66547dd1d1bSDag-Erling Smørgrav 		state->lockfd = -1;
66647dd1d1bSDag-Erling Smørgrav 	}
66747dd1d1bSDag-Erling Smørgrav 	if (nstatefile)
66847dd1d1bSDag-Erling Smørgrav 		unlink(nstatefile);
66947dd1d1bSDag-Erling Smørgrav 	free(statefile);
67047dd1d1bSDag-Erling Smørgrav 	free(ostatefile);
67147dd1d1bSDag-Erling Smørgrav 	free(nstatefile);
67247dd1d1bSDag-Erling Smørgrav 	sshbuf_free(b);
67347dd1d1bSDag-Erling Smørgrav 	sshbuf_free(enc);
67447dd1d1bSDag-Erling Smørgrav 	return ret;
67547dd1d1bSDag-Erling Smørgrav }
67647dd1d1bSDag-Erling Smørgrav 
67747dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_serialize_state(const struct sshkey * k,struct sshbuf * b)67847dd1d1bSDag-Erling Smørgrav sshkey_xmss_serialize_state(const struct sshkey *k, struct sshbuf *b)
67947dd1d1bSDag-Erling Smørgrav {
68047dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
68147dd1d1bSDag-Erling Smørgrav 	treehash_inst *th;
68247dd1d1bSDag-Erling Smørgrav 	u_int32_t i, node;
68347dd1d1bSDag-Erling Smørgrav 	int r;
68447dd1d1bSDag-Erling Smørgrav 
68547dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
68647dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
68747dd1d1bSDag-Erling Smørgrav 	if (state->stack == NULL)
68847dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
68947dd1d1bSDag-Erling Smørgrav 	state->stackoffset = state->bds.stackoffset;	/* copy back */
69047dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_put_cstring(b, SSH_XMSS_K2_MAGIC)) != 0 ||
69147dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, state->idx)) != 0 ||
69247dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_string(b, state->stack, num_stack(state))) != 0 ||
69347dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, state->stackoffset)) != 0 ||
69447dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_string(b, state->stacklevels, num_stacklevels(state))) != 0 ||
69547dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_string(b, state->auth, num_auth(state))) != 0 ||
69647dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_string(b, state->keep, num_keep(state))) != 0 ||
69747dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_string(b, state->th_nodes, num_th_nodes(state))) != 0 ||
69847dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_string(b, state->retain, num_retain(state))) != 0 ||
69947dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, num_treehash(state))) != 0)
70047dd1d1bSDag-Erling Smørgrav 		return r;
70147dd1d1bSDag-Erling Smørgrav 	for (i = 0; i < num_treehash(state); i++) {
70247dd1d1bSDag-Erling Smørgrav 		th = &state->treehash[i];
70347dd1d1bSDag-Erling Smørgrav 		node = th->node - state->th_nodes;
70447dd1d1bSDag-Erling Smørgrav 		if ((r = sshbuf_put_u32(b, th->h)) != 0 ||
70547dd1d1bSDag-Erling Smørgrav 		    (r = sshbuf_put_u32(b, th->next_idx)) != 0 ||
70647dd1d1bSDag-Erling Smørgrav 		    (r = sshbuf_put_u32(b, th->stackusage)) != 0 ||
70747dd1d1bSDag-Erling Smørgrav 		    (r = sshbuf_put_u8(b, th->completed)) != 0 ||
70847dd1d1bSDag-Erling Smørgrav 		    (r = sshbuf_put_u32(b, node)) != 0)
70947dd1d1bSDag-Erling Smørgrav 			return r;
71047dd1d1bSDag-Erling Smørgrav 	}
71147dd1d1bSDag-Erling Smørgrav 	return 0;
71247dd1d1bSDag-Erling Smørgrav }
71347dd1d1bSDag-Erling Smørgrav 
71447dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_serialize_state_opt(const struct sshkey * k,struct sshbuf * b,enum sshkey_serialize_rep opts)71547dd1d1bSDag-Erling Smørgrav sshkey_xmss_serialize_state_opt(const struct sshkey *k, struct sshbuf *b,
71647dd1d1bSDag-Erling Smørgrav     enum sshkey_serialize_rep opts)
71747dd1d1bSDag-Erling Smørgrav {
71847dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
71947dd1d1bSDag-Erling Smørgrav 	int r = SSH_ERR_INVALID_ARGUMENT;
72019261079SEd Maste 	u_char have_stack, have_filename, have_enc;
72147dd1d1bSDag-Erling Smørgrav 
72247dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
72347dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
72447dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(b, opts)) != 0)
72547dd1d1bSDag-Erling Smørgrav 		return r;
72647dd1d1bSDag-Erling Smørgrav 	switch (opts) {
72747dd1d1bSDag-Erling Smørgrav 	case SSHKEY_SERIALIZE_STATE:
72847dd1d1bSDag-Erling Smørgrav 		r = sshkey_xmss_serialize_state(k, b);
72947dd1d1bSDag-Erling Smørgrav 		break;
73047dd1d1bSDag-Erling Smørgrav 	case SSHKEY_SERIALIZE_FULL:
73147dd1d1bSDag-Erling Smørgrav 		if ((r = sshkey_xmss_serialize_enc_key(k, b)) != 0)
73219261079SEd Maste 			return r;
73347dd1d1bSDag-Erling Smørgrav 		r = sshkey_xmss_serialize_state(k, b);
73447dd1d1bSDag-Erling Smørgrav 		break;
73519261079SEd Maste 	case SSHKEY_SERIALIZE_SHIELD:
73619261079SEd Maste 		/* all of stack/filename/enc are optional */
73719261079SEd Maste 		have_stack = state->stack != NULL;
73819261079SEd Maste 		if ((r = sshbuf_put_u8(b, have_stack)) != 0)
73919261079SEd Maste 			return r;
74019261079SEd Maste 		if (have_stack) {
74119261079SEd Maste 			state->idx = PEEK_U32(k->xmss_sk);	/* update */
74219261079SEd Maste 			if ((r = sshkey_xmss_serialize_state(k, b)) != 0)
74319261079SEd Maste 				return r;
74419261079SEd Maste 		}
74519261079SEd Maste 		have_filename = k->xmss_filename != NULL;
74619261079SEd Maste 		if ((r = sshbuf_put_u8(b, have_filename)) != 0)
74719261079SEd Maste 			return r;
74819261079SEd Maste 		if (have_filename &&
74919261079SEd Maste 		    (r = sshbuf_put_cstring(b, k->xmss_filename)) != 0)
75019261079SEd Maste 			return r;
75119261079SEd Maste 		have_enc = state->enc_keyiv != NULL;
75219261079SEd Maste 		if ((r = sshbuf_put_u8(b, have_enc)) != 0)
75319261079SEd Maste 			return r;
75419261079SEd Maste 		if (have_enc &&
75519261079SEd Maste 		    (r = sshkey_xmss_serialize_enc_key(k, b)) != 0)
75619261079SEd Maste 			return r;
75719261079SEd Maste 		if ((r = sshbuf_put_u32(b, state->maxidx)) != 0 ||
75819261079SEd Maste 		    (r = sshbuf_put_u8(b, state->allow_update)) != 0)
75919261079SEd Maste 			return r;
76019261079SEd Maste 		break;
76147dd1d1bSDag-Erling Smørgrav 	case SSHKEY_SERIALIZE_DEFAULT:
76247dd1d1bSDag-Erling Smørgrav 		r = 0;
76347dd1d1bSDag-Erling Smørgrav 		break;
76447dd1d1bSDag-Erling Smørgrav 	default:
76547dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_ARGUMENT;
76647dd1d1bSDag-Erling Smørgrav 		break;
76747dd1d1bSDag-Erling Smørgrav 	}
76847dd1d1bSDag-Erling Smørgrav 	return r;
76947dd1d1bSDag-Erling Smørgrav }
77047dd1d1bSDag-Erling Smørgrav 
77147dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_deserialize_state(struct sshkey * k,struct sshbuf * b)77247dd1d1bSDag-Erling Smørgrav sshkey_xmss_deserialize_state(struct sshkey *k, struct sshbuf *b)
77347dd1d1bSDag-Erling Smørgrav {
77447dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
77547dd1d1bSDag-Erling Smørgrav 	treehash_inst *th;
77647dd1d1bSDag-Erling Smørgrav 	u_int32_t i, lh, node;
77747dd1d1bSDag-Erling Smørgrav 	size_t ls, lsl, la, lk, ln, lr;
77847dd1d1bSDag-Erling Smørgrav 	char *magic;
77919261079SEd Maste 	int r = SSH_ERR_INTERNAL_ERROR;
78047dd1d1bSDag-Erling Smørgrav 
78147dd1d1bSDag-Erling Smørgrav 	if (state == NULL)
78247dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
78347dd1d1bSDag-Erling Smørgrav 	if (k->xmss_sk == NULL)
78447dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
78547dd1d1bSDag-Erling Smørgrav 	if ((state->treehash = calloc(num_treehash(state),
78647dd1d1bSDag-Erling Smørgrav 	    sizeof(treehash_inst))) == NULL)
78747dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
78847dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_get_cstring(b, &magic, NULL)) != 0 ||
78947dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(b, &state->idx)) != 0 ||
79047dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_string(b, &state->stack, &ls)) != 0 ||
79147dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(b, &state->stackoffset)) != 0 ||
79247dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_string(b, &state->stacklevels, &lsl)) != 0 ||
79347dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_string(b, &state->auth, &la)) != 0 ||
79447dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_string(b, &state->keep, &lk)) != 0 ||
79547dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_string(b, &state->th_nodes, &ln)) != 0 ||
79647dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_string(b, &state->retain, &lr)) != 0 ||
79747dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(b, &lh)) != 0)
79819261079SEd Maste 		goto out;
79919261079SEd Maste 	if (strcmp(magic, SSH_XMSS_K2_MAGIC) != 0) {
80019261079SEd Maste 		r = SSH_ERR_INVALID_ARGUMENT;
80119261079SEd Maste 		goto out;
80219261079SEd Maste 	}
80347dd1d1bSDag-Erling Smørgrav 	/* XXX check stackoffset */
80447dd1d1bSDag-Erling Smørgrav 	if (ls != num_stack(state) ||
80547dd1d1bSDag-Erling Smørgrav 	    lsl != num_stacklevels(state) ||
80647dd1d1bSDag-Erling Smørgrav 	    la != num_auth(state) ||
80747dd1d1bSDag-Erling Smørgrav 	    lk != num_keep(state) ||
80847dd1d1bSDag-Erling Smørgrav 	    ln != num_th_nodes(state) ||
80947dd1d1bSDag-Erling Smørgrav 	    lr != num_retain(state) ||
81019261079SEd Maste 	    lh != num_treehash(state)) {
81119261079SEd Maste 		r = SSH_ERR_INVALID_ARGUMENT;
81219261079SEd Maste 		goto out;
81319261079SEd Maste 	}
81447dd1d1bSDag-Erling Smørgrav 	for (i = 0; i < num_treehash(state); i++) {
81547dd1d1bSDag-Erling Smørgrav 		th = &state->treehash[i];
81647dd1d1bSDag-Erling Smørgrav 		if ((r = sshbuf_get_u32(b, &th->h)) != 0 ||
81747dd1d1bSDag-Erling Smørgrav 		    (r = sshbuf_get_u32(b, &th->next_idx)) != 0 ||
81847dd1d1bSDag-Erling Smørgrav 		    (r = sshbuf_get_u32(b, &th->stackusage)) != 0 ||
81947dd1d1bSDag-Erling Smørgrav 		    (r = sshbuf_get_u8(b, &th->completed)) != 0 ||
82047dd1d1bSDag-Erling Smørgrav 		    (r = sshbuf_get_u32(b, &node)) != 0)
82119261079SEd Maste 			goto out;
82247dd1d1bSDag-Erling Smørgrav 		if (node < num_th_nodes(state))
82347dd1d1bSDag-Erling Smørgrav 			th->node = &state->th_nodes[node];
82447dd1d1bSDag-Erling Smørgrav 	}
82547dd1d1bSDag-Erling Smørgrav 	POKE_U32(k->xmss_sk, state->idx);
82647dd1d1bSDag-Erling Smørgrav 	xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
82747dd1d1bSDag-Erling Smørgrav 	    state->stacklevels, state->auth, state->keep, state->treehash,
82847dd1d1bSDag-Erling Smørgrav 	    state->retain, 0);
82919261079SEd Maste 	/* success */
83019261079SEd Maste 	r = 0;
83119261079SEd Maste  out:
83219261079SEd Maste 	free(magic);
83319261079SEd Maste 	return r;
83447dd1d1bSDag-Erling Smørgrav }
83547dd1d1bSDag-Erling Smørgrav 
83647dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_deserialize_state_opt(struct sshkey * k,struct sshbuf * b)83747dd1d1bSDag-Erling Smørgrav sshkey_xmss_deserialize_state_opt(struct sshkey *k, struct sshbuf *b)
83847dd1d1bSDag-Erling Smørgrav {
83919261079SEd Maste 	struct ssh_xmss_state *state = k->xmss_state;
84047dd1d1bSDag-Erling Smørgrav 	enum sshkey_serialize_rep opts;
84119261079SEd Maste 	u_char have_state, have_stack, have_filename, have_enc;
84247dd1d1bSDag-Erling Smørgrav 	int r;
84347dd1d1bSDag-Erling Smørgrav 
84447dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_get_u8(b, &have_state)) != 0)
84547dd1d1bSDag-Erling Smørgrav 		return r;
84647dd1d1bSDag-Erling Smørgrav 
84747dd1d1bSDag-Erling Smørgrav 	opts = have_state;
84847dd1d1bSDag-Erling Smørgrav 	switch (opts) {
84947dd1d1bSDag-Erling Smørgrav 	case SSHKEY_SERIALIZE_DEFAULT:
85047dd1d1bSDag-Erling Smørgrav 		r = 0;
85147dd1d1bSDag-Erling Smørgrav 		break;
85219261079SEd Maste 	case SSHKEY_SERIALIZE_SHIELD:
85319261079SEd Maste 		if ((r = sshbuf_get_u8(b, &have_stack)) != 0)
85419261079SEd Maste 			return r;
85519261079SEd Maste 		if (have_stack &&
85619261079SEd Maste 		    (r = sshkey_xmss_deserialize_state(k, b)) != 0)
85719261079SEd Maste 			return r;
85819261079SEd Maste 		if ((r = sshbuf_get_u8(b, &have_filename)) != 0)
85919261079SEd Maste 			return r;
86019261079SEd Maste 		if (have_filename &&
86119261079SEd Maste 		    (r = sshbuf_get_cstring(b, &k->xmss_filename, NULL)) != 0)
86219261079SEd Maste 			return r;
86319261079SEd Maste 		if ((r = sshbuf_get_u8(b, &have_enc)) != 0)
86419261079SEd Maste 			return r;
86519261079SEd Maste 		if (have_enc &&
86619261079SEd Maste 		    (r = sshkey_xmss_deserialize_enc_key(k, b)) != 0)
86719261079SEd Maste 			return r;
86819261079SEd Maste 		if ((r = sshbuf_get_u32(b, &state->maxidx)) != 0 ||
86919261079SEd Maste 		    (r = sshbuf_get_u8(b, &state->allow_update)) != 0)
87019261079SEd Maste 			return r;
87119261079SEd Maste 		break;
87247dd1d1bSDag-Erling Smørgrav 	case SSHKEY_SERIALIZE_STATE:
87347dd1d1bSDag-Erling Smørgrav 		if ((r = sshkey_xmss_deserialize_state(k, b)) != 0)
87447dd1d1bSDag-Erling Smørgrav 			return r;
87547dd1d1bSDag-Erling Smørgrav 		break;
87647dd1d1bSDag-Erling Smørgrav 	case SSHKEY_SERIALIZE_FULL:
87747dd1d1bSDag-Erling Smørgrav 		if ((r = sshkey_xmss_deserialize_enc_key(k, b)) != 0 ||
87847dd1d1bSDag-Erling Smørgrav 		    (r = sshkey_xmss_deserialize_state(k, b)) != 0)
87947dd1d1bSDag-Erling Smørgrav 			return r;
88047dd1d1bSDag-Erling Smørgrav 		break;
88147dd1d1bSDag-Erling Smørgrav 	default:
88247dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_FORMAT;
88347dd1d1bSDag-Erling Smørgrav 		break;
88447dd1d1bSDag-Erling Smørgrav 	}
88547dd1d1bSDag-Erling Smørgrav 	return r;
88647dd1d1bSDag-Erling Smørgrav }
88747dd1d1bSDag-Erling Smørgrav 
88847dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_encrypt_state(const struct sshkey * k,struct sshbuf * b,struct sshbuf ** retp)88947dd1d1bSDag-Erling Smørgrav sshkey_xmss_encrypt_state(const struct sshkey *k, struct sshbuf *b,
89047dd1d1bSDag-Erling Smørgrav    struct sshbuf **retp)
89147dd1d1bSDag-Erling Smørgrav {
89247dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
89347dd1d1bSDag-Erling Smørgrav 	struct sshbuf *encrypted = NULL, *encoded = NULL, *padded = NULL;
89447dd1d1bSDag-Erling Smørgrav 	struct sshcipher_ctx *ciphercontext = NULL;
89547dd1d1bSDag-Erling Smørgrav 	const struct sshcipher *cipher;
89647dd1d1bSDag-Erling Smørgrav 	u_char *cp, *key, *iv = NULL;
89747dd1d1bSDag-Erling Smørgrav 	size_t i, keylen, ivlen, blocksize, authlen, encrypted_len, aadlen;
89847dd1d1bSDag-Erling Smørgrav 	int r = SSH_ERR_INTERNAL_ERROR;
89947dd1d1bSDag-Erling Smørgrav 
90047dd1d1bSDag-Erling Smørgrav 	if (retp != NULL)
90147dd1d1bSDag-Erling Smørgrav 		*retp = NULL;
90247dd1d1bSDag-Erling Smørgrav 	if (state == NULL ||
90347dd1d1bSDag-Erling Smørgrav 	    state->enc_keyiv == NULL ||
90447dd1d1bSDag-Erling Smørgrav 	    state->enc_ciphername == NULL)
90547dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
90647dd1d1bSDag-Erling Smørgrav 	if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
90747dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INTERNAL_ERROR;
90847dd1d1bSDag-Erling Smørgrav 		goto out;
90947dd1d1bSDag-Erling Smørgrav 	}
91047dd1d1bSDag-Erling Smørgrav 	blocksize = cipher_blocksize(cipher);
91147dd1d1bSDag-Erling Smørgrav 	keylen = cipher_keylen(cipher);
91247dd1d1bSDag-Erling Smørgrav 	ivlen = cipher_ivlen(cipher);
91347dd1d1bSDag-Erling Smørgrav 	authlen = cipher_authlen(cipher);
91447dd1d1bSDag-Erling Smørgrav 	if (state->enc_keyiv_len != keylen + ivlen) {
91547dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_FORMAT;
91647dd1d1bSDag-Erling Smørgrav 		goto out;
91747dd1d1bSDag-Erling Smørgrav 	}
91847dd1d1bSDag-Erling Smørgrav 	key = state->enc_keyiv;
91947dd1d1bSDag-Erling Smørgrav 	if ((encrypted = sshbuf_new()) == NULL ||
92047dd1d1bSDag-Erling Smørgrav 	    (encoded = sshbuf_new()) == NULL ||
92147dd1d1bSDag-Erling Smørgrav 	    (padded = sshbuf_new()) == NULL ||
92247dd1d1bSDag-Erling Smørgrav 	    (iv = malloc(ivlen)) == NULL) {
92347dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
92447dd1d1bSDag-Erling Smørgrav 		goto out;
92547dd1d1bSDag-Erling Smørgrav 	}
92647dd1d1bSDag-Erling Smørgrav 
92747dd1d1bSDag-Erling Smørgrav 	/* replace first 4 bytes of IV with index to ensure uniqueness */
92847dd1d1bSDag-Erling Smørgrav 	memcpy(iv, key + keylen, ivlen);
92947dd1d1bSDag-Erling Smørgrav 	POKE_U32(iv, state->idx);
93047dd1d1bSDag-Erling Smørgrav 
93147dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_put(encoded, XMSS_MAGIC, sizeof(XMSS_MAGIC))) != 0 ||
93247dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(encoded, state->idx)) != 0)
93347dd1d1bSDag-Erling Smørgrav 		goto out;
93447dd1d1bSDag-Erling Smørgrav 
93547dd1d1bSDag-Erling Smørgrav 	/* padded state will be encrypted */
93647dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_putb(padded, b)) != 0)
93747dd1d1bSDag-Erling Smørgrav 		goto out;
93847dd1d1bSDag-Erling Smørgrav 	i = 0;
93947dd1d1bSDag-Erling Smørgrav 	while (sshbuf_len(padded) % blocksize) {
94047dd1d1bSDag-Erling Smørgrav 		if ((r = sshbuf_put_u8(padded, ++i & 0xff)) != 0)
94147dd1d1bSDag-Erling Smørgrav 			goto out;
94247dd1d1bSDag-Erling Smørgrav 	}
94347dd1d1bSDag-Erling Smørgrav 	encrypted_len = sshbuf_len(padded);
94447dd1d1bSDag-Erling Smørgrav 
94547dd1d1bSDag-Erling Smørgrav 	/* header including the length of state is used as AAD */
94647dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_put_u32(encoded, encrypted_len)) != 0)
94747dd1d1bSDag-Erling Smørgrav 		goto out;
94847dd1d1bSDag-Erling Smørgrav 	aadlen = sshbuf_len(encoded);
94947dd1d1bSDag-Erling Smørgrav 
95047dd1d1bSDag-Erling Smørgrav 	/* concat header and state */
95147dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_putb(encoded, padded)) != 0)
95247dd1d1bSDag-Erling Smørgrav 		goto out;
95347dd1d1bSDag-Erling Smørgrav 
95447dd1d1bSDag-Erling Smørgrav 	/* reserve space for encryption of encoded data plus auth tag */
95547dd1d1bSDag-Erling Smørgrav 	/* encrypt at offset addlen */
95647dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_reserve(encrypted,
95747dd1d1bSDag-Erling Smørgrav 	    encrypted_len + aadlen + authlen, &cp)) != 0 ||
95847dd1d1bSDag-Erling Smørgrav 	    (r = cipher_init(&ciphercontext, cipher, key, keylen,
95947dd1d1bSDag-Erling Smørgrav 	    iv, ivlen, 1)) != 0 ||
96047dd1d1bSDag-Erling Smørgrav 	    (r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encoded),
96147dd1d1bSDag-Erling Smørgrav 	    encrypted_len, aadlen, authlen)) != 0)
96247dd1d1bSDag-Erling Smørgrav 		goto out;
96347dd1d1bSDag-Erling Smørgrav 
96447dd1d1bSDag-Erling Smørgrav 	/* success */
96547dd1d1bSDag-Erling Smørgrav 	r = 0;
96647dd1d1bSDag-Erling Smørgrav  out:
96747dd1d1bSDag-Erling Smørgrav 	if (retp != NULL) {
96847dd1d1bSDag-Erling Smørgrav 		*retp = encrypted;
96947dd1d1bSDag-Erling Smørgrav 		encrypted = NULL;
97047dd1d1bSDag-Erling Smørgrav 	}
97147dd1d1bSDag-Erling Smørgrav 	sshbuf_free(padded);
97247dd1d1bSDag-Erling Smørgrav 	sshbuf_free(encoded);
97347dd1d1bSDag-Erling Smørgrav 	sshbuf_free(encrypted);
97447dd1d1bSDag-Erling Smørgrav 	cipher_free(ciphercontext);
97547dd1d1bSDag-Erling Smørgrav 	free(iv);
97647dd1d1bSDag-Erling Smørgrav 	return r;
97747dd1d1bSDag-Erling Smørgrav }
97847dd1d1bSDag-Erling Smørgrav 
97947dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_decrypt_state(const struct sshkey * k,struct sshbuf * encoded,struct sshbuf ** retp)98047dd1d1bSDag-Erling Smørgrav sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded,
98147dd1d1bSDag-Erling Smørgrav    struct sshbuf **retp)
98247dd1d1bSDag-Erling Smørgrav {
98347dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
98447dd1d1bSDag-Erling Smørgrav 	struct sshbuf *copy = NULL, *decrypted = NULL;
98547dd1d1bSDag-Erling Smørgrav 	struct sshcipher_ctx *ciphercontext = NULL;
98647dd1d1bSDag-Erling Smørgrav 	const struct sshcipher *cipher = NULL;
98747dd1d1bSDag-Erling Smørgrav 	u_char *key, *iv = NULL, *dp;
98847dd1d1bSDag-Erling Smørgrav 	size_t keylen, ivlen, authlen, aadlen;
98947dd1d1bSDag-Erling Smørgrav 	u_int blocksize, encrypted_len, index;
99047dd1d1bSDag-Erling Smørgrav 	int r = SSH_ERR_INTERNAL_ERROR;
99147dd1d1bSDag-Erling Smørgrav 
99247dd1d1bSDag-Erling Smørgrav 	if (retp != NULL)
99347dd1d1bSDag-Erling Smørgrav 		*retp = NULL;
99447dd1d1bSDag-Erling Smørgrav 	if (state == NULL ||
99547dd1d1bSDag-Erling Smørgrav 	    state->enc_keyiv == NULL ||
99647dd1d1bSDag-Erling Smørgrav 	    state->enc_ciphername == NULL)
99747dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
99847dd1d1bSDag-Erling Smørgrav 	if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
99947dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_FORMAT;
100047dd1d1bSDag-Erling Smørgrav 		goto out;
100147dd1d1bSDag-Erling Smørgrav 	}
100247dd1d1bSDag-Erling Smørgrav 	blocksize = cipher_blocksize(cipher);
100347dd1d1bSDag-Erling Smørgrav 	keylen = cipher_keylen(cipher);
100447dd1d1bSDag-Erling Smørgrav 	ivlen = cipher_ivlen(cipher);
100547dd1d1bSDag-Erling Smørgrav 	authlen = cipher_authlen(cipher);
100647dd1d1bSDag-Erling Smørgrav 	if (state->enc_keyiv_len != keylen + ivlen) {
100747dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INTERNAL_ERROR;
100847dd1d1bSDag-Erling Smørgrav 		goto out;
100947dd1d1bSDag-Erling Smørgrav 	}
101047dd1d1bSDag-Erling Smørgrav 	key = state->enc_keyiv;
101147dd1d1bSDag-Erling Smørgrav 
101247dd1d1bSDag-Erling Smørgrav 	if ((copy = sshbuf_fromb(encoded)) == NULL ||
101347dd1d1bSDag-Erling Smørgrav 	    (decrypted = sshbuf_new()) == NULL ||
101447dd1d1bSDag-Erling Smørgrav 	    (iv = malloc(ivlen)) == NULL) {
101547dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
101647dd1d1bSDag-Erling Smørgrav 		goto out;
101747dd1d1bSDag-Erling Smørgrav 	}
101847dd1d1bSDag-Erling Smørgrav 
101947dd1d1bSDag-Erling Smørgrav 	/* check magic */
102047dd1d1bSDag-Erling Smørgrav 	if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) ||
102147dd1d1bSDag-Erling Smørgrav 	    memcmp(sshbuf_ptr(encoded), XMSS_MAGIC, sizeof(XMSS_MAGIC))) {
102247dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_FORMAT;
102347dd1d1bSDag-Erling Smørgrav 		goto out;
102447dd1d1bSDag-Erling Smørgrav 	}
102547dd1d1bSDag-Erling Smørgrav 	/* parse public portion */
102647dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 ||
102747dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(encoded, &index)) != 0 ||
102847dd1d1bSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0)
102947dd1d1bSDag-Erling Smørgrav 		goto out;
103047dd1d1bSDag-Erling Smørgrav 
103147dd1d1bSDag-Erling Smørgrav 	/* check size of encrypted key blob */
103247dd1d1bSDag-Erling Smørgrav 	if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
103347dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_FORMAT;
103447dd1d1bSDag-Erling Smørgrav 		goto out;
103547dd1d1bSDag-Erling Smørgrav 	}
103647dd1d1bSDag-Erling Smørgrav 	/* check that an appropriate amount of auth data is present */
103719261079SEd Maste 	if (sshbuf_len(encoded) < authlen ||
103819261079SEd Maste 	    sshbuf_len(encoded) - authlen < encrypted_len) {
103947dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_FORMAT;
104047dd1d1bSDag-Erling Smørgrav 		goto out;
104147dd1d1bSDag-Erling Smørgrav 	}
104247dd1d1bSDag-Erling Smørgrav 
104347dd1d1bSDag-Erling Smørgrav 	aadlen = sshbuf_len(copy) - sshbuf_len(encoded);
104447dd1d1bSDag-Erling Smørgrav 
104547dd1d1bSDag-Erling Smørgrav 	/* replace first 4 bytes of IV with index to ensure uniqueness */
104647dd1d1bSDag-Erling Smørgrav 	memcpy(iv, key + keylen, ivlen);
104747dd1d1bSDag-Erling Smørgrav 	POKE_U32(iv, index);
104847dd1d1bSDag-Erling Smørgrav 
104947dd1d1bSDag-Erling Smørgrav 	/* decrypt private state of key */
105047dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 ||
105147dd1d1bSDag-Erling Smørgrav 	    (r = cipher_init(&ciphercontext, cipher, key, keylen,
105247dd1d1bSDag-Erling Smørgrav 	    iv, ivlen, 0)) != 0 ||
105347dd1d1bSDag-Erling Smørgrav 	    (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy),
105447dd1d1bSDag-Erling Smørgrav 	    encrypted_len, aadlen, authlen)) != 0)
105547dd1d1bSDag-Erling Smørgrav 		goto out;
105647dd1d1bSDag-Erling Smørgrav 
105747dd1d1bSDag-Erling Smørgrav 	/* there should be no trailing data */
105847dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0)
105947dd1d1bSDag-Erling Smørgrav 		goto out;
106047dd1d1bSDag-Erling Smørgrav 	if (sshbuf_len(encoded) != 0) {
106147dd1d1bSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_FORMAT;
106247dd1d1bSDag-Erling Smørgrav 		goto out;
106347dd1d1bSDag-Erling Smørgrav 	}
106447dd1d1bSDag-Erling Smørgrav 
106547dd1d1bSDag-Erling Smørgrav 	/* remove AAD */
106647dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_consume(decrypted, aadlen)) != 0)
106747dd1d1bSDag-Erling Smørgrav 		goto out;
106847dd1d1bSDag-Erling Smørgrav 	/* XXX encrypted includes unchecked padding */
106947dd1d1bSDag-Erling Smørgrav 
107047dd1d1bSDag-Erling Smørgrav 	/* success */
107147dd1d1bSDag-Erling Smørgrav 	r = 0;
107247dd1d1bSDag-Erling Smørgrav 	if (retp != NULL) {
107347dd1d1bSDag-Erling Smørgrav 		*retp = decrypted;
107447dd1d1bSDag-Erling Smørgrav 		decrypted = NULL;
107547dd1d1bSDag-Erling Smørgrav 	}
107647dd1d1bSDag-Erling Smørgrav  out:
107747dd1d1bSDag-Erling Smørgrav 	cipher_free(ciphercontext);
107847dd1d1bSDag-Erling Smørgrav 	sshbuf_free(copy);
107947dd1d1bSDag-Erling Smørgrav 	sshbuf_free(decrypted);
108047dd1d1bSDag-Erling Smørgrav 	free(iv);
108147dd1d1bSDag-Erling Smørgrav 	return r;
108247dd1d1bSDag-Erling Smørgrav }
108347dd1d1bSDag-Erling Smørgrav 
108447dd1d1bSDag-Erling Smørgrav u_int32_t
sshkey_xmss_signatures_left(const struct sshkey * k)108547dd1d1bSDag-Erling Smørgrav sshkey_xmss_signatures_left(const struct sshkey *k)
108647dd1d1bSDag-Erling Smørgrav {
108747dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
108847dd1d1bSDag-Erling Smørgrav 	u_int32_t idx;
108947dd1d1bSDag-Erling Smørgrav 
109047dd1d1bSDag-Erling Smørgrav 	if (sshkey_type_plain(k->type) == KEY_XMSS && state &&
109147dd1d1bSDag-Erling Smørgrav 	    state->maxidx) {
109247dd1d1bSDag-Erling Smørgrav 		idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
109347dd1d1bSDag-Erling Smørgrav 		if (idx < state->maxidx)
109447dd1d1bSDag-Erling Smørgrav 			return state->maxidx - idx;
109547dd1d1bSDag-Erling Smørgrav 	}
109647dd1d1bSDag-Erling Smørgrav 	return 0;
109747dd1d1bSDag-Erling Smørgrav }
109847dd1d1bSDag-Erling Smørgrav 
109947dd1d1bSDag-Erling Smørgrav int
sshkey_xmss_enable_maxsign(struct sshkey * k,u_int32_t maxsign)110047dd1d1bSDag-Erling Smørgrav sshkey_xmss_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
110147dd1d1bSDag-Erling Smørgrav {
110247dd1d1bSDag-Erling Smørgrav 	struct ssh_xmss_state *state = k->xmss_state;
110347dd1d1bSDag-Erling Smørgrav 
110447dd1d1bSDag-Erling Smørgrav 	if (sshkey_type_plain(k->type) != KEY_XMSS)
110547dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
110647dd1d1bSDag-Erling Smørgrav 	if (maxsign == 0)
110747dd1d1bSDag-Erling Smørgrav 		return 0;
110847dd1d1bSDag-Erling Smørgrav 	if (state->idx + maxsign < state->idx)
110947dd1d1bSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
111047dd1d1bSDag-Erling Smørgrav 	state->maxidx = state->idx + maxsign;
111147dd1d1bSDag-Erling Smørgrav 	return 0;
111247dd1d1bSDag-Erling Smørgrav }
111347dd1d1bSDag-Erling Smørgrav #endif /* WITH_XMSS */
1114