public static byte[] Decrypt(byte[] bytes, Dictionary<ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            Transform_Header transformHeader = Smb2Utility.UnmarshalStructure<Transform_Header>(bytes);

            if (transformHeader.SessionId == 0 || !cryptoInfoTable.ContainsKey(transformHeader.SessionId))
                throw new InvalidOperationException("Invalid SessionId in TRANSFORM_HEADER.");

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[transformHeader.SessionId];

            using (var bcrypt = new BCryptAlgorithm("AES"))
            {
                int nonceLength = 0;
                BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                GetCryptoParams(cryptoInfo, CryptoOperationType.Decrypt, out mode, out nonceLength);
                bcrypt.Mode = mode;
                bcrypt.Key = role == Smb2Role.Server ? cryptoInfo.ServerInKey : cryptoInfo.ServerOutKey;
                return bcrypt.Decrypt(bytes.Skip(52).ToArray(), transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(), bytes.Skip(20).Take(32).ToArray(), transformHeader.Signature);
            }
        }
        /// <summary>
        /// Encrypt a byte array. 
        /// Many data in NRPC struct is required to be encrypted.
        /// </summary>
        /// <param name="isAesNegotiated">
        /// Is AES negotiated during secure channel setup. 
        /// If AES is not negotiated, use RC4 to encrypt.
        /// </param>
        /// <param name="sessionKey">
        /// Session key.
        /// </param>
        /// <param name="buffer">
        /// Buffer to encrypt.
        /// </param>
        /// <returns>
        /// Encrypted buffer.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown when sessionKey or buffer is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown when session key length is incorrect.
        /// </exception>
        public static byte[] EncryptBuffer(
            bool isAesNegotiated,
            byte[] sessionKey,
            byte[] buffer)
        {
            if (sessionKey == null)
            {
                throw new ArgumentNullException("sessionKey");
            }
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (sessionKey.Length != NETLOGON_SESSION_KEY_LENGTH)
            {
                throw new ArgumentException("Session key length is incorrect.", "sessionKey");
            }

            if (isAesNegotiated)
            {
                using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
                {
                    aes.Mode = BCryptCipherMode.CFB;
                    aes.Key = sessionKey;
                    aes.IV = new byte[aes.BlockSize];
                    return aes.Encrypt(buffer);
                }
            }
            else
            {
                using (RC4 rc4 = RC4.Create())
                {
                    rc4.Key = sessionKey;
                    return rc4.CreateEncryptor().TransformFinalBlock(buffer, 0, buffer.Length);
                }
            }
        }
        /// <summary>
        /// Compute the Netlogon Credential.
        /// </summary>
        /// <param name="algorithm">
        /// Algorithm to compute the Netlogon authenticator. 
        /// </param>
        /// <param name="input">
        /// A byte array that contains the input.
        /// </param>
        /// <param name="sessionKey">
        /// Session Key.
        /// </param>
        /// <returns>Netlogon Credential</returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown when input or sessionKey parameter 
        /// passed to the method is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown when computation algorithm is not supported. 
        /// Thrown when session key length is incorrect.
        /// </exception>
        public static byte[] ComputeNetlogonCredential(
            NrpcComputeNetlogonCredentialAlgorithm algorithm,
            byte[] input,
            byte[] sessionKey)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }
            if (sessionKey == null)
            {
                throw new ArgumentNullException("sessionKey");
            }
            if (sessionKey.Length != NETLOGON_SESSION_KEY_LENGTH)
            {
                throw new ArgumentException("Session key length is incorrect.", "sessionKey");
            }

            byte[] credential;

            switch (algorithm)
            {
                case NrpcComputeNetlogonCredentialAlgorithm.AES128:
                    //ComputeNetlogonCredential(Input, Sk, Output)
                    //SET IV = 0
                    //CALL AesEncrypt(Input, Sk, IV, Output)

                    using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
                    {
                        aes.Mode = BCryptCipherMode.CFB;
                        aes.Key = sessionKey;
                        aes.IV = new byte[aes.BlockSize]; //AES128's IV is 128 bits.
                        credential = aes.Encrypt(input);
                    }

                    break;

                case NrpcComputeNetlogonCredentialAlgorithm.DESECB:
                    //ComputeNetlogonCredential(Input, Sk, Output)
                    //SET k1 to bytes(0, 6, Sk)
                    //CALL InitLMKey(k1, k3)
                    //SET k2 to bytes(7, 13, Sk)
                    //CALL InitLMKey(k2, k4)
                    //CALL DES_ECB(Input, k3, &output1)
                    //CALL DES_ECB(output1, k4, &output2)
                    //SET Output to output2

                    byte[] k1 = ArrayUtility.SubArray(sessionKey, 0, 7);
                    byte[] k2 = ArrayUtility.SubArray(sessionKey, 7, 7);

                    using (DES des = DES.Create())
                    {
                        des.Mode = CipherMode.ECB;
                        des.Padding = PaddingMode.None;

                        des.Key = InitLMKey(k1);
                        byte[] output1 = des.CreateEncryptor().TransformFinalBlock(input, 0, input.Length);

                        des.Key = InitLMKey(k2);
                        byte[] output2 = des.CreateEncryptor().TransformFinalBlock(output1, 0, output1.Length);

                        credential = output2;
                    }

                    break;

                default:
                    throw new ArgumentException(
                        "Specified netlogon credential computation algorithm is not valid.",
                        "algorithm");
            }

            return credential;
        }
        /// <summary>
        /// validate netlogon signature token when AES is negotiated
        /// </summary>
        /// <param name="sequenceNumber">sequence number</param>
        /// <param name="sessionKey">session key</param>
        /// <param name="requestConfidentiality">confidentiality is required or not</param>
        /// <param name="isClientSideOutbound">Is client side outbound message.</param>
        /// <param name="securityBuffers">
        /// Security buffer, contains plain-text if requestConfidentiality is false; 
        /// or cipher-text if requestConfidentiality is true; 
        /// and signature.
        /// </param>
        /// <returns>true if validate success; otherwise, false.</returns>
        private static bool ValidateNetlogonSignatureTokenWhenAesIsNegotiated(
            ref ulong sequenceNumber,
            byte[] sessionKey,
            bool requestConfidentiality,
            bool isClientSideOutbound,
            SecurityBuffer[] securityBuffers)
        {
            byte[] plainText;
            byte[] cipherText;
            byte[] token;

            //If AES is negotiated, a server receives an NL_AUTH_SHA2_SIGNATURE structure
            token = SspiUtility.ConcatenateSecurityBuffers(securityBuffers, SecurityBufferType.Token);
            NL_AUTH_SHA2_SIGNATURE nlAuthSha2Sign = TypeMarshal.ToStruct<NL_AUTH_SHA2_SIGNATURE>(token);

            //The SignatureAlgorithm bytes MUST be verified to ensure:
            //If AES is negotiated, the first byte is set to 0x13.
            //The second byte is set to 0x00
            if (nlAuthSha2Sign.SignatureAlgorithm != NL_AUTH_SHA2_SIGNATURE_SignatureAlgorithm_Values.HMACSHA256)
            {
                return false;
            }

            if (requestConfidentiality)
            {
                //If the Confidentiality option is requested from the application,
                //then the SealAlgorithm MUST be verified to ensure that
                //if AES is negotiated, the first byte is set to 0x1A;
                //The second byte is set to 0x00.
                if (nlAuthSha2Sign.SealAlgorithm != NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.AES128)
                {
                    return false;
                }
            }
            else
            {
                //If the Confidentiality option is not requested,
                //then the SealAlgorithm MUST be verified to contain all 0xff bytes.
                if (nlAuthSha2Sign.SealAlgorithm != NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.NotEncrypted)
                {
                    return false;
                }
            }

            //The Pad MUST be verified to contain all 0xff bytes.
            if (nlAuthSha2Sign.Pad != NL_AUTH_SHA2_SIGNATURE_Pad_Values.V1)
            {
                return false;
            }

            //The Flags data MAY be<86> disregarded.

            //The SequenceNumber MUST be decrypted.
            //If AES is negotiated, then the server MUST use the AES128 algorithm
            //and a key of SessionKey and an initialization vector constructed
            //by concatenating the checksum with itself (thus getting 16 bytes of data).
            using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
            {
                aes.Mode = BCryptCipherMode.CFB;
                aes.Key = sessionKey;
                aes.IV = ArrayUtility.ConcatenateArrays(
                    nlAuthSha2Sign.Checksum,
                    nlAuthSha2Sign.Checksum);
                nlAuthSha2Sign.SequenceNumber = aes.Decrypt(nlAuthSha2Sign.SequenceNumber);
            }

            //A local copy of SequenceNumber MUST be computed using the following algorithm.
            byte[] copySequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound);

            //The SequenceNumber MUST be compared to CopySeqNumber.
            if (!ArrayUtility.CompareArrays(
                    copySequenceNumber,
                    nlAuthSha2Sign.SequenceNumber))
            {
                return false;
            }

            //ServerSequenceNumber MUST be incremented.
            sequenceNumber += 1;

            if (requestConfidentiality)
            {
                //If the Confidentiality option is requested,
                //the Confounder and the data MUST be decrypted.
                byte[] aesEncryptionKey = ComputeEncryptionKeyWhenAesIsNegotiated(sessionKey);

                using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
                {
                    aes.Mode = BCryptCipherMode.CFB;
                    aes.Key = aesEncryptionKey;
                    aes.IV = ArrayUtility.ConcatenateArrays(
                        nlAuthSha2Sign.SequenceNumber,
                        nlAuthSha2Sign.SequenceNumber);
                    nlAuthSha2Sign.Confounder = aes.Decrypt(nlAuthSha2Sign.Confounder);

                    cipherText = SspiUtility.ConcatenateReadWriteSecurityBuffers(
                        securityBuffers,
                        SecurityBufferType.Data);
                    plainText = aes.Decrypt(cipherText);
                }

                SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, plainText);
            }

            //Compute signature
            plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers);
            byte[] checksum = ComputeSignatureWhenAesIsNegotiated(
                nlAuthSha2Sign,
                sessionKey,
                requestConfidentiality,
                plainText);

            //The first 8 bytes of the computed signature MUST be compared to the checksum.
            if (checksum != null
                && checksum.Length >= NL_AUTH_SIGNATURE_CHECKSUM_LENGTH
                && nlAuthSha2Sign.Checksum != null
                && nlAuthSha2Sign.Checksum.Length >= NL_AUTH_SIGNATURE_CHECKSUM_LENGTH)
            {
                for (int i = 0; i < NL_AUTH_SIGNATURE_CHECKSUM_LENGTH; i++)
                {
                    if (checksum[i] != nlAuthSha2Sign.Checksum[i])
                    {
                        return false;
                    }
                }
            }

            return true;
        }
        /// <summary>
        /// initial netlogon signature token when AES is negotiated
        /// </summary>
        /// <param name="sequenceNumber">sequence number</param>
        /// <param name="sessionKey">session key</param>
        /// <param name="requestConfidentiality">confidentiality is required or not</param>
        /// <param name="isClientSideOutbound">Is client side outbound message.</param>
        /// <param name="securityBuffers">
        /// Security buffers, contains input plain-text; output cipher-text and signature.
        /// </param>
        private static void InitialNetlogonSignatureTokenWhenAesIsNegotiated(
            ref ulong sequenceNumber,
            byte[] sessionKey,
            bool requestConfidentiality,
            bool isClientSideOutbound,
            SecurityBuffer[] securityBuffers)
        {
            byte[] plainText;
            byte[] cipherText;
            byte[] token;
            NL_AUTH_SHA2_SIGNATURE nlAuthSha2Sign = new NL_AUTH_SHA2_SIGNATURE();

            //The SignatureAlgorithm first byte MUST be set to 0x13,
            //and the second byte MUST be set to 0x00.
            nlAuthSha2Sign.SignatureAlgorithm =
                NL_AUTH_SHA2_SIGNATURE_SignatureAlgorithm_Values.HMACSHA256;

            if (requestConfidentiality)
            {
                //If the Confidentiality option (section 3.3.1) is
                //requested from the application, then the SealAlgorithm
                //first byte MUST be set to 0x1A, the second byte MUST
                //be set to 0x00, and the Confounder MUST be filled with
                //cryptographically random data.
                nlAuthSha2Sign.SealAlgorithm =
                    NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.AES128;
                nlAuthSha2Sign.Confounder = GenerateNonce(NL_AUTH_SIGNATURE_CONFOUNDER_LENGTH);
            }
            else
            {
                //If the Confidentiality option (section 3.3.1) is not
                //requested, then the SealAlgorithm MUST be filled with
                //two bytes of 0xff, and the Confounder is not included in the token.
                nlAuthSha2Sign.SealAlgorithm =
                    NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.NotEncrypted;
                nlAuthSha2Sign.Confounder = null;
            }

            //The Pad MUST be filled with 0xff bytes.
            nlAuthSha2Sign.Pad = NL_AUTH_SHA2_SIGNATURE_Pad_Values.V1;

            //The Flags MUST be filled with 0x00 bytes.
            nlAuthSha2Sign.Flags = Flags_Values.V1;

            //The SequenceNumber MUST be computed using the following algorithm.
            nlAuthSha2Sign.SequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound);

            //The ClientSequenceNumber MUST be incremented by 1.
            sequenceNumber += 1;

            //Compute signature
            plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers);
            nlAuthSha2Sign.Checksum = ComputeSignatureWhenAesIsNegotiated(
                nlAuthSha2Sign,
                sessionKey,
                requestConfidentiality,
                plainText);

            //If the Confidentiality option is requested, the data and the Confounder
            //field MUST be encrypted. If AES is negotiated then the server MUST use
            //the AES-128 algorithm using the SessionKey with an initialization
            //vector constructed by concatenating the sequence number with
            //itself twice (thus getting 16 bytes of data)
            if (requestConfidentiality)
            {
                byte[] aesEncryptionKey = ComputeEncryptionKeyWhenAesIsNegotiated(sessionKey);

                using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
                {
                    aes.Mode = BCryptCipherMode.CFB;
                    aes.Key = aesEncryptionKey;
                    aes.IV = ArrayUtility.ConcatenateArrays(
                        nlAuthSha2Sign.SequenceNumber,
                        nlAuthSha2Sign.SequenceNumber);
                    nlAuthSha2Sign.Confounder = aes.Encrypt(nlAuthSha2Sign.Confounder);

                    plainText = SspiUtility.ConcatenateReadWriteSecurityBuffers(
                        securityBuffers,
                        SecurityBufferType.Data);
                    cipherText = aes.Encrypt(plainText);
                }

                SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, cipherText);
            }
            else
            {
                cipherText = null;
            }

            //The SequenceNumber MUST be encrypted.
            //If AES is negotiated, then the server MUST use the AES-128
            //algorithm using the SessionKey with an initialization vector
            //constructed by concatenating the first 8 bytes of the
            //checksum with itself twice (thus getting 16 bytes of data)
            using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
            {
                aes.Mode = BCryptCipherMode.CFB;
                aes.Key = sessionKey;
                aes.IV = ArrayUtility.ConcatenateArrays(
                    nlAuthSha2Sign.Checksum, // Checksum is only 8 bytes
                    nlAuthSha2Sign.Checksum);
                nlAuthSha2Sign.SequenceNumber = aes.Encrypt(nlAuthSha2Sign.SequenceNumber);
            }

            nlAuthSha2Sign.Dummy = GenerateNonce(NL_AUTH_SHA2_SIGNATURE_DUMMY_LENGTH); // 24 == size of dummy

            token = TypeMarshal.ToBytes(nlAuthSha2Sign);
            SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Token, token);
        }
        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;
        }