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