【问题标题】:EVP_EncryptFinal_ex returns error when no plaintext is provided未提供明文时,EVP_EncryptFinal_ex 返回错误
【发布时间】:2021-02-19 13:57:47
【问题描述】:

我使用为 OpenSSL 加密函数提供高级接口的 EVP 函数来编写执行加密和解密操作的库。该库使用 EVP_aes_128_gcm 进行加密和解密。

我能够提供密钥、IV、AAD、纯文本并成功获得密文、标签

但是,当我只想对 AAD 进行身份验证但不想进行任何加密时,问题就来了。所以我从NIST

中获取了以下测试用例

密钥 = 77be63708971c4e240d1cb79e8d77feb
IV = e0e00f19fed7ba0136a797f3
AAD = 7a43ec1d9c0a5a78a0b16533a6213cab
标签 = 209fcc8d3675ed938e9c7166709dd946
PT =
CT =

现在我将 EVP_EncryptFinal_ex API 的返回值设为 0,根据文档,这至少是一个错误:

EVP_EncryptInit_ex()、EVP_EncryptUpdate() 和 EVP_EncryptFinal_ex() 成功返回 1,失败返回 0。

但是,当我尝试打印错误时,我没有收到任何错误:

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;
}

更有趣的一点是,如果我不检查返回码并在之后获取标签,我会得到正确的标签为 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,所以没有填充。所以下面的语句在这种情况下是无效的:

如果填充被禁用,那么 EVP_EncryptFinal_ex() 将不会加密任何 更多数据,如果任何数据保留在部分中,它将返回错误 块:即如果总数据长度不是块的倍数 大小。

有什么我可能做错的想法吗?

【问题讨论】:

  • 为您提供更好的标签
  • 我无法重现您的测试向量的问题,即 EVP_EncryptFinal_ex() 在我的机器上返回 1。请发布您的代码。您使用的是哪个 OpenSSL 版本?
  • @Topaco 其实我想通了。在调用 EVP_EncryptFinal_ex 之前,我在空的纯文本上调用 EVP_EncryptUpdate。如果您只期望 AAD 的签名但不涉及加密,那么您不需要调用它。删除这个 API 实际上解决了这个问题。无论如何感谢您的支持。
  • 很高兴听到它正在工作。对我来说,无论是否对空明文调用 EVP_EncryptUpdate(),它都适用,但这可能是实现细节。
  • @Topaco 实际上,当我像顺序语句一样在纯 c 中实现时,它工作正常。但是,有一个 c++ 模板样式类,其中包含 init、update、final 等函数,并且这些函数由用户按顺序调用。当我使用这种通用模板样式时,我遇到了问题。

标签: c++11 encryption openssl aes aes-gcm


【解决方案1】:

似乎我在 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;
}

【讨论】:

  • 你看,这里更快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-07
  • 1970-01-01
  • 1970-01-01
  • 2016-12-22
  • 1970-01-01
  • 2017-02-08
  • 1970-01-01
相关资源
最近更新 更多