blob: e9e6387ede8096f5b7c1ff77800865938a2c12fc [file] [log] [blame]
Shawn Willdenc1d1fee2016-01-26 22:44:56 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "keystore"
18
19#include <arpa/inet.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <string.h>
23
24#include <cutils/log.h>
25
26#include "blob.h"
27#include "entropy.h"
28
29#include "keystore_utils.h"
30
Shawn Willdene9830582017-04-18 10:47:57 -060031namespace {
32
33constexpr size_t kGcmIvSizeBytes = 96 / 8;
34
35template <typename T, void (*FreeFunc)(T*)> struct OpenSslObjectDeleter {
36 void operator()(T* p) { FreeFunc(p); }
37};
38
39#define DEFINE_OPENSSL_OBJECT_POINTER(name) \
40 typedef OpenSslObjectDeleter<name, name##_free> name##_Delete; \
41 typedef std::unique_ptr<name, name##_Delete> name##_Ptr;
42
43DEFINE_OPENSSL_OBJECT_POINTER(EVP_CIPHER_CTX);
44
45#ifdef __clang__
46#define OPTNONE __attribute__((optnone))
47#else // not __clang__
48#define OPTNONE __attribute__((optimize("O0")))
49#endif // not __clang__
50
51class ArrayEraser {
52 public:
53 ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {}
54 OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); }
55
56 private:
57 uint8_t* mArr;
58 size_t mSize;
59};
60
61/*
62 * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit key at 'key', 96-bit IV at 'iv' and write
63 * output to 'out' (which may be the same location as 'in') and 128-bit tag to 'tag'.
64 */
65ResponseCode AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
66 const uint8_t* iv, uint8_t* tag) {
67 const EVP_CIPHER* cipher = EVP_aes_128_gcm();
68 EVP_CIPHER_CTX_Ptr ctx(EVP_CIPHER_CTX_new());
69
70 EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
71 EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
72
73 std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
74 uint8_t* out_pos = out_tmp.get();
75 int out_len;
76
77 EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len);
78 out_pos += out_len;
79 EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len);
80 out_pos += out_len;
81 if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
82 ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len,
83 out_pos - out_tmp.get());
84 return ResponseCode::SYSTEM_ERROR;
85 }
86
87 std::copy(out_tmp.get(), out_pos, out);
88 EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag);
89
90 return ResponseCode::NO_ERROR;
91}
92
93/*
94 * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit key at 'key', 96-bit IV at 'iv', checking
95 * 128-bit tag at 'tag' and writing plaintext to 'out' (which may be the same location as 'in').
96 */
97ResponseCode AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
98 const uint8_t* iv, const uint8_t* tag) {
99 const EVP_CIPHER* cipher = EVP_aes_128_gcm();
100 EVP_CIPHER_CTX_Ptr ctx(EVP_CIPHER_CTX_new());
101
102 EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
103 EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
104 EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag));
105
106 std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
107 ArrayEraser out_eraser(out_tmp.get(), len);
108 uint8_t* out_pos = out_tmp.get();
109 int out_len;
110
111 EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
112 out_pos += out_len;
113 if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
114 ALOGD("Failed to decrypt blob; ciphertext or tag is likely corrupted");
115 return ResponseCode::SYSTEM_ERROR;
116 }
117 out_pos += out_len;
118 if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
119 ALOGD("Encrypted plaintext is the wrong size, expected %zu, got %zd", len,
120 out_pos - out_tmp.get());
121 return ResponseCode::SYSTEM_ERROR;
122 }
123
124 std::copy(out_tmp.get(), out_pos, out);
125
126 return ResponseCode::NO_ERROR;
127}
128
129} // namespace
130
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700131Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
132 BlobType type) {
133 memset(&mBlob, 0, sizeof(mBlob));
Shawn Willdene9830582017-04-18 10:47:57 -0600134 if (valueLength > kValueSize) {
135 valueLength = kValueSize;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700136 ALOGW("Provided blob length too large");
137 }
Shawn Willdene9830582017-04-18 10:47:57 -0600138 if (infoLength + valueLength > kValueSize) {
139 infoLength = kValueSize - valueLength;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700140 ALOGW("Provided info length too large");
141 }
142 mBlob.length = valueLength;
143 memcpy(mBlob.value, value, valueLength);
144
145 mBlob.info = infoLength;
146 memcpy(mBlob.value + valueLength, info, infoLength);
147
148 mBlob.version = CURRENT_BLOB_VERSION;
149 mBlob.type = uint8_t(type);
150
151 if (type == TYPE_MASTER_KEY) {
152 mBlob.flags = KEYSTORE_FLAG_ENCRYPTED;
153 } else {
154 mBlob.flags = KEYSTORE_FLAG_NONE;
155 }
156}
157
Shawn Willdene9830582017-04-18 10:47:57 -0600158Blob::Blob(blobv3 b) {
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700159 mBlob = b;
160}
161
162Blob::Blob() {
163 memset(&mBlob, 0, sizeof(mBlob));
164}
165
166bool Blob::isEncrypted() const {
167 if (mBlob.version < 2) {
168 return true;
169 }
170
171 return mBlob.flags & KEYSTORE_FLAG_ENCRYPTED;
172}
173
Shawn Willdend5a24e62017-02-28 13:53:24 -0700174bool Blob::isSuperEncrypted() const {
175 return mBlob.flags & KEYSTORE_FLAG_SUPER_ENCRYPTED;
176}
177
Rubin Xu67899de2017-04-21 19:15:13 +0100178bool Blob::isCriticalToDeviceEncryption() const {
179 return mBlob.flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
180}
181
Shawn Willdend5a24e62017-02-28 13:53:24 -0700182inline uint8_t setFlag(uint8_t flags, bool set, KeyStoreFlag flag) {
183 return set ? (flags | flag) : (flags & ~flag);
184}
185
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700186void Blob::setEncrypted(bool encrypted) {
Shawn Willdend5a24e62017-02-28 13:53:24 -0700187 mBlob.flags = setFlag(mBlob.flags, encrypted, KEYSTORE_FLAG_ENCRYPTED);
188}
189
Rubin Xu67899de2017-04-21 19:15:13 +0100190void Blob::setSuperEncrypted(bool superEncrypted) {
191 mBlob.flags = setFlag(mBlob.flags, superEncrypted, KEYSTORE_FLAG_SUPER_ENCRYPTED);
192}
Rubin Xu48477952017-04-19 14:16:57 +0100193
Rubin Xu67899de2017-04-21 19:15:13 +0100194void Blob::setCriticalToDeviceEncryption(bool critical) {
195 mBlob.flags = setFlag(mBlob.flags, critical, KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700196}
197
198void Blob::setFallback(bool fallback) {
199 if (fallback) {
200 mBlob.flags |= KEYSTORE_FLAG_FALLBACK;
201 } else {
202 mBlob.flags &= ~KEYSTORE_FLAG_FALLBACK;
203 }
204}
205
Shawn Willdene9830582017-04-18 10:47:57 -0600206ResponseCode Blob::writeBlob(const std::string& filename, const uint8_t* aes_key, State state,
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700207 Entropy* entropy) {
Shawn Willdene9830582017-04-18 10:47:57 -0600208 ALOGV("writing blob %s", filename.c_str());
209
210 const size_t dataLength = mBlob.length;
211 mBlob.length = htonl(mBlob.length);
212
Shawn Willdend5a24e62017-02-28 13:53:24 -0700213 if (isEncrypted() || isSuperEncrypted()) {
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700214 if (state != STATE_NO_ERROR) {
215 ALOGD("couldn't insert encrypted blob while not unlocked");
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100216 return ResponseCode::LOCKED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700217 }
218
Shawn Willdene9830582017-04-18 10:47:57 -0600219 memset(mBlob.initialization_vector, 0, AES_BLOCK_SIZE);
220 if (!entropy->generate_random_data(mBlob.initialization_vector, kGcmIvSizeBytes)) {
221 ALOGW("Could not read random data for: %s", filename.c_str());
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100222 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700223 }
Shawn Willdene9830582017-04-18 10:47:57 -0600224
225 auto rc = AES_gcm_encrypt(mBlob.value /* in */, mBlob.value /* out */, dataLength, aes_key,
226 mBlob.initialization_vector, mBlob.aead_tag);
227 if (rc != ResponseCode::NO_ERROR) return rc;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700228 }
229
Shawn Willdene9830582017-04-18 10:47:57 -0600230 size_t fileLength = offsetof(blobv3, value) + dataLength + mBlob.info;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700231
232 const char* tmpFileName = ".tmp";
233 int out =
234 TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
235 if (out < 0) {
236 ALOGW("could not open file: %s: %s", tmpFileName, strerror(errno));
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100237 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700238 }
Shawn Willdene9830582017-04-18 10:47:57 -0600239
240 const size_t writtenBytes = writeFully(out, (uint8_t*)&mBlob, fileLength);
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700241 if (close(out) != 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100242 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700243 }
244 if (writtenBytes != fileLength) {
245 ALOGW("blob not fully written %zu != %zu", writtenBytes, fileLength);
246 unlink(tmpFileName);
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100247 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700248 }
Shawn Willdene9830582017-04-18 10:47:57 -0600249 if (rename(tmpFileName, filename.c_str()) == -1) {
250 ALOGW("could not rename blob to %s: %s", filename.c_str(), strerror(errno));
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100251 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700252 }
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100253 return ResponseCode::NO_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700254}
255
Shawn Willdene9830582017-04-18 10:47:57 -0600256ResponseCode Blob::readBlob(const std::string& filename, const uint8_t* aes_key, State state) {
257 ALOGV("reading blob %s", filename.c_str());
258 const int in = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY));
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700259 if (in < 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100260 return (errno == ENOENT) ? ResponseCode::KEY_NOT_FOUND : ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700261 }
Shawn Willdene9830582017-04-18 10:47:57 -0600262
263 // fileLength may be less than sizeof(mBlob)
264 const size_t fileLength = readFully(in, (uint8_t*)&mBlob, sizeof(mBlob));
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700265 if (close(in) != 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100266 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700267 }
268
269 if (fileLength == 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100270 return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700271 }
272
Shawn Willdend5a24e62017-02-28 13:53:24 -0700273 if ((isEncrypted() || isSuperEncrypted()) && (state != STATE_NO_ERROR)) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100274 return ResponseCode::LOCKED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700275 }
276
Shawn Willdene9830582017-04-18 10:47:57 -0600277 if (fileLength < offsetof(blobv3, value)) return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700278
Shawn Willdene9830582017-04-18 10:47:57 -0600279 if (mBlob.version == 3) {
280 const ssize_t encryptedLength = ntohl(mBlob.length);
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700281
Shawn Willdene9830582017-04-18 10:47:57 -0600282 if (isEncrypted() || isSuperEncrypted()) {
283 auto rc = AES_gcm_decrypt(mBlob.value /* in */, mBlob.value /* out */, encryptedLength,
284 aes_key, mBlob.initialization_vector, mBlob.aead_tag);
285 if (rc != ResponseCode::NO_ERROR) return rc;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700286 }
Shawn Willdene9830582017-04-18 10:47:57 -0600287 } else if (mBlob.version < 3) {
288 blobv2& blob = reinterpret_cast<blobv2&>(mBlob);
289 const size_t headerLength = offsetof(blobv2, encrypted);
290 const ssize_t encryptedLength = fileLength - headerLength - blob.info;
291 if (encryptedLength < 0) return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700292
Shawn Willdene9830582017-04-18 10:47:57 -0600293 if (isEncrypted() || isSuperEncrypted()) {
294 if (encryptedLength % AES_BLOCK_SIZE != 0) {
295 return ResponseCode::VALUE_CORRUPTED;
296 }
297
298 AES_KEY key;
299 AES_set_decrypt_key(aes_key, kAesKeySize * 8, &key);
300 AES_cbc_encrypt(blob.encrypted, blob.encrypted, encryptedLength, &key, blob.vector,
301 AES_DECRYPT);
302 key = {}; // clear key
303
304 uint8_t computedDigest[MD5_DIGEST_LENGTH];
305 ssize_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
306 MD5(blob.digested, digestedLength, computedDigest);
307 if (memcmp(blob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
308 return ResponseCode::VALUE_CORRUPTED;
309 }
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700310 }
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700311 }
312
Shawn Willdene9830582017-04-18 10:47:57 -0600313 const ssize_t maxValueLength = fileLength - offsetof(blobv3, value) - mBlob.info;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700314 mBlob.length = ntohl(mBlob.length);
Shawn Willdene9830582017-04-18 10:47:57 -0600315 if (mBlob.length < 0 || mBlob.length > maxValueLength ||
316 mBlob.length + mBlob.info + AES_BLOCK_SIZE > static_cast<ssize_t>(sizeof(mBlob.value))) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100317 return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700318 }
Shawn Willdene9830582017-04-18 10:47:57 -0600319
320 if (mBlob.info != 0 && mBlob.version < 3) {
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700321 // move info from after padding to after data
Shawn Willdene9830582017-04-18 10:47:57 -0600322 memmove(mBlob.value + mBlob.length, mBlob.value + maxValueLength, mBlob.info);
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700323 }
Shawn Willdene9830582017-04-18 10:47:57 -0600324
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100325 return ResponseCode::NO_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700326}