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