0

HMAC-SHA1 implementation for Mac based on CommonCrypto;

allow Windows HMAC-SHA1 to use keys longer than 16 bytes.
Review URL: http://codereview.chromium.org/218

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1724 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
mmentovai@google.com
2008-09-04 19:00:37 +00:00
parent 261d17188f
commit 301415ea99
8 changed files with 236 additions and 52 deletions

@ -80,7 +80,7 @@ if env['PLATFORM'] == 'win32':
'clipboard_util.cc',
'event_recorder.cc',
'file_version_info.cc',
'hmac.cc', # Depends on OpenSSL; Mac team will investigate.
'hmac_win.cc',
# This group all depends on MessageLoop.
'idle_timer.cc',

@ -81,6 +81,8 @@
7BAE30E70E6D93A300C3F750 /* simple_thread.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BAE30E20E6D939800C3F750 /* simple_thread.cc */; };
7BAE38AC0E6EFDBA00C3F750 /* thread_local_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BAE38A90E6EFD9900C3F750 /* thread_local_posix.cc */; };
7BAE38AF0E6EFDC300C3F750 /* thread_local_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BAE38AB0E6EFD9900C3F750 /* thread_local_unittest.cc */; };
7BAE392A0E6F4EEF00C3F750 /* hmac_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BAE39220E6F4EEC00C3F750 /* hmac_mac.cc */; };
7BAE392B0E6F4EF200C3F750 /* hmac_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BAE39240E6F4EEC00C3F750 /* hmac_unittest.cc */; };
7BAF501C0E50B84200CA8A07 /* base_paths.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF501B0E50B84200CA8A07 /* base_paths.cc */; };
7BAF50760E50B8F100CA8A07 /* file_version_info_mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF50740E50B8F100CA8A07 /* file_version_info_mac.mm */; };
7BD8F4A10E65AA4600034DE9 /* process_util_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BD8F49F0E65AA2400034DE9 /* process_util_posix.cc */; };
@ -351,6 +353,9 @@
7BAE38A90E6EFD9900C3F750 /* thread_local_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_local_posix.cc; sourceTree = "<group>"; };
7BAE38AA0E6EFD9900C3F750 /* thread_local_storage_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_local_storage_unittest.cc; sourceTree = "<group>"; };
7BAE38AB0E6EFD9900C3F750 /* thread_local_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_local_unittest.cc; sourceTree = "<group>"; };
7BAE39220E6F4EEC00C3F750 /* hmac_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hmac_mac.cc; sourceTree = "<group>"; };
7BAE39240E6F4EEC00C3F750 /* hmac_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hmac_unittest.cc; sourceTree = "<group>"; };
7BAE39BA0E6F576200C3F750 /* hmac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hmac.h; sourceTree = "<group>"; };
7BAF4F7B0E50A3BD00CA8A07 /* logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = logging.h; sourceTree = "<group>"; };
7BAF501B0E50B84200CA8A07 /* base_paths.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_paths.cc; sourceTree = "<group>"; };
7BAF50740E50B8F100CA8A07 /* file_version_info_mac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = file_version_info_mac.mm; sourceTree = "<group>"; };
@ -768,6 +773,9 @@
93611B160E5A875D00F9405D /* histogram.cc */,
93611B170E5A875D00F9405D /* histogram.h */,
93611B190E5A878400F9405D /* histogram_unittest.cc */,
7BAE39BA0E6F576200C3F750 /* hmac.h */,
7BAE39220E6F4EEC00C3F750 /* hmac_mac.cc */,
7BAE39240E6F4EEC00C3F750 /* hmac_unittest.cc */,
8254030F0D92D1E80006B936 /* iat_patch.cc */,
825403100D92D1E80006B936 /* iat_patch.h */,
825403110D92D1E80006B936 /* icu_util.cc */,
@ -1210,6 +1218,7 @@
A5A0268E0E4A2BDC00498DA9 /* file_util_posix.cc in Sources */,
7BAF50760E50B8F100CA8A07 /* file_version_info_mac.mm in Sources */,
93611B180E5A875D00F9405D /* histogram.cc in Sources */,
7BAE392A0E6F4EEF00C3F750 /* hmac_mac.cc in Sources */,
8216A5060E34DBDD00EE374C /* icu_util.cc in Sources */,
ABF4B9AF0DC2BC6200A6E319 /* json_reader.cc in Sources */,
ABF4B9B00DC2BC6500A6E319 /* json_writer.cc in Sources */,
@ -1284,6 +1293,7 @@
A5CE1D2B0E55F4D800AD0606 /* file_util_unittest.cc in Sources */,
7B78D3910E54FE0100609465 /* file_version_info_unittest.cc in Sources */,
93611B1A0E5A878400F9405D /* histogram_unittest.cc in Sources */,
7BAE392B0E6F4EF200C3F750 /* hmac_unittest.cc in Sources */,
AB956E0A0E5DDC0900BBE9D8 /* image_operations_unittest.cc in Sources */,
7B78D3920E54FE0100609465 /* json_reader_unittest.cc in Sources */,
7B78D3930E54FE0100609465 /* json_writer_unittest.cc in Sources */,

@ -310,11 +310,11 @@
>
</File>
<File
RelativePath="..\hmac.cc"
RelativePath="..\hmac.h"
>
</File>
<File
RelativePath="..\hmac.h"
RelativePath="..\hmac_win.cc"
>
</File>
<File

@ -1,20 +1,26 @@
// Copyright (c) 2006-2008 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.
//
// Utility class for calculating the HMAC for a given message. We currently
// only support SHA1 for the hash algorithm, but this can be extended easily.
#ifndef BASE_HMAC_H__
#define BASE_HMAC_H__
#ifndef BASE_HMAC_H_
#define BASE_HMAC_H_
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#include <wincrypt.h>
#endif
#include <string>
#include "base/basictypes.h"
namespace base {
class HMAC {
public:
// The set of supported hash functions. Extend as required.
@ -25,16 +31,17 @@ class HMAC {
HMAC(HashAlgorithm hash_alg, const unsigned char* key, int key_length);
~HMAC();
// Returns the HMAC in 'digest' for the message in 'data' and the key
// specified in the contructor.
// Calculates the HMAC for the message in |data| using the algorithm and key
// supplied to the constructor. The HMAC is returned in |digest|, which
// has |digest_length| bytes of storage available.
bool Sign(const std::string& data, unsigned char* digest, int digest_length);
private:
#if defined(OS_POSIX)
HashAlgorithm hash_alg_;
std::string key_;
#elif defined(OS_WIN)
// Import the key so that we don't have to store it ourself.
// TODO(paulg): Bug: http://b/1084719, 'ImportKey' will not currently work on
// Windows 2000 since it requires special handling for importing
// keys. See this link for details:
// http://www.derkeiler.com/Newsgroups/microsoft.public.platformsdk.security/2004-06/0270.html
void ImportKey(const unsigned char* key, int key_length);
// Returns the SHA1 hash of 'data' and 'key' in 'digest'. If there was any
@ -43,10 +50,6 @@ class HMAC {
unsigned char* digest,
int digest_length);
// Required for the SHA1 key_blob struct. We limit this to 16 bytes since
// Windows 2000 doesn't support keys larger than that.
static const int kMaxKeySize = 16;
// The hash algorithm to use.
HashAlgorithm hash_alg_;
@ -54,10 +57,11 @@ class HMAC {
HCRYPTPROV provider_;
HCRYPTHASH hash_;
HCRYPTKEY hkey_;
#endif // OS_WIN
DISALLOW_EVIL_CONSTRUCTORS(HMAC);
DISALLOW_COPY_AND_ASSIGN(HMAC);
};
} // namespace base
#endif // BASE_HMAC_H__
#endif // BASE_HMAC_H_

52
base/hmac_mac.cc Normal file

@ -0,0 +1,52 @@
// Copyright (c) 2008 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 "base/hmac.h"
#include <CommonCrypto/CommonHMAC.h>
#include "base/logging.h"
namespace base {
HMAC::HMAC(HashAlgorithm hash_alg, const unsigned char* key, int key_length)
: hash_alg_(hash_alg),
key_(reinterpret_cast<const char*>(key), key_length) {
}
HMAC::~HMAC() {
// Zero out key copy.
key_.assign(key_.length(), std::string::value_type());
key_.clear();
key_.reserve(0);
}
bool HMAC::Sign(const std::string& data,
unsigned char* digest,
int digest_length) {
CCHmacAlgorithm algorithm;
int algorithm_digest_length;
switch (hash_alg_) {
case SHA1:
algorithm = kCCHmacAlgSHA1;
algorithm_digest_length = CC_SHA1_DIGEST_LENGTH;
break;
default:
NOTREACHED();
return false;
}
if (digest_length < algorithm_digest_length) {
NOTREACHED();
return false;
}
CCHmac(algorithm,
key_.data(), key_.length(), data.data(), data.length(),
digest);
return true;
}
} // namespace base

@ -7,21 +7,23 @@
#include "base/hmac.h"
#include "testing/gtest/include/gtest/gtest.h"
static const int kKeySize = 16;
static const int kDigestSize = 20;
// Client key.
const unsigned char kClientKey[kKeySize] =
{ 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd,
0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 };
TEST(HMACTest, HmacSafeBrowsingResponseTest) {
const int kKeySize = 16;
// Expected HMAC result using kMessage and kClientKey.
const unsigned char kReceivedHmac[kDigestSize] =
{ 0xb9, 0x3c, 0xd6, 0xf0, 0x49, 0x47, 0xe2, 0x52,
0x59, 0x7a, 0xbd, 0x1f, 0x2b, 0x4c, 0x83, 0xad,
0x86, 0xd2, 0x48, 0x85 };
// Client key.
const unsigned char kClientKey[kKeySize] =
{ 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd,
0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 };
const char kMessage[] =
// Expected HMAC result using kMessage and kClientKey.
const unsigned char kReceivedHmac[kDigestSize] =
{ 0xb9, 0x3c, 0xd6, 0xf0, 0x49, 0x47, 0xe2, 0x52,
0x59, 0x7a, 0xbd, 0x1f, 0x2b, 0x4c, 0x83, 0xad,
0x86, 0xd2, 0x48, 0x85 };
const char kMessage[] =
"n:1896\ni:goog-malware-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav"
"ar_s_445-450\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_439-444\nu:s"
".ytimg.com/safebrowsing/rd/goog-malware-shavar_s_437\nu:s.ytimg.com/safebrowsi"
@ -47,12 +49,115 @@ const char kMessage[] =
"-phish-shavar_a_2629-2631\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2"
"626-2628\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2625\n";
TEST(HMACTest, HmacSafeBrowsingResponseTest) {
std::string message_data(kMessage);
HMAC hmac(HMAC::SHA1, kClientKey, kKeySize);
base::HMAC hmac(base::HMAC::SHA1, kClientKey, kKeySize);
unsigned char calculated_hmac[kDigestSize];
EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kDigestSize));
EXPECT_EQ(memcmp(kReceivedHmac, calculated_hmac, kDigestSize), 0);
}
// Test cases from RFC 2202 section 3
TEST(HMACTest, RFC2202TestCases) {
const struct {
const char *key;
const int key_len;
const char *data;
const int data_len;
const char *digest;
} cases[] = {
{ "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B"
"\x0B\x0B\x0B\x0B", 20,
"Hi There", 8,
"\xB6\x17\x31\x86\x55\x05\x72\x64\xE2\x8B\xC0\xB6\xFB\x37\x8C\x8E"
"\xF1\x46\xBE\x00" },
{ "Jefe", 4,
"what do ya want for nothing?", 28,
"\xEF\xFC\xDF\x6A\xE5\xEB\x2F\xA2\xD2\x74\x16\xD5\xF1\x84\xDF\x9C"
"\x25\x9A\x7C\x79" },
{ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA", 20,
"\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
"\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
"\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
"\xDD\xDD", 50,
"\x12\x5D\x73\x42\xB9\xAC\x11\xCD\x91\xA3\x9A\xF4\x8A\xA1\x7B\x4F"
"\x63\xF1\x75\xD3" },
{ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19", 25,
"\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
"\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
"\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
"\xCD\xCD", 50,
"\x4C\x90\x07\xF4\x02\x62\x50\xC6\xBC\x84\x14\xF9\xBF\x50\xC8\x6C"
"\x2D\x72\x35\xDA" },
{ "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C"
"\x0C\x0C\x0C\x0C", 20,
"Test With Truncation", 20,
"\x4C\x1A\x03\x42\x4B\x55\xE0\x7F\xE7\xF2\x7B\xE1\xD5\x8B\xB9\x32"
"\x4A\x9A\x5A\x04" },
{ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
80,
"Test Using Larger Than Block-Size Key - Hash Key First", 54,
"\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55"
"\xED\x40\x21\x12" },
{ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
80,
"Test Using Larger Than Block-Size Key and Larger "
"Than One Block-Size Data", 73,
"\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08"
"\xBB\xFF\x1A\x91" }
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
base::HMAC hmac(base::HMAC::SHA1,
reinterpret_cast<const unsigned char*>(cases[i].key),
cases[i].key_len);
std::string data_string(cases[i].data, cases[i].data_len);
unsigned char digest[kDigestSize];
EXPECT_TRUE(hmac.Sign(data_string, digest, kDigestSize));
EXPECT_EQ(memcmp(cases[i].digest, digest, kDigestSize), 0);
}
}
TEST(HMACTest, HMACObjectReuse) {
const char *key =
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA";
const int key_len = 80;
const struct {
const char *data;
const int data_len;
const char *digest;
} cases[] = {
{ "Test Using Larger Than Block-Size Key - Hash Key First", 54,
"\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55"
"\xED\x40\x21\x12" },
{ "Test Using Larger Than Block-Size Key and Larger "
"Than One Block-Size Data", 73,
"\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08"
"\xBB\xFF\x1A\x91" }
};
base::HMAC hmac(base::HMAC::SHA1,
reinterpret_cast<const unsigned char*>(key), key_len);
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
std::string data_string(cases[i].data, cases[i].data_len);
unsigned char digest[kDigestSize];
EXPECT_TRUE(hmac.Sign(data_string, digest, kDigestSize));
EXPECT_EQ(memcmp(cases[i].digest, digest, kDigestSize), 0);
}
}

@ -3,8 +3,14 @@
// found in the LICENSE file.
#include "base/hmac.h"
#include <algorithm>
#include <vector>
#include "base/logging.h"
namespace base {
HMAC::HMAC(HashAlgorithm hash_alg, const unsigned char* key, int key_length)
: hash_alg_(hash_alg),
provider_(NULL),
@ -41,30 +47,36 @@ bool HMAC::Sign(const std::string& data,
}
void HMAC::ImportKey(const unsigned char* key, int key_length) {
if (key_length > kMaxKeySize) {
NOTREACHED();
return;
}
// This code doesn't work on Win2k because PLAINTEXTKEYBLOB and
// CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000. PLAINTEXTKEYBLOB
// allows the import of an unencrypted key. For Win2k support, a cubmbersome
// exponent-of-one key procedure must be used:
// http://support.microsoft.com/kb/228786/en-us
// CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes.
struct {
struct KeyBlob {
BLOBHEADER header;
DWORD key_size;
BYTE key_data[kMaxKeySize];
} key_blob;
key_blob.header.bType = PLAINTEXTKEYBLOB;
key_blob.header.bVersion = CUR_BLOB_VERSION;
key_blob.header.reserved = 0;
key_blob.header.aiKeyAlg = CALG_RC2;
key_blob.key_size = key_length;
memcpy(key_blob.key_data, key, key_length);
BYTE key_data[1];
};
size_t key_blob_size = std::max(offsetof(KeyBlob, key_data) + key_length,
sizeof(KeyBlob));
std::vector<BYTE> key_blob_storage = std::vector<BYTE>(key_blob_size);
KeyBlob* key_blob = reinterpret_cast<KeyBlob*>(&key_blob_storage[0]);
key_blob->header.bType = PLAINTEXTKEYBLOB;
key_blob->header.bVersion = CUR_BLOB_VERSION;
key_blob->header.reserved = 0;
key_blob->header.aiKeyAlg = CALG_RC2;
key_blob->key_size = key_length;
memcpy(key_blob->key_data, key, key_length);
if (!CryptImportKey(provider_,
reinterpret_cast<const BYTE *>(&key_blob),
sizeof(key_blob), 0, 0, &hkey_))
if (!CryptImportKey(provider_, &key_blob_storage[0], key_blob_storage.size(),
0, CRYPT_IPSEC_HMAC_KEY, &hkey_)) {
hkey_ = NULL;
}
// Destroy the copy of the key.
SecureZeroMemory(key_blob.key_data, key_length);
SecureZeroMemory(key_blob->key_data, key_length);
}
bool HMAC::SignWithSHA1(const std::string& data,
@ -95,3 +107,4 @@ bool HMAC::SignWithSHA1(const std::string& data,
return true;
}
} // namespace base

@ -147,9 +147,9 @@ bool VerifyMAC(const std::string& key, const std::string& mac,
std::string decoded_mac;
net::Base64Decode(mac_copy, &decoded_mac);
HMAC hmac(HMAC::SHA1,
reinterpret_cast<const unsigned char*>(decoded_key.data()),
static_cast<int>(decoded_key.length()));
base::HMAC hmac(base::HMAC::SHA1,
reinterpret_cast<const unsigned char*>(decoded_key.data()),
static_cast<int>(decoded_key.length()));
const std::string data_str(data, data_length);
unsigned char digest[kSafeBrowsingMacDigestSize];
if (!hmac.Sign(data_str, digest, kSafeBrowsingMacDigestSize))