EVP_EncryptFinal_ex returns 未提供明文时出错
EVP_EncryptFinal_ex returns error when no plaintext is provided
我使用为 OpenSSL 加密函数提供高级接口的 EVP 函数来编写执行加密和解密操作的库。此库使用 EVP_aes_128_gcm 进行加密和解密。
我能够提供密钥、IV、AAD、纯文本并成功获得密文、标签。
但是,当我只想对AAD 进行身份验证而不需要任何加密时,问题就来了。所以我从 NIST
中获取了以下测试用例
密钥=77be63708971c4e240d1cb79e8d77feb
IV = e0e00f19fed7ba0136a797f3
AAD = 7a43ec1d9c0a5a78a0b16533a6213cab
标记 = 209fcc8d3675ed938e9c7166709dd946
PT =
CT =
现在我在 EVP_EncryptFinal_ex API 上得到 return 值作为 0至少根据文档是一个错误:
EVP_EncryptInit_ex(), EVP_EncryptUpdate() and EVP_EncryptFinal_ex()
return 1 for success and 0 for failure.
但是,当我尝试打印错误时,我没有收到任何错误:
EVP_EncryptFinal_ex 失败 - OpenSSL 错误:error:00000000:lib(0):func(0):reason(0)
int ret = EVP_EncryptFinal_ex(ctx, outbuf + outlen, &outlen);
if (ret <= 0)
{
printf("EVP_EncryptFinal_ex failed - OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return -1;
}
更有趣的一点是,如果我不检查 return 代码并在之后获取标签,我会得到正确的标签 209fcc8d3675ed938e9c7166709dd946
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag)) {
printf("EVP_CIPHER_CTX_ctrl: failed\n");
return -1;
}
此外,我使用的是 AES-GCM,因此没有填充。因此,以下声明对于这种情况无效:
If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any
more data and it will return an error if any data remains in a partial
block: that is if the total data length is not a multiple of the block
size.
有什么我可能做错的想法吗?
似乎我在 AAD 之后在空明文上调用 EVP_EncryptUpdate 有时会导致问题。但是,删除空纯文本上的 API EVP_EncryptUpdate 可以解决问题。
您可以在下面找到完整的代码示例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <vector>
int main()
{
constexpr int TAG_LENGTH = 16;
const std::vector<uint8_t> key{0x77,0xbe,0x63,0x70,0x89,0x71,0xc4,0xe2,0x40,0xd1,0xcb,0x79,0xe8,0xd7,0x7f,0xeb};
const std::vector<uint8_t> iv{0xe0,0xe0,0x0f,0x19,0xfe,0xd7,0xba,0x01,0x36,0xa7,0x97,0xf3};
const std::vector<uint8_t> aad{0x7a,0x43,0xec,0x1d,0x9c,0x0a,0x5a,0x78,0xa0,0xb1,0x65,0x33,0xa6,0x21,0x3c,0xab};
const std::vector<uint8_t> expected_tag{0x20,0x9f,0xcc,0x8d,0x36,0x75,0xed,0x93,0x8e,0x9c,0x71,0x66,0x70,0x9d,0xd9,0x46};
std::vector<uint8_t> actualtag;
actualtag.resize(TAG_LENGTH);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (ctx == nullptr)
{
printf("EVP_CIPHER_CTX_new: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
if (!EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr))
{
printf("EVP_EncryptInit_ex: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, iv.size(), nullptr))
{
printf("EVP_CIPHER_CTX_ctrl: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
if (!EVP_EncryptInit_ex(ctx, nullptr, nullptr, key.data(), iv.data()))
{
printf("EVP_EncryptInit_ex: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
int length = 0;
if (!EVP_EncryptUpdate(ctx, nullptr, &length, aad.data(), aad.size()))
{
printf("EVP_EncryptUpdate: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
if (!EVP_EncryptFinal_ex(ctx, nullptr, &length))
{
printf("EVP_EncryptFinal_ex: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
/* Get tag */
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, TAG_LENGTH, actualtag.data()))
{
printf("EVP_CIPHER_CTX_ctrl: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
printf("Actual Tag: ");
for (auto i: actualtag)
printf("%02hhx", i);
printf("\n");
printf("Expected Tag: ");
for (auto i: expected_tag)
printf("%02hhx", i);
printf("\n");
EVP_CIPHER_CTX_free(ctx);
return EXIT_SUCCESS;
}
我使用为 OpenSSL 加密函数提供高级接口的 EVP 函数来编写执行加密和解密操作的库。此库使用 EVP_aes_128_gcm 进行加密和解密。
我能够提供密钥、IV、AAD、纯文本并成功获得密文、标签。
但是,当我只想对AAD 进行身份验证而不需要任何加密时,问题就来了。所以我从 NIST
中获取了以下测试用例密钥=77be63708971c4e240d1cb79e8d77feb
IV = e0e00f19fed7ba0136a797f3
AAD = 7a43ec1d9c0a5a78a0b16533a6213cab
标记 = 209fcc8d3675ed938e9c7166709dd946
PT =
CT =
现在我在 EVP_EncryptFinal_ex API 上得到 return 值作为 0至少根据文档是一个错误:
EVP_EncryptInit_ex(), EVP_EncryptUpdate() and EVP_EncryptFinal_ex() return 1 for success and 0 for failure.
但是,当我尝试打印错误时,我没有收到任何错误:
EVP_EncryptFinal_ex 失败 - OpenSSL 错误:error:00000000:lib(0):func(0):reason(0)
int ret = EVP_EncryptFinal_ex(ctx, outbuf + outlen, &outlen);
if (ret <= 0)
{
printf("EVP_EncryptFinal_ex failed - OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return -1;
}
更有趣的一点是,如果我不检查 return 代码并在之后获取标签,我会得到正确的标签 209fcc8d3675ed938e9c7166709dd946
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag)) {
printf("EVP_CIPHER_CTX_ctrl: failed\n");
return -1;
}
此外,我使用的是 AES-GCM,因此没有填充。因此,以下声明对于这种情况无效:
If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any more data and it will return an error if any data remains in a partial block: that is if the total data length is not a multiple of the block size.
有什么我可能做错的想法吗?
似乎我在 AAD 之后在空明文上调用 EVP_EncryptUpdate 有时会导致问题。但是,删除空纯文本上的 API EVP_EncryptUpdate 可以解决问题。
您可以在下面找到完整的代码示例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <vector>
int main()
{
constexpr int TAG_LENGTH = 16;
const std::vector<uint8_t> key{0x77,0xbe,0x63,0x70,0x89,0x71,0xc4,0xe2,0x40,0xd1,0xcb,0x79,0xe8,0xd7,0x7f,0xeb};
const std::vector<uint8_t> iv{0xe0,0xe0,0x0f,0x19,0xfe,0xd7,0xba,0x01,0x36,0xa7,0x97,0xf3};
const std::vector<uint8_t> aad{0x7a,0x43,0xec,0x1d,0x9c,0x0a,0x5a,0x78,0xa0,0xb1,0x65,0x33,0xa6,0x21,0x3c,0xab};
const std::vector<uint8_t> expected_tag{0x20,0x9f,0xcc,0x8d,0x36,0x75,0xed,0x93,0x8e,0x9c,0x71,0x66,0x70,0x9d,0xd9,0x46};
std::vector<uint8_t> actualtag;
actualtag.resize(TAG_LENGTH);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (ctx == nullptr)
{
printf("EVP_CIPHER_CTX_new: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
if (!EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr))
{
printf("EVP_EncryptInit_ex: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, iv.size(), nullptr))
{
printf("EVP_CIPHER_CTX_ctrl: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
if (!EVP_EncryptInit_ex(ctx, nullptr, nullptr, key.data(), iv.data()))
{
printf("EVP_EncryptInit_ex: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
int length = 0;
if (!EVP_EncryptUpdate(ctx, nullptr, &length, aad.data(), aad.size()))
{
printf("EVP_EncryptUpdate: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
if (!EVP_EncryptFinal_ex(ctx, nullptr, &length))
{
printf("EVP_EncryptFinal_ex: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
/* Get tag */
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, TAG_LENGTH, actualtag.data()))
{
printf("EVP_CIPHER_CTX_ctrl: OpenSSL error: %s", ERR_error_string(ERR_get_error(), nullptr));
return EXIT_FAILURE;
}
printf("Actual Tag: ");
for (auto i: actualtag)
printf("%02hhx", i);
printf("\n");
printf("Expected Tag: ");
for (auto i: expected_tag)
printf("%02hhx", i);
printf("\n");
EVP_CIPHER_CTX_free(ctx);
return EXIT_SUCCESS;
}