• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 // Tests on exact results from cryptographic operations are based on test data
11 // provided in [MS-NLMP] Version 28.0 [1] Section 4.2.
12 //
13 // Additional sanity checks on the low level hashing operations test for
14 // properties of the outputs, such as whether the hashes change, whether they
15 // should be zeroed out, or whether they should be the same or different.
16 //
17 // [1] https://msdn.microsoft.com/en-us/library/cc236621.aspx
18 
19 #include "net/ntlm/ntlm.h"
20 
21 #include <iterator>
22 #include <string>
23 
24 #include "base/ranges/algorithm.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "net/ntlm/ntlm_test_data.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 
29 namespace net::ntlm {
30 
31 namespace {
32 
MakeDomainAvPair()33 AvPair MakeDomainAvPair() {
34   return AvPair(TargetInfoAvId::kDomainName,
35                 std::vector<uint8_t>{std::begin(test::kNtlmDomainRaw),
36                                      std::end(test::kNtlmDomainRaw)});
37 }
38 
MakeServerAvPair()39 AvPair MakeServerAvPair() {
40   return AvPair(TargetInfoAvId::kServerName,
41                 std::vector<uint8_t>{std::begin(test::kServerRaw),
42                                      std::end(test::kServerRaw)});
43 }
44 
45 // Clear the least significant bit in each byte.
ClearLsb(base::span<uint8_t> data)46 void ClearLsb(base::span<uint8_t> data) {
47   for (uint8_t& byte : data) {
48     byte &= ~1;
49   }
50 }
51 
52 }  // namespace
53 
TEST(NtlmTest,MapHashToDesKeysAllOnes)54 TEST(NtlmTest, MapHashToDesKeysAllOnes) {
55   // Test mapping an NTLM hash with all 1 bits.
56   const uint8_t hash[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
57                             0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
58   const uint8_t expected[24] = {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
59                                 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
60                                 0xfe, 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00};
61 
62   uint8_t result[24];
63   Create3DesKeysFromNtlmHash(hash, result);
64   // The least significant bit in result from |Create3DesKeysFromNtlmHash|
65   // is undefined, so clear it to do memcmp.
66   ClearLsb(result);
67 
68   EXPECT_TRUE(base::ranges::equal(expected, result));
69 }
70 
TEST(NtlmTest,MapHashToDesKeysAllZeros)71 TEST(NtlmTest, MapHashToDesKeysAllZeros) {
72   // Test mapping an NTLM hash with all 0 bits.
73   const uint8_t hash[16] = {0x00};
74   const uint8_t expected[24] = {0x00};
75 
76   uint8_t result[24];
77   Create3DesKeysFromNtlmHash(hash, result);
78   // The least significant bit in result from |Create3DesKeysFromNtlmHash|
79   // is undefined, so clear it to do memcmp.
80   ClearLsb(result);
81 
82   EXPECT_TRUE(base::ranges::equal(expected, result));
83 }
84 
TEST(NtlmTest,MapHashToDesKeysAlternatingBits)85 TEST(NtlmTest, MapHashToDesKeysAlternatingBits) {
86   // Test mapping an NTLM hash with alternating 0 and 1 bits.
87   const uint8_t hash[16] = {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
88                             0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa};
89   const uint8_t expected[24] = {0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54,
90                                 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54,
91                                 0xaa, 0x54, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00};
92 
93   uint8_t result[24];
94   Create3DesKeysFromNtlmHash(hash, result);
95   // The least significant bit in result from |Create3DesKeysFromNtlmHash|
96   // is undefined, so clear it to do memcmp.
97   ClearLsb(result);
98 
99   EXPECT_TRUE(base::ranges::equal(expected, result));
100 }
101 
TEST(NtlmTest,GenerateNtlmHashV1PasswordSpecTests)102 TEST(NtlmTest, GenerateNtlmHashV1PasswordSpecTests) {
103   uint8_t hash[kNtlmHashLen];
104   GenerateNtlmHashV1(test::kPassword, hash);
105   ASSERT_EQ(0, memcmp(hash, test::kExpectedNtlmHashV1, kNtlmHashLen));
106 }
107 
TEST(NtlmTest,GenerateNtlmHashV1PasswordChangesHash)108 TEST(NtlmTest, GenerateNtlmHashV1PasswordChangesHash) {
109   std::u16string password1 = u"pwd01";
110   std::u16string password2 = u"pwd02";
111   uint8_t hash1[kNtlmHashLen];
112   uint8_t hash2[kNtlmHashLen];
113 
114   GenerateNtlmHashV1(password1, hash1);
115   GenerateNtlmHashV1(password2, hash2);
116 
117   // Verify that the hash is different with a different password.
118   ASSERT_NE(0, memcmp(hash1, hash2, kNtlmHashLen));
119 }
120 
TEST(NtlmTest,GenerateResponsesV1SpecTests)121 TEST(NtlmTest, GenerateResponsesV1SpecTests) {
122   uint8_t lm_response[kResponseLenV1];
123   uint8_t ntlm_response[kResponseLenV1];
124   GenerateResponsesV1(test::kPassword, test::kServerChallenge, lm_response,
125                       ntlm_response);
126 
127   ASSERT_EQ(
128       0, memcmp(test::kExpectedNtlmResponseV1, ntlm_response, kResponseLenV1));
129 
130   // This implementation never sends an LMv1 response (spec equivalent of the
131   // client variable NoLMResponseNTLMv1 being false) so the LM response is
132   // equal to the NTLM response when
133   // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY is not negotiated. See
134   // [MS-NLMP] Section 3.3.1.
135   ASSERT_EQ(0,
136             memcmp(test::kExpectedNtlmResponseV1, lm_response, kResponseLenV1));
137 }
138 
TEST(NtlmTest,GenerateResponsesV1WithSessionSecuritySpecTests)139 TEST(NtlmTest, GenerateResponsesV1WithSessionSecuritySpecTests) {
140   uint8_t lm_response[kResponseLenV1];
141   uint8_t ntlm_response[kResponseLenV1];
142   GenerateResponsesV1WithSessionSecurity(
143       test::kPassword, test::kServerChallenge, test::kClientChallenge,
144       lm_response, ntlm_response);
145 
146   ASSERT_EQ(0, memcmp(test::kExpectedLmResponseWithV1SS, lm_response,
147                       kResponseLenV1));
148   ASSERT_EQ(0, memcmp(test::kExpectedNtlmResponseWithV1SS, ntlm_response,
149                       kResponseLenV1));
150 }
151 
TEST(NtlmTest,GenerateResponsesV1WithSessionSecurityClientChallengeUsed)152 TEST(NtlmTest, GenerateResponsesV1WithSessionSecurityClientChallengeUsed) {
153   uint8_t lm_response1[kResponseLenV1];
154   uint8_t lm_response2[kResponseLenV1];
155   uint8_t ntlm_response1[kResponseLenV1];
156   uint8_t ntlm_response2[kResponseLenV1];
157   uint8_t client_challenge1[kChallengeLen];
158   uint8_t client_challenge2[kChallengeLen];
159 
160   memset(client_challenge1, 0x01, kChallengeLen);
161   memset(client_challenge2, 0x02, kChallengeLen);
162 
163   GenerateResponsesV1WithSessionSecurity(
164       test::kPassword, test::kServerChallenge, client_challenge1, lm_response1,
165       ntlm_response1);
166   GenerateResponsesV1WithSessionSecurity(
167       test::kPassword, test::kServerChallenge, client_challenge2, lm_response2,
168       ntlm_response2);
169 
170   // The point of session security is that the client can introduce some
171   // randomness, so verify different client_challenge gives a different result.
172   ASSERT_NE(0, memcmp(lm_response1, lm_response2, kResponseLenV1));
173   ASSERT_NE(0, memcmp(ntlm_response1, ntlm_response2, kResponseLenV1));
174 
175   // With session security the lm and ntlm hash should be different.
176   ASSERT_NE(0, memcmp(lm_response1, ntlm_response1, kResponseLenV1));
177   ASSERT_NE(0, memcmp(lm_response2, ntlm_response2, kResponseLenV1));
178 }
179 
TEST(NtlmTest,GenerateResponsesV1WithSessionSecurityVerifySSUsed)180 TEST(NtlmTest, GenerateResponsesV1WithSessionSecurityVerifySSUsed) {
181   uint8_t lm_response1[kResponseLenV1];
182   uint8_t lm_response2[kResponseLenV1];
183   uint8_t ntlm_response1[kResponseLenV1];
184   uint8_t ntlm_response2[kResponseLenV1];
185 
186   GenerateResponsesV1WithSessionSecurity(
187       test::kPassword, test::kServerChallenge, test::kClientChallenge,
188       lm_response1, ntlm_response1);
189   GenerateResponsesV1(test::kPassword, test::kServerChallenge, lm_response2,
190                       ntlm_response2);
191 
192   // Verify that the responses with session security are not the
193   // same as without it.
194   ASSERT_NE(0, memcmp(lm_response1, lm_response2, kResponseLenV1));
195   ASSERT_NE(0, memcmp(ntlm_response1, ntlm_response2, kResponseLenV1));
196 }
197 
198 // ------------------------------------------------
199 // NTLM V2 specific tests.
200 // ------------------------------------------------
201 
TEST(NtlmTest,GenerateNtlmHashV2SpecTests)202 TEST(NtlmTest, GenerateNtlmHashV2SpecTests) {
203   uint8_t hash[kNtlmHashLen];
204   GenerateNtlmHashV2(test::kNtlmDomain, test::kUser, test::kPassword, hash);
205   ASSERT_EQ(0, memcmp(hash, test::kExpectedNtlmHashV2, kNtlmHashLen));
206 }
207 
TEST(NtlmTest,GenerateProofInputV2SpecTests)208 TEST(NtlmTest, GenerateProofInputV2SpecTests) {
209   std::vector<uint8_t> proof_input;
210   proof_input =
211       GenerateProofInputV2(test::kServerTimestamp, test::kClientChallenge);
212   ASSERT_EQ(kProofInputLenV2, proof_input.size());
213 
214   // |GenerateProofInputV2| generates the first |kProofInputLenV2| bytes of
215   // what [MS-NLMP] calls "temp".
216   ASSERT_EQ(0, memcmp(test::kExpectedTempFromSpecV2, proof_input.data(),
217                       proof_input.size()));
218 }
219 
TEST(NtlmTest,GenerateNtlmProofV2SpecTests)220 TEST(NtlmTest, GenerateNtlmProofV2SpecTests) {
221   // Only the first |kProofInputLenV2| bytes of |test::kExpectedTempFromSpecV2|
222   // are read and this is equivalent to the output of |GenerateProofInputV2|.
223   // See |GenerateProofInputV2SpecTests| for validation.
224   uint8_t v2_proof[kNtlmProofLenV2];
225   GenerateNtlmProofV2(
226       test::kExpectedNtlmHashV2, test::kServerChallenge,
227       base::span(test::kExpectedTempFromSpecV2).first<kProofInputLenV2>(),
228       test::kExpectedTargetInfoFromSpecV2, v2_proof);
229 
230   ASSERT_EQ(0,
231             memcmp(test::kExpectedProofFromSpecV2, v2_proof, kNtlmProofLenV2));
232 }
233 
TEST(NtlmTest,GenerateSessionBaseKeyV2SpecTests)234 TEST(NtlmTest, GenerateSessionBaseKeyV2SpecTests) {
235   // Generate the session base key.
236   uint8_t session_base_key[kSessionKeyLenV2];
237   GenerateSessionBaseKeyV2(test::kExpectedNtlmHashV2,
238                            test::kExpectedProofFromSpecV2, session_base_key);
239 
240   // Verify the session base key.
241   ASSERT_EQ(0, memcmp(test::kExpectedSessionBaseKeyFromSpecV2, session_base_key,
242                       kSessionKeyLenV2));
243 }
244 
TEST(NtlmTest,GenerateSessionBaseKeyWithClientTimestampV2SpecTests)245 TEST(NtlmTest, GenerateSessionBaseKeyWithClientTimestampV2SpecTests) {
246   // Generate the session base key.
247   uint8_t session_base_key[kSessionKeyLenV2];
248   GenerateSessionBaseKeyV2(
249       test::kExpectedNtlmHashV2,
250       test::kExpectedProofSpecResponseWithClientTimestampV2, session_base_key);
251 
252   // Verify the session base key.
253   ASSERT_EQ(0, memcmp(test::kExpectedSessionBaseKeyWithClientTimestampV2,
254                       session_base_key, kSessionKeyLenV2));
255 }
256 
TEST(NtlmTest,GenerateChannelBindingHashV2SpecTests)257 TEST(NtlmTest, GenerateChannelBindingHashV2SpecTests) {
258   uint8_t v2_channel_binding_hash[kChannelBindingsHashLen];
259   GenerateChannelBindingHashV2(
260       reinterpret_cast<const char*>(test::kChannelBindings),
261       v2_channel_binding_hash);
262 
263   ASSERT_EQ(0, memcmp(test::kExpectedChannelBindingHashV2,
264                       v2_channel_binding_hash, kChannelBindingsHashLen));
265 }
266 
TEST(NtlmTest,GenerateMicV2Simple)267 TEST(NtlmTest, GenerateMicV2Simple) {
268   // The MIC is defined as HMAC_MD5(session_base_key, CONCAT(a, b, c)) where
269   // a, b, c are the negotiate, challenge and authenticate messages
270   // respectively.
271   //
272   // This compares a simple set of inputs to a precalculated result.
273   const std::vector<uint8_t> a{0x44, 0x44, 0x44, 0x44};
274   const std::vector<uint8_t> b{0x66, 0x66, 0x66, 0x66, 0x66, 0x66};
275   const std::vector<uint8_t> c{0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88};
276 
277   // expected_mic = HMAC_MD5(
278   //          key=8de40ccadbc14a82f15cb0ad0de95ca3,
279   //          input=444444446666666666668888888888888888)
280   uint8_t expected_mic[kMicLenV2] = {0x71, 0xfe, 0xef, 0xd7, 0x76, 0xd4,
281                                      0x42, 0xa8, 0x5f, 0x6e, 0x18, 0x0a,
282                                      0x6b, 0x02, 0x47, 0x20};
283 
284   uint8_t mic[kMicLenV2];
285   GenerateMicV2(test::kExpectedSessionBaseKeyFromSpecV2, a, b, c, mic);
286   ASSERT_EQ(0, memcmp(expected_mic, mic, kMicLenV2));
287 }
288 
TEST(NtlmTest,GenerateMicSpecResponseV2)289 TEST(NtlmTest, GenerateMicSpecResponseV2) {
290   std::vector<uint8_t> authenticate_msg(
291       std::begin(test::kExpectedAuthenticateMsgSpecResponseV2),
292       std::end(test::kExpectedAuthenticateMsgSpecResponseV2));
293   memset(&authenticate_msg[kMicOffsetV2], 0x00, kMicLenV2);
294 
295   uint8_t mic[kMicLenV2];
296   GenerateMicV2(test::kExpectedSessionBaseKeyWithClientTimestampV2,
297                 test::kExpectedNegotiateMsg, test::kChallengeMsgFromSpecV2,
298                 authenticate_msg, mic);
299   ASSERT_EQ(0, memcmp(test::kExpectedMicV2, mic, kMicLenV2));
300 }
301 
TEST(NtlmTest,GenerateUpdatedTargetInfo)302 TEST(NtlmTest, GenerateUpdatedTargetInfo) {
303   // This constructs a std::vector<AvPair> that corresponds to the test input
304   // values in [MS-NLMP] Section 4.2.4.
305   std::vector<AvPair> server_av_pairs;
306   server_av_pairs.push_back(MakeDomainAvPair());
307   server_av_pairs.push_back(MakeServerAvPair());
308 
309   uint64_t server_timestamp = UINT64_MAX;
310   std::vector<uint8_t> updated_target_info = GenerateUpdatedTargetInfo(
311       true, true, reinterpret_cast<const char*>(test::kChannelBindings),
312       test::kNtlmSpn, server_av_pairs, &server_timestamp);
313 
314   // With MIC and EPA enabled 3 additional AvPairs will be added.
315   // 1) A flags AVPair with the MIC_PRESENT bit set.
316   // 2) A channel bindings AVPair containing the channel bindings hash.
317   // 3) A target name AVPair containing the SPN of the server.
318   ASSERT_EQ(std::size(test::kExpectedTargetInfoSpecResponseV2),
319             updated_target_info.size());
320   ASSERT_EQ(0, memcmp(test::kExpectedTargetInfoSpecResponseV2,
321                       updated_target_info.data(), updated_target_info.size()));
322 }
323 
TEST(NtlmTest,GenerateUpdatedTargetInfoNoEpaOrMic)324 TEST(NtlmTest, GenerateUpdatedTargetInfoNoEpaOrMic) {
325   // This constructs a std::vector<AvPair> that corresponds to the test input
326   // values in [MS-NLMP] Section 4.2.4.
327   std::vector<AvPair> server_av_pairs;
328   server_av_pairs.push_back(MakeDomainAvPair());
329   server_av_pairs.push_back(MakeServerAvPair());
330 
331   uint64_t server_timestamp = UINT64_MAX;
332 
333   // When both EPA and MIC are false the target info does not get modified by
334   // the client.
335   std::vector<uint8_t> updated_target_info = GenerateUpdatedTargetInfo(
336       false, false, reinterpret_cast<const char*>(test::kChannelBindings),
337       test::kNtlmSpn, server_av_pairs, &server_timestamp);
338   ASSERT_EQ(std::size(test::kExpectedTargetInfoFromSpecV2),
339             updated_target_info.size());
340   ASSERT_EQ(0, memcmp(test::kExpectedTargetInfoFromSpecV2,
341                       updated_target_info.data(), updated_target_info.size()));
342 }
343 
TEST(NtlmTest,GenerateUpdatedTargetInfoWithServerTimestamp)344 TEST(NtlmTest, GenerateUpdatedTargetInfoWithServerTimestamp) {
345   // This constructs a std::vector<AvPair> that corresponds to the test input
346   // values in [MS-NLMP] Section 4.2.4 with an additional server timestamp.
347   std::vector<AvPair> server_av_pairs;
348   server_av_pairs.push_back(MakeDomainAvPair());
349   server_av_pairs.push_back(MakeServerAvPair());
350 
351   // Set the timestamp to |test::kServerTimestamp| and the buffer to all zeros.
352   AvPair pair(TargetInfoAvId::kTimestamp,
353               std::vector<uint8_t>(sizeof(uint64_t), 0));
354   pair.timestamp = test::kServerTimestamp;
355   server_av_pairs.push_back(std::move(pair));
356 
357   uint64_t server_timestamp = UINT64_MAX;
358   // When both EPA and MIC are false the target info does not get modified by
359   // the client.
360   std::vector<uint8_t> updated_target_info = GenerateUpdatedTargetInfo(
361       false, false, reinterpret_cast<const char*>(test::kChannelBindings),
362       test::kNtlmSpn, server_av_pairs, &server_timestamp);
363   // Verify that the server timestamp was read from the target info.
364   ASSERT_EQ(test::kServerTimestamp, server_timestamp);
365   ASSERT_EQ(std::size(test::kExpectedTargetInfoFromSpecPlusServerTimestampV2),
366             updated_target_info.size());
367   ASSERT_EQ(0, memcmp(test::kExpectedTargetInfoFromSpecPlusServerTimestampV2,
368                       updated_target_info.data(), updated_target_info.size()));
369 }
370 
TEST(NtlmTest,GenerateUpdatedTargetInfoWhenServerSendsNoTargetInfo)371 TEST(NtlmTest, GenerateUpdatedTargetInfoWhenServerSendsNoTargetInfo) {
372   // In some older implementations the server supports NTLMv2 but does not
373   // send target info. This manifests as an empty list of AvPairs.
374   std::vector<AvPair> server_av_pairs;
375 
376   uint64_t server_timestamp = UINT64_MAX;
377   std::vector<uint8_t> updated_target_info = GenerateUpdatedTargetInfo(
378       true, true, reinterpret_cast<const char*>(test::kChannelBindings),
379       test::kNtlmSpn, server_av_pairs, &server_timestamp);
380 
381   // With MIC and EPA enabled 3 additional AvPairs will be added.
382   // 1) A flags AVPair with the MIC_PRESENT bit set.
383   // 2) A channel bindings AVPair containing the channel bindings hash.
384   // 3) A target name AVPair containing the SPN of the server.
385   //
386   // Compared to the spec example in |GenerateUpdatedTargetInfo| the result
387   // is the same but with the first 32 bytes (which were the Domain and
388   // Server pairs) not present.
389   const size_t kMissingServerPairsLength = 32;
390 
391   ASSERT_EQ(std::size(test::kExpectedTargetInfoSpecResponseV2) -
392                 kMissingServerPairsLength,
393             updated_target_info.size());
394   ASSERT_EQ(0, memcmp(test::kExpectedTargetInfoSpecResponseV2 +
395                           kMissingServerPairsLength,
396                       updated_target_info.data(), updated_target_info.size()));
397 }
398 
TEST(NtlmTest,GenerateNtlmProofV2)399 TEST(NtlmTest, GenerateNtlmProofV2) {
400   uint8_t proof[kNtlmProofLenV2];
401 
402   GenerateNtlmProofV2(
403       test::kExpectedNtlmHashV2, test::kServerChallenge,
404       base::span(test::kExpectedTempFromSpecV2).first<kProofInputLenV2>(),
405       test::kExpectedTargetInfoSpecResponseV2, proof);
406   ASSERT_EQ(0,
407             memcmp(test::kExpectedProofSpecResponseV2, proof, kNtlmProofLenV2));
408 }
409 
TEST(NtlmTest,GenerateNtlmProofWithClientTimestampV2)410 TEST(NtlmTest, GenerateNtlmProofWithClientTimestampV2) {
411   uint8_t proof[kNtlmProofLenV2];
412 
413   // Since the test data for "temp" in the spec does not include the client
414   // timestamp, a separate proof test value must be validated for use in full
415   // message validation.
416   GenerateNtlmProofV2(test::kExpectedNtlmHashV2, test::kServerChallenge,
417                       base::span(test::kExpectedTempWithClientTimestampV2)
418                           .first<kProofInputLenV2>(),
419                       test::kExpectedTargetInfoSpecResponseV2, proof);
420   ASSERT_EQ(0, memcmp(test::kExpectedProofSpecResponseWithClientTimestampV2,
421                       proof, kNtlmProofLenV2));
422 }
423 
424 }  // namespace net::ntlm
425