Chromium Code Reviews| 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..64b8f2b46a993316a89dc6ffc9fefe8a08071eac |
| --- /dev/null |
| +++ b/net/http/ntlm_client.cc |
| @@ -0,0 +1,131 @@ |
| +// Copyright 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/http/des.h" |
| +#include "net/http/md4.h" |
| +#include "net/http/ntlm_buffer_writer.h" |
| + |
| +namespace net { |
| + |
| +namespace ntlm { |
| + |
| +void GenerateNtlmHashV1(const base::string16& password, uint8_t* hash) { |
| + size_t length = password.length() * 2; |
| + // hash param must have at least 16 bytes of space. |
| + NtlmBufferWriter writer(length); |
| + |
| + // The writer will handle the big endian case if necessary. |
| + bool result = writer.WriteUtf16String(password); |
| + DCHECK(result); |
| + |
| + std::unique_ptr<uint8_t[]> buffer = writer.ReleaseBuffer(); |
| + |
| + weak_crypto::MD4Sum(buffer.get(), length, hash); |
| +} |
| + |
| +void 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 GenerateNtlmResponseV1(const base::string16& password, |
| + const uint8_t* challenge, |
| + uint8_t* ntlm_response) { |
| + uint8_t ntlm_hash[NTLM_HASH_LEN]; |
| + GenerateNtlmHashV1(password, ntlm_hash); |
| + GenerateResponseDesl(ntlm_hash, challenge, ntlm_response); |
| +} |
| + |
| +void 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, RESPONSE_V1_LEN); |
| +} |
| + |
| +void 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, CHALLENGE_LEN); |
| + memset(lm_response + CHALLENGE_LEN, 0, RESPONSE_V1_LEN - CHALLENGE_LEN); |
| +} |
| + |
| +void 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), |
| + CHALLENGE_LEN)); |
| + base::MD5Update( |
| + &ctx, base::StringPiece(reinterpret_cast<const char*>(client_challenge), |
| + CHALLENGE_LEN)); |
| + |
| + base::MD5Final(session_hash, &ctx); |
| +} |
| + |
| +void 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[NTLM_HASH_LEN]; |
| + GenerateNtlmHashV1(password, ntlm_hash); |
|
asanka
2017/06/23 21:29:11
Minor nit: It's easier to follow along with the sp
|
| + |
| + // 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 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); |
| +} |
| + |
| +} // namespace ntlm |
| +} // namespace net |