// FIXME	[KeyContainerPermission (SecurityAction.Assert, KeyContainerName = "DAPI",
//			Flags = KeyContainerPermissionFlags.Open | KeyContainerPermissionFlags.Create)]
        public static byte[] Protect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope)
        {
            if (userData == null)
            {
                throw new ArgumentNullException("userData");
            }

            Rijndael aes = Rijndael.Create();

            aes.KeySize = 128;

            byte[] encdata = null;
            using (MemoryStream ms = new MemoryStream())
            {
                ICryptoTransform t = aes.CreateEncryptor();
                using (CryptoStream cs = new CryptoStream(ms, t, CryptoStreamMode.Write))
                {
                    cs.Write(userData, 0, userData.Length);
                    cs.Close();
                    encdata = ms.ToArray();
                }
            }

            byte[] key    = null;
            byte[] iv     = null;
            byte[] secret = null;
            byte[] header = null;
            SHA256 hash   = SHA256.Create();

            try
            {
                key    = aes.Key;
                iv     = aes.IV;
                secret = new byte[1 + 1 + 16 + 1 + 16 + 1 + 32];

                byte[] digest = hash.ComputeHash(userData);
                if ((optionalEntropy != null) && (optionalEntropy.Length > 0))
                {
                    // the same optionalEntropy will be required to get the data back
                    byte[] mask = hash.ComputeHash(optionalEntropy);
                    for (int i = 0; i < 16; i++)
                    {
                        key[i] ^= mask[i];
                        iv[i]  ^= mask[i + 16];
                    }
                    secret[0] = 2; // entropy
                }
                else
                {
                    secret[0] = 1; // without entropy
                }

                secret[1] = 16;  // key size
                Buffer.BlockCopy(key, 0, secret, 2, 16);
                secret[18] = 16; // iv size
                Buffer.BlockCopy(iv, 0, secret, 19, 16);
                secret[35] = 32; // digest size
                Buffer.BlockCopy(digest, 0, secret, 36, 32);

                RSAOAEPKeyExchangeFormatter formatter = new RSAOAEPKeyExchangeFormatter(GetKey(scope));
                header = formatter.CreateKeyExchange(secret);
            }
            finally
            {
                if (key != null)
                {
                    Array.Clear(key, 0, key.Length);
                    key = null;
                }
                if (secret != null)
                {
                    Array.Clear(secret, 0, secret.Length);
                    secret = null;
                }
                if (iv != null)
                {
                    Array.Clear(iv, 0, iv.Length);
                    iv = null;
                }
                aes.Clear();
                hash.Clear();
            }

            byte[] result = new byte[header.Length + encdata.Length];
            Buffer.BlockCopy(header, 0, result, 0, header.Length);
            Buffer.BlockCopy(encdata, 0, result, header.Length, encdata.Length);
            return(result);
        }
// FIXME	[KeyContainerPermission (SecurityAction.Assert, KeyContainerName = "DAPI",
//			Flags = KeyContainerPermissionFlags.Open | KeyContainerPermissionFlags.Decrypt)]
        public static byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope)
        {
            if (encryptedData == null)
            {
                throw new ArgumentNullException("encryptedData");
            }

            byte[] decdata = null;

            Rijndael aes        = Rijndael.Create();
            RSA      rsa        = GetKey(scope);
            int      headerSize = (rsa.KeySize >> 3);
            bool     valid1     = (encryptedData.Length >= headerSize);

            if (!valid1)
            {
                headerSize = encryptedData.Length;
            }

            byte[] header = new byte[headerSize];
            Buffer.BlockCopy(encryptedData, 0, header, 0, headerSize);

            byte[] secret = null;
            byte[] key    = null;
            byte[] iv     = null;
            bool   valid2 = false;
            bool   valid3 = false;
            bool   valid4 = false;
            SHA256 hash   = SHA256.Create();

            try
            {
                try
                {
                    RSAOAEPKeyExchangeDeformatter deformatter = new RSAOAEPKeyExchangeDeformatter(rsa);
                    secret = deformatter.DecryptKeyExchange(header);
                    valid2 = (secret.Length == 68);
                }
                catch
                {
                    valid2 = false;
                }

                if (!valid2)
                {
                    secret = new byte[68];
                }

                // known values for structure (version 1 or 2)
                valid3 = ((secret[1] == 16) && (secret[18] == 16) && (secret[35] == 32));

                key = new byte [16];
                Buffer.BlockCopy(secret, 2, key, 0, 16);
                iv = new byte [16];
                Buffer.BlockCopy(secret, 19, iv, 0, 16);

                if ((optionalEntropy != null) && (optionalEntropy.Length > 0))
                {
                    // the decrypted data won't be valid if the entropy isn't
                    // the same as the one used to protect (encrypt) it
                    byte[] mask = hash.ComputeHash(optionalEntropy);
                    for (int i = 0; i < 16; i++)
                    {
                        key[i] ^= mask[i];
                        iv[i]  ^= mask[i + 16];
                    }
                    valid3 &= (secret[0] == 2); // with entropy
                }
                else
                {
                    valid3 &= (secret[0] == 1); // without entropy
                }

                using (MemoryStream ms = new MemoryStream())
                {
                    ICryptoTransform t = aes.CreateDecryptor(key, iv);
                    using (CryptoStream cs = new CryptoStream(ms, t, CryptoStreamMode.Write))
                    {
                        try
                        {
                            cs.Write(encryptedData, headerSize, encryptedData.Length - headerSize);
                            cs.Close();
                        }
                        catch
                        {
                            // whatever, we keep going
                        }
                    }
                    decdata = ms.ToArray();
                }

                byte[] digest = hash.ComputeHash(decdata);
                valid4 = true;
                for (int i = 0; i < 32; i++)
                {
                    if (digest [i] != secret [36 + i])
                    {
                        valid4 = false;
                    }
                }
            }
            finally
            {
                if (key != null)
                {
                    Array.Clear(key, 0, key.Length);
                    key = null;
                }
                if (secret != null)
                {
                    Array.Clear(secret, 0, secret.Length);
                    secret = null;
                }
                if (iv != null)
                {
                    Array.Clear(iv, 0, iv.Length);
                    iv = null;
                }
                aes.Clear();
                hash.Clear();
            }

            // single point of error (also limits timing informations)
            if (!valid1 || !valid2 || !valid3 || !valid4)
            {
                if (decdata != null)
                {
                    Array.Clear(decdata, 0, decdata.Length);
                    decdata = null;
                }
                throw new CryptographicException(Locale.GetText("Invalid data."));
            }
            return(decdata);
        }