// '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; var 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"); }
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh448506(v=vs.85).aspx internal static extern int BCryptKeyDerivation( [In] BCryptKeyHandle hKey, [In] BCryptBufferDesc *pParameterList, [In] byte *pbDerivedKey, [In] uint cbDerivedKey, [Out] out uint pcbResult, [In] uint dwFlags);
public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested) { Debug.Assert(password != null); Debug.Assert(salt != null); Debug.Assert(iterationCount > 0); Debug.Assert(numBytesRequested > 0); string algorithmName = PrfToCngAlgorithmId(prf); fixed(byte *pbHeapAllocatedSalt = salt) { byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers byte *pbSalt = (pbHeapAllocatedSalt != null) ? pbHeapAllocatedSalt : &dummy; byte[] retVal = new byte[numBytesRequested]; using (BCryptKeyHandle keyHandle = PasswordToPbkdfKeyHandle(password, CachedAlgorithmHandles.PBKDF2, prf)) { fixed(byte *pbRetVal = retVal) { Pbkdf2Win8ImplStep2(keyHandle, algorithmName, pbSalt, (uint)salt.Length, (ulong)iterationCount, pbRetVal, (uint)retVal.Length); } return(retVal); } } }
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375453(v=vs.85).aspx internal static extern int BCryptGenerateSymmetricKey( [In] BCryptAlgorithmHandle hAlgorithm, [Out] out BCryptKeyHandle phKey, [In] IntPtr pbKeyObject, [In] uint cbKeyObject, [In] byte *pbSecret, [In] uint cbSecret, [In] uint dwFlags);
internal static extern int BCryptKeyDerivation( #endif BCryptKeyHandle hKey, BCryptBufferDesc *pParameterList, byte *pbDerivedKey, uint cbDerivedKey, out uint pcbResult, uint dwFlags);
internal static extern int BCryptGenerateSymmetricKey( #endif BCryptAlgorithmHandle hAlgorithm, out BCryptKeyHandle phKey, IntPtr pbKeyObject, uint cbKeyObject, byte *pbSecret, uint cbSecret, uint dwFlags);
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375391(v=vs.85).aspx internal static extern int BCryptDecrypt( [In] BCryptKeyHandle hKey, [In] byte *pbInput, [In] uint cbInput, [In] void *pPaddingInfo, [In] byte *pbIV, [In] uint cbIV, [In] byte *pbOutput, [In] uint cbOutput, [Out] out uint pcbResult, [In] BCryptEncryptFlags dwFlags);
internal static extern int BCryptDecrypt( #endif BCryptKeyHandle hKey, byte *pbInput, uint cbInput, void *pPaddingInfo, byte *pbIV, uint cbIV, byte *pbOutput, uint cbOutput, out uint pcbResult, BCryptEncryptFlags dwFlags);
private static void Pbkdf2Win8ImplStep2(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 = hashAlgorithm.GetTotalByteLengthIncludingNullTerminator(); // 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; var 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; var 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); var 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 var resizedDecryptedPayload = new byte[dwActualDecryptedByteCount]; Buffer.BlockCopy(decryptedPayload, 0, resizedDecryptedPayload, 0, resizedDecryptedPayload.Length); return(resizedDecryptedPayload); } }
public Win8SP800_108_CTR_HMACSHA512Provider(byte *pbKdk, uint cbKdk) { _keyHandle = ImportKey(pbKdk, cbKdk); }