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