xref: /openbsd-src/usr.bin/ssh/ssh-ed25519-sk.c (revision a2c931d988e31395022f1322d85edf6de1b6b1db)
1*a2c931d9Sdjm /* $OpenBSD: ssh-ed25519-sk.c,v 1.15 2022/10/28 00:44:44 djm Exp $ */
259bab1bcSmarkus /*
359bab1bcSmarkus  * Copyright (c) 2019 Markus Friedl.  All rights reserved.
459bab1bcSmarkus  *
559bab1bcSmarkus  * Permission to use, copy, modify, and distribute this software for any
659bab1bcSmarkus  * purpose with or without fee is hereby granted, provided that the above
759bab1bcSmarkus  * copyright notice and this permission notice appear in all copies.
859bab1bcSmarkus  *
959bab1bcSmarkus  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1059bab1bcSmarkus  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1159bab1bcSmarkus  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1259bab1bcSmarkus  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1359bab1bcSmarkus  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1459bab1bcSmarkus  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1559bab1bcSmarkus  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1659bab1bcSmarkus  */
17584bea6cSdjm 
18584bea6cSdjm /* #define DEBUG_SK 1 */
19584bea6cSdjm 
2059bab1bcSmarkus #define SSHKEY_INTERNAL
2159bab1bcSmarkus #include <sys/types.h>
2259bab1bcSmarkus #include <limits.h>
2359bab1bcSmarkus 
2459bab1bcSmarkus #include "crypto_api.h"
2559bab1bcSmarkus 
2659bab1bcSmarkus #include <string.h>
2759bab1bcSmarkus #include <stdarg.h>
2859bab1bcSmarkus 
2959bab1bcSmarkus #include "log.h"
3059bab1bcSmarkus #include "sshbuf.h"
3159bab1bcSmarkus #include "sshkey.h"
3259bab1bcSmarkus #include "ssherr.h"
3359bab1bcSmarkus #include "ssh.h"
3459bab1bcSmarkus #include "digest.h"
3559bab1bcSmarkus 
36712f5ecfSdjm /* Reuse some ED25519 internals */
37712f5ecfSdjm extern struct sshkey_impl_funcs sshkey_ed25519_funcs;
38712f5ecfSdjm 
399c1667dbSdjm static void
ssh_ed25519_sk_cleanup(struct sshkey * k)409c1667dbSdjm ssh_ed25519_sk_cleanup(struct sshkey *k)
419c1667dbSdjm {
42712f5ecfSdjm 	sshkey_sk_cleanup(k);
43712f5ecfSdjm 	sshkey_ed25519_funcs.cleanup(k);
44712f5ecfSdjm }
45712f5ecfSdjm 
46712f5ecfSdjm static int
ssh_ed25519_sk_equal(const struct sshkey * a,const struct sshkey * b)47712f5ecfSdjm ssh_ed25519_sk_equal(const struct sshkey *a, const struct sshkey *b)
48712f5ecfSdjm {
49712f5ecfSdjm 	if (!sshkey_sk_fields_equal(a, b))
50712f5ecfSdjm 		return 0;
51712f5ecfSdjm 	if (!sshkey_ed25519_funcs.equal(a, b))
52712f5ecfSdjm 		return 0;
53712f5ecfSdjm 	return 1;
549c1667dbSdjm }
559c1667dbSdjm 
56eefcf659Sdjm static int
ssh_ed25519_sk_serialize_public(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)57eefcf659Sdjm ssh_ed25519_sk_serialize_public(const struct sshkey *key, struct sshbuf *b,
58c8d92406Sdjm     enum sshkey_serialize_rep opts)
59eefcf659Sdjm {
60eefcf659Sdjm 	int r;
61eefcf659Sdjm 
62c8d92406Sdjm 	if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0)
63eefcf659Sdjm 		return r;
64eefcf659Sdjm 	if ((r = sshkey_serialize_sk(key, b)) != 0)
65eefcf659Sdjm 		return r;
66eefcf659Sdjm 
67eefcf659Sdjm 	return 0;
68eefcf659Sdjm }
69eefcf659Sdjm 
700d39f001Sdjm static int
ssh_ed25519_sk_serialize_private(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)71d03db38bSdjm ssh_ed25519_sk_serialize_private(const struct sshkey *key, struct sshbuf *b,
72d03db38bSdjm     enum sshkey_serialize_rep opts)
73d03db38bSdjm {
74d03db38bSdjm 	int r;
75d03db38bSdjm 
76d03db38bSdjm 	if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0)
77d03db38bSdjm 		return r;
78d03db38bSdjm 	if ((r = sshkey_serialize_private_sk(key, b)) != 0)
79d03db38bSdjm 		return r;
80d03db38bSdjm 
81d03db38bSdjm 	return 0;
82d03db38bSdjm }
83d03db38bSdjm 
84d03db38bSdjm static int
ssh_ed25519_sk_copy_public(const struct sshkey * from,struct sshkey * to)850d39f001Sdjm ssh_ed25519_sk_copy_public(const struct sshkey *from, struct sshkey *to)
860d39f001Sdjm {
870d39f001Sdjm 	int r;
880d39f001Sdjm 
890d39f001Sdjm 	if ((r = sshkey_ed25519_funcs.copy_public(from, to)) != 0)
900d39f001Sdjm 		return r;
910d39f001Sdjm 	if ((r = sshkey_copy_public_sk(from, to)) != 0)
920d39f001Sdjm 		return r;
930d39f001Sdjm 	return 0;
940d39f001Sdjm }
950d39f001Sdjm 
96c8d92406Sdjm static int
ssh_ed25519_sk_deserialize_public(const char * ktype,struct sshbuf * b,struct sshkey * key)97c8d92406Sdjm ssh_ed25519_sk_deserialize_public(const char *ktype, struct sshbuf *b,
98c8d92406Sdjm     struct sshkey *key)
99c8d92406Sdjm {
100c8d92406Sdjm 	int r;
101c8d92406Sdjm 
102c8d92406Sdjm 	if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0)
103c8d92406Sdjm 		return r;
104c8d92406Sdjm 	if ((r = sshkey_deserialize_sk(b, key)) != 0)
105c8d92406Sdjm 		return r;
106c8d92406Sdjm 	return 0;
107c8d92406Sdjm }
108c8d92406Sdjm 
109c5c174faSdjm static int
ssh_ed25519_sk_deserialize_private(const char * ktype,struct sshbuf * b,struct sshkey * key)110*a2c931d9Sdjm ssh_ed25519_sk_deserialize_private(const char *ktype, struct sshbuf *b,
111*a2c931d9Sdjm     struct sshkey *key)
112*a2c931d9Sdjm {
113*a2c931d9Sdjm 	int r;
114*a2c931d9Sdjm 
115*a2c931d9Sdjm 	if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0)
116*a2c931d9Sdjm 		return r;
117*a2c931d9Sdjm 	if ((r = sshkey_private_deserialize_sk(b, key)) != 0)
118*a2c931d9Sdjm 		return r;
119*a2c931d9Sdjm 	return 0;
120*a2c931d9Sdjm }
121*a2c931d9Sdjm 
122*a2c931d9Sdjm static int
ssh_ed25519_sk_verify(const struct sshkey * key,const u_char * sig,size_t siglen,const u_char * data,size_t dlen,const char * alg,u_int compat,struct sshkey_sig_details ** detailsp)12359bab1bcSmarkus ssh_ed25519_sk_verify(const struct sshkey *key,
124c5c174faSdjm     const u_char *sig, size_t siglen,
125c5c174faSdjm     const u_char *data, size_t dlen, const char *alg, u_int compat,
126493ad5b0Sdjm     struct sshkey_sig_details **detailsp)
12759bab1bcSmarkus {
12859bab1bcSmarkus 	struct sshbuf *b = NULL;
12959bab1bcSmarkus 	struct sshbuf *encoded = NULL;
13059bab1bcSmarkus 	char *ktype = NULL;
13159bab1bcSmarkus 	const u_char *sigblob;
13259bab1bcSmarkus 	const u_char *sm;
13359bab1bcSmarkus 	u_char *m = NULL;
13459bab1bcSmarkus 	u_char apphash[32];
13559bab1bcSmarkus 	u_char msghash[32];
13659bab1bcSmarkus 	u_char sig_flags;
13759bab1bcSmarkus 	u_int sig_counter;
13859bab1bcSmarkus 	size_t len;
13959bab1bcSmarkus 	unsigned long long smlen = 0, mlen = 0;
14059bab1bcSmarkus 	int r = SSH_ERR_INTERNAL_ERROR;
14159bab1bcSmarkus 	int ret;
142493ad5b0Sdjm 	struct sshkey_sig_details *details = NULL;
143493ad5b0Sdjm 
144493ad5b0Sdjm 	if (detailsp != NULL)
145493ad5b0Sdjm 		*detailsp = NULL;
14659bab1bcSmarkus 
14759bab1bcSmarkus 	if (key == NULL ||
14859bab1bcSmarkus 	    sshkey_type_plain(key->type) != KEY_ED25519_SK ||
14959bab1bcSmarkus 	    key->ed25519_pk == NULL ||
150c5c174faSdjm 	    sig == NULL || siglen == 0)
15159bab1bcSmarkus 		return SSH_ERR_INVALID_ARGUMENT;
15259bab1bcSmarkus 
153c5c174faSdjm 	if ((b = sshbuf_from(sig, siglen)) == NULL)
15459bab1bcSmarkus 		return SSH_ERR_ALLOC_FAIL;
15559bab1bcSmarkus 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
15637ada6ffSmarkus 	    sshbuf_get_string_direct(b, &sigblob, &len) != 0 ||
15737ada6ffSmarkus 	    sshbuf_get_u8(b, &sig_flags) != 0 ||
15837ada6ffSmarkus 	    sshbuf_get_u32(b, &sig_counter) != 0) {
15959bab1bcSmarkus 		r = SSH_ERR_INVALID_FORMAT;
16059bab1bcSmarkus 		goto out;
16159bab1bcSmarkus 	}
162584bea6cSdjm #ifdef DEBUG_SK
163584bea6cSdjm 	fprintf(stderr, "%s: data:\n", __func__);
164584bea6cSdjm 	/* sshbuf_dump_data(data, datalen, stderr); */
165584bea6cSdjm 	fprintf(stderr, "%s: sigblob:\n", __func__);
166584bea6cSdjm 	sshbuf_dump_data(sigblob, len, stderr);
167584bea6cSdjm 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
168584bea6cSdjm 	    __func__, sig_flags, sig_counter);
169584bea6cSdjm #endif
17059bab1bcSmarkus 	if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
17159bab1bcSmarkus 		r = SSH_ERR_KEY_TYPE_MISMATCH;
17259bab1bcSmarkus 		goto out;
17359bab1bcSmarkus 	}
17459bab1bcSmarkus 	if (sshbuf_len(b) != 0) {
17559bab1bcSmarkus 		r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
17659bab1bcSmarkus 		goto out;
17759bab1bcSmarkus 	}
17859bab1bcSmarkus 	if (len > crypto_sign_ed25519_BYTES) {
17959bab1bcSmarkus 		r = SSH_ERR_INVALID_FORMAT;
18059bab1bcSmarkus 		goto out;
18159bab1bcSmarkus 	}
18259bab1bcSmarkus 	if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
18359bab1bcSmarkus 	    strlen(key->sk_application), apphash, sizeof(apphash)) != 0 ||
184c5c174faSdjm 	    ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen,
18559bab1bcSmarkus 	    msghash, sizeof(msghash)) != 0) {
18659bab1bcSmarkus 		r = SSH_ERR_INVALID_ARGUMENT;
18759bab1bcSmarkus 		goto out;
18859bab1bcSmarkus 	}
189584bea6cSdjm #ifdef DEBUG_SK
190584bea6cSdjm 	fprintf(stderr, "%s: hashed application:\n", __func__);
191584bea6cSdjm 	sshbuf_dump_data(apphash, sizeof(apphash), stderr);
192584bea6cSdjm 	fprintf(stderr, "%s: hashed message:\n", __func__);
193584bea6cSdjm 	sshbuf_dump_data(msghash, sizeof(msghash), stderr);
194584bea6cSdjm #endif
195493ad5b0Sdjm 	if ((details = calloc(1, sizeof(*details))) == NULL) {
196493ad5b0Sdjm 		r = SSH_ERR_ALLOC_FAIL;
197493ad5b0Sdjm 		goto out;
198493ad5b0Sdjm 	}
199493ad5b0Sdjm 	details->sk_counter = sig_counter;
200493ad5b0Sdjm 	details->sk_flags = sig_flags;
20159bab1bcSmarkus 	if ((encoded = sshbuf_new()) == NULL) {
20259bab1bcSmarkus 		r = SSH_ERR_ALLOC_FAIL;
20359bab1bcSmarkus 		goto out;
20459bab1bcSmarkus 	}
20559bab1bcSmarkus 	if (sshbuf_put(encoded, sigblob, len) != 0 ||
20659bab1bcSmarkus 	    sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 ||
20759bab1bcSmarkus 	    sshbuf_put_u8(encoded, sig_flags) != 0 ||
20859bab1bcSmarkus 	    sshbuf_put_u32(encoded, sig_counter) != 0 ||
20959bab1bcSmarkus 	    sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) {
21059bab1bcSmarkus 		r = SSH_ERR_ALLOC_FAIL;
21159bab1bcSmarkus 		goto out;
21259bab1bcSmarkus 	}
213584bea6cSdjm #ifdef DEBUG_SK
214584bea6cSdjm 	fprintf(stderr, "%s: signed buf:\n", __func__);
215584bea6cSdjm 	sshbuf_dump(encoded, stderr);
216584bea6cSdjm #endif
21759bab1bcSmarkus 	sm = sshbuf_ptr(encoded);
21859bab1bcSmarkus 	smlen = sshbuf_len(encoded);
21959bab1bcSmarkus 	mlen = smlen;
22059bab1bcSmarkus 	if ((m = malloc(smlen)) == NULL) {
22159bab1bcSmarkus 		r = SSH_ERR_ALLOC_FAIL;
22259bab1bcSmarkus 		goto out;
22359bab1bcSmarkus 	}
22459bab1bcSmarkus 	if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
22559bab1bcSmarkus 	    key->ed25519_pk)) != 0) {
22648e6b99dSdjm 		debug2_f("crypto_sign_ed25519_open failed: %d", ret);
22759bab1bcSmarkus 	}
22859bab1bcSmarkus 	if (ret != 0 || mlen != smlen - len) {
22959bab1bcSmarkus 		r = SSH_ERR_SIGNATURE_INVALID;
23059bab1bcSmarkus 		goto out;
23159bab1bcSmarkus 	}
23259bab1bcSmarkus 	/* XXX compare 'm' and 'sm + len' ? */
23359bab1bcSmarkus 	/* success */
23459bab1bcSmarkus 	r = 0;
235493ad5b0Sdjm 	if (detailsp != NULL) {
236493ad5b0Sdjm 		*detailsp = details;
237493ad5b0Sdjm 		details = NULL;
238493ad5b0Sdjm 	}
23959bab1bcSmarkus  out:
240c9831b39Sjsg 	if (m != NULL)
241c9831b39Sjsg 		freezero(m, smlen); /* NB mlen may be invalid if r != 0 */
242493ad5b0Sdjm 	sshkey_sig_details_free(details);
24359bab1bcSmarkus 	sshbuf_free(b);
24459bab1bcSmarkus 	sshbuf_free(encoded);
24559bab1bcSmarkus 	free(ktype);
24659bab1bcSmarkus 	return r;
24759bab1bcSmarkus }
2489c1667dbSdjm 
2499c1667dbSdjm static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = {
2509c1667dbSdjm 	/* .size = */		NULL,
2519c1667dbSdjm 	/* .alloc = */		NULL,
2529c1667dbSdjm 	/* .cleanup = */	ssh_ed25519_sk_cleanup,
253712f5ecfSdjm 	/* .equal = */		ssh_ed25519_sk_equal,
254eefcf659Sdjm 	/* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public,
255c8d92406Sdjm 	/* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public,
256d03db38bSdjm 	/* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private,
257*a2c931d9Sdjm 	/* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private,
258b6025febSdjm 	/* .generate = */	NULL,
2590d39f001Sdjm 	/* .copy_public = */	ssh_ed25519_sk_copy_public,
260c5c174faSdjm 	/* .sign = */		NULL,
261c5c174faSdjm 	/* .verify = */		ssh_ed25519_sk_verify,
2629c1667dbSdjm };
2639c1667dbSdjm 
2649c1667dbSdjm const struct sshkey_impl sshkey_ed25519_sk_impl = {
2659c1667dbSdjm 	/* .name = */		"sk-ssh-ed25519@openssh.com",
2669c1667dbSdjm 	/* .shortname = */	"ED25519-SK",
2679c1667dbSdjm 	/* .sigalg = */		NULL,
2689c1667dbSdjm 	/* .type = */		KEY_ED25519_SK,
2699c1667dbSdjm 	/* .nid = */		0,
2709c1667dbSdjm 	/* .cert = */		0,
2719c1667dbSdjm 	/* .sigonly = */	0,
2729c1667dbSdjm 	/* .keybits = */	256,
2739c1667dbSdjm 	/* .funcs = */		&sshkey_ed25519_sk_funcs,
2749c1667dbSdjm };
2759c1667dbSdjm 
2769c1667dbSdjm const struct sshkey_impl sshkey_ed25519_sk_cert_impl = {
2779c1667dbSdjm 	/* .name = */		"sk-ssh-ed25519-cert-v01@openssh.com",
2789c1667dbSdjm 	/* .shortname = */	"ED25519-SK-CERT",
2799c1667dbSdjm 	/* .sigalg = */		NULL,
2809c1667dbSdjm 	/* .type = */		KEY_ED25519_SK_CERT,
2819c1667dbSdjm 	/* .nid = */		0,
2829c1667dbSdjm 	/* .cert = */		1,
2839c1667dbSdjm 	/* .sigonly = */	0,
2849c1667dbSdjm 	/* .keybits = */	256,
2859c1667dbSdjm 	/* .funcs = */		&sshkey_ed25519_sk_funcs,
2869c1667dbSdjm };
287