1/*
2 * Copyright (c) 2021 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include "fido.h"
8
9static int
10aes256_cbc(const fido_blob_t *key, const u_char *iv, const fido_blob_t *in,
11    fido_blob_t *out, int encrypt)
12{
13	EVP_CIPHER_CTX *ctx = NULL;
14	const EVP_CIPHER *cipher;
15	int ok = -1;
16
17	memset(out, 0, sizeof(*out));
18
19	if (key->len != 32) {
20		fido_log_debug("%s: invalid key len %zu", __func__, key->len);
21		goto fail;
22	}
23	if (in->len > UINT_MAX || in->len % 16 || in->len == 0) {
24		fido_log_debug("%s: invalid input len %zu", __func__, in->len);
25		goto fail;
26	}
27	out->len = in->len;
28	if ((out->ptr = calloc(1, out->len)) == NULL) {
29		fido_log_debug("%s: calloc", __func__);
30		goto fail;
31	}
32	if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
33	    (cipher = EVP_aes_256_cbc()) == NULL) {
34		fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
35		goto fail;
36	}
37	if (EVP_CipherInit(ctx, cipher, key->ptr, iv, encrypt) == 0 ||
38	    EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)out->len) < 0) {
39		fido_log_debug("%s: EVP_Cipher", __func__);
40		goto fail;
41	}
42
43	ok = 0;
44fail:
45	if (ctx != NULL)
46		EVP_CIPHER_CTX_free(ctx);
47	if (ok < 0)
48		fido_blob_reset(out);
49
50	return ok;
51}
52
53static int
54aes256_cbc_proto1(const fido_blob_t *key, const fido_blob_t *in,
55    fido_blob_t *out, int encrypt)
56{
57	u_char iv[16];
58
59	memset(&iv, 0, sizeof(iv));
60
61	return aes256_cbc(key, iv, in, out, encrypt);
62}
63
64static int
65aes256_cbc_fips(const fido_blob_t *secret, const fido_blob_t *in,
66    fido_blob_t *out, int encrypt)
67{
68	fido_blob_t key, cin, cout;
69	u_char iv[16];
70
71	memset(out, 0, sizeof(*out));
72
73	if (secret->len != 64) {
74		fido_log_debug("%s: invalid secret len %zu", __func__,
75		    secret->len);
76		return -1;
77	}
78	if (in->len < sizeof(iv)) {
79		fido_log_debug("%s: invalid input len %zu", __func__, in->len);
80		return -1;
81	}
82	if (encrypt) {
83		if (fido_get_random(iv, sizeof(iv)) < 0) {
84			fido_log_debug("%s: fido_get_random", __func__);
85			return -1;
86		}
87		cin = *in;
88	} else {
89		memcpy(iv, in->ptr, sizeof(iv));
90		cin.ptr = in->ptr + sizeof(iv);
91		cin.len = in->len - sizeof(iv);
92	}
93	key.ptr = secret->ptr + 32;
94	key.len = secret->len - 32;
95	if (aes256_cbc(&key, iv, &cin, &cout, encrypt) < 0)
96		return -1;
97	if (encrypt) {
98		if (cout.len > SIZE_MAX - sizeof(iv) ||
99		    (out->ptr = calloc(1, sizeof(iv) + cout.len)) == NULL) {
100			fido_blob_reset(&cout);
101			return -1;
102		}
103		out->len = sizeof(iv) + cout.len;
104		memcpy(out->ptr, iv, sizeof(iv));
105		memcpy(out->ptr + sizeof(iv), cout.ptr, cout.len);
106		fido_blob_reset(&cout);
107	} else
108		*out = cout;
109
110	return 0;
111}
112
113static int
114aes256_gcm(const fido_blob_t *key, const fido_blob_t *nonce,
115    const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out,
116    int encrypt)
117{
118	EVP_CIPHER_CTX *ctx = NULL;
119	const EVP_CIPHER *cipher;
120	size_t textlen;
121	int ok = -1;
122
123	memset(out, 0, sizeof(*out));
124
125	if (nonce->len != 12 || key->len != 32 || aad->len > UINT_MAX) {
126		fido_log_debug("%s: invalid params %zu, %zu, %zu", __func__,
127		    nonce->len, key->len, aad->len);
128		goto fail;
129	}
130	if (in->len > UINT_MAX || in->len > SIZE_MAX - 16 || in->len < 16) {
131		fido_log_debug("%s: invalid input len %zu", __func__, in->len);
132		goto fail;
133	}
134	/* add tag to (on encrypt) or trim tag from the output (on decrypt) */
135	out->len = encrypt ? in->len + 16 : in->len - 16;
136	if ((out->ptr = calloc(1, out->len)) == NULL) {
137		fido_log_debug("%s: calloc", __func__);
138		goto fail;
139	}
140	if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
141	    (cipher = EVP_aes_256_gcm()) == NULL) {
142		fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
143		goto fail;
144	}
145	if (EVP_CipherInit(ctx, cipher, key->ptr, nonce->ptr, encrypt) == 0) {
146		fido_log_debug("%s: EVP_CipherInit", __func__);
147		goto fail;
148	}
149
150	if (encrypt)
151		textlen = in->len;
152	else {
153		textlen = in->len - 16;
154		/* point openssl at the mac tag */
155		if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
156		    in->ptr + in->len - 16) == 0) {
157			fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
158			goto fail;
159		}
160	}
161	/* the last EVP_Cipher() will either compute or verify the mac tag */
162	if (EVP_Cipher(ctx, NULL, aad->ptr, (u_int)aad->len) < 0 ||
163	    EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)textlen) < 0 ||
164	    EVP_Cipher(ctx, NULL, NULL, 0) < 0) {
165		fido_log_debug("%s: EVP_Cipher", __func__);
166		goto fail;
167	}
168	if (encrypt) {
169		/* append the mac tag */
170		if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16,
171		    out->ptr + out->len - 16) == 0) {
172			fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
173			goto fail;
174		}
175	}
176
177	ok = 0;
178fail:
179	if (ctx != NULL)
180		EVP_CIPHER_CTX_free(ctx);
181	if (ok < 0)
182		fido_blob_reset(out);
183
184	return ok;
185}
186
187int
188aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *secret,
189    const fido_blob_t *in, fido_blob_t *out)
190{
191	return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
192	    in, out, 1) : aes256_cbc_proto1(secret, in, out, 1);
193}
194
195int
196aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *secret,
197    const fido_blob_t *in, fido_blob_t *out)
198{
199	return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
200	    in, out, 0) : aes256_cbc_proto1(secret, in, out, 0);
201}
202
203int
204aes256_gcm_enc(const fido_blob_t *key, const fido_blob_t *nonce,
205    const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
206{
207	return aes256_gcm(key, nonce, aad, in, out, 1);
208}
209
210int
211aes256_gcm_dec(const fido_blob_t *key, const fido_blob_t *nonce,
212    const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
213{
214	return aes256_gcm(key, nonce, aad, in, out, 0);
215}
216