Я пытался использовать BCRYPT_ALG_HANDLE_HMAC
на BcryptOpenAlgorithmProvider
, и, как я и ожидал, я получаю NOT_SUPPORTED
при использовании BCRYPT_AES_GMAC_ALGORITHM
с BCRYPT_ALG_HANDLE_HMAC
.
BCRYPT_AES_GMAC_ALGORITHM
кажется сломанным. Не используйте его.
Используйте AES/GCM через BCRYPT_AES_ALGORITHM
. Не шифруйте никакие данные. Подтвердить подлинность только aad. Результирующий тег — это GMAC поверх AAD.
Как использовать AES-GMAC с секретом в BCrypt?
Шаги для создания GMAC:
- Получить алгоритм AES
- Установить режим GCM
- Ключ шифра
- Установите одноразовый номер
- Подтвердить подлинность объявления
"aad" означает "дополнительно аутентифицированные данные". Это отличается от обычных текстовых данных, которые традиционно шифруются.
Ниже приведены программы для MAC-адреса как для Bcrypt, так и для Crypto++. Поскольку вы не шифруете данные, вызов BCryptEncrypt
выглядит так:
status = BCryptEncrypt(
hKey,
NULL, 0,
(PVOID)&aadInfo,
NULL, 0,
NULL, 0,
&ulWritten,
0
);
Обратите внимание: SP800-38D указано максимальное одноразовое значение 264-1, но Microsoft ограничивает одноразовый номер 12 байтами. Если вы попытаетесь использовать 16 байтов, что соответствует размеру блока AES, результатом будет 0xc000000d
или STATUS_INVALID_PARAMETER
. Документы Microsoft не документируют ограничения.
Вот программа Microsoft Bcrypt.
#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;
}
И вот результат.
>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
Вот код для Crypto++. Вы можете сделать то же самое в Botan или 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;
}
И вот результат.
$ g++ test.cxx ./libcryptopp.a -o test.exe
$ ./test.exe
Message: Not so secret additionally authenticated data
GMAC: 3A1158D288CD796899F0366CDF594020
person
jww
schedule
13.08.2019