Commit 163381df authored by Zentaro Kavanagh's avatar Zentaro Kavanagh Committed by Commit Bot

Replace internal crypto implementations with boringssl.

- Replace uses of MD5 implementation in base with boringssl.
- Replace uses of internal MD4 implementation with boringssl.
- Use DES directly from boringssl and remove mozilla DES code.
- The existing DES code was partially using boringssl already.
- The existing DES code was setting parity itself. Now uses
boringssl's DES_set_odd_parity
- Re-implemented the mapping of a 128 bit NTLM hash 
onto three DES keys in Create3DesKeysFromNtlmHash

BUG=chromium:755368,chromium:22532

Change-Id: I9f363b66c32dc8f7529627887ac46023403637e4
Reviewed-on: https://chromium-review.googlesource.com/616801
Commit-Queue: Zentaro Kavanagh <zentaro@google.com>
Reviewed-by: default avatarDavid Benjamin <davidben@chromium.org>
Reviewed-by: default avatarRyan Sleevi <rsleevi@chromium.org>
Reviewed-by: default avatarAsanka Herath <asanka@chromium.org>
Cr-Commit-Position: refs/heads/master@{#505285}
parent 9b8d378a
......@@ -1028,10 +1028,6 @@ component("net") {
"nqe/throughput_analyzer.cc",
"nqe/throughput_analyzer.h",
"nqe/weighted_observation.h",
"ntlm/des.cc",
"ntlm/des.h",
"ntlm/md4.cc",
"ntlm/md4.h",
"ntlm/ntlm.cc",
"ntlm/ntlm.h",
"ntlm/ntlm_buffer_reader.cc",
......@@ -1786,10 +1782,6 @@ component("net") {
if (is_win) {
sources -= [
"ntlm/des.cc",
"ntlm/des.h",
"ntlm/md4.cc",
"ntlm/md4.h",
"ntlm/ntlm.cc",
"ntlm/ntlm.h",
"ntlm/ntlm_buffer_reader.cc",
......@@ -4919,8 +4911,6 @@ test("net_unittests") {
"nqe/observation_buffer_unittest.cc",
"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",
......@@ -5339,8 +5329,6 @@ test("net_unittests") {
if (is_win) {
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",
......
// Copyright (c) 2011 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/ntlm/des.h"
#include "base/logging.h"
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/des.h"
// The iOS version of DESEncrypt is our own code.
// DESSetKeyParity and DESMakeKey are based on
// mozilla/security/manager/ssl/src/nsNTLMAuthModule.cpp, CVS rev. 1.14.
/* clang-format off */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is IBM Corporation.
* Portions created by IBM Corporation are Copyright (C) 2003
* IBM Corporation. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@meer.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// Set odd parity bit (in least significant bit position).
static uint8_t DESSetKeyParity(uint8_t x) {
if ((((x >> 7) ^ (x >> 6) ^ (x >> 5) ^
(x >> 4) ^ (x >> 3) ^ (x >> 2) ^
(x >> 1)) & 0x01) == 0) {
x |= 0x01;
} else {
x &= 0xfe;
}
return x;
}
namespace net {
void DESMakeKey(const uint8_t* raw, uint8_t* key) {
key[0] = DESSetKeyParity(raw[0]);
key[1] = DESSetKeyParity((raw[0] << 7) | (raw[1] >> 1));
key[2] = DESSetKeyParity((raw[1] << 6) | (raw[2] >> 2));
key[3] = DESSetKeyParity((raw[2] << 5) | (raw[3] >> 3));
key[4] = DESSetKeyParity((raw[3] << 4) | (raw[4] >> 4));
key[5] = DESSetKeyParity((raw[4] << 3) | (raw[5] >> 5));
key[6] = DESSetKeyParity((raw[5] << 2) | (raw[6] >> 6));
key[7] = DESSetKeyParity((raw[6] << 1));
}
void DESEncrypt(const uint8_t* key, const uint8_t* src, uint8_t* hash) {
crypto::EnsureOpenSSLInit();
DES_key_schedule ks;
DES_set_key(
reinterpret_cast<const DES_cblock*>(key), &ks);
DES_ecb_encrypt(reinterpret_cast<const DES_cblock*>(src),
reinterpret_cast<DES_cblock*>(hash), &ks, DES_ENCRYPT);
}
} // namespace net
// Copyright (c) 2011 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.
#ifndef NET_HTTP_DES_H_
#define NET_HTTP_DES_H_
#include <stdint.h>
#include "net/base/net_export.h"
namespace net {
//-----------------------------------------------------------------------------
// DES support code for NTLM authentication.
//
// TODO(wtc): Turn this into a C++ API and move it to the base module.
// Build a 64-bit DES key from a 56-bit raw key.
NET_EXPORT_PRIVATE void DESMakeKey(const uint8_t* raw, uint8_t* key);
// Run the DES encryption algorithm in ECB mode on one block (8 bytes) of
// data. |key| is a DES key (8 bytes), |src| is the input plaintext (8
// bytes), and |hash| is an 8-byte buffer receiving the output ciphertext.
NET_EXPORT_PRIVATE void DESEncrypt(const uint8_t* key,
const uint8_t* src,
uint8_t* hash);
} // namespace net
#endif // NET_HTTP_DES_H_
// Copyright (c) 2012 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 <string.h>
#include "net/ntlm/des.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
// This test vector comes from the NSS FIPS power-up self-test.
TEST(DESTest, KnownAnswerTest1) {
// DES known key (56-bits).
static const uint8_t des_known_key[] = "ANSI DES";
// DES known plaintext (64-bits).
static const uint8_t des_ecb_known_plaintext[] = "Netscape";
// DES known ciphertext (64-bits).
static const uint8_t des_ecb_known_ciphertext[] = {0x26, 0x14, 0xe9, 0xc3,
0x28, 0x80, 0x50, 0xb0};
uint8_t ciphertext[8];
memset(ciphertext, 0xaf, sizeof(ciphertext));
DESEncrypt(des_known_key, des_ecb_known_plaintext, ciphertext);
EXPECT_EQ(0, memcmp(ciphertext, des_ecb_known_ciphertext, 8));
}
// This test vector comes from NIST Special Publication 800-17, Modes of
// Operation Validation System (MOVS): Requirements and Procedures, Appendix
// A, page 124.
TEST(DESTest, KnownAnswerTest2) {
static const uint8_t key[] = {0x10, 0x31, 0x6e, 0x02, 0x8c, 0x8f, 0x3b, 0x4a};
static const uint8_t plaintext[] = {0, 0, 0, 0, 0, 0, 0, 0};
static const uint8_t known_ciphertext[] = {0x82, 0xdc, 0xba, 0xfb,
0xde, 0xab, 0x66, 0x02};
uint8_t ciphertext[8];
memset(ciphertext, 0xaf, sizeof(ciphertext));
DESEncrypt(key, plaintext, ciphertext);
EXPECT_EQ(0, memcmp(ciphertext, known_ciphertext, 8));
}
} // namespace net
// This is mozilla/security/manager/ssl/src/md4.c, CVS rev. 1.1, with trivial
// changes to port it to our source tree.
//
// WARNING: MD4 is cryptographically weak. Do not use MD4 except in NTLM
// authentication.
/* clang-format off */
/* vim:set ts=2 sw=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is IBM Corporation.
* Portions created by IBM Corporation are Copyright (C) 2003
* IBM Corporation. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@meer.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* "clean room" MD4 implementation (see RFC 1320)
*/
#include "net/ntlm/md4.h"
#include <string.h>
typedef uint32_t Uint32;
typedef uint8_t Uint8;
/* the "conditional" function */
#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))
/* the "majority" function */
#define G(x,y,z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
/* the "parity" function */
#define H(x,y,z) ((x) ^ (y) ^ (z))
/* rotate n-bits to the left */
#define ROTL(x,n) (((x) << (n)) | ((x) >> (0x20 - n)))
/* round 1: [abcd k s]: a = (a + F(b,c,d) + X[k]) <<< s */
#define RD1(a,b,c,d,k,s) a += F(b,c,d) + X[k]; a = ROTL(a,s)
/* round 2: [abcd k s]: a = (a + G(b,c,d) + X[k] + MAGIC) <<< s */
#define RD2(a,b,c,d,k,s) a += G(b,c,d) + X[k] + 0x5A827999; a = ROTL(a,s)
/* round 3: [abcd k s]: a = (a + H(b,c,d) + X[k] + MAGIC) <<< s */
#define RD3(a,b,c,d,k,s) a += H(b,c,d) + X[k] + 0x6ED9EBA1; a = ROTL(a,s)
/* converts from word array to byte array, len is number of bytes */
static void w2b(Uint8 *out, const Uint32 *in, Uint32 len)
{
Uint8 *bp; const Uint32 *wp, *wpend;
bp = out;
wp = in;
wpend = wp + (len >> 2);
for (; wp != wpend; ++wp, bp += 4)
{
bp[0] = (Uint8) ((*wp ) & 0xFF);
bp[1] = (Uint8) ((*wp >> 8) & 0xFF);
bp[2] = (Uint8) ((*wp >> 16) & 0xFF);
bp[3] = (Uint8) ((*wp >> 24) & 0xFF);
}
}
/* converts from byte array to word array, len is number of bytes */
static void b2w(Uint32 *out, const Uint8 *in, Uint32 len)
{
Uint32 *wp; const Uint8 *bp, *bpend;
wp = out;
bp = in;
bpend = in + len;
for (; bp != bpend; bp += 4, ++wp)
{
*wp = (Uint32) (bp[0] ) |
(Uint32) (bp[1] << 8) |
(Uint32) (bp[2] << 16) |
(Uint32) (bp[3] << 24);
}
}
/* update state: data is 64 bytes in length */
static void md4step(Uint32 state[4], const Uint8 *data)
{
Uint32 A, B, C, D, X[16];
b2w(X, data, 64);
A = state[0];
B = state[1];
C = state[2];
D = state[3];
RD1(A,B,C,D, 0,3); RD1(D,A,B,C, 1,7); RD1(C,D,A,B, 2,11); RD1(B,C,D,A, 3,19);
RD1(A,B,C,D, 4,3); RD1(D,A,B,C, 5,7); RD1(C,D,A,B, 6,11); RD1(B,C,D,A, 7,19);
RD1(A,B,C,D, 8,3); RD1(D,A,B,C, 9,7); RD1(C,D,A,B,10,11); RD1(B,C,D,A,11,19);
RD1(A,B,C,D,12,3); RD1(D,A,B,C,13,7); RD1(C,D,A,B,14,11); RD1(B,C,D,A,15,19);
RD2(A,B,C,D, 0,3); RD2(D,A,B,C, 4,5); RD2(C,D,A,B, 8, 9); RD2(B,C,D,A,12,13);
RD2(A,B,C,D, 1,3); RD2(D,A,B,C, 5,5); RD2(C,D,A,B, 9, 9); RD2(B,C,D,A,13,13);
RD2(A,B,C,D, 2,3); RD2(D,A,B,C, 6,5); RD2(C,D,A,B,10, 9); RD2(B,C,D,A,14,13);
RD2(A,B,C,D, 3,3); RD2(D,A,B,C, 7,5); RD2(C,D,A,B,11, 9); RD2(B,C,D,A,15,13);
RD3(A,B,C,D, 0,3); RD3(D,A,B,C, 8,9); RD3(C,D,A,B, 4,11); RD3(B,C,D,A,12,15);
RD3(A,B,C,D, 2,3); RD3(D,A,B,C,10,9); RD3(C,D,A,B, 6,11); RD3(B,C,D,A,14,15);
RD3(A,B,C,D, 1,3); RD3(D,A,B,C, 9,9); RD3(C,D,A,B, 5,11); RD3(B,C,D,A,13,15);
RD3(A,B,C,D, 3,3); RD3(D,A,B,C,11,9); RD3(C,D,A,B, 7,11); RD3(B,C,D,A,15,15);
state[0] += A;
state[1] += B;
state[2] += C;
state[3] += D;
}
namespace net {
namespace weak_crypto {
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;
state[1] = 0xEFCDAB89;
state[2] = 0x98BADCFE;
state[3] = 0x10325476;
/* compute number of complete 64-byte segments contained in input */
m = inputLen >> 6;
/* digest first m segments */
for (i=0; i<m; ++i)
md4step(state, (input + (i << 6)));
/* build final buffer */
n = inputLen % 64;
memcpy(final, input + (m << 6), n);
final[n] = 0x80;
memset(final + n + 1, 0, 120 - (n + 1));
// 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)
md4step(state, final + 64);
/* copy state to result */
w2b(result, state, 16);
}
} // namespace weak_crypto
} // namespace net
// This is mozilla/security/manager/ssl/src/md4.h, CVS rev. 1.1, with trivial
// changes to port it to our source tree.
//
// WARNING: MD4 is cryptographically weak. Do not use MD4 except in NTLM
// authentication.
/* vim:set ts=2 sw=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is IBM Corporation.
* Portions created by IBM Corporation are Copyright (C) 2003
* IBM Corporation. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@meer.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef NET_HTTP_MD4_H_
#define NET_HTTP_MD4_H_
#include <stdint.h>
#include "net/base/net_export.h"
namespace net {
namespace weak_crypto {
/**
* MD4Sum - computes the MD4 sum over the input buffer per RFC 1320
*
* @param input
* buffer containing input data
* @param inputLen
* length of input buffer (number of bytes)
* @param result
* 16-byte buffer that will contain the MD4 sum upon return
*
* NOTE: MD4 is superceded by MD5. do not use MD4 unless required by the
* protocol you are implementing (e.g., NTLM requires MD4).
*
* NOTE: this interface is designed for relatively small buffers. A streaming
* interface would make more sense if that were a requirement. Currently, this
* is good enough for the applications we care about.
*/
NET_EXPORT_PRIVATE void MD4Sum(const uint8_t* input,
uint32_t inputLen,
uint8_t* result);
} // namespace weak_crypto
} // namespace net
#endif // NET_HTTP_MD4_H_
// 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
......@@ -10,10 +10,11 @@
#include "base/md5.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_string_util.h"
#include "net/ntlm/des.h"
#include "net/ntlm/md4.h"
#include "net/ntlm/ntlm_buffer_writer.h"
#include "third_party/boringssl/src/include/openssl/des.h"
#include "third_party/boringssl/src/include/openssl/hmac.h"
#include "third_party/boringssl/src/include/openssl/md4.h"
#include "third_party/boringssl/src/include/openssl/md5.h"
namespace net {
namespace ntlm {
......@@ -90,20 +91,16 @@ void UpdateTargetInfoAvPairs(bool is_mic_enabled,
}
if (is_epa_enabled) {
if (channel_bindings.empty()) {
// When no channel bindings are supplied, the channel binding hash is
// set to all zeros.
av_pairs->emplace_back(TargetInfoAvId::kChannelBindings,
Buffer(kChannelBindingsHashLen, 0));
} else {
// Hash the channel bindings.
base::MD5Digest channel_bindings_hash;
GenerateChannelBindingHashV2(channel_bindings, &channel_bindings_hash);
av_pairs->emplace_back(
TargetInfoAvId::kChannelBindings,
Buffer(channel_bindings_hash.a, kChannelBindingsHashLen));
Buffer channel_bindings_hash(kChannelBindingsHashLen, 0);
// Hash the channel bindings if they exist otherwise they remain zeros.
if (!channel_bindings.empty()) {
GenerateChannelBindingHashV2(channel_bindings, &channel_bindings_hash[0]);
}
av_pairs->emplace_back(TargetInfoAvId::kChannelBindings,
std::move(channel_bindings_hash));
// Convert the SPN to little endian unicode.
base::string16 spn16 = base::UTF8ToUTF16(spn);
NtlmBufferWriter spn_writer(spn16.length() * 2);
......@@ -136,43 +133,78 @@ Buffer WriteUpdatedTargetInfo(const std::vector<AvPair>& av_pairs,
return writer.Pass();
}
// Reads 7 bytes (56 bits) from |key_56| and writes them into 8 bytes of
// |key_64| with 7 bits in every byte. The least significant bits are
// undefined and a subsequent operation will set those bits with a parity bit.
// |key_56| must contain 7 bytes.
// |key_64| must contain 8 bytes.
void Splay56To64(const uint8_t* key_56, uint8_t* key_64) {
key_64[0] = key_56[0];
key_64[1] = key_56[0] << 7 | key_56[1] >> 1;
key_64[2] = key_56[1] << 6 | key_56[2] >> 2;
key_64[3] = key_56[2] << 5 | key_56[3] >> 3;
key_64[4] = key_56[3] << 4 | key_56[4] >> 4;
key_64[5] = key_56[4] << 3 | key_56[5] >> 5;
key_64[6] = key_56[5] << 2 | key_56[6] >> 6;
key_64[7] = key_56[6] << 1;
}
} // namespace
void Create3DesKeysFromNtlmHash(const uint8_t* ntlm_hash, uint8_t* keys) {
// Put the first 112 bits from |ntlm_hash| into the first 16 bytes of
// |keys|.
Splay56To64(ntlm_hash, keys);
Splay56To64(ntlm_hash + 7, keys + 8);
// Put the next 2x 7 bits in bytes 16 and 17 of |keys|, then
// the last 2 bits in byte 18, then zero pad the rest of the final key.
keys[16] = ntlm_hash[14];
keys[17] = ntlm_hash[14] << 7 | ntlm_hash[15] >> 1;
keys[18] = ntlm_hash[15] << 6;
memset(keys + 19, 0, 5);
}
void GenerateNtlmHashV1(const base::string16& password, uint8_t* hash) {
size_t length = password.length() * 2;
NtlmBufferWriter writer(length);
// The writer will handle the big endian case if necessary.
bool result = writer.WriteUtf16String(password);
bool result = writer.WriteUtf16String(password) && writer.IsEndOfBuffer();
DCHECK(result);
weak_crypto::MD4Sum(
reinterpret_cast<const uint8_t*>(writer.GetBuffer().data()), length,
hash);
MD4(writer.GetBuffer().data(), writer.GetLength(), hash);
}
void GenerateResponseDesl(const uint8_t* hash,
const uint8_t* challenge,
uint8_t* response) {
// See DESL(K, D) function in [MS-NLMP] Section 6
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);
constexpr size_t block_count = 3;
constexpr size_t block_size = sizeof(DES_cblock);
static_assert(kChallengeLen == block_size,
"kChallengeLen must equal block_size");
static_assert(kResponseLenV1 == block_count * block_size,
"kResponseLenV1 must equal block_count * block_size");
const DES_cblock* challenge_block =
reinterpret_cast<const DES_cblock*>(challenge);
uint8_t keys[block_count * block_size];
// Map the NTLM hash to three 8 byte DES keys, with 7 bits of the key in each
// byte and the least significant bit set with odd parity. Then encrypt the
// 8 byte challenge with each of the three keys. This produces three 8 byte
// encrypted blocks into |response|.
Create3DesKeysFromNtlmHash(hash, keys);
for (size_t ix = 0; ix < block_count * block_size; ix += block_size) {
DES_cblock* key_block = reinterpret_cast<DES_cblock*>(keys + ix);
DES_cblock* response_block = reinterpret_cast<DES_cblock*>(response + ix);
DES_key_schedule key_schedule;
DES_set_odd_parity(key_block);
DES_set_key(key_block, &key_schedule);
DES_ecb_encrypt(challenge_block, response_block, &key_schedule,
DES_ENCRYPT);
}
}
void GenerateNtlmResponseV1(const base::string16& password,
......@@ -204,17 +236,12 @@ void GenerateLMResponseV1WithSessionSecurity(const uint8_t* client_challenge,
void GenerateSessionHashV1WithSessionSecurity(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),
kChallengeLen));
base::MD5Update(
&ctx, base::StringPiece(reinterpret_cast<const char*>(client_challenge),
kChallengeLen));
base::MD5Final(session_hash, &ctx);
uint8_t* session_hash) {
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, server_challenge, kChallengeLen);
MD5_Update(&ctx, client_challenge, kChallengeLen);
MD5_Final(session_hash, &ctx);
}
void GenerateNtlmResponseV1WithSessionSecurity(const base::string16& password,
......@@ -226,12 +253,12 @@ void GenerateNtlmResponseV1WithSessionSecurity(const base::string16& password,
GenerateNtlmHashV1(password, ntlm_hash);
// Generate the NTLMv1 Session Hash.
base::MD5Digest session_hash;
uint8_t session_hash[kNtlmHashLen];
GenerateSessionHashV1WithSessionSecurity(server_challenge, client_challenge,
&session_hash);
session_hash);
// Only the first 8 bytes of |session_hash.a| are actually used.
GenerateResponseDesl(ntlm_hash, session_hash.a, ntlm_response);
// Only the first 8 bytes of |session_hash| are actually used.
GenerateResponseDesl(ntlm_hash, session_hash, ntlm_response);
}
void GenerateResponsesV1WithSessionSecurity(const base::string16& password,
......@@ -263,12 +290,12 @@ void GenerateNtlmHashV2(const base::string16& domain,
input_writer.IsEndOfBuffer();
DCHECK(writer_result);
bssl::ScopedHMAC_CTX ctx;
HMAC_Init_ex(ctx.get(), v1_hash, sizeof(v1_hash), EVP_md5(), NULL);
DCHECK_EQ(sizeof(v1_hash), HMAC_size(ctx.get()));
HMAC_Update(ctx.get(), input_writer.GetBuffer().data(),
input_writer.GetLength());
HMAC_Final(ctx.get(), v2_hash, nullptr);
unsigned int outlen = kNtlmHashLen;
v2_hash =
HMAC(EVP_md5(), v1_hash, sizeof(v1_hash), input_writer.GetBuffer().data(),
input_writer.GetLength(), v2_hash, &outlen);
DCHECK_NE(nullptr, v2_hash);
DCHECK_EQ(sizeof(v1_hash), outlen);
}
Buffer GenerateProofInputV2(uint64_t timestamp,
......@@ -305,28 +332,26 @@ void GenerateNtlmProofV2(const uint8_t* v2_hash,
void GenerateSessionBaseKeyV2(const uint8_t* v2_hash,
const uint8_t* v2_proof,
uint8_t* session_key) {
bssl::ScopedHMAC_CTX ctx;
HMAC_Init_ex(ctx.get(), v2_hash, kNtlmHashLen, EVP_md5(), NULL);
DCHECK_EQ(kSessionKeyLenV2, HMAC_size(ctx.get()));
HMAC_Update(ctx.get(), v2_proof, kNtlmProofLenV2);
HMAC_Final(ctx.get(), session_key, nullptr);
unsigned int outlen = kSessionKeyLenV2;
session_key = HMAC(EVP_md5(), v2_hash, kNtlmHashLen, v2_proof,
kNtlmProofLenV2, session_key, &outlen);
DCHECK_NE(nullptr, session_key);
DCHECK_EQ(kSessionKeyLenV2, outlen);
}
void GenerateChannelBindingHashV2(const std::string& channel_bindings,
base::MD5Digest* channel_bindings_hash) {
uint8_t* channel_bindings_hash) {
NtlmBufferWriter writer(kEpaUnhashedStructHeaderLen);
bool result = writer.WriteZeros(16) &&
writer.WriteUInt32(channel_bindings.length()) &&
writer.IsEndOfBuffer();
DCHECK(result);
base::MD5Context ctx;
base::MD5Init(&ctx);
base::MD5Update(&ctx, base::StringPiece(reinterpret_cast<const char*>(
writer.GetBuffer().data()),
writer.GetBuffer().size()));
base::MD5Update(&ctx, channel_bindings);
base::MD5Final(channel_bindings_hash, &ctx);
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, writer.GetBuffer().data(), writer.GetBuffer().size());
MD5_Update(&ctx, channel_bindings.data(), channel_bindings.size());
MD5_Final(channel_bindings_hash, &ctx);
}
void GenerateMicV2(const uint8_t* session_key,
......
......@@ -22,13 +22,18 @@
#include "net/base/net_export.h"
#include "net/ntlm/ntlm_constants.h"
namespace base {
struct MD5Digest;
}
namespace net {
namespace ntlm {
// Maps the bits in the NTLM Hash into 3 DES keys. The DES keys each have 56
// bits stored in the 7 most significant bits of 8 bytes. The least
// significant bit is undefined and will subsequently be set with odd parity
// prior to use.
// |ntlm_hash| must contain 16 bytes.
// |keys| must contain 24 bytes.
NET_EXPORT_PRIVATE void Create3DesKeysFromNtlmHash(const uint8_t* ntlm_hash,
uint8_t* keys);
// Generates the NTLMv1 Hash and writes the |kNtlmHashLen| byte result to
// |hash|. Defined by NTOWFv1() in [MS-NLMP] Section 3.3.1.
NET_EXPORT_PRIVATE void GenerateNtlmHashV1(const base::string16& password,
......@@ -92,10 +97,11 @@ NET_EXPORT_PRIVATE void GenerateLMResponseV1WithSessionSecurity(
//
// |server_challenge| must contain |kChallengeLen| bytes.
// |client_challenge| must contain |kChallengeLen| bytes.
// |session_hash| must contain |kNtlmHashLen|.
NET_EXPORT_PRIVATE void GenerateSessionHashV1WithSessionSecurity(
const uint8_t* server_challenge,
const uint8_t* client_challenge,
base::MD5Digest* session_hash);
uint8_t* session_hash);
// Generates the NTLM Response for NTLMv1 with session security.
// Defined by ComputeResponse() in [MS-NLMP] Section 3.3.1 for the
......@@ -216,7 +222,7 @@ NET_EXPORT_PRIVATE void GenerateSessionBaseKeyV2(const uint8_t* v2_hash,
// channel_bindings_hash = MD5(ClientChannelBindingsUnhashed)
NET_EXPORT_PRIVATE void GenerateChannelBindingHashV2(
const std::string& channel_bindings,
base::MD5Digest* channel_bindings_hash);
uint8_t* channel_bindings_hash);
// The Message Integrity Check (MIC) is a hash calculated over all three
// messages in the NTLM protocol. The MIC field in the authenticate message
......
......@@ -36,8 +36,63 @@ AvPair MakeServerAvPair() {
Buffer(test::kServerRaw, arraysize(test::kServerRaw)));
}
// Clear the least significant bit in each byte.
void ClearLsb(uint8_t* data, size_t len) {
for (size_t i = 0; i < len; i++) {
data[i] &= ~1;
}
}
} // namespace
TEST(NtlmTest, MapHashToDesKeysAllOnes) {
// Test mapping an NTLM hash with all 1 bits.
const uint8_t hash[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const uint8_t expected[24] = {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t result[24];
Create3DesKeysFromNtlmHash(hash, result);
// The least significant bit in result from |Create3DesKeysFromNtlmHash|
// is undefined, so clear it to do memcmp.
ClearLsb(result, arraysize(result));
ASSERT_EQ(0, memcmp(expected, result, arraysize(expected)));
}
TEST(NtlmTest, MapHashToDesKeysAllZeros) {
// Test mapping an NTLM hash with all 0 bits.
const uint8_t hash[16] = {0x00};
const uint8_t expected[24] = {0x00};
uint8_t result[24];
Create3DesKeysFromNtlmHash(hash, result);
// The least significant bit in result from |Create3DesKeysFromNtlmHash|
// is undefined, so clear it to do memcmp.
ClearLsb(result, arraysize(result));
ASSERT_EQ(0, memcmp(expected, result, arraysize(expected)));
}
TEST(NtlmTest, MapHashToDesKeysAlternatingBits) {
// Test mapping an NTLM hash with alternating 0 and 1 bits.
const uint8_t hash[16] = {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa};
const uint8_t expected[24] = {0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54,
0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54,
0xaa, 0x54, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t result[24];
Create3DesKeysFromNtlmHash(hash, result);
// The least significant bit in result from |Create3DesKeysFromNtlmHash|
// is undefined, so clear it to do memcmp.
ClearLsb(result, arraysize(result));
ASSERT_EQ(0, memcmp(expected, result, arraysize(expected)));
}
TEST(NtlmTest, GenerateNtlmHashV1PasswordSpecTests) {
uint8_t hash[kNtlmHashLen];
GenerateNtlmHashV1(test::kPassword, hash);
......@@ -195,12 +250,11 @@ TEST(NtlmTest, GenerateSessionBaseKeyWithClientTimestampV2SpecTests) {
}
TEST(NtlmTest, GenerateChannelBindingHashV2SpecTests) {
base::MD5Digest v2_channel_binding_hash;
GenerateChannelBindingHashV2(test::kChannelBindings,
&v2_channel_binding_hash);
uint8_t v2_channel_binding_hash[kChannelBindingsHashLen];
GenerateChannelBindingHashV2(test::kChannelBindings, v2_channel_binding_hash);
ASSERT_EQ(0, memcmp(test::kExpectedChannelBindingHashV2,
v2_channel_binding_hash.a, kChannelBindingsHashLen));
v2_channel_binding_hash, kChannelBindingsHashLen));
}
TEST(NtlmTest, GenerateMicV2Simple) {
......
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