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 #include "includes.h" 21 22 #define SSHKEY_INTERNAL 23 #include <sys/types.h> 24 #include <limits.h> 25 26 #include "crypto_api.h" 27 28 #include <string.h> 29 #include <stdarg.h> 30 31 #include "log.h" 32 #include "sshbuf.h" 33 #include "sshkey.h" 34 #include "ssherr.h" 35 #include "ssh.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 const u_char *sm; 135 u_char *m = NULL; 136 u_char apphash[32]; 137 u_char msghash[32]; 138 u_char sig_flags; 139 u_int sig_counter; 140 size_t len; 141 unsigned long long smlen = 0, mlen = 0; 142 int r = SSH_ERR_INTERNAL_ERROR; 143 int ret; 144 struct sshkey_sig_details *details = NULL; 145 146 if (detailsp != NULL) 147 *detailsp = NULL; 148 149 if (key == NULL || 150 sshkey_type_plain(key->type) != KEY_ED25519_SK || 151 key->ed25519_pk == NULL || 152 sig == NULL || siglen == 0) 153 return SSH_ERR_INVALID_ARGUMENT; 154 155 if ((b = sshbuf_from(sig, siglen)) == NULL) 156 return SSH_ERR_ALLOC_FAIL; 157 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 158 sshbuf_get_string_direct(b, &sigblob, &len) != 0 || 159 sshbuf_get_u8(b, &sig_flags) != 0 || 160 sshbuf_get_u32(b, &sig_counter) != 0) { 161 r = SSH_ERR_INVALID_FORMAT; 162 goto out; 163 } 164 #ifdef DEBUG_SK 165 fprintf(stderr, "%s: data:\n", __func__); 166 /* sshbuf_dump_data(data, datalen, stderr); */ 167 fprintf(stderr, "%s: sigblob:\n", __func__); 168 sshbuf_dump_data(sigblob, len, stderr); 169 fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 170 __func__, sig_flags, sig_counter); 171 #endif 172 if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { 173 r = SSH_ERR_KEY_TYPE_MISMATCH; 174 goto out; 175 } 176 if (sshbuf_len(b) != 0) { 177 r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 178 goto out; 179 } 180 if (len > crypto_sign_ed25519_BYTES) { 181 r = SSH_ERR_INVALID_FORMAT; 182 goto out; 183 } 184 if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application, 185 strlen(key->sk_application), apphash, sizeof(apphash)) != 0 || 186 ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen, 187 msghash, sizeof(msghash)) != 0) { 188 r = SSH_ERR_INVALID_ARGUMENT; 189 goto out; 190 } 191 #ifdef DEBUG_SK 192 fprintf(stderr, "%s: hashed application:\n", __func__); 193 sshbuf_dump_data(apphash, sizeof(apphash), stderr); 194 fprintf(stderr, "%s: hashed message:\n", __func__); 195 sshbuf_dump_data(msghash, sizeof(msghash), stderr); 196 #endif 197 if ((details = calloc(1, sizeof(*details))) == NULL) { 198 r = SSH_ERR_ALLOC_FAIL; 199 goto out; 200 } 201 details->sk_counter = sig_counter; 202 details->sk_flags = sig_flags; 203 if ((encoded = sshbuf_new()) == NULL) { 204 r = SSH_ERR_ALLOC_FAIL; 205 goto out; 206 } 207 if (sshbuf_put(encoded, sigblob, len) != 0 || 208 sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 || 209 sshbuf_put_u8(encoded, sig_flags) != 0 || 210 sshbuf_put_u32(encoded, sig_counter) != 0 || 211 sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) { 212 r = SSH_ERR_ALLOC_FAIL; 213 goto out; 214 } 215 #ifdef DEBUG_SK 216 fprintf(stderr, "%s: signed buf:\n", __func__); 217 sshbuf_dump(encoded, stderr); 218 #endif 219 sm = sshbuf_ptr(encoded); 220 smlen = sshbuf_len(encoded); 221 mlen = smlen; 222 if ((m = malloc(smlen)) == NULL) { 223 r = SSH_ERR_ALLOC_FAIL; 224 goto out; 225 } 226 if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, 227 key->ed25519_pk)) != 0) { 228 debug2_f("crypto_sign_ed25519_open failed: %d", ret); 229 } 230 if (ret != 0 || mlen != smlen - len) { 231 r = SSH_ERR_SIGNATURE_INVALID; 232 goto out; 233 } 234 /* XXX compare 'm' and 'sm + len' ? */ 235 /* success */ 236 r = 0; 237 if (detailsp != NULL) { 238 *detailsp = details; 239 details = NULL; 240 } 241 out: 242 if (m != NULL) 243 freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ 244 sshkey_sig_details_free(details); 245 sshbuf_free(b); 246 sshbuf_free(encoded); 247 free(ktype); 248 return r; 249 } 250 251 static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = { 252 /* .size = */ NULL, 253 /* .alloc = */ NULL, 254 /* .cleanup = */ ssh_ed25519_sk_cleanup, 255 /* .equal = */ ssh_ed25519_sk_equal, 256 /* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public, 257 /* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public, 258 /* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private, 259 /* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private, 260 /* .generate = */ NULL, 261 /* .copy_public = */ ssh_ed25519_sk_copy_public, 262 /* .sign = */ NULL, 263 /* .verify = */ ssh_ed25519_sk_verify, 264 }; 265 266 const struct sshkey_impl sshkey_ed25519_sk_impl = { 267 /* .name = */ "sk-ssh-ed25519@openssh.com", 268 /* .shortname = */ "ED25519-SK", 269 /* .sigalg = */ NULL, 270 /* .type = */ KEY_ED25519_SK, 271 /* .nid = */ 0, 272 /* .cert = */ 0, 273 /* .sigonly = */ 0, 274 /* .keybits = */ 256, 275 /* .funcs = */ &sshkey_ed25519_sk_funcs, 276 }; 277 278 const struct sshkey_impl sshkey_ed25519_sk_cert_impl = { 279 /* .name = */ "sk-ssh-ed25519-cert-v01@openssh.com", 280 /* .shortname = */ "ED25519-SK-CERT", 281 /* .sigalg = */ NULL, 282 /* .type = */ KEY_ED25519_SK_CERT, 283 /* .nid = */ 0, 284 /* .cert = */ 1, 285 /* .sigonly = */ 0, 286 /* .keybits = */ 256, 287 /* .funcs = */ &sshkey_ed25519_sk_funcs, 288 }; 289