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