/// <summary> /// Create SEGMENT_INFO_MESSAGE package. /// </summary> /// <param name="connectionInfoPort"> /// A 16-bit unsigned integer that MUST be set by the client to the port /// on which it is listening as a server-role peer, for use with the retrieval protocol. /// </param> /// <param name="segmentInformation">A Content Information data structure.</param> /// <returns>Return the SEGMENT_INFO_MESSAGE.</returns> public SEGMENT_INFO_MESSAGE CreateSegmentInfoMessage( int connectionInfoPort, Content_Information_Data_Structure segmentInformation) { MESSAGE_HEADER messageHeader; // MajorVersion (1 byte): The major part of the version, which MUST be 0x01. // MinorVersion (1 byte): The minor part of the version, which MUST be 0x00. // Padding (4 bytes): The value of this field is indeterminate and MUST be ignored on processing messageHeader.MajorVersion = 1; messageHeader.MinorVersion = 0; messageHeader.MsgType = PCHC_MESSAGE_TYPE.SEGMENT_INFO_MESSAGE; messageHeader.Padding = new byte[4]; Content_Information_Data_Structure segmentInfomation = segmentInformation; CONNECTION_INFORMATION connectionInfo; // Padding (6 bytes): The value of this field is indeterminated and MUST be ignored on processing. // Port (2 bytes): A 16-bit unsigned integer that MUST be set by the client to the port on // which it is listening as a server-role peer, for use with the retrieval protocol. connectionInfo.Padding = new byte[6]; connectionInfo.Port = (ushort)connectionInfoPort; // ContentTag (16 bytes): A structure consisting of 16 bytes of opaque data. byte[] contentTag = new byte[16]; SEGMENT_INFO_MESSAGE segmentInfoMessage; segmentInfoMessage.ConnectionInfo = connectionInfo; segmentInfoMessage.ContentTag = contentTag; segmentInfoMessage.MsgHeader = messageHeader; segmentInfoMessage.SegmentInfo = segmentInfomation; return(segmentInfoMessage); }
public void VerifyHashGenerationV1(byte[] content, Content_Information_Data_Structure contentInfo) { const int BLOCKBYTECOUNT = 0x10000; const int SEGMENTBLOCKCOUNT = 512; dwHashAlgo_Values hashAlgo = contentInfo.dwHashAlgo; int blockTotalCount = content.Length / BLOCKBYTECOUNT; if (content.Length > BLOCKBYTECOUNT * blockTotalCount) { blockTotalCount = blockTotalCount + 1; } int segmentCount = blockTotalCount / SEGMENTBLOCKCOUNT; if (blockTotalCount > SEGMENTBLOCKCOUNT * segmentCount) { segmentCount = segmentCount + 1; } HashAlgorithm hashAlgorithm; HMAC hmacAlgorithm; int blockHashSize; PccrcUtility.GetHashAlgorithm(hashAlgo, out hashAlgorithm, out hmacAlgorithm, out blockHashSize); hmacAlgorithm.Key = hashAlgorithm.ComputeHash(testConfig.ServerSecret); for (int segmentIndex = 0; segmentIndex < segmentCount; segmentIndex++) { List <byte> blockHashList = new List <byte>(); int blockCount = (segmentIndex == segmentCount - 1) ? (blockTotalCount % SEGMENTBLOCKCOUNT) : (SEGMENTBLOCKCOUNT); for (int blockIndex = 0; blockIndex < blockCount; blockIndex++) { var block = content.Skip(BLOCKBYTECOUNT * SEGMENTBLOCKCOUNT * segmentIndex + BLOCKBYTECOUNT * blockIndex).Take(BLOCKBYTECOUNT).ToArray(); byte[] blockHash = hashAlgorithm.ComputeHash(block); testSite.Assert.IsTrue( blockHash.SequenceEqual((contentInfo.blocks[segmentIndex].BlockHashes).Skip(blockIndex * blockHashSize).Take(blockHashSize).ToArray()), "The local calculated block hash in Segment: {0} Block: {1} should cosistent with the received value.", segmentIndex, blockIndex); blockHashList.AddRange(blockHash); } byte[] hod = hashAlgorithm.ComputeHash(blockHashList.ToArray()); testSite.Assert.IsTrue( hod.SequenceEqual(contentInfo.segments[segmentIndex].SegmentHashOfData), "The local calculated Hod should cosistent with the received value."); byte[] kp = hmacAlgorithm.ComputeHash(hod); testSite.Assert.IsTrue( kp.SequenceEqual(contentInfo.segments[segmentIndex].SegmentSecret), "The local calculated Kp should cosistent with the received value."); } }
public Content_Information_Data_Structure CreateContentInformationV1() { var contentInformation = new Content_Information_Data_Structure { Version = 0x0100, dwHashAlgo = dwHashAlgo_Values.SHA256, cSegments = 1, segments = new SegmentDescription[] { new SegmentDescription { cbBlockSize = DefaultBlockSize, cbSegment = DefaultBlockSize, SegmentHashOfData = TestUtility.GenerateRandomArray(32), SegmentSecret = TestUtility.GenerateRandomArray(32), ullOffsetInContent = 0 } }, dwOffsetInFirstSegment = 0, dwReadBytesInLastSegment = DefaultBlockSize, blocks = new SegmentContentBlocks[] { new SegmentContentBlocks { cBlocks = 1, BlockHashes = TestUtility.GenerateRandomArray(32), } } }; return(contentInformation); }
/// <summary> /// This action is used to send SEGMENT_INFO_MESSAGE request to and receive the /// correspondent Response Message from the hosted cache server. /// </summary> /// <param name="paddingInMessageHeader">An array formed by bytes for message header padding</param> /// <param name="pccrrPort">The port on which MS-PCCRR server-role will be listening. </param> /// <param name="paddingInConnectionInformation">An array formed by bytes for connection information padding</param> /// <param name="segmentInformation">The segment information.</param> /// <returns>Return the response message of the SegmentInfoMessage</returns> public ResponseMessage SendSegmentInfoMessage( byte[] paddingInMessageHeader, int pccrrPort, byte[] paddingInConnectionInformation, SegmentInformation segmentInformation) { // Convert the struct segmentInformation in adapter to the format in stack Content_Information_Data_Structure segmentInformationStack = ServerHelper.ConvertTostackForContentInfo(segmentInformation); // Create the SEGMENT_INFO_MESSAGE struct defined in stack for SendSegmentInfoMessage method SEGMENT_INFO_MESSAGE segmentInfoMessage = this.pchcClient.CreateSegmentInfoMessage( pccrrPort, segmentInformationStack); segmentInfoMessage.MsgHeader.Padding = paddingInMessageHeader; segmentInfoMessage.ConnectionInfo.Padding = paddingInConnectionInformation; this.ValidateSegmentInfoMessage(segmentInfoMessage); try { RESPONSE_MESSAGE responseMessageStack = this.pchcClient.SendSegmentInfoMessage(segmentInfoMessage); ResponseMessage responseMessage = ServerHelper.ConvertFromStackForResponseMsg(responseMessageStack); this.ValidateSegmentInfoResponse(responseMessage); return(responseMessage); } catch (NoRESPONSEMESSAGEException) { throw new NoResponseMessageException(); } }
/// <summary> /// Get the content Info from the content server. /// </summary> /// <param name="serverAddress"> The content server address.</param> /// <param name="port"> The port that the content server is listening.</param> /// <param name="uri"> The path of the file to request.</param> /// <returns> Returns content information if success.</returns> public Content_Information_Data_Structure GetContentInfo(string serverAddress, int port, string uri) { if (this.pccrtpResponse == null) { this.GetPccrtpResponse(serverAddress, port, uri); } Content_Information_Data_Structure contentInfoStack = this.pccrtpResponse.ContentInfo; return(contentInfoStack); }
/// <summary> /// Offer the block hashes to the hosted cache server. /// </summary> /// <param name="contentInfo">The content information</param> public void OfferHostedCacheContentInfo(Content_Information_Data_Structure contentInfo) { // It's important to make sure the hosted cache server having the block hash. int connectionInfoPort = int.Parse(Site.Properties["PCHC.SegmentInfoMessage.PccrrTransPort"]); SEGMENT_INFO_MESSAGE segmentInfoMsgStack = this.pchcClient.CreateSegmentInfoMessage( connectionInfoPort, contentInfo); this.pchcClient.SendSegmentInfoMessage(segmentInfoMsgStack); }
/// <summary> /// Convert the ContentInformaiton struct defined in stack to adapter /// </summary> /// <param name="contentInfoStack">The contentInformation defined in pccrc</param> /// <returns>Return the ContentInformaiton type defined in adapter</returns> public static SegmentInformation ConvertFromstackForContentInfo(Content_Information_Data_Structure contentInfoStack) { SegmentInformation contentInfo; contentInfo.DwHashAlgo = ConvertFromStackFordwHash(contentInfoStack.dwHashAlgo); contentInfo.DwOffsetInFirstSegment = contentInfoStack.dwOffsetInFirstSegment; contentInfo.DwReadBytesInLastSegment = contentInfoStack.dwReadBytesInLastSegment; contentInfo.Version = contentInfoStack.Version; contentInfo.CSegments = contentInfoStack.cSegments; contentInfo.Blocks = ConvertFromStackForSegBlocks(contentInfoStack.blocks); contentInfo.Segments = ConvertFromStackForSegDescription(contentInfoStack.segments); return(contentInfo); }
/// <summary> /// Generate PCCRTP response message to reply the client request. /// </summary> /// <param name="resourceLocator">The client request URI.</param> /// <param name="serverSecret">The server secret set on the server endpoint.</param> /// <returns>Returns the PCCRTP response message.</returns> public PccrtpResponse GenerateResponseMessage(string resourceLocator, string serverSecret) { PccrtpResponse pccrtpResponse = new PccrtpResponse(); Dictionary <string, string> tempHttpHeader = new Dictionary <string, string>(); HashGeneration hashHelp = new HashGeneration(serverSecret, dwHashAlgo_Values.SHA256); byte[] fileData = PccrcUtility.ReadFile(resourceLocator); Content_Information_Data_Structure contentInfo = hashHelp.GenerateContentInformation(fileData); tempHttpHeader.Add(CONTENTENCODING, "peerdist"); tempHttpHeader.Add(XP2PPEERDIST, "Version=1.0, ContentLength=" + fileData.Length); pccrtpResponse.HttpHeader = tempHttpHeader; pccrtpResponse.PayloadData = contentInfo.ToByteArray(); return(pccrtpResponse); }
public void HostedCacheServer_PchcServer_InitialOffer_SegmentInfoRetrieved() { CheckApplicability(); Content_Information_Data_Structure contentInformation = contentInformationUtility.CreateContentInformationV1(); PCHCClient pchcClient = new PCHCClient( TransferProtocol.HTTPS, testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPSListenPort, PchcConsts.HttpsUrl, testConfig.DomainName, testConfig.UserName, testConfig.UserPassword); BaseTestSite.Log.Add( LogEntryKind.Debug, "Send initial offer message to hosted cache server"); INITIAL_OFFER_MESSAGE initialOfferMessage = pchcClient.CreateInitialOfferMessage( testConfig.ClientContentRetrievalListenPort, contentInformation.GetSegmentId(0)); pchcClient.SendInitialOfferMessage(initialOfferMessage); BaseTestSite.Log.Add( LogEntryKind.Debug, "Supply segment info to hosted cache server"); SEGMENT_INFO_MESSAGE segmentInfoMessage = pchcClient.CreateSegmentInfoMessage( testConfig.ClientContentRetrievalListenPort, contentInformation, 0); pchcClient.SendSegmentInfoMessage(segmentInfoMessage); Microsoft.Protocols.TestTools.StackSdk.BranchCache.Pchc.RESPONSE_MESSAGE responseMessage2 = pchcClient.SendInitialOfferMessage(initialOfferMessage); TestClassBase.BaseTestSite.Assert.AreEqual <RESPONSE_CODE>( RESPONSE_CODE.INTERESTED, responseMessage2.ResponseCode, @"The hosted cache MUST specify a response code of 0 if it already has block hash."); }
/// <summary> /// Validate pccrc cSegments part. /// </summary> /// <param name="segmentInformation">Content information data structure.</param> /// <param name="hashLength">The hash length.</param> private void ValidatePccrcCSegment(Content_Information_Data_Structure segmentInformation, int hashLength) { #region MS-PCCRC_R67 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R67, the actual number of SegmentDescription fields in segments field is {0}", segmentInformation.segments.Length); // Verify MS-PCCRC requirement: MS-PCCRC_R67 Site.CaptureRequirementIfAreEqual <uint>( segmentInformation.cSegments, (uint)segmentInformation.segments.Length, PCCRCDOCSHORTNAME, 67, @"[In SegmentDescription] The segments field is composed of a number cSegments of SegmentDescription fields."); #endregion #region MS-PCCRC_R69 bool isVerifyR69 = true; for (int i = 0; i < segmentInformation.cSegments - 1; i++) { if (segmentInformation.segments[i].cbSegment != STANDARDSEGMENTSIZE) { isVerifyR69 = false; break; } } // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R69, the actual size of every segment except for the last segment is {0}32 megabytes", isVerifyR69 ? string.Empty : "not "); // Verify MS-PCCRC requirement: MS-PCCRC_R69 Site.CaptureRequirementIfIsTrue( isVerifyR69, PCCRCDOCSHORTNAME, 69, @"[In SegmentDescription] Every segment except for the last segment must be exactly 32 megabytes in size"); #endregion #region MS-PCCRC_R74 bool isVerifyR74 = true; for (int i = 1; i < segmentInformation.cSegments; i++) { ulong offset = segmentInformation.segments[i - 1].ullOffsetInContent + segmentInformation.segments[i - 1].cbSegment; if (segmentInformation.segments[i].ullOffsetInContent != offset) { isVerifyR74 = false; break; } } if (segmentInformation.segments[0].ullOffsetInContent != 0) { isVerifyR74 = false; } // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R74, the ullOffsetInContent in every SegmentDescription field is {0}content offset at which the start of the segment begins.", isVerifyR74 ? string.Empty : "not "); // Verify MS-PCCRC requirement: MS-PCCRC_R74 Site.CaptureRequirementIfIsTrue( isVerifyR74, PCCRCDOCSHORTNAME, 74, @"[In SegmentDescription] ullOffsetInContent (8 bytes): Content offset at which the start of the segment begins."); #endregion #region MS-PCCRC_R75 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R75. The actual size of the cbSegment is {0}.", Marshal.SizeOf(segmentInformation.segments[0].cbSegment)); // Verify MS-PCCRC requirement: MS-PCCRC_R75 Site.CaptureRequirementIfAreEqual <int>( 4, Marshal.SizeOf(segmentInformation.segments[0].cbSegment), PCCRCDOCSHORTNAME, 75, @"[In SegmentDescription] cbSegment (4 bytes): Total number of bytes in the segment, regardless of how many of those bytes intersect the content range."); #endregion #region MS-PCCRC_R76 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R76. The actual size of the cbSegment is {0}.", Marshal.SizeOf(segmentInformation.segments[0].cbBlockSize)); // Verify MS-PCCRC requirement: MS-PCCRC_R76 Site.CaptureRequirementIfAreEqual <int>( 4, Marshal.SizeOf(segmentInformation.segments[0].cbBlockSize), PCCRCDOCSHORTNAME, 76, @"[In SegmentDescription] cbBlockSize (4 bytes): Length of a content block within this segment, in bytes."); #endregion #region MS-PCCRC_R77 bool isVerifyR77 = true; for (int i = 0; i < segmentInformation.cSegments; i++) { if (segmentInformation.segments[i].cbBlockSize != 65536) { isVerifyR77 = false; } } // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R77, the block size of every segment is {0}65536 bytes", isVerifyR77 ? string.Empty : "not "); // Verify MS-PCCRC requirement: MS-PCCRC_R77 Site.CaptureRequirementIfIsTrue( isVerifyR77, PCCRCDOCSHORTNAME, 77, @"[In SegmentDescription] cbBlockSize (4 bytes): Every segment MUST use the block size of 65536 bytes."); #endregion #region MS-PCCRC_R79 bool isVerifyR79 = true; for (int i = 0; i < segmentInformation.cSegments; i++) { if (segmentInformation.segments[i].SegmentHashOfData.Length != hashLength) { isVerifyR79 = false; } } // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R79, the SegmentHashOfData is {0} of length 32", isVerifyR79 ? string.Empty : "not "); // Verify MS-PCCRC requirement: MS-PCCRC_R79 Site.CaptureRequirementIfIsTrue( isVerifyR79, PCCRCDOCSHORTNAME, 79, @"[In SegmentDescription] SegmentHashOfData (variable): The hash is of length 32 if dwHashAlgo at the start of the Content Information was 0x800C = SHA-256."); #endregion #region MS-PCCRC_R83 bool isVerifyR83 = true; for (int i = 0; i < segmentInformation.cSegments; i++) { if (segmentInformation.segments[i].SegmentSecret.Length != hashLength) { isVerifyR83 = false; } } // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R83, the actual length of the SegmentSecret is {0}", hashLength); // Verify MS-PCCRC requirement: MS-PCCRC_R83 Site.CaptureRequirementIfIsTrue( isVerifyR83, PCCRCDOCSHORTNAME, 83, @"[In SegmentDescription] SegmentSecret (variable):The hash is of length 32 if dwHashAlgo at the start of the Content Information was 0x800C = SHA-256."); #endregion }
/// <summary> /// Validate the SegmentInformation in the Segment_info-Message. /// </summary> /// <param name="segmentInformation">The segmentInformation need to be validate.</param> private void ValidateSegmentInformation(Content_Information_Data_Structure segmentInformation) { // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCHC_R43:The SegmentInformation is an instance of Content_Information_Data_Structure."); // Capture MS-PCHC R43 Site.CaptureRequirement( 43, @"[In SEGMENT_INFO_MESSAGE] SegmentInformation (variable): A Content Information data structure ([MS-PCCRC] section 2.3)."); // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCHC_R45:The SegmentInformation is an instance of Content_Information_Data_Structure which surely contains the subfields of the segment's Content Information data structure, and SegmentContentBlocks. And can be validated unmarshalled correctly from stack."); // Capture MS-PCHC R45 Site.CaptureRequirement( 45, @"[In SEGMENT_INFO_MESSAGE] The SegmentInformation field also contains the subfields of the segment's Content Information data structure, SegmentDescription, and SegmentContentBlocks, as specified in [MS-PCCRC] sections 2.3.1.1 and 2.3.1.2, respectively."); // Capture MS-PCHC R49 Site.CaptureRequirementIfAreEqual <uint>( 1, segmentInformation.cSegments, 49, "[In SEGMENT_INFO_MESSAGE, SEGMENT_INFORMATION (variable)] The cSegments field MUST be set to 1."); // Capture MS-PCHC R50 Site.Assert.IsInstanceOfType( segmentInformation.segments, typeof(SegmentDescription[]), @"Validate the segments is SegmentDescription array, in MS-PCHC client, only the size of the SegmentDescription will be verified,the actual is {0} array.", segmentInformation.segments); Site.CaptureRequirementIfAreEqual <int>( 1, segmentInformation.segments.Length, 50, @"[In SEGMENT_INFO_MESSAGE, SEGMENT_INFORMATION (variable)] The segments field MUST contain the single SegmentDescription ([MS-PCCRC] section 2.3.1.1) in the original Content Information data structure corresponding to the segment being offered."); // Capture MS-PCHC R51 Site.Assert.IsInstanceOfType( segmentInformation.blocks, typeof(SegmentContentBlocks[]), @"Validate the blocks is SegmentContentBlocks array, in MS-PCHC client, only the size of the SegmentContentBlocks will be verified, the actual is {0} array.", segmentInformation.blocks); Site.CaptureRequirementIfAreEqual <int>( 1, segmentInformation.blocks.Length, 51, @"[In SEGMENT_INFO_MESSAGE, SEGMENT_INFORMATION (variable)] The blocks field MUST contain a single SegmentContentBlocks ([MS-PCCRC] section 2.3.1.2) corresponding to the segment being offered, copied from the blocks field in the original Content Information data structure."); }
public void HostedCacheServer_PccrrClient_MessageHeader_CryptoAlgoId(CryptoAlgoId_Values algoId) { CheckApplicability(); EventQueue eventQueue = new EventQueue(BaseTestSite); eventQueue.Timeout = testConfig.Timeout; byte[] content = TestUtility.GenerateRandomArray(ContentInformationUtility.DefaultBlockSize); Content_Information_Data_Structure contentInformation = contentInformationUtility.CreateContentInformationV1(); ProtoVersion protoVersion = new ProtoVersion { MajorVersion = 1, MinorVersion = 0 }; BaseTestSite.Log.Add( LogEntryKind.Debug, "Start PCCRR server to be ready to serve content to hosted cache server"); using (PccrrTestServerV1 pccrrTestServer = new PccrrTestServerV1()) { pccrrTestServer.Start( testConfig.ClientContentRetrievalListenPort, algoId, protoVersion, contentInformation, content, eventQueue); PCHCClient pchcClient = new PCHCClient( TransferProtocol.HTTPS, testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPSListenPort, PchcConsts.HttpsUrl, testConfig.DomainName, testConfig.UserName, testConfig.UserPassword); SEGMENT_INFO_MESSAGE segmentInfoMessage = pchcClient.CreateSegmentInfoMessage( testConfig.ClientContentRetrievalListenPort, contentInformation, 0); pchcClient.SendSegmentInfoMessage(segmentInfoMessage); BaseTestSite.Log.Add( LogEntryKind.Debug, "Offer content block 0 of segment 0 to hosted cache server to hosted cache server"); int blockCount = 0; TestUtility.DoUntilSucceed(delegate() { eventQueue.Expect <MessageArrivedEventArgs>(typeof(PccrrServer).GetEvent("MessageArrived"), delegate(System.Net.IPEndPoint sender, PccrrPacket pccrrPacket) { var pccrrGetBlksRequest = pccrrPacket as PccrrGETBLKSRequestPacket; if (pccrrGetBlksRequest != null) { blockCount++; } }); return(blockCount == 1); }, TimeSpan.MaxValue, TimeSpan.Zero); BaseTestSite.Log.Add( LogEntryKind.Debug, "Wait until cache is available on hosted cache server"); TestUtility.DoUntilSucceed(() => sutControlAdapter.IsLocalCacheExisted(testConfig.HostedCacheServerComputerFQDNOrNetBiosName), testConfig.Timeout, testConfig.RetryInterval); } PccrrClient pccrrClient = new PccrrClient(testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPListenPort); Aes aes = PccrrUtitlity.CreateAes(algoId); BaseTestSite.Log.Add( LogEntryKind.Debug, "Retrieve block 0 of segment 0 from hosted cache server", 0); PccrrGETBLKSRequestPacket pccrrBlkRequest = pccrrClient.CreateMsgGetBlksRequest( contentInformation.GetSegmentId(0), algoId, MsgType_Values.MSG_GETBLKS, (uint)0, 1); pccrrClient.SendPacket( pccrrBlkRequest, testConfig.Timeout); PccrrBLKResponsePacket pccrrBlkResponse = (PccrrBLKResponsePacket)pccrrClient.ExpectPacket(); byte[] block = pccrrBlkResponse.MsgBLK.Block; if (algoId != CryptoAlgoId_Values.NoEncryption) { block = PccrrUtitlity.Decrypt(aes, block, contentInformation.segments[0].SegmentSecret, pccrrBlkResponse.MsgBLK.IVBlock); } BaseTestSite.Assert.IsTrue( Enumerable.SequenceEqual(content, block), "The retrieved cached data should be the same as server data."); }
public void HostedCacheServer_PccrrClient_MessageHeader_ProtoVerIncompatible() { CheckApplicability(); EventQueue eventQueue = new EventQueue(BaseTestSite); eventQueue.Timeout = testConfig.Timeout; Content_Information_Data_Structure contentInformation = contentInformationUtility.CreateContentInformationV1(); CryptoAlgoId_Values cryptoAlgoId = CryptoAlgoId_Values.AES_128; ProtoVersion protoVersion = new ProtoVersion { MajorVersion = 1, MinorVersion = 0 }; BaseTestSite.Log.Add( LogEntryKind.Debug, "Start PCCRR server to be ready to serve content to hosted cache server"); using (PccrrTestIncompatibleProtoVerServer pccrrTestServer = new PccrrTestIncompatibleProtoVerServer()) { pccrrTestServer.Start( testConfig.ClientContentRetrievalListenPort, cryptoAlgoId, protoVersion, contentInformation, new byte[0], eventQueue); PCHCClient pchcClient = new PCHCClient( TransferProtocol.HTTPS, testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPSListenPort, PchcConsts.HttpsUrl, testConfig.DomainName, testConfig.UserName, testConfig.UserPassword); SEGMENT_INFO_MESSAGE segmentInfoMessage = pchcClient.CreateSegmentInfoMessage( testConfig.ClientContentRetrievalListenPort, contentInformation, 0); pchcClient.SendSegmentInfoMessage(segmentInfoMessage); BaseTestSite.Log.Add( LogEntryKind.Debug, "Offer PccrrBLKSResponse with incompatible proto version to hosted cache server"); int blockCount = 0; TestUtility.DoUntilSucceed(delegate() { eventQueue.Expect <MessageArrivedEventArgs>(typeof(PccrrServer).GetEvent("MessageArrived"), delegate(System.Net.IPEndPoint sender, PccrrPacket pccrrPacket) { var pccrrGetBlksRequest = pccrrPacket as PccrrGETBLKSRequestPacket; if (pccrrGetBlksRequest != null) { blockCount++; } }); return(blockCount == 1); }, TimeSpan.MaxValue, TimeSpan.Zero); TestUtility.DoUntilSucceed(() => sutControlAdapter.IsLocalCacheExisted(testConfig.HostedCacheServerComputerFQDNOrNetBiosName), testConfig.NegativeTestTimeout, testConfig.RetryInterval); } }
public void HostedCacheServer_PchcServer_InitialOffer_ContentRereieved() { CheckApplicability(); EventQueue eventQueue = new EventQueue(BaseTestSite); eventQueue.Timeout = testConfig.Timeout; Content_Information_Data_Structure contentInformation = contentInformationUtility.CreateContentInformationV1(); CryptoAlgoId_Values cryptoAlgoId = CryptoAlgoId_Values.AES_128; using (PccrrTestServerV1 pccrrTestServer = new PccrrTestServerV1()) { BaseTestSite.Log.Add( LogEntryKind.Debug, "Start PCCRR server to be ready to serve content to hosted cache server"); pccrrTestServer.Start( testConfig.ClientContentRetrievalListenPort, cryptoAlgoId, new ProtoVersion { MajorVersion = 1, MinorVersion = 0 }, contentInformation, TestUtility.GenerateRandomArray(ContentInformationUtility.DefaultBlockSize), eventQueue); PCHCClient pchcClient = new PCHCClient( TransferProtocol.HTTPS, testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPSListenPort, PchcConsts.HttpsUrl, testConfig.DomainName, testConfig.UserName, testConfig.UserPassword); BaseTestSite.Log.Add( LogEntryKind.Debug, "Send initial offer message to hosted cache server"); SEGMENT_INFO_MESSAGE segmentInfoMessage = pchcClient.CreateSegmentInfoMessage( testConfig.ClientContentRetrievalListenPort, contentInformation, 0); pchcClient.SendSegmentInfoMessage(segmentInfoMessage); BaseTestSite.Log.Add( LogEntryKind.Debug, "Make sure all blocks in segment 0 are retrieved by hosted cache server"); int blockCount = 0; TestUtility.DoUntilSucceed(delegate() { eventQueue.Expect <MessageArrivedEventArgs>(typeof(PccrrServer).GetEvent("MessageArrived"), delegate(System.Net.IPEndPoint sender, PccrrPacket pccrrPacket) { var pccrrGetBlksRequest = pccrrPacket as PccrrGETBLKSRequestPacket; if (pccrrGetBlksRequest != null) { blockCount++; } }); return(blockCount == contentInformation.segments[0].BlockCount); }, TimeSpan.MaxValue, TimeSpan.Zero); BaseTestSite.Log.Add( LogEntryKind.Debug, "Wait until cache is available on hosted cache server"); TestUtility.DoUntilSucceed(() => sutControlAdapter.IsLocalCacheExisted(testConfig.HostedCacheServerComputerFQDNOrNetBiosName), testConfig.Timeout, testConfig.RetryInterval); INITIAL_OFFER_MESSAGE initialOfferMessage = pchcClient.CreateInitialOfferMessage( testConfig.ClientContentRetrievalListenPort, contentInformation.GetSegmentId(0)); Microsoft.Protocols.TestTools.StackSdk.BranchCache.Pchc.RESPONSE_MESSAGE responseMessage2 = pchcClient.SendInitialOfferMessage(initialOfferMessage); TestClassBase.BaseTestSite.Assert.AreEqual <RESPONSE_CODE>( RESPONSE_CODE.INTERESTED, responseMessage2.ResponseCode, @"The hosted cache MUST specify a response code of 0 if it already has block data and block hash."); } }
public void HostedCacheServer_BVT_CacheOfferingRetrieval_V1() { CheckApplicability(); EventQueue eventQueue = new EventQueue(BaseTestSite); eventQueue.Timeout = testConfig.Timeout; BaseTestSite.Log.Add( LogEntryKind.Debug, "Trigger hash generation on content server"); byte[] content = contentInformationUtility.RetrieveContentData(); Content_Information_Data_Structure contentInformation = PccrcUtility.ParseContentInformation(contentInformationUtility.RetrieveContentInformation(BranchCacheVersion.V1)); CryptoAlgoId_Values cryptoAlgoId = CryptoAlgoId_Values.AES_128; PccrrClient pccrrClient = new PccrrClient(testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPListenPort); for (int i = 0; i < contentInformation.cSegments; ++i) { var pccrrBlkListRequest = pccrrClient.CreateMsgGetBlkListRequest( contentInformation.GetSegmentId(i), new BLOCK_RANGE[] { new BLOCK_RANGE { Index = 0, Count = contentInformation.segments[i].BlockCount } }, cryptoAlgoId, MsgType_Values.MSG_GETBLKLIST); pccrrClient.SendPacket( pccrrBlkListRequest, testConfig.Timeout); var pccrrBlkListResponse = (PccrrBLKLISTResponsePacket)pccrrClient.ExpectPacket(); BaseTestSite.Assert.AreEqual <uint>( 0, pccrrBlkListResponse.MsgBLKLIST.BlockRangeCount, "The server MUST set the BlockRangeCount field to zero if it doesn't have the requested blocks data."); } BaseTestSite.Log.Add( LogEntryKind.Debug, "Retrieve content information from content server"); using (PccrrTestServerV1 pccrrTestServer = new PccrrTestServerV1()) { BaseTestSite.Log.Add( LogEntryKind.Debug, "Start PCCRR server to be ready to serve content to hosted cache server"); pccrrTestServer.Start( testConfig.ClientContentRetrievalListenPort, cryptoAlgoId, contentInformation, content, eventQueue); PCHCClient pchcClient = new PCHCClient( TransferProtocol.HTTPS, testConfig.HostedCacheServerComputerName, testConfig.HostedCacheServerHTTPSListenPort, PchcConsts.HttpsUrl, testConfig.DomainName, testConfig.UserName, testConfig.UserPassword); for (int i = 0; i < contentInformation.cSegments; i++) { BaseTestSite.Log.Add( LogEntryKind.Debug, "Offer content segment {0} to hosted cache server", i); INITIAL_OFFER_MESSAGE initialOfferMessage = pchcClient.CreateInitialOfferMessage( testConfig.ClientContentRetrievalListenPort, contentInformation.GetSegmentId(i)); Microsoft.Protocols.TestTools.StackSdk.BranchCache.Pchc.RESPONSE_MESSAGE responseMessage = pchcClient.SendInitialOfferMessage(initialOfferMessage); TestClassBase.BaseTestSite.Assert.AreEqual <RESPONSE_CODE>( RESPONSE_CODE.INTERESTED, responseMessage.ResponseCode, @"The hosted cache MUST specify a response code of 1 if its list of block hashes associated with the segment is incomplete."); BaseTestSite.Log.Add( LogEntryKind.Debug, "Supply segment info to hosted cache server"); SEGMENT_INFO_MESSAGE segmentInfoMessage = pchcClient.CreateSegmentInfoMessage( testConfig.ClientContentRetrievalListenPort, contentInformation, i); responseMessage = pchcClient.SendSegmentInfoMessage(segmentInfoMessage); TestClassBase.BaseTestSite.Assert.AreEqual <RESPONSE_CODE>( RESPONSE_CODE.OK, responseMessage.ResponseCode, @"The hosted cache MUST send a response code of 0 when SEGMENT_INFO_MESSAGE request received"); BaseTestSite.Log.Add( LogEntryKind.Debug, "Make sure all blocks in segment {0} are retrieved by hosted cache server", i); int blockCount = 0; TestUtility.DoUntilSucceed(delegate() { eventQueue.Expect <MessageArrivedEventArgs>(typeof(PccrrServer).GetEvent("MessageArrived"), delegate(System.Net.IPEndPoint sender, PccrrPacket pccrrPacket) { var pccrrGetBlksRequest = pccrrPacket as PccrrGETBLKSRequestPacket; if (pccrrGetBlksRequest != null) { blockCount++; } }); return(blockCount == contentInformation.segments[i].BlockCount); }, TimeSpan.MaxValue, TimeSpan.Zero); } BaseTestSite.Log.Add( LogEntryKind.Debug, "Wait until cache is available on hosted cache server"); TestUtility.DoUntilSucceed(() => sutControlAdapter.IsLocalCacheExisted(testConfig.HostedCacheServerComputerFQDNOrNetBiosName), testConfig.Timeout, testConfig.RetryInterval); } List <byte> retrievedContent = new List <byte>(); BaseTestSite.Log.Add( LogEntryKind.Debug, "Negotiate PCCRR version"); var pccrrNegotiateRequest = pccrrClient.CreateMsgNegoRequest( new ProtoVersion { MajorVersion = 1, MinorVersion = 0 }, new ProtoVersion { MajorVersion = 1, MinorVersion = ushort.MaxValue }, cryptoAlgoId, MsgType_Values.MSG_NEGO_REQ); pccrrClient.SendPacket( pccrrNegotiateRequest, testConfig.Timeout); var pccrrNegotiateResponse = (PccrrNegoResponsePacket)pccrrClient.ExpectPacket(); if (testConfig.SupportBranchCacheV1) { BaseTestSite.Assert.IsTrue( pccrrNegotiateResponse.MsgNegoResp.MinSupporteProtocolVersion.MajorVersion <= 1 && pccrrNegotiateResponse.MsgNegoResp.MaxSupporteProtocolVersion.MajorVersion >= 1, "SupportedProtocolVersion doesn't match configuration"); } if (testConfig.SupportBranchCacheV2) { BaseTestSite.Assert.IsTrue( pccrrNegotiateResponse.MsgNegoResp.MinSupporteProtocolVersion.MajorVersion <= 2 && pccrrNegotiateResponse.MsgNegoResp.MaxSupporteProtocolVersion.MajorVersion >= 2, "SupportedProtocolVersion doesn't match configuration"); } Aes aes = PccrrUtitlity.CreateAes(cryptoAlgoId); for (int i = 0; i < contentInformation.cSegments; i++) { BaseTestSite.Log.Add( LogEntryKind.Debug, "Retrieve block list for segment {0}", i); var pccrrBlkListRequest = pccrrClient.CreateMsgGetBlkListRequest( contentInformation.GetSegmentId(i), new BLOCK_RANGE[] { new BLOCK_RANGE { Index = 0, Count = contentInformation.segments[i].BlockCount } }, cryptoAlgoId, MsgType_Values.MSG_GETBLKLIST); pccrrClient.SendPacket( pccrrBlkListRequest, testConfig.Timeout); var pccrrBlkListResponse = (PccrrBLKLISTResponsePacket)pccrrClient.ExpectPacket(); BaseTestSite.Assert.AreNotEqual <uint>( 0, pccrrBlkListResponse.MsgBLKLIST.BlockRangeCount, "The server MUST set the BlockRangeCount field to a value greater than zero if it has the requested blocks data."); for (int j = 0; j < contentInformation.segments[i].BlockCount; j++) { BaseTestSite.Log.Add( LogEntryKind.Debug, "Retrieve block {0} for segment {1}", j, i); PccrrGETBLKSRequestPacket pccrrBlkRequest = pccrrClient.CreateMsgGetBlksRequest( contentInformation.GetSegmentId(i), cryptoAlgoId, MsgType_Values.MSG_GETBLKS, (uint)j, 1); pccrrClient.SendPacket( pccrrBlkRequest, testConfig.Timeout); PccrrBLKResponsePacket pccrrBlkResponse = (PccrrBLKResponsePacket)pccrrClient.ExpectPacket(); BaseTestSite.Assert.AreNotEqual <uint>( 0, pccrrBlkResponse.MsgBLK.SizeOfBlock, "The server MUST set the SizeOfBlock field to a value greater than zero if it has the requested blocks data."); byte[] block = pccrrBlkResponse.MsgBLK.Block; if (cryptoAlgoId != CryptoAlgoId_Values.NoEncryption) { block = PccrrUtitlity.Decrypt(aes, block, contentInformation.segments[i].SegmentSecret, pccrrBlkResponse.MsgBLK.IVBlock); } retrievedContent.AddRange(block); } } BaseTestSite.Assert.IsTrue( Enumerable.SequenceEqual(content, retrievedContent), "The retrieved cached data should be the same as server data."); }
/// <summary> /// Verify Data Structure of Content Information defined in section 2.3. /// </summary> /// <param name="pccrtpResponse">The HTTP resopnse.</param> private void VerifyDataStructure(PccrtpResponse pccrtpResponse) { Content_Information_Data_Structure contentInfo = pccrtpResponse.ContentInfo; #region MS-PCCRC_R58 int hashLength = 0; int valueOfDwHashAlgo = 0; switch (pccrtpResponse.ContentInfo.dwHashAlgo) { case dwHashAlgo_Values.V1: valueOfDwHashAlgo = 0x0000800C; hashLength = 32; break; case dwHashAlgo_Values.V2: hashLength = 48; valueOfDwHashAlgo = 0x0000800D; break; case dwHashAlgo_Values.V3: hashLength = 64; valueOfDwHashAlgo = 0x0000800E; break; default: break; } // Add the debug information. Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R58, the actual length of dwHashAlgo is: {0}", Marshal.SizeOf(valueOfDwHashAlgo)); // Verify MS-PCCRC requirement: MS-PCCRC_R58 Site.CaptureRequirementIfAreEqual <int>( 4, Marshal.SizeOf(valueOfDwHashAlgo), 58, @"[In Content Information Data Structure Version 1.0] dwHashAlgo (4 bytes): Hash algorithm to use. <2> "); #endregion #region MS-PCCRC_R581 bool isVerifyR581 = valueOfDwHashAlgo == 0x0000800C || valueOfDwHashAlgo == 0x0000800D || valueOfDwHashAlgo == 0x0000800E; // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R581, the actual value of dwHashAlgo is 0x{0:X8}.", valueOfDwHashAlgo); // Verify MS-PCCRC requirement: MS-PCCRC_R581 Site.CaptureRequirementIfIsTrue( isVerifyR581, 581, @"[In Content Information Data Structure Version 1.0] dwHashAlgo (4 bytes): MUST be one of the following values:0x0000800C,0x0000800D,0x0000800E."); #endregion #region MS-PCCRC_R59 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R59, , the actual value of dwHashAlgo is 0x{0:X8}.", valueOfDwHashAlgo); // Verify MS-PCCRC requirement: MS-PCCRTP_R59 Site.CaptureRequirementIfAreEqual <int>( 0x0000800C, valueOfDwHashAlgo, 59, @"[In Content Information Data Structure Version 1.0] dwHashAlgo (4 bytes): When use the SHA-256 hash algorithm, the value is 0x0000800C."); #endregion #region MS-PCCRC_R54 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R54, all fields are {0}in little-endian byte order", BitConverter.IsLittleEndian ? string.Empty : "not "); // Verify MS-PCCRC requirement: MS-PCCRC_R54 Site.CaptureRequirementIfIsTrue( BitConverter.IsLittleEndian, 54, @"[In Content Information Data Structure Version 1.0] All fields are in little-endian byte order."); #endregion #region MS-PCCRC_R57 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R57, the actual value of Version is 0x{0:X8}", contentInfo.Version); // Verify MS-PCCRC requirement: MS-PCCRTP_R57 Site.CaptureRequirementIfAreEqual <int>( 0x0100, contentInfo.Version, 57, @"[In Content Information Data Structure Version 1.0] Version (2 bytes): MUST be 0x0100."); #endregion #region MS-PCCRC_R53 // The Version is parsed at the first 2 byte word in Content Information by stack. // If MS-PCCRC_R57 is verified successfully, MS-PCCRC_R53 is verified and so captured directly. Site.CaptureRequirement( 53, @"[In Content Information Data Structure Version 1.0] Content Information starts with a single 2 byte WORD value representing the data structure version."); #endregion #region MS-PCCRC_R56 // The Version is parsed at the first 2 byte word in Content Information by stack. // If MS-PCCRC_R57 is verified successfully, it indicates that the low byte is the minor version number // and the high byte is the major version number. Site.CaptureRequirement( 56, @"[In Content Information Data Structure Version 1.0] Version (2 bytes): The low byte is the minor version number and the high byte is the major version number."); #endregion #region MS-PCCRC_R55 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R55, the actual size of the Version is {0}", Marshal.SizeOf(contentInfo.Version)); // Verify MS-PCCRC requirement: MS-PCCRTP_R55 Site.CaptureRequirementIfAreEqual <int>( 2, Marshal.SizeOf(contentInfo.Version), 55, @"[In Content Information Data Structure Version 1.0] Version (2 bytes): Content Information version (0x0100 is version 1.0)."); #endregion #region MS-PCCRC_R64 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R64, the actual value of the cSegments is {0}", contentInfo.cSegments); // Verify MS-PCCRC requirement: MS-PCCRC_R64 Site.CaptureRequirementIfAreEqual <int>( contentInfo.segments.Length, (int)contentInfo.cSegments, 64, @"[In Content Information Data Structure Version 1.0] cSegments (4 bytes): The number of segments which intersect the content range and hence are contained in the Content Information data structure."); #endregion #region MS-PCCRC_R65 bool isVerifyR65 = true; for (int i = 0; i < contentInfo.cSegments; i++) { if (contentInfo.segments[i].cbBlockSize == STANDARDBBLOCKSIZE && contentInfo.segments[i].SegmentHashOfData.Length == hashLength && contentInfo.segments[i].SegmentSecret.Length == hashLength) { isVerifyR65 = true; } else { isVerifyR65 = false; break; } } // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R65, the segments variable {0} contain the Segment start offset, length, block size, SegmentHashofData and SegmentSecret for each segment.", isVerifyR65 ? string.Empty : "does't"); // Verify MS-PCCRC requirement: MS-PCCRC_R65 Site.CaptureRequirementIfIsTrue( isVerifyR65, 65, @"[In Content Information Data Structure Version 1.0] segments (variable): Segment start offset, length, block size, SegmentHashofData and SegmentSecret for each segment."); #endregion }
/// <summary> /// Verify Content, Segments, and Blocks defined in section 2.1. /// </summary> /// <param name="pccrtpResponse">The HTTP resopnse.</param> private void VerifyContentSegmentsBlocks(PccrtpResponse pccrtpResponse) { Content_Information_Data_Structure contentInfo = pccrtpResponse.ContentInfo; #region MS-PCCRC_R3 // Add the debug information Site.Log.Add( LogEntryKind.Debug, "Verify MS-PCCRC_R3.segment is {0} type", pccrtpResponse.HttpHeader[ACCEPTRANGES]); bool isVerifyR3 = pccrtpResponse.HttpHeader[ACCEPTRANGES].Equals("bytes"); // Verify MS-PCCRC requirement: MS-PCCRC_R3 Site.CaptureRequirementIfIsTrue( isVerifyR3, 3, @"[In Content, Segments, and Blocks] Each segment is a binary string."); #endregion #region MS-PCCRC_R10 bool isVerifyR10 = true; for (int i = 0; i < contentInfo.cSegments - 1; i++) { if (contentInfo.segments[i].cbBlockSize != STANDARDBBLOCKSIZE) { isVerifyR10 = false; break; } } // Check for the last block in the last segment, which may be shorter than the standard block size (64 KB). if (contentInfo.segments[contentInfo.cSegments - 1].cbBlockSize > STANDARDBBLOCKSIZE) { isVerifyR10 = false; } // Add the debug information Site.Log.Add( LogEntryKind.Debug, "Verify MS-PCCRC_R10. Each block size is {0} 64 kilobytes", isVerifyR10 ? string.Empty : "not"); // Verify MS-PCCRC requirement: MS-PCCRC_R10 Site.CaptureRequirementIfIsTrue( isVerifyR10, 10, @"[In Content, Segments, and Blocks] Each block is a binary string of a fixed size (64 kilobytes), except for the last block in the last segment, which again may be shorter."); #endregion #region MS-PCCRC_R4 string allLength = string.Empty; if (pccrtpResponse.HttpHeader.ContainsKey(CONTENTRANGE)) { // Get the value of content length in the response for partial request. allLength = pccrtpResponse.HttpHeader[CONTENTRANGE]; allLength = allLength.Substring(allLength.IndexOf('/') + 1); } else if (pccrtpResponse.HttpHeader.ContainsKey(XP2PPEERDIST)) { // Get the value of content length in the response for full request. allLength = pccrtpResponse.HttpHeader[XP2PPEERDIST]; allLength = allLength.Substring(allLength.LastIndexOf('=') + 1); } bool isVerifyR4 = true; long contentLength = 0; try { contentLength = Convert.ToInt64(allLength); } catch (FormatException e) { throw new FormatException(e.ToString()); } long lastSegmentLength = 0; for (int i = 0; i < contentInfo.cSegments; i++) { // If there are multiple segments, check each segment is of a standard size (32 MB). if (contentLength > STANDARDSEGMENTSIZE) { if (contentInfo.segments[i].cbSegment != STANDARDSEGMENTSIZE) { isVerifyR4 = false; break; } contentLength -= STANDARDSEGMENTSIZE; } else { // Check for the last segment, which may be shorter than the standard segment size (32 MB). if (contentLength != contentInfo.segments[i].cbSegment) { if (i > 0) { lastSegmentLength = contentLength; } isVerifyR4 = false; break; } } } // Add the debug information Site.Log.Add( LogEntryKind.Debug, "Verify MS-PCCRC_R4.segment size is {0}32 megabytes except possibly the last segment", isVerifyR4 ? string.Empty : "not "); // Verify MS-PCCRC requirement: MS-PCCRC_R4 Site.CaptureRequirementIfIsTrue( isVerifyR4, 4, @"[In Content, Segments, and Blocks] Each segment is of a standard size (32 megabytes), except possibly the last segment which may be smaller if the content size is not a multiple of the standard segment size."); #endregion // MS-PCCRC_R63 is blocked by TDI(Techical Document Issue) about inaccurate definition of // dwReadBytesInLastSegment if the HTTP request is a range retrieval request. if (bool.Parse(Site.Properties.Get("PCCRC.IsTDI.65999.Fixed"))) { #region MS-PCCRC_R63 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R63, the actual value of the dwReadBytesInLastSegment is {0}", contentInfo.dwReadBytesInLastSegment); // Verify MS-PCCRC requirement: MS-PCCRC_R63 Site.CaptureRequirementIfAreEqual <long>( lastSegmentLength, contentInfo.dwReadBytesInLastSegment, 63, @"[In Content Information Data Structure Version 1.0] dwReadBytesInLastSegment (4 bytes): Total number of bytes of the content range which lie within the final segment in the Content Information data structure."); #endregion } }
/// <summary> /// Validate the segmentInformation In SEGMTNE_INFO_MESSAGE. /// </summary> /// <param name="segmentInformation">The segmetnInformation.</param> private void ValidateSegmentInformationFromPccrc(Content_Information_Data_Structure segmentInformation) { int valueOfDwHashAlgo = 0; int hashLength = 0; switch (segmentInformation.dwHashAlgo) { case dwHashAlgo_Values.V1: valueOfDwHashAlgo = 0x0000800C; hashLength = 32; break; case dwHashAlgo_Values.V2: hashLength = 48; valueOfDwHashAlgo = 0x0000800D; break; case dwHashAlgo_Values.V3: hashLength = 64; valueOfDwHashAlgo = 0x0000800E; break; default: break; } #region MS-PCCRC_R54 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R54, all fields are {0}in little-endian byte order", BitConverter.IsLittleEndian ? string.Empty : "not "); // Verify MS-PCCRC requirement: MS-PCCRC_R54 Site.CaptureRequirementIfIsTrue( BitConverter.IsLittleEndian, PCCRCDOCSHORTNAME, 54, @"[In Content Information Data Structure Version] All fields[Version,dwHashAlgo, dwOffsetInFirstSegment,dwReadBytesInLastSegment,cSegments,segments (variable), blocks (variable)] are in little-endian byte order."); #endregion #region MS-PCCRC_R55 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R55, the actual value of Version is 0x{0:X8}", segmentInformation.Version); // Verify MS-PCCRC requirement: MS-PCCRC_R55 Site.CaptureRequirementIfAreEqual <int>( 0x0100, segmentInformation.Version, PCCRCDOCSHORTNAME, 55, @"[In Content Information Data Structure Version] Version (2 bytes): Content Information version (0x0100 is version 1.0)."); #endregion this.ValidatePccrcSegmentInfoVersion(segmentInformation, valueOfDwHashAlgo, hashLength); this.ValidatePccrcCSegment(segmentInformation, hashLength); #region MS-PCCRC_R86 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R86, the actual number of SegmentContentBlocks fields in blocks field is {0}", segmentInformation.blocks.Length); // Verify MS-PCCRC requirement: MS-PCCRC_R86 Site.CaptureRequirementIfAreEqual <uint>( segmentInformation.cSegments, (uint)segmentInformation.blocks.Length, PCCRCDOCSHORTNAME, 86, @"[In SegmentContentBlocks] The blocks field contains a number cSegments of SegmentContentBlocks fields."); #endregion #region MS-PCCRC_R89 bool isVerifyR89 = true; foreach (SegmentContentBlocks segContentBlocks in segmentInformation.blocks) { if (segContentBlocks.BlockHashes.Length != segContentBlocks.cBlocks * hashLength) { isVerifyR89 = false; break; } } // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R89, the actual size of BlockHashes field is {0} cBlokcs * {1}", isVerifyR89 ? string.Empty : "not ", hashLength); // Verify MS-PCCRC requirement: MS-PCCRC_R89 Site.CaptureRequirementIfIsTrue( isVerifyR89, PCCRCDOCSHORTNAME, 89, @"[In SegmentContentBlocks] BlockHashes (variable):The size of this field is cBlocks * (32, 48 or 64, depending on which hash was used)."); #endregion }
private void executeButton_Click(object sender, EventArgs e) { try { if (!CheckInput()) { return; } logger.Clear(); #region Read settings from UI var version = (BranchCacheVersion)branchCacheVersionComboBox.SelectedItem; var operationMode = (OperationMode)operationModeComboBox.SelectedItem; var transport = (ContentInformationTransport)transportComboBox.SelectedItem; var serverSecret = serverSecretTextBox.Text; var filePath = filePathTextBox.Text; var hashAlgoValue = (dwHashAlgo_Values)hashAlgorithmComboBox.SelectedItem; HashAlgorithm hashAlgorithm; HMAC hmacAlgorithm; int hashBlockSize; string server = null; string file = null; string sharedFolder = null; Match filePathMatch = null; switch (transport) { case ContentInformationTransport.PCCRTP: filePathMatch = Regex.Match(filePath, httpFilePathPattern); server = filePathMatch.Groups["Server"].Value; file = filePathMatch.Groups["FileName"].Value; break; case ContentInformationTransport.SMB2: filePathMatch = Regex.Match(filePath, smb2FilePathPattern); server = filePathMatch.Groups["Server"].Value; sharedFolder = filePathMatch.Groups["SharedFolder"].Value; file = filePathMatch.Groups["FileName"].Value; break; default: throw new NotImplementedException(); } SecurityPackageType securityPackageType = (SecurityPackageType)smb2AuthenticationComboBox.SelectedItem; string domainName = domainNameTextBox.Text; string userName = userNameTextBox.Text; string userPassword = userPasswordTextBox.Text; #endregion var timeout = TimeSpan.FromSeconds(60); byte[] content; byte[] contentInformation; Content_Information_Data_Structure contentInformationStructure = new Content_Information_Data_Structure(); Content_Information_Data_Structure_V2 contentInformationStructureV2 = new Content_Information_Data_Structure_V2(); #region Read content and content information if (operationMode == OperationMode.RemoteHashVerification) { switch (transport) { case ContentInformationTransport.PCCRTP: PccrtpClient pccrtpClient = new PccrtpClient(); PccrtpRequest pccrtpRequest = pccrtpClient.CreatePccrtpRequest( server, 80, file, version); PccrtpResponse pccrtpResponse = pccrtpClient.SendHttpRequest( HttpVersionType.HttpVersion11, pccrtpRequest, (int)timeout.TotalMilliseconds); if (pccrtpResponse.HttpResponse.ContentEncoding == "peerdist") { contentInformation = pccrtpResponse.PayloadData; content = Utility.DownloadHTTPFile(server, file); } else { content = pccrtpResponse.PayloadData; Thread.Sleep(5000); // Wait for hash generation pccrtpResponse = pccrtpClient.SendHttpRequest( HttpVersionType.HttpVersion11, pccrtpRequest, (int)timeout.TotalMilliseconds); contentInformation = pccrtpResponse.PayloadData; } break; case ContentInformationTransport.SMB2: using (Smb2ClientTransport smb2Client = new Smb2ClientTransport(timeout)) { smb2Client.OpenFile( server, sharedFolder, file, securityPackageType, domainName, userName, userPassword, AccessMask.GENERIC_READ); content = smb2Client.ReadAllBytes(); Thread.Sleep(5000); // Wait for hash generation HASH_HEADER hashHeader; smb2Client.ReadHash( SRV_READ_HASH_Request_HashType_Values.SRV_HASH_TYPE_PEER_DIST, version == BranchCacheVersion.V1 ? SRV_READ_HASH_Request_HashVersion_Values.SRV_HASH_VER_1 : SRV_READ_HASH_Request_HashVersion_Values.SRV_HASH_VER_2, version == BranchCacheVersion.V1 ? SRV_READ_HASH_Request_HashRetrievalType_Values.SRV_HASH_RETRIEVE_HASH_BASED : SRV_READ_HASH_Request_HashRetrievalType_Values.SRV_HASH_RETRIEVE_FILE_BASED, 0, uint.MaxValue, out hashHeader, out contentInformation); } break; default: throw new NotImplementedException(); } switch (version) { case BranchCacheVersion.V1: contentInformationStructure = PccrcUtility.ParseContentInformation(contentInformation); break; case BranchCacheVersion.V2: contentInformationStructureV2 = PccrcUtility.ParseContentInformationV2(contentInformation); break; default: throw new NotImplementedException(); } } else { content = File.ReadAllBytes(filePath); } #endregion #region Calculate hash and execute verification switch (version) { case BranchCacheVersion.V1: if (operationMode == OperationMode.RemoteHashVerification) { PccrcUtility.GetHashAlgorithm(contentInformationStructure.dwHashAlgo, out hashAlgorithm, out hmacAlgorithm, out hashBlockSize); } else { PccrcUtility.GetHashAlgorithm(hashAlgoValue, out hashAlgorithm, out hmacAlgorithm, out hashBlockSize); } hmacAlgorithm.Key = hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(serverSecret)); logger.LogInfo( "Ks = Hash(ServerSecret): {0}", Utility.ToHexString(hmacAlgorithm.Key)); logger.NewLine(); int blockTotalCount = content.Length / BLOCKBYTECOUNT; if (content.Length > BLOCKBYTECOUNT * blockTotalCount) { blockTotalCount = blockTotalCount + 1; } int segmentCount = blockTotalCount / SEGMENTBLOCKCOUNT; if (blockTotalCount > SEGMENTBLOCKCOUNT * segmentCount) { segmentCount = segmentCount + 1; } for (int segmentIndex = 0; segmentIndex < segmentCount; segmentIndex++) { logger.LogInfo("Segment{0}", segmentIndex); logger.NewLine(); logger.Indent(); List <byte> blockHashList = new List <byte>(); List <byte> tempList = new List <byte>(); int blockCount = (segmentIndex == segmentCount - 1) ? (blockTotalCount % SEGMENTBLOCKCOUNT) : (SEGMENTBLOCKCOUNT); for (int blockIndex = 0; blockIndex < blockCount; blockIndex++) { logger.LogInfo( "Block{0} Offset {1} Length {2}", blockIndex, BLOCKBYTECOUNT * SEGMENTBLOCKCOUNT * segmentIndex + BLOCKBYTECOUNT * blockIndex, BLOCKBYTECOUNT); logger.NewLine(); logger.Indent(); var block = content.Skip(BLOCKBYTECOUNT * SEGMENTBLOCKCOUNT * segmentIndex + BLOCKBYTECOUNT * blockIndex).Take(BLOCKBYTECOUNT).ToArray(); byte[] blockHash = hashAlgorithm.ComputeHash(block); logger.LogInfo("BlockHash{0} = Hash(Block): {1}", blockIndex, Utility.ToHexString(blockHash)); if (operationMode == OperationMode.RemoteHashVerification && !blockHash.SequenceEqual(contentInformationStructure.blocks[segmentIndex].BlockHashes.Skip(blockIndex * hashBlockSize).Take(hashBlockSize))) { logger.LogError("Server Returned: {0}", Utility.ToHexString(contentInformationStructure.blocks[segmentIndex].BlockHashes.Skip(blockIndex * hashBlockSize).Take(hashBlockSize).ToArray())); } blockHashList.AddRange(blockHash); logger.Unindent(); logger.NewLine(); } byte[] hod = hashAlgorithm.ComputeHash(blockHashList.ToArray()); logger.LogInfo( "HoD = Hash(BlockHash0 + BlockHash1 + ... + BlockHashN): {0}", Utility.ToHexString(hod)); if (operationMode == OperationMode.RemoteHashVerification && !hod.SequenceEqual(contentInformationStructure.segments[segmentIndex].SegmentHashOfData)) { logger.LogError("Server Returned: {0}", Utility.ToHexString(contentInformationStructure.segments[segmentIndex].SegmentHashOfData)); } logger.NewLine(); byte[] kp = hmacAlgorithm.ComputeHash(hod); logger.LogInfo( "Kp = HMAC(Ks, HoD): {0}", Utility.ToHexString(kp)); if (operationMode == OperationMode.RemoteHashVerification && !kp.SequenceEqual(contentInformationStructure.segments[segmentIndex].SegmentSecret)) { logger.LogError("Server Returned: {0}", Utility.ToHexString(contentInformationStructure.segments[segmentIndex].SegmentSecret)); } logger.NewLine(); tempList.AddRange(hod); tempList.AddRange(Encoding.Unicode.GetBytes(HOHODK_APPEND_STRING)); byte[] hoHoDK = hashAlgorithm.ComputeHash(tempList.ToArray()); logger.LogInfo( "hoHoDK = HMAC(HoD + \"MS_P2P_CACHING\"): {0}", Utility.ToHexString(hoHoDK)); logger.NewLine(); logger.Unindent(); } break; case BranchCacheVersion.V2: PccrcUtility.GetHashAlgorithm(dwHashAlgoV2_Values.TRUNCATED_SHA512, out hashAlgorithm, out hmacAlgorithm); hmacAlgorithm.Key = hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(serverSecret)).Take(32).ToArray(); logger.LogInfo( "Ks = Hash(ServerSecret): {0}", Utility.ToHexString(hmacAlgorithm.Key)); logger.NewLine(); int segmentLength = BLOCKBYTECOUNT; int chunkCount = 1; if (operationMode == OperationMode.RemoteHashVerification) { chunkCount = contentInformationStructureV2.chunks.Length; } int segmentOffset = 0; for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++) { logger.LogInfo("Chunk{0}", chunkIndex); logger.NewLine(); logger.Indent(); segmentCount = content.Length / segmentLength; if (content.Length > segmentCount * segmentLength) { segmentCount++; } if (operationMode == OperationMode.RemoteHashVerification) { segmentCount = contentInformationStructureV2.chunks[chunkIndex].chunkData.Length; } for (int segmentIndex = 0; segmentIndex < segmentCount; ++segmentIndex) { logger.LogInfo( "Segment{0} Offset {1} Length {2}", segmentIndex, segmentOffset, BLOCKBYTECOUNT); logger.NewLine(); logger.Indent(); if (operationMode == OperationMode.RemoteHashVerification) { segmentLength = (int)contentInformationStructureV2.chunks[chunkIndex].chunkData[segmentIndex].cbSegment; } List <byte> tempList = new List <byte>(); var segment = content.Skip(segmentOffset).Take(segmentLength).ToArray(); segmentOffset += segmentLength; //TRANCATED_SHA_512 byte[] hod = hashAlgorithm.ComputeHash(segment).Take(32).ToArray(); logger.LogInfo( "HoD = Hash(Segment): {0}", Utility.ToHexString(hod)); if (operationMode == OperationMode.RemoteHashVerification && !hod.SequenceEqual(contentInformationStructureV2.chunks[chunkIndex].chunkData[segmentIndex].SegmentHashOfData)) { logger.LogError("Server Returned: {0}", Utility.ToHexString(contentInformationStructureV2.chunks[chunkIndex].chunkData[segmentIndex].SegmentHashOfData)); } logger.NewLine(); byte[] kp = hmacAlgorithm.ComputeHash(hod).Take(32).ToArray(); logger.LogInfo( "Kp = HMAC(Ks, HoD): {0}", Utility.ToHexString(kp)); if (operationMode == OperationMode.RemoteHashVerification && !kp.SequenceEqual(contentInformationStructureV2.chunks[chunkIndex].chunkData[segmentIndex].SegmentSecret)) { logger.LogError("Server Returned: {0}", Utility.ToHexString(contentInformationStructureV2.chunks[chunkIndex].chunkData[segmentIndex].SegmentSecret)); } logger.NewLine(); tempList.AddRange(hod); tempList.AddRange(Encoding.Unicode.GetBytes(HOHODK_APPEND_STRING)); byte[] hoHoDK = hashAlgorithm.ComputeHash(tempList.ToArray()); logger.LogInfo( "hoHoDK = HMAC(HoD + \"MS_P2P_CACHING\"): {0}", Utility.ToHexString(hoHoDK)); logger.NewLine(); logger.Unindent(); } } break; default: throw new NotImplementedException(); } if (operationMode == OperationMode.RemoteHashVerification) { if (logger.HasError) { Utility.ShowMessageBox("Hash verification error found!", MessageBoxIcon.Error); } else { Utility.ShowMessageBox("Hash verification passed!", MessageBoxIcon.Information); } } #endregion } catch (Exception ex) { Utility.ShowMessageBox(ex.Message + "\r\n\r\n" + ex.StackTrace, MessageBoxIcon.Error); } }
/// <summary> /// Validate pccrc segment info version part. /// </summary> /// <param name="segmentInformation">Content Information data structure.</param> /// <param name="valueOfDwHashAlgo">Value of DwHashAlgo.</param> /// <param name="hashLength">The length of hash.</param> private void ValidatePccrcSegmentInfoVersion(Content_Information_Data_Structure segmentInformation, int valueOfDwHashAlgo, int hashLength) { #region MS-PCCRC_R57 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R57, the actual value of Version is 0x{0:X8}", segmentInformation.Version); // Verify MS-PCCRC requirement: MS-PCCRC_R57 Site.CaptureRequirementIfAreEqual <int>( 0x0100, segmentInformation.Version, PCCRCDOCSHORTNAME, 57, @"[In Content Information Data Structure Version] Version (2 bytes): MUST be 0x0100."); #endregion #region MS-PCCRC_R53 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R53:Content Information starts the data structure version. The Version is parsed at the first 2 byte word in Content Information by stack. If MS-PCCRC_R57 is verified successfully, MS-PCCRC_R53 is captured directly."); Site.CaptureRequirement( PCCRCDOCSHORTNAME, 53, @"[In Content Information Data Structure Version] Content Information starts with a single 2 byte WORD value representing the data structure version."); #endregion #region MS-PCCRC_R56 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R56, Version is parsed at the first 2 byte word in Content Information by stack. If MS-PCCRC_R57 is verified successfully, it indicates that the low byte is the minor version numberand the high byte is the major version number."); Site.CaptureRequirement( PCCRCDOCSHORTNAME, 56, @"[In Content Information Data Structure Version] Version (2 bytes): The low byte is the minor version number and the high byte is the major version number."); #endregion #region MS-PCCRC_R58 // Add the debug information. Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R58, the actual length of dwHashAlgo is: {0}", Marshal.SizeOf(valueOfDwHashAlgo)); // Verify MS-PCCRC requirement: MS-PCCRC_R58 Site.CaptureRequirementIfAreEqual <int>( 4, Marshal.SizeOf(valueOfDwHashAlgo), PCCRCDOCSHORTNAME, 58, @"[In Content Information Data Structure Version 1.0] dwHashAlgo (4 bytes): Hash algorithm to use. <2> "); #endregion #region MS-PCCRC_R581 bool isVerifyR581 = valueOfDwHashAlgo == 0x0000800C || valueOfDwHashAlgo == 0x0000800D || valueOfDwHashAlgo == 0x0000800E; // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R581, the actual value of dwHashAlgo is 0x{0:X8}.", valueOfDwHashAlgo); // Verify MS-PCCRC requirement: MS-PCCRC_R581 Site.CaptureRequirementIfIsTrue( isVerifyR581, PCCRCDOCSHORTNAME, 581, @"[In Content Information Data Structure Version 1.0] dwHashAlgo (4 bytes): MUST be one of the following values:0x0000800C,0x0000800D,0x0000800E."); #endregion #region MS-PCCRC_R59 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R59, When segmentInformation.dwHashAlgo is 0x0000800C, it is the SHA-256 hash algorithm. The actual value of dwHashAlgo is 0x{0:X8}.", valueOfDwHashAlgo); // Verify MS-PCCRC requirement: MS-PCCRC_R59 Site.CaptureRequirementIfAreEqual <int>( 0x0000800C, valueOfDwHashAlgo, PCCRCDOCSHORTNAME, 59, @"[In Content Information Data Structure Version] dwHashAlgo (4 bytes): When use the SHA-256 hash algorithm, the value is 0x0000800C."); #endregion #region MS-PCCRC_R62 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R62, dwOffsetInFirstSegment (4 bytes). In MS-PCHC client, only the size of the dwOffsetInFirstSegment will be verified."); // Verify MS-PCCRC requirement: MS-PCCRC_R62 Site.CaptureRequirementIfAreEqual <int>( Marshal.SizeOf(segmentInformation.dwOffsetInFirstSegment), 4, PCCRCDOCSHORTNAME, 62, @"[In Content Information Data Structure Version] dwOffsetInFirstSegment (4 bytes): Number of bytes into the first segment within the Content Information data structure at which the content range begins."); #endregion #region MS-PCCRC_R63 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R63, dwReadBytesInLastSegment (4 bytes). In MS_PCHC client, only the size of the dwReadBytesInLastSegment will be verified."); // Verify MS-PCCRC requirement: MS-PCCRC_R63 Site.CaptureRequirementIfAreEqual <int>( Marshal.SizeOf(segmentInformation.dwReadBytesInLastSegment), 4, PCCRCDOCSHORTNAME, 63, @"[In Content Information Data Structure Version] dwReadBytesInLastSegment (4 bytes): Total number of bytes of the content range which lie within the final segment in the Content Information data structure."); #endregion #region MS-PCCRC_R64 // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R64, the actual value of the cSegments is {0}, the size of the cSegments is {1} bytes.", segmentInformation.cSegments, Marshal.SizeOf(segmentInformation.cSegments)); // Verify MS-PCCRC requirement: MS-PCCRC_R64 Site.CaptureRequirementIfAreEqual <int>( segmentInformation.segments.Length, (int)segmentInformation.cSegments, PCCRCDOCSHORTNAME, 64, @"[In Content Information Data Structure Version] cSegments (4 bytes): The number of segments which intersect the content range and hence are contained in the Content Information data structure."); #endregion #region MS-PCCRC_R65 bool isVerifyR65 = true; for (int i = 0; i < segmentInformation.cSegments; i++) { if (!(segmentInformation.segments[i].cbBlockSize == 65536 && segmentInformation.segments[i].SegmentHashOfData.Length == hashLength && segmentInformation.segments[i].SegmentSecret.Length == hashLength)) { isVerifyR65 = false; break; } } // Add the debug information Site.Log.Add( LogEntryKind.Debug, @"Verify MS-PCCRC_R65, the segments variable {0} the Segment start offset, length, block size, SegmentHashofData and SegmentSecret for each segment.", isVerifyR65 ? "contains" : "does't contain"); // Verify MS-PCCRC requirement: MS-PCCRC_R65 Site.CaptureRequirementIfIsTrue( isVerifyR65, PCCRCDOCSHORTNAME, 65, @"[In Content Information Data Structure Version] segments (variable): Segment start offset, length, block size, SegmentHashofData and SegmentSecret for each segment."); #endregion }
public void Start(int port, CryptoAlgoId_Values cryptoAlgoId, ProtoVersion protoVersion, Content_Information_Data_Structure contentInformation, byte[] content, EventQueue eventQueue) { this.contentInformation = contentInformation; base.Start(port, cryptoAlgoId, protoVersion, content, eventQueue); }