1

I am new to CNG. I am playing with the basic program from msdn site. I have modified the input plain string and testing the output using other websites that provides the aes cbc encrypted output. Unfortunately only first half matches and the next half is not matching. It would be great if someone can point me where the mistake lies,

original code from msdn is here.

Here is the output generated from my code (below). Please note that there is no difference in my code apart from modifying the input plain string. enter image description here

Here is the output from the online website (http://aes.online-domain-tools.com/ and anothersite) enter image description here

The first half ends at "B0 C4 29 18".. after that the 2nd half doesn't match.

Here is the code snippet

#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>

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

#ifndef STATUS_UNSUCCESSFUL
#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)
#endif // !STATUS_UNSUCCESSFUL

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#endif

void
print_inhex(char *buf, int len) {
    for (int i = 0; i < len; i++)
        printf(" %02x", buf[i]);
    printf("\n");
}

const BYTE rgbPlaintext[] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};

static const BYTE rgbIV[] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};

static const BYTE rgbAES128Key[] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};


void
CNG_aes_cbc()
{

    BCRYPT_ALG_HANDLE       hAesAlg = NULL;
    BCRYPT_KEY_HANDLE       hKey = NULL;
    NTSTATUS                status = STATUS_UNSUCCESSFUL;
    DWORD                   cbCipherText = 0,
        cbPlainText = 0,
        cbData = 0,
        cbKeyObject = 0,
        cbBlockLen = 0,
        cbBlob = 0;
    PBYTE                   pbCipherText = NULL,
        pbPlainText = NULL,
        pbKeyObject = NULL,
        pbIV = NULL,
        pbBlob = NULL;

    // Open an algorithm handle.
    if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hAesAlg, BCRYPT_AES_ALGORITHM, NULL, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
        goto Cleanup;
    }

    // Calculate the size of the buffer to hold the KeyObject.
    if (!NT_SUCCESS(status = BCryptGetProperty(hAesAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbData, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    // Allocate the key object on the heap.
    pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
    if (NULL == pbKeyObject) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    // Calculate the block length for the IV.
    if (!NT_SUCCESS(status = BCryptGetProperty(hAesAlg, BCRYPT_BLOCK_LENGTH, (PBYTE)&cbBlockLen, sizeof(DWORD), &cbData, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    // Determine whether the cbBlockLen is not longer than the IV length.
    if (cbBlockLen > sizeof(rgbIV)) {
        wprintf(L"**** block length is longer than the provided IV length\n");
        goto Cleanup;
    }

    // Allocate a buffer for the IV. The buffer is consumed during the 
    // encrypt/decrypt process.
    pbIV = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlockLen);
    if (NULL == pbIV) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    memcpy(pbIV, rgbIV, cbBlockLen);

    if (!NT_SUCCESS(status = BCryptSetProperty(hAesAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptSetProperty\n", status);
        goto Cleanup;
    }

    // Generate the key from supplied input key bytes.
    if (!NT_SUCCESS(status = BCryptGenerateSymmetricKey(hAesAlg, &hKey, pbKeyObject, cbKeyObject, (PBYTE)rgbAES128Key, sizeof(rgbAES128Key), 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptGenerateSymmetricKey\n", status);
        goto Cleanup;
    }


    // Save another copy of the key for later.
    if (!NT_SUCCESS(status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, NULL, 0, &cbBlob, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptExportKey\n", status);
        goto Cleanup;
    }


    // Allocate the buffer to hold the BLOB.
    PUCHAR pbBlob_1 = (PUCHAR)malloc(sizeof(PUCHAR) * cbBlob);
    //pbBlob = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlob);
    if (NULL == pbBlob_1) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, pbBlob_1, cbBlob, &cbBlob, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptExportKey\n", status);
        goto Cleanup;
    }

    PUCHAR blob = pbBlob_1 + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER);
    int len = cbBlob - sizeof(BCRYPT_KEY_DATA_BLOB_HEADER);
    printf("key:");
    print_inhex(blob, len);

    cbPlainText = sizeof(rgbPlaintext);
    pbPlainText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbPlainText);
    if (NULL == pbPlainText) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    /*memcpy(pbPlainText, rgbPlaintext, sizeof(rgbPlaintext));*/
    char *test_msg = "This is my test msg";
    cbPlainText = strlen(test_msg) + 1;
    memcpy(pbPlainText, test_msg, cbPlainText);

    printf("plain text:");
    print_inhex(test_msg, strlen(test_msg));

    // Get the output buffer size.
    if (!NT_SUCCESS(status = BCryptEncrypt(hKey, pbPlainText, cbPlainText, NULL, pbIV, cbBlockLen, NULL, 0, &cbCipherText, BCRYPT_BLOCK_PADDING)))  {
        wprintf(L"**** Error 0x%x returned by BCryptEncrypt\n", status);
        goto Cleanup;
    }

    pbCipherText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbCipherText);
    if (NULL == pbCipherText) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    // Use the key to encrypt the plaintext buffer.
    // For block sized messages, block padding will add an extra block.
    if (!NT_SUCCESS(status = BCryptEncrypt(hKey, pbPlainText, cbPlainText, NULL, pbIV, 
                         cbBlockLen, pbCipherText, cbCipherText, &cbData, BCRYPT_BLOCK_PADDING))){
        wprintf(L"**** Error 0x%x returned by BCryptEncrypt\n", status);
        goto Cleanup;
    }

    printf("cipher text:");
    for (int i = 0; i < cbCipherText; i++)
        printf(" %02x", pbCipherText[i]);

    wprintf(L"\nSuccess!\n");

Cleanup:  
    if (hAesAlg)
        BCryptCloseAlgorithmProvider(hAesAlg, 0);

    if (hKey)
        BCryptDestroyKey(hKey);

    if (pbCipherText)
        HeapFree(GetProcessHeap(), 0, pbCipherText);

    if (pbKeyObject)
        HeapFree(GetProcessHeap(), 0, pbKeyObject);

    if (pbIV)
        HeapFree(GetProcessHeap(), 0, pbIV);
}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
ABCDEFG
  • 187
  • 3
  • 11
  • The IV is not 256-bits, it is in hex w=here each character represents 4-bits, 32-chars * 4-bits = 128-bits. Also don't provide the input, key and IV in a screen shot. – zaph Mar 25 '17 at 00:50
  • But I am using the same IV in my program and online websites as well.. none of them complained.. it is the same code from msdn so I am assuming that IV might not be the issue. – ABCDEFG Mar 25 '17 at 00:53
  • The length of the data is not an exact multiple of the block size (16-bytes for AES) so padding is added but. That is where the implementation either rejects the data, pads with a default method such as 0x00 (cryptomathic), PKCS#7 (the generally used padding) or whatever junbk follews the provided data in memory. It is best to specify the padding on instantiation of AES. – zaph Mar 25 '17 at 01:11
  • Thanks for your reply... As per https://msdn.microsoft.com/en-us/library/windows/desktop/aa375421(v=vs.85).aspx... For symmetric keys the only allowed options are 0 or BCRYPT_BLOCK_PADDING... It doesn't talk about "padding on instantiation of AES".. could you please provide more details how to do this using CNG. – ABCDEFG Mar 25 '17 at 01:37

2 Answers2

3

You aren't consistent with your value for cbPlainText.

Asides:

  • You also have some very scary copy/realloc code where you write a string over a buffer not guaranteed as big as the string).
  • You also defined NT_SUCCESS in such a way that it returns whether or not something failed. 0 is success, !0 is failure.

You hex-printed up to strlen of tst_msg. But you set cbPlainText = strlen(tst_msg) + 1. If you set it to strlen(tst_msg) then you get @zaph's answer (46CC2228E81B2A05E8E8EBF2B0C42918EC496128D7C45BD0B19BB2D6452A3936).

You don't match the website because you used CNG with PKCS#7 padding, and the website uses zero-padding. You could identify the padding used in the website by taking your output ciphertext and putting it as the plaintext, then hitting decrypt. It then says your input was 54686973206973206d792074657374206d736700000000000000000000000000. Or, if you add 00 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C on your input to the website you'll get your original answer. Or add 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D and you'll get @zaph's answer.

So, things to do:

  • Don't re-evaluate the length of things to print, make one variable (cbPlainText) and stick with it.
  • AES is a block cipher algorithm. All block ciphers require complete blocks, a deficient last block must be padded (and under removable padding schemes a complete final block requires yet another block). Learn what this means before continuing. https://en.wikipedia.org/wiki/Padding_(cryptography)#Symmetric_cryptography
  • When something seems wrong with encryption, look at the decrypted output.
    • Very often the decrypted output with "no padding" is revealing.
  • Learn C, and how memory works. Or switch to C# and have a less steep learning curve.
bartonjs
  • 30,352
  • 2
  • 71
  • 111
0

The length of the data is not an exact multiple of the block size (16-bytes for AES) so padding is added but. That is where the implementation either rejects the data, pads with a default method such as 0x00 (cryptomathic), PKCS#7 (the generally used padding) or whatever junk follews the provided data in memory.

Don't use BCryptEncrypt, use AES Class

SymmetricAlgorithm.Padding Property Note: The default is PaddingMode.PKCS7.

It is best to specify the padding on instantiation of AES.

See PaddingMode Enumeration: PKCS7
The PKCS #7 padding string consists of a sequence of bytes, each of which is equal to the total number of padding bytes added.

Manually adding PKCS#7 padding to:

cryptommathic AES:

produces: 46CC2228E81B2A05E8E8EBF2B0C42918EC496128D7C45BD0B19BB2D6452A3936

zaph
  • 111,848
  • 21
  • 189
  • 228