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