1 /* $OpenBSD: ssh-ed25519.c,v 1.19 2022/10/28 00:44:44 djm Exp $ */ 2 /* 3 * Copyright (c) 2013 Markus Friedl <markus@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 18 #include "includes.h" 19 20 #include <sys/types.h> 21 #include <limits.h> 22 23 #include "crypto_api.h" 24 25 #include <string.h> 26 #include <stdarg.h> 27 28 #include "log.h" 29 #include "sshbuf.h" 30 #define SSHKEY_INTERNAL 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, len; 153 unsigned long long smlen; 154 int r, ret; 155 struct sshbuf *b = NULL; 156 157 if (lenp != NULL) 158 *lenp = 0; 159 if (sigp != NULL) 160 *sigp = NULL; 161 162 if (key == NULL || 163 sshkey_type_plain(key->type) != KEY_ED25519 || 164 key->ed25519_sk == NULL || 165 datalen >= INT_MAX - crypto_sign_ed25519_BYTES) 166 return SSH_ERR_INVALID_ARGUMENT; 167 smlen = slen = datalen + crypto_sign_ed25519_BYTES; 168 if ((sig = malloc(slen)) == NULL) 169 return SSH_ERR_ALLOC_FAIL; 170 171 if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, 172 key->ed25519_sk)) != 0 || smlen <= datalen) { 173 r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ 174 goto out; 175 } 176 /* encode signature */ 177 if ((b = sshbuf_new()) == NULL) { 178 r = SSH_ERR_ALLOC_FAIL; 179 goto out; 180 } 181 if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 || 182 (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) 183 goto out; 184 len = sshbuf_len(b); 185 if (sigp != NULL) { 186 if ((*sigp = malloc(len)) == NULL) { 187 r = SSH_ERR_ALLOC_FAIL; 188 goto out; 189 } 190 memcpy(*sigp, sshbuf_ptr(b), len); 191 } 192 if (lenp != NULL) 193 *lenp = len; 194 /* success */ 195 r = 0; 196 out: 197 sshbuf_free(b); 198 if (sig != NULL) 199 freezero(sig, slen); 200 201 return r; 202 } 203 204 static int 205 ssh_ed25519_verify(const struct sshkey *key, 206 const u_char *sig, size_t siglen, 207 const u_char *data, size_t dlen, const char *alg, u_int compat, 208 struct sshkey_sig_details **detailsp) 209 { 210 struct sshbuf *b = NULL; 211 char *ktype = NULL; 212 const u_char *sigblob; 213 u_char *sm = NULL, *m = NULL; 214 size_t len; 215 unsigned long long smlen = 0, mlen = 0; 216 int r, ret; 217 218 if (key == NULL || 219 sshkey_type_plain(key->type) != KEY_ED25519 || 220 key->ed25519_pk == NULL || 221 dlen >= INT_MAX - crypto_sign_ed25519_BYTES || 222 sig == NULL || siglen == 0) 223 return SSH_ERR_INVALID_ARGUMENT; 224 225 if ((b = sshbuf_from(sig, siglen)) == NULL) 226 return SSH_ERR_ALLOC_FAIL; 227 if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || 228 (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) 229 goto out; 230 if (strcmp("ssh-ed25519", ktype) != 0) { 231 r = SSH_ERR_KEY_TYPE_MISMATCH; 232 goto out; 233 } 234 if (sshbuf_len(b) != 0) { 235 r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 236 goto out; 237 } 238 if (len > crypto_sign_ed25519_BYTES) { 239 r = SSH_ERR_INVALID_FORMAT; 240 goto out; 241 } 242 if (dlen >= SIZE_MAX - len) { 243 r = SSH_ERR_INVALID_ARGUMENT; 244 goto out; 245 } 246 smlen = len + dlen; 247 mlen = smlen; 248 if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { 249 r = SSH_ERR_ALLOC_FAIL; 250 goto out; 251 } 252 memcpy(sm, sigblob, len); 253 memcpy(sm+len, data, dlen); 254 if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, 255 key->ed25519_pk)) != 0) { 256 debug2_f("crypto_sign_ed25519_open failed: %d", ret); 257 } 258 if (ret != 0 || mlen != dlen) { 259 r = SSH_ERR_SIGNATURE_INVALID; 260 goto out; 261 } 262 /* XXX compare 'm' and 'data' ? */ 263 /* success */ 264 r = 0; 265 out: 266 if (sm != NULL) 267 freezero(sm, smlen); 268 if (m != NULL) 269 freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ 270 sshbuf_free(b); 271 free(ktype); 272 return r; 273 } 274 275 /* NB. not static; used by ED25519-SK */ 276 const struct sshkey_impl_funcs sshkey_ed25519_funcs = { 277 /* .size = */ NULL, 278 /* .alloc = */ NULL, 279 /* .cleanup = */ ssh_ed25519_cleanup, 280 /* .equal = */ ssh_ed25519_equal, 281 /* .ssh_serialize_public = */ ssh_ed25519_serialize_public, 282 /* .ssh_deserialize_public = */ ssh_ed25519_deserialize_public, 283 /* .ssh_serialize_private = */ ssh_ed25519_serialize_private, 284 /* .ssh_deserialize_private = */ ssh_ed25519_deserialize_private, 285 /* .generate = */ ssh_ed25519_generate, 286 /* .copy_public = */ ssh_ed25519_copy_public, 287 /* .sign = */ ssh_ed25519_sign, 288 /* .verify = */ ssh_ed25519_verify, 289 }; 290 291 const struct sshkey_impl sshkey_ed25519_impl = { 292 /* .name = */ "ssh-ed25519", 293 /* .shortname = */ "ED25519", 294 /* .sigalg = */ NULL, 295 /* .type = */ KEY_ED25519, 296 /* .nid = */ 0, 297 /* .cert = */ 0, 298 /* .sigonly = */ 0, 299 /* .keybits = */ 256, 300 /* .funcs = */ &sshkey_ed25519_funcs, 301 }; 302 303 const struct sshkey_impl sshkey_ed25519_cert_impl = { 304 /* .name = */ "ssh-ed25519-cert-v01@openssh.com", 305 /* .shortname = */ "ED25519-CERT", 306 /* .sigalg = */ NULL, 307 /* .type = */ KEY_ED25519_CERT, 308 /* .nid = */ 0, 309 /* .cert = */ 1, 310 /* .sigonly = */ 0, 311 /* .keybits = */ 256, 312 /* .funcs = */ &sshkey_ed25519_funcs, 313 }; 314