Coverage Report

Created: 2025-10-13 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssh/ssh-ed25519.c
Line
Count
Source
1
/* $OpenBSD: ssh-ed25519.c,v 1.20 2025/07/24 06:12:08 djm Exp $ */
2
/*
3
 * Copyright (c) 2013 Markus Friedl <markus@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include "includes.h"
19
20
#include <sys/types.h>
21
#include <limits.h>
22
23
#include "crypto_api.h"
24
25
#include <string.h>
26
#include <stdarg.h>
27
28
#include "log.h"
29
#include "sshbuf.h"
30
#define SSHKEY_INTERNAL
31
#include "sshkey.h"
32
#include "ssherr.h"
33
#include "ssh.h"
34
35
static void
36
ssh_ed25519_cleanup(struct sshkey *k)
37
2.26k
{
38
2.26k
  freezero(k->ed25519_pk, ED25519_PK_SZ);
39
2.26k
  freezero(k->ed25519_sk, ED25519_SK_SZ);
40
2.26k
  k->ed25519_pk = NULL;
41
2.26k
  k->ed25519_sk = NULL;
42
2.26k
}
43
44
static int
45
ssh_ed25519_equal(const struct sshkey *a, const struct sshkey *b)
46
0
{
47
0
  if (a->ed25519_pk == NULL || b->ed25519_pk == NULL)
48
0
    return 0;
49
0
  if (memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) != 0)
50
0
    return 0;
51
0
  return 1;
52
0
}
53
54
static int
55
ssh_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b,
56
    enum sshkey_serialize_rep opts)
57
0
{
58
0
  int r;
59
60
0
  if (key->ed25519_pk == NULL)
61
0
    return SSH_ERR_INVALID_ARGUMENT;
62
0
  if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0)
63
0
    return r;
64
65
0
  return 0;
66
0
}
67
68
static int
69
ssh_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b,
70
    enum sshkey_serialize_rep opts)
71
0
{
72
0
  int r;
73
74
0
  if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0 ||
75
0
      (r = sshbuf_put_string(b, key->ed25519_sk, ED25519_SK_SZ)) != 0)
76
0
    return r;
77
78
0
  return 0;
79
0
}
80
81
static int
82
ssh_ed25519_generate(struct sshkey *k, int bits)
83
0
{
84
0
  if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL ||
85
0
      (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL)
86
0
    return SSH_ERR_ALLOC_FAIL;
87
0
  crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
88
0
  return 0;
89
0
}
90
91
static int
92
ssh_ed25519_copy_public(const struct sshkey *from, struct sshkey *to)
93
0
{
94
0
  if (from->ed25519_pk == NULL)
95
0
    return 0; /* XXX SSH_ERR_INTERNAL_ERROR ? */
96
0
  if ((to->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL)
97
0
    return SSH_ERR_ALLOC_FAIL;
98
0
  memcpy(to->ed25519_pk, from->ed25519_pk, ED25519_PK_SZ);
99
0
  return 0;
100
0
}
101
102
static int
103
ssh_ed25519_deserialize_public(const char *ktype, struct sshbuf *b,
104
    struct sshkey *key)
105
2.39k
{
106
2.39k
  u_char *pk = NULL;
107
2.39k
  size_t len = 0;
108
2.39k
  int r;
109
110
2.39k
  if ((r = sshbuf_get_string(b, &pk, &len)) != 0)
111
60
    return r;
112
2.33k
  if (len != ED25519_PK_SZ) {
113
74
    freezero(pk, len);
114
74
    return SSH_ERR_INVALID_FORMAT;
115
74
  }
116
2.25k
  key->ed25519_pk = pk;
117
2.25k
  return 0;
118
2.33k
}
119
120
static int
121
ssh_ed25519_deserialize_private(const char *ktype, struct sshbuf *b,
122
    struct sshkey *key)
123
265
{
124
265
  int r;
125
265
  size_t sklen = 0;
126
265
  u_char *ed25519_sk = NULL;
127
128
265
  if ((r = ssh_ed25519_deserialize_public(NULL, b, key)) != 0)
129
106
    goto out;
130
159
  if ((r = sshbuf_get_string(b, &ed25519_sk, &sklen)) != 0)
131
7
    goto out;
132
152
  if (sklen != ED25519_SK_SZ) {
133
24
    r = SSH_ERR_INVALID_FORMAT;
134
24
    goto out;
135
24
  }
136
128
  key->ed25519_sk = ed25519_sk;
137
128
  ed25519_sk = NULL; /* transferred */
138
  /* success */
139
128
  r = 0;
140
265
 out:
141
265
  freezero(ed25519_sk, sklen);
142
265
  return r;
143
128
}
144
145
static int
146
ssh_ed25519_sign(struct sshkey *key,
147
    u_char **sigp, size_t *lenp,
148
    const u_char *data, size_t datalen,
149
    const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
150
0
{
151
0
  u_char *sig = NULL;
152
0
  size_t slen = 0;
153
0
  unsigned long long smlen;
154
0
  int r, ret;
155
156
0
  if (lenp != NULL)
157
0
    *lenp = 0;
158
0
  if (sigp != NULL)
159
0
    *sigp = NULL;
160
161
0
  if (key == NULL ||
162
0
      sshkey_type_plain(key->type) != KEY_ED25519 ||
163
0
      key->ed25519_sk == NULL ||
164
0
      datalen >= INT_MAX - crypto_sign_ed25519_BYTES)
165
0
    return SSH_ERR_INVALID_ARGUMENT;
166
0
  smlen = slen = datalen + crypto_sign_ed25519_BYTES;
167
0
  if ((sig = malloc(slen)) == NULL)
168
0
    return SSH_ERR_ALLOC_FAIL;
169
170
0
  if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen,
171
0
      key->ed25519_sk)) != 0 || smlen <= datalen) {
172
0
    r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */
173
0
    goto out;
174
0
  }
175
0
  if ((r = ssh_ed25519_encode_store_sig(sig, smlen - datalen,
176
0
      sigp, lenp)) != 0)
177
0
    goto out;
178
179
  /* success */
180
0
  r = 0;
181
0
 out:
182
0
  freezero(sig, slen);
183
0
  return r;
184
0
}
185
186
int
187
ssh_ed25519_encode_store_sig(const u_char *sig, size_t slen,
188
    u_char **sigp, size_t *lenp)
189
0
{
190
0
  struct sshbuf *b = NULL;
191
0
  int r = -1;
192
0
  size_t len;
193
194
0
  if (lenp != NULL)
195
0
    *lenp = 0;
196
0
  if (sigp != NULL)
197
0
    *sigp = NULL;
198
199
0
  if (slen != crypto_sign_ed25519_BYTES)
200
0
    return SSH_ERR_INVALID_ARGUMENT;
201
202
  /* encode signature */
203
0
  if ((b = sshbuf_new()) == NULL) {
204
0
    r = SSH_ERR_ALLOC_FAIL;
205
0
    goto out;
206
0
  }
207
0
  if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 ||
208
0
      (r = sshbuf_put_string(b, sig, slen)) != 0)
209
0
    goto out;
210
0
  len = sshbuf_len(b);
211
0
  if (sigp != NULL) {
212
0
    if ((*sigp = malloc(len)) == NULL) {
213
0
      r = SSH_ERR_ALLOC_FAIL;
214
0
      goto out;
215
0
    }
216
0
    memcpy(*sigp, sshbuf_ptr(b), len);
217
0
  }
218
0
  if (lenp != NULL)
219
0
    *lenp = len;
220
  /* success */
221
0
  r = 0;
222
0
 out:
223
0
  sshbuf_free(b);
224
0
  return r;
225
0
}
226
227
static int
228
ssh_ed25519_verify(const struct sshkey *key,
229
    const u_char *sig, size_t siglen,
230
    const u_char *data, size_t dlen, const char *alg, u_int compat,
231
    struct sshkey_sig_details **detailsp)
232
179
{
233
179
  struct sshbuf *b = NULL;
234
179
  char *ktype = NULL;
235
179
  const u_char *sigblob;
236
179
  u_char *sm = NULL, *m = NULL;
237
179
  size_t len;
238
179
  unsigned long long smlen = 0, mlen = 0;
239
179
  int r, ret;
240
241
179
  if (key == NULL ||
242
179
      sshkey_type_plain(key->type) != KEY_ED25519 ||
243
179
      key->ed25519_pk == NULL ||
244
179
      dlen >= INT_MAX - crypto_sign_ed25519_BYTES ||
245
179
      sig == NULL || siglen == 0)
246
0
    return SSH_ERR_INVALID_ARGUMENT;
247
248
179
  if ((b = sshbuf_from(sig, siglen)) == NULL)
249
0
    return SSH_ERR_ALLOC_FAIL;
250
179
  if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
251
156
      (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
252
25
    goto out;
253
154
  if (strcmp("ssh-ed25519", ktype) != 0) {
254
99
    r = SSH_ERR_KEY_TYPE_MISMATCH;
255
99
    goto out;
256
99
  }
257
55
  if (sshbuf_len(b) != 0) {
258
2
    r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
259
2
    goto out;
260
2
  }
261
53
  if (len > crypto_sign_ed25519_BYTES) {
262
1
    r = SSH_ERR_INVALID_FORMAT;
263
1
    goto out;
264
1
  }
265
52
  if (dlen >= SIZE_MAX - len) {
266
0
    r = SSH_ERR_INVALID_ARGUMENT;
267
0
    goto out;
268
0
  }
269
52
  smlen = len + dlen;
270
52
  mlen = smlen;
271
52
  if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) {
272
0
    r = SSH_ERR_ALLOC_FAIL;
273
0
    goto out;
274
0
  }
275
52
  memcpy(sm, sigblob, len);
276
52
  memcpy(sm+len, data, dlen);
277
52
  if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
278
52
      key->ed25519_pk)) != 0) {
279
50
    debug2_f("crypto_sign_ed25519_open failed: %d", ret);
280
50
  }
281
52
  if (ret != 0 || mlen != dlen) {
282
52
    r = SSH_ERR_SIGNATURE_INVALID;
283
52
    goto out;
284
52
  }
285
  /* XXX compare 'm' and 'data' ? */
286
  /* success */
287
0
  r = 0;
288
179
 out:
289
179
  if (sm != NULL)
290
52
    freezero(sm, smlen);
291
179
  if (m != NULL)
292
52
    freezero(m, smlen); /* NB mlen may be invalid if r != 0 */
293
179
  sshbuf_free(b);
294
179
  free(ktype);
295
179
  return r;
296
0
}
297
298
/* NB. not static; used by ED25519-SK */
299
const struct sshkey_impl_funcs sshkey_ed25519_funcs = {
300
  /* .size = */   NULL,
301
  /* .alloc = */    NULL,
302
  /* .cleanup = */  ssh_ed25519_cleanup,
303
  /* .equal = */    ssh_ed25519_equal,
304
  /* .ssh_serialize_public = */ ssh_ed25519_serialize_public,
305
  /* .ssh_deserialize_public = */ ssh_ed25519_deserialize_public,
306
  /* .ssh_serialize_private = */ ssh_ed25519_serialize_private,
307
  /* .ssh_deserialize_private = */ ssh_ed25519_deserialize_private,
308
  /* .generate = */ ssh_ed25519_generate,
309
  /* .copy_public = */  ssh_ed25519_copy_public,
310
  /* .sign = */   ssh_ed25519_sign,
311
  /* .verify = */   ssh_ed25519_verify,
312
};
313
314
const struct sshkey_impl sshkey_ed25519_impl = {
315
  /* .name = */   "ssh-ed25519",
316
  /* .shortname = */  "ED25519",
317
  /* .sigalg = */   NULL,
318
  /* .type = */   KEY_ED25519,
319
  /* .nid = */    0,
320
  /* .cert = */   0,
321
  /* .sigonly = */  0,
322
  /* .keybits = */  256,
323
  /* .funcs = */    &sshkey_ed25519_funcs,
324
};
325
326
const struct sshkey_impl sshkey_ed25519_cert_impl = {
327
  /* .name = */   "ssh-ed25519-cert-v01@openssh.com",
328
  /* .shortname = */  "ED25519-CERT",
329
  /* .sigalg = */   NULL,
330
  /* .type = */   KEY_ED25519_CERT,
331
  /* .nid = */    0,
332
  /* .cert = */   1,
333
  /* .sigonly = */  0,
334
  /* .keybits = */  256,
335
  /* .funcs = */    &sshkey_ed25519_funcs,
336
};