Coverage Report

Created: 2026-02-26 06:46

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.22 2026/02/14 00:18:34 jsg 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
34
static void
35
ssh_ed25519_cleanup(struct sshkey *k)
36
32.3k
{
37
32.3k
  freezero(k->ed25519_pk, ED25519_PK_SZ);
38
32.3k
  freezero(k->ed25519_sk, ED25519_SK_SZ);
39
32.3k
  k->ed25519_pk = NULL;
40
32.3k
  k->ed25519_sk = NULL;
41
32.3k
}
42
43
static int
44
ssh_ed25519_equal(const struct sshkey *a, const struct sshkey *b)
45
269
{
46
269
  if (a->ed25519_pk == NULL || b->ed25519_pk == NULL)
47
0
    return 0;
48
269
  if (memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) != 0)
49
91
    return 0;
50
178
  return 1;
51
269
}
52
53
static int
54
ssh_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b,
55
    enum sshkey_serialize_rep opts)
56
99
{
57
99
  int r;
58
59
99
  if (key->ed25519_pk == NULL)
60
0
    return SSH_ERR_INVALID_ARGUMENT;
61
99
  if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0)
62
0
    return r;
63
64
99
  return 0;
65
99
}
66
67
static int
68
ssh_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b,
69
    enum sshkey_serialize_rep opts)
70
0
{
71
0
  int r;
72
73
0
  if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0 ||
74
0
      (r = sshbuf_put_string(b, key->ed25519_sk, ED25519_SK_SZ)) != 0)
75
0
    return r;
76
77
0
  return 0;
78
0
}
79
80
static int
81
ssh_ed25519_generate(struct sshkey *k, int bits)
82
0
{
83
0
  if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL ||
84
0
      (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL)
85
0
    return SSH_ERR_ALLOC_FAIL;
86
0
  crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
87
0
  return 0;
88
0
}
89
90
static int
91
ssh_ed25519_copy_public(const struct sshkey *from, struct sshkey *to)
92
32.0k
{
93
32.0k
  if (from->ed25519_pk == NULL)
94
0
    return 0; /* XXX SSH_ERR_INTERNAL_ERROR ? */
95
32.0k
  if ((to->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL)
96
0
    return SSH_ERR_ALLOC_FAIL;
97
32.0k
  memcpy(to->ed25519_pk, from->ed25519_pk, ED25519_PK_SZ);
98
32.0k
  return 0;
99
32.0k
}
100
101
static int
102
ssh_ed25519_deserialize_public(const char *ktype, struct sshbuf *b,
103
    struct sshkey *key)
104
284
{
105
284
  u_char *pk = NULL;
106
284
  size_t len = 0;
107
284
  int r;
108
109
284
  if ((r = sshbuf_get_string(b, &pk, &len)) != 0)
110
4
    return r;
111
280
  if (len != ED25519_PK_SZ) {
112
5
    freezero(pk, len);
113
5
    return SSH_ERR_INVALID_FORMAT;
114
5
  }
115
275
  key->ed25519_pk = pk;
116
275
  return 0;
117
280
}
118
119
static int
120
ssh_ed25519_deserialize_private(const char *ktype, struct sshbuf *b,
121
    struct sshkey *key)
122
1
{
123
1
  int r;
124
1
  size_t sklen = 0;
125
1
  u_char *ed25519_sk = NULL;
126
127
1
  if ((r = ssh_ed25519_deserialize_public(NULL, b, key)) != 0)
128
0
    goto out;
129
1
  if ((r = sshbuf_get_string(b, &ed25519_sk, &sklen)) != 0)
130
0
    goto out;
131
1
  if (sklen != ED25519_SK_SZ) {
132
0
    r = SSH_ERR_INVALID_FORMAT;
133
0
    goto out;
134
0
  }
135
1
  key->ed25519_sk = ed25519_sk;
136
1
  ed25519_sk = NULL; /* transferred */
137
  /* success */
138
1
  r = 0;
139
1
 out:
140
1
  freezero(ed25519_sk, sklen);
141
1
  return r;
142
1
}
143
144
static int
145
ssh_ed25519_sign(struct sshkey *key,
146
    u_char **sigp, size_t *lenp,
147
    const u_char *data, size_t datalen,
148
    const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
149
99
{
150
99
  u_char *sig = NULL;
151
99
  size_t slen = 0;
152
99
  unsigned long long smlen;
153
99
  int r, ret;
154
155
99
  if (lenp != NULL)
156
99
    *lenp = 0;
157
99
  if (sigp != NULL)
158
99
    *sigp = NULL;
159
160
99
  if (key == NULL ||
161
99
      sshkey_type_plain(key->type) != KEY_ED25519 ||
162
99
      key->ed25519_sk == NULL ||
163
99
      datalen >= INT_MAX - crypto_sign_ed25519_BYTES)
164
0
    return SSH_ERR_INVALID_ARGUMENT;
165
99
  smlen = slen = datalen + crypto_sign_ed25519_BYTES;
166
99
  if ((sig = malloc(slen)) == NULL)
167
0
    return SSH_ERR_ALLOC_FAIL;
168
169
99
  if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen,
170
99
      key->ed25519_sk)) != 0 || smlen <= datalen) {
171
0
    r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */
172
0
    goto out;
173
0
  }
174
99
  if ((r = ssh_ed25519_encode_store_sig(sig, smlen - datalen,
175
99
      sigp, lenp)) != 0)
176
0
    goto out;
177
178
  /* success */
179
99
  r = 0;
180
99
 out:
181
99
  freezero(sig, slen);
182
99
  return r;
183
99
}
184
185
int
186
ssh_ed25519_encode_store_sig(const u_char *sig, size_t slen,
187
    u_char **sigp, size_t *lenp)
188
99
{
189
99
  struct sshbuf *b = NULL;
190
99
  int r = -1;
191
99
  size_t len;
192
193
99
  if (lenp != NULL)
194
99
    *lenp = 0;
195
99
  if (sigp != NULL)
196
99
    *sigp = NULL;
197
198
99
  if (slen != crypto_sign_ed25519_BYTES)
199
0
    return SSH_ERR_INVALID_ARGUMENT;
200
201
  /* encode signature */
202
99
  if ((b = sshbuf_new()) == NULL) {
203
0
    r = SSH_ERR_ALLOC_FAIL;
204
0
    goto out;
205
0
  }
206
99
  if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 ||
207
99
      (r = sshbuf_put_string(b, sig, slen)) != 0)
208
0
    goto out;
209
99
  len = sshbuf_len(b);
210
99
  if (sigp != NULL) {
211
99
    if ((*sigp = malloc(len)) == NULL) {
212
0
      r = SSH_ERR_ALLOC_FAIL;
213
0
      goto out;
214
0
    }
215
99
    memcpy(*sigp, sshbuf_ptr(b), len);
216
99
  }
217
99
  if (lenp != NULL)
218
99
    *lenp = len;
219
  /* success */
220
99
  r = 0;
221
99
 out:
222
99
  sshbuf_free(b);
223
99
  return r;
224
99
}
225
226
static int
227
ssh_ed25519_verify(const struct sshkey *key,
228
    const u_char *sig, size_t siglen,
229
    const u_char *data, size_t dlen, const char *alg, u_int compat,
230
    struct sshkey_sig_details **detailsp)
231
153
{
232
153
  struct sshbuf *b = NULL;
233
153
  char *ktype = NULL;
234
153
  const u_char *sigblob;
235
153
  u_char *sm = NULL, *m = NULL;
236
153
  size_t len;
237
153
  unsigned long long smlen = 0, mlen = 0;
238
153
  int r, ret;
239
240
153
  if (key == NULL ||
241
153
      sshkey_type_plain(key->type) != KEY_ED25519 ||
242
153
      key->ed25519_pk == NULL ||
243
153
      dlen >= INT_MAX - crypto_sign_ed25519_BYTES ||
244
153
      sig == NULL || siglen == 0)
245
0
    return SSH_ERR_INVALID_ARGUMENT;
246
247
153
  if ((b = sshbuf_from(sig, siglen)) == NULL)
248
0
    return SSH_ERR_ALLOC_FAIL;
249
153
  if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
250
146
      (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
251
56
    goto out;
252
97
  if (strcmp("ssh-ed25519", ktype) != 0) {
253
84
    r = SSH_ERR_KEY_TYPE_MISMATCH;
254
84
    goto out;
255
84
  }
256
13
  if (sshbuf_len(b) != 0) {
257
3
    r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
258
3
    goto out;
259
3
  }
260
10
  if (len > crypto_sign_ed25519_BYTES) {
261
0
    r = SSH_ERR_INVALID_FORMAT;
262
0
    goto out;
263
0
  }
264
10
  if (dlen >= SIZE_MAX - len) {
265
0
    r = SSH_ERR_INVALID_ARGUMENT;
266
0
    goto out;
267
0
  }
268
10
  smlen = len + dlen;
269
10
  mlen = smlen;
270
10
  if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) {
271
0
    r = SSH_ERR_ALLOC_FAIL;
272
0
    goto out;
273
0
  }
274
10
  memcpy(sm, sigblob, len);
275
10
  memcpy(sm+len, data, dlen);
276
10
  if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
277
10
      key->ed25519_pk)) != 0) {
278
10
    debug2_f("crypto_sign_ed25519_open failed: %d", ret);
279
10
  }
280
10
  if (ret != 0 || mlen != dlen) {
281
10
    r = SSH_ERR_SIGNATURE_INVALID;
282
10
    goto out;
283
10
  }
284
  /* XXX compare 'm' and 'data' ? */
285
  /* success */
286
0
  r = 0;
287
153
 out:
288
153
  if (sm != NULL)
289
10
    freezero(sm, smlen);
290
153
  if (m != NULL)
291
10
    freezero(m, smlen); /* NB mlen may be invalid if r != 0 */
292
153
  sshbuf_free(b);
293
153
  free(ktype);
294
153
  return r;
295
0
}
296
297
/* NB. not static; used by ED25519-SK */
298
const struct sshkey_impl_funcs sshkey_ed25519_funcs = {
299
  /* .size = */   NULL,
300
  /* .alloc = */    NULL,
301
  /* .cleanup = */  ssh_ed25519_cleanup,
302
  /* .equal = */    ssh_ed25519_equal,
303
  /* .ssh_serialize_public = */ ssh_ed25519_serialize_public,
304
  /* .ssh_deserialize_public = */ ssh_ed25519_deserialize_public,
305
  /* .ssh_serialize_private = */ ssh_ed25519_serialize_private,
306
  /* .ssh_deserialize_private = */ ssh_ed25519_deserialize_private,
307
  /* .generate = */ ssh_ed25519_generate,
308
  /* .copy_public = */  ssh_ed25519_copy_public,
309
  /* .sign = */   ssh_ed25519_sign,
310
  /* .verify = */   ssh_ed25519_verify,
311
};
312
313
const struct sshkey_impl sshkey_ed25519_impl = {
314
  /* .name = */   "ssh-ed25519",
315
  /* .shortname = */  "ED25519",
316
  /* .sigalg = */   NULL,
317
  /* .type = */   KEY_ED25519,
318
  /* .nid = */    0,
319
  /* .cert = */   0,
320
  /* .sigonly = */  0,
321
  /* .keybits = */  256,
322
  /* .funcs = */    &sshkey_ed25519_funcs,
323
};
324
325
const struct sshkey_impl sshkey_ed25519_cert_impl = {
326
  /* .name = */   "ssh-ed25519-cert-v01@openssh.com",
327
  /* .shortname = */  "ED25519-CERT",
328
  /* .sigalg = */   NULL,
329
  /* .type = */   KEY_ED25519_CERT,
330
  /* .nid = */    0,
331
  /* .cert = */   1,
332
  /* .sigonly = */  0,
333
  /* .keybits = */  256,
334
  /* .funcs = */    &sshkey_ed25519_funcs,
335
};