1d75efeb7Sdjm /*
2ab19a69eSdjm * Copyright (c) 2018-2022 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/rsa.h>
9d75efeb7Sdjm #include <openssl/obj_mac.h>
10d75efeb7Sdjm
11d75efeb7Sdjm #include "fido.h"
12d75efeb7Sdjm #include "fido/rs256.h"
13d75efeb7Sdjm
14ab19a69eSdjm #if OPENSSL_VERSION_NUMBER >= 0x30000000
15ab19a69eSdjm #define get0_RSA(x) EVP_PKEY_get0_RSA((x))
16ab19a69eSdjm #else
17ab19a69eSdjm #define get0_RSA(x) EVP_PKEY_get0((x))
18ab19a69eSdjm #endif
19d75efeb7Sdjm
20*7dcdad29Stb #define PRAGMA(s)
21ab19a69eSdjm
22ab19a69eSdjm static EVP_MD *
rs256_get_EVP_MD(void)23ab19a69eSdjm rs256_get_EVP_MD(void)
24ab19a69eSdjm {
25*7dcdad29Stb PRAGMA("GCC diagnostic push");
26*7dcdad29Stb PRAGMA("GCC diagnostic ignored \"-Wcast-qual\"");
27aeab8fe0Stb return ((EVP_MD *)EVP_sha256());
28*7dcdad29Stb PRAGMA("GCC diagnostic pop");
29ab19a69eSdjm }
30ab19a69eSdjm
31d75efeb7Sdjm static int
decode_bignum(const cbor_item_t * item,void * ptr,size_t len)32d75efeb7Sdjm decode_bignum(const cbor_item_t *item, void *ptr, size_t len)
33d75efeb7Sdjm {
34d75efeb7Sdjm if (cbor_isa_bytestring(item) == false ||
35d75efeb7Sdjm cbor_bytestring_is_definite(item) == false ||
36d75efeb7Sdjm cbor_bytestring_length(item) != len) {
3732a20e26Sdjm fido_log_debug("%s: cbor type", __func__);
38d75efeb7Sdjm return (-1);
39d75efeb7Sdjm }
40d75efeb7Sdjm
41d75efeb7Sdjm memcpy(ptr, cbor_bytestring_handle(item), len);
42d75efeb7Sdjm
43d75efeb7Sdjm return (0);
44d75efeb7Sdjm }
45d75efeb7Sdjm
46d75efeb7Sdjm static int
decode_rsa_pubkey(const cbor_item_t * key,const cbor_item_t * val,void * arg)47d75efeb7Sdjm decode_rsa_pubkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
48d75efeb7Sdjm {
49d75efeb7Sdjm rs256_pk_t *k = arg;
50d75efeb7Sdjm
51d75efeb7Sdjm if (cbor_isa_negint(key) == false ||
52d75efeb7Sdjm cbor_int_get_width(key) != CBOR_INT_8)
53d75efeb7Sdjm return (0); /* ignore */
54d75efeb7Sdjm
55d75efeb7Sdjm switch (cbor_get_uint8(key)) {
56d75efeb7Sdjm case 0: /* modulus */
57d75efeb7Sdjm return (decode_bignum(val, &k->n, sizeof(k->n)));
58d75efeb7Sdjm case 1: /* public exponent */
59d75efeb7Sdjm return (decode_bignum(val, &k->e, sizeof(k->e)));
60d75efeb7Sdjm }
61d75efeb7Sdjm
62d75efeb7Sdjm return (0); /* ignore */
63d75efeb7Sdjm }
64d75efeb7Sdjm
65d75efeb7Sdjm int
rs256_pk_decode(const cbor_item_t * item,rs256_pk_t * k)66d75efeb7Sdjm rs256_pk_decode(const cbor_item_t *item, rs256_pk_t *k)
67d75efeb7Sdjm {
68d75efeb7Sdjm if (cbor_isa_map(item) == false ||
69d75efeb7Sdjm cbor_map_is_definite(item) == false ||
70d75efeb7Sdjm cbor_map_iter(item, k, decode_rsa_pubkey) < 0) {
7132a20e26Sdjm fido_log_debug("%s: cbor type", __func__);
72d75efeb7Sdjm return (-1);
73d75efeb7Sdjm }
74d75efeb7Sdjm
75d75efeb7Sdjm return (0);
76d75efeb7Sdjm }
77d75efeb7Sdjm
78d75efeb7Sdjm rs256_pk_t *
rs256_pk_new(void)79d75efeb7Sdjm rs256_pk_new(void)
80d75efeb7Sdjm {
81d75efeb7Sdjm return (calloc(1, sizeof(rs256_pk_t)));
82d75efeb7Sdjm }
83d75efeb7Sdjm
84d75efeb7Sdjm void
rs256_pk_free(rs256_pk_t ** pkp)85d75efeb7Sdjm rs256_pk_free(rs256_pk_t **pkp)
86d75efeb7Sdjm {
87d75efeb7Sdjm rs256_pk_t *pk;
88d75efeb7Sdjm
89d75efeb7Sdjm if (pkp == NULL || (pk = *pkp) == NULL)
90d75efeb7Sdjm return;
91d75efeb7Sdjm
92c4a807edSdjm freezero(pk, sizeof(*pk));
93d75efeb7Sdjm *pkp = NULL;
94d75efeb7Sdjm }
95d75efeb7Sdjm
96d75efeb7Sdjm int
rs256_pk_from_ptr(rs256_pk_t * pk,const void * ptr,size_t len)97d75efeb7Sdjm rs256_pk_from_ptr(rs256_pk_t *pk, const void *ptr, size_t len)
98d75efeb7Sdjm {
99ab19a69eSdjm EVP_PKEY *pkey;
100ab19a69eSdjm
101d75efeb7Sdjm if (len < sizeof(*pk))
102d75efeb7Sdjm return (FIDO_ERR_INVALID_ARGUMENT);
103d75efeb7Sdjm
104d75efeb7Sdjm memcpy(pk, ptr, sizeof(*pk));
105d75efeb7Sdjm
106ab19a69eSdjm if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
107ab19a69eSdjm fido_log_debug("%s: rs256_pk_to_EVP_PKEY", __func__);
108ab19a69eSdjm return (FIDO_ERR_INVALID_ARGUMENT);
109ab19a69eSdjm }
110ab19a69eSdjm
111ab19a69eSdjm EVP_PKEY_free(pkey);
112ab19a69eSdjm
113d75efeb7Sdjm return (FIDO_OK);
114d75efeb7Sdjm }
115d75efeb7Sdjm
116d75efeb7Sdjm EVP_PKEY *
rs256_pk_to_EVP_PKEY(const rs256_pk_t * k)117d75efeb7Sdjm rs256_pk_to_EVP_PKEY(const rs256_pk_t *k)
118d75efeb7Sdjm {
119d75efeb7Sdjm RSA *rsa = NULL;
120d75efeb7Sdjm EVP_PKEY *pkey = NULL;
121d75efeb7Sdjm BIGNUM *n = NULL;
122d75efeb7Sdjm BIGNUM *e = NULL;
123d75efeb7Sdjm int ok = -1;
124d75efeb7Sdjm
125d75efeb7Sdjm if ((n = BN_new()) == NULL || (e = BN_new()) == NULL)
126d75efeb7Sdjm goto fail;
127d75efeb7Sdjm
128d75efeb7Sdjm if (BN_bin2bn(k->n, sizeof(k->n), n) == NULL ||
129d75efeb7Sdjm BN_bin2bn(k->e, sizeof(k->e), e) == NULL) {
13032a20e26Sdjm fido_log_debug("%s: BN_bin2bn", __func__);
131d75efeb7Sdjm goto fail;
132d75efeb7Sdjm }
133d75efeb7Sdjm
134d75efeb7Sdjm if ((rsa = RSA_new()) == NULL || RSA_set0_key(rsa, n, e, NULL) == 0) {
13532a20e26Sdjm fido_log_debug("%s: RSA_set0_key", __func__);
136d75efeb7Sdjm goto fail;
137d75efeb7Sdjm }
138d75efeb7Sdjm
139d75efeb7Sdjm /* at this point, n and e belong to rsa */
140d75efeb7Sdjm n = NULL;
141d75efeb7Sdjm e = NULL;
142d75efeb7Sdjm
143ab19a69eSdjm if (RSA_bits(rsa) != 2048) {
144ab19a69eSdjm fido_log_debug("%s: invalid key length", __func__);
145ab19a69eSdjm goto fail;
146ab19a69eSdjm }
147ab19a69eSdjm
148d75efeb7Sdjm if ((pkey = EVP_PKEY_new()) == NULL ||
149d75efeb7Sdjm EVP_PKEY_assign_RSA(pkey, rsa) == 0) {
15032a20e26Sdjm fido_log_debug("%s: EVP_PKEY_assign_RSA", __func__);
151d75efeb7Sdjm goto fail;
152d75efeb7Sdjm }
153d75efeb7Sdjm
154d75efeb7Sdjm rsa = NULL; /* at this point, rsa belongs to evp */
155d75efeb7Sdjm
156d75efeb7Sdjm ok = 0;
157d75efeb7Sdjm fail:
158d75efeb7Sdjm if (n != NULL)
159d75efeb7Sdjm BN_free(n);
160d75efeb7Sdjm if (e != NULL)
161d75efeb7Sdjm BN_free(e);
162d75efeb7Sdjm if (rsa != NULL)
163d75efeb7Sdjm RSA_free(rsa);
164d75efeb7Sdjm if (ok < 0 && pkey != NULL) {
165d75efeb7Sdjm EVP_PKEY_free(pkey);
166d75efeb7Sdjm pkey = NULL;
167d75efeb7Sdjm }
168d75efeb7Sdjm
169d75efeb7Sdjm return (pkey);
170d75efeb7Sdjm }
171d75efeb7Sdjm
172d75efeb7Sdjm int
rs256_pk_from_RSA(rs256_pk_t * pk,const RSA * rsa)173d75efeb7Sdjm rs256_pk_from_RSA(rs256_pk_t *pk, const RSA *rsa)
174d75efeb7Sdjm {
175d75efeb7Sdjm const BIGNUM *n = NULL;
176d75efeb7Sdjm const BIGNUM *e = NULL;
177d75efeb7Sdjm const BIGNUM *d = NULL;
178d75efeb7Sdjm int k;
179d75efeb7Sdjm
180d75efeb7Sdjm if (RSA_bits(rsa) != 2048) {
18132a20e26Sdjm fido_log_debug("%s: invalid key length", __func__);
182d75efeb7Sdjm return (FIDO_ERR_INVALID_ARGUMENT);
183d75efeb7Sdjm }
184d75efeb7Sdjm
185d75efeb7Sdjm RSA_get0_key(rsa, &n, &e, &d);
186d75efeb7Sdjm
187d75efeb7Sdjm if (n == NULL || e == NULL) {
18832a20e26Sdjm fido_log_debug("%s: RSA_get0_key", __func__);
189d75efeb7Sdjm return (FIDO_ERR_INTERNAL);
190d75efeb7Sdjm }
191d75efeb7Sdjm
192d75efeb7Sdjm if ((k = BN_num_bytes(n)) < 0 || (size_t)k > sizeof(pk->n) ||
193d75efeb7Sdjm (k = BN_num_bytes(e)) < 0 || (size_t)k > sizeof(pk->e)) {
19432a20e26Sdjm fido_log_debug("%s: invalid key", __func__);
195d75efeb7Sdjm return (FIDO_ERR_INTERNAL);
196d75efeb7Sdjm }
197d75efeb7Sdjm
198d75efeb7Sdjm if ((k = BN_bn2bin(n, pk->n)) < 0 || (size_t)k > sizeof(pk->n) ||
199d75efeb7Sdjm (k = BN_bn2bin(e, pk->e)) < 0 || (size_t)k > sizeof(pk->e)) {
20032a20e26Sdjm fido_log_debug("%s: BN_bn2bin", __func__);
201d75efeb7Sdjm return (FIDO_ERR_INTERNAL);
202d75efeb7Sdjm }
203d75efeb7Sdjm
204d75efeb7Sdjm return (FIDO_OK);
205d75efeb7Sdjm }
206ab19a69eSdjm
207ab19a69eSdjm int
rs256_pk_from_EVP_PKEY(rs256_pk_t * pk,const EVP_PKEY * pkey)208ab19a69eSdjm rs256_pk_from_EVP_PKEY(rs256_pk_t *pk, const EVP_PKEY *pkey)
209ab19a69eSdjm {
210ab19a69eSdjm const RSA *rsa;
211ab19a69eSdjm
212ab19a69eSdjm if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA ||
213ab19a69eSdjm (rsa = get0_RSA(pkey)) == NULL)
214ab19a69eSdjm return (FIDO_ERR_INVALID_ARGUMENT);
215ab19a69eSdjm
216ab19a69eSdjm return (rs256_pk_from_RSA(pk, rsa));
217ab19a69eSdjm }
218ab19a69eSdjm
219ab19a69eSdjm int
rs256_verify_sig(const fido_blob_t * dgst,EVP_PKEY * pkey,const fido_blob_t * sig)220ab19a69eSdjm rs256_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
221ab19a69eSdjm const fido_blob_t *sig)
222ab19a69eSdjm {
223ab19a69eSdjm EVP_PKEY_CTX *pctx = NULL;
224ab19a69eSdjm EVP_MD *md = NULL;
225ab19a69eSdjm int ok = -1;
226ab19a69eSdjm
227ab19a69eSdjm if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
228ab19a69eSdjm fido_log_debug("%s: EVP_PKEY_base_id", __func__);
229ab19a69eSdjm goto fail;
230ab19a69eSdjm }
231ab19a69eSdjm
232ab19a69eSdjm if ((md = rs256_get_EVP_MD()) == NULL) {
233ab19a69eSdjm fido_log_debug("%s: rs256_get_EVP_MD", __func__);
234ab19a69eSdjm goto fail;
235ab19a69eSdjm }
236ab19a69eSdjm
237ab19a69eSdjm if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
238ab19a69eSdjm EVP_PKEY_verify_init(pctx) != 1 ||
239ab19a69eSdjm EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1 ||
240ab19a69eSdjm EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) {
241ab19a69eSdjm fido_log_debug("%s: EVP_PKEY_CTX", __func__);
242ab19a69eSdjm goto fail;
243ab19a69eSdjm }
244ab19a69eSdjm
245ab19a69eSdjm if (EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
246ab19a69eSdjm dgst->len) != 1) {
247ab19a69eSdjm fido_log_debug("%s: EVP_PKEY_verify", __func__);
248ab19a69eSdjm goto fail;
249ab19a69eSdjm }
250ab19a69eSdjm
251ab19a69eSdjm ok = 0;
252ab19a69eSdjm fail:
253ab19a69eSdjm EVP_PKEY_CTX_free(pctx);
254ab19a69eSdjm
255ab19a69eSdjm return (ok);
256ab19a69eSdjm }
257ab19a69eSdjm
258ab19a69eSdjm int
rs256_pk_verify_sig(const fido_blob_t * dgst,const rs256_pk_t * pk,const fido_blob_t * sig)259ab19a69eSdjm rs256_pk_verify_sig(const fido_blob_t *dgst, const rs256_pk_t *pk,
260ab19a69eSdjm const fido_blob_t *sig)
261ab19a69eSdjm {
262ab19a69eSdjm EVP_PKEY *pkey;
263ab19a69eSdjm int ok = -1;
264ab19a69eSdjm
265ab19a69eSdjm if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL ||
266ab19a69eSdjm rs256_verify_sig(dgst, pkey, sig) < 0) {
267ab19a69eSdjm fido_log_debug("%s: rs256_verify_sig", __func__);
268ab19a69eSdjm goto fail;
269ab19a69eSdjm }
270ab19a69eSdjm
271ab19a69eSdjm ok = 0;
272ab19a69eSdjm fail:
273ab19a69eSdjm EVP_PKEY_free(pkey);
274ab19a69eSdjm
275ab19a69eSdjm return (ok);
276ab19a69eSdjm }
277