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