// Creates a provider from the given secret.
        public static ISP800_108_CTR_HMACSHA512Provider CreateProvider(ProtectedMemoryBlob kdk)
        {
            uint secretLengthInBytes = checked ((uint)kdk.Length);

            if (secretLengthInBytes == 0)
            {
                return(CreateEmptyProvider());
            }
            else
            {
                fixed(byte *pbPlaintextSecret = new byte[secretLengthInBytes])
                {
                    try
                    {
                        kdk.WriteSecretIntoBuffer(pbPlaintextSecret, checked ((int)secretLengthInBytes));
                        return(CreateProvider(pbPlaintextSecret, secretLengthInBytes));
                    }
                    finally
                    {
                        UnsafeBufferUtil.SecureZeroMemory(pbPlaintextSecret, secretLengthInBytes);
                    }
                }
            }
        }
        public byte[] Decrypt(ArraySegment <byte> protectedPayload, ArraySegment <byte> additionalAuthenticatedData)
        {
            protectedPayload.Validate();
            additionalAuthenticatedData.Validate();

            // Argument checking - input must at the absolute minimum contain a key modifier, IV, and MAC
            if (protectedPayload.Count < checked (KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + _validationAlgorithmDigestLengthInBytes))
            {
                throw Error.CryptCommon_PayloadInvalid();
            }

            // Assumption: protectedPayload := { keyModifier | IV | encryptedData | MAC(IV | encryptedPayload) }

            try
            {
                // Step 1: Extract the key modifier and IV from the payload.

                int keyModifierOffset; // position in protectedPayload.Array where key modifier begins
                int ivOffset;          // position in protectedPayload.Array where key modifier ends / IV begins
                int ciphertextOffset;  // position in protectedPayload.Array where IV ends / ciphertext begins
                int macOffset;         // position in protectedPayload.Array where ciphertext ends / MAC begins
                int eofOffset;         // position in protectedPayload.Array where MAC ends

                checked
                {
                    keyModifierOffset = protectedPayload.Offset;
                    ivOffset          = keyModifierOffset + KEY_MODIFIER_SIZE_IN_BYTES;
                    ciphertextOffset  = ivOffset + _symmetricAlgorithmBlockSizeInBytes;
                }

                ArraySegment <byte> keyModifier = new ArraySegment <byte>(protectedPayload.Array, keyModifierOffset, ivOffset - keyModifierOffset);
                byte[] iv = new byte[_symmetricAlgorithmBlockSizeInBytes];
                Buffer.BlockCopy(protectedPayload.Array, ivOffset, iv, 0, iv.Length);

                // Step 2: Decrypt the KDK and use it to restore the original encryption and MAC keys.
                // We pin all unencrypted keys to limit their exposure via GC relocation.

                byte[] decryptedKdk      = new byte[_keyDerivationKey.Length];
                byte[] decryptionSubkey  = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
                byte[] validationSubkey  = new byte[_validationAlgorithmSubkeyLengthInBytes];
                byte[] derivedKeysBuffer = new byte[checked (decryptionSubkey.Length + validationSubkey.Length)];

                fixed(byte *__unused__1 = decryptedKdk)
                fixed(byte *__unused__2 = decryptionSubkey)
                fixed(byte *__unused__3 = validationSubkey)
                fixed(byte *__unused__4 = derivedKeysBuffer)
                {
                    try
                    {
                        _keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment <byte>(decryptedKdk));
                        ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader(
                            kdk: decryptedKdk,
                            label: additionalAuthenticatedData,
                            contextHeader: _contextHeader,
                            context: keyModifier,
                            prfFactory: _kdkPrfFactory,
                            output: new ArraySegment <byte>(derivedKeysBuffer));

                        Buffer.BlockCopy(derivedKeysBuffer, 0, decryptionSubkey, 0, decryptionSubkey.Length);
                        Buffer.BlockCopy(derivedKeysBuffer, decryptionSubkey.Length, validationSubkey, 0, validationSubkey.Length);

                        // Step 3: Calculate the correct MAC for this payload.
                        // correctHash := MAC(IV || ciphertext)
                        byte[] correctHash;

                        using (var hashAlgorithm = CreateValidationAlgorithm(validationSubkey))
                        {
                            checked
                            {
                                eofOffset = protectedPayload.Offset + protectedPayload.Count;
                                macOffset = eofOffset - _validationAlgorithmDigestLengthInBytes;
                            }

                            correctHash = hashAlgorithm.ComputeHash(protectedPayload.Array, ivOffset, macOffset - ivOffset);
                        }

                        // Step 4: Validate the MAC provided as part of the payload.

                        if (!CryptoUtil.TimeConstantBuffersAreEqual(correctHash, 0, correctHash.Length, protectedPayload.Array, macOffset, eofOffset - macOffset))
                        {
                            throw Error.CryptCommon_PayloadInvalid(); // integrity check failure
                        }

                        // Step 5: Decipher the ciphertext and return it to the caller.

                        using (var symmetricAlgorithm = CreateSymmetricAlgorithm())
                            using (var cryptoTransform = symmetricAlgorithm.CreateDecryptor(decryptionSubkey, iv))
                            {
                                var outputStream = new MemoryStream();
                                using (var cryptoStream = new CryptoStream(outputStream, cryptoTransform, CryptoStreamMode.Write))
                                {
                                    cryptoStream.Write(protectedPayload.Array, ciphertextOffset, macOffset - ciphertextOffset);
                                    cryptoStream.FlushFinalBlock();

                                    // At this point, outputStream := { plaintext }, and we're done!
                                    return(outputStream.ToArray());
                                }
                            }
                    }
                    finally
                    {
                        // delete since these contain secret material
                        Array.Clear(decryptedKdk, 0, decryptedKdk.Length);
                        Array.Clear(decryptionSubkey, 0, decryptionSubkey.Length);
                        Array.Clear(validationSubkey, 0, validationSubkey.Length);
                        Array.Clear(derivedKeysBuffer, 0, derivedKeysBuffer.Length);
                    }
                }
            }
            catch (Exception ex) when(!(ex is CryptographicException))
            {
                // Homogenize all exceptions to CryptographicException.
                throw Error.CryptCommon_GenericError(ex);
            }
        }