Fast-path revises client input packets from the first byte with the goal of improving bandwidth. The TPKT (see [T123]), X.224 (see [X224]) and MCS SDrq (see [T125]) headers are replaced, the Security Header is collapsed into the fast-path input header, and the Share Data Header is replaced by a new fast-path format. The contents of the input notification events (see section ) are also changed to reduce their size, particularly by removing or reducing headers. Support for fast-path input is advertised in the Input Capability Set.
file:///C:/ts_dev/TestSuites/MS-RDPBCGR/TestSuite/Src/TD/latest_XMLS_16may/RDPBCGR/ _rfc_ms-rdpbcgr2_1_7_1_2.xml
Inheritance: RdpbcgrClientPdu
        /// <summary>
        /// Decode Fast-path Update PDU
        /// </summary>
        /// <param name="serverSessionContext">server session context</param>
        /// <param name="data">data to be parsed</param>
        /// <returns>decoded Fast-path Update PDU</returns>
        public StackPacket DecodeTsFpInputPDU(RdpbcgrServerSessionContext serverSessionContext, byte[] data)
        {
            int currentIndex = 0;
            TS_FP_INPUT_PDU pdu = new TS_FP_INPUT_PDU();

            pdu.fpInputHeader.actionCode = ParseByte(data, ref currentIndex);
            nested_TS_FP_UPDATE_PDU_fpOutputHeader_actionCode_Values actionCode;
            byte numberEvents;
            encryptionFlagsChgd_Values encryptionFlags;
            GetFpInputHeaderInfo(pdu.fpInputHeader.actionCode, out actionCode, out numberEvents, out encryptionFlags);

            pdu.length1 = ParseByte(data, ref currentIndex);

            if ((ConstValue.MOST_SIGNIFICANT_BIT_FILTER & pdu.length1) != pdu.length1)
            {
                // length2 is present (since the most significant bit of length1 is set)
                pdu.length2 = ParseByte(data, ref currentIndex);
            }

            // TS_FP_UPDATE_PDU: fipsInformation
            if (EncryptionLevel.ENCRYPTION_LEVEL_FIPS == serverSessionContext.RdpEncryptionLevel)
            {
                pdu.fipsInformation = ParseTsFpFipsInfo(data, ref currentIndex);
            }

            // TS_FP_UPDATE_PDU: dataSignature
            if (IsFlagExist((byte)encryptionFlags, (byte)encryptionFlagsChgd_Values.FASTPATH_OUTPUT_ENCRYPTED))
            {
                // pdu were encrypted, data signature exists
                pdu.dataSignature = GetBytes(data, ref currentIndex,
                    ConstValue.TS_FP_UPDATE_PDU_DATA_SIGNATURE_LENGTH);
            }
            else
            {
                pdu.dataSignature = null;
            }

            // Decryption
            bool isSalted = IsFlagExist((byte)encryptionFlags,
                (byte)encryptionFlagsChgd_Values.FASTPATH_OUTPUT_SECURE_CHECKSUM);
            byte[] remainData = GetBytes(data, ref currentIndex, (data.Length - currentIndex));
            byte[] decryptedData = DecryptFastPathInputData(serverSessionContext, remainData, pdu.dataSignature, isSalted);

            // Decrypted data index
            int decryptedDataIndex = 0;

            //[yunzed]
            if( numberEvents == 0 )
            {
                Console.WriteLine("numberEvents is 0, so parse the additional numberEvents");
                pdu.numberEvents = ParseByte(decryptedData, ref decryptedDataIndex);
            }

            // TS_FP_UPDATE_PDU: fpOutputUpdates
            pdu.fpInputEvents= ParseTsFpInputEvents(decryptedData, ref decryptedDataIndex);

            // ETW Provider Dump Message
            if (pdu.dataSignature != null)
            {
                // Fast-Path encrypted
                string messageName = "RDPBCGR:" + pdu.GetType().Name;
                ExtendedLogger.DumpMessage(messageName, RdpbcgrUtility.DumpLevel_Layer3, pdu.GetType().Name, decryptedData);
            }

            // Check if data length exceeded expectation
            VerifyDataLength(decryptedData.Length, decryptedDataIndex, ConstValue.ERROR_MESSAGE_DATA_LENGTH_EXCEEDED);
            return pdu;
        }
        /// <summary>
        /// 2.2.8.1.2 Client Fast-Path Input Event PDU (TS_FP_INPUT_PDU)
        /// </summary>
        /// <param name="fpInputPdu"></param>
        public void VerifyPdu(TS_FP_INPUT_PDU fpInputPdu)
        {
            int actionCode;
            int numberEvents;
            int encryptionFlags;
            TS_FP_INPUT_EVENT e;

            actionCode = fpInputPdu.fpInputHeader.actionCode&0x3;
            numberEvents = (fpInputPdu.fpInputHeader.actionCode & 0x3c) >> 2;
            encryptionFlags = (fpInputPdu.fpInputHeader.actionCode & 0xc0) >> 6;

            CaptureRequirement(actionCode == 0 || actionCode == 3, 1700);
            if( fpInputPdu.numberEvents != 0 )
            {
                CaptureRequirement(numberEvents == 0, 1704);
            }

            if (this.serverConfig.encryptionLevel == EncryptionLevel.ENCRYPTION_LEVEL_FIPS)
            {
                site.CaptureRequirementIfAreEqual<TS_FP_FIPS_INFO_length_Values>(TS_FP_FIPS_INFO_length_Values.V1, fpInputPdu.fipsInformation.length, 1713,
                    @"In TS_FP_INPUT_PDU structure, the fipsInformation is present when the Encryption Level "
                    + @"selected by the server is ENCRYPTION_LEVEL_FIPS (4).In TS_FP_INPUT_PDU structure, the"
                    + @" fipsInformation is present when the Encryption Level selected by the server is "
                    + @"ENCRYPTION_LEVEL_FIPS (4).");
            }

            Console.WriteLine("encryptionFlags= " + encryptionFlags);
            if (fpInputPdu.dataSignature == null)
                Console.WriteLine("fpInputPdu.dataSignature is null");

            if (encryptionFlags == (int)encryptionFlags_Values.FASTPATH_INPUT_ENCRYPTED)
            {
                site.CaptureRequirementIfIsNotNull(fpInputPdu.dataSignature, 1715,
                    @"In TS_FP_INPUT_PDU structure, the dataSignature MUST be present if the FASTPATH_INPUT_ENCRYPTED"
                    + @" flag is set in the fpInputHeader field.");
            }

            if (numberEvents == 0)
            {
                //or fpInputPdu.numberEvents!=0
                site.CaptureRequirementIfIsNotNull(fpInputPdu.numberEvents, 1717,
                    @"In TS_FP_INPUT_PDU structure, the numberEvents field is present if the numberEvents bit field in"
                    + @" the fast-path header byte is zero.");
            }

            if (fpInputPdu.numberEvents == 0)
            {
                site.CaptureRequirementIfAreEqual<int>(numberEvents, fpInputPdu.fpInputEvents.Count, 1718,
                      @"In TS_FP_INPUT_PDU structure, the number of events present in fpInputEvents array is "
                      + @"given by the numberEvents bit field in the fast-path header byte, or by the numberEvents"
                      + @" field in the Fast-Path Input Event PDU (if it is present).");
            }
            else
            {
                site.CaptureRequirementIfAreEqual<int>(fpInputPdu.numberEvents, fpInputPdu.fpInputEvents.Count, 1718,
                    @"In TS_FP_INPUT_PDU structure, the number of events present in fpInputEvents array is given"
                    + @" by the numberEvents bit field in the fast-path header byte, or by the numberEvents field "
                    + @"in the Fast-Path Input Event PDU (if it is present).");
            }

            if (this.serverConfig.encryptionLevel == EncryptionLevel.ENCRYPTION_LEVEL_FIPS)
            {
                site.CaptureRequirementIfAreEqual<TS_FP_FIPS_INFO_length_Values>(TS_FP_FIPS_INFO_length_Values.V1, fpInputPdu.fipsInformation.length, 1721,
                    @"In TS_FP_FIPS_INFO structure, the length field MUST be set to 0x0010 (16 bytes).");
                //ConstValue.TSFIPS_VERSION1
                site.CaptureRequirementIfAreEqual<byte>(1, fpInputPdu.fipsInformation.version, 1723,
                    @"In TS_FP_FIPS_INFO structure, the version field SHOULD be set to TSFIPS_VERSION1 (0x01).");
            }

            if( numberEvents == 0 )
                numberEvents = fpInputPdu.numberEvents;

            for (int i = 0; i < fpInputPdu.fpInputEvents.Count; i++)
            {
                e = fpInputPdu.fpInputEvents[i];
                VerifyStructure(e);
            }
        }
        /// <summary>
        /// Create an instance of the class that is identical to the current PDU. 
        /// </summary>
        /// <returns>The new instance.</returns>
        public override StackPacket Clone()
        {
            TS_FP_INPUT_PDU cloneFpInputPdu = new TS_FP_INPUT_PDU(context);

            cloneFpInputPdu.dataSignature = RdpbcgrUtility.CloneByteArray(dataSignature);
            cloneFpInputPdu.fpInputHeader = fpInputHeader;
            cloneFpInputPdu.length1 = length1;
            cloneFpInputPdu.length2 = length2;
            cloneFpInputPdu.numberEvents = numberEvents;
            cloneFpInputPdu.fipsInformation = fipsInformation;

            if (fpInputEvents != null)
            {
                cloneFpInputPdu.fpInputEvents = new Collection<TS_FP_INPUT_EVENT>();
                for (int count = 0; count < fpInputEvents.Count; count++)
                {
                    cloneFpInputPdu.fpInputEvents.Add(fpInputEvents[count]);
                }
            }

            return cloneFpInputPdu;
        }