1 /* $OpenBSD: ssh-ed25519-sk.c,v 1.15 2022/10/28 00:44:44 djm Exp $ */ 2 /* 3 * Copyright (c) 2019 Markus Friedl. All rights reserved. 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 /* #define DEBUG_SK 1 */ 19 20 #define SSHKEY_INTERNAL 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 #include "digest.h" 35 36 /* Reuse some ED25519 internals */ 37 extern struct sshkey_impl_funcs sshkey_ed25519_funcs; 38 39 static void 40 ssh_ed25519_sk_cleanup(struct sshkey *k) 41 { 42 sshkey_sk_cleanup(k); 43 sshkey_ed25519_funcs.cleanup(k); 44 } 45 46 static int 47 ssh_ed25519_sk_equal(const struct sshkey *a, const struct sshkey *b) 48 { 49 if (!sshkey_sk_fields_equal(a, b)) 50 return 0; 51 if (!sshkey_ed25519_funcs.equal(a, b)) 52 return 0; 53 return 1; 54 } 55 56 static int 57 ssh_ed25519_sk_serialize_public(const struct sshkey *key, struct sshbuf *b, 58 enum sshkey_serialize_rep opts) 59 { 60 int r; 61 62 if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0) 63 return r; 64 if ((r = sshkey_serialize_sk(key, b)) != 0) 65 return r; 66 67 return 0; 68 } 69 70 static int 71 ssh_ed25519_sk_serialize_private(const struct sshkey *key, struct sshbuf *b, 72 enum sshkey_serialize_rep opts) 73 { 74 int r; 75 76 if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0) 77 return r; 78 if ((r = sshkey_serialize_private_sk(key, b)) != 0) 79 return r; 80 81 return 0; 82 } 83 84 static int 85 ssh_ed25519_sk_copy_public(const struct sshkey *from, struct sshkey *to) 86 { 87 int r; 88 89 if ((r = sshkey_ed25519_funcs.copy_public(from, to)) != 0) 90 return r; 91 if ((r = sshkey_copy_public_sk(from, to)) != 0) 92 return r; 93 return 0; 94 } 95 96 static int 97 ssh_ed25519_sk_deserialize_public(const char *ktype, struct sshbuf *b, 98 struct sshkey *key) 99 { 100 int r; 101 102 if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0) 103 return r; 104 if ((r = sshkey_deserialize_sk(b, key)) != 0) 105 return r; 106 return 0; 107 } 108 109 static int 110 ssh_ed25519_sk_deserialize_private(const char *ktype, struct sshbuf *b, 111 struct sshkey *key) 112 { 113 int r; 114 115 if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0) 116 return r; 117 if ((r = sshkey_private_deserialize_sk(b, key)) != 0) 118 return r; 119 return 0; 120 } 121 122 static int 123 ssh_ed25519_sk_verify(const struct sshkey *key, 124 const u_char *sig, size_t siglen, 125 const u_char *data, size_t dlen, const char *alg, u_int compat, 126 struct sshkey_sig_details **detailsp) 127 { 128 struct sshbuf *b = NULL; 129 struct sshbuf *encoded = NULL; 130 char *ktype = NULL; 131 const u_char *sigblob; 132 const u_char *sm; 133 u_char *m = NULL; 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 unsigned long long smlen = 0, mlen = 0; 140 int r = SSH_ERR_INTERNAL_ERROR; 141 int ret; 142 struct sshkey_sig_details *details = NULL; 143 144 if (detailsp != NULL) 145 *detailsp = NULL; 146 147 if (key == NULL || 148 sshkey_type_plain(key->type) != KEY_ED25519_SK || 149 key->ed25519_pk == NULL || 150 sig == NULL || siglen == 0) 151 return SSH_ERR_INVALID_ARGUMENT; 152 153 if ((b = sshbuf_from(sig, siglen)) == NULL) 154 return SSH_ERR_ALLOC_FAIL; 155 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 156 sshbuf_get_string_direct(b, &sigblob, &len) != 0 || 157 sshbuf_get_u8(b, &sig_flags) != 0 || 158 sshbuf_get_u32(b, &sig_counter) != 0) { 159 r = SSH_ERR_INVALID_FORMAT; 160 goto out; 161 } 162 #ifdef DEBUG_SK 163 fprintf(stderr, "%s: data:\n", __func__); 164 /* sshbuf_dump_data(data, datalen, stderr); */ 165 fprintf(stderr, "%s: sigblob:\n", __func__); 166 sshbuf_dump_data(sigblob, len, stderr); 167 fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 168 __func__, sig_flags, sig_counter); 169 #endif 170 if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { 171 r = SSH_ERR_KEY_TYPE_MISMATCH; 172 goto out; 173 } 174 if (sshbuf_len(b) != 0) { 175 r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 176 goto out; 177 } 178 if (len > crypto_sign_ed25519_BYTES) { 179 r = SSH_ERR_INVALID_FORMAT; 180 goto out; 181 } 182 if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application, 183 strlen(key->sk_application), apphash, sizeof(apphash)) != 0 || 184 ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen, 185 msghash, sizeof(msghash)) != 0) { 186 r = SSH_ERR_INVALID_ARGUMENT; 187 goto out; 188 } 189 #ifdef DEBUG_SK 190 fprintf(stderr, "%s: hashed application:\n", __func__); 191 sshbuf_dump_data(apphash, sizeof(apphash), stderr); 192 fprintf(stderr, "%s: hashed message:\n", __func__); 193 sshbuf_dump_data(msghash, sizeof(msghash), stderr); 194 #endif 195 if ((details = calloc(1, sizeof(*details))) == NULL) { 196 r = SSH_ERR_ALLOC_FAIL; 197 goto out; 198 } 199 details->sk_counter = sig_counter; 200 details->sk_flags = sig_flags; 201 if ((encoded = sshbuf_new()) == NULL) { 202 r = SSH_ERR_ALLOC_FAIL; 203 goto out; 204 } 205 if (sshbuf_put(encoded, sigblob, len) != 0 || 206 sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 || 207 sshbuf_put_u8(encoded, sig_flags) != 0 || 208 sshbuf_put_u32(encoded, sig_counter) != 0 || 209 sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) { 210 r = SSH_ERR_ALLOC_FAIL; 211 goto out; 212 } 213 #ifdef DEBUG_SK 214 fprintf(stderr, "%s: signed buf:\n", __func__); 215 sshbuf_dump(encoded, stderr); 216 #endif 217 sm = sshbuf_ptr(encoded); 218 smlen = sshbuf_len(encoded); 219 mlen = smlen; 220 if ((m = malloc(smlen)) == NULL) { 221 r = SSH_ERR_ALLOC_FAIL; 222 goto out; 223 } 224 if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, 225 key->ed25519_pk)) != 0) { 226 debug2_f("crypto_sign_ed25519_open failed: %d", ret); 227 } 228 if (ret != 0 || mlen != smlen - len) { 229 r = SSH_ERR_SIGNATURE_INVALID; 230 goto out; 231 } 232 /* XXX compare 'm' and 'sm + len' ? */ 233 /* success */ 234 r = 0; 235 if (detailsp != NULL) { 236 *detailsp = details; 237 details = NULL; 238 } 239 out: 240 if (m != NULL) 241 freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ 242 sshkey_sig_details_free(details); 243 sshbuf_free(b); 244 sshbuf_free(encoded); 245 free(ktype); 246 return r; 247 } 248 249 static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = { 250 /* .size = */ NULL, 251 /* .alloc = */ NULL, 252 /* .cleanup = */ ssh_ed25519_sk_cleanup, 253 /* .equal = */ ssh_ed25519_sk_equal, 254 /* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public, 255 /* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public, 256 /* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private, 257 /* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private, 258 /* .generate = */ NULL, 259 /* .copy_public = */ ssh_ed25519_sk_copy_public, 260 /* .sign = */ NULL, 261 /* .verify = */ ssh_ed25519_sk_verify, 262 }; 263 264 const struct sshkey_impl sshkey_ed25519_sk_impl = { 265 /* .name = */ "sk-ssh-ed25519@openssh.com", 266 /* .shortname = */ "ED25519-SK", 267 /* .sigalg = */ NULL, 268 /* .type = */ KEY_ED25519_SK, 269 /* .nid = */ 0, 270 /* .cert = */ 0, 271 /* .sigonly = */ 0, 272 /* .keybits = */ 256, 273 /* .funcs = */ &sshkey_ed25519_sk_funcs, 274 }; 275 276 const struct sshkey_impl sshkey_ed25519_sk_cert_impl = { 277 /* .name = */ "sk-ssh-ed25519-cert-v01@openssh.com", 278 /* .shortname = */ "ED25519-SK-CERT", 279 /* .sigalg = */ NULL, 280 /* .type = */ KEY_ED25519_SK_CERT, 281 /* .nid = */ 0, 282 /* .cert = */ 1, 283 /* .sigonly = */ 0, 284 /* .keybits = */ 256, 285 /* .funcs = */ &sshkey_ed25519_sk_funcs, 286 }; 287