xref: /openbsd/lib/libfido2/src/aes256.c (revision c4a807ed)
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 
9 static int
aes256_cbc(const fido_blob_t * key,const u_char * iv,const fido_blob_t * in,fido_blob_t * out,int encrypt)10 aes256_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;
44 fail:
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 
53 static int
aes256_cbc_proto1(const fido_blob_t * key,const fido_blob_t * in,fido_blob_t * out,int encrypt)54 aes256_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 
64 static int
aes256_cbc_fips(const fido_blob_t * secret,const fido_blob_t * in,fido_blob_t * out,int encrypt)65 aes256_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 
113 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)114 aes256_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;
178 fail:
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 
187 int
aes256_cbc_enc(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * in,fido_blob_t * out)188 aes256_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 
195 int
aes256_cbc_dec(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * in,fido_blob_t * out)196 aes256_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 
203 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)204 aes256_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 
210 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)211 aes256_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