Coverage Report

Created: 2023-06-07 07:08

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