locked
Why isn't Encrypt( decrypt( data ) ) exactly data? I'm using the APIs BCryptGenerateSymmetricKey, BCryptDecrypt, BCryptEncrypt for AES RRS feed

  • Question

  •  I found a rather odd situation.   Encrypt( decrypt( encdata )  )  is not the original encdata!

    The output is:

    encdata    08 3a 8b 04 4c 3a e0 89 eb 82 a9 bc d4 24 e6 4e
    plaintext  11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 00
    ciphertext 6d 26 49 c8 88 ae c6 98 cf fd f6 4c 3e 4a dc b8

    The AES decrypt worked as expected, but the following encrypt failed to produce the expected value.  And there are no error returns.

    I stripped out the classes and std::vectors and here is the essential code. 

    // encdata    08 3a 8b 04 4c 3a e0 89 eb 82 a9 bc d4 24 e6 4e
    // plaintext  11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 00
    // ciphertext 6d 26 49 c8 88 ae c6 98 cf fd f6 4c 3e 4a dc b8
    //
    // But I expect ciphertext to be the same as encdata
    //
    bool showCryptoNgBug( std::ostream & out )
    {
    BCRYPT_ALG_HANDLE	hAlgProv;
    BCRYPT_KEY_HANDLE	hKey;
    ULONG				lenOutput= 0;
    DWORD				dwFlags = 0;		// No block padding
    
    unsigned char keyBytes[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x0 };
    unsigned char encdata[]=   { 0x8, 0x3a, 0x8b, 0x4, 0x4c, 0x3a, 0xe0, 0x89, 0xeb, 0x82, 0xa9, 0xbc, 0xd4, 0x24, 0xe6, 0x4e };
    const int	size= 16;
    unsigned char ciphertext[size], plaintext[size];
    
    ::BCryptOpenAlgorithmProvider( &hAlgProv, BCRYPT_AES_ALGORITHM, NULL, 0 );
    
    ::BCryptGenerateSymmetricKey( hAlgProv, &hKey, NULL, NULL, (PBYTE)keyBytes, sizeof(keyBytes), 0 );
    
    ::BCryptDecrypt( hKey, encdata, size, 0, 0, 0,
                     plaintext, size, &lenOutput, dwFlags );	
    
    ::BCryptEncrypt( hKey, plaintext, size, 0, 0, 0,
                    ciphertext, size, &lenOutput, dwFlags );	
    
    
    out	<<	"encdata    ";	show( encdata, size, out );
    out	<<	"plaintext  ";	show( plaintext, size, out );
    out	<<	"ciphertext ";  show( ciphertext, size, out );
    
    out << "Huh? encrypt( decrypt( encdata ) ) should be encdata ! " << endl;
    return encdata[0] == ciphertext[0];
    }
    
    void show( unsigned char data[], int datasize, std::ostream & out )
    {
    for( unsigned i=0; i< datasize; i++ ) {
    	int v= (int) data[i];
    	if ( v < 16 ) out << "0";
    	out << std::hex << v << " ";
    }
    out << std::endl;
    }
    

    Is there is an undocumented restriction that you can't change the mode of an AES key from decryption to encryption?

     

    Sunday, April 21, 2013 2:21 AM

Answers

  • I think you should make sure you use the same IV in both cases.
    • Marked as answer by Andrew7Webb Monday, April 22, 2013 12:05 PM
    Monday, April 22, 2013 4:26 AM

All replies

  • I think you should make sure you use the same IV in both cases.
    • Marked as answer by Andrew7Webb Monday, April 22, 2013 12:05 PM
    Monday, April 22, 2013 4:26 AM
  • I would have expected that using a NULL for an IV in both cases would get me consistent behavior. You are right, if I explicitly add the IV of all zeros, then the functions work correctly.

    // The address of a buffer that contains the initialization vector (IV) to use during decryption.

    // This function will modify the contents of this buffer. If you need to reuse the IV later, make sure you make a copy of this buffer before calling this function. // This parameter is optional and can be NULL if no IV is used. // The required size of the IV can be obtained by calling the BCryptGetProperty function to get the BCRYPT_BLOCK_LENGTH property. This will provide the size of a block for the algorithm, which is also the size of the IV. unsigned char initValDe[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; unsigned char initValEn[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; if ( ERROR_SUCCESS == ::BCryptOpenAlgorithmProvider( &hAlgProv, BCRYPT_AES_ALGORITHM, NULL, 0 ) ) { if ( ERROR_SUCCESS == ::BCryptGenerateSymmetricKey( hAlgProv, &hKey, NULL, NULL, (PBYTE)keyBytes, sizeof(keyBytes), 0 ) ) { if ( ERROR_SUCCESS == ::BCryptDecrypt( hKey, encdata, size, 0, initValDe, size, plaintext, size, &lenOutput, dwFlags ) ) { out << "plaintext "; show( plaintext, size, out ); if ( ERROR_SUCCESS == ::BCryptEncrypt( hKey, plaintext, size, 0, initValEn, size, ciphertext, size, &lenOutput, dwFlags ) ) {

    Note that you CANNOT reuse the IV as the first BCryptDecrypt modifies it.

    So the documentation is incorrect.  the IV is NOT optional except in trivial cases.

    Monday, April 22, 2013 12:04 PM
  • the problem is that the default AlgorithmProvider loads AES in CBC mode. 

    In this you can make sure by calling BCryptGetProperty  function  winth BCRYPT_CHAINING_MODEparammetr.
    This means that you need initialization vectors and etc., which you write 

    unsigned char initValDe[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    unsigned char initValEn[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    you need to change the algorithmin to ECB mode by calling BCryptSetProperty function with parameter BCRYPT_CHAIN_MODE_ECB.

    PS: good luck 

    sorry for my bad english


    • Edited by GoodOstik Monday, June 3, 2013 9:19 AM
    Monday, June 3, 2013 9:17 AM