private byte[] UnprotectCore(byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
        {
            Debug.Assert(protectedData != null);

            try
            {
                // argument & state checking
                if (protectedData.Length < sizeof(uint) /* magic header */ + sizeof(Guid) /* key id */)
                {
                    // payload must contain at least the magic header and key id
                    throw Error.ProtectionProvider_BadMagicHeader();
                }

                // Need to check that protectedData := { magicHeader || keyId || encryptorSpecificProtectedPayload }

                // Parse the payload version number and key id.
                uint magicHeaderFromPayload;
                Guid keyIdFromPayload;
                fixed(byte *pbInput = protectedData)
                {
                    magicHeaderFromPayload = ReadBigEndian32BitInteger(pbInput);
                    keyIdFromPayload       = Read32bitAlignedGuid(&pbInput[sizeof(uint)]);
                }

                // Are the magic header and version information correct?
                int payloadVersion;
                if (!TryGetVersionFromMagicHeader(magicHeaderFromPayload, out payloadVersion))
                {
                    throw Error.ProtectionProvider_BadMagicHeader();
                }
                else if (payloadVersion != 0)
                {
                    throw Error.ProtectionProvider_BadVersion();
                }

                if (_logger.IsDebugLevelEnabled())
                {
                    _logger.PerformingUnprotectOperationToKeyWithPurposes(keyIdFromPayload, JoinPurposesForLog(Purposes));
                }

                // Find the correct encryptor in the keyring.
                bool keyWasRevoked;
                var  currentKeyRing     = _keyRingProvider.GetCurrentKeyRing();
                var  requestedEncryptor = currentKeyRing.GetAuthenticatedEncryptorByKeyId(keyIdFromPayload, out keyWasRevoked);
                if (requestedEncryptor == null)
                {
                    _logger?.KeyWasNotFoundInTheKeyRingUnprotectOperationCannotProceed(keyIdFromPayload);
                    throw Error.Common_KeyNotFound(keyIdFromPayload);
                }

                // Do we need to notify the caller that he should reprotect the data?
                status = UnprotectStatus.Ok;
                if (keyIdFromPayload != currentKeyRing.DefaultKeyId)
                {
                    status = UnprotectStatus.DefaultEncryptionKeyChanged;
                }

                // Do we need to notify the caller that this key was revoked?
                if (keyWasRevoked)
                {
                    if (allowOperationsOnRevokedKeys)
                    {
                        _logger?.KeyWasRevokedCallerRequestedUnprotectOperationProceedRegardless(keyIdFromPayload);
                        status = UnprotectStatus.DecryptionKeyWasRevoked;
                    }
                    else
                    {
                        _logger?.KeyWasRevokedUnprotectOperationCannotProceed(keyIdFromPayload);
                        throw Error.Common_KeyRevoked(keyIdFromPayload);
                    }
                }

                // Perform the decryption operation.
                ArraySegment <byte> ciphertext = new ArraySegment <byte>(protectedData, sizeof(uint) + sizeof(Guid), protectedData.Length - (sizeof(uint) + sizeof(Guid))); // chop off magic header + encryptor id
                ArraySegment <byte> additionalAuthenticatedData = new ArraySegment <byte>(_aadTemplate.GetAadForKey(keyIdFromPayload, isProtecting: false));

                // At this point, cipherText := { encryptorSpecificPayload },
                // so all that's left is to invoke the decryption routine directly.
                return(requestedEncryptor.Decrypt(ciphertext, additionalAuthenticatedData)
                       ?? CryptoUtil.Fail <byte[]>("IAuthenticatedEncryptor.Decrypt returned null."));
            }
            catch (Exception ex) when(ex.RequiresHomogenization())
            {
                // homogenize all failures to CryptographicException
                throw Error.DecryptionFailed(ex);
            }
        }
        private byte[] UnprotectCore(byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
        {
            Debug.Assert(protectedData != null);

            try
            {
                // argument & state checking
                if (protectedData.Length < sizeof(uint) /* magic header */ + sizeof(Guid) /* key id */)
                {
                    // payload must contain at least the magic header and key id
                    throw Error.ProtectionProvider_BadMagicHeader();
                }

                // Need to check that protectedData := { magicHeader || keyId || encryptorSpecificProtectedPayload }

                // Parse the payload version number and key id.
                uint magicHeaderFromPayload;
                Guid keyIdFromPayload;
                fixed (byte* pbInput = protectedData)
                {
                    magicHeaderFromPayload = ReadBigEndian32BitInteger(pbInput);
                    keyIdFromPayload = Read32bitAlignedGuid(&pbInput[sizeof(uint)]);
                }

                // Are the magic header and version information correct?
                int payloadVersion;
                if (!TryGetVersionFromMagicHeader(magicHeaderFromPayload, out payloadVersion))
                {
                    throw Error.ProtectionProvider_BadMagicHeader();
                }
                else if (payloadVersion != 0)
                {
                    throw Error.ProtectionProvider_BadVersion();
                }

                if (_logger.IsDebugLevelEnabled())
                {
                    _logger.LogDebugF($"Performing unprotect operation to key {keyIdFromPayload:B} with purposes {JoinPurposesForLog(Purposes)}.");
                }

                // Find the correct encryptor in the keyring.
                bool keyWasRevoked;
                var currentKeyRing = _keyRingProvider.GetCurrentKeyRing();
                var requestedEncryptor = currentKeyRing.GetAuthenticatedEncryptorByKeyId(keyIdFromPayload, out keyWasRevoked);
                if (requestedEncryptor == null)
                {
                    if (_logger.IsDebugLevelEnabled())
                    {
                        _logger.LogDebugF($"Key {keyIdFromPayload:B} was not found in the key ring. Unprotect operation cannot proceed.");
                    }
                    throw Error.Common_KeyNotFound(keyIdFromPayload);
                }

                // Do we need to notify the caller that he should reprotect the data?
                status = UnprotectStatus.Ok;
                if (keyIdFromPayload != currentKeyRing.DefaultKeyId)
                {
                    status = UnprotectStatus.DefaultEncryptionKeyChanged;
                }

                // Do we need to notify the caller that this key was revoked?
                if (keyWasRevoked)
                {
                    if (allowOperationsOnRevokedKeys)
                    {
                        if (_logger.IsVerboseLevelEnabled())
                        {
                            _logger.LogVerboseF($"Key {keyIdFromPayload:B} was revoked. Caller requested unprotect operation proceed regardless.");
                        }
                        status = UnprotectStatus.DecryptionKeyWasRevoked;
                    }
                    else
                    {
                        if (_logger.IsVerboseLevelEnabled())
                        {
                            _logger.LogVerboseF($"Key {keyIdFromPayload:B} was revoked. Unprotect operation cannot proceed.");
                        }
                        throw Error.Common_KeyRevoked(keyIdFromPayload);
                    }
                }

                // Perform the decryption operation.
                ArraySegment<byte> ciphertext = new ArraySegment<byte>(protectedData, sizeof(uint) + sizeof(Guid), protectedData.Length - (sizeof(uint) + sizeof(Guid))); // chop off magic header + encryptor id
                ArraySegment<byte> additionalAuthenticatedData = new ArraySegment<byte>(_aadTemplate.GetAadForKey(keyIdFromPayload, isProtecting: false));

                // At this point, cipherText := { encryptorSpecificPayload },
                // so all that's left is to invoke the decryption routine directly.
                return requestedEncryptor.Decrypt(ciphertext, additionalAuthenticatedData)
                    ?? CryptoUtil.Fail<byte[]>("IAuthenticatedEncryptor.Decrypt returned null.");
            }
            catch (Exception ex) when (ex.RequiresHomogenization())
            {
                // homogenize all failures to CryptographicException
                throw Error.DecryptionFailed(ex);
            }
        }