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 || in->len < 16) {
132 fido_log_debug("%s: invalid input len %zu", __func__, in->len);
133 goto fail;
134 }
135 /* add tag to (on encrypt) or trim tag from the output (on decrypt) */
136 out->len = encrypt ? in->len + 16 : in->len - 16;
137 if ((out->ptr = calloc(1, out->len)) == NULL) {
138 fido_log_debug("%s: calloc", __func__);
139 goto fail;
140 }
141 if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
142 (cipher = EVP_aes_256_gcm()) == NULL) {
143 fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
144 goto fail;
145 }
146 if (EVP_CipherInit(ctx, cipher, key->ptr, nonce->ptr, encrypt) == 0) {
147 fido_log_debug("%s: EVP_CipherInit", __func__);
148 goto fail;
149 }
150
151 if (encrypt)
152 textlen = in->len;
153 else {
154 textlen = in->len - 16;
155 /* point openssl at the mac tag */
156 if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
157 in->ptr + in->len - 16) == 0) {
158 fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
159 goto fail;
160 }
161 }
162 /* the last EVP_Cipher() will either compute or verify the mac tag */
163 if (EVP_Cipher(ctx, NULL, aad->ptr, (u_int)aad->len) < 0 ||
164 EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)textlen) < 0 ||
165 EVP_Cipher(ctx, NULL, NULL, 0) < 0) {
166 fido_log_debug("%s: EVP_Cipher", __func__);
167 goto fail;
168 }
169 if (encrypt) {
170 /* append the mac tag */
171 if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16,
172 out->ptr + out->len - 16) == 0) {
173 fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
174 goto fail;
175 }
176 }
177
178 ok = 0;
179 fail:
180 if (ctx != NULL)
181 EVP_CIPHER_CTX_free(ctx);
182 if (ok < 0)
183 fido_blob_reset(out);
184
185 return ok;
186 }
187
188 int
aes256_cbc_enc(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * in,fido_blob_t * out)189 aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *secret,
190 const fido_blob_t *in, fido_blob_t *out)
191 {
192 return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
193 in, out, 1) : aes256_cbc_proto1(secret, in, out, 1);
194 }
195
196 int
aes256_cbc_dec(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * in,fido_blob_t * out)197 aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *secret,
198 const fido_blob_t *in, fido_blob_t *out)
199 {
200 return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
201 in, out, 0) : aes256_cbc_proto1(secret, in, out, 0);
202 }
203
204 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)205 aes256_gcm_enc(const fido_blob_t *key, const fido_blob_t *nonce,
206 const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
207 {
208 return aes256_gcm(key, nonce, aad, in, out, 1);
209 }
210
211 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)212 aes256_gcm_dec(const fido_blob_t *key, const fido_blob_t *nonce,
213 const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
214 {
215 return aes256_gcm(key, nonce, aad, in, out, 0);
216 }
217