xref: /openbsd-src/lib/libfido2/src/rs256.c (revision 7dcdad297d63e268919cb38314e7671fb269df52)
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