2015-06-08 5 views
10

Используя API CNG для Windows, я могу зашифровать и дешифровать отдельные блоки данных с помощью аутентификации, используя AES в режиме GCM. Теперь я хочу зашифровать и дешифровать несколько буферов подряд.Как подключить вызовы BCryptEncrypt и BCryptDecrypt с использованием AES в режиме GCM?

Согласно documentation for CNG, следующий сценарий поддерживается:

Если вход шифрования или дешифрования разбросана по нескольким буферов, то вы должны цепь вызовы BCryptEncrypt и функций BCryptDecrypt. Цепочка указывается установкой флага BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG в члене dwFlags.

Если я правильно понимаю, это означает, что я могу вызвать BCryptEncrypt последовательно на нескольких буферов получить тег аутентификации для комбинированных буферов в конце. Аналогично, я могу последовательно вызывать BCryptDecrypt на нескольких буферах, откладывая фактическую проверку подлинности до конца. Я не могу заставить это работать, но похоже, что значение для dwFlags игнорируется. Всякий раз, когда я использую BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG, я получаю возвращаемое значение 0xc000a002, которое равно STATUS_AUTH_TAG_MISMATCH, как определено в ntstatus.h.

Несмотря на то, что параметр pbIV обозначен как in/out, элементы, на которые указывает параметр pbIV, не изменяются BCryptEncrypt(). Это ожидалось? Я также посмотрел на поле pbNonce в структуре BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, указав указатель pPaddingInfo, но этот тоже не изменяется. Я также попытался «вручную» продвинуть IV, изменив содержимое сам по схеме счетчика, но это тоже не помогло.

Какая правильная процедура для цепочки BCryptEncrypt и/или BCryptDecrypt функций успешно?

ответ

6

Мне удалось заставить его работать. Похоже, что проблема в MSDN, следует упомянуть настройку BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG вместо BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG.

#include <windows.h> 
#include <assert.h> 
#include <vector> 
#include <Bcrypt.h> 
#pragma comment(lib, "bcrypt.lib") 

std::vector<BYTE> MakePatternBytes(size_t a_Length) 
{ 
    std::vector<BYTE> result(a_Length); 
    for (size_t i = 0; i < result.size(); i++) 
    { 
     result[i] = (BYTE)i; 
    } 

    return result; 
} 

std::vector<BYTE> MakeRandomBytes(size_t a_Length) 
{ 
    std::vector<BYTE> result(a_Length); 
    for (size_t i = 0; i < result.size(); i++) 
    { 
     result[i] = (BYTE)rand(); 
    } 

    return result; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    NTSTATUS bcryptResult = 0; 
    DWORD bytesDone = 0; 

    BCRYPT_ALG_HANDLE algHandle = 0; 
    bcryptResult = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_AES_ALGORITHM, 0, 0); 
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptOpenAlgorithmProvider"); 

    bcryptResult = BCryptSetProperty(algHandle, BCRYPT_CHAINING_MODE, (BYTE*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0); 
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptSetProperty(BCRYPT_CHAINING_MODE)"); 

    BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths; 
    bcryptResult = BCryptGetProperty(algHandle, BCRYPT_AUTH_TAG_LENGTH, (BYTE*)&authTagLengths, sizeof(authTagLengths), &bytesDone, 0); 
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_AUTH_TAG_LENGTH)"); 

    DWORD blockLength = 0; 
    bcryptResult = BCryptGetProperty(algHandle, BCRYPT_BLOCK_LENGTH, (BYTE*)&blockLength, sizeof(blockLength), &bytesDone, 0); 
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_BLOCK_LENGTH)"); 

    BCRYPT_KEY_HANDLE keyHandle = 0; 
    { 
     const std::vector<BYTE> key = MakeRandomBytes(blockLength); 
     bcryptResult = BCryptGenerateSymmetricKey(algHandle, &keyHandle, 0, 0, (PUCHAR)&key[0], key.size(), 0); 
     assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGenerateSymmetricKey"); 
    } 

    const size_t GCM_NONCE_SIZE = 12; 
    const std::vector<BYTE> origNonce = MakeRandomBytes(GCM_NONCE_SIZE); 
    const std::vector<BYTE> origData = MakePatternBytes(256); 

    // Encrypt data as a whole 
    std::vector<BYTE> encrypted = origData; 
    std::vector<BYTE> authTag(authTagLengths.dwMinLength); 
    { 
     BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; 
     BCRYPT_INIT_AUTH_MODE_INFO(authInfo); 
     authInfo.pbNonce = (PUCHAR)&origNonce[0]; 
     authInfo.cbNonce = origNonce.size(); 
     authInfo.pbTag = &authTag[0]; 
     authInfo.cbTag = authTag.size(); 

     bcryptResult = BCryptEncrypt 
      (
      keyHandle, 
      &encrypted[0], encrypted.size(), 
      &authInfo, 
      0, 0, 
      &encrypted[0], encrypted.size(), 
      &bytesDone, 0 
      ); 

     assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptEncrypt"); 
     assert(bytesDone == encrypted.size()); 
    } 

    // Decrypt data in two parts 
    std::vector<BYTE> decrypted = encrypted; 
    { 
     DWORD partSize = decrypted.size()/2; 

     std::vector<BYTE> macContext(authTagLengths.dwMaxLength); 

     BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; 
     BCRYPT_INIT_AUTH_MODE_INFO(authInfo); 
     authInfo.pbNonce = (PUCHAR)&origNonce[0]; 
     authInfo.cbNonce = origNonce.size(); 
     authInfo.pbTag = &authTag[0]; 
     authInfo.cbTag = authTag.size(); 
     authInfo.pbMacContext = &macContext[0]; 
     authInfo.cbMacContext = macContext.size(); 

     // IV value is ignored on first call to BCryptDecrypt. 
     // This buffer will be used to keep internal IV used for chaining. 
     std::vector<BYTE> contextIV(blockLength); 

     // First part 
     authInfo.dwFlags = BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG; 
     bcryptResult = BCryptDecrypt 
      (
      keyHandle, 
      &decrypted[0*partSize], partSize, 
      &authInfo, 
      &contextIV[0], contextIV.size(), 
      &decrypted[0*partSize], partSize, 
      &bytesDone, 0 
      ); 

     assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt"); 
     assert(bytesDone == partSize); 

     // Second part 
     authInfo.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG; 
     bcryptResult = BCryptDecrypt 
      (
      keyHandle, 
      &decrypted[1*partSize], partSize, 
      &authInfo, 
      &contextIV[0], contextIV.size(), 
      &decrypted[1*partSize], partSize, 
      &bytesDone, 0 
      ); 

     assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt"); 
     assert(bytesDone == partSize); 
    } 

    // Check decryption 
    assert(decrypted == origData); 

    // Cleanup 
    BCryptDestroyKey(keyHandle); 
    BCryptCloseAlgorithmProvider(algHandle, 0); 

    return 0; 
} 
+0

Спасибо за предложение, что имеет смысл. Похоже, что это не сработало, я добавил к этому вопросу дополнительный текст. –

+0

Я полностью изменил свой ответ. – Codeguard

+0

Фантастический, большое спасибо. Одно замечание: 'trashIV' фактически содержит расширенный IV, поэтому требуется для правильной работы в режиме цепочки. Моя ошибка заключалась в том, что я сделал IV такого же размера, как и nonce (в дополнение к использованию «неправильного» значения dwFlags). –