xref: /netbsd-src/external/bsd/libfido2/dist/src/eddsa.c (revision 2d40c4512a84c0d064ec30a492c5e2a14d230bc3)
1ba9bdd8bSchristos /*
2*2d40c451Schristos  * Copyright (c) 2019-2021 Yubico AB. All rights reserved.
3ba9bdd8bSchristos  * Use of this source code is governed by a BSD-style
4ba9bdd8bSchristos  * license that can be found in the LICENSE file.
5*2d40c451Schristos  * SPDX-License-Identifier: BSD-2-Clause
6ba9bdd8bSchristos  */
7ba9bdd8bSchristos 
8ba9bdd8bSchristos #include <openssl/bn.h>
9ba9bdd8bSchristos #include <openssl/obj_mac.h>
10ba9bdd8bSchristos 
11ba9bdd8bSchristos #include "fido.h"
12ba9bdd8bSchristos #include "fido/eddsa.h"
13ba9bdd8bSchristos 
14*2d40c451Schristos #if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3070000f
15ba9bdd8bSchristos EVP_PKEY *
EVP_PKEY_new_raw_public_key(int type,ENGINE * e,const unsigned char * key,size_t keylen)16ba9bdd8bSchristos EVP_PKEY_new_raw_public_key(int type, ENGINE *e, const unsigned char *key,
17ba9bdd8bSchristos     size_t keylen)
18ba9bdd8bSchristos {
19ba9bdd8bSchristos 	(void)type;
20ba9bdd8bSchristos 	(void)e;
21ba9bdd8bSchristos 	(void)key;
22ba9bdd8bSchristos 	(void)keylen;
23ba9bdd8bSchristos 
241fc1e710Schristos 	fido_log_debug("%s: unimplemented", __func__);
251fc1e710Schristos 
26ba9bdd8bSchristos 	return (NULL);
27ba9bdd8bSchristos }
28ba9bdd8bSchristos 
29ba9bdd8bSchristos int
EVP_PKEY_get_raw_public_key(const EVP_PKEY * pkey,unsigned char * pub,size_t * len)30ba9bdd8bSchristos EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
31ba9bdd8bSchristos     size_t *len)
32ba9bdd8bSchristos {
33ba9bdd8bSchristos 	(void)pkey;
34ba9bdd8bSchristos 	(void)pub;
35ba9bdd8bSchristos 	(void)len;
36ba9bdd8bSchristos 
371fc1e710Schristos 	fido_log_debug("%s: unimplemented", __func__);
381fc1e710Schristos 
39ba9bdd8bSchristos 	return (0);
40ba9bdd8bSchristos }
41*2d40c451Schristos #endif /* LIBRESSL_VERSION_NUMBER */
42ba9bdd8bSchristos 
43*2d40c451Schristos #if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3040000f
44ba9bdd8bSchristos int
EVP_DigestVerify(EVP_MD_CTX * ctx,const unsigned char * sigret,size_t siglen,const unsigned char * tbs,size_t tbslen)45ba9bdd8bSchristos EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen,
46ba9bdd8bSchristos     const unsigned char *tbs, size_t tbslen)
47ba9bdd8bSchristos {
48ba9bdd8bSchristos 	(void)ctx;
49ba9bdd8bSchristos 	(void)sigret;
50ba9bdd8bSchristos 	(void)siglen;
51ba9bdd8bSchristos 	(void)tbs;
52ba9bdd8bSchristos 	(void)tbslen;
53ba9bdd8bSchristos 
541fc1e710Schristos 	fido_log_debug("%s: unimplemented", __func__);
551fc1e710Schristos 
56ba9bdd8bSchristos 	return (0);
57ba9bdd8bSchristos }
58*2d40c451Schristos #endif /* LIBRESSL_VERSION_NUMBER < 0x3040000f */
59ba9bdd8bSchristos 
60ba9bdd8bSchristos static int
decode_coord(const cbor_item_t * item,void * xy,size_t xy_len)61ba9bdd8bSchristos decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
62ba9bdd8bSchristos {
63ba9bdd8bSchristos 	if (cbor_isa_bytestring(item) == false ||
64ba9bdd8bSchristos 	    cbor_bytestring_is_definite(item) == false ||
65ba9bdd8bSchristos 	    cbor_bytestring_length(item) != xy_len) {
66ba9bdd8bSchristos 		fido_log_debug("%s: cbor type", __func__);
67ba9bdd8bSchristos 		return (-1);
68ba9bdd8bSchristos 	}
69ba9bdd8bSchristos 
70ba9bdd8bSchristos 	memcpy(xy, cbor_bytestring_handle(item), xy_len);
71ba9bdd8bSchristos 
72ba9bdd8bSchristos 	return (0);
73ba9bdd8bSchristos }
74ba9bdd8bSchristos 
75ba9bdd8bSchristos static int
decode_pubkey_point(const cbor_item_t * key,const cbor_item_t * val,void * arg)76ba9bdd8bSchristos decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
77ba9bdd8bSchristos {
78ba9bdd8bSchristos 	eddsa_pk_t *k = arg;
79ba9bdd8bSchristos 
80ba9bdd8bSchristos 	if (cbor_isa_negint(key) == false ||
81ba9bdd8bSchristos 	    cbor_int_get_width(key) != CBOR_INT_8)
82ba9bdd8bSchristos 		return (0); /* ignore */
83ba9bdd8bSchristos 
84ba9bdd8bSchristos 	switch (cbor_get_uint8(key)) {
85ba9bdd8bSchristos 	case 1: /* x coordinate */
86ba9bdd8bSchristos 		return (decode_coord(val, &k->x, sizeof(k->x)));
87ba9bdd8bSchristos 	}
88ba9bdd8bSchristos 
89ba9bdd8bSchristos 	return (0); /* ignore */
90ba9bdd8bSchristos }
91ba9bdd8bSchristos 
92ba9bdd8bSchristos int
eddsa_pk_decode(const cbor_item_t * item,eddsa_pk_t * k)93ba9bdd8bSchristos eddsa_pk_decode(const cbor_item_t *item, eddsa_pk_t *k)
94ba9bdd8bSchristos {
95ba9bdd8bSchristos 	if (cbor_isa_map(item) == false ||
96ba9bdd8bSchristos 	    cbor_map_is_definite(item) == false ||
97ba9bdd8bSchristos 	    cbor_map_iter(item, k, decode_pubkey_point) < 0) {
98ba9bdd8bSchristos 		fido_log_debug("%s: cbor type", __func__);
99ba9bdd8bSchristos 		return (-1);
100ba9bdd8bSchristos 	}
101ba9bdd8bSchristos 
102ba9bdd8bSchristos 	return (0);
103ba9bdd8bSchristos }
104ba9bdd8bSchristos 
105ba9bdd8bSchristos eddsa_pk_t *
eddsa_pk_new(void)106ba9bdd8bSchristos eddsa_pk_new(void)
107ba9bdd8bSchristos {
108ba9bdd8bSchristos 	return (calloc(1, sizeof(eddsa_pk_t)));
109ba9bdd8bSchristos }
110ba9bdd8bSchristos 
111ba9bdd8bSchristos void
eddsa_pk_free(eddsa_pk_t ** pkp)112ba9bdd8bSchristos eddsa_pk_free(eddsa_pk_t **pkp)
113ba9bdd8bSchristos {
114ba9bdd8bSchristos 	eddsa_pk_t *pk;
115ba9bdd8bSchristos 
116ba9bdd8bSchristos 	if (pkp == NULL || (pk = *pkp) == NULL)
117ba9bdd8bSchristos 		return;
118ba9bdd8bSchristos 
11995dbdf32Schristos 	freezero(pk, sizeof(*pk));
120ba9bdd8bSchristos 	*pkp = NULL;
121ba9bdd8bSchristos }
122ba9bdd8bSchristos 
123ba9bdd8bSchristos int
eddsa_pk_from_ptr(eddsa_pk_t * pk,const void * ptr,size_t len)124ba9bdd8bSchristos eddsa_pk_from_ptr(eddsa_pk_t *pk, const void *ptr, size_t len)
125ba9bdd8bSchristos {
126*2d40c451Schristos 	EVP_PKEY *pkey;
127*2d40c451Schristos 
128ba9bdd8bSchristos 	if (len < sizeof(*pk))
129ba9bdd8bSchristos 		return (FIDO_ERR_INVALID_ARGUMENT);
130ba9bdd8bSchristos 
131ba9bdd8bSchristos 	memcpy(pk, ptr, sizeof(*pk));
132ba9bdd8bSchristos 
133*2d40c451Schristos 	if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
134*2d40c451Schristos 		fido_log_debug("%s: eddsa_pk_to_EVP_PKEY", __func__);
135*2d40c451Schristos 		return (FIDO_ERR_INVALID_ARGUMENT);
136*2d40c451Schristos 	}
137*2d40c451Schristos 
138*2d40c451Schristos 	EVP_PKEY_free(pkey);
139*2d40c451Schristos 
140ba9bdd8bSchristos 	return (FIDO_OK);
141ba9bdd8bSchristos }
142ba9bdd8bSchristos 
143ba9bdd8bSchristos EVP_PKEY *
eddsa_pk_to_EVP_PKEY(const eddsa_pk_t * k)144ba9bdd8bSchristos eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *k)
145ba9bdd8bSchristos {
146ba9bdd8bSchristos 	EVP_PKEY *pkey = NULL;
147ba9bdd8bSchristos 
148ba9bdd8bSchristos 	if ((pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, k->x,
149ba9bdd8bSchristos 	    sizeof(k->x))) == NULL)
150ba9bdd8bSchristos 		fido_log_debug("%s: EVP_PKEY_new_raw_public_key", __func__);
151ba9bdd8bSchristos 
152ba9bdd8bSchristos 	return (pkey);
153ba9bdd8bSchristos }
154ba9bdd8bSchristos 
155ba9bdd8bSchristos int
eddsa_pk_from_EVP_PKEY(eddsa_pk_t * pk,const EVP_PKEY * pkey)156ba9bdd8bSchristos eddsa_pk_from_EVP_PKEY(eddsa_pk_t *pk, const EVP_PKEY *pkey)
157ba9bdd8bSchristos {
158ba9bdd8bSchristos 	size_t len = 0;
159ba9bdd8bSchristos 
160*2d40c451Schristos 	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_ED25519)
161*2d40c451Schristos 		return (FIDO_ERR_INVALID_ARGUMENT);
162ba9bdd8bSchristos 	if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 ||
163ba9bdd8bSchristos 	    len != sizeof(pk->x))
164ba9bdd8bSchristos 		return (FIDO_ERR_INTERNAL);
165ba9bdd8bSchristos 	if (EVP_PKEY_get_raw_public_key(pkey, pk->x, &len) != 1 ||
166ba9bdd8bSchristos 	    len != sizeof(pk->x))
167ba9bdd8bSchristos 		return (FIDO_ERR_INTERNAL);
168ba9bdd8bSchristos 
169ba9bdd8bSchristos 	return (FIDO_OK);
170ba9bdd8bSchristos }
171*2d40c451Schristos 
172*2d40c451Schristos int
eddsa_verify_sig(const fido_blob_t * dgst,EVP_PKEY * pkey,const fido_blob_t * sig)173*2d40c451Schristos eddsa_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
174*2d40c451Schristos     const fido_blob_t *sig)
175*2d40c451Schristos {
176*2d40c451Schristos 	EVP_MD_CTX	*mdctx = NULL;
177*2d40c451Schristos 	int		 ok = -1;
178*2d40c451Schristos 
179*2d40c451Schristos 	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_ED25519) {
180*2d40c451Schristos 		fido_log_debug("%s: EVP_PKEY_base_id", __func__);
181*2d40c451Schristos 		goto fail;
182*2d40c451Schristos 	}
183*2d40c451Schristos 
184*2d40c451Schristos 	/* EVP_DigestVerify needs ints */
185*2d40c451Schristos 	if (dgst->len > INT_MAX || sig->len > INT_MAX) {
186*2d40c451Schristos 		fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
187*2d40c451Schristos 		    dgst->len, sig->len);
188*2d40c451Schristos 		return (-1);
189*2d40c451Schristos 	}
190*2d40c451Schristos 
191*2d40c451Schristos 	if ((mdctx = EVP_MD_CTX_new()) == NULL) {
192*2d40c451Schristos 		fido_log_debug("%s: EVP_MD_CTX_new", __func__);
193*2d40c451Schristos 		goto fail;
194*2d40c451Schristos 	}
195*2d40c451Schristos 
196*2d40c451Schristos 	if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
197*2d40c451Schristos 		fido_log_debug("%s: EVP_DigestVerifyInit", __func__);
198*2d40c451Schristos 		goto fail;
199*2d40c451Schristos 	}
200*2d40c451Schristos 
201*2d40c451Schristos 	if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
202*2d40c451Schristos 	    dgst->len) != 1) {
203*2d40c451Schristos 		fido_log_debug("%s: EVP_DigestVerify", __func__);
204*2d40c451Schristos 		goto fail;
205*2d40c451Schristos 	}
206*2d40c451Schristos 
207*2d40c451Schristos 	ok = 0;
208*2d40c451Schristos fail:
209*2d40c451Schristos 	EVP_MD_CTX_free(mdctx);
210*2d40c451Schristos 
211*2d40c451Schristos 	return (ok);
212*2d40c451Schristos }
213*2d40c451Schristos 
214*2d40c451Schristos int
eddsa_pk_verify_sig(const fido_blob_t * dgst,const eddsa_pk_t * pk,const fido_blob_t * sig)215*2d40c451Schristos eddsa_pk_verify_sig(const fido_blob_t *dgst, const eddsa_pk_t *pk,
216*2d40c451Schristos     const fido_blob_t *sig)
217*2d40c451Schristos {
218*2d40c451Schristos 	EVP_PKEY	*pkey;
219*2d40c451Schristos 	int		 ok = -1;
220*2d40c451Schristos 
221*2d40c451Schristos 	if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL ||
222*2d40c451Schristos 	    eddsa_verify_sig(dgst, pkey, sig) < 0) {
223*2d40c451Schristos 		fido_log_debug("%s: eddsa_verify_sig", __func__);
224*2d40c451Schristos 		goto fail;
225*2d40c451Schristos 	}
226*2d40c451Schristos 
227*2d40c451Schristos 	ok = 0;
228*2d40c451Schristos fail:
229*2d40c451Schristos 	EVP_PKEY_free(pkey);
230*2d40c451Schristos 
231*2d40c451Schristos 	return (ok);
232*2d40c451Schristos }
233