|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | // Tests on exact results from cryptographic operations are based on test data | 
|  | // provided in [MS-NLMP] Version 28.0 [1] Section 4.2. | 
|  | // | 
|  | // Additional sanity checks on the low level hashing operations test for | 
|  | // properties of the outputs, such as whether the hashes change, whether they | 
|  | // should be zeroed out, or whether they should be the same or different. | 
|  | // | 
|  | // [1] https://msdn.microsoft.com/en-us/library/cc236621.aspx | 
|  |  | 
|  | #include "net/ntlm/ntlm.h" | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "base/md5.h" | 
|  | #include "base/stl_util.h" | 
|  | #include "base/strings/string16.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "net/ntlm/ntlm_test_data.h" | 
|  | #include "starboard/memory.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace net { | 
|  | namespace ntlm { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | AvPair MakeDomainAvPair() { | 
|  | return AvPair(TargetInfoAvId::kDomainName, | 
|  | std::vector<uint8_t>{std::begin(test::kNtlmDomainRaw), | 
|  | std::end(test::kNtlmDomainRaw)}); | 
|  | } | 
|  |  | 
|  | AvPair MakeServerAvPair() { | 
|  | return AvPair(TargetInfoAvId::kServerName, | 
|  | std::vector<uint8_t>{std::begin(test::kServerRaw), | 
|  | std::end(test::kServerRaw)}); | 
|  | } | 
|  |  | 
|  | // Clear the least significant bit in each byte. | 
|  | void ClearLsb(base::span<uint8_t> data) { | 
|  | for (uint8_t& byte : data) { | 
|  | byte &= ~1; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST(NtlmTest, MapHashToDesKeysAllOnes) { | 
|  | // Test mapping an NTLM hash with all 1 bits. | 
|  | const uint8_t hash[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | 
|  | const uint8_t expected[24] = {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, | 
|  | 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, | 
|  | 0xfe, 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00}; | 
|  |  | 
|  | uint8_t result[24]; | 
|  | Create3DesKeysFromNtlmHash(hash, result); | 
|  | // The least significant bit in result from |Create3DesKeysFromNtlmHash| | 
|  | // is undefined, so clear it to do memcmp. | 
|  | ClearLsb(result); | 
|  |  | 
|  | EXPECT_EQ(base::make_span(expected), base::make_span(result)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, MapHashToDesKeysAllZeros) { | 
|  | // Test mapping an NTLM hash with all 0 bits. | 
|  | const uint8_t hash[16] = {0x00}; | 
|  | const uint8_t expected[24] = {0x00}; | 
|  |  | 
|  | uint8_t result[24]; | 
|  | Create3DesKeysFromNtlmHash(hash, result); | 
|  | // The least significant bit in result from |Create3DesKeysFromNtlmHash| | 
|  | // is undefined, so clear it to do memcmp. | 
|  | ClearLsb(result); | 
|  |  | 
|  | EXPECT_EQ(base::make_span(expected), base::make_span(result)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, MapHashToDesKeysAlternatingBits) { | 
|  | // Test mapping an NTLM hash with alternating 0 and 1 bits. | 
|  | const uint8_t hash[16] = {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, | 
|  | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; | 
|  | const uint8_t expected[24] = {0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, | 
|  | 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, | 
|  | 0xaa, 0x54, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00}; | 
|  |  | 
|  | uint8_t result[24]; | 
|  | Create3DesKeysFromNtlmHash(hash, result); | 
|  | // The least significant bit in result from |Create3DesKeysFromNtlmHash| | 
|  | // is undefined, so clear it to do memcmp. | 
|  | ClearLsb(result); | 
|  |  | 
|  | EXPECT_EQ(base::make_span(expected), base::make_span(result)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateNtlmHashV1PasswordSpecTests) { | 
|  | uint8_t hash[kNtlmHashLen]; | 
|  | GenerateNtlmHashV1(test::kPassword, hash); | 
|  | ASSERT_EQ(0, SbMemoryCompare(hash, test::kExpectedNtlmHashV1, kNtlmHashLen)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateNtlmHashV1PasswordChangesHash) { | 
|  | base::string16 password1 = base::UTF8ToUTF16("pwd01"); | 
|  | base::string16 password2 = base::UTF8ToUTF16("pwd02"); | 
|  | uint8_t hash1[kNtlmHashLen]; | 
|  | uint8_t hash2[kNtlmHashLen]; | 
|  |  | 
|  | GenerateNtlmHashV1(password1, hash1); | 
|  | GenerateNtlmHashV1(password2, hash2); | 
|  |  | 
|  | // Verify that the hash is different with a different password. | 
|  | ASSERT_NE(0, SbMemoryCompare(hash1, hash2, kNtlmHashLen)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateResponsesV1SpecTests) { | 
|  | uint8_t lm_response[kResponseLenV1]; | 
|  | uint8_t ntlm_response[kResponseLenV1]; | 
|  | GenerateResponsesV1(test::kPassword, test::kServerChallenge, lm_response, | 
|  | ntlm_response); | 
|  |  | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedNtlmResponseV1, ntlm_response, | 
|  | kResponseLenV1)); | 
|  |  | 
|  | // This implementation never sends an LMv1 response (spec equivalent of the | 
|  | // client variable NoLMResponseNTLMv1 being false) so the LM response is | 
|  | // equal to the NTLM response when | 
|  | // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY is not negotiated. See | 
|  | // [MS-NLMP] Section 3.3.1. | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedNtlmResponseV1, lm_response, | 
|  | kResponseLenV1)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateResponsesV1WithSessionSecuritySpecTests) { | 
|  | uint8_t lm_response[kResponseLenV1]; | 
|  | uint8_t ntlm_response[kResponseLenV1]; | 
|  | GenerateResponsesV1WithSessionSecurity( | 
|  | test::kPassword, test::kServerChallenge, test::kClientChallenge, | 
|  | lm_response, ntlm_response); | 
|  |  | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedLmResponseWithV1SS, lm_response, | 
|  | kResponseLenV1)); | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedNtlmResponseWithV1SS, | 
|  | ntlm_response, kResponseLenV1)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateResponsesV1WithSessionSecurityClientChallengeUsed) { | 
|  | uint8_t lm_response1[kResponseLenV1]; | 
|  | uint8_t lm_response2[kResponseLenV1]; | 
|  | uint8_t ntlm_response1[kResponseLenV1]; | 
|  | uint8_t ntlm_response2[kResponseLenV1]; | 
|  | uint8_t client_challenge1[kChallengeLen]; | 
|  | uint8_t client_challenge2[kChallengeLen]; | 
|  |  | 
|  | SbMemorySet(client_challenge1, 0x01, kChallengeLen); | 
|  | SbMemorySet(client_challenge2, 0x02, kChallengeLen); | 
|  |  | 
|  | GenerateResponsesV1WithSessionSecurity( | 
|  | test::kPassword, test::kServerChallenge, client_challenge1, lm_response1, | 
|  | ntlm_response1); | 
|  | GenerateResponsesV1WithSessionSecurity( | 
|  | test::kPassword, test::kServerChallenge, client_challenge2, lm_response2, | 
|  | ntlm_response2); | 
|  |  | 
|  | // The point of session security is that the client can introduce some | 
|  | // randomness, so verify different client_challenge gives a different result. | 
|  | ASSERT_NE(0, SbMemoryCompare(lm_response1, lm_response2, kResponseLenV1)); | 
|  | ASSERT_NE(0, SbMemoryCompare(ntlm_response1, ntlm_response2, kResponseLenV1)); | 
|  |  | 
|  | // With session security the lm and ntlm hash should be different. | 
|  | ASSERT_NE(0, SbMemoryCompare(lm_response1, ntlm_response1, kResponseLenV1)); | 
|  | ASSERT_NE(0, SbMemoryCompare(lm_response2, ntlm_response2, kResponseLenV1)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateResponsesV1WithSessionSecurityVerifySSUsed) { | 
|  | uint8_t lm_response1[kResponseLenV1]; | 
|  | uint8_t lm_response2[kResponseLenV1]; | 
|  | uint8_t ntlm_response1[kResponseLenV1]; | 
|  | uint8_t ntlm_response2[kResponseLenV1]; | 
|  |  | 
|  | GenerateResponsesV1WithSessionSecurity( | 
|  | test::kPassword, test::kServerChallenge, test::kClientChallenge, | 
|  | lm_response1, ntlm_response1); | 
|  | GenerateResponsesV1(test::kPassword, test::kServerChallenge, lm_response2, | 
|  | ntlm_response2); | 
|  |  | 
|  | // Verify that the responses with session security are not the | 
|  | // same as without it. | 
|  | ASSERT_NE(0, SbMemoryCompare(lm_response1, lm_response2, kResponseLenV1)); | 
|  | ASSERT_NE(0, SbMemoryCompare(ntlm_response1, ntlm_response2, kResponseLenV1)); | 
|  | } | 
|  |  | 
|  | // ------------------------------------------------ | 
|  | // NTLM V2 specific tests. | 
|  | // ------------------------------------------------ | 
|  |  | 
|  | TEST(NtlmTest, GenerateNtlmHashV2SpecTests) { | 
|  | uint8_t hash[kNtlmHashLen]; | 
|  | GenerateNtlmHashV2(test::kNtlmDomain, test::kUser, test::kPassword, hash); | 
|  | ASSERT_EQ(0, SbMemoryCompare(hash, test::kExpectedNtlmHashV2, kNtlmHashLen)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateProofInputV2SpecTests) { | 
|  | std::vector<uint8_t> proof_input; | 
|  | proof_input = | 
|  | GenerateProofInputV2(test::kServerTimestamp, test::kClientChallenge); | 
|  | ASSERT_EQ(kProofInputLenV2, proof_input.size()); | 
|  |  | 
|  | // |GenerateProofInputV2| generates the first |kProofInputLenV2| bytes of | 
|  | // what [MS-NLMP] calls "temp". | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedTempFromSpecV2, | 
|  | proof_input.data(), proof_input.size())); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateNtlmProofV2SpecTests) { | 
|  | // Only the first |kProofInputLenV2| bytes of |test::kExpectedTempFromSpecV2| | 
|  | // are read and this is equivalent to the output of |GenerateProofInputV2|. | 
|  | // See |GenerateProofInputV2SpecTests| for validation. | 
|  | uint8_t v2_proof[kNtlmProofLenV2]; | 
|  | GenerateNtlmProofV2(test::kExpectedNtlmHashV2, test::kServerChallenge, | 
|  | base::make_span(test::kExpectedTempFromSpecV2) | 
|  | .subspan<0, kProofInputLenV2>(), | 
|  | test::kExpectedTargetInfoFromSpecV2, v2_proof); | 
|  |  | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedProofFromSpecV2, v2_proof, | 
|  | kNtlmProofLenV2)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateSessionBaseKeyV2SpecTests) { | 
|  | // Generate the session base key. | 
|  | uint8_t session_base_key[kSessionKeyLenV2]; | 
|  | GenerateSessionBaseKeyV2(test::kExpectedNtlmHashV2, | 
|  | test::kExpectedProofFromSpecV2, session_base_key); | 
|  |  | 
|  | // Verify the session base key. | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedSessionBaseKeyFromSpecV2, | 
|  | session_base_key, kSessionKeyLenV2)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateSessionBaseKeyWithClientTimestampV2SpecTests) { | 
|  | // Generate the session base key. | 
|  | uint8_t session_base_key[kSessionKeyLenV2]; | 
|  | GenerateSessionBaseKeyV2( | 
|  | test::kExpectedNtlmHashV2, | 
|  | test::kExpectedProofSpecResponseWithClientTimestampV2, session_base_key); | 
|  |  | 
|  | // Verify the session base key. | 
|  | ASSERT_EQ(0, | 
|  | SbMemoryCompare(test::kExpectedSessionBaseKeyWithClientTimestampV2, | 
|  | session_base_key, kSessionKeyLenV2)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateChannelBindingHashV2SpecTests) { | 
|  | uint8_t v2_channel_binding_hash[kChannelBindingsHashLen]; | 
|  | GenerateChannelBindingHashV2(test::kChannelBindings, v2_channel_binding_hash); | 
|  |  | 
|  | ASSERT_EQ(0, | 
|  | SbMemoryCompare(test::kExpectedChannelBindingHashV2, | 
|  | v2_channel_binding_hash, kChannelBindingsHashLen)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateMicV2Simple) { | 
|  | // The MIC is defined as HMAC_MD5(session_base_key, CONCAT(a, b, c)) where | 
|  | // a, b, c are the negotiate, challenge and authenticate messages | 
|  | // respectively. | 
|  | // | 
|  | // This compares a simple set of inputs to a precalculated result. | 
|  | const std::vector<uint8_t> a{0x44, 0x44, 0x44, 0x44}; | 
|  | const std::vector<uint8_t> b{0x66, 0x66, 0x66, 0x66, 0x66, 0x66}; | 
|  | const std::vector<uint8_t> c{0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}; | 
|  |  | 
|  | // expected_mic = HMAC_MD5( | 
|  | //          key=8de40ccadbc14a82f15cb0ad0de95ca3, | 
|  | //          input=444444446666666666668888888888888888) | 
|  | uint8_t expected_mic[kMicLenV2] = {0x71, 0xfe, 0xef, 0xd7, 0x76, 0xd4, | 
|  | 0x42, 0xa8, 0x5f, 0x6e, 0x18, 0x0a, | 
|  | 0x6b, 0x02, 0x47, 0x20}; | 
|  |  | 
|  | uint8_t mic[kMicLenV2]; | 
|  | #ifdef STARBOARD | 
|  | GenerateMicV2(test::kExpectedSessionBaseKeyFromSpecV2, | 
|  | base::span<const uint8_t>(a.data(), a.size()), | 
|  | base::span<const uint8_t>(b.data(), b.size()), | 
|  | base::span<const uint8_t>(c.data(), c.size()), mic); | 
|  | #else | 
|  | GenerateMicV2(test::kExpectedSessionBaseKeyFromSpecV2, a, b, c, mic); | 
|  | #endif | 
|  | ASSERT_EQ(0, SbMemoryCompare(expected_mic, mic, kMicLenV2)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateMicSpecResponseV2) { | 
|  | std::vector<uint8_t> authenticate_msg( | 
|  | std::begin(test::kExpectedAuthenticateMsgSpecResponseV2), | 
|  | std::end(test::kExpectedAuthenticateMsgSpecResponseV2)); | 
|  | SbMemorySet(&authenticate_msg[kMicOffsetV2], 0x00, kMicLenV2); | 
|  |  | 
|  | uint8_t mic[kMicLenV2]; | 
|  | #ifdef STARBOARD | 
|  | GenerateMicV2( | 
|  | test::kExpectedSessionBaseKeyWithClientTimestampV2, | 
|  | base::span<const uint8_t>(test::kExpectedNegotiateMsg, | 
|  | sizeof(test::kExpectedNegotiateMsg)), | 
|  | base::span<const uint8_t>(test::kChallengeMsgFromSpecV2, | 
|  | sizeof(test::kChallengeMsgFromSpecV2)), | 
|  | base::span<const uint8_t>(authenticate_msg.data(), | 
|  | authenticate_msg.size()), | 
|  | mic); | 
|  | #else | 
|  | GenerateMicV2(test::kExpectedSessionBaseKeyWithClientTimestampV2, | 
|  | test::kExpectedNegotiateMsg, test::kChallengeMsgFromSpecV2, | 
|  | authenticate_msg, mic); | 
|  | #endif | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedMicV2, mic, kMicLenV2)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateUpdatedTargetInfo) { | 
|  | // This constructs a std::vector<AvPair> that corresponds to the test input | 
|  | // values in [MS-NLMP] Section 4.2.4. | 
|  | std::vector<AvPair> server_av_pairs; | 
|  | server_av_pairs.push_back(MakeDomainAvPair()); | 
|  | server_av_pairs.push_back(MakeServerAvPair()); | 
|  |  | 
|  | uint64_t server_timestamp = UINT64_MAX; | 
|  | std::vector<uint8_t> updated_target_info = GenerateUpdatedTargetInfo( | 
|  | true, true, test::kChannelBindings, test::kNtlmSpn, server_av_pairs, | 
|  | &server_timestamp); | 
|  |  | 
|  | // With MIC and EPA enabled 3 additional AvPairs will be added. | 
|  | // 1) A flags AVPair with the MIC_PRESENT bit set. | 
|  | // 2) A channel bindings AVPair containing the channel bindings hash. | 
|  | // 3) A target name AVPair containing the SPN of the server. | 
|  | ASSERT_EQ(base::size(test::kExpectedTargetInfoSpecResponseV2), | 
|  | updated_target_info.size()); | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedTargetInfoSpecResponseV2, | 
|  | updated_target_info.data(), | 
|  | updated_target_info.size())); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateUpdatedTargetInfoNoEpaOrMic) { | 
|  | // This constructs a std::vector<AvPair> that corresponds to the test input | 
|  | // values in [MS-NLMP] Section 4.2.4. | 
|  | std::vector<AvPair> server_av_pairs; | 
|  | server_av_pairs.push_back(MakeDomainAvPair()); | 
|  | server_av_pairs.push_back(MakeServerAvPair()); | 
|  |  | 
|  | uint64_t server_timestamp = UINT64_MAX; | 
|  |  | 
|  | // When both EPA and MIC are false the target info does not get modified by | 
|  | // the client. | 
|  | std::vector<uint8_t> updated_target_info = GenerateUpdatedTargetInfo( | 
|  | false, false, test::kChannelBindings, test::kNtlmSpn, server_av_pairs, | 
|  | &server_timestamp); | 
|  | ASSERT_EQ(base::size(test::kExpectedTargetInfoFromSpecV2), | 
|  | updated_target_info.size()); | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedTargetInfoFromSpecV2, | 
|  | updated_target_info.data(), | 
|  | updated_target_info.size())); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateUpdatedTargetInfoWithServerTimestamp) { | 
|  | // This constructs a std::vector<AvPair> that corresponds to the test input | 
|  | // values in [MS-NLMP] Section 4.2.4 with an additional server timestamp. | 
|  | std::vector<AvPair> server_av_pairs; | 
|  | server_av_pairs.push_back(MakeDomainAvPair()); | 
|  | server_av_pairs.push_back(MakeServerAvPair()); | 
|  |  | 
|  | // Set the timestamp to |test::kServerTimestamp| and the buffer to all zeros. | 
|  | AvPair pair(TargetInfoAvId::kTimestamp, | 
|  | std::vector<uint8_t>(sizeof(uint64_t), 0)); | 
|  | pair.timestamp = test::kServerTimestamp; | 
|  | server_av_pairs.push_back(std::move(pair)); | 
|  |  | 
|  | uint64_t server_timestamp = UINT64_MAX; | 
|  | // When both EPA and MIC are false the target info does not get modified by | 
|  | // the client. | 
|  | std::vector<uint8_t> updated_target_info = GenerateUpdatedTargetInfo( | 
|  | false, false, test::kChannelBindings, test::kNtlmSpn, server_av_pairs, | 
|  | &server_timestamp); | 
|  | // Verify that the server timestamp was read from the target info. | 
|  | ASSERT_EQ(test::kServerTimestamp, server_timestamp); | 
|  | ASSERT_EQ(base::size(test::kExpectedTargetInfoFromSpecPlusServerTimestampV2), | 
|  | updated_target_info.size()); | 
|  | ASSERT_EQ(0, SbMemoryCompare( | 
|  | test::kExpectedTargetInfoFromSpecPlusServerTimestampV2, | 
|  | updated_target_info.data(), updated_target_info.size())); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateUpdatedTargetInfoWhenServerSendsNoTargetInfo) { | 
|  | // In some older implementations the server supports NTLMv2 but does not | 
|  | // send target info. This manifests as an empty list of AvPairs. | 
|  | std::vector<AvPair> server_av_pairs; | 
|  |  | 
|  | uint64_t server_timestamp = UINT64_MAX; | 
|  | std::vector<uint8_t> updated_target_info = GenerateUpdatedTargetInfo( | 
|  | true, true, test::kChannelBindings, test::kNtlmSpn, server_av_pairs, | 
|  | &server_timestamp); | 
|  |  | 
|  | // With MIC and EPA enabled 3 additional AvPairs will be added. | 
|  | // 1) A flags AVPair with the MIC_PRESENT bit set. | 
|  | // 2) A channel bindings AVPair containing the channel bindings hash. | 
|  | // 3) A target name AVPair containing the SPN of the server. | 
|  | // | 
|  | // Compared to the spec example in |GenerateUpdatedTargetInfo| the result | 
|  | // is the same but with the first 32 bytes (which were the Domain and | 
|  | // Server pairs) not present. | 
|  | const size_t kMissingServerPairsLength = 32; | 
|  |  | 
|  | ASSERT_EQ(base::size(test::kExpectedTargetInfoSpecResponseV2) - | 
|  | kMissingServerPairsLength, | 
|  | updated_target_info.size()); | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedTargetInfoSpecResponseV2 + | 
|  | kMissingServerPairsLength, | 
|  | updated_target_info.data(), | 
|  | updated_target_info.size())); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateNtlmProofV2) { | 
|  | uint8_t proof[kNtlmProofLenV2]; | 
|  |  | 
|  | GenerateNtlmProofV2(test::kExpectedNtlmHashV2, test::kServerChallenge, | 
|  | base::make_span(test::kExpectedTempFromSpecV2) | 
|  | .subspan<0, kProofInputLenV2>(), | 
|  | test::kExpectedTargetInfoSpecResponseV2, proof); | 
|  | ASSERT_EQ(0, SbMemoryCompare(test::kExpectedProofSpecResponseV2, proof, | 
|  | kNtlmProofLenV2)); | 
|  | } | 
|  |  | 
|  | TEST(NtlmTest, GenerateNtlmProofWithClientTimestampV2) { | 
|  | uint8_t proof[kNtlmProofLenV2]; | 
|  |  | 
|  | // Since the test data for "temp" in the spec does not include the client | 
|  | // timestamp, a separate proof test value must be validated for use in full | 
|  | // message validation. | 
|  | GenerateNtlmProofV2(test::kExpectedNtlmHashV2, test::kServerChallenge, | 
|  | base::make_span(test::kExpectedTempWithClientTimestampV2) | 
|  | .subspan<0, kProofInputLenV2>(), | 
|  | test::kExpectedTargetInfoSpecResponseV2, proof); | 
|  | ASSERT_EQ( | 
|  | 0, SbMemoryCompare(test::kExpectedProofSpecResponseWithClientTimestampV2, | 
|  | proof, kNtlmProofLenV2)); | 
|  | } | 
|  |  | 
|  | }  // namespace ntlm | 
|  | }  // namespace net |