1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "crypto/hmac.h"
11
12 #include <stddef.h>
13
14 #include <algorithm>
15 #include <string>
16
17 #include "base/check.h"
18 #include "base/check_op.h"
19 #include "base/notreached.h"
20 #include "base/stl_util.h"
21 #include "crypto/openssl_util.h"
22 #include "crypto/secure_util.h"
23 #include "crypto/symmetric_key.h"
24 #include "third_party/boringssl/src/include/openssl/hmac.h"
25
26 namespace crypto {
27
HMAC(HashAlgorithm hash_alg)28 HMAC::HMAC(HashAlgorithm hash_alg) : hash_alg_(hash_alg), initialized_(false) {
29 // Only SHA-1 and SHA-256 hash algorithms are supported now.
30 DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256);
31 }
32
~HMAC()33 HMAC::~HMAC() {
34 // Zero out key copy.
35 key_.assign(key_.size(), 0);
36 base::STLClearObject(&key_);
37 }
38
DigestLength() const39 size_t HMAC::DigestLength() const {
40 switch (hash_alg_) {
41 case SHA1:
42 return 20;
43 case SHA256:
44 return 32;
45 default:
46 NOTREACHED();
47 }
48 }
49
Init(const unsigned char * key,size_t key_length)50 bool HMAC::Init(const unsigned char* key, size_t key_length) {
51 // Init must not be called more than once on the same HMAC object.
52 DCHECK(!initialized_);
53 initialized_ = true;
54 key_.assign(key, key + key_length);
55 return true;
56 }
57
Init(const SymmetricKey * key)58 bool HMAC::Init(const SymmetricKey* key) {
59 return Init(key->key());
60 }
61
Sign(std::string_view data,unsigned char * digest,size_t digest_length) const62 bool HMAC::Sign(std::string_view data,
63 unsigned char* digest,
64 size_t digest_length) const {
65 return Sign(base::as_byte_span(data), base::span(digest, digest_length));
66 }
67
Sign(base::span<const uint8_t> data,base::span<uint8_t> digest) const68 bool HMAC::Sign(base::span<const uint8_t> data,
69 base::span<uint8_t> digest) const {
70 DCHECK(initialized_);
71
72 if (digest.size() > DigestLength())
73 return false;
74
75 ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> result(digest.data(),
76 digest.size());
77 return !!::HMAC(hash_alg_ == SHA1 ? EVP_sha1() : EVP_sha256(), key_.data(),
78 key_.size(), data.data(), data.size(), result.safe_buffer(),
79 nullptr);
80 }
81
Verify(std::string_view data,std::string_view digest) const82 bool HMAC::Verify(std::string_view data, std::string_view digest) const {
83 return Verify(base::as_byte_span(data), base::as_byte_span(digest));
84 }
85
Verify(base::span<const uint8_t> data,base::span<const uint8_t> digest) const86 bool HMAC::Verify(base::span<const uint8_t> data,
87 base::span<const uint8_t> digest) const {
88 if (digest.size() != DigestLength())
89 return false;
90 return VerifyTruncated(data, digest);
91 }
92
VerifyTruncated(std::string_view data,std::string_view digest) const93 bool HMAC::VerifyTruncated(std::string_view data,
94 std::string_view digest) const {
95 return VerifyTruncated(base::as_byte_span(data), base::as_byte_span(digest));
96 }
97
VerifyTruncated(base::span<const uint8_t> data,base::span<const uint8_t> digest) const98 bool HMAC::VerifyTruncated(base::span<const uint8_t> data,
99 base::span<const uint8_t> digest) const {
100 if (digest.empty())
101 return false;
102
103 size_t digest_length = DigestLength();
104 if (digest.size() > digest_length)
105 return false;
106
107 std::array<uint8_t, EVP_MAX_MD_SIZE> computed_buffer;
108 auto computed_digest = base::span(computed_buffer).first(digest.size());
109 if (!Sign(data, computed_digest)) {
110 return false;
111 }
112
113 return SecureMemEqual(digest, computed_digest);
114 }
115
116 namespace hmac {
117
118 namespace {
119
EVPMDForHashKind(crypto::hash::HashKind kind)120 const EVP_MD* EVPMDForHashKind(crypto::hash::HashKind kind) {
121 switch (kind) {
122 case crypto::hash::HashKind::kSha1:
123 return EVP_sha1();
124 case crypto::hash::HashKind::kSha256:
125 return EVP_sha256();
126 case crypto::hash::HashKind::kSha512:
127 return EVP_sha512();
128 }
129 NOTREACHED();
130 }
131
132 } // namespace
133
Sign(crypto::hash::HashKind kind,base::span<const uint8_t> key,base::span<const uint8_t> data,base::span<uint8_t> hmac)134 void Sign(crypto::hash::HashKind kind,
135 base::span<const uint8_t> key,
136 base::span<const uint8_t> data,
137 base::span<uint8_t> hmac) {
138 const EVP_MD* md = EVPMDForHashKind(kind);
139 CHECK_EQ(hmac.size(), EVP_MD_size(md));
140
141 bssl::ScopedHMAC_CTX ctx;
142 CHECK(HMAC_Init_ex(ctx.get(), key.data(), key.size(), EVPMDForHashKind(kind),
143 nullptr));
144 CHECK(HMAC_Update(ctx.get(), data.data(), data.size()));
145 CHECK(HMAC_Final(ctx.get(), hmac.data(), nullptr));
146 }
147
Verify(crypto::hash::HashKind kind,base::span<const uint8_t> key,base::span<const uint8_t> data,base::span<const uint8_t> hmac)148 bool Verify(crypto::hash::HashKind kind,
149 base::span<const uint8_t> key,
150 base::span<const uint8_t> data,
151 base::span<const uint8_t> hmac) {
152 const EVP_MD* md = EVPMDForHashKind(kind);
153 CHECK_EQ(hmac.size(), EVP_MD_size(md));
154
155 std::array<uint8_t, EVP_MAX_MD_SIZE> computed_buf;
156 base::span<uint8_t> computed =
157 base::span(computed_buf).first(EVP_MD_size(md));
158
159 Sign(kind, key, data, computed);
160 return crypto::SecureMemEqual(computed, hmac);
161 }
162
SignSha1(base::span<const uint8_t> key,base::span<const uint8_t> data)163 std::array<uint8_t, crypto::hash::kSha1Size> SignSha1(
164 base::span<const uint8_t> key,
165 base::span<const uint8_t> data) {
166 std::array<uint8_t, crypto::hash::kSha1Size> result;
167 Sign(crypto::hash::HashKind::kSha1, key, data, result);
168 return result;
169 }
170
SignSha256(base::span<const uint8_t> key,base::span<const uint8_t> data)171 std::array<uint8_t, crypto::hash::kSha256Size> SignSha256(
172 base::span<const uint8_t> key,
173 base::span<const uint8_t> data) {
174 std::array<uint8_t, crypto::hash::kSha256Size> result;
175 Sign(crypto::hash::HashKind::kSha256, key, data, result);
176 return result;
177 }
178
SignSha512(base::span<const uint8_t> key,base::span<const uint8_t> data)179 std::array<uint8_t, crypto::hash::kSha512Size> SignSha512(
180 base::span<const uint8_t> key,
181 base::span<const uint8_t> data) {
182 std::array<uint8_t, crypto::hash::kSha512Size> result;
183 Sign(crypto::hash::HashKind::kSha512, key, data, result);
184 return result;
185 }
186
VerifySha1(base::span<const uint8_t> key,base::span<const uint8_t> data,base::span<const uint8_t,20> hmac)187 bool VerifySha1(base::span<const uint8_t> key,
188 base::span<const uint8_t> data,
189 base::span<const uint8_t, 20> hmac) {
190 return Verify(crypto::hash::HashKind::kSha1, key, data, hmac);
191 }
192
VerifySha256(base::span<const uint8_t> key,base::span<const uint8_t> data,base::span<const uint8_t,32> hmac)193 bool VerifySha256(base::span<const uint8_t> key,
194 base::span<const uint8_t> data,
195 base::span<const uint8_t, 32> hmac) {
196 return Verify(crypto::hash::HashKind::kSha256, key, data, hmac);
197 }
198
VerifySha512(base::span<const uint8_t> key,base::span<const uint8_t> data,base::span<const uint8_t,64> hmac)199 bool VerifySha512(base::span<const uint8_t> key,
200 base::span<const uint8_t> data,
201 base::span<const uint8_t, 64> hmac) {
202 return Verify(crypto::hash::HashKind::kSha512, key, data, hmac);
203 }
204
205 } // namespace hmac
206
207 } // namespace crypto
208