public GcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, IBCryptGenRandom genRandom = null) { // Is the key size appropriate? AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked(symmetricAlgorithmKeySizeInBytes * 8)); CryptoUtil.Assert(symmetricAlgorithmHandle.GetCipherBlockLength() == 128 / 8, "GCM requires a block cipher algorithm with a 128-bit block size."); _genRandom = genRandom ?? BCryptGenRandomImpl.Instance; _sp800_108_ctr_hmac_provider = SP800_108_CTR_HMACSHA512Util.CreateProvider(keyDerivationKey); _symmetricAlgorithmHandle = symmetricAlgorithmHandle; _symmetricAlgorithmSubkeyLengthInBytes = symmetricAlgorithmKeySizeInBytes; _contextHeader = CreateContextHeader(); }
public CbcAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, BCryptAlgorithmHandle hmacAlgorithmHandle, IBCryptGenRandom genRandom = null) { _genRandom = genRandom ?? BCryptGenRandomImpl.Instance; _sp800_108_ctr_hmac_provider = SP800_108_CTR_HMACSHA512Util.CreateProvider(keyDerivationKey); _symmetricAlgorithmHandle = symmetricAlgorithmHandle; _symmetricAlgorithmBlockSizeInBytes = symmetricAlgorithmHandle.GetCipherBlockLength(); _symmetricAlgorithmSubkeyLengthInBytes = symmetricAlgorithmKeySizeInBytes; _hmacAlgorithmHandle = hmacAlgorithmHandle; _hmacAlgorithmDigestLengthInBytes = hmacAlgorithmHandle.GetHashDigestLength(); _hmacAlgorithmSubkeyLengthInBytes = _hmacAlgorithmDigestLengthInBytes; // for simplicity we'll generate HMAC subkeys with a length equal to the digest length // Argument checking on the algorithms and lengths passed in to us AlgorithmAssert.IsAllowableSymmetricAlgorithmBlockSize(checked(_symmetricAlgorithmBlockSizeInBytes * 8)); AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked(_symmetricAlgorithmSubkeyLengthInBytes * 8)); AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked(_hmacAlgorithmDigestLengthInBytes * 8)); _contextHeader = CreateContextHeader(); }
private static BCryptKeyHandle PasswordToPbkdfKeyHandle(string password, BCryptAlgorithmHandle pbkdf2AlgHandle, KeyDerivationPrf prf) { byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers // Convert password string to bytes. // Allocate on the stack whenever we can to save allocations. int cbPasswordBuffer = Encoding.UTF8.GetMaxByteCount(password.Length); fixed (byte* pbHeapAllocatedPasswordBuffer = (cbPasswordBuffer > Constants.MAX_STACKALLOC_BYTES) ? new byte[cbPasswordBuffer] : null) { byte* pbPasswordBuffer = pbHeapAllocatedPasswordBuffer; if (pbPasswordBuffer == null) { if (cbPasswordBuffer == 0) { pbPasswordBuffer = &dummy; } else { byte* pbStackAllocPasswordBuffer = stackalloc byte[cbPasswordBuffer]; // will be released when the frame unwinds pbPasswordBuffer = pbStackAllocPasswordBuffer; } } try { int cbPasswordBufferUsed; // we're not filling the entire buffer, just a partial buffer fixed (char* pszPassword = password) { cbPasswordBufferUsed = Encoding.UTF8.GetBytes(pszPassword, password.Length, pbPasswordBuffer, cbPasswordBuffer); } return PasswordToPbkdfKeyHandleStep2(pbkdf2AlgHandle, pbPasswordBuffer, (uint)cbPasswordBufferUsed, prf); } finally { UnsafeBufferUtil.SecureZeroMemory(pbPasswordBuffer, cbPasswordBuffer); } } }
// We don't actually need to hold a reference to the algorithm handle, as the native CNG library // already holds the reference for us. But once we create a hash from an algorithm provider, odds // are good that we'll create another hash from the same algorithm provider at some point in the // future. And since algorithm providers are expensive to create, we'll hold a strong reference // to all known in-use providers. This way the cached algorithm provider handles utility class // doesn't keep creating providers over and over. internal void SetAlgorithmProviderHandle(BCryptAlgorithmHandle algProviderHandle) { _algProviderHandle = algProviderHandle; }
private static BCryptKeyHandle PasswordToPbkdfKeyHandleStep2(BCryptAlgorithmHandle pbkdf2AlgHandle, byte* pbPassword, uint cbPassword, KeyDerivationPrf prf) { const uint PBKDF2_MAX_KEYLENGTH_IN_BYTES = 2048; // GetSupportedKeyLengths() on a Win8 box; value should never be lowered in any future version of Windows if (cbPassword <= PBKDF2_MAX_KEYLENGTH_IN_BYTES) { // Common case: the password is small enough to be consumed directly by the PBKDF2 algorithm. return pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword); } else { // Rare case: password is very long; we must hash manually. // PBKDF2 uses the PRFs in HMAC mode, and when the HMAC input key exceeds the hash function's // block length the key is hashed and run back through the key initialization function. BCryptAlgorithmHandle prfAlgorithmHandle; // cached; don't dispose switch (prf) { case KeyDerivationPrf.HMACSHA1: prfAlgorithmHandle = CachedAlgorithmHandles.SHA1; break; case KeyDerivationPrf.HMACSHA256: prfAlgorithmHandle = CachedAlgorithmHandles.SHA256; break; case KeyDerivationPrf.HMACSHA512: prfAlgorithmHandle = CachedAlgorithmHandles.SHA512; break; default: throw CryptoUtil.Fail("Unrecognized PRF."); } // Final sanity check: don't hash the password if the HMAC key initialization function wouldn't have done it for us. if (cbPassword <= prfAlgorithmHandle.GetHashBlockLength() /* in bytes */) { return pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword); } // Hash the password and use the hash as input to PBKDF2. uint cbPasswordDigest = prfAlgorithmHandle.GetHashDigestLength(); CryptoUtil.Assert(cbPasswordDigest > 0, "cbPasswordDigest > 0"); fixed (byte* pbPasswordDigest = new byte[cbPasswordDigest]) { try { using (var hashHandle = prfAlgorithmHandle.CreateHash()) { hashHandle.HashData(pbPassword, cbPassword, pbPasswordDigest, cbPasswordDigest); } return pbkdf2AlgHandle.GenerateSymmetricKey(pbPasswordDigest, cbPasswordDigest); } finally { UnsafeBufferUtil.SecureZeroMemory(pbPasswordDigest, cbPasswordDigest); } } } }
// We don't actually need to hold a reference to the algorithm handle, as the native CNG library // already holds the reference for us. But once we create a key from an algorithm provider, odds // are good that we'll create another key from the same algorithm provider at some point in the // future. And since algorithm providers are expensive to create, we'll hold a strong reference // to all known in-use providers. This way the cached algorithm provider handles utility class // doesn't keep creating providers over and over. internal void SetAlgorithmProviderHandle(BCryptAlgorithmHandle algProviderHandle) { _algProviderHandle = algProviderHandle; }
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you. protected override bool ReleaseHandle() { _algProviderHandle = null; return (UnsafeNativeMethods.BCryptDestroyKey(handle) == 0); }
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you. protected override bool ReleaseHandle() { _algProviderHandle = null; return(UnsafeNativeMethods.BCryptDestroyKey(handle) == 0); }