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