diff --git a/COPYRIGHTS b/COPYRIGHTS index 2bb3dffe7..97cd84f49 100644 --- a/COPYRIGHTS +++ b/COPYRIGHTS @@ -12,19 +12,29 @@ For code originating from OpenSSL: * Note that in OpenSSL the file crypto/bn/rsa_sup_mul.c does no longer * exist, it was removed with commit https://github.com/openssl/openssl/commit/4209ce68d8fe8b1506494efa03d378d05baf9ff8 * - usr/lib/common/constant_time.h: Copied unchanged from OpenSSL from - include/internal/constant_time.h + * include/internal/constant_time.h * - The implementation of function rsa_parse_block_type_2() in * usr/lib/common/mech_rsa.c is copied from OpenSSL's function * ossl_rsa_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c * and is slightly modified to fit to the OpenCryptoki environment. * See comment in function rsa_parse_block_type_2() for a list of changes. + * - The implementation of function openssl_specific_rsa_derive_kdk() in + * usr/lib/common/mech_openssl.c is copiled from OpenSSL's function + * derive_kdk() in crypto/rsa/rsa_ossl.c and is slightly modified to fit to + * the OpenCryptoki environment. See comment in function + * openssl_specific_rsa_derive_kdk() for a list of changes. + * - The implementation of function openssl_specific_rsa_prf() in + * usr/lib/common/mech_openssl.c is copiled from OpenSSL's function + * ossl_rsa_prf() in crypto/rsa/rsapk1.c and is slightly modified to fit to + * the OpenCryptoki environment. See comment in function + * openssl_specific_rsa_prf() for a list of changes. * - The implementation of function decode_eme_oaep() in * usr/lib/common/mech_rsa.c is copied from OpenSSL's function * RSA_padding_check_PKCS1_OAEP_mgf1() in crypto/rsa/rsa_oaep.c and is * slightly modified to fit to the OpenCryptoki environment. See comment in * function decode_eme_oaep() for a list of changes. * - * Copyright 1999-2023 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1999-2024 The OpenSSL Project Authors. All Rights Reserved. * * The OpenSSL code is licensed under the Apache License 2.0 (the "License"). * You can obtain a copy in the file LICENSE in the OpenSSL source distribution diff --git a/usr/lib/common/h_extern.h b/usr/lib/common/h_extern.h index 7400c6dba..1d79a4f71 100644 --- a/usr/lib/common/h_extern.h +++ b/usr/lib/common/h_extern.h @@ -731,7 +731,8 @@ CK_RV rsa_format_block(STDLL_TokData_t *tokdata, CK_RV rsa_parse_block(CK_BYTE *in_data, CK_ULONG in_data_len, CK_BYTE *out_data, - CK_ULONG *out_data_len, CK_ULONG type); + CK_ULONG *out_data_len, CK_ULONG type, + CK_BYTE *kdk, CK_ULONG kdklen); CK_RV get_mgf_mech(CK_RSA_PKCS_MGF_TYPE mgf, CK_MECHANISM_TYPE *mech); @@ -3182,6 +3183,14 @@ CK_RV openssl_specific_hmac_update(SIGN_VERIFY_CONTEXT *ctx, CK_BYTE *in_data, CK_RV openssl_specific_hmac_final(SIGN_VERIFY_CONTEXT *ctx, CK_BYTE *signature, CK_ULONG *sig_len, CK_BBOOL sign); +CK_RV openssl_specific_rsa_derive_kdk(STDLL_TokData_t *tokdata, OBJECT *key_obj, + const CK_BYTE *in, CK_ULONG inlen, + CK_BYTE *kdk, CK_ULONG kdklen); +CK_RV openssl_specific_rsa_prf(CK_BYTE *out, CK_ULONG outlen, + const char *label, CK_ULONG labellen, + const CK_BYTE *kdk, CK_ULONG kdklen, + uint16_t bitlen); + #include "tok_spec_struct.h" extern token_spec_t token_specific; diff --git a/usr/lib/common/mech_openssl.c b/usr/lib/common/mech_openssl.c index 9983fcb3b..da5152896 100644 --- a/usr/lib/common/mech_openssl.c +++ b/usr/lib/common/mech_openssl.c @@ -1154,6 +1154,7 @@ CK_RV openssl_specific_rsa_pkcs_decrypt(STDLL_TokData_t *tokdata, CK_RV rc; CK_BYTE out[MAX_RSA_KEYLEN]; CK_ULONG modulus_bytes; + unsigned char kdk[SHA256_HASH_SIZE] = { 0 }; modulus_bytes = in_data_len; @@ -1163,7 +1164,16 @@ CK_RV openssl_specific_rsa_pkcs_decrypt(STDLL_TokData_t *tokdata, goto done; } - rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_2); + rc = openssl_specific_rsa_derive_kdk(tokdata, key_obj, + in_data, in_data_len, + kdk, sizeof(kdk)); + if (rc != CKR_OK) { + TRACE_DEVEL("openssl_specific_rsa_derive_kdk failed\n"); + goto done; + } + + rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_2, + kdk, sizeof(kdk)); done: OPENSSL_cleanse(out, sizeof(out)); @@ -1254,7 +1264,7 @@ CK_RV openssl_specific_rsa_pkcs_verify(STDLL_TokData_t *tokdata, SESSION *sess, } rc = rsa_parse_block(out, modulus_bytes, out_data, &out_data_len, - PKCS_BT_1); + PKCS_BT_1, NULL, 0); if (rc == CKR_ENCRYPTED_DATA_INVALID) { TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID)); return CKR_SIGNATURE_INVALID; @@ -1318,7 +1328,8 @@ CK_RV openssl_specific_rsa_pkcs_verify_recover(STDLL_TokData_t *tokdata, return rc; } - rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_1); + rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_1, + NULL, 0); if (rc == CKR_ENCRYPTED_DATA_INVALID) { TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID)); return CKR_SIGNATURE_INVALID; @@ -4983,3 +4994,388 @@ CK_RV openssl_specific_hmac_final(SIGN_VERIFY_CONTEXT *ctx, CK_BYTE *signature, ctx->context = NULL; return rv; } + +static CK_RV calc_rsa_priv_exp(STDLL_TokData_t *tokdata, OBJECT *key_obj, + CK_BYTE *priv_exp, CK_ULONG priv_exp_len) +{ + CK_ATTRIBUTE *modulus = NULL, *pub_exp = NULL; + CK_ATTRIBUTE *prime1 = NULL, *prime2 = NULL; + BN_CTX *bn_ctx; + BIGNUM *n, *e, *p, *q, *d; + CK_RV rc; + + UNUSED(tokdata); + + bn_ctx = BN_CTX_secure_new(); + if (bn_ctx == NULL) { + TRACE_ERROR("BN_CTX_secure_new failed\n"); + return CKR_FUNCTION_FAILED; + } + + /* Get modulus a BIGNUM */ + rc = template_attribute_get_non_empty(key_obj->template, CKA_MODULUS, + &modulus); + if (rc != CKR_OK) { + TRACE_ERROR("Failed to get CKA_MODULUS\n"); + goto done; + } + + n = BN_CTX_get(bn_ctx); + if (n == NULL || + BN_bin2bn(modulus->pValue, modulus->ulValueLen, n) == NULL) { + TRACE_ERROR("BN_CTX_get/BN_bin2bn failed for modulus\n"); + rc = CKR_FUNCTION_FAILED; + goto done; + } + BN_set_flags(n, BN_FLG_CONSTTIME); + + /* Get public exponent a BIGNUM */ + rc = template_attribute_get_non_empty(key_obj->template, + CKA_PUBLIC_EXPONENT, &pub_exp); + if (rc != CKR_OK) { + TRACE_ERROR("Failed to get CKA_PUBLIC_EXPONENT\n"); + goto done; + } + + e = BN_CTX_get(bn_ctx); + if (e == NULL || + BN_bin2bn(pub_exp->pValue, pub_exp->ulValueLen, e) == NULL) { + TRACE_ERROR("BN_CTX_get/BN_bin2bn failed for public exponent\n"); + rc = CKR_FUNCTION_FAILED; + goto done; + } + BN_set_flags(e, BN_FLG_CONSTTIME); + + /* Get prime1 a BIGNUM */ + rc = template_attribute_get_non_empty(key_obj->template, CKA_PRIME_1, + &prime1); + if (rc != CKR_OK) { + TRACE_ERROR("Failed to get CKA_PRIME_1\n"); + goto done; + } + + p = BN_CTX_get(bn_ctx); + if (p == NULL || + BN_bin2bn(prime1->pValue, prime1->ulValueLen, p) == NULL) { + TRACE_ERROR("BN_CTX_get/BN_bin2bn failed for prime1\n"); + rc = CKR_FUNCTION_FAILED; + goto done; + } + BN_set_flags(p, BN_FLG_CONSTTIME); + + /* Get prime2 a BIGNUM */ + rc = template_attribute_get_non_empty(key_obj->template, CKA_PRIME_2, + &prime2); + if (rc != CKR_OK) { + TRACE_ERROR("Failed to get CKA_PRIME_2\n"); + goto done; + } + + q = BN_CTX_get(bn_ctx); + if (q == NULL || + BN_bin2bn(prime2->pValue, prime2->ulValueLen, q) == NULL) { + TRACE_ERROR("BN_CTX_get/BN_bin2bn failed for prime2\n"); + rc = CKR_FUNCTION_FAILED; + goto done; + } + BN_set_flags(q, BN_FLG_CONSTTIME); + + d = BN_CTX_get(bn_ctx); + if (d == NULL) { + TRACE_ERROR("BN_CTX_get failed to get d\n"); + rc = CKR_FUNCTION_FAILED; + goto done; + } + BN_set_flags(d, BN_FLG_CONSTTIME); + + /* + * phi(n) = (p - 1 )(q - 1) = n - p - q + 1 + * d = e ^{-1} mod phi(n). + */ + if (BN_copy(d, n) == NULL || + BN_sub(d, d, p) == 0 || + BN_sub(d, d, q) == 0 || + BN_add_word(d, 1) == 0 || + BN_mod_inverse(d, e, d, bn_ctx) == NULL) { + TRACE_ERROR("Failed to calculate private key part d\n"); + rc = CKR_FUNCTION_FAILED; + goto done; + } + + if (BN_bn2binpad(d, priv_exp, priv_exp_len) <= 0) { + TRACE_ERROR("BN_bn2binpad failed\n"); + rc = CKR_FUNCTION_FAILED; + goto done; + } + +done: + BN_CTX_free(bn_ctx); + + return rc; +} + +CK_RV openssl_specific_rsa_derive_kdk(STDLL_TokData_t *tokdata, OBJECT *key_obj, + const CK_BYTE *in, CK_ULONG inlen, + CK_BYTE *kdk, CK_ULONG kdklen) +{ + CK_ATTRIBUTE *priv_exp_attr = NULL, *modulus = NULL; + CK_BYTE *priv_exp = NULL, *buf = NULL; + EVP_PKEY *pkey = NULL; + EVP_MD_CTX *mdctx = NULL; + const EVP_MD *md = NULL; + size_t md_len; + unsigned char d_hash[SHA256_HASH_SIZE] = { 0 }; + CK_RV rc; + + /* + * The implementation of this function is copied from OpenSSL's function + * derive_kdk() in crypto/rsa/rsa_ossl.c and is slightly modified to fit to + * the OpenCryptoki environment. + * Changes include: + * - Different variable and define names. + * - Usage of TRACE_ERROR to report errors and issue debug messages. + * - Different return codes. + * - Different code to get the private key component 'd'. + * - Use of the EVP APIs instead of the internal APIs for Digest and HMAC + * operations. + */ + + if (kdklen != SHA256_HASH_SIZE) { + TRACE_ERROR("KDK length is wrong\n"); + return CKR_ARGUMENTS_BAD; + } + + rc = template_attribute_get_non_empty(key_obj->template, CKA_MODULUS, + &modulus); + if (rc != CKR_OK) { + TRACE_ERROR("Failed to get CKA_MODULUS\n"); + return rc; + } + + buf = calloc(1, modulus->ulValueLen); + if (buf == NULL) { + TRACE_ERROR("Failed to allocate a buffer for private exponent\n"); + return CKR_HOST_MEMORY; + } + + rc = template_attribute_get_non_empty(key_obj->template, + CKA_PRIVATE_EXPONENT, &priv_exp_attr); + if (rc != CKR_OK && rc != CKR_TEMPLATE_INCOMPLETE) { + TRACE_ERROR("Failed to get CKA_PRIVATE_EXPONENT\n"); + goto out; + } + + if (priv_exp_attr == NULL) { + rc = calc_rsa_priv_exp(tokdata, key_obj, buf, modulus->ulValueLen); + if (rc != CKR_OK) { + TRACE_ERROR("calc_rsa_priv_exp failed\n"); + goto out; + } + priv_exp = buf; + } else { + if (priv_exp_attr->ulValueLen < modulus->ulValueLen) { + memcpy(buf + modulus->ulValueLen - priv_exp_attr->ulValueLen, + priv_exp_attr->pValue, priv_exp_attr->ulValueLen); + priv_exp = buf; + } else { + priv_exp = (CK_BYTE *)priv_exp_attr->pValue + + priv_exp_attr->ulValueLen - modulus->ulValueLen; + } + } + + /* + * we use hardcoded hash so that migrating between versions that use + * different hash doesn't provide a Bleichenbacher oracle: + * if the attacker can see that different versions return different + * messages for the same ciphertext, they'll know that the message is + * synthetically generated, which means that the padding check failed + */ + md = EVP_sha256(); + if (md == NULL) { + TRACE_ERROR("EVP_sha256 failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + if (EVP_Digest(priv_exp, modulus->ulValueLen, d_hash, NULL, + md, NULL) <= 0) { + TRACE_ERROR("EVP_Digest failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, d_hash, sizeof(d_hash)); + if (pkey == NULL) { + TRACE_ERROR("EVP_PKEY_new_mac_key() failed.\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + mdctx = EVP_MD_CTX_create(); + if (mdctx == NULL) { + TRACE_ERROR("EVP_MD_CTX_create() failed.\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + if (EVP_DigestSignInit(mdctx, NULL, md, NULL, pkey) != 1) { + TRACE_ERROR("EVP_DigestSignInit failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + if (inlen < modulus->ulValueLen) { + memset(buf, 0, modulus->ulValueLen - inlen); + if (EVP_DigestSignUpdate(mdctx, buf, modulus->ulValueLen - inlen)!= 1) { + TRACE_ERROR("EVP_DigestSignUpdate failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + } + if (EVP_DigestSignUpdate(mdctx, in, inlen) != 1) { + TRACE_ERROR("EVP_DigestSignUpdate failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + md_len = kdklen; + if (EVP_DigestSignFinal(mdctx, kdk, &md_len) != 1 || + md_len != kdklen) { + TRACE_ERROR("EVP_DigestSignFinal failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + rc = CKR_OK; + +out: + if (buf != NULL) + free(buf); + if (pkey != NULL) + EVP_PKEY_free(pkey); + if (mdctx != NULL) + EVP_MD_CTX_free(mdctx); + + return rc; +} + +CK_RV openssl_specific_rsa_prf(CK_BYTE *out, CK_ULONG outlen, + const char *label, CK_ULONG labellen, + const CK_BYTE *kdk, CK_ULONG kdklen, + uint16_t bitlen) +{ + CK_RV rc; + CK_ULONG pos; + uint16_t iter = 0; + unsigned char be_iter[sizeof(iter)]; + unsigned char be_bitlen[sizeof(bitlen)]; + EVP_PKEY *pkey = NULL; + EVP_MD_CTX *mdctx = NULL; + unsigned char hmac_out[SHA256_HASH_SIZE]; + size_t md_len; + + /* + * The implementation of this function is copied from OpenSSL's function + * ossl_rsa_prf() in crypto/rsa/rsapk1.c and is slightly modified to fit to + * the providers environment. + * Changes include: + * - Different variable and define names. + * - Usage of TRACE_ERROR report errors and issue debug messages. + * - Different return codes. + * - Use of the EVP API instead of the internal APIs for HMAC operations. + */ + + if (kdklen != SHA256_HASH_SIZE) { + TRACE_ERROR("invalid kdklen\n"); + return CKR_ARGUMENTS_BAD; + } + if (outlen * 8 != bitlen) { + TRACE_ERROR("invalid outlen\n"); + return CKR_ARGUMENTS_BAD; + } + + be_bitlen[0] = (bitlen >> 8) & 0xff; + be_bitlen[1] = bitlen & 0xff; + + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, kdk, kdklen); + if (pkey == NULL) { + TRACE_ERROR("EVP_PKEY_new_mac_key() failed.\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + mdctx = EVP_MD_CTX_create(); + if (mdctx == NULL) { + TRACE_ERROR("EVP_MD_CTX_create() failed.\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + /* + * we use hardcoded hash so that migrating between versions that use + * different hash doesn't provide a Bleichenbacher oracle: + * if the attacker can see that different versions return different + * messages for the same ciphertext, they'll know that the message is + * synthetically generated, which means that the padding check failed + */ + for (pos = 0; pos < outlen; pos += SHA256_HASH_SIZE, iter++) { + if (EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) { + TRACE_ERROR("EVP_DigestSignInit failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + be_iter[0] = (iter >> 8) & 0xff; + be_iter[1] = iter & 0xff; + + if (EVP_DigestSignUpdate(mdctx, be_iter, sizeof(be_iter)) != 1) { + TRACE_ERROR("EVP_DigestSignUpdate failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + if (EVP_DigestSignUpdate(mdctx, (unsigned char *)label, labellen) != 1) { + TRACE_ERROR("EVP_DigestSignUpdate failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + if (EVP_DigestSignUpdate(mdctx, be_bitlen, sizeof(be_bitlen)) != 1) { + TRACE_ERROR("EVP_DigestSignUpdate failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + + /* + * HMAC_Final requires the output buffer to fit the whole MAC + * value, so we need to use the intermediate buffer for the last + * unaligned block + */ + md_len = SHA256_HASH_SIZE; + if (pos + SHA256_HASH_SIZE > outlen) { + md_len = sizeof(hmac_out); + if (EVP_DigestSignFinal(mdctx, hmac_out, &md_len) != 1) { + TRACE_ERROR("EVP_DigestSignFinal failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + memcpy(out + pos, hmac_out, outlen - pos); + } else { + md_len = outlen - pos; + if (EVP_DigestSignFinal(mdctx, out + pos, &md_len) != 1) { + TRACE_ERROR("EVP_DigestSignFinal failed\n"); + rc = CKR_FUNCTION_FAILED; + goto out; + } + } + } + + rc = CKR_OK; + +out: + if (pkey != NULL) + EVP_PKEY_free(pkey); + if (mdctx != NULL) + EVP_MD_CTX_free(mdctx); + + return rc; +} + diff --git a/usr/lib/common/mech_rsa.c b/usr/lib/common/mech_rsa.c index 7bab1a84f..7dc9589a2 100644 --- a/usr/lib/common/mech_rsa.c +++ b/usr/lib/common/mech_rsa.c @@ -289,21 +289,34 @@ static CK_RV rsa_parse_block_type_1(CK_BYTE *in_data, return rc; } +#define MAX_LEN_GEN_TRIES 128 + static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, CK_ULONG in_data_len, CK_BYTE *out_data, - CK_ULONG *out_data_len) + CK_ULONG *out_data_len, + CK_BYTE *kdk, CK_ULONG kdklen) { - int i; - unsigned char *em = NULL; - unsigned int good, found_zero_byte, mask, equals0; - int zero_index = 0, msg_index, mlen = -1; - int out_len = *out_data_len; - int rsa_size = in_data_len; + unsigned int good = 0, found_zero_byte, equals0; + size_t zero_index = 0, msg_index; + unsigned char *synthetic = NULL; + int synthetic_length; + uint16_t len_candidate; + unsigned char candidate_lengths[MAX_LEN_GEN_TRIES * sizeof(len_candidate)]; + uint16_t len_mask; + uint16_t max_sep_offset; + int synth_msg_index = 0; + size_t i, j; + CK_RV rc; + + if (kdk == NULL || kdklen == 0) { + TRACE_DEVEL("%s\n", ock_err(ERR_ARGUMENTS_BAD)); + return CKR_ARGUMENTS_BAD; + } /* * The implementation of this function is copied from OpenSSL's function - * RSA_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c + * ossl_rsa_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c * and is slightly modified to fit to the OpenCryptoki environment. * * The OpenSSL code is licensed under the Apache License 2.0. @@ -328,27 +341,67 @@ static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, * PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography Standard", * section 7.2.2. */ - if (rsa_size < RSA_PKCS1_PADDING_SIZE) { + if (in_data_len < RSA_PKCS1_PADDING_SIZE) { TRACE_DEVEL("%s\n", ock_err(ERR_FUNCTION_FAILED)); return CKR_FUNCTION_FAILED; } - em = malloc(rsa_size); - if (em == NULL) { - TRACE_DEVEL("%s\n", ock_err(ERR_HOST_MEMORY)); + /* Generate a random message to return in case the padding checks fail. */ + synthetic = calloc(1, in_data_len); + if (synthetic == NULL) { + TRACE_ERROR("Failed to allocate synthetic buffer"); return CKR_HOST_MEMORY; } - /* in_data_len is always equal to rsa_size */ - memcpy(em, in_data, rsa_size); + rc = openssl_specific_rsa_prf(synthetic, in_data_len, "message", 7, + kdk, kdklen, in_data_len * 8); + if (rc != CKR_OK) + goto out; + + /* decide how long the random message should be */ + rc = openssl_specific_rsa_prf(candidate_lengths, + sizeof(candidate_lengths), + "length", 6, kdk, kdklen, + MAX_LEN_GEN_TRIES * + sizeof(len_candidate) * 8); + if (rc != CKR_OK) + goto out; - good = constant_time_is_zero(em[0]); - good &= constant_time_eq(em[1], 2); + /* + * max message size is the size of the modulus size minus 2 bytes for + * version and padding type and a minimum of 8 bytes padding + */ + len_mask = max_sep_offset = in_data_len - 2 - 8; + /* + * we want a mask so let's propagate the high bit to all positions less + * significant than it + */ + len_mask |= len_mask >> 1; + len_mask |= len_mask >> 2; + len_mask |= len_mask >> 4; + len_mask |= len_mask >> 8; + + synthetic_length = 0; + for (i = 0; i < MAX_LEN_GEN_TRIES * (int)sizeof(len_candidate); + i += sizeof(len_candidate)) { + len_candidate = (candidate_lengths[i] << 8) | + candidate_lengths[i + 1]; + len_candidate &= len_mask; + + synthetic_length = constant_time_select_int( + constant_time_lt(len_candidate, max_sep_offset), + len_candidate, synthetic_length); + } + + synth_msg_index = in_data_len - synthetic_length; + + good = constant_time_is_zero(in_data[0]); + good &= constant_time_eq(in_data[1], 2); /* scan over padding data */ found_zero_byte = 0; - for (i = 2; i < rsa_size; i++) { - equals0 = constant_time_is_zero(em[i]); + for (i = 2; i < in_data_len; i++) { + equals0 = constant_time_is_zero(in_data[i]); zero_index = constant_time_select_int(~found_zero_byte & equals0, i, zero_index); @@ -356,7 +409,7 @@ static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, } /* - * PS must be at least 8 bytes long, and it starts two bytes into |em|. + * PS must be at least 8 bytes long, and it starts two bytes into |in_data|. * If we never found a 0-byte, then |zero_index| is 0 and the check * also fails. */ @@ -367,53 +420,41 @@ static CK_RV rsa_parse_block_type_2(CK_BYTE *in_data, * but in this case we also do not copy the message out. */ msg_index = zero_index + 1; - mlen = rsa_size - msg_index; /* - * For good measure, do this check in constant time as well. + * old code returned an error in case the decrypted message wouldn't fit + * into the |out_data|, since that would leak information, return the + * synthetic message instead */ - good &= constant_time_ge(out_len, mlen); + good &= constant_time_ge(*out_data_len, in_data_len - msg_index); + + msg_index = constant_time_select_int(good, msg_index, synth_msg_index); /* - * Move the result in-place by |rsa_size|-RSA_PKCS1_PADDING_SIZE-|mlen| - * bytes to the left. - * Then if |good| move |mlen| bytes from |em|+RSA_PKCS1_PADDING_SIZE to - * |out_data|. Otherwise leave |out_data| unchanged. - * Copy the memory back in a way that does not reveal the size of - * the data being copied via a timing side channel. This requires copying - * parts of the buffer multiple times based on the bits set in the real - * length. Clear bits do a non-copy with identical access pattern. - * The loop below has overall complexity of O(N*log(N)). + * since at this point the |msg_index| does not provide the signal + * indicating if the padding check failed or not, we don't have to worry + * about leaking the length of returned message, we still need to ensure + * that we read contents of both buffers so that cache accesses don't leak + * the value of |good| */ - out_len = constant_time_select_int( - constant_time_lt(rsa_size - RSA_PKCS1_PADDING_SIZE, out_len), - rsa_size - RSA_PKCS1_PADDING_SIZE, - out_len); - for (msg_index = 1; msg_index < rsa_size - RSA_PKCS1_PADDING_SIZE; - msg_index <<= 1) { - mask = ~constant_time_eq( - msg_index & (rsa_size - RSA_PKCS1_PADDING_SIZE - mlen), 0); - for (i = RSA_PKCS1_PADDING_SIZE; i < rsa_size - msg_index; i++) - em[i] = constant_time_select_8(mask, em[i + msg_index], em[i]); - } - for (i = 0; i < out_len; i++) { - mask = good & constant_time_lt(i, mlen); - out_data[i] = constant_time_select_8( - mask, em[i + RSA_PKCS1_PADDING_SIZE], out_data[i]); - } + for (i = msg_index, j = 0; i < in_data_len && j < *out_data_len; + i++, j++) + out_data[j] = constant_time_select_8(good, in_data[i], synthetic[i]); - OPENSSL_cleanse(em, rsa_size); - free(em); + *out_data_len = j; - *out_data_len = constant_time_select_int(good, mlen, 0); +out: + if (synthetic != NULL) + free(synthetic); - return constant_time_select_int(good, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); + return rc; } CK_RV rsa_parse_block(CK_BYTE *in_data, CK_ULONG in_data_len, CK_BYTE *out_data, - CK_ULONG *out_data_len, CK_ULONG type) + CK_ULONG *out_data_len, CK_ULONG type, + CK_BYTE *kdk, CK_ULONG kdklen) { switch (type) { case PKCS_BT_1: @@ -421,7 +462,7 @@ CK_RV rsa_parse_block(CK_BYTE *in_data, out_data, out_data_len); case PKCS_BT_2: return rsa_parse_block_type_2(in_data, in_data_len, - out_data, out_data_len); + out_data, out_data_len, kdk, kdklen); } return CKR_ARGUMENTS_BAD;