How do I use AES-GMAC with a secret in BCrypt?

我是研究僧i 提交于 2019-12-11 03:45:16

问题


I am trying to use AES-GMAC with BCrypt (Microsoft CNG if that helps) but the documentation seems contradictory to what I would expect.

RFC 4543 states that AES-GMAC requires a secret key (which I was expecting), but BCryptCreateHash function states that pbSecret is not used unless the BCRYPT_ALG_HANDLE_HMAC is provided.

I've tried using the BCRYPT_ALG_HANDLE_HMAC on BcryptOpenAlgorithmProvider and as I expected, I get a NOT_SUPPORTED when using BCRYPT_AES_GMAC_ALGORITHM with BCRYPT_ALG_HANDLE_HMAC.

The process I was expecting was something like:

BCryptOpenAlgorithmProvider(*with no HMAC flag*)
BCryptCreateHash(*including secret*)
BCryptHashData()
BCryptFinaliseHash()

Am I misunderstanding something here?


回答1:


I've tried using the BCRYPT_ALG_HANDLE_HMAC on BcryptOpenAlgorithmProvider and as I expected, I get a NOT_SUPPORTED when using BCRYPT_AES_GMAC_ALGORITHM with BCRYPT_ALG_HANDLE_HMAC.

BCRYPT_AES_GMAC_ALGORITHM seems broken. Don't use it.

Use AES/GCM via BCRYPT_AES_ALGORITHM. Don't encrypt any data. Only authenticate the aad. The resulting tag is the GMAC over the AAD.


How do I use AES-GMAC with a secret in BCrypt?

The steps to create the GMAC are:

  • Get AES algorithm
  • Set GCM mode
  • Key the cipher
  • Set the nonce
  • Authenticate the aad

"aad" is "additionally authenticated data". It is different than plain text data that is traditionally encrypted.

Programs to MAC the aad are below for both Bcrypt and Crypto++. Since you don't encrypt data, the call to BCryptEncrypt looks like:

status = BCryptEncrypt(
    hKey,
    NULL, 0,
    (PVOID)&aadInfo,
    NULL, 0,
    NULL, 0,
    &ulWritten,
    0
);

Note well: SP800-38D specifies max nonce of 264-1, but Microsoft limits the nonce to 12 bytes. If you try to use 16 bytes, which is AES blocksize, then the result is 0xc000000d or STATUS_INVALID_PARAMETER. The Microsoft docs fail to document the limitations.


Here is the Microsoft Bcrypt program.

#include <Windows.h>
#include <bcrypt.h>

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <memory>
#include <stdexcept>

#pragma comment (lib, "bcrypt.lib")

std::string NtStatusToString(const CHAR* operation, NTSTATUS status)
{
    std::ostringstream oss;
    oss << operation << ", 0x" << std::hex << status;

    switch(status)
    {
        case 0xc0000000:
            oss << " (STATUS_SUCCESS)";
            break;
        case 0xC0000008:
            oss << " (STATUS_INVALID_HANDLE)";
            break;
        case 0xc000000d:
            oss << " (STATUS_INVALID_PARAMETER)";
            break;
        case 0xc00000bb:
            oss << " (STATUS_NOT_SUPPORTED)";
            break;
        case 0xC0000225:
            oss << " (STATUS_NOT_FOUND)";
            break;
    }
    return oss.str();
}

std::string ArrayToHexString(const UCHAR arr[], size_t size)
{
    std::ostringstream oss;
    for (size_t i=0; i<size; ++i)
    {
        oss << std::hex << std::setw(2) << std::setfill('0');
        oss << (unsigned int)arr[i];
    }
    return oss.str();
}

int main(int argc, char* argv[])
{
    BCRYPT_ALG_HANDLE hAlgorithm = 0;
    BCRYPT_KEY_HANDLE hKey = 0;
    UCHAR* pbKeyObject = 0;
    ULONG cbKeyObjectLength = 0;

    // SP800-38D specifies max nonce of 2^64-1, but
    // Microsoft limits the nonce to 12 bytes.

    UCHAR key[16] = {0};
    UCHAR  iv[12] = {0};
    UCHAR tag[16] = {0};

    // The data to be GMAC'd. It is not encrypted.
    std::string aad("Not so secret additionally authenticated data");

    try
    {
        NTSTATUS status = 0;
        ULONG ulWritten = 0;

        ////////////////////////////////////////

        status = BCryptOpenAlgorithmProvider(
            &hAlgorithm,
            BCRYPT_AES_ALGORITHM,
            NULL, 0
        );

        if (!BCRYPT_SUCCESS(status))
            throw std::runtime_error(
                NtStatusToString("BCryptOpenAlgorithmProvider", status));

        status = BCryptSetProperty(
            hAlgorithm,
            BCRYPT_CHAINING_MODE,
            (UCHAR*)BCRYPT_CHAIN_MODE_GCM,
            sizeof(BCRYPT_CHAIN_MODE_GCM),
            0
        );

        if (!BCRYPT_SUCCESS(status))
            throw std::runtime_error(
                NtStatusToString("BCryptSetProperty (BCRYPT_CHAINING_MODE)", status));

        ////////////////////////////////////////

        status = BCryptGetProperty(
            hAlgorithm,
            BCRYPT_OBJECT_LENGTH,
            (PUCHAR)&cbKeyObjectLength,
            sizeof(cbKeyObjectLength),
            &ulWritten,
            0
        );

        if (!BCRYPT_SUCCESS(status))
            throw std::runtime_error(
                NtStatusToString("BCryptGetProperty (BCRYPT_OBJECT_LENGTH)", status));

        pbKeyObject = new UCHAR[cbKeyObjectLength];

        if (!pbKeyObject)
            throw std::runtime_error("pbKeyObject");

        ////////////////////////////////////////

        status = BCryptGenerateSymmetricKey(
            hAlgorithm,
            &hKey,
            pbKeyObject,
            cbKeyObjectLength,
            key,
            sizeof(key),
            0
        );

        if (!BCRYPT_SUCCESS(status))
            throw std::runtime_error(
                NtStatusToString("BCryptGenerateSymmetricKey", status));

        ////////////////////////////////////////

        BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO aadInfo;
        BCRYPT_INIT_AUTH_MODE_INFO(aadInfo);

        aadInfo.pbNonce = iv;
        aadInfo.cbNonce = sizeof(iv);

        // Awful API design; non-const pointer.
        aadInfo.pbAuthData = reinterpret_cast<UCHAR*>(&aad[0]);
        aadInfo.cbAuthData = static_cast<ULONG>(aad.size());
        aadInfo.cbAAD = static_cast<ULONG>(aad.size());

        aadInfo.pbTag = tag;
        aadInfo.cbTag = sizeof(tag);

        ////////////////////////////////////////

        status = BCryptEncrypt(
            hKey,
            NULL, 0,
            (PVOID)&aadInfo,
            NULL, 0,
            NULL, 0,
            &ulWritten,
            0
        );

        if (!BCRYPT_SUCCESS(status))
            throw std::runtime_error(
                NtStatusToString("BCryptEncrypt", status));

        std::cout << "Message: " << aad << std::endl;
        std::cout << "GMAC: " << ArrayToHexString(tag, sizeof(tag));
    }
    catch (const std::exception& ex)
    {
        std::cerr << "Exception: " << ex.what() << std::endl;
    }

    if (hKey)
        BCryptDestroyKey(hKey);

    if (hAlgorithm)
        BCryptCloseAlgorithmProvider(hAlgorithm, 0);

    // Destroy after handles
    if (pbKeyObject)
        delete [] pbKeyObject;

    return 0;
}

And here is the result.

>cl.exe /DWINVER=0x0600 /TP /GR /EHsc bcrypt-gmac.cpp /link
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24210 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

bcrypt-gmac.cpp
Microsoft (R) Incremental Linker Version 14.00.24210.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:bcrypt-gmac.exe
bcrypt-gmac.obj

>.\bcrypt-gmac.exe
Message: Not so secret additionally authenticated data
GMAC: 3a1158d288cd796899f0366cdf594020

Here is the code for Crypto++. You can do the same in Botan or OpenSSL.

#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "hex.h"
#include "aes.h"
#include "gcm.h"

#include <iostream>
#include <string>

int main(int argc, char* argv[])
{
    using namespace CryptoPP;

    byte key[16] = {0};
    byte  iv[12] = {0};
    byte tag[16] = {0};

    std::string aad("Not so secret additionally authenticated data");

    try
    {
        GCM< AES >::Encryption enc;
        enc.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));

        // AuthenticatedEncryptionFilter defines two
        //   channels: DEFAULT_CHANNEL and AAD_CHANNEL
        //   DEFAULT_CHANNEL is encrypted and authenticated,
        //   AAD_CHANNEL is authenticated.
        AuthenticatedEncryptionFilter ef(enc,
            new ArraySink(tag, sizeof(tag)),
            false, 16 /* Tag size, MAC_AT_END */
        ); // AuthenticatedEncryptionFilter

        // Authenticated data *must* be pushed before
        //  Confidential/Authenticated data. Otherwise
        //  we must catch the BadState exception
        ef.ChannelPut(AAD_CHANNEL, (const byte*)aad.data(), aad.size());
        ef.ChannelMessageEnd(AAD_CHANNEL);

        // Confidential data comes after authenticated data.
        // This is a limitation due to CCM mode, not GCM mode.
        //ef.ChannelPut(DEFAULT_CHANNEL, pdata.data(), pdata.size());
        //ef.ChannelMessageEnd(DEFAULT_CHANNEL);

        // Signal end of message
        ef.MessageEnd();

        std::cout << "Message: " << aad << std::endl;
        std::cout << "GMAC: ";
        StringSource(tag, sizeof(tag), true, new HexEncoder(new FileSink(std::cout)));
        std::cout << std::endl;
    }
    catch(CryptoPP::Exception& ex)
    {
        std::cerr << "Exception: " << ex.what() << std::endl;
    }

    return 0;
}

And here is the result.

$ g++ test.cxx ./libcryptopp.a -o test.exe

$ ./test.exe
Message: Not so secret additionally authenticated data
GMAC: 3A1158D288CD796899F0366CDF594020


来源:https://stackoverflow.com/questions/57456546/how-do-i-use-aes-gmac-with-a-secret-in-bcrypt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!