Home | History | Annotate | Line # | Download | only in src
      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
     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
     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
     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
    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
    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
    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
    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
    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