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