xref: /dflybsd-src/crypto/openssh/ssh-ed25519-sk.c (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
1*ba1276acSMatthew Dillon /* $OpenBSD: ssh-ed25519-sk.c,v 1.15 2022/10/28 00:44:44 djm Exp $ */
20cbfa66cSDaniel Fojt /*
30cbfa66cSDaniel Fojt  * Copyright (c) 2019 Markus Friedl.  All rights reserved.
40cbfa66cSDaniel Fojt  *
50cbfa66cSDaniel Fojt  * Permission to use, copy, modify, and distribute this software for any
60cbfa66cSDaniel Fojt  * purpose with or without fee is hereby granted, provided that the above
70cbfa66cSDaniel Fojt  * copyright notice and this permission notice appear in all copies.
80cbfa66cSDaniel Fojt  *
90cbfa66cSDaniel Fojt  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
100cbfa66cSDaniel Fojt  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
110cbfa66cSDaniel Fojt  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
120cbfa66cSDaniel Fojt  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
130cbfa66cSDaniel Fojt  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
140cbfa66cSDaniel Fojt  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
150cbfa66cSDaniel Fojt  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
160cbfa66cSDaniel Fojt  */
170cbfa66cSDaniel Fojt 
180cbfa66cSDaniel Fojt /* #define DEBUG_SK 1 */
190cbfa66cSDaniel Fojt 
200cbfa66cSDaniel Fojt #include "includes.h"
210cbfa66cSDaniel Fojt 
220cbfa66cSDaniel Fojt #define SSHKEY_INTERNAL
230cbfa66cSDaniel Fojt #include <sys/types.h>
240cbfa66cSDaniel Fojt #include <limits.h>
250cbfa66cSDaniel Fojt 
260cbfa66cSDaniel Fojt #include "crypto_api.h"
270cbfa66cSDaniel Fojt 
280cbfa66cSDaniel Fojt #include <string.h>
290cbfa66cSDaniel Fojt #include <stdarg.h>
300cbfa66cSDaniel Fojt 
310cbfa66cSDaniel Fojt #include "log.h"
320cbfa66cSDaniel Fojt #include "sshbuf.h"
330cbfa66cSDaniel Fojt #include "sshkey.h"
340cbfa66cSDaniel Fojt #include "ssherr.h"
350cbfa66cSDaniel Fojt #include "ssh.h"
360cbfa66cSDaniel Fojt #include "digest.h"
370cbfa66cSDaniel Fojt 
38*ba1276acSMatthew Dillon /* Reuse some ED25519 internals */
39*ba1276acSMatthew Dillon extern struct sshkey_impl_funcs sshkey_ed25519_funcs;
40*ba1276acSMatthew Dillon 
41*ba1276acSMatthew Dillon static void
ssh_ed25519_sk_cleanup(struct sshkey * k)42*ba1276acSMatthew Dillon ssh_ed25519_sk_cleanup(struct sshkey *k)
43*ba1276acSMatthew Dillon {
44*ba1276acSMatthew Dillon 	sshkey_sk_cleanup(k);
45*ba1276acSMatthew Dillon 	sshkey_ed25519_funcs.cleanup(k);
46*ba1276acSMatthew Dillon }
47*ba1276acSMatthew Dillon 
48*ba1276acSMatthew Dillon static int
ssh_ed25519_sk_equal(const struct sshkey * a,const struct sshkey * b)49*ba1276acSMatthew Dillon ssh_ed25519_sk_equal(const struct sshkey *a, const struct sshkey *b)
50*ba1276acSMatthew Dillon {
51*ba1276acSMatthew Dillon 	if (!sshkey_sk_fields_equal(a, b))
52*ba1276acSMatthew Dillon 		return 0;
53*ba1276acSMatthew Dillon 	if (!sshkey_ed25519_funcs.equal(a, b))
54*ba1276acSMatthew Dillon 		return 0;
55*ba1276acSMatthew Dillon 	return 1;
56*ba1276acSMatthew Dillon }
57*ba1276acSMatthew Dillon 
58*ba1276acSMatthew Dillon static int
ssh_ed25519_sk_serialize_public(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)59*ba1276acSMatthew Dillon ssh_ed25519_sk_serialize_public(const struct sshkey *key, struct sshbuf *b,
60*ba1276acSMatthew Dillon     enum sshkey_serialize_rep opts)
61*ba1276acSMatthew Dillon {
62*ba1276acSMatthew Dillon 	int r;
63*ba1276acSMatthew Dillon 
64*ba1276acSMatthew Dillon 	if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0)
65*ba1276acSMatthew Dillon 		return r;
66*ba1276acSMatthew Dillon 	if ((r = sshkey_serialize_sk(key, b)) != 0)
67*ba1276acSMatthew Dillon 		return r;
68*ba1276acSMatthew Dillon 
69*ba1276acSMatthew Dillon 	return 0;
70*ba1276acSMatthew Dillon }
71*ba1276acSMatthew Dillon 
72*ba1276acSMatthew Dillon static int
ssh_ed25519_sk_serialize_private(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)73*ba1276acSMatthew Dillon ssh_ed25519_sk_serialize_private(const struct sshkey *key, struct sshbuf *b,
74*ba1276acSMatthew Dillon     enum sshkey_serialize_rep opts)
75*ba1276acSMatthew Dillon {
76*ba1276acSMatthew Dillon 	int r;
77*ba1276acSMatthew Dillon 
78*ba1276acSMatthew Dillon 	if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0)
79*ba1276acSMatthew Dillon 		return r;
80*ba1276acSMatthew Dillon 	if ((r = sshkey_serialize_private_sk(key, b)) != 0)
81*ba1276acSMatthew Dillon 		return r;
82*ba1276acSMatthew Dillon 
83*ba1276acSMatthew Dillon 	return 0;
84*ba1276acSMatthew Dillon }
85*ba1276acSMatthew Dillon 
86*ba1276acSMatthew Dillon static int
ssh_ed25519_sk_copy_public(const struct sshkey * from,struct sshkey * to)87*ba1276acSMatthew Dillon ssh_ed25519_sk_copy_public(const struct sshkey *from, struct sshkey *to)
88*ba1276acSMatthew Dillon {
89*ba1276acSMatthew Dillon 	int r;
90*ba1276acSMatthew Dillon 
91*ba1276acSMatthew Dillon 	if ((r = sshkey_ed25519_funcs.copy_public(from, to)) != 0)
92*ba1276acSMatthew Dillon 		return r;
93*ba1276acSMatthew Dillon 	if ((r = sshkey_copy_public_sk(from, to)) != 0)
94*ba1276acSMatthew Dillon 		return r;
95*ba1276acSMatthew Dillon 	return 0;
96*ba1276acSMatthew Dillon }
97*ba1276acSMatthew Dillon 
98*ba1276acSMatthew Dillon static int
ssh_ed25519_sk_deserialize_public(const char * ktype,struct sshbuf * b,struct sshkey * key)99*ba1276acSMatthew Dillon ssh_ed25519_sk_deserialize_public(const char *ktype, struct sshbuf *b,
100*ba1276acSMatthew Dillon     struct sshkey *key)
101*ba1276acSMatthew Dillon {
102*ba1276acSMatthew Dillon 	int r;
103*ba1276acSMatthew Dillon 
104*ba1276acSMatthew Dillon 	if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0)
105*ba1276acSMatthew Dillon 		return r;
106*ba1276acSMatthew Dillon 	if ((r = sshkey_deserialize_sk(b, key)) != 0)
107*ba1276acSMatthew Dillon 		return r;
108*ba1276acSMatthew Dillon 	return 0;
109*ba1276acSMatthew Dillon }
110*ba1276acSMatthew Dillon 
111*ba1276acSMatthew Dillon static int
ssh_ed25519_sk_deserialize_private(const char * ktype,struct sshbuf * b,struct sshkey * key)112*ba1276acSMatthew Dillon ssh_ed25519_sk_deserialize_private(const char *ktype, struct sshbuf *b,
113*ba1276acSMatthew Dillon     struct sshkey *key)
114*ba1276acSMatthew Dillon {
115*ba1276acSMatthew Dillon 	int r;
116*ba1276acSMatthew Dillon 
117*ba1276acSMatthew Dillon 	if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0)
118*ba1276acSMatthew Dillon 		return r;
119*ba1276acSMatthew Dillon 	if ((r = sshkey_private_deserialize_sk(b, key)) != 0)
120*ba1276acSMatthew Dillon 		return r;
121*ba1276acSMatthew Dillon 	return 0;
122*ba1276acSMatthew Dillon }
123*ba1276acSMatthew Dillon 
124*ba1276acSMatthew Dillon 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)1250cbfa66cSDaniel Fojt ssh_ed25519_sk_verify(const struct sshkey *key,
126*ba1276acSMatthew Dillon     const u_char *sig, size_t siglen,
127*ba1276acSMatthew Dillon     const u_char *data, size_t dlen, const char *alg, u_int compat,
1280cbfa66cSDaniel Fojt     struct sshkey_sig_details **detailsp)
1290cbfa66cSDaniel Fojt {
1300cbfa66cSDaniel Fojt 	struct sshbuf *b = NULL;
1310cbfa66cSDaniel Fojt 	struct sshbuf *encoded = NULL;
1320cbfa66cSDaniel Fojt 	char *ktype = NULL;
1330cbfa66cSDaniel Fojt 	const u_char *sigblob;
1340cbfa66cSDaniel Fojt 	const u_char *sm;
1350cbfa66cSDaniel Fojt 	u_char *m = NULL;
1360cbfa66cSDaniel Fojt 	u_char apphash[32];
1370cbfa66cSDaniel Fojt 	u_char msghash[32];
1380cbfa66cSDaniel Fojt 	u_char sig_flags;
1390cbfa66cSDaniel Fojt 	u_int sig_counter;
1400cbfa66cSDaniel Fojt 	size_t len;
1410cbfa66cSDaniel Fojt 	unsigned long long smlen = 0, mlen = 0;
1420cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
1430cbfa66cSDaniel Fojt 	int ret;
1440cbfa66cSDaniel Fojt 	struct sshkey_sig_details *details = NULL;
1450cbfa66cSDaniel Fojt 
1460cbfa66cSDaniel Fojt 	if (detailsp != NULL)
1470cbfa66cSDaniel Fojt 		*detailsp = NULL;
1480cbfa66cSDaniel Fojt 
1490cbfa66cSDaniel Fojt 	if (key == NULL ||
1500cbfa66cSDaniel Fojt 	    sshkey_type_plain(key->type) != KEY_ED25519_SK ||
1510cbfa66cSDaniel Fojt 	    key->ed25519_pk == NULL ||
152*ba1276acSMatthew Dillon 	    sig == NULL || siglen == 0)
1530cbfa66cSDaniel Fojt 		return SSH_ERR_INVALID_ARGUMENT;
1540cbfa66cSDaniel Fojt 
155*ba1276acSMatthew Dillon 	if ((b = sshbuf_from(sig, siglen)) == NULL)
1560cbfa66cSDaniel Fojt 		return SSH_ERR_ALLOC_FAIL;
1570cbfa66cSDaniel Fojt 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
1580cbfa66cSDaniel Fojt 	    sshbuf_get_string_direct(b, &sigblob, &len) != 0 ||
1590cbfa66cSDaniel Fojt 	    sshbuf_get_u8(b, &sig_flags) != 0 ||
1600cbfa66cSDaniel Fojt 	    sshbuf_get_u32(b, &sig_counter) != 0) {
1610cbfa66cSDaniel Fojt 		r = SSH_ERR_INVALID_FORMAT;
1620cbfa66cSDaniel Fojt 		goto out;
1630cbfa66cSDaniel Fojt 	}
1640cbfa66cSDaniel Fojt #ifdef DEBUG_SK
1650cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: data:\n", __func__);
1660cbfa66cSDaniel Fojt 	/* sshbuf_dump_data(data, datalen, stderr); */
1670cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: sigblob:\n", __func__);
1680cbfa66cSDaniel Fojt 	sshbuf_dump_data(sigblob, len, stderr);
1690cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
1700cbfa66cSDaniel Fojt 	    __func__, sig_flags, sig_counter);
1710cbfa66cSDaniel Fojt #endif
1720cbfa66cSDaniel Fojt 	if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
1730cbfa66cSDaniel Fojt 		r = SSH_ERR_KEY_TYPE_MISMATCH;
1740cbfa66cSDaniel Fojt 		goto out;
1750cbfa66cSDaniel Fojt 	}
1760cbfa66cSDaniel Fojt 	if (sshbuf_len(b) != 0) {
1770cbfa66cSDaniel Fojt 		r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
1780cbfa66cSDaniel Fojt 		goto out;
1790cbfa66cSDaniel Fojt 	}
1800cbfa66cSDaniel Fojt 	if (len > crypto_sign_ed25519_BYTES) {
1810cbfa66cSDaniel Fojt 		r = SSH_ERR_INVALID_FORMAT;
1820cbfa66cSDaniel Fojt 		goto out;
1830cbfa66cSDaniel Fojt 	}
1840cbfa66cSDaniel Fojt 	if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
1850cbfa66cSDaniel Fojt 	    strlen(key->sk_application), apphash, sizeof(apphash)) != 0 ||
186*ba1276acSMatthew Dillon 	    ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen,
1870cbfa66cSDaniel Fojt 	    msghash, sizeof(msghash)) != 0) {
1880cbfa66cSDaniel Fojt 		r = SSH_ERR_INVALID_ARGUMENT;
1890cbfa66cSDaniel Fojt 		goto out;
1900cbfa66cSDaniel Fojt 	}
1910cbfa66cSDaniel Fojt #ifdef DEBUG_SK
1920cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: hashed application:\n", __func__);
1930cbfa66cSDaniel Fojt 	sshbuf_dump_data(apphash, sizeof(apphash), stderr);
1940cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: hashed message:\n", __func__);
1950cbfa66cSDaniel Fojt 	sshbuf_dump_data(msghash, sizeof(msghash), stderr);
1960cbfa66cSDaniel Fojt #endif
1970cbfa66cSDaniel Fojt 	if ((details = calloc(1, sizeof(*details))) == NULL) {
1980cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
1990cbfa66cSDaniel Fojt 		goto out;
2000cbfa66cSDaniel Fojt 	}
2010cbfa66cSDaniel Fojt 	details->sk_counter = sig_counter;
2020cbfa66cSDaniel Fojt 	details->sk_flags = sig_flags;
2030cbfa66cSDaniel Fojt 	if ((encoded = sshbuf_new()) == NULL) {
2040cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
2050cbfa66cSDaniel Fojt 		goto out;
2060cbfa66cSDaniel Fojt 	}
2070cbfa66cSDaniel Fojt 	if (sshbuf_put(encoded, sigblob, len) != 0 ||
2080cbfa66cSDaniel Fojt 	    sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 ||
2090cbfa66cSDaniel Fojt 	    sshbuf_put_u8(encoded, sig_flags) != 0 ||
2100cbfa66cSDaniel Fojt 	    sshbuf_put_u32(encoded, sig_counter) != 0 ||
2110cbfa66cSDaniel Fojt 	    sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) {
2120cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
2130cbfa66cSDaniel Fojt 		goto out;
2140cbfa66cSDaniel Fojt 	}
2150cbfa66cSDaniel Fojt #ifdef DEBUG_SK
2160cbfa66cSDaniel Fojt 	fprintf(stderr, "%s: signed buf:\n", __func__);
2170cbfa66cSDaniel Fojt 	sshbuf_dump(encoded, stderr);
2180cbfa66cSDaniel Fojt #endif
2190cbfa66cSDaniel Fojt 	sm = sshbuf_ptr(encoded);
2200cbfa66cSDaniel Fojt 	smlen = sshbuf_len(encoded);
2210cbfa66cSDaniel Fojt 	mlen = smlen;
2220cbfa66cSDaniel Fojt 	if ((m = malloc(smlen)) == NULL) {
2230cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
2240cbfa66cSDaniel Fojt 		goto out;
2250cbfa66cSDaniel Fojt 	}
2260cbfa66cSDaniel Fojt 	if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
2270cbfa66cSDaniel Fojt 	    key->ed25519_pk)) != 0) {
22850a69bb5SSascha Wildner 		debug2_f("crypto_sign_ed25519_open failed: %d", ret);
2290cbfa66cSDaniel Fojt 	}
2300cbfa66cSDaniel Fojt 	if (ret != 0 || mlen != smlen - len) {
2310cbfa66cSDaniel Fojt 		r = SSH_ERR_SIGNATURE_INVALID;
2320cbfa66cSDaniel Fojt 		goto out;
2330cbfa66cSDaniel Fojt 	}
2340cbfa66cSDaniel Fojt 	/* XXX compare 'm' and 'sm + len' ? */
2350cbfa66cSDaniel Fojt 	/* success */
2360cbfa66cSDaniel Fojt 	r = 0;
2370cbfa66cSDaniel Fojt 	if (detailsp != NULL) {
2380cbfa66cSDaniel Fojt 		*detailsp = details;
2390cbfa66cSDaniel Fojt 		details = NULL;
2400cbfa66cSDaniel Fojt 	}
2410cbfa66cSDaniel Fojt  out:
2420cbfa66cSDaniel Fojt 	if (m != NULL)
2430cbfa66cSDaniel Fojt 		freezero(m, smlen); /* NB mlen may be invalid if r != 0 */
2440cbfa66cSDaniel Fojt 	sshkey_sig_details_free(details);
2450cbfa66cSDaniel Fojt 	sshbuf_free(b);
2460cbfa66cSDaniel Fojt 	sshbuf_free(encoded);
2470cbfa66cSDaniel Fojt 	free(ktype);
2480cbfa66cSDaniel Fojt 	return r;
2490cbfa66cSDaniel Fojt }
250*ba1276acSMatthew Dillon 
251*ba1276acSMatthew Dillon static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = {
252*ba1276acSMatthew Dillon 	/* .size = */		NULL,
253*ba1276acSMatthew Dillon 	/* .alloc = */		NULL,
254*ba1276acSMatthew Dillon 	/* .cleanup = */	ssh_ed25519_sk_cleanup,
255*ba1276acSMatthew Dillon 	/* .equal = */		ssh_ed25519_sk_equal,
256*ba1276acSMatthew Dillon 	/* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public,
257*ba1276acSMatthew Dillon 	/* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public,
258*ba1276acSMatthew Dillon 	/* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private,
259*ba1276acSMatthew Dillon 	/* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private,
260*ba1276acSMatthew Dillon 	/* .generate = */	NULL,
261*ba1276acSMatthew Dillon 	/* .copy_public = */	ssh_ed25519_sk_copy_public,
262*ba1276acSMatthew Dillon 	/* .sign = */		NULL,
263*ba1276acSMatthew Dillon 	/* .verify = */		ssh_ed25519_sk_verify,
264*ba1276acSMatthew Dillon };
265*ba1276acSMatthew Dillon 
266*ba1276acSMatthew Dillon const struct sshkey_impl sshkey_ed25519_sk_impl = {
267*ba1276acSMatthew Dillon 	/* .name = */		"sk-ssh-ed25519@openssh.com",
268*ba1276acSMatthew Dillon 	/* .shortname = */	"ED25519-SK",
269*ba1276acSMatthew Dillon 	/* .sigalg = */		NULL,
270*ba1276acSMatthew Dillon 	/* .type = */		KEY_ED25519_SK,
271*ba1276acSMatthew Dillon 	/* .nid = */		0,
272*ba1276acSMatthew Dillon 	/* .cert = */		0,
273*ba1276acSMatthew Dillon 	/* .sigonly = */	0,
274*ba1276acSMatthew Dillon 	/* .keybits = */	256,
275*ba1276acSMatthew Dillon 	/* .funcs = */		&sshkey_ed25519_sk_funcs,
276*ba1276acSMatthew Dillon };
277*ba1276acSMatthew Dillon 
278*ba1276acSMatthew Dillon const struct sshkey_impl sshkey_ed25519_sk_cert_impl = {
279*ba1276acSMatthew Dillon 	/* .name = */		"sk-ssh-ed25519-cert-v01@openssh.com",
280*ba1276acSMatthew Dillon 	/* .shortname = */	"ED25519-SK-CERT",
281*ba1276acSMatthew Dillon 	/* .sigalg = */		NULL,
282*ba1276acSMatthew Dillon 	/* .type = */		KEY_ED25519_SK_CERT,
283*ba1276acSMatthew Dillon 	/* .nid = */		0,
284*ba1276acSMatthew Dillon 	/* .cert = */		1,
285*ba1276acSMatthew Dillon 	/* .sigonly = */	0,
286*ba1276acSMatthew Dillon 	/* .keybits = */	256,
287*ba1276acSMatthew Dillon 	/* .funcs = */		&sshkey_ed25519_sk_funcs,
288*ba1276acSMatthew Dillon };
289