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