1 /* $OpenBSD: ssh-ed25519.c,v 1.20 2025/07/24 06:12:08 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 #define SSHKEY_INTERNAL 18 #include "includes.h" 19 __RCSID("$NetBSD: ssh-ed25519.c,v 1.11 2025/10/11 15:45:07 christos Exp $"); 20 21 #include <sys/types.h> 22 #include <limits.h> 23 24 #include "crypto_api.h" 25 26 #include <string.h> 27 #include <stdarg.h> 28 29 #include "log.h" 30 #include "sshbuf.h" 31 #include "sshkey.h" 32 #include "ssherr.h" 33 #include "ssh.h" 34 35 static void 36 ssh_ed25519_cleanup(struct sshkey *k) 37 { 38 freezero(k->ed25519_pk, ED25519_PK_SZ); 39 freezero(k->ed25519_sk, ED25519_SK_SZ); 40 k->ed25519_pk = NULL; 41 k->ed25519_sk = NULL; 42 } 43 44 static int 45 ssh_ed25519_equal(const struct sshkey *a, const struct sshkey *b) 46 { 47 if (a->ed25519_pk == NULL || b->ed25519_pk == NULL) 48 return 0; 49 if (memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) != 0) 50 return 0; 51 return 1; 52 } 53 54 static int 55 ssh_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b, 56 enum sshkey_serialize_rep opts) 57 { 58 int r; 59 60 if (key->ed25519_pk == NULL) 61 return SSH_ERR_INVALID_ARGUMENT; 62 if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0) 63 return r; 64 65 return 0; 66 } 67 68 static int 69 ssh_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b, 70 enum sshkey_serialize_rep opts) 71 { 72 int r; 73 74 if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0 || 75 (r = sshbuf_put_string(b, key->ed25519_sk, ED25519_SK_SZ)) != 0) 76 return r; 77 78 return 0; 79 } 80 81 static int 82 ssh_ed25519_generate(struct sshkey *k, int bits) 83 { 84 if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL || 85 (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) 86 return SSH_ERR_ALLOC_FAIL; 87 crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); 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 = NULL; 152 size_t slen = 0; 153 unsigned long long smlen; 154 int r, ret; 155 156 if (lenp != NULL) 157 *lenp = 0; 158 if (sigp != NULL) 159 *sigp = NULL; 160 161 if (key == NULL || 162 sshkey_type_plain(key->type) != KEY_ED25519 || 163 key->ed25519_sk == NULL || 164 datalen >= INT_MAX - crypto_sign_ed25519_BYTES) 165 return SSH_ERR_INVALID_ARGUMENT; 166 smlen = slen = datalen + crypto_sign_ed25519_BYTES; 167 if ((sig = malloc(slen)) == NULL) 168 return SSH_ERR_ALLOC_FAIL; 169 170 if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, 171 key->ed25519_sk)) != 0 || smlen <= datalen) { 172 r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ 173 goto out; 174 } 175 if ((r = ssh_ed25519_encode_store_sig(sig, smlen - datalen, 176 sigp, lenp)) != 0) 177 goto out; 178 179 /* success */ 180 r = 0; 181 out: 182 freezero(sig, slen); 183 return r; 184 } 185 186 int 187 ssh_ed25519_encode_store_sig(const u_char *sig, size_t slen, 188 u_char **sigp, size_t *lenp) 189 { 190 struct sshbuf *b = NULL; 191 int r = -1; 192 size_t len; 193 194 if (lenp != NULL) 195 *lenp = 0; 196 if (sigp != NULL) 197 *sigp = NULL; 198 199 if (slen != crypto_sign_ed25519_BYTES) 200 return SSH_ERR_INVALID_ARGUMENT; 201 202 /* encode signature */ 203 if ((b = sshbuf_new()) == NULL) { 204 r = SSH_ERR_ALLOC_FAIL; 205 goto out; 206 } 207 if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 || 208 (r = sshbuf_put_string(b, sig, slen)) != 0) 209 goto out; 210 len = sshbuf_len(b); 211 if (sigp != NULL) { 212 if ((*sigp = malloc(len)) == NULL) { 213 r = SSH_ERR_ALLOC_FAIL; 214 goto out; 215 } 216 memcpy(*sigp, sshbuf_ptr(b), len); 217 } 218 if (lenp != NULL) 219 *lenp = len; 220 /* success */ 221 r = 0; 222 out: 223 sshbuf_free(b); 224 return r; 225 } 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 { 233 struct sshbuf *b = NULL; 234 char *ktype = NULL; 235 const u_char *sigblob; 236 u_char *sm = NULL, *m = NULL; 237 size_t len; 238 unsigned long long smlen = 0, mlen = 0; 239 int r, ret; 240 241 if (key == NULL || 242 sshkey_type_plain(key->type) != KEY_ED25519 || 243 key->ed25519_pk == NULL || 244 dlen >= INT_MAX - crypto_sign_ed25519_BYTES || 245 sig == NULL || siglen == 0) 246 return SSH_ERR_INVALID_ARGUMENT; 247 248 if ((b = sshbuf_from(sig, siglen)) == NULL) 249 return SSH_ERR_ALLOC_FAIL; 250 if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || 251 (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) 252 goto out; 253 if (strcmp("ssh-ed25519", ktype) != 0) { 254 r = SSH_ERR_KEY_TYPE_MISMATCH; 255 goto out; 256 } 257 if (sshbuf_len(b) != 0) { 258 r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 259 goto out; 260 } 261 if (len > crypto_sign_ed25519_BYTES) { 262 r = SSH_ERR_INVALID_FORMAT; 263 goto out; 264 } 265 if (dlen >= SIZE_MAX - len) { 266 r = SSH_ERR_INVALID_ARGUMENT; 267 goto out; 268 } 269 smlen = len + dlen; 270 mlen = smlen; 271 if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { 272 r = SSH_ERR_ALLOC_FAIL; 273 goto out; 274 } 275 memcpy(sm, sigblob, len); 276 memcpy(sm+len, data, dlen); 277 if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, 278 key->ed25519_pk)) != 0) { 279 debug2_f("crypto_sign_ed25519_open failed: %d", ret); 280 } 281 if (ret != 0 || mlen != dlen) { 282 r = SSH_ERR_SIGNATURE_INVALID; 283 goto out; 284 } 285 /* XXX compare 'm' and 'data' ? */ 286 /* success */ 287 r = 0; 288 out: 289 if (sm != NULL) 290 freezero(sm, smlen); 291 if (m != NULL) 292 freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ 293 sshbuf_free(b); 294 free(ktype); 295 return r; 296 } 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 (at) 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 }; 337