2015-06-08 5 views
10

Utilizzando l'API CNG di Windows, sono in grado di crittografare e decrittografare singoli blocchi di dati con l'autenticazione, utilizzando AES in modalità GCM. Ora voglio crittografare e decrittografare più buffer di fila.Come concatenare le chiamate BCryptEncrypt e BCryptDecrypt utilizzando AES in modalità GCM?

Secondo documentation for CNG, il seguente scenario è supportato:

Se l'ingresso di crittografia o decrittografia è sparsa su più buffer, allora è necessario chiamate catena al BCryptEncrypt e funzioni BCryptDecrypt. Il concatenamento viene indicato impostando il flag BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG nel membro dwFlags.

Se comprendo correttamente, questo significa che posso invocare BCryptEncrypt sequenza su più buffer di avere la tag di autenticazione per i buffer uniti alla fine. Allo stesso modo, posso richiamare lo BCryptDecrypt in sequenza su più buffer mentre rimandiamo il controllo dell'autenticazione reale fino alla fine. Non riesco a farlo funzionare, sembra che il valore di dwFlags sia ignorato. Ogni volta che utilizzo BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG, ottengo un valore restituito di 0xc000a002, che equivale a STATUS_AUTH_TAG_MISMATCH come definito in ntstatus.h.

Anche se il parametro pbIV è contrassegnato come in/out, gli elementi indicati dal parametro pbIV non vengono modificati da BCryptEncrypt(). È previsto? Ho anche guardato il campo pbNonce nella struttura BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, puntato dal puntatore pPaddingInfo, ma anche questo non viene modificato. Ho anche provato "manualmente" a far avanzare l'IV, modificando me stesso i contenuti secondo lo schema del contatore, ma non è stato d'aiuto.

Qual è la procedura corretta per collegare correttamente le funzioni BCryptEncrypt e/o BCryptDecrypt?

risposta

6

Sono riuscito a farlo funzionare. Sembra che il problema sia in MSDN, dovrebbe menzionare l'impostazione BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG anziché 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

Grazie per il suggerimento, ha senso. Però non sembra funzionare, ho aggiunto del testo aggiuntivo a riguardo alla mia domanda. –

+0

Ho cambiato completamente la mia risposta. – Codeguard

+0

Fantastico, molte grazie. Un'osservazione: "trashIV" contiene effettivamente l'IV avanzato, quindi è necessario per il corretto funzionamento in modalità concatenamento. Il mio errore è stato che avevo fatto la IV delle stesse dimensioni del nonce (oltre a usare il valore '' dwFlags' 'sbagliato). –

Problemi correlati