1

I am trying to use Windows CNG BCRYPT_XTS_AES_ALGORITHM algorithm to encrypt and decrypt the files. As part of this, I had written the following code for encryption and decryption.

When I use this code with BCRYPT_AES_ALGORITHM algorithm, both encrypt and decrypt are working fine. But when the same is being used with BCRYPT_XTS_AES_ALGORITHM, it is throwing the STATUS_INVALID_PARAMETER error in BCryptGenerateSymmetricKey API.

Any help on this is very much appreciated.

auto AesCrypt::CreateAESProviderAlgo()->void
{
    auto status = BCryptOpenAlgorithmProvider(&m_aesHandle, BCRYPT_AES_ALGORITHM, nullptr, 0);
    //auto status = BCryptOpenAlgorithmProvider(&m_aesHandle, BCRYPT_XTS_AES_ALGORITHM, nullptr, 0);
    if (0 != status) {
        N2S_THROW("BCryptException::Failed to get provider for BCRYPT_XTS_AES_ALGORITHM. Reason: " + GetErrorCodeAsString(status));
    }

    DWORD cbData = 0;
    DWORD cbKeyObject = 0;
    status = BCryptGetProperty(m_aesHandle, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbData, 0);
    if (0 != status) {
        N2S_THROW("CreateSymmetricKeySHA1Hash::BCryptGetProperty return with error " + GetErrorCodeAsString(status));
    }

    m_pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
    if (nullptr == m_pbKeyObject) {
        N2S_THROW("CreateSymmetricKeySHA1Hash::Memory allocation failed.");
    }

    status = BCryptGenerateSymmetricKey(m_aesHandle, &m_keyHandle, m_pbKeyObject, cbKeyObject, (PUCHAR)m_encrptKey.c_str(), SYMM_KEY_SIZE_SECRET, 0);
    if (0 != status) {
        N2S_THROW("CreateSymmetricKeySHA1Hash::BCryptGenerateSymmetricKey return with error " + GetErrorCodeAsString(status));
    }
}

auto AesCrypt::ProcessEncryptFile() ->void
{
    DWORD bytesToSave = 0;
    UCHAR bufFileToOpen[BLOCK_SIZE] = { 0 };
    UCHAR bufFileToSave[BLOCK_SIZE * 2] = { 0 }; // TODO: Need to alloc on heap and reuse it.

    auto toReadBytes = GetFileSize(m_workOnFile);

    for (;;) // TODO: Need to take out the duplicate code from both locations.
    {
        m_readStream.read((CHAR *)bufFileToOpen, BLOCK_SIZE);
        auto bytesRead = m_readStream.gcount();

        if (0 == bytesRead) {
            N2S_THROW("Error reading the file " + GetStringFromWstring(m_workOnFile));
        }

        toReadBytes -= bytesRead;

        if (0 != toReadBytes && bytesRead == BLOCK_SIZE) {
            GetCryptStatus(BCryptEncrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, bufFileToSave, (ULONG)bytesRead, &bytesToSave, 0));
            m_writeStream.write((CHAR *)bufFileToSave, bytesToSave);
            continue;
        }

        // Reading the last byte
        if (0 != BCryptEncrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, nullptr, 0, &bytesToSave, BCRYPT_BLOCK_PADDING)) {
            N2S_THROW("BCryptEncrypt::Error receiving the size required for the ciphertext.");
        }

        GetCryptStatus(BCryptEncrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, bufFileToSave, bytesToSave, &bytesToSave, BCRYPT_BLOCK_PADDING));
        m_writeStream.write((CHAR *)bufFileToSave, bytesToSave);
        return; // Last block done.
    }
}

auto AesCrypt::ProcessDecryptFile()->void
{
    DWORD bytesToSave = 0;
    UCHAR bufFileToOpen[BLOCK_SIZE] = { 0 };
    UCHAR bufFileToSave[BLOCK_SIZE * 2] = { 0 }; // TODO: Need to alloc on heap and reuse it.

    auto toReadBytes = GetFileSize(m_workOnFile);

    for (;;)
    {
        m_readStream.read((CHAR *)bufFileToOpen, BLOCK_SIZE);
        auto bytesRead = m_readStream.gcount();

        if (0 == bytesRead) {
            N2S_THROW("Error reading the file " + GetStringFromWstring(m_workOnFile));
        }

        toReadBytes -= bytesRead;

        if (0 != toReadBytes && bytesRead == BLOCK_SIZE) {
            GetCryptStatus(BCryptDecrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, bufFileToSave, (ULONG)bytesRead, &bytesToSave, 0));
            m_writeStream.write((CHAR *)bufFileToSave, bytesToSave);
            continue;
        }

        // Reading last block data
        if (0 != BCryptDecrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, nullptr, 0, &bytesToSave, BCRYPT_BLOCK_PADDING)) {
            N2S_THROW("BCryptEncrypt::Error receiving the size required for the ciphertext.");
        }

        GetCryptStatus(BCryptDecrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, bufFileToSave, bytesToSave, &bytesToSave, BCRYPT_BLOCK_PADDING));
        m_writeStream.write((CHAR *)bufFileToSave, bytesToSave);
        return; // Last block done.
    }
}
jww
  • 97,681
  • 90
  • 411
  • 885
Dev
  • 21
  • 3
  • If I remember correctly, `BCRYPT_SUCCESS` macro evaluates `true` on non-negative. So you may try replacing the `0 != status` with macro `!BCRYPT_SUCCESS(status)`, then see what happens. In additions, you may also take a look at this answer => [Are there any examples of how to perform AES-XTS encryption using CNG?](https://social.msdn.microsoft.com/Forums/en-US/f1318e80-ce34-41b0-9523-3a0cfd2d1c1d/are-there-any-examples-of-how-to-perform-aesxts-encryption-using-cng#b7eec463-5316-4379-8c49-65eee2b6e24b) – sandthorn Jul 23 '18 at 06:07
  • I tried the BCRYPT_SUCCESS macro, still I am facing the same issue. I already tried the given link, but I couldn't get much info from that link. Any other help is very much appreciated. – Dev Jul 23 '18 at 23:20
  • `STATUS_INVALID_PARAMETER` means perhaps you might input wrong types of variable. Make sure your `m_keyHandle` is type `BCRYPT_KEY_HANDLE` and `SYMM_KEY_SIZE_SECRET` is type `ULONG` and less or equals `m_encrptKey.size()`. I tried compile against Win10SDK v10.0.16299.0 and function `BCryptGenerateSymmetricKey` returns `BCRYPT_SUCCESS` status => [godbolt](https://godbolt.org/g/yk5bpt). document => [`BCryptGenerateSymmetricKey`](https://learn.microsoft.com/en-us/windows/desktop/api/bcrypt/nf-bcrypt-bcryptgeneratesymmetrickey) – sandthorn Jul 31 '18 at 11:28
  • Maybe you need to use a different provider? Another person was having trouble with `BCRYPT_AES_GMAC_ALGORITHM`, but I never figured out why. Also see [How do I use AES-GMAC with a secret in BCrypt?](https://stackoverflow.com/q/57456546/608639) and [How to determine which of 23 parameters are STATUS_INVALID_PARAMETER?](https://stackoverflow.com/q/57487839/608639) – jww Aug 19 '19 at 09:58

1 Answers1

2

In your AesCrypt::CreateAESProviderAlgo method above, insure that SYMM_KEY_SIZE_SECRET is 32 and that the associated string is also 32 bytes long.

A copy and paste of your function into my test program worked for me when SYMM_KEY_SIZE_SECRET was specified as 32. It didn't work when I specified a SYMM_KEY_SIZE_SECRET value of 16.

In addition, this algorithm is only supported on Windows 10 and above.

Further info:

XTS is a 2 key variant of AES. It also requires a Message Block Size (sector size) and a Sector Number.

I used BCryptGenerateSymmetricKey to build the key. I took an input 'string' of either 32 bytes (2 16 byte keys) or 64 bytes (2 32 byte keys) to generate the keys from the 'string'. No other values were accepted.

The Message Block Size is set as the BCRYPT_MESSAGE_BLOCK_LENGTH parameter on the key handle. It does not have to be an integral multiple of the AES block length - this is the difference between XTS and XEX.

The Sector Number is supplied as the IV value in the Encrypt / Decrypt APIs. For me, only a length of 8 for the IV was accepted.

Working code snippet:

    BCRYPT_ALG_HANDLE m_aesHandle;
    auto status = BCryptOpenAlgorithmProvider(&m_aesHandle, /* BCRYPT_XTS_AES_ALGORITHM */ L"XTS-AES", nullptr, 0);
    if (0 != status)
    {
        csMsg.Format(TEXT("\r\nBCryptException::Failed to get provider for BCRYPT_XTS_AES_ALGORITHM. Error: %d"), status);
        csText += csMsg;
    }
    else
    {
        csMsg.Format(TEXT("\r\nBCryptOpenAlgorithmProvider XTS SUCCESS!"));
        csText += csMsg;

        DWORD cbData = 0;
        DWORD cbKeyObject = 0;
        status = BCryptGetProperty(m_aesHandle, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbData, 0);
        if (0 != status)
        {
            csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_OBJECT_LENGTH return error %d (0x%x)"), status, status);
            csText += csMsg;
        }
        else
        {
            csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_OBJECT_LENGTH returned object length %u"), cbKeyObject);
            csText += csMsg;

            PBYTE m_pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
            if (nullptr == m_pbKeyObject)
            {
                csMsg.Format(TEXT("\r\nMemory allocation failed."));
                csText += csMsg;
            }
            else
            {
                BCRYPT_KEY_HANDLE m_keyHandle;
                CHAR String[] = "1234567890abcdef01234567890abcdef1234567890abcdef01234567890abcdef";
                static ULONG uStringLen = 32;
                BYTE Dummy[128] = { 0 };

                status = BCryptGenerateSymmetricKey(m_aesHandle, &m_keyHandle, m_pbKeyObject, cbKeyObject, (PUCHAR)String, uStringLen, 0);
                if (0 != status)
                {
                    csMsg.Format(TEXT("\r\nBCryptGenerateSymmetricKey return with error %d (0x%x)"), status, status);
                    csText += csMsg;
                }
                else
                {
                    csMsg.Format(TEXT("\r\nBCryptGenerateSymmetricKey SUCCESS!"));
                    csText += csMsg;

                    DWORD cbKey = 0;
                    status = BCryptGetProperty(m_keyHandle, BCRYPT_KEY_LENGTH, (PBYTE)&cbKey, sizeof(DWORD), &cbData, 0);
                    if (0 != status)
                    {
                        csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_KEY_LENGTH return error %d (0x%x)"), status, status);
                        csText += csMsg;
                    }
                    else
                    {
                        csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_KEY_LENGTH returned key length %u"), cbKey);
                        csText += csMsg;
                    }

                    DWORD cbBlock = 0;
                    status = BCryptGetProperty(m_aesHandle, BCRYPT_BLOCK_LENGTH, (PBYTE)&cbBlock, sizeof(DWORD), &cbData, 0);
                    if (0 != status)
                    {
                        csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_BLOCK_LENGTH return error %d (0x%x)"), status, status);
                        csText += csMsg;
                    }
                    else
                    {
                        csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_BLOCK_LENGTH returned key block length %u"), cbBlock);
                        csText += csMsg;
                    }

                    DWORD cbMessage = 512;
                    status = BCryptSetProperty(m_keyHandle, /* BCRYPT_MESSAGE_BLOCK_LENGTH */ L"MessageBlockLength", (PBYTE)&cbMessage, sizeof(DWORD), 0);
                    if (0 != status)
                    {
                        csMsg.Format(TEXT("\r\nBCryptSetProperty BCRYPT_MESSAGE_BLOCK_LENGTH return error %d (0x%x)"), status, status);
                        csText += csMsg;
                    }
                    else
                    {
                        csMsg.Format(TEXT("\r\nBCryptSetProperty BCRYPT_MESSAGE_BLOCK_LENGTH SUCCESS message length %u"), cbMessage);
                        csText += csMsg;
                    }

                    BYTE PlaintextBuf[512], CipherTextBuf[512], DecryptedTextBuf[512];
                    int i;

                    for (i = 0; i < _countof(PlaintextBuf); i++)
                    {
                        PlaintextBuf[i] = String[i % _countof(String)];
                    }

                    BYTE IV[] = 
                    { 
                         0,  1,  2,  3,  4,  5,  6, 7, 
                         8,  9, 10, 11, 12, 13, 14, 15, 
                        16, 17, 18, 19, 20, 21, 22, 23, 
                        24, 25, 26, 27, 28, 29, 30, 31, 
                    };
                    static DWORD dwIVLen = 8;

                    status = BCryptEncrypt(m_keyHandle, PlaintextBuf, sizeof(PlaintextBuf), nullptr, 
                        IV, dwIVLen, CipherTextBuf, sizeof(CipherTextBuf), &cbData, 0);
                    if (0 != status)
                    {
                        csMsg.Format(TEXT("\r\nBCryptEncrypt return with error %d (0x%x)"), status, status);
                        csText += csMsg;
                    }
                    else
                    {
                        csMsg.Format(TEXT("\r\nBCryptEncrypt SUCCESS!"));
                        csText += csMsg;

                        status = BCryptDecrypt(m_keyHandle, CipherTextBuf, sizeof(CipherTextBuf), nullptr,
                            IV, dwIVLen, DecryptedTextBuf, sizeof(DecryptedTextBuf), &cbData, 0);
                        if (0 != status)
                        {
                            csMsg.Format(TEXT("\r\nBCryptDecrypt return with error %d (0x%x)"), status, status);
                            csText += csMsg;
                        }
                        else
                        {
                            csMsg.Format(TEXT("\r\nBCryptDecrypt SUCCESS!"));
                            csText += csMsg;

                            bool bGood = true;
                            for (i = 0; i < _countof(PlaintextBuf); i++)
                            {
                                if (PlaintextBuf[i] != DecryptedTextBuf[i])
                                {
                                    bGood = false;
                                    break;
                                }
                            }

                            if (bGood)
                                csMsg.Format(TEXT("\r\nCrypt Verification SUCCESS!"));
                            else
                                csMsg.Format(TEXT("\r\nCrypt Verification FAILURE, pos=%u!"), i);
                            csText += csMsg;
                        }
                    }

                    BCryptDestroyKey(m_keyHandle);
                }

                HeapFree(GetProcessHeap(), 0, m_pbKeyObject);
            }
        }

        BCryptCloseAlgorithmProvider(m_aesHandle, 0);
    }
David C
  • 21
  • 4