public override bool TryFinalizeHashAndReset(Span <byte> destination, out int bytesWritten)
        {
            if (destination.Length < _hashSize)
            {
                bytesWritten = 0;
                return(false);
            }

            NTSTATUS ntStatus = Interop.BCrypt.BCryptFinishHash(_hHash, destination, _hashSize, 0);

            if (ntStatus != NTSTATUS.STATUS_SUCCESS)
            {
                throw Interop.BCrypt.CreateCryptographicException(ntStatus);
            }

            bytesWritten = _hashSize;
            ResetHashObject();
            return(true);
        }
        private static byte[] ExportKeyBlob(SafeBCryptKeyHandle bCryptKeyHandle, CngKeyBlobFormat blobFormat)
        {
            string blobFormatString = blobFormat.Format;

            int      numBytesNeeded = 0;
            NTSTATUS ntStatus       = Interop.BCrypt.BCryptExportKey(bCryptKeyHandle, IntPtr.Zero, blobFormatString, null, 0, out numBytesNeeded, 0);

            if (ntStatus != NTSTATUS.STATUS_SUCCESS)
            {
                throw new CryptographicException(Interop.Kernel32.GetMessage((int)ntStatus));
            }

            byte[] keyBlob = new byte[numBytesNeeded];
            ntStatus = Interop.BCrypt.BCryptExportKey(bCryptKeyHandle, IntPtr.Zero, blobFormatString, keyBlob, keyBlob.Length, out numBytesNeeded, 0);
            if (ntStatus != NTSTATUS.STATUS_SUCCESS)
            {
                throw new CryptographicException(Interop.Kernel32.GetMessage((int)ntStatus));
            }

            Array.Resize(ref keyBlob, numBytesNeeded);
            return(keyBlob);
        }
Exemple #3
0
        public override void Reset()
        {
            if (_reusable && !_running)
            {
                return;
            }

            DestroyHash();

            BCryptCreateHashFlags flags = _reusable ?
                                          BCryptCreateHashFlags.BCRYPT_HASH_REUSABLE_FLAG :
                                          BCryptCreateHashFlags.None;

            SafeBCryptHashHandle hHash;
            NTSTATUS             ntStatus = Interop.BCrypt.BCryptCreateHash(_hAlgorithm, out hHash, IntPtr.Zero, 0, _key, _key == null ? 0 : _key.Length, flags);

            if (ntStatus != NTSTATUS.STATUS_SUCCESS)
            {
                throw Interop.BCrypt.CreateCryptographicException(ntStatus);
            }

            _hHash = hHash;
        }
Exemple #4
0
        private static byte[] CreateCryptoRandomByteArray(int byteLength)
        {
            // We need to fill a byte array with cryptographically-strong random bytes, but we can't reference
            // System.Security.Cryptography.RandomNumberGenerator.dll due to layering.  Instead, we just
            // call to BCryptGenRandom directly, which is all that RandomNumberGenerator does.

            var arr = new byte[byteLength];

            Interop.BCrypt.NTSTATUS status = Interop.BCrypt.BCryptGenRandom(arr, arr.Length);
            if (status == Interop.BCrypt.NTSTATUS.STATUS_SUCCESS)
            {
                return(arr);
            }
            else if (status == Interop.BCrypt.NTSTATUS.STATUS_NO_MEMORY)
            {
                throw new OutOfMemoryException();
            }
            else
            {
                Debug.Fail("BCryptGenRandom should only fail due to OOM or invalid args / handle inputs.");
                throw new InvalidOperationException();
            }
        }
Exemple #5
0
        internal HashProviderCng(string hashAlgId, ReadOnlySpan <byte> key, bool isHmac)
        {
            BCryptOpenAlgorithmProviderFlags dwFlags = BCryptOpenAlgorithmProviderFlags.None;

            if (isHmac)
            {
                _key     = key.ToArray();
                dwFlags |= BCryptOpenAlgorithmProviderFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG;
            }

            _hAlgorithm = Interop.BCrypt.BCryptAlgorithmCache.GetCachedBCryptAlgorithmHandle(hashAlgId, dwFlags, out _hashSize);

            // Win7 won't set hHash to a valid handle, Win8+ will; and both will set _hHash.
            // So keep hHash trapped in this scope to prevent (mis-)use of it.
            {
                SafeBCryptHashHandle hHash;
                NTSTATUS             ntStatus = Interop.BCrypt.BCryptCreateHash(_hAlgorithm, out hHash, IntPtr.Zero, 0, key, key == null ? 0 : key.Length, BCryptCreateHashFlags.BCRYPT_HASH_REUSABLE_FLAG);
                if (ntStatus == NTSTATUS.STATUS_INVALID_PARAMETER)
                {
                    hHash.Dispose();
                    // If we got here, we're running on a downlevel OS (pre-Win8) that doesn't support reusable CNG hash objects. Fall back to creating a
                    // new HASH object each time.
                    Reset();
                }
                else if (ntStatus != NTSTATUS.STATUS_SUCCESS)
                {
                    hHash.Dispose();
                    throw Interop.BCrypt.CreateCryptographicException(ntStatus);
                }
                else
                {
                    _hHash    = hHash;
                    _reusable = true;
                }
            }
        }
Exemple #6
0
        private static unsafe void GetCryptoRandomBytes(byte *bytes, int byteCount)
        {
            // We need to fill a byte array with cryptographically-strong random bytes, but we can't reference
            // System.Security.Cryptography.RandomNumberGenerator.dll due to layering.  Instead, we just
            // call to BCryptGenRandom directly, which is all that RandomNumberGenerator does.

            Debug.Assert(bytes != null);
            Debug.Assert(byteCount >= 0);

            Interop.BCrypt.NTSTATUS status = Interop.BCrypt.BCryptGenRandom(bytes, byteCount);
            if (status == Interop.BCrypt.NTSTATUS.STATUS_SUCCESS)
            {
                return;
            }
            else if (status == Interop.BCrypt.NTSTATUS.STATUS_NO_MEMORY)
            {
                throw new OutOfMemoryException();
            }
            else
            {
                Debug.Fail("BCryptGenRandom should only fail due to OOM or invalid args / handle inputs.");
                throw new InvalidOperationException();
            }
        }
Exemple #7
0
        protected sealed override bool ReleaseHandle()
        {
            NTSTATUS ntStatus = Interop.BCrypt.BCryptDestroySecret(handle);

            return(ntStatus == NTSTATUS.STATUS_SUCCESS);
        }
        protected sealed override bool ReleaseHandle()
        {
            NTSTATUS ntStatus = Interop.BCrypt.BCryptCloseAlgorithmProvider(handle, 0);

            return(ntStatus == NTSTATUS.STATUS_SUCCESS);
        }
Exemple #9
0
        private static unsafe void FillKeyDerivation(
            ReadOnlySpan <byte> password,
            ReadOnlySpan <byte> salt,
            int iterations,
            string hashAlgorithmName,
            Span <byte> destination)
        {
            SafeBCryptKeyHandle keyHandle;
            int hashBlockSizeBytes = GetHashBlockSize(hashAlgorithmName);

            // stackalloc 0 to let compiler know this cannot escape.
            scoped Span <byte>         clearSpan;
            scoped ReadOnlySpan <byte> symmetricKeyMaterial;
            int symmetricKeyMaterialLength;

            if (password.IsEmpty)
            {
                // CNG won't accept a null pointer for the password.
                symmetricKeyMaterial       = stackalloc byte[1];
                symmetricKeyMaterialLength = 0;
                clearSpan = default;
            }
            else if (password.Length <= hashBlockSizeBytes)
            {
                // Password is small enough to use as-is.
                symmetricKeyMaterial       = password;
                symmetricKeyMaterialLength = password.Length;
                clearSpan = default;
            }
            else
            {
                // RFC 2104: "The key for HMAC can be of any length (keys longer than B bytes are
                //     first hashed using H).
                //     We denote by B the byte-length of such
                //     blocks (B=64 for all the above mentioned examples of hash functions)
                //
                // Windows' PBKDF2 will do this up to a point. To ensure we accept arbitrary inputs for
                // PBKDF2, we do the hashing ourselves.
                Span <byte> hashBuffer = stackalloc byte[512 / 8]; // 64 bytes is SHA512, the largest digest handled.
                int         hashBufferSize;

                switch (hashAlgorithmName)
                {
                case HashAlgorithmNames.SHA1:
                    hashBufferSize = SHA1.HashData(password, hashBuffer);
                    break;

                case HashAlgorithmNames.SHA256:
                    hashBufferSize = SHA256.HashData(password, hashBuffer);
                    break;

                case HashAlgorithmNames.SHA384:
                    hashBufferSize = SHA384.HashData(password, hashBuffer);
                    break;

                case HashAlgorithmNames.SHA512:
                    hashBufferSize = SHA512.HashData(password, hashBuffer);
                    break;

                default:
                    Debug.Fail($"Unexpected hash algorithm '{hashAlgorithmName}'");
                    throw new CryptographicException();
                }

                clearSpan                  = hashBuffer.Slice(0, hashBufferSize);
                symmetricKeyMaterial       = clearSpan;
                symmetricKeyMaterialLength = hashBufferSize;
            }

            Debug.Assert(symmetricKeyMaterial.Length > 0);

            NTSTATUS generateKeyStatus;

            if (Interop.BCrypt.PseudoHandlesSupported)
            {
                fixed(byte *pSymmetricKeyMaterial = symmetricKeyMaterial)
                {
                    generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
                        (nuint)BCryptAlgPseudoHandle.BCRYPT_PBKDF2_ALG_HANDLE,
                        out keyHandle,
                        pbKeyObject: IntPtr.Zero,
                        cbKeyObject: 0,
                        pSymmetricKeyMaterial,
                        symmetricKeyMaterialLength,
                        dwFlags: 0);
                }
            }
            else
            {
                if (s_pbkdf2AlgorithmHandle is null)
                {
                    NTSTATUS openStatus = Interop.BCrypt.BCryptOpenAlgorithmProvider(
                        out SafeBCryptAlgorithmHandle pbkdf2AlgorithmHandle,
                        Internal.NativeCrypto.BCryptNative.AlgorithmName.Pbkdf2,
                        null,
                        BCryptOpenAlgorithmProviderFlags.None);

                    if (openStatus != NTSTATUS.STATUS_SUCCESS)
                    {
                        pbkdf2AlgorithmHandle.Dispose();
                        CryptographicOperations.ZeroMemory(clearSpan);
                        throw Interop.BCrypt.CreateCryptographicException(openStatus);
                    }

                    // This might race, and that's okay. Worst case the algorithm is opened
                    // more than once, and the ones that lost will get cleaned up during collection.
                    Interlocked.CompareExchange(ref s_pbkdf2AlgorithmHandle, pbkdf2AlgorithmHandle, null);
                }

                fixed(byte *pSymmetricKeyMaterial = symmetricKeyMaterial)
                {
                    generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
                        s_pbkdf2AlgorithmHandle,
                        out keyHandle,
                        pbKeyObject: IntPtr.Zero,
                        cbKeyObject: 0,
                        pSymmetricKeyMaterial,
                        symmetricKeyMaterialLength,
                        dwFlags: 0);
                }
            }

            CryptographicOperations.ZeroMemory(clearSpan);

            if (generateKeyStatus != NTSTATUS.STATUS_SUCCESS)
            {
                keyHandle.Dispose();
                throw Interop.BCrypt.CreateCryptographicException(generateKeyStatus);
            }

            Debug.Assert(!keyHandle.IsInvalid);

            ulong kdfIterations = (ulong)iterations; // Previously asserted to be positive.

            using (keyHandle)
                fixed(char *pHashAlgorithmName = hashAlgorithmName)
                fixed(byte *pSalt        = salt)
                fixed(byte *pDestination = destination)
                {
                    Span <BCryptBuffer> buffers = stackalloc BCryptBuffer[3];

                    buffers[0].BufferType = CngBufferDescriptors.KDF_ITERATION_COUNT;
                    buffers[0].pvBuffer   = (IntPtr)(&kdfIterations);
                    buffers[0].cbBuffer   = sizeof(ulong);

                    buffers[1].BufferType = CngBufferDescriptors.KDF_SALT;
                    buffers[1].pvBuffer   = (IntPtr)pSalt;
                    buffers[1].cbBuffer   = salt.Length;

                    buffers[2].BufferType = CngBufferDescriptors.KDF_HASH_ALGORITHM;
                    buffers[2].pvBuffer   = (IntPtr)pHashAlgorithmName;

                    // C# spec: "A char* value produced by fixing a string instance always points to a null-terminated string"
                    buffers[2].cbBuffer = checked ((hashAlgorithmName.Length + 1) * sizeof(char)); // Add null terminator.

                    fixed(BCryptBuffer *pBuffers = buffers)
                    {
                        Interop.BCrypt.BCryptBufferDesc bufferDesc;
                        bufferDesc.ulVersion = Interop.BCrypt.BCRYPTBUFFER_VERSION;
                        bufferDesc.cBuffers  = buffers.Length;
                        bufferDesc.pBuffers  = (IntPtr)pBuffers;

                        NTSTATUS deriveStatus = Interop.BCrypt.BCryptKeyDerivation(
                            keyHandle,
                            &bufferDesc,
                            pDestination,
                            destination.Length,
                            out uint resultLength,
                            dwFlags: 0);

                        if (deriveStatus != NTSTATUS.STATUS_SUCCESS)
                        {
                            throw Interop.BCrypt.CreateCryptographicException(deriveStatus);
                        }

                        if (destination.Length != resultLength)
                        {
                            Debug.Fail("PBKDF2 resultLength != destination.Length");
                            throw new CryptographicException();
                        }
                    }
                }
        }