1 /* $NetBSD: ssh-ed25519-sk.c,v 1.7 2026/10/07 17:32:08 christos Exp $ */ 2 /* $OpenBSD: ssh-ed25519-sk.c,v 1.17 2026/09/16 00:43:00 djm Exp $ */ 3 4 /* 5 * Copyright (c) 2019 Markus Friedl. All rights reserved. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 #include "includes.h" 20 __RCSID("$NetBSD: ssh-ed25519-sk.c,v 1.7 2026/10/07 17:32:08 christos Exp $"); 21 22 /* #define DEBUG_SK 1 */ 23 24 #define SSHKEY_INTERNAL 25 #include <sys/types.h> 26 27 #include "crypto_api.h" 28 29 #include <string.h> 30 #include <stdarg.h> 31 32 #include "log.h" 33 #include "sshbuf.h" 34 #include "sshkey.h" 35 #include "ssherr.h" 36 #include "digest.h" 37 38 /* Reuse some ED25519 internals */ 39 extern struct sshkey_impl_funcs sshkey_ed25519_funcs; 40 41 static void 42 ssh_ed25519_sk_cleanup(struct sshkey *k) 43 { 44 sshkey_sk_cleanup(k); 45 sshkey_ed25519_funcs.cleanup(k); 46 } 47 48 static int 49 ssh_ed25519_sk_equal(const struct sshkey *a, const struct sshkey *b) 50 { 51 if (!sshkey_sk_fields_equal(a, b)) 52 return 0; 53 if (!sshkey_ed25519_funcs.equal(a, b)) 54 return 0; 55 return 1; 56 } 57 58 static int 59 ssh_ed25519_sk_serialize_public(const struct sshkey *key, struct sshbuf *b, 60 enum sshkey_serialize_rep opts) 61 { 62 int r; 63 64 if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0) 65 return r; 66 if ((r = sshkey_serialize_sk(key, b)) != 0) 67 return r; 68 69 return 0; 70 } 71 72 static int 73 ssh_ed25519_sk_serialize_private(const struct sshkey *key, struct sshbuf *b, 74 enum sshkey_serialize_rep opts) 75 { 76 int r; 77 78 if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0) 79 return r; 80 if ((r = sshkey_serialize_private_sk(key, b)) != 0) 81 return r; 82 83 return 0; 84 } 85 86 static int 87 ssh_ed25519_sk_copy_public(const struct sshkey *from, struct sshkey *to) 88 { 89 int r; 90 91 if ((r = sshkey_ed25519_funcs.copy_public(from, to)) != 0) 92 return r; 93 if ((r = sshkey_copy_public_sk(from, to)) != 0) 94 return r; 95 return 0; 96 } 97 98 static int 99 ssh_ed25519_sk_deserialize_public(const char *ktype, struct sshbuf *b, 100 struct sshkey *key) 101 { 102 int r; 103 104 if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0) 105 return r; 106 if ((r = sshkey_deserialize_sk(b, key)) != 0) 107 return r; 108 return 0; 109 } 110 111 static int 112 ssh_ed25519_sk_deserialize_private(const char *ktype, struct sshbuf *b, 113 struct sshkey *key) 114 { 115 int r; 116 117 if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0) 118 return r; 119 if ((r = sshkey_private_deserialize_sk(b, key)) != 0) 120 return r; 121 return 0; 122 } 123 124 static int 125 ssh_ed25519_sk_verify(const struct sshkey *key, 126 const u_char *sig, size_t siglen, 127 const u_char *data, size_t dlen, const char *alg, u_int compat, 128 struct sshkey_sig_details **detailsp) 129 { 130 struct sshbuf *b = NULL; 131 struct sshbuf *encoded = NULL; 132 char *ktype = NULL; 133 const u_char *sigblob; 134 u_char apphash[32]; 135 u_char msghash[32]; 136 u_char sig_flags; 137 u_int sig_counter; 138 size_t len; 139 int r = SSH_ERR_INTERNAL_ERROR; 140 int ret; 141 struct sshkey_sig_details *details = NULL; 142 143 if (detailsp != NULL) 144 *detailsp = NULL; 145 146 if (key == NULL || 147 sshkey_type_plain(key->type) != KEY_ED25519_SK || 148 key->ed25519_pk == NULL || 149 sig == NULL || siglen == 0) 150 return SSH_ERR_INVALID_ARGUMENT; 151 152 if ((b = sshbuf_from(sig, siglen)) == NULL) 153 return SSH_ERR_ALLOC_FAIL; 154 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 155 sshbuf_get_string_direct(b, &sigblob, &len) != 0 || 156 sshbuf_get_u8(b, &sig_flags) != 0 || 157 sshbuf_get_u32(b, &sig_counter) != 0) { 158 r = SSH_ERR_INVALID_FORMAT; 159 goto out; 160 } 161 #ifdef DEBUG_SK 162 fprintf(stderr, "%s: data:\n", __func__); 163 /* sshbuf_dump_data(data, datalen, stderr); */ 164 fprintf(stderr, "%s: sigblob:\n", __func__); 165 sshbuf_dump_data(sigblob, len, stderr); 166 fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 167 __func__, sig_flags, sig_counter); 168 #endif 169 if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { 170 r = SSH_ERR_KEY_TYPE_MISMATCH; 171 goto out; 172 } 173 if (sshbuf_len(b) != 0) { 174 r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 175 goto out; 176 } 177 if (len != crypto_sign_ed25519_BYTES) { 178 r = SSH_ERR_INVALID_FORMAT; 179 goto out; 180 } 181 if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application, 182 strlen(key->sk_application), apphash, sizeof(apphash)) != 0 || 183 ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen, 184 msghash, sizeof(msghash)) != 0) { 185 r = SSH_ERR_INVALID_ARGUMENT; 186 goto out; 187 } 188 #ifdef DEBUG_SK 189 fprintf(stderr, "%s: hashed application:\n", __func__); 190 sshbuf_dump_data(apphash, sizeof(apphash), stderr); 191 fprintf(stderr, "%s: hashed message:\n", __func__); 192 sshbuf_dump_data(msghash, sizeof(msghash), stderr); 193 #endif 194 if ((details = calloc(1, sizeof(*details))) == NULL) { 195 r = SSH_ERR_ALLOC_FAIL; 196 goto out; 197 } 198 details->sk_counter = sig_counter; 199 details->sk_flags = sig_flags; 200 if ((encoded = sshbuf_new()) == NULL) { 201 r = SSH_ERR_ALLOC_FAIL; 202 goto out; 203 } 204 if (sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 || 205 sshbuf_put_u8(encoded, sig_flags) != 0 || 206 sshbuf_put_u32(encoded, sig_counter) != 0 || 207 sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) { 208 r = SSH_ERR_ALLOC_FAIL; 209 goto out; 210 } 211 #ifdef DEBUG_SK 212 fprintf(stderr, "%s: signed buf:\n", __func__); 213 sshbuf_dump(encoded, stderr); 214 #endif 215 if ((ret = crypto_sign_ed25519_verify_detached(sigblob, 216 sshbuf_ptr(encoded), sshbuf_len(encoded), 217 key->ed25519_pk)) != 0) { 218 debug2_f("crypto_sign_ed25519_verify_detached failed: %d", ret); 219 } 220 if (ret != 0) { 221 r = SSH_ERR_SIGNATURE_INVALID; 222 goto out; 223 } 224 /* success */ 225 r = 0; 226 if (detailsp != NULL) { 227 *detailsp = details; 228 details = NULL; 229 } 230 out: 231 sshkey_sig_details_free(details); 232 sshbuf_free(b); 233 sshbuf_free(encoded); 234 free(ktype); 235 return r; 236 } 237 238 static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = { 239 /* .size = */ NULL, 240 /* .alloc = */ NULL, 241 /* .cleanup = */ ssh_ed25519_sk_cleanup, 242 /* .equal = */ ssh_ed25519_sk_equal, 243 /* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public, 244 /* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public, 245 /* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private, 246 /* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private, 247 /* .generate = */ NULL, 248 /* .copy_public = */ ssh_ed25519_sk_copy_public, 249 /* .sign = */ NULL, 250 /* .verify = */ ssh_ed25519_sk_verify, 251 }; 252 253 const struct sshkey_impl sshkey_ed25519_sk_impl = { 254 /* .name = */ "sk-ssh-ed25519 (at) openssh.com", 255 /* .shortname = */ "ED25519-SK", 256 /* .sigalg = */ NULL, 257 /* .type = */ KEY_ED25519_SK, 258 /* .nid = */ 0, 259 /* .cert = */ 0, 260 /* .sigonly = */ 0, 261 /* .keybits = */ 256, 262 /* .funcs = */ &sshkey_ed25519_sk_funcs, 263 }; 264 265 const struct sshkey_impl sshkey_ed25519_sk_cert_impl = { 266 /* .name = */ "sk-ssh-ed25519-cert-v01 (at) openssh.com", 267 /* .shortname = */ "ED25519-SK-CERT", 268 /* .sigalg = */ NULL, 269 /* .type = */ KEY_ED25519_SK_CERT, 270 /* .nid = */ 0, 271 /* .cert = */ 1, 272 /* .sigonly = */ 0, 273 /* .keybits = */ 256, 274 /* .funcs = */ &sshkey_ed25519_sk_funcs, 275 }; 276