Index: net/http/ntlm_client.cc |
diff --git a/net/http/ntlm_client.cc b/net/http/ntlm_client.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..035c9650711a30bc3018da06339e1b779ea7d511 |
--- /dev/null |
+++ b/net/http/ntlm_client.cc |
@@ -0,0 +1,149 @@ |
+// Copyright (c) 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. |
+ |
+#include "net/http/ntlm_client.h" |
+ |
+#include <string.h> |
+ |
+#include "base/logging.h" |
+#include "base/md5.h" |
+#include "net/base/zap.h" |
+#include "net/http/des.h" |
+#include "net/http/md4.h" |
+#include "net/http/ntlm_buffer_writer.h" |
+ |
+namespace net { |
+ |
+NtlmClient::NtlmClient(uint32_t negotiate_flags) |
+ : negotiate_flags_(negotiate_flags) { |
+ // Just generate the negotiate message once and hold on to it. It never |
+ // changes and in a NTLMv2 it's used as an input |
+ // to the Message Integrity Check in the Authenticate message. |
+ GenerateNegotiateMessage(); |
+} |
+ |
+NtlmClient::~NtlmClient() {} |
+ |
+void NtlmClient::GenerateNtlmHashV1(const base::string16& password, |
+ uint8_t* hash) { |
+ // hash param must have at least 16 bytes of space. |
+ NtlmBufferWriter writer(password.length() * 2); |
+ |
+ // The writer will handle the big endian case if necessary. |
+ writer.WriteUnicodeString(password); |
+ weak_crypto::MD4Sum(writer.GetBufferPtr(), writer.GetLength(), hash); |
+ ZapBuf(writer.GetBufferPtr(), writer.GetLength()); |
+} |
+ |
+void NtlmClient::GenerateResponseDesl(const uint8_t* hash, |
+ const uint8_t* challenge, |
+ uint8_t* response) { |
+ // hash param must be 16 bytes. |
+ // challenge param must be 8 bytes. |
+ // response param must be at least 24 bytes. |
+ |
+ // See DESL(K, D) function in Section 6 Appendix A of NTLMSSP specification. |
+ uint8_t key1[8]; |
+ uint8_t key2[8]; |
+ uint8_t key3[8]; |
+ |
+ // The last 2 bytes of the hash are zero padded (5 zeros) as the |
+ // input to generate key3. |
+ uint8_t padded_hash[7]; |
+ padded_hash[0] = hash[14]; |
+ padded_hash[1] = hash[15]; |
+ memset(padded_hash + 2, 0, 5); |
+ |
+ DESMakeKey(hash, key1); |
+ DESMakeKey(hash + 7, key2); |
+ DESMakeKey(padded_hash, key3); |
+ |
+ DESEncrypt(key1, challenge, response); |
+ DESEncrypt(key2, challenge, response + 8); |
+ DESEncrypt(key3, challenge, response + 16); |
+} |
+ |
+void NtlmClient::GenerateNtlmResponseV1(const base::string16& password, |
+ const uint8_t* challenge, |
+ uint8_t* ntlm_response) { |
+ uint8_t ntlm_hash[NtlmMessage::NTLM_HASH_LEN]; |
+ GenerateNtlmHashV1(password, ntlm_hash); |
+ GenerateResponseDesl(ntlm_hash, challenge, ntlm_response); |
+} |
+ |
+void NtlmClient::GenerateResponsesV1(const base::string16& password, |
+ const uint8_t* server_challenge, |
+ uint8_t* lm_response, |
+ uint8_t* ntlm_response) { |
+ GenerateNtlmResponseV1(password, server_challenge, ntlm_response); |
+ |
+ // In NTLM v1 (with LMv1 disabled), the lm_response and ntlm_response are the |
+ // same. So just copy the ntlm_response into the lm_response. |
+ memcpy(lm_response, ntlm_response, NtlmMessage::RESPONSE_V1_LEN); |
+} |
+ |
+void NtlmClient::GenerateLMResponseV1WithSS(const uint8_t* client_challenge, |
+ uint8_t* lm_response) { |
+ // In NTLM v1 with Session Security (aka NTLM2) the lm_response is 8 bytes of |
+ // client challenge and 16 bytes of zeros. (See 3.3.1) |
+ memcpy(lm_response, client_challenge, NtlmMessage::CHALLENGE_LEN); |
+ memset(lm_response + NtlmMessage::CHALLENGE_LEN, 0, |
+ NtlmMessage::RESPONSE_V1_LEN - NtlmMessage::CHALLENGE_LEN); |
+} |
+ |
+void NtlmClient::GenerateSessionHashV1WithSS(const uint8_t* server_challenge, |
+ const uint8_t* client_challenge, |
+ base::MD5Digest* session_hash) { |
+ base::MD5Context ctx; |
+ base::MD5Init(&ctx); |
+ base::MD5Update( |
+ &ctx, base::StringPiece(reinterpret_cast<const char*>(server_challenge), |
+ NtlmMessage::CHALLENGE_LEN)); |
+ base::MD5Update( |
+ &ctx, base::StringPiece(reinterpret_cast<const char*>(client_challenge), |
+ NtlmMessage::CHALLENGE_LEN)); |
+ |
+ base::MD5Final(session_hash, &ctx); |
+} |
+ |
+void NtlmClient::GenerateNtlmResponseV1WithSS(const base::string16& password, |
+ const uint8_t* server_challenge, |
+ const uint8_t* client_challenge, |
+ uint8_t* ntlm_response) { |
+ // Generate the NTLMv1 Hash. |
+ uint8_t ntlm_hash[NtlmMessage::NTLM_HASH_LEN]; |
+ GenerateNtlmHashV1(password, ntlm_hash); |
+ |
+ // Generate the NTLMv1 Session Hash. |
+ base::MD5Digest session_hash; |
+ GenerateSessionHashV1WithSS(server_challenge, client_challenge, |
+ &session_hash); |
+ |
+ // Only the first 8 bytes of |session_hash.a| are actually used. |
+ GenerateResponseDesl(ntlm_hash, session_hash.a, ntlm_response); |
+} |
+ |
+void NtlmClient::GenerateResponsesV1WithSS(const base::string16& password, |
+ const uint8_t* server_challenge, |
+ const uint8_t* client_challenge, |
+ uint8_t* lm_response, |
+ uint8_t* ntlm_response) { |
+ GenerateLMResponseV1WithSS(client_challenge, lm_response); |
+ GenerateNtlmResponseV1WithSS(password, server_challenge, client_challenge, |
+ ntlm_response); |
+} |
+ |
+void NtlmClient::GenerateNegotiateMessage() { |
+ NtlmBufferWriter writer(NtlmMessage::NEGOTIATE_MESSAGE_LEN); |
+ bool result = writer.WriteMessageHeader(NtlmMessage::MESSAGE_NEGOTIATE) && |
+ writer.WriteUInt32(negotiate_flags_) && |
+ writer.WriteEmptySecurityBuffer() && |
+ writer.WriteEmptySecurityBuffer() && writer.IsEndOfBuffer(); |
+ |
+ DCHECK(result); |
+ |
+ negotiate_message_.reset(writer.ReleaseBufferPtr()); |
+} |
+ |
+} // namespace net |