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 #define SSHKEY_INTERNAL 18 #include <sys/types.h> 19 #include <limits.h> 20 21 #include "crypto_api.h" 22 23 #include <string.h> 24 #include <stdarg.h> 25 26 #include "log.h" 27 #include "sshbuf.h" 28 #include "sshkey.h" 29 #include "ssherr.h" 30 #include "ssh.h" 31 32 static void 33 ssh_ed25519_cleanup(struct sshkey *k) 34 { 35 freezero(k->ed25519_pk, ED25519_PK_SZ); 36 freezero(k->ed25519_sk, ED25519_SK_SZ); 37 k->ed25519_pk = NULL; 38 k->ed25519_sk = NULL; 39 } 40 41 static int 42 ssh_ed25519_equal(const struct sshkey *a, const struct sshkey *b) 43 { 44 if (a->ed25519_pk == NULL || b->ed25519_pk == NULL) 45 return 0; 46 if (memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) != 0) 47 return 0; 48 return 1; 49 } 50 51 static int 52 ssh_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b, 53 enum sshkey_serialize_rep opts) 54 { 55 int r; 56 57 if (key->ed25519_pk == NULL) 58 return SSH_ERR_INVALID_ARGUMENT; 59 if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0) 60 return r; 61 62 return 0; 63 } 64 65 static int 66 ssh_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b, 67 enum sshkey_serialize_rep opts) 68 { 69 int r; 70 71 if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0 || 72 (r = sshbuf_put_string(b, key->ed25519_sk, ED25519_SK_SZ)) != 0) 73 return r; 74 75 return 0; 76 } 77 78 static int 79 ssh_ed25519_generate(struct sshkey *k, int bits) 80 { 81 if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL || 82 (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) 83 return SSH_ERR_ALLOC_FAIL; 84 crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); 85 return 0; 86 } 87 88 static int 89 ssh_ed25519_copy_public(const struct sshkey *from, struct sshkey *to) 90 { 91 if (from->ed25519_pk == NULL) 92 return 0; /* XXX SSH_ERR_INTERNAL_ERROR ? */ 93 if ((to->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) 94 return SSH_ERR_ALLOC_FAIL; 95 memcpy(to->ed25519_pk, from->ed25519_pk, ED25519_PK_SZ); 96 return 0; 97 } 98 99 static int 100 ssh_ed25519_deserialize_public(const char *ktype, struct sshbuf *b, 101 struct sshkey *key) 102 { 103 u_char *pk = NULL; 104 size_t len = 0; 105 int r; 106 107 if ((r = sshbuf_get_string(b, &pk, &len)) != 0) 108 return r; 109 if (len != ED25519_PK_SZ) { 110 freezero(pk, len); 111 return SSH_ERR_INVALID_FORMAT; 112 } 113 key->ed25519_pk = pk; 114 return 0; 115 } 116 117 static int 118 ssh_ed25519_deserialize_private(const char *ktype, struct sshbuf *b, 119 struct sshkey *key) 120 { 121 int r; 122 size_t sklen = 0; 123 u_char *ed25519_sk = NULL; 124 125 if ((r = ssh_ed25519_deserialize_public(NULL, b, key)) != 0) 126 goto out; 127 if ((r = sshbuf_get_string(b, &ed25519_sk, &sklen)) != 0) 128 goto out; 129 if (sklen != ED25519_SK_SZ) { 130 r = SSH_ERR_INVALID_FORMAT; 131 goto out; 132 } 133 key->ed25519_sk = ed25519_sk; 134 ed25519_sk = NULL; /* transferred */ 135 /* success */ 136 r = 0; 137 out: 138 freezero(ed25519_sk, sklen); 139 return r; 140 } 141 142 static int 143 ssh_ed25519_sign(struct sshkey *key, 144 u_char **sigp, size_t *lenp, 145 const u_char *data, size_t datalen, 146 const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) 147 { 148 u_char *sig = NULL; 149 size_t slen = 0, len; 150 unsigned long long smlen; 151 int r, ret; 152 struct sshbuf *b = NULL; 153 154 if (lenp != NULL) 155 *lenp = 0; 156 if (sigp != NULL) 157 *sigp = NULL; 158 159 if (key == NULL || 160 sshkey_type_plain(key->type) != KEY_ED25519 || 161 key->ed25519_sk == NULL || 162 datalen >= INT_MAX - crypto_sign_ed25519_BYTES) 163 return SSH_ERR_INVALID_ARGUMENT; 164 smlen = slen = datalen + crypto_sign_ed25519_BYTES; 165 if ((sig = malloc(slen)) == NULL) 166 return SSH_ERR_ALLOC_FAIL; 167 168 if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, 169 key->ed25519_sk)) != 0 || smlen <= datalen) { 170 r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ 171 goto out; 172 } 173 /* encode signature */ 174 if ((b = sshbuf_new()) == NULL) { 175 r = SSH_ERR_ALLOC_FAIL; 176 goto out; 177 } 178 if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 || 179 (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) 180 goto out; 181 len = sshbuf_len(b); 182 if (sigp != NULL) { 183 if ((*sigp = malloc(len)) == NULL) { 184 r = SSH_ERR_ALLOC_FAIL; 185 goto out; 186 } 187 memcpy(*sigp, sshbuf_ptr(b), len); 188 } 189 if (lenp != NULL) 190 *lenp = len; 191 /* success */ 192 r = 0; 193 out: 194 sshbuf_free(b); 195 if (sig != NULL) 196 freezero(sig, slen); 197 198 return r; 199 } 200 201 static int 202 ssh_ed25519_verify(const struct sshkey *key, 203 const u_char *sig, size_t siglen, 204 const u_char *data, size_t dlen, const char *alg, u_int compat, 205 struct sshkey_sig_details **detailsp) 206 { 207 struct sshbuf *b = NULL; 208 char *ktype = NULL; 209 const u_char *sigblob; 210 u_char *sm = NULL, *m = NULL; 211 size_t len; 212 unsigned long long smlen = 0, mlen = 0; 213 int r, ret; 214 215 if (key == NULL || 216 sshkey_type_plain(key->type) != KEY_ED25519 || 217 key->ed25519_pk == NULL || 218 dlen >= INT_MAX - crypto_sign_ed25519_BYTES || 219 sig == NULL || siglen == 0) 220 return SSH_ERR_INVALID_ARGUMENT; 221 222 if ((b = sshbuf_from(sig, siglen)) == NULL) 223 return SSH_ERR_ALLOC_FAIL; 224 if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || 225 (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) 226 goto out; 227 if (strcmp("ssh-ed25519", ktype) != 0) { 228 r = SSH_ERR_KEY_TYPE_MISMATCH; 229 goto out; 230 } 231 if (sshbuf_len(b) != 0) { 232 r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 233 goto out; 234 } 235 if (len > crypto_sign_ed25519_BYTES) { 236 r = SSH_ERR_INVALID_FORMAT; 237 goto out; 238 } 239 if (dlen >= SIZE_MAX - len) { 240 r = SSH_ERR_INVALID_ARGUMENT; 241 goto out; 242 } 243 smlen = len + dlen; 244 mlen = smlen; 245 if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { 246 r = SSH_ERR_ALLOC_FAIL; 247 goto out; 248 } 249 memcpy(sm, sigblob, len); 250 memcpy(sm+len, data, dlen); 251 if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, 252 key->ed25519_pk)) != 0) { 253 debug2_f("crypto_sign_ed25519_open failed: %d", ret); 254 } 255 if (ret != 0 || mlen != dlen) { 256 r = SSH_ERR_SIGNATURE_INVALID; 257 goto out; 258 } 259 /* XXX compare 'm' and 'data' ? */ 260 /* success */ 261 r = 0; 262 out: 263 if (sm != NULL) 264 freezero(sm, smlen); 265 if (m != NULL) 266 freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ 267 sshbuf_free(b); 268 free(ktype); 269 return r; 270 } 271 272 /* NB. not static; used by ED25519-SK */ 273 const struct sshkey_impl_funcs sshkey_ed25519_funcs = { 274 /* .size = */ NULL, 275 /* .alloc = */ NULL, 276 /* .cleanup = */ ssh_ed25519_cleanup, 277 /* .equal = */ ssh_ed25519_equal, 278 /* .ssh_serialize_public = */ ssh_ed25519_serialize_public, 279 /* .ssh_deserialize_public = */ ssh_ed25519_deserialize_public, 280 /* .ssh_serialize_private = */ ssh_ed25519_serialize_private, 281 /* .ssh_deserialize_private = */ ssh_ed25519_deserialize_private, 282 /* .generate = */ ssh_ed25519_generate, 283 /* .copy_public = */ ssh_ed25519_copy_public, 284 /* .sign = */ ssh_ed25519_sign, 285 /* .verify = */ ssh_ed25519_verify, 286 }; 287 288 const struct sshkey_impl sshkey_ed25519_impl = { 289 /* .name = */ "ssh-ed25519", 290 /* .shortname = */ "ED25519", 291 /* .sigalg = */ NULL, 292 /* .type = */ KEY_ED25519, 293 /* .nid = */ 0, 294 /* .cert = */ 0, 295 /* .sigonly = */ 0, 296 /* .keybits = */ 256, 297 /* .funcs = */ &sshkey_ed25519_funcs, 298 }; 299 300 const struct sshkey_impl sshkey_ed25519_cert_impl = { 301 /* .name = */ "ssh-ed25519-cert-v01@openssh.com", 302 /* .shortname = */ "ED25519-CERT", 303 /* .sigalg = */ NULL, 304 /* .type = */ KEY_ED25519_CERT, 305 /* .nid = */ 0, 306 /* .cert = */ 1, 307 /* .sigonly = */ 0, 308 /* .keybits = */ 256, 309 /* .funcs = */ &sshkey_ed25519_funcs, 310 }; 311