Commit 4e3aae84 authored by Zentaro Kavanagh's avatar Zentaro Kavanagh Committed by Commit Bot

Fix bug in MD4 implementation that creates incorrect hash.

- Inputs with length % 64 >= 56 potentially generate the wrong hash.
- In MD4Sum function the last 4 bytes of the final array were never
  initialized.
- This would result in NTLM failing due to incorrect hash when the
  password length % 32 >= 28 (unicode password).
- Fixes fuzzer bug that was caught by new NTLM fuzzer.
- Added test suite from RFC 1320 for MD4.
- Md4Test.RfcTest6_Mod56CornerCase fails without this fix.

BUG=chromium:752491

Change-Id: If7b718b8c80f00819200332a027bb2f430b8f68e
Reviewed-on: https://chromium-review.googlesource.com/602693
Commit-Queue: Zentaro Kavanagh <zentaro@google.com>
Reviewed-by: default avatarAsanka Herath <asanka@chromium.org>
Cr-Commit-Position: refs/heads/master@{#492432}
parent 6371da0f
......@@ -4908,6 +4908,7 @@ test("net_unittests") {
"nqe/socket_watcher_unittest.cc",
"nqe/throughput_analyzer_unittest.cc",
"ntlm/des_unittest.cc",
"ntlm/md4_unittest.cc",
"ntlm/ntlm_buffer_reader_unittest.cc",
"ntlm/ntlm_buffer_writer_unittest.cc",
"ntlm/ntlm_client_unittest.cc",
......@@ -5327,6 +5328,7 @@ test("net_unittests") {
sources -= [
"http/http_auth_handler_ntlm_portable_unittest.cc",
"ntlm/des_unittest.cc",
"ntlm/md4_unittest.cc",
"ntlm/ntlm_buffer_reader_unittest.cc",
"ntlm/ntlm_buffer_writer_unittest.cc",
"ntlm/ntlm_client_unittest.cc",
......
......@@ -150,6 +150,7 @@ void MD4Sum(const Uint8 *input, Uint32 inputLen, Uint8 *result)
{
Uint8 final[128];
Uint32 i, n, m, state[4];
Uint32 inputLenArr[2];
/* magic initial states */
state[0] = 0x67452301;
......@@ -170,8 +171,15 @@ void MD4Sum(const Uint8 *input, Uint32 inputLen, Uint8 *result)
final[n] = 0x80;
memset(final + n + 1, 0, 120 - (n + 1));
inputLen = inputLen << 3;
w2b(final + (n >= 56 ? 120 : 56), &inputLen, 4);
// Store the length of the input in bits in two 32 bit values with the
// least significant word first in inputLenArr. w2b will then convert the
// 2x 32 bit values each to 4x bytes in little endian order. This results
// in writing 64 bits in little endian order.
//
// See the MD4Update function in RFC1320 Section A.1 (page 10).
inputLenArr[0] = static_cast<Uint32>(inputLen << 3);
inputLenArr[1] = static_cast<Uint32>(inputLen >> 29);
w2b(final + (n >= 56 ? 120 : 56), inputLenArr, 8);
md4step(state, final);
if (n >= 56)
......
......@@ -46,6 +46,8 @@
#include <stdint.h>
#include "net/base/net_export.h"
namespace net {
namespace weak_crypto {
......@@ -66,7 +68,9 @@ namespace weak_crypto {
* interface would make more sense if that were a requirement. Currently, this
* is good enough for the applications we care about.
*/
void MD4Sum(const uint8_t* input, uint32_t inputLen, uint8_t* result);
NET_EXPORT_PRIVATE void MD4Sum(const uint8_t* input,
uint32_t inputLen,
uint8_t* result);
} // namespace weak_crypto
} // namespace net
......
// 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.
// Test cases are from RFC 1320. https://tools.ietf.org/html/rfc1320
#include "net/ntlm/md4.h"
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
void Md4String(const std::string& input, uint8_t* hash) {
weak_crypto::MD4Sum(reinterpret_cast<const uint8_t*>(input.data()),
input.length(), hash);
}
} // namespace
TEST(Md4Test, RfcTest1_EmptyString) {
uint8_t actual_hash[16];
const uint8_t expected_hash[16] = {0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a,
0xe9, 0x31, 0xb7, 0x3c, 0x59, 0xd7,
0xe0, 0xc0, 0x89, 0xc0};
Md4String("", actual_hash);
ASSERT_EQ(0, memcmp(expected_hash, actual_hash, 16));
}
TEST(Md4Test, RfcTest2_OneChar) {
uint8_t actual_hash[16];
const uint8_t expected_hash[16] = {0xbd, 0xe5, 0x2c, 0xb3, 0x1d, 0xe3,
0x3e, 0x46, 0x24, 0x5e, 0x05, 0xfb,
0xdb, 0xd6, 0xfb, 0x24};
Md4String("a", actual_hash);
ASSERT_EQ(0, memcmp(expected_hash, actual_hash, 16));
}
TEST(Md4Test, RfcTest3_Abc) {
uint8_t actual_hash[16];
const uint8_t expected_hash[16] = {0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21,
0xd8, 0x52, 0x5f, 0xc1, 0x0a, 0xe8,
0x7a, 0xa6, 0x72, 0x9d};
Md4String("abc", actual_hash);
ASSERT_EQ(0, memcmp(expected_hash, actual_hash, 16));
}
TEST(Md4Test, RfcTest4_MessageDigest) {
uint8_t actual_hash[16];
const uint8_t expected_hash[16] = {0xd9, 0x13, 0x0a, 0x81, 0x64, 0x54,
0x9f, 0xe8, 0x18, 0x87, 0x48, 0x06,
0xe1, 0xc7, 0x01, 0x4b};
Md4String("message digest", actual_hash);
ASSERT_EQ(0, memcmp(expected_hash, actual_hash, 16));
}
TEST(Md4Test, RfcTest5_Alphabet) {
uint8_t actual_hash[16];
const uint8_t expected_hash[16] = {0xd7, 0x9e, 0x1c, 0x30, 0x8a, 0xa5,
0xbb, 0xcd, 0xee, 0xa8, 0xed, 0x63,
0xdf, 0x41, 0x2d, 0xa9};
Md4String("abcdefghijklmnopqrstuvwxyz", actual_hash);
ASSERT_EQ(0, memcmp(expected_hash, actual_hash, 16));
}
TEST(Md4Test, RfcTest6_Mod56CornerCase) {
uint8_t actual_hash[16];
const uint8_t expected_hash[16] = {0x04, 0x3f, 0x85, 0x82, 0xf2, 0x41,
0xdb, 0x35, 0x1c, 0xe6, 0x27, 0xe1,
0x53, 0xe7, 0xf0, 0xe4};
// The string is 62 bytes long. Inputs where (len % 64 >= 56) == true
// hit a special case in the implementation.
Md4String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
actual_hash);
ASSERT_EQ(0, memcmp(expected_hash, actual_hash, 16));
}
TEST(Md4Test, RfcTest7_LongerThanOneBlock) {
uint8_t actual_hash[16];
const uint8_t expected_hash[16] = {0xe3, 0x3b, 0x4d, 0xdc, 0x9c, 0x38,
0xf2, 0x19, 0x9c, 0x3e, 0x7b, 0x16,
0x4f, 0xcc, 0x05, 0x36};
// The string is 70 bytes long. MD4 processes data in 64 byte chunks.
Md4String(
"123456789012345678901234567890123456789012345678901234567890123456789012"
"34567890",
actual_hash);
ASSERT_EQ(0, memcmp(expected_hash, actual_hash, 16));
}
} // namespace net
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment