2025-11-28 00:35:46 +09:00

833 lines
30 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// File: KeyDerivationWithPersistedKeys.cpp
//
// Contents: This sample shows how to create a persisted KDF key, and then derive
// ephemeral KDF/symmetric keys from the persisted KDF key using the NCryptKeyDerivation API.
// The derived ephemeral AES key is used to encrypt and decrypt a message in CBC mode( non authentication mode )
//
//
#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS
#include <winternl.h>
#include <ntstatus.h>
#include <winerror.h>
#include <stdio.h>
#include <ncrypt.h>
#include <bcrypt.h>
#include <sal.h>
static const
BYTE Secret[20] =
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
};
static const
BYTE GenericParameter[20] =
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
};
static const
BYTE Message[20] =
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
};
//
// Alternatively, these values can be found using NCryptGetProperty(NCRYPT_LENGTHS_PROPERTY)
// on an AES key
//
static const
DWORD AesKeyLength = 16; // in bytes
//
// Utilities and helper functions
//
//----------------------------------------------------------------------------
//
// ReportError
// Prints error information to the console
//
//----------------------------------------------------------------------------
void
ReportError(
_In_ DWORD dwErrCode
)
{
wprintf( L"Error: 0x%08x (%d)\n", dwErrCode, dwErrCode );
}
NTSTATUS
EncryptData(
_In_ BCRYPT_KEY_HANDLE KeyHandle,
_In_ DWORD KeyObjectLength,
_In_reads_bytes_(InitVectorLength)
PBYTE InitVector,
_In_ DWORD InitVectorLength,
_In_reads_bytes_(ChainingModeLength)
PBYTE ChainingMode,
_In_ DWORD ChainingModeLength,
_In_reads_bytes_(PlainTextLength)
PBYTE PlainText,
_In_ DWORD PlainTextLength,
_Outptr_result_bytebuffer_(*CipherTextLengthPointer)
PBYTE *CipherTextPointer,
_Out_ DWORD *CipherTextLengthPointer
)
{
NTSTATUS Status;
BCRYPT_KEY_HANDLE EncryptKeyHandle = NULL;
DWORD ResultLength = 0;
PBYTE TempInitVector = NULL;
DWORD TempInitVectorLength = 0;
PBYTE CipherText = NULL;
DWORD CipherTextLength = 0;
PBYTE KeyObject = NULL;
//
// Allocate KeyObject on the heap
//
KeyObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, KeyObjectLength);
if( NULL == KeyObject )
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
//
// Generate a duplicate AES key
//
Status = BCryptDuplicateKey(
KeyHandle, // A pointer to key handle
&EncryptKeyHandle, // A pointer to BCRYPT_KEY_HANDLE that recieves a handle to the duplicate key
KeyObject, // A pointer to the buffer that recieves the duplicate key object
KeyObjectLength, // Size of the buffer in bytes
0); // Flags
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
//
// Set the chaining mode on the key handle
//
Status = BCryptSetProperty(
EncryptKeyHandle, // Handle to a CNG object
BCRYPT_CHAINING_MODE, // Property name(null terminated unicode string)
ChainingMode, // Address of the buffer that contains the new property value
ChainingModeLength, // Size of the buffer in bytes
0); // Flags
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
//
// Copy initialization vector into a temporary initialization vector buffer
// Because after an encrypt/decrypt operation, the IV buffer is overwritten.
//
TempInitVector = (PBYTE)HeapAlloc (GetProcessHeap(), 0, InitVectorLength);
if( NULL == TempInitVector )
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
TempInitVectorLength = InitVectorLength;
memcpy(TempInitVector, InitVector, TempInitVectorLength);
//
// Get CipherText's length
// If the program can compute the length of cipher text(based on algorihtm and chaining mode info.), this call can be avoided.
//
Status = BCryptEncrypt(
EncryptKeyHandle, // Handle to a key which is used to encrypt
PlainText, // Address of the buffer that contains the plaintext
PlainTextLength, // Size of the buffer in bytes
NULL, // A pointer to padding info, used with asymmetric and authenticated encryption; else set to NULL
TempInitVector, // Address of the buffer that contains the IV.
TempInitVectorLength, // Size of the IV buffer in bytes
NULL, // Address of the buffer the recieves the ciphertext
0, // Size of the buffer in bytes
&CipherTextLength, // Variable that recieves number of bytes copied to ciphertext buffer
BCRYPT_BLOCK_PADDING); // Flags; Block padding allows to pad data to the next block size
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
//
// Allocate Cipher Text on the heap
//
CipherText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, CipherTextLength);
if( NULL == CipherText )
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
//
// Peform encyption
// For block length messages, block padding will add an extra block
//
Status = BCryptEncrypt(
EncryptKeyHandle, // Handle to a key which is used to encrypt
PlainText, // Address of the buffer that contains the plaintext
PlainTextLength, // Size of the buffer in bytes
NULL, // A pointer to padding info, used with asymmetric and authenticated encryption; else set to NULL
TempInitVector, // Address of the buffer that contains the IV.
TempInitVectorLength, // Size of the IV buffer in bytes
CipherText, // Address of the buffer the recieves the ciphertext
CipherTextLength, // Size of the buffer in bytes
&ResultLength, // Variable that recieves number of bytes copied to ciphertext buffer
BCRYPT_BLOCK_PADDING); // Flags; Block padding allows to pad data to the next block size
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
*CipherTextPointer = CipherText;
CipherText = NULL;
*CipherTextLengthPointer = CipherTextLength;
cleanup:
if( NULL != CipherText)
{
SecureZeroMemory(CipherText, CipherTextLength);
HeapFree(GetProcessHeap(), 0, CipherText);
}
if( NULL != KeyObject)
{
SecureZeroMemory(KeyObject, KeyObjectLength);
HeapFree(GetProcessHeap(), 0, KeyObject);
}
if( NULL != TempInitVector )
{
HeapFree(GetProcessHeap(), 0, TempInitVector);
}
if( NULL != EncryptKeyHandle )
{
BCryptDestroyKey(EncryptKeyHandle);
}
return Status;
}
//-----------------------------------------------------------------------------
//
// Decrypt Data
//
//-----------------------------------------------------------------------------
NTSTATUS
DecryptData(
_In_ BCRYPT_KEY_HANDLE KeyHandle,
_In_ DWORD KeyObjectLength,
_In_reads_bytes_(InitVectorLength)
PBYTE InitVector,
_In_ DWORD InitVectorLength,
_In_reads_bytes_(ChainingModeLength)
PBYTE ChainingMode,
_In_ DWORD ChainingModeLength,
_In_reads_bytes_(CipherTextLength)
PBYTE CipherText,
_In_ DWORD CipherTextLength,
_Outptr_result_bytebuffer_(*PlainTextLengthPointer)
PBYTE *PlainTextPointer,
_Out_ DWORD *PlainTextLengthPointer
)
{
NTSTATUS Status;
PBYTE TempInitVector = NULL;
DWORD TempInitVectorLength = 0;
PBYTE PlainText = NULL;
DWORD PlainTextLength = 0;
DWORD ResultLength = 0;
BCRYPT_KEY_HANDLE DecryptKeyHandle = NULL;
PBYTE KeyObject = NULL;
//
// Allocate KeyObject on the heap
//
KeyObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, KeyObjectLength);
if( NULL == KeyObject )
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
//
// Generate a duplicate AES key
//
Status = BCryptDuplicateKey(
KeyHandle, // A pointer to key handle
&DecryptKeyHandle, // A pointer to BCRYPT_KEY_HANDLE that recieves a handle to the duplicate key
KeyObject, // A pointer to the buffer that recieves the duplicate key object
KeyObjectLength, // Size of the buffer in bytes
0); // Flags
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
//
// Set the chaining mode on the key handle
//
Status = BCryptSetProperty(
DecryptKeyHandle, // Handle to a CNG object
BCRYPT_CHAINING_MODE, // Property name(null terminated unicode string)
ChainingMode, // Address of the buffer that contains the new property value
ChainingModeLength, // Size of the buffer in bytes
0); // Flags
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
//
// Copy initialization vector into a temporary initialization vector buffer
// Because after an encrypt/decrypt operation, the IV buffer is overwritten.
//
TempInitVector = (PBYTE)HeapAlloc (GetProcessHeap(), 0, InitVectorLength);
if( NULL == TempInitVector )
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
TempInitVectorLength = InitVectorLength;
memcpy(TempInitVector, InitVector, TempInitVectorLength);
//
// Get CipherText's length
// If the program can compute the length of cipher text(based on algorihtm and chaining mode info.), this call can be avoided.
//
Status = BCryptDecrypt(
DecryptKeyHandle, // Handle to a key which is used to encrypt
CipherText, // Address of the buffer that contains the ciphertext
CipherTextLength, // Size of the buffer in bytes
NULL, // A pointer to padding info, used with asymmetric and authenticated encryption; else set to NULL
TempInitVector, // Address of the buffer that contains the IV.
TempInitVectorLength, // Size of the IV buffer in bytes
NULL, // Address of the buffer the recieves the plaintext
0, // Size of the buffer in bytes
&PlainTextLength, // Variable that recieves number of bytes copied to plaintext buffer
BCRYPT_BLOCK_PADDING); // Flags; Block padding allows to pad data to the next block size
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
PlainText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, PlainTextLength);
if(NULL == PlainText)
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
//
// Decrypt CipherText
//
Status = BCryptDecrypt(
DecryptKeyHandle, // Handle to a key which is used to encrypt
CipherText, // Address of the buffer that contains the ciphertext
CipherTextLength, // Size of the buffer in bytes
NULL, // A pointer to padding info, used with asymmetric and authenticated encryption; else set to NULL
TempInitVector, // Address of the buffer that contains the IV.
TempInitVectorLength, // Size of the IV buffer in bytes
PlainText, // Address of the buffer the recieves the plaintext
PlainTextLength, // Size of the buffer in bytes
&ResultLength, // Variable that recieves number of bytes copied to plaintext buffer
BCRYPT_BLOCK_PADDING); // Flags; Block padding allows to pad data to the next block size
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
*PlainTextPointer = PlainText;
PlainText = NULL;
*PlainTextLengthPointer = PlainTextLength;
cleanup:
if( NULL != PlainText )
{
HeapFree(GetProcessHeap(), 0, PlainText);
}
if( NULL != KeyObject)
{
SecureZeroMemory(KeyObject, KeyObjectLength);
HeapFree(GetProcessHeap(), 0, KeyObject);
}
if( NULL != TempInitVector )
{
HeapFree(GetProcessHeap(), 0, TempInitVector);
}
if( NULL != DecryptKeyHandle )
{
BCryptDestroyKey(DecryptKeyHandle);
}
return Status;
}
//-----------------------------------------------------------------------------
//
// wmain
//
//-----------------------------------------------------------------------------
DWORD
__cdecl
wmain(
_In_ int argc,
_In_reads_(argc) LPWSTR argv[]
)
{
NTSTATUS Status;
SECURITY_STATUS secStatus = S_OK;
NCRYPT_PROV_HANDLE ProviderHandle = 0;
NCRYPT_KEY_HANDLE KdfKeyHandle = 0;
BCRYPT_KEY_HANDLE AesKeyHandle = NULL;
BCRYPT_ALG_HANDLE AesAlgHandle = NULL;
LPCWSTR KdfKeyName = L"SampleKDFKey";
NCryptBuffer GenericKdfParameters[2] = {0};
NCryptBufferDesc GenericKdfParamList;
PBYTE IVBuffer = NULL;
DWORD IVBufferLength = 0;
PBYTE EncryptedMessage = NULL;
DWORD EncryptedMessageLength = 0;
PBYTE DecryptedMessage = NULL;
DWORD DecryptedMessageLength = 0;
DWORD ResultLength = 0;
DWORD AesKeyMaterialLength = AesKeyLength;
PBYTE AesKeyMaterial = NULL;
DWORD KeyObjectLength = 0;
DWORD BlockLength = 0;
//
// Open Microsoft KSP (Key Storage Provider)
//
secStatus = NCryptOpenStorageProvider(
&ProviderHandle, // Pointer to a variable that recieves the provider handle
MS_KEY_STORAGE_PROVIDER, // Storage provider identifier(null terminated unicode string); If NULL, default provider is loaded
0); // Flags
if( FAILED(secStatus) )
{
ReportError(secStatus);
goto cleanup;
}
//
// Creating a persisted KDF key
// KDF Algorithm used: SP800-108 Hmac counter mode
//
// NCRYPT_OVERWRITE_KEY_FLAG - If a key already exists in the container with the specified name, the existing key will be overwritten.
//
secStatus = NCryptCreatePersistedKey(
ProviderHandle, // Handle of the key storage provider
&KdfKeyHandle, // Address of the variable that recieves the key handle
NCRYPT_SP800108_CTR_HMAC_ALGORITHM, // Algorithm name (null terminated unicode string)
KdfKeyName, // Key name (null terminated unicode string)
0, // Legacy identifier (AT_KEYEXCHANGE, AT_SIGNATURE or 0 )
NCRYPT_OVERWRITE_KEY_FLAG); // Flags; If a key already exists in the container with the specified name, the existing key will be overwritten.
if( FAILED(secStatus) )
{
ReportError(secStatus);
goto cleanup;
}
//
// Set the secret on the KDF key handle
//
secStatus = NCryptSetProperty(
KdfKeyHandle, // Handle of the key storage object
NCRYPT_KDF_SECRET_VALUE, // Property name (null terminated unicode string)
(PBYTE)Secret, // Address of the buffer that contains the property value
sizeof(Secret), // Size of the buffer in bytes
0); // Flags
if( FAILED(secStatus) )
{
ReportError(secStatus);
goto cleanup;
}
//
// Finalize the key generation process
// The key is usable here onwards
//
secStatus = NCryptFinalizeKey(
KdfKeyHandle, // Handle of the key - that has to be finalized
0); // Flags
if( FAILED(secStatus) )
{
ReportError(secStatus);
goto cleanup;
}
//
// Construct parameter list
//
//
// Generic parameters:
// KDF_GENERIC_PARAMETER and KDF_HASH_ALGORITHM are the generic parameters that can be passed for the following KDF algorithms:
// BCRYPT/NCRYPT_SP800108_CTR_HMAC_ALGORITHM
// KDF_GENERIC_PARAMETER = KDF_LABEL||0x00||KDF_CONTEXT
// BCRYPT/NCRYPT_SP80056A_CONCAT_ALGORITHM
// KDF_GENERIC_PARAMETER = KDF_ALGORITHMID || KDF_PARTYUINFO || KDF_PARTYVINFO {|| KDF_SUPPPUBINFO } {|| KDF_SUPPPRIVINFO }
// BCRYPT/NCRYPT_PBKDF2_ALGORITHM
// KDF_GENERIC_PARAMETER = KDF_SALT
// BCRYPT/NCRYPT_CAPI_KDF_ALGORITHM
// KDF_GENERIC_PARAMETER = Not used
//
// Alternatively, KDF specific parameters can be passed as well.
// For NCRYPT_SP800108_CTR_HMAC_ALGORITHM:
// KDF_HASH_ALGORITHM, KDF_LABEL and KDF_CONTEXT are required
// For NCRYPT_SP80056A_CONCAT_ALGORITHM:
// KDF_HASH_ALGORITHM, KDF_ALGORITHMID, KDF_PARTYUINFO, KDF_PARTYVINFO are required
// KDF_SUPPPUBINFO, KDF_SUPPPRIVINFO are optional
// For NCRYPT_PBKDF2_ALGORITHM
// KDF_HASH_ALGORITHM is required
// KDF_ITERATION_COUNT, KDF_SALT are optional
// Iteration count, (if not specified) will default to 10,000
// For NCRYPT_CAPI_KDF_ALGORITHM
// KDF_HASH_ALGORITHM is required
//
//
// Generic parameters are used in the sample
// KDF_HASH_ALGORITHM
//
GenericKdfParameters[0].cbBuffer = sizeof(BCRYPT_SHA256_ALGORITHM);
GenericKdfParameters[0].BufferType = KDF_HASH_ALGORITHM;
GenericKdfParameters[0].pvBuffer = (void*)BCRYPT_SHA256_ALGORITHM;
//
// KDF_GENERIC_PARAMETER
//
GenericKdfParameters[1].cbBuffer = sizeof(GenericParameter);
GenericKdfParameters[1].BufferType = KDF_GENERIC_PARAMETER;
GenericKdfParameters[1].pvBuffer = (void*)GenericParameter;
GenericKdfParamList.ulVersion = NCRYPTBUFFER_VERSION;
GenericKdfParamList.cBuffers = ARRAYSIZE(GenericKdfParameters);
GenericKdfParamList.pBuffers = GenericKdfParameters;
//
// Allocate the AesKeyMaterial on the heap
//
AesKeyMaterial = (PBYTE) HeapAlloc( GetProcessHeap(), 0, AesKeyMaterialLength);
if( NULL == AesKeyMaterial )
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
//
// Derive an ephemeral key
//
secStatus = NCryptKeyDerivation(
KdfKeyHandle, // Handel of the key
&GenericKdfParamList, // Address of the NCryptBufferDesc structure that contains the KDF parameters
AesKeyMaterial, // Variable that recieves the derived key material
AesKeyMaterialLength, // Size of the buffer in bytes
&ResultLength,
0); // Flags
if( FAILED(secStatus) )
{
ReportError(secStatus);
goto cleanup;
}
//
// Open an algorithm handle
//
Status = BCryptOpenAlgorithmProvider(
&AesAlgHandle, // Alg Handle pointer
BCRYPT_AES_ALGORITHM, // Cryptographic Algorithm name (null terminated unicode string)
NULL, // Provider name; if null, the default provider is loaded
0); // Flags
if( !NT_SUCCESS(Status) )
{
secStatus = HRESULT_FROM_NT(Status);
ReportError(secStatus);
goto cleanup;
}
//
// Generate an AES key from the key bytes
//
Status = BCryptGenerateSymmetricKey(
AesAlgHandle, // Algorithm provider handle
&AesKeyHandle, // A pointer to key handle
NULL, // A pointer to the buffer that recieves the key object;NULL implies memory is allocated and freed by the function
0, // Size of the buffer in bytes
(PBYTE)AesKeyMaterial, // A pointer to a buffer that contains the key material
AesKeyMaterialLength, // Size of the buffer in bytes
0); // Flags
if( !NT_SUCCESS(Status) )
{
secStatus = HRESULT_FROM_NT(Status);
ReportError(secStatus);
goto cleanup;
}
//
// Obtain key object size
//
Status = BCryptGetProperty(
AesAlgHandle, // Handle to a CNG object
BCRYPT_OBJECT_LENGTH, // Property name (null terminated unicode string)
(PBYTE)&KeyObjectLength, // Addr of the output buffer which recieves the property value
sizeof (KeyObjectLength), // Size of the buffer in the bytes
&ResultLength, // Number of bytes that were copied into the buffer
0); // Flags
if( !NT_SUCCESS(Status) )
{
secStatus = HRESULT_FROM_NT(Status);
ReportError(secStatus);
goto cleanup;
}
//
// Obtain block size
//
Status = BCryptGetProperty(
AesAlgHandle, // Handle to a CNG object
BCRYPT_BLOCK_LENGTH, // Property name (null terminated unicode string)
(PBYTE)&BlockLength, // Addr of the output buffer which recieves the property value
sizeof (BlockLength), // Size of the buffer in the bytes
&ResultLength, // Number of bytes that were copied into the buffer
0); // Flags
if( !NT_SUCCESS(Status) )
{
secStatus = HRESULT_FROM_NT(Status);
ReportError(secStatus);
goto cleanup;
}
IVBufferLength = BlockLength;
//
// Allocate the InitVector on the heap
//
IVBuffer = (PBYTE) HeapAlloc( GetProcessHeap(), 0, IVBufferLength);
if( NULL == IVBuffer )
{
secStatus = NTE_NO_MEMORY;
ReportError(secStatus);
goto cleanup;
}
//
// Generate IV randomly
//
Status = BCryptGenRandom (
NULL, // Alg Handle pointer; If NULL, the default provider is chosen
(PBYTE)IVBuffer, // Address of the buffer that recieves the random number(s)
IVBufferLength, // Size of the buffer in bytes
BCRYPT_USE_SYSTEM_PREFERRED_RNG); // Flags
if( !NT_SUCCESS(Status) )
{
secStatus = HRESULT_FROM_NT(Status);
ReportError(secStatus);
goto cleanup;
}
//
// Encrypt plain text
//
Status = EncryptData(
AesKeyHandle,
KeyObjectLength,
IVBuffer,
IVBufferLength,
(PBYTE)BCRYPT_CHAIN_MODE_CBC,
sizeof (BCRYPT_CHAIN_MODE_CBC),
(PBYTE)Message,
sizeof (Message),
&EncryptedMessage,
&EncryptedMessageLength);
if( !NT_SUCCESS(Status) )
{
secStatus = HRESULT_FROM_NT(Status);
ReportError(secStatus);
goto cleanup;
}
//
// Decrypt Cipher text
//
Status = DecryptData(
AesKeyHandle,
KeyObjectLength,
IVBuffer,
IVBufferLength,
(PBYTE)BCRYPT_CHAIN_MODE_CBC,
sizeof (BCRYPT_CHAIN_MODE_CBC),
EncryptedMessage,
EncryptedMessageLength,
&DecryptedMessage,
&DecryptedMessageLength);
if( !NT_SUCCESS(Status) )
{
secStatus = HRESULT_FROM_NT(Status);
ReportError(secStatus);
goto cleanup;
}
//
// Optional : Check if the original message and the message obtained after decrypt are the same
//
if( 0 != (memcmp(Message, DecryptedMessage, sizeof (Message))) )
{
secStatus = NTE_FAIL;
ReportError(secStatus);
goto cleanup;
}
secStatus = S_OK;
wprintf(L"Success!\n");
cleanup:
if( NULL != EncryptedMessage )
{
HeapFree( GetProcessHeap(), 0, EncryptedMessage);
EncryptedMessage = NULL;
}
if( NULL != DecryptedMessage )
{
HeapFree( GetProcessHeap(), 0, DecryptedMessage);
DecryptedMessage = NULL;
}
if( NULL != AesKeyMaterial )
{
SecureZeroMemory(AesKeyMaterial, AesKeyMaterialLength);
HeapFree( GetProcessHeap(), 0, AesKeyMaterial);
AesKeyMaterial = NULL;
}
if( NULL != IVBuffer )
{
HeapFree( GetProcessHeap(), 0, IVBuffer);
IVBuffer = NULL;
}
if( NULL != AesKeyHandle )
{
BCryptDestroyKey(AesKeyHandle);
AesKeyHandle = NULL;
}
if( NULL != AesAlgHandle )
{
BCryptCloseAlgorithmProvider(AesAlgHandle, 0);
AesAlgHandle = NULL;
}
if( NULL != KdfKeyHandle )
{
NCryptDeleteKey(KdfKeyHandle, 0);
KdfKeyHandle = 0;
}
if( NULL != ProviderHandle )
{
NCryptFreeObject(ProviderHandle); // The handle of the object to free
ProviderHandle = 0;
}
return (DWORD)secStatus;
UNREFERENCED_PARAMETER( argc );
UNREFERENCED_PARAMETER( argv );
}