Coverage Report

Created: 2025-07-01 06:04

/src/hpn-ssh/ssh-xmss.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD: ssh-xmss.c,v 1.14 2022/10/28 00:44:44 djm Exp $*/
2
/*
3
 * Copyright (c) 2017 Stefan-Lukas Gazdag.
4
 * Copyright (c) 2017 Markus Friedl.
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
#include "includes.h"
19
#ifdef WITH_XMSS
20
21
#define SSHKEY_INTERNAL
22
#include <sys/types.h>
23
#include <limits.h>
24
25
#include <stdlib.h>
26
#include <string.h>
27
#include <stdarg.h>
28
#ifdef HAVE_STDINT_H
29
# include <stdint.h>
30
#endif
31
#include <unistd.h>
32
33
#include "log.h"
34
#include "sshbuf.h"
35
#include "sshkey.h"
36
#include "sshkey-xmss.h"
37
#include "ssherr.h"
38
#include "ssh.h"
39
40
#include "xmss_fast.h"
41
42
static void
43
ssh_xmss_cleanup(struct sshkey *k)
44
0
{
45
0
  freezero(k->xmss_pk, sshkey_xmss_pklen(k));
46
0
  freezero(k->xmss_sk, sshkey_xmss_sklen(k));
47
0
  sshkey_xmss_free_state(k);
48
0
  free(k->xmss_name);
49
0
  free(k->xmss_filename);
50
0
  k->xmss_pk = NULL;
51
0
  k->xmss_sk = NULL;
52
0
  k->xmss_name = NULL;
53
0
  k->xmss_filename = NULL;
54
0
}
55
56
static int
57
ssh_xmss_equal(const struct sshkey *a, const struct sshkey *b)
58
0
{
59
0
  if (a->xmss_pk == NULL || b->xmss_pk == NULL)
60
0
    return 0;
61
0
  if (sshkey_xmss_pklen(a) != sshkey_xmss_pklen(b))
62
0
    return 0;
63
0
  if (memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) != 0)
64
0
    return 0;
65
0
  return 1;
66
0
}
67
68
static int
69
ssh_xmss_serialize_public(const struct sshkey *key, struct sshbuf *b,
70
    enum sshkey_serialize_rep opts)
71
0
{
72
0
  int r;
73
74
0
  if (key->xmss_name == NULL || key->xmss_pk == NULL ||
75
0
      sshkey_xmss_pklen(key) == 0)
76
0
    return SSH_ERR_INVALID_ARGUMENT;
77
0
  if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
78
0
      (r = sshbuf_put_string(b, key->xmss_pk,
79
0
      sshkey_xmss_pklen(key))) != 0 ||
80
0
      (r = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0)
81
0
    return r;
82
83
0
  return 0;
84
0
}
85
86
static int
87
ssh_xmss_serialize_private(const struct sshkey *key, struct sshbuf *b,
88
    enum sshkey_serialize_rep opts)
89
0
{
90
0
  int r;
91
92
0
  if (key->xmss_name == NULL)
93
0
    return SSH_ERR_INVALID_ARGUMENT;
94
  /* Note: can't reuse ssh_xmss_serialize_public because of sk order */
95
0
  if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
96
0
      (r = sshbuf_put_string(b, key->xmss_pk,
97
0
      sshkey_xmss_pklen(key))) != 0 ||
98
0
      (r = sshbuf_put_string(b, key->xmss_sk,
99
0
      sshkey_xmss_sklen(key))) != 0 ||
100
0
      (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
101
0
    return r;
102
103
0
  return 0;
104
0
}
105
106
static int
107
ssh_xmss_copy_public(const struct sshkey *from, struct sshkey *to)
108
0
{
109
0
  int r = SSH_ERR_INTERNAL_ERROR;
110
0
  u_int32_t left;
111
0
  size_t pklen;
112
113
0
  if ((r = sshkey_xmss_init(to, from->xmss_name)) != 0)
114
0
    return r;
115
0
  if (from->xmss_pk == NULL)
116
0
    return 0; /* XXX SSH_ERR_INTERNAL_ERROR ? */
117
118
0
  if ((pklen = sshkey_xmss_pklen(from)) == 0 ||
119
0
      sshkey_xmss_pklen(to) != pklen)
120
0
    return SSH_ERR_INTERNAL_ERROR;
121
0
  if ((to->xmss_pk = malloc(pklen)) == NULL)
122
0
    return SSH_ERR_ALLOC_FAIL;
123
0
  memcpy(to->xmss_pk, from->xmss_pk, pklen);
124
  /* simulate number of signatures left on pubkey */
125
0
  left = sshkey_xmss_signatures_left(from);
126
0
  if (left)
127
0
    sshkey_xmss_enable_maxsign(to, left);
128
0
  return 0;
129
0
}
130
131
static int
132
ssh_xmss_deserialize_public(const char *ktype, struct sshbuf *b,
133
    struct sshkey *key)
134
0
{
135
0
  size_t len = 0;
136
0
  char *xmss_name = NULL;
137
0
  u_char *pk = NULL;
138
0
  int ret = SSH_ERR_INTERNAL_ERROR;
139
140
0
  if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0)
141
0
    goto out;
142
0
  if ((ret = sshkey_xmss_init(key, xmss_name)) != 0)
143
0
    goto out;
144
0
  if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
145
0
    goto out;
146
0
  if (len == 0 || len != sshkey_xmss_pklen(key)) {
147
0
    ret = SSH_ERR_INVALID_FORMAT;
148
0
    goto out;
149
0
  }
150
0
  key->xmss_pk = pk;
151
0
  pk = NULL;
152
0
  if (!sshkey_is_cert(key) &&
153
0
      (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0)
154
0
    goto out;
155
  /* success */
156
0
  ret = 0;
157
0
 out:
158
0
  free(xmss_name);
159
0
  freezero(pk, len);
160
0
  return ret;
161
0
}
162
163
static int
164
ssh_xmss_deserialize_private(const char *ktype, struct sshbuf *b,
165
    struct sshkey *key)
166
0
{
167
0
  int r;
168
0
  char *xmss_name = NULL;
169
0
  size_t pklen = 0, sklen = 0;
170
0
  u_char *xmss_pk = NULL, *xmss_sk = NULL;
171
172
  /* Note: can't reuse ssh_xmss_deserialize_public because of sk order */
173
0
  if ((r = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0 ||
174
0
      (r = sshbuf_get_string(b, &xmss_pk, &pklen)) != 0 ||
175
0
      (r = sshbuf_get_string(b, &xmss_sk, &sklen)) != 0)
176
0
    goto out;
177
0
  if (!sshkey_is_cert(key) &&
178
0
      (r = sshkey_xmss_init(key, xmss_name)) != 0)
179
0
    goto out;
180
0
  if (pklen != sshkey_xmss_pklen(key) ||
181
0
      sklen != sshkey_xmss_sklen(key)) {
182
0
    r = SSH_ERR_INVALID_FORMAT;
183
0
    goto out;
184
0
  }
185
0
  key->xmss_pk = xmss_pk;
186
0
  key->xmss_sk = xmss_sk;
187
0
  xmss_pk = xmss_sk = NULL;
188
  /* optional internal state */
189
0
  if ((r = sshkey_xmss_deserialize_state_opt(key, b)) != 0)
190
0
    goto out;
191
  /* success */
192
0
  r = 0;
193
0
 out:
194
0
  free(xmss_name);
195
0
  freezero(xmss_pk, pklen);
196
0
  freezero(xmss_sk, sklen);
197
0
  return r;
198
0
}
199
200
static int
201
ssh_xmss_sign(struct sshkey *key,
202
    u_char **sigp, size_t *lenp,
203
    const u_char *data, size_t datalen,
204
    const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
205
0
{
206
0
  u_char *sig = NULL;
207
0
  size_t slen = 0, len = 0, required_siglen;
208
0
  unsigned long long smlen;
209
0
  int r, ret;
210
0
  struct sshbuf *b = NULL;
211
212
0
  if (lenp != NULL)
213
0
    *lenp = 0;
214
0
  if (sigp != NULL)
215
0
    *sigp = NULL;
216
217
0
  if (key == NULL ||
218
0
      sshkey_type_plain(key->type) != KEY_XMSS ||
219
0
      key->xmss_sk == NULL ||
220
0
      sshkey_xmss_params(key) == NULL)
221
0
    return SSH_ERR_INVALID_ARGUMENT;
222
0
  if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0)
223
0
    return r;
224
0
  if (datalen >= INT_MAX - required_siglen)
225
0
    return SSH_ERR_INVALID_ARGUMENT;
226
0
  smlen = slen = datalen + required_siglen;
227
0
  if ((sig = malloc(slen)) == NULL)
228
0
    return SSH_ERR_ALLOC_FAIL;
229
0
  if ((r = sshkey_xmss_get_state(key, 1)) != 0)
230
0
    goto out;
231
0
  if ((ret = xmss_sign(key->xmss_sk, sshkey_xmss_bds_state(key), sig, &smlen,
232
0
      data, datalen, sshkey_xmss_params(key))) != 0 || smlen <= datalen) {
233
0
    r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */
234
0
    goto out;
235
0
  }
236
  /* encode signature */
237
0
  if ((b = sshbuf_new()) == NULL) {
238
0
    r = SSH_ERR_ALLOC_FAIL;
239
0
    goto out;
240
0
  }
241
0
  if ((r = sshbuf_put_cstring(b, "ssh-xmss@openssh.com")) != 0 ||
242
0
      (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0)
243
0
    goto out;
244
0
  len = sshbuf_len(b);
245
0
  if (sigp != NULL) {
246
0
    if ((*sigp = malloc(len)) == NULL) {
247
0
      r = SSH_ERR_ALLOC_FAIL;
248
0
      goto out;
249
0
    }
250
0
    memcpy(*sigp, sshbuf_ptr(b), len);
251
0
  }
252
0
  if (lenp != NULL)
253
0
    *lenp = len;
254
  /* success */
255
0
  r = 0;
256
0
 out:
257
0
  if ((ret = sshkey_xmss_update_state(key, 1)) != 0) {
258
    /* discard signature since we cannot update the state */
259
0
    if (r == 0 && sigp != NULL && *sigp != NULL) {
260
0
      explicit_bzero(*sigp, len);
261
0
      free(*sigp);
262
0
    }
263
0
    if (sigp != NULL)
264
0
      *sigp = NULL;
265
0
    if (lenp != NULL)
266
0
      *lenp = 0;
267
0
    r = ret;
268
0
  }
269
0
  sshbuf_free(b);
270
0
  if (sig != NULL)
271
0
    freezero(sig, slen);
272
273
0
  return r;
274
0
}
275
276
static int
277
ssh_xmss_verify(const struct sshkey *key,
278
    const u_char *sig, size_t siglen,
279
    const u_char *data, size_t dlen, const char *alg, u_int compat,
280
    struct sshkey_sig_details **detailsp)
281
0
{
282
0
  struct sshbuf *b = NULL;
283
0
  char *ktype = NULL;
284
0
  const u_char *sigblob;
285
0
  u_char *sm = NULL, *m = NULL;
286
0
  size_t len, required_siglen;
287
0
  unsigned long long smlen = 0, mlen = 0;
288
0
  int r, ret;
289
290
0
  if (key == NULL ||
291
0
      sshkey_type_plain(key->type) != KEY_XMSS ||
292
0
      key->xmss_pk == NULL ||
293
0
      sshkey_xmss_params(key) == NULL ||
294
0
      sig == NULL || siglen == 0)
295
0
    return SSH_ERR_INVALID_ARGUMENT;
296
0
  if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0)
297
0
    return r;
298
0
  if (dlen >= INT_MAX - required_siglen)
299
0
    return SSH_ERR_INVALID_ARGUMENT;
300
301
0
  if ((b = sshbuf_from(sig, siglen)) == NULL)
302
0
    return SSH_ERR_ALLOC_FAIL;
303
0
  if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
304
0
      (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
305
0
    goto out;
306
0
  if (strcmp("ssh-xmss@openssh.com", ktype) != 0) {
307
0
    r = SSH_ERR_KEY_TYPE_MISMATCH;
308
0
    goto out;
309
0
  }
310
0
  if (sshbuf_len(b) != 0) {
311
0
    r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
312
0
    goto out;
313
0
  }
314
0
  if (len != required_siglen) {
315
0
    r = SSH_ERR_INVALID_FORMAT;
316
0
    goto out;
317
0
  }
318
0
  if (dlen >= SIZE_MAX - len) {
319
0
    r = SSH_ERR_INVALID_ARGUMENT;
320
0
    goto out;
321
0
  }
322
0
  smlen = len + dlen;
323
0
  mlen = smlen;
324
0
  if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) {
325
0
    r = SSH_ERR_ALLOC_FAIL;
326
0
    goto out;
327
0
  }
328
0
  memcpy(sm, sigblob, len);
329
0
  memcpy(sm+len, data, dlen);
330
0
  if ((ret = xmss_sign_open(m, &mlen, sm, smlen,
331
0
      key->xmss_pk, sshkey_xmss_params(key))) != 0) {
332
0
    debug2_f("xmss_sign_open failed: %d", ret);
333
0
  }
334
0
  if (ret != 0 || mlen != dlen) {
335
0
    r = SSH_ERR_SIGNATURE_INVALID;
336
0
    goto out;
337
0
  }
338
  /* XXX compare 'm' and 'data' ? */
339
  /* success */
340
0
  r = 0;
341
0
 out:
342
0
  if (sm != NULL)
343
0
    freezero(sm, smlen);
344
0
  if (m != NULL)
345
0
    freezero(m, smlen);
346
0
  sshbuf_free(b);
347
0
  free(ktype);
348
0
  return r;
349
0
}
350
351
static const struct sshkey_impl_funcs sshkey_xmss_funcs = {
352
  /* .size = */   NULL,
353
  /* .alloc = */    NULL,
354
  /* .cleanup = */  ssh_xmss_cleanup,
355
  /* .equal = */    ssh_xmss_equal,
356
  /* .ssh_serialize_public = */ ssh_xmss_serialize_public,
357
  /* .ssh_deserialize_public = */ ssh_xmss_deserialize_public,
358
  /* .ssh_serialize_private = */ ssh_xmss_serialize_private,
359
  /* .ssh_deserialize_private = */ ssh_xmss_deserialize_private,
360
  /* .generate = */ sshkey_xmss_generate_private_key,
361
  /* .copy_public = */  ssh_xmss_copy_public,
362
  /* .sign = */   ssh_xmss_sign,
363
  /* .verify = */   ssh_xmss_verify,
364
};
365
366
const struct sshkey_impl sshkey_xmss_impl = {
367
  /* .name = */   "ssh-xmss@openssh.com",
368
  /* .shortname = */  "XMSS",
369
  /* .sigalg = */   NULL,
370
  /* .type = */   KEY_XMSS,
371
  /* .nid = */    0,
372
  /* .cert = */   0,
373
  /* .sigonly = */  0,
374
  /* .keybits = */  256,
375
  /* .funcs = */    &sshkey_xmss_funcs,
376
};
377
378
const struct sshkey_impl sshkey_xmss_cert_impl = {
379
  /* .name = */   "ssh-xmss-cert-v01@openssh.com",
380
  /* .shortname = */  "XMSS-CERT",
381
  /* .sigalg = */   NULL,
382
  /* .type = */   KEY_XMSS_CERT,
383
  /* .nid = */    0,
384
  /* .cert = */   1,
385
  /* .sigonly = */  0,
386
  /* .keybits = */  256,
387
  /* .funcs = */    &sshkey_xmss_funcs,
388
};
389
#endif /* WITH_XMSS */