ntlm_unittest.cc 17 KB

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