/// <summary>
        /// Decrypt a packet encrypted with CCMP
        /// </summary>
        /// <param name="encryptedPacket">The encrypted packet</param>
        /// <param name="temporalKey">The temporal key</param>
        /// <param name="headerMode">Mode in which headers should be handled</param>
        /// <param name="decrypted">Decrypted packet</param>
        /// <returns>True if successful, false if not</returns>
        /// <remarks>Ported from airdecap-ng</remarks>
        public static bool TryDecryptCCMP(
            IEEE802_11 encryptedPacket,
            byte[] temporalKey,
            DecryptionHeaderMode headerMode,
            out IPacket decrypted)
        {
            if (temporalKey.Length != 16)
            {
                // throw new ArgumentException("temporalKey must be 16 bytes");
                decrypted = null;
                return(false);
            }

            var  decryptedBytes = encryptedPacket.ToArray();
            int  z, data_len, blocks, last, offset;
            bool is_a4, is_qos;

            var B0 = new byte[16];
            var B = new byte[16];
            var MIC                = new byte[16];
            var PacketNumber       = new byte[6];
            var AdditionalAuthData = new byte[32];

            is_a4  = (decryptedBytes[1] & 3) == 3;
            is_qos = (decryptedBytes[0] & 0x8C) == 0x88;
            z      = 24 + 6 * (is_a4 ? 1 : 0);
            z     += 2 * (is_qos ? 1 : 0);

            PacketNumber[0] = decryptedBytes[z + 7];
            PacketNumber[1] = decryptedBytes[z + 6];
            PacketNumber[2] = decryptedBytes[z + 5];
            PacketNumber[3] = decryptedBytes[z + 4];
            PacketNumber[4] = decryptedBytes[z + 1];
            PacketNumber[5] = decryptedBytes[z + 0];

            data_len = decryptedBytes.Length - z - 8 - 8;

            B0[0] = 0x59;
            B0[1] = 0;
            Array.Copy(decryptedBytes, 10, B0, 2, 6);
            Array.Copy(PacketNumber, 0, B0, 8, 6);
            B0[14] = (byte)((data_len >> 8) & 0xFF);
            B0[15] = (byte)(data_len & 0xFF);

            AdditionalAuthData[2] = (byte)(decryptedBytes[0] & 0x8F);
            AdditionalAuthData[3] = (byte)(decryptedBytes[1] & 0xC7);
            Array.Copy(decryptedBytes, 4, AdditionalAuthData, 4, 3 * 6);
            AdditionalAuthData[22] = (byte)(decryptedBytes[22] & 0x0F);

            if (is_a4)
            {
                Array.Copy(decryptedBytes, 24, AdditionalAuthData, 24, 6);

                if (is_qos)
                {
                    AdditionalAuthData[30] = (byte)(decryptedBytes[z - 2] & 0x0F);
                    AdditionalAuthData[31] = 0;
                    B0[1] = AdditionalAuthData[30];
                    AdditionalAuthData[1] = 22 + 2 + 6;
                }
                else
                {
                    AdditionalAuthData[30] = 0;
                    AdditionalAuthData[31] = 0;

                    B0[1] = 0;
                    AdditionalAuthData[1] = 22 + 6;
                }
            }
            else
            {
                if (is_qos)
                {
                    AdditionalAuthData[24] = (byte)(decryptedBytes[z - 2] & 0x0F);
                    AdditionalAuthData[25] = 0;
                    B0[1] = AdditionalAuthData[24];
                    AdditionalAuthData[1] = 22 + 2;
                }
                else
                {
                    AdditionalAuthData[24] = 0;
                    AdditionalAuthData[25] = 0;

                    B0[1] = 0;
                    AdditionalAuthData[1] = 22;
                }
            }

            using (var aesFactory = Aes.Create())
            {
                aesFactory.Mode = CipherMode.ECB;
                aesFactory.Key  = temporalKey;
                var aes = aesFactory.CreateEncryptor();

                aes.TransformBlock(B0, 0, B0.Length, MIC, 0);
                CFunctions.XOR(MIC, 0, AdditionalAuthData, 16);
                aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0);
                CFunctions.XOR(MIC, 0, AdditionalAuthData.Skip(16).ToArray(), 16);
                aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0);

                B0[0] &= 0x07;
                B0[14] = 0;
                B0[15] = 0;
                aes.TransformBlock(B0, 0, B0.Length, B, 0);
                CFunctions.XOR(decryptedBytes, decryptedBytes.Length - 8, B, 8);

                blocks = (data_len + 16 - 1) / 16;
                last   = data_len % 16;
                offset = z + 8;

                for (var i = 1; i <= blocks; i++)
                {
                    var n = last > 0 && i == blocks ? last : 16;

                    B0[14] = (byte)((i >> 8) & 0xFF);
                    B0[15] = (byte)(i & 0xFF);

                    aes.TransformBlock(B0, 0, B0.Length, B, 0);
                    CFunctions.XOR(decryptedBytes, offset, B, n);
                    CFunctions.XOR(MIC, 0, decryptedBytes.Skip(offset).ToArray(), n);
                    aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0);

                    offset += n;
                }
            }

            switch (headerMode)
            {
            case DecryptionHeaderMode.Removed:
            {
                decryptedBytes[1] &= 191;         // Remove the protected bit
                var decryptedBytesList = decryptedBytes.ToList();
                decryptedBytesList.RemoveRange(34 - 8, 8);
                // Remove CCMP Parameters (otherwise, without the protected bit it will break the parsing)
                IEEE802_11 decryptedWithHeader;
                if (IEEE802_11Factory.Instance.TryParse(decryptedBytesList.ToArray(), out decryptedWithHeader))
                {
                    decrypted = decryptedWithHeader.Payload;         // Remove the altered 802.11 header
                }
                else
                {
                    decrypted = null;
                    return(false);
                }

                break;
            }

            case DecryptionHeaderMode.Modified:
            {
                decryptedBytes[1] &= 191;         // Remove the protected bit
                var decryptedBytesList = decryptedBytes.ToList();
                decryptedBytesList.RemoveRange(34 - 8, 8);
                // Remove CCMP Parameters (otherwise, without the protected bit it will break the parsing)
                if (!IEEE802_11Factory.Instance.TryParse(decryptedBytesList.ToArray(), out decrypted))
                {
                    return(false);
                }
                break;
            }

            case DecryptionHeaderMode.Untouched:
                if (!IEEE802_11Factory.Instance.TryParse(decryptedBytes.ToArray(), out decrypted))
                {
                    return(false);
                }
                break;

            default:
                decrypted = null;
                return(false);
            }

            return(CFunctions.memcmp(decryptedBytes.Skip(offset).Take(8).ToArray(), MIC.Take(8).ToArray()) == 0);
        }
        /// <summary>
        /// Decrypt a packet encrypted with CCMP
        /// </summary>
        /// <param name="encryptedPacket">The encrypted packet</param>
        /// <param name="temporalKey">The temporal key</param>
        /// <param name="headerMode">Mode in which headers should be handled</param>
        /// <param name="decrypted">Decrypted packet</param>
        /// <returns>True if successful, false if not</returns>
        /// <remarks>Ported from airdecap-ng</remarks>
        public static bool TryDecryptCCMP(
            IEEE802_11 encryptedPacket,
            byte[] temporalKey,
            DecryptionHeaderMode headerMode,
            out IPacket decrypted)
        {
            if (temporalKey.Length != 16)
            {
                // throw new ArgumentException("temporalKey must be 16 bytes");
                decrypted = null;
                return false;
            }

            var decryptedBytes = encryptedPacket.ToArray();
            int z, data_len, blocks, last, offset;
            bool is_a4, is_qos;

            var B0 = new byte[16];
            var B = new byte[16];
            var MIC = new byte[16];
            var PacketNumber = new byte[6];
            var AdditionalAuthData = new byte[32];

            is_a4 = (decryptedBytes[1] & 3) == 3;
            is_qos = (decryptedBytes[0] & 0x8C) == 0x88;
            z = 24 + 6 * (is_a4 ? 1 : 0);
            z += 2 * (is_qos ? 1 : 0);

            PacketNumber[0] = decryptedBytes[z + 7];
            PacketNumber[1] = decryptedBytes[z + 6];
            PacketNumber[2] = decryptedBytes[z + 5];
            PacketNumber[3] = decryptedBytes[z + 4];
            PacketNumber[4] = decryptedBytes[z + 1];
            PacketNumber[5] = decryptedBytes[z + 0];

            data_len = decryptedBytes.Length - z - 8 - 8;

            B0[0] = 0x59;
            B0[1] = 0;
            Array.Copy(decryptedBytes, 10, B0, 2, 6);
            Array.Copy(PacketNumber, 0, B0, 8, 6);
            B0[14] = (byte)((data_len >> 8) & 0xFF);
            B0[15] = (byte)(data_len & 0xFF);

            AdditionalAuthData[2] = (byte)(decryptedBytes[0] & 0x8F);
            AdditionalAuthData[3] = (byte)(decryptedBytes[1] & 0xC7);
            Array.Copy(decryptedBytes, 4, AdditionalAuthData, 4, 3 * 6);
            AdditionalAuthData[22] = (byte)(decryptedBytes[22] & 0x0F);

            if (is_a4)
            {
                Array.Copy(decryptedBytes, 24, AdditionalAuthData, 24, 6);

                if (is_qos)
                {
                    AdditionalAuthData[30] = (byte)(decryptedBytes[z - 2] & 0x0F);
                    AdditionalAuthData[31] = 0;
                    B0[1] = AdditionalAuthData[30];
                    AdditionalAuthData[1] = 22 + 2 + 6;
                }
                else
                {
                    AdditionalAuthData[30] = 0;
                    AdditionalAuthData[31] = 0;

                    B0[1] = 0;
                    AdditionalAuthData[1] = 22 + 6;
                }
            }
            else
            {
                if (is_qos)
                {
                    AdditionalAuthData[24] = (byte)(decryptedBytes[z - 2] & 0x0F);
                    AdditionalAuthData[25] = 0;
                    B0[1] = AdditionalAuthData[24];
                    AdditionalAuthData[1] = 22 + 2;
                }
                else
                {
                    AdditionalAuthData[24] = 0;
                    AdditionalAuthData[25] = 0;

                    B0[1] = 0;
                    AdditionalAuthData[1] = 22;
                }
            }

            using (var aesFactory = Aes.Create())
            {
                aesFactory.Mode = CipherMode.ECB;
                aesFactory.Key = temporalKey;
                var aes = aesFactory.CreateEncryptor();

                aes.TransformBlock(B0, 0, B0.Length, MIC, 0);
                CFunctions.XOR(MIC, 0, AdditionalAuthData, 16);
                aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0);
                CFunctions.XOR(MIC, 0, AdditionalAuthData.Skip(16).ToArray(), 16);
                aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0);

                B0[0] &= 0x07;
                B0[14] = 0;
                B0[15] = 0;
                aes.TransformBlock(B0, 0, B0.Length, B, 0);
                CFunctions.XOR(decryptedBytes, decryptedBytes.Length - 8, B, 8);

                blocks = (data_len + 16 - 1) / 16;
                last = data_len % 16;
                offset = z + 8;

                for (var i = 1; i <= blocks; i++)
                {
                    var n = last > 0 && i == blocks ? last : 16;

                    B0[14] = (byte)((i >> 8) & 0xFF);
                    B0[15] = (byte)(i & 0xFF);

                    aes.TransformBlock(B0, 0, B0.Length, B, 0);
                    CFunctions.XOR(decryptedBytes, offset, B, n);
                    CFunctions.XOR(MIC, 0, decryptedBytes.Skip(offset).ToArray(), n);
                    aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0);

                    offset += n;
                }
            }

            switch (headerMode)
            {
                case DecryptionHeaderMode.Removed:
                    {
                        decryptedBytes[1] &= 191; // Remove the protected bit
                        var decryptedBytesList = decryptedBytes.ToList();
                        decryptedBytesList.RemoveRange(34 - 8, 8);
                            // Remove CCMP Parameters (otherwise, without the protected bit it will break the parsing)
                        IEEE802_11 decryptedWithHeader;
                        if (IEEE802_11Factory.Instance.TryParse(decryptedBytesList.ToArray(), out decryptedWithHeader))
                        {
                            decrypted = decryptedWithHeader.Payload; // Remove the altered 802.11 header
                        }
                        else
                        {
                            decrypted = null;
                            return false;
                        }

                        break;
                    }
                case DecryptionHeaderMode.Modified:
                    {
                        decryptedBytes[1] &= 191; // Remove the protected bit
                        var decryptedBytesList = decryptedBytes.ToList();
                        decryptedBytesList.RemoveRange(34 - 8, 8);
                            // Remove CCMP Parameters (otherwise, without the protected bit it will break the parsing)
                        if (!IEEE802_11Factory.Instance.TryParse(decryptedBytesList.ToArray(), out decrypted))
                        {
                            return false;
                        }
                        break;
                    }
                case DecryptionHeaderMode.Untouched:
                    if (!IEEE802_11Factory.Instance.TryParse(decryptedBytes.ToArray(), out decrypted))
                    {
                        return false;
                    }
                    break;

                default:
                    decrypted = null;
                    return false;
            }

            return CFunctions.memcmp(decryptedBytes.Skip(offset).Take(8).ToArray(), MIC.Take(8).ToArray()) == 0;
        }