Annotation of src/usr.bin/ssh/ssh-ed25519.c, Revision 1.19
1.19 ! djm 1: /* $OpenBSD: ssh-ed25519.c,v 1.18 2022/10/28 00:44:17 djm Exp $ */
1.1 markus 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: */
1.4 djm 17: #define SSHKEY_INTERNAL
1.1 markus 18: #include <sys/types.h>
1.4 djm 19: #include <limits.h>
1.1 markus 20:
21: #include "crypto_api.h"
22:
23: #include <string.h>
24: #include <stdarg.h>
25:
26: #include "log.h"
1.6 markus 27: #include "sshbuf.h"
1.4 djm 28: #include "sshkey.h"
29: #include "ssherr.h"
1.1 markus 30: #include "ssh.h"
31:
1.11 djm 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:
1.12 djm 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:
1.13 djm 51: static int
52: ssh_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b,
1.16 djm 53: enum sshkey_serialize_rep opts)
1.13 djm 54: {
55: int r;
56:
57: if (key->ed25519_pk == NULL)
58: return SSH_ERR_INVALID_ARGUMENT;
1.16 djm 59: if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0)
1.13 djm 60: return r;
61:
62: return 0;
63: }
64:
1.14 djm 65: static int
1.18 djm 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
1.14 djm 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:
1.15 djm 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:
1.16 djm 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:
1.17 djm 117: static int
1.19 ! djm 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
1.17 djm 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)
1.1 markus 147: {
1.4 djm 148: u_char *sig = NULL;
149: size_t slen = 0, len;
1.1 markus 150: unsigned long long smlen;
1.4 djm 151: int r, ret;
152: struct sshbuf *b = NULL;
1.1 markus 153:
1.4 djm 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;
1.1 markus 164: smlen = slen = datalen + crypto_sign_ed25519_BYTES;
1.4 djm 165: if ((sig = malloc(slen)) == NULL)
166: return SSH_ERR_ALLOC_FAIL;
1.1 markus 167:
168: if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen,
169: key->ed25519_sk)) != 0 || smlen <= datalen) {
1.4 djm 170: r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */
171: goto out;
1.1 markus 172: }
173: /* encode signature */
1.4 djm 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: }
1.1 markus 189: if (lenp != NULL)
190: *lenp = len;
1.4 djm 191: /* success */
192: r = 0;
193: out:
194: sshbuf_free(b);
1.10 djm 195: if (sig != NULL)
1.8 jsg 196: freezero(sig, slen);
1.1 markus 197:
1.4 djm 198: return r;
1.1 markus 199: }
200:
1.17 djm 201: static int
1.4 djm 202: ssh_ed25519_verify(const struct sshkey *key,
1.17 djm 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)
1.1 markus 206: {
1.4 djm 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 ||
1.17 djm 218: dlen >= INT_MAX - crypto_sign_ed25519_BYTES ||
219: sig == NULL || siglen == 0)
1.4 djm 220: return SSH_ERR_INVALID_ARGUMENT;
221:
1.17 djm 222: if ((b = sshbuf_from(sig, siglen)) == NULL)
1.4 djm 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;
1.1 markus 227: if (strcmp("ssh-ed25519", ktype) != 0) {
1.4 djm 228: r = SSH_ERR_KEY_TYPE_MISMATCH;
229: goto out;
1.1 markus 230: }
1.4 djm 231: if (sshbuf_len(b) != 0) {
232: r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
233: goto out;
1.1 markus 234: }
235: if (len > crypto_sign_ed25519_BYTES) {
1.4 djm 236: r = SSH_ERR_INVALID_FORMAT;
237: goto out;
1.1 markus 238: }
1.17 djm 239: if (dlen >= SIZE_MAX - len) {
1.5 daniel 240: r = SSH_ERR_INVALID_ARGUMENT;
241: goto out;
242: }
1.17 djm 243: smlen = len + dlen;
1.4 djm 244: mlen = smlen;
1.6 markus 245: if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) {
1.4 djm 246: r = SSH_ERR_ALLOC_FAIL;
247: goto out;
248: }
1.1 markus 249: memcpy(sm, sigblob, len);
1.17 djm 250: memcpy(sm+len, data, dlen);
1.1 markus 251: if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
252: key->ed25519_pk)) != 0) {
1.9 djm 253: debug2_f("crypto_sign_ed25519_open failed: %d", ret);
1.1 markus 254: }
1.17 djm 255: if (ret != 0 || mlen != dlen) {
1.4 djm 256: r = SSH_ERR_SIGNATURE_INVALID;
257: goto out;
1.1 markus 258: }
259: /* XXX compare 'm' and 'data' ? */
1.4 djm 260: /* success */
261: r = 0;
262: out:
1.10 djm 263: if (sm != NULL)
1.8 jsg 264: freezero(sm, smlen);
1.10 djm 265: if (m != NULL)
1.8 jsg 266: freezero(m, smlen); /* NB mlen may be invalid if r != 0 */
1.4 djm 267: sshbuf_free(b);
268: free(ktype);
269: return r;
270: }
1.11 djm 271:
1.12 djm 272: /* NB. not static; used by ED25519-SK */
273: const struct sshkey_impl_funcs sshkey_ed25519_funcs = {
1.11 djm 274: /* .size = */ NULL,
275: /* .alloc = */ NULL,
276: /* .cleanup = */ ssh_ed25519_cleanup,
1.12 djm 277: /* .equal = */ ssh_ed25519_equal,
1.13 djm 278: /* .ssh_serialize_public = */ ssh_ed25519_serialize_public,
1.16 djm 279: /* .ssh_deserialize_public = */ ssh_ed25519_deserialize_public,
1.18 djm 280: /* .ssh_serialize_private = */ ssh_ed25519_serialize_private,
1.19 ! djm 281: /* .ssh_deserialize_private = */ ssh_ed25519_deserialize_private,
1.14 djm 282: /* .generate = */ ssh_ed25519_generate,
1.15 djm 283: /* .copy_public = */ ssh_ed25519_copy_public,
1.17 djm 284: /* .sign = */ ssh_ed25519_sign,
285: /* .verify = */ ssh_ed25519_verify,
1.11 djm 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: };