/// <summary>
        /// Sign, compress and encrypt for Single or Compound packet.
        /// </summary>
        public static Smb2Packet SignCompressAndEncrypt(Smb2Packet originalPacket, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2CompressionInfo compressioninfo, Smb2Role role)
        {
            ulong sessionId;
            bool  isCompound        = false;
            bool  notEncryptNotSign = false;
            bool  notEncrypt        = false;
            var   compressedPacket  = originalPacket;

            if (originalPacket is Smb2SinglePacket)
            {
                Smb2SinglePacket singlePacket = originalPacket as Smb2SinglePacket;
                sessionId = singlePacket.Header.SessionId;
                // [MS-SMB2] Section 3.2.4.1.8, the request being sent is SMB2 NEGOTIATE,
                // or the request being sent is SMB2 SESSION_SETUP with the SMB2_SESSION_FLAG_BINDING bit set in the Flags field,
                // the client MUST NOT encrypt the message
                if (sessionId == 0 ||
                    (singlePacket.Header.Command == Smb2Command.NEGOTIATE && (singlePacket is Smb2NegotiateRequestPacket)))
                {
                    notEncryptNotSign = true;
                }
                else if ((singlePacket.Header.Command == Smb2Command.SESSION_SETUP && (singlePacket is Smb2SessionSetupRequestPacket) &&
                          (singlePacket as Smb2SessionSetupRequestPacket).PayLoad.Flags == SESSION_SETUP_Request_Flags.SESSION_FLAG_BINDING))
                {
                    notEncrypt = true;
                }
            }
            else if (originalPacket is Smb2CompoundPacket)
            {
                isCompound = true;
                // The subsequent request in compound packet should use the SessionId of the first request for encryption
                sessionId = (originalPacket as Smb2CompoundPacket).Packets[0].Header.SessionId;
            }
            else
            {
                throw new NotImplementedException(string.Format("Signing and encryption are not implemented for packet: {0}", originalPacket.ToString()));
            }

            if (sessionId == 0 || notEncryptNotSign || !cryptoInfoTable.ContainsKey(sessionId))
            {
                if (originalPacket is Smb2CompressiblePacket)
                {
                    compressedPacket = Smb2Compression.Compress(originalPacket as Smb2CompressiblePacket, compressioninfo, role);
                }
                return(compressedPacket);
            }

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[sessionId];

            #region Encrypt
            // Try to encrypt the message whenever the encryption is supported or not except for sesstion setup.
            // If it's not supported, do it for negative test.
            // For compound packet, the encryption is done for the entire message.
            if (!notEncrypt)
            {
                if (originalPacket is Smb2CompressiblePacket)
                {
                    compressedPacket = Smb2Compression.Compress(originalPacket as Smb2CompressiblePacket, compressioninfo, role);
                }
                var encryptedPacket = Encrypt(sessionId, cryptoInfo, role, compressedPacket, originalPacket);
                if (encryptedPacket != null)
                {
                    return(encryptedPacket);
                }
            }
            #endregion

            #region Sign
            if (cryptoInfo.EnableSessionSigning)
            {
                if (isCompound)
                {
                    // Calculate signature for every packet in the chain
                    foreach (Smb2SinglePacket packet in (originalPacket as Smb2CompoundPacket).Packets)
                    {
                        // If the packet is the first one in the chain or the unralated one, use its own SessionId for sign and encrypt
                        // If it's not the first one and it's the related one, use the SessionId of the first request for sign and encrypt
                        if (!packet.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS))
                        {
                            sessionId  = packet.Header.SessionId;
                            cryptoInfo = cryptoInfoTable[sessionId];
                        }

                        packet.Header.Signature = Sign(cryptoInfo, packet.ToBytes());
                    }
                }
                else
                {
                    (originalPacket as Smb2SinglePacket).Header.Signature = Sign(cryptoInfo, originalPacket.ToBytes());
                }
            }
            #endregion
            if (originalPacket is Smb2CompressiblePacket)
            {
                compressedPacket = Smb2Compression.Compress(originalPacket as Smb2CompressiblePacket, compressioninfo, role);
            }
            return(compressedPacket);
        }
        private static Smb2EncryptedPacket Encrypt(ulong sessionId, Smb2CryptoInfo cryptoInfo, Smb2Role role, Smb2Packet packet, Smb2Packet packetBeforCompression)
        {
            Packet_Header header;

            if (packetBeforCompression is Smb2SinglePacket)
            {
                header = (packetBeforCompression as Smb2SinglePacket).Header;
            }
            else if (packetBeforCompression is Smb2CompoundPacket)
            {
                header = (packetBeforCompression as Smb2CompoundPacket).Packets[0].Header;
            }
            else
            {
                throw new InvalidOperationException("Unsupported SMB2 packet type!");
            }

            // Encrypt all messages after session setup if global encryption enabled.
            // Encrypt all messages after tree connect if global encryption disabled but share encryption enabled.
            if ((cryptoInfo.EnableSessionEncryption ||
                 (cryptoInfo.EnableTreeEncryption.Contains(header.TreeId) &&
                  header.Command != Smb2Command.TREE_CONNECT
                 )
                 )
                )
            {
                byte[]           originalBinary  = packet.ToBytes();
                Transform_Header transformHeader = new Transform_Header
                {
                    ProtocolId          = Smb2Consts.ProtocolIdInTransformHeader,
                    OriginalMessageSize = (uint)originalBinary.Length,
                    SessionId           = sessionId,
                    Signature           = new byte[16]
                };

                if (cryptoInfo.Dialect == DialectRevision.Smb311)
                {
                    transformHeader.Flags = TransformHeaderFlags.Encrypted;
                }
                else
                {
                    transformHeader.EncryptionAlgorithm = EncryptionAlgorithm.ENCRYPTION_AES128_CCM;
                }

                byte[] encrypted = new byte[originalBinary.Length];
                byte[] tag       = new byte[16];
                byte[] key       = role == Smb2Role.Server ? cryptoInfo.ServerOutKey : cryptoInfo.ServerInKey;

                // The reserved field (5 bytes for CCM, 4 bytes for GCM) must be set to zero.
                byte[] nonce = new byte[16];

                if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES128_CCM)
                {
                    int nonceLength = Smb2Consts.AES128CCM_Nonce_Length;
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    using (var cipher = new AesCcm(key))
                    {
                        cipher.Encrypt(
                            transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                            originalBinary,
                            encrypted,
                            tag,
                            // Use the fields including and after Nonce field as auth data
                            Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray());
                    }
                }
                else if (cryptoInfo.CipherId == EncryptionAlgorithm.ENCRYPTION_AES128_GCM)
                {
                    int nonceLength = Smb2Consts.AES128GCM_Nonce_Length;
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    using (var cipher = new AesGcm(key))
                    {
                        cipher.Encrypt(
                            transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                            originalBinary,
                            encrypted,
                            tag,
                            // Use the fields including and after Nonce field as auth data
                            Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray());
                    }
                }
                else
                {
                    throw new InvalidOperationException(String.Format(
                                                            "Invalid encryption algorithm is set in Smb2CryptoInfo when encrypting: {0}", cryptoInfo.CipherId));
                }

                transformHeader.Signature = tag;

                var encryptedPacket = new Smb2EncryptedPacket();
                encryptedPacket.Header       = transformHeader;
                encryptedPacket.EncryptdData = encrypted;

                return(encryptedPacket);
            }

            // Return null if the message is not required to be encrypted.
            return(null);
        }
        private static byte[] Encrypt(ulong sessionId, Smb2CryptoInfo cryptoInfo, Smb2Role role, Smb2Packet originalPacket)
        {
            Packet_Header header;

            if (originalPacket is Smb2SinglePacket)
            {
                header = (originalPacket as Smb2SinglePacket).Header;
            }
            else
            {
                header = (originalPacket as Smb2CompoundPacket).Packets[0].Header;
            }

            // Encrypt all messages after session setup if global encryption enabled.
            // Encrypt all messages after tree connect if global encryption disabled but share encryption enabled.
            if ((cryptoInfo.EnableSessionEncryption ||
                 (cryptoInfo.EnableTreeEncryption.Contains(header.TreeId) &&
                  header.Command != Smb2Command.TREE_CONNECT
                 )
                 )
                )
            {
                using (var bcrypt = new BCryptAlgorithm("AES"))
                {
                    byte[]           originalBinary  = originalPacket.ToBytes();
                    Transform_Header transformHeader = new Transform_Header
                    {
                        ProtocolId          = Smb2Consts.ProtocolIdInTransformHeader,
                        OriginalMessageSize = (uint)originalBinary.Length,
                        SessionId           = sessionId,
                        Signature           = new byte[16]
                    };

                    if (cryptoInfo.Dialect == DialectRevision.Smb311)
                    {
                        transformHeader.Flags = TransformHeaderFlags.Encrypted;
                    }
                    else
                    {
                        transformHeader.EncryptionAlgorithm = EncryptionAlgorithm.ENCRYPTION_AES128_CCM;
                    }

                    byte[] tag;

                    int nonceLength       = 0;
                    BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                    GetCryptoParams(cryptoInfo, CryptoOperationType.Encrypt, out mode, out nonceLength);
                    bcrypt.Mode = mode;
                    bcrypt.Key  = role == Smb2Role.Server ? cryptoInfo.ServerOutKey : cryptoInfo.ServerInKey;
                    // The reserved field (5 bytes for CCM, 4 bytes for GCM) must be set to zero.
                    byte[] nonce = new byte[16];
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    byte[] output = bcrypt.Encrypt(
                        originalBinary,
                        transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                        // Use the fields including and after Nonce field as auth data
                        Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray(),
                        // Signature is 16 bytes in length
                        16,
                        out tag);
                    transformHeader.Signature = tag;
                    return(Smb2Utility.MarshalStructure(transformHeader).Concat(output).ToArray());
                }
            }

            // Return null if the message is not required to be encrypted.
            return(null);
        }
        /// <summary>
        /// Sign and encrypt for Single or Compound packet.
        /// </summary>
        public static byte[] SignAndEncrypt(Smb2Packet originalPacket, Dictionary<ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            ulong sessionId;
            bool isCompound = false;

            if (originalPacket is Smb2SinglePacket)
            {
                sessionId = (originalPacket as Smb2SinglePacket).Header.SessionId;
            }
            else if (originalPacket is Smb2CompoundPacket)
            {
                isCompound = true;
                // The subsequent request in compound packet should use the SessionId of the first request for encryption
                sessionId = (originalPacket as Smb2CompoundPacket).Packets[0].Header.SessionId;
            }
            else
            {
                throw new NotImplementedException(string.Format("Signing and encryption are not implemented for packet: {0}", originalPacket.ToString()));
            }

            // Signing and encryption not required if session id is not available yet
            if (sessionId == 0 || !cryptoInfoTable.ContainsKey(sessionId))
                return originalPacket.ToBytes();

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[sessionId];

            #region Encrypt
            // Try to encrypt the message whenever the encryption is supported or not.
            // If it's not supported, do it for negative test.
            // For compound packet, the encryption is done for the entire message.
            byte[] encryptedBinary = Encrypt(sessionId, cryptoInfo, role, originalPacket);
            if (encryptedBinary != null)
                return encryptedBinary;
            #endregion

            #region Sign
            if (cryptoInfo.EnableSessionSigning)
            {
                if (isCompound)
                {
                    // Calculate signature for every packet in the chain
                    foreach (Smb2SinglePacket packet in (originalPacket as Smb2CompoundPacket).Packets)
                    {
                        // If the packet is the first one in the chain or the unralated one, use its own SessionId for sign and encrypt
                        // If it's not the first one and it's the related one, use the SessionId of the first request for sign and encrypt
                        if (!packet.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS))
                        {
                            sessionId = packet.Header.SessionId;
                            cryptoInfo = cryptoInfoTable[sessionId];
                        }

                        packet.Header.Signature = Sign(cryptoInfo, packet.ToBytes());
                    }
                }
                else
                {
                    (originalPacket as Smb2SinglePacket).Header.Signature = Sign(cryptoInfo, originalPacket.ToBytes());
                }
            }
            #endregion

            return originalPacket.ToBytes();
        }
        private static byte[] Encrypt(ulong sessionId, Smb2CryptoInfo cryptoInfo, Smb2Role role, Smb2Packet originalPacket)
        {
            Packet_Header header;
            if (originalPacket is Smb2SinglePacket)
            {
                header = (originalPacket as Smb2SinglePacket).Header;
            }
            else
            {
                header = (originalPacket as Smb2CompoundPacket).Packets[0].Header;
            }

            // Encrypt all messages after session setup if global encryption enabled.
            // Encrypt all messages after tree connect if global encryption disabled but share encryption enabled.
            if (header.Command != Smb2Command.NEGOTIATE
             && header.Command != Smb2Command.SESSION_SETUP
             && (cryptoInfo.EnableSessionEncryption
                 || (cryptoInfo.EnableTreeEncryption.Contains(header.TreeId)
                     && header.Command != Smb2Command.TREE_CONNECT
                     )
                 )
             )
            {
                using (var bcrypt = new BCryptAlgorithm("AES"))
                {
                    byte[] originalBinary = originalPacket.ToBytes();
                    Transform_Header transformHeader = new Transform_Header
                    {
                        ProtocolId = Smb2Consts.ProtocolIdInTransformHeader,
                        OriginalMessageSize = (uint)originalBinary.Length,
                        SessionId = sessionId,
                        Signature = new byte[16]
                    };

                    if (cryptoInfo.Dialect == DialectRevision.Smb311)
                    {
                        transformHeader.Flags = TransformHeaderFlags.Encrypted;
                    }
                    else
                    {
                        transformHeader.EncryptionAlgorithm = EncryptionAlgorithm.ENCRYPTION_AES128_CCM;
                    }

                    byte[] tag;

                    int nonceLength = 0;
                    BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                    GetCryptoParams(cryptoInfo, CryptoOperationType.Encrypt, out mode, out nonceLength);
                    bcrypt.Mode = mode;
                    bcrypt.Key = role == Smb2Role.Server ? cryptoInfo.ServerOutKey : cryptoInfo.ServerInKey;
                    // The reserved field (5 bytes for CCM, 4 bytes for GCM) must be set to zero.
                    byte[] nonce = new byte[16];
                    Buffer.BlockCopy(Guid.NewGuid().ToByteArray(), 0, nonce, 0, nonceLength);
                    transformHeader.Nonce = new Guid(nonce);

                    byte[] output = bcrypt.Encrypt(
                        originalBinary,
                        transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(),
                        // Use the fields including and after Nonce field as auth data
                        Smb2Utility.MarshalStructure(transformHeader).Skip(20).ToArray(),
                        // Signature is 16 bytes in length
                        16,
                        out tag);
                    transformHeader.Signature = tag;
                    return Smb2Utility.MarshalStructure(transformHeader).Concat(output).ToArray();
                }
            }

            // Return null if the message is not required to be encrypted.
            return null;
        }
        /// <summary>
        /// Sign and encrypt for Single or Compound packet.
        /// </summary>
        public static byte[] SignAndEncrypt(Smb2Packet originalPacket, Dictionary <ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            ulong sessionId;
            bool  isCompound = false;

            if (originalPacket is Smb2SinglePacket)
            {
                sessionId = (originalPacket as Smb2SinglePacket).Header.SessionId;
            }
            else if (originalPacket is Smb2CompoundPacket)
            {
                isCompound = true;
                // The subsequent request in compound packet should use the SessionId of the first request for encryption
                sessionId = (originalPacket as Smb2CompoundPacket).Packets[0].Header.SessionId;
            }
            else
            {
                throw new NotImplementedException(string.Format("Signing and encryption are not implemented for packet: {0}", originalPacket.ToString()));
            }

            // Signing and encryption not required if session id is not available yet
            if (sessionId == 0 || !cryptoInfoTable.ContainsKey(sessionId))
            {
                return(originalPacket.ToBytes());
            }

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[sessionId];

            #region Encrypt
            // Try to encrypt the message whenever the encryption is supported or not.
            // If it's not supported, do it for negative test.
            // For compound packet, the encryption is done for the entire message.
            byte[] encryptedBinary = Encrypt(sessionId, cryptoInfo, role, originalPacket);
            if (encryptedBinary != null)
            {
                return(encryptedBinary);
            }
            #endregion

            #region Sign
            if (cryptoInfo.EnableSessionSigning)
            {
                if (isCompound)
                {
                    // Calculate signature for every packet in the chain
                    foreach (Smb2SinglePacket packet in (originalPacket as Smb2CompoundPacket).Packets)
                    {
                        // If the packet is the first one in the chain or the unralated one, use its own SessionId for sign and encrypt
                        // If it's not the first one and it's the related one, use the SessionId of the first request for sign and encrypt
                        if (!packet.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS))
                        {
                            sessionId  = packet.Header.SessionId;
                            cryptoInfo = cryptoInfoTable[sessionId];
                        }

                        packet.Header.Signature = Sign(cryptoInfo, packet.ToBytes());
                    }
                }
                else
                {
                    (originalPacket as Smb2SinglePacket).Header.Signature = Sign(cryptoInfo, originalPacket.ToBytes());
                }
            }
            #endregion

            return(originalPacket.ToBytes());
        }