1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16
17 #include "tink/aead/aes_ctr_hmac_aead_key_manager.h"
18
19 #include <cstdint>
20 #include <functional>
21 #include <map>
22 #include <memory>
23 #include <string>
24 #include <utility>
25
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/str_cat.h"
29 #include "tink/aead.h"
30 #include "tink/mac.h"
31 #include "tink/mac/hmac_key_manager.h"
32 #include "tink/subtle/aes_ctr_boringssl.h"
33 #include "tink/subtle/encrypt_then_authenticate.h"
34 #include "tink/subtle/ind_cpa_cipher.h"
35 #include "tink/subtle/random.h"
36 #include "tink/util/enums.h"
37 #include "tink/util/input_stream_util.h"
38 #include "tink/util/secret_data.h"
39 #include "tink/util/status.h"
40 #include "tink/util/statusor.h"
41 #include "tink/util/validation.h"
42 #include "proto/aes_ctr.pb.h"
43 #include "proto/aes_ctr_hmac_aead.pb.h"
44 #include "proto/common.pb.h"
45 #include "proto/hmac.pb.h"
46
47 namespace crypto {
48 namespace tink {
49
50 namespace {
51 constexpr int kMinKeySizeInBytes = 16;
52 constexpr int kMinIvSizeInBytes = 12;
53 constexpr int kMinTagSizeInBytes = 10;
54 }
55
56 using ::crypto::tink::util::Enums;
57 using ::crypto::tink::util::Status;
58 using ::crypto::tink::util::StatusOr;
59 using ::google::crypto::tink::AesCtrHmacAeadKey;
60 using ::google::crypto::tink::AesCtrHmacAeadKeyFormat;
61 using ::google::crypto::tink::AesCtrKey;
62 using ::google::crypto::tink::HashType;
63 using ::google::crypto::tink::HmacKey;
64
CreateKey(const AesCtrHmacAeadKeyFormat & aes_ctr_hmac_aead_key_format) const65 StatusOr<AesCtrHmacAeadKey> AesCtrHmacAeadKeyManager::CreateKey(
66 const AesCtrHmacAeadKeyFormat& aes_ctr_hmac_aead_key_format) const {
67 AesCtrHmacAeadKey aes_ctr_hmac_aead_key;
68 aes_ctr_hmac_aead_key.set_version(get_version());
69
70 // Generate AesCtrKey.
71 auto aes_ctr_key = aes_ctr_hmac_aead_key.mutable_aes_ctr_key();
72 aes_ctr_key->set_version(get_version());
73 *(aes_ctr_key->mutable_params()) =
74 aes_ctr_hmac_aead_key_format.aes_ctr_key_format().params();
75 aes_ctr_key->set_key_value(subtle::Random::GetRandomBytes(
76 aes_ctr_hmac_aead_key_format.aes_ctr_key_format().key_size()));
77
78 // Generate HmacKey.
79 auto hmac_key_or = HmacKeyManager().CreateKey(
80 aes_ctr_hmac_aead_key_format.hmac_key_format());
81 if (!hmac_key_or.status().ok()) {
82 return hmac_key_or.status();
83 }
84 *aes_ctr_hmac_aead_key.mutable_hmac_key() = hmac_key_or.value();
85
86 return aes_ctr_hmac_aead_key;
87 }
88
Create(const AesCtrHmacAeadKey & key) const89 StatusOr<std::unique_ptr<Aead>> AesCtrHmacAeadKeyManager::AeadFactory::Create(
90 const AesCtrHmacAeadKey& key) const {
91 auto aes_ctr_result = subtle::AesCtrBoringSsl::New(
92 util::SecretDataFromStringView(key.aes_ctr_key().key_value()),
93 key.aes_ctr_key().params().iv_size());
94 if (!aes_ctr_result.ok()) return aes_ctr_result.status();
95
96 auto hmac_result = HmacKeyManager().GetPrimitive<Mac>(key.hmac_key());
97 if (!hmac_result.ok()) return hmac_result.status();
98
99 auto cipher_res = subtle::EncryptThenAuthenticate::New(
100 std::move(aes_ctr_result.value()), std::move(hmac_result.value()),
101 key.hmac_key().params().tag_size());
102 if (!cipher_res.ok()) {
103 return cipher_res.status();
104 }
105 return std::move(cipher_res.value());
106 }
107
ValidateKey(const AesCtrHmacAeadKey & key) const108 Status AesCtrHmacAeadKeyManager::ValidateKey(
109 const AesCtrHmacAeadKey& key) const {
110 Status status = ValidateVersion(key.version(), get_version());
111 if (!status.ok()) return status;
112
113 status = ValidateVersion(key.aes_ctr_key().version(), get_version());
114 if (!status.ok()) return status;
115
116 // Validate AesCtrKey.
117 auto aes_ctr_key = key.aes_ctr_key();
118 uint32_t aes_key_size = aes_ctr_key.key_value().size();
119 status = ValidateAesKeySize(aes_key_size);
120 if (!status.ok()) {
121 return status;
122 }
123 if (aes_ctr_key.params().iv_size() < kMinIvSizeInBytes ||
124 aes_ctr_key.params().iv_size() > 16) {
125 return util::Status(absl::StatusCode::kInvalidArgument,
126 "Invalid AesCtrHmacAeadKey: IV size out of range.");
127 }
128 return HmacKeyManager().ValidateKey(key.hmac_key());
129 }
130
ValidateKeyFormat(const AesCtrHmacAeadKeyFormat & key_format) const131 Status AesCtrHmacAeadKeyManager::ValidateKeyFormat(
132 const AesCtrHmacAeadKeyFormat& key_format) const {
133 // Validate AesCtrKeyFormat.
134 auto aes_ctr_key_format = key_format.aes_ctr_key_format();
135 auto status = ValidateAesKeySize(aes_ctr_key_format.key_size());
136 if (!status.ok()) {
137 return status;
138 }
139 if (aes_ctr_key_format.params().iv_size() < kMinIvSizeInBytes ||
140 aes_ctr_key_format.params().iv_size() > 16) {
141 return util::Status(
142 absl::StatusCode::kInvalidArgument,
143 "Invalid AesCtrHmacAeadKeyFormat: IV size out of range.");
144 }
145
146 // Validate HmacKeyFormat.
147 auto hmac_key_format = key_format.hmac_key_format();
148 if (hmac_key_format.key_size() < kMinKeySizeInBytes) {
149 return util::Status(
150 absl::StatusCode::kInvalidArgument,
151 "Invalid AesCtrHmacAeadKeyFormat: HMAC key_size is too small.");
152 }
153 auto params = hmac_key_format.params();
154 if (params.tag_size() < kMinTagSizeInBytes) {
155 return util::Status(absl::StatusCode::kInvalidArgument,
156 absl::StrCat("Invalid HmacParams: tag_size ",
157 params.tag_size(), " is too small."));
158 }
159 std::map<HashType, uint32_t> max_tag_size = {{HashType::SHA1, 20},
160 {HashType::SHA224, 28},
161 {HashType::SHA256, 32},
162 {HashType::SHA384, 48},
163 {HashType::SHA512, 64}};
164 if (max_tag_size.find(params.hash()) == max_tag_size.end()) {
165 return util::Status(
166 absl::StatusCode::kInvalidArgument,
167 absl::StrCat("Invalid HmacParams: HashType '",
168 Enums::HashName(params.hash()), "' not supported."));
169 } else {
170 if (params.tag_size() > max_tag_size[params.hash()]) {
171 return util::Status(
172 absl::StatusCode::kInvalidArgument,
173 absl::StrCat("Invalid HmacParams: tag_size ", params.tag_size(),
174 " is too big for HashType '",
175 Enums::HashName(params.hash()), "'."));
176 }
177 }
178
179 return HmacKeyManager().ValidateKeyFormat(key_format.hmac_key_format());
180 }
181
182 // To ensure the resulting key can provide key commitment, the AES-CTR key must
183 // be derived first, then the HMAC key. This avoids situation where it's
184 // possible to brute force raw key material so that the 32th byte of the
185 // keystream is a 0 Give party A a key with this raw key material, saying that
186 // the size of the HMAC key is 32 bytes and the size of the AES key is 16 bytes.
187 // Give party B a key with this raw key material, saying that the size of the
188 // HMAC key is 31 bytes and the size of the AES key is 16 bytes. Since HMAC will
189 // pad the key with zeroes, this leads to both parties using the same HMAC key,
190 // but a different AES key (offset by 1 byte)
DeriveKey(const AesCtrHmacAeadKeyFormat & key_format,InputStream * input_stream) const191 StatusOr<AesCtrHmacAeadKey> AesCtrHmacAeadKeyManager::DeriveKey(
192 const AesCtrHmacAeadKeyFormat& key_format,
193 InputStream* input_stream) const {
194 Status status = ValidateKeyFormat(key_format);
195 if (!status.ok()) {
196 return status;
197 }
198 StatusOr<std::string> aes_ctr_randomness = ReadBytesFromStream(
199 key_format.aes_ctr_key_format().key_size(), input_stream);
200 if (!aes_ctr_randomness.ok()) {
201 if (absl::IsOutOfRange(aes_ctr_randomness.status())) {
202 return crypto::tink::util::Status(
203 absl::StatusCode::kInvalidArgument,
204 "Could not get enough pseudorandomness from input stream");
205 }
206 return aes_ctr_randomness.status();
207 }
208 StatusOr<HmacKey> hmac_key =
209 HmacKeyManager().DeriveKey(key_format.hmac_key_format(), input_stream);
210 if (!hmac_key.ok()) {
211 return hmac_key.status();
212 }
213
214 google::crypto::tink::AesCtrHmacAeadKey key;
215 key.set_version(get_version());
216 *key.mutable_hmac_key() = hmac_key.value();
217
218 AesCtrKey* aes_ctr_key = key.mutable_aes_ctr_key();
219 aes_ctr_key->set_version(get_version());
220 aes_ctr_key->set_key_value(aes_ctr_randomness.value());
221 *aes_ctr_key->mutable_params() = key_format.aes_ctr_key_format().params();
222
223 status = ValidateKey(key);
224 if (!status.ok()) {
225 return status;
226 }
227 return key;
228 }
229
230 } // namespace tink
231 } // namespace crypto
232