private static void DeriveKeyCore(BCryptKeyHandle pbkdf2KeyHandle, string hashAlgorithm, byte* pbSalt, uint cbSalt, ulong iterCount, byte* pbDerivedBytes, uint cbDerivedBytes) { // First, build the buffers necessary to pass (hash alg, salt, iter count) into the KDF BCryptBuffer* pBuffers = stackalloc BCryptBuffer[3]; pBuffers[0].BufferType = BCryptKeyDerivationBufferType.KDF_ITERATION_COUNT; pBuffers[0].pvBuffer = (IntPtr)(&iterCount); pBuffers[0].cbBuffer = sizeof(ulong); pBuffers[1].BufferType = BCryptKeyDerivationBufferType.KDF_SALT; pBuffers[1].pvBuffer = (IntPtr)pbSalt; pBuffers[1].cbBuffer = cbSalt; fixed (char* pszHashAlgorithm = hashAlgorithm) { pBuffers[2].BufferType = BCryptKeyDerivationBufferType.KDF_HASH_ALGORITHM; pBuffers[2].pvBuffer = (IntPtr)pszHashAlgorithm; pBuffers[2].cbBuffer = GetTotalByteLengthIncludingNullTerminator(hashAlgorithm); // Add the header which points to the buffers BCryptBufferDesc bufferDesc = default(BCryptBufferDesc); BCryptBufferDesc.Initialize(ref bufferDesc); bufferDesc.cBuffers = 3; bufferDesc.pBuffers = pBuffers; // Finally, import the KDK into the KDF algorithm, then invoke the KDF uint numBytesDerived; int ntstatus = UnsafeNativeMethods.BCryptKeyDerivation( hKey: pbkdf2KeyHandle, pParameterList: &bufferDesc, pbDerivedKey: pbDerivedBytes, cbDerivedKey: cbDerivedBytes, pcbResult: out numBytesDerived, dwFlags: 0); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); // Final sanity checks before returning control to caller. CryptoUtil.Assert(numBytesDerived == cbDerivedBytes, "numBytesDerived == cbDerivedBytes"); } }
private uint GetCbcEncryptedOutputSizeWithPadding(BCryptKeyHandle symmetricKeyHandle, byte* pbInput, uint cbInput) { // ok for this memory to remain uninitialized since nobody depends on it byte* pbIV = stackalloc byte[checked((int)_symmetricAlgorithmBlockSizeInBytes)]; // Calling BCryptEncrypt with a null output pointer will cause it to return the total number // of bytes required for the output buffer. uint dwResult; int ntstatus = UnsafeNativeMethods.BCryptEncrypt( hKey: symmetricKeyHandle, pbInput: pbInput, cbInput: cbInput, pPaddingInfo: null, pbIV: pbIV, cbIV: _symmetricAlgorithmBlockSizeInBytes, pbOutput: null, cbOutput: 0, pcbResult: out dwResult, dwFlags: BCryptEncryptFlags.BCRYPT_BLOCK_PADDING); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); return dwResult; }
// 'pbIV' must be a pointer to a buffer equal in length to the symmetric algorithm block size. private byte[] DoCbcDecrypt(BCryptKeyHandle symmetricKeyHandle, byte* pbIV, byte* pbInput, uint cbInput) { // BCryptDecrypt mutates the provided IV; we need to clone it to prevent mutation of the original value byte* pbClonedIV = stackalloc byte[checked((int)_symmetricAlgorithmBlockSizeInBytes)]; UnsafeBufferUtil.BlockCopy(from: pbIV, to: pbClonedIV, byteCount: _symmetricAlgorithmBlockSizeInBytes); // First, figure out how large an output buffer we require. // Ideally we'd be able to transform the last block ourselves and strip // off the padding before creating the return value array, but we don't // know the actual padding scheme being used under the covers (we can't // assume PKCS#7). So unfortunately we're stuck with the temporary buffer. // (Querying the output size won't mutate the IV.) uint dwEstimatedDecryptedByteCount; int ntstatus = UnsafeNativeMethods.BCryptDecrypt( hKey: symmetricKeyHandle, pbInput: pbInput, cbInput: cbInput, pPaddingInfo: null, pbIV: pbClonedIV, cbIV: _symmetricAlgorithmBlockSizeInBytes, pbOutput: null, cbOutput: 0, pcbResult: out dwEstimatedDecryptedByteCount, dwFlags: BCryptEncryptFlags.BCRYPT_BLOCK_PADDING); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); byte[] decryptedPayload = new byte[dwEstimatedDecryptedByteCount]; uint dwActualDecryptedByteCount; fixed (byte* pbDecryptedPayload = decryptedPayload) { byte dummy; // Perform the actual decryption. ntstatus = UnsafeNativeMethods.BCryptDecrypt( hKey: symmetricKeyHandle, pbInput: pbInput, cbInput: cbInput, pPaddingInfo: null, pbIV: pbClonedIV, cbIV: _symmetricAlgorithmBlockSizeInBytes, pbOutput: (pbDecryptedPayload != null) ? pbDecryptedPayload : &dummy, // CLR won't pin zero-length arrays cbOutput: dwEstimatedDecryptedByteCount, pcbResult: out dwActualDecryptedByteCount, dwFlags: BCryptEncryptFlags.BCRYPT_BLOCK_PADDING); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); } // Decryption finished! CryptoUtil.Assert(dwActualDecryptedByteCount <= dwEstimatedDecryptedByteCount, "dwActualDecryptedByteCount <= dwEstimatedDecryptedByteCount"); if (dwActualDecryptedByteCount == dwEstimatedDecryptedByteCount) { // payload takes up the entire buffer return decryptedPayload; } else { // payload takes up only a partial buffer byte[] resizedDecryptedPayload = new byte[dwActualDecryptedByteCount]; Buffer.BlockCopy(decryptedPayload, 0, resizedDecryptedPayload, 0, resizedDecryptedPayload.Length); return resizedDecryptedPayload; } }
// 'pbIV' must be a pointer to a buffer equal in length to the symmetric algorithm block size. private void DoCbcEncrypt(BCryptKeyHandle symmetricKeyHandle, byte* pbIV, byte* pbInput, uint cbInput, byte* pbOutput, uint cbOutput) { // BCryptEncrypt mutates the provided IV; we need to clone it to prevent mutation of the original value byte* pbClonedIV = stackalloc byte[checked((int)_symmetricAlgorithmBlockSizeInBytes)]; UnsafeBufferUtil.BlockCopy(from: pbIV, to: pbClonedIV, byteCount: _symmetricAlgorithmBlockSizeInBytes); uint dwEncryptedBytes; int ntstatus = UnsafeNativeMethods.BCryptEncrypt( hKey: symmetricKeyHandle, pbInput: pbInput, cbInput: cbInput, pPaddingInfo: null, pbIV: pbClonedIV, cbIV: _symmetricAlgorithmBlockSizeInBytes, pbOutput: pbOutput, cbOutput: cbOutput, pcbResult: out dwEncryptedBytes, dwFlags: BCryptEncryptFlags.BCRYPT_BLOCK_PADDING); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); // Need to make sure we didn't underrun the buffer - means caller passed a bad value CryptoUtil.Assert(dwEncryptedBytes == cbOutput, "dwEncryptedBytes == cbOutput"); }
public Win8SP800_108_CTR_HMACSHA512Provider(byte* pbKdk, uint cbKdk) { _keyHandle = ImportKey(pbKdk, cbKdk); }