1/*
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
31namespace {
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#if defined(__clang__)
46#define OPTNONE __attribute__((optnone))
47#elif defined(__GNUC__)
48#define OPTNONE __attribute__((optimize("O0")))
49#else
50#error Need a definition for OPTNONE
51#endif
52
53class ArrayEraser {
54  public:
55    ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {}
56    OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); }
57
58  private:
59    volatile uint8_t* mArr;
60    size_t mSize;
61};
62
63/*
64 * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit key at 'key', 96-bit IV at 'iv' and write
65 * output to 'out' (which may be the same location as 'in') and 128-bit tag to 'tag'.
66 */
67ResponseCode AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
68                             const uint8_t* iv, uint8_t* tag) {
69    const EVP_CIPHER* cipher = EVP_aes_128_gcm();
70    EVP_CIPHER_CTX_Ptr ctx(EVP_CIPHER_CTX_new());
71
72    EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
73    EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
74
75    std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
76    uint8_t* out_pos = out_tmp.get();
77    int out_len;
78
79    EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len);
80    out_pos += out_len;
81    EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len);
82    out_pos += out_len;
83    if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
84        ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len,
85              out_pos - out_tmp.get());
86        return ResponseCode::SYSTEM_ERROR;
87    }
88
89    std::copy(out_tmp.get(), out_pos, out);
90    EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag);
91
92    return ResponseCode::NO_ERROR;
93}
94
95/*
96 * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit key at 'key', 96-bit IV at 'iv', checking
97 * 128-bit tag at 'tag' and writing plaintext to 'out' (which may be the same location as 'in').
98 */
99ResponseCode AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
100                             const uint8_t* iv, const uint8_t* tag) {
101    const EVP_CIPHER* cipher = EVP_aes_128_gcm();
102    EVP_CIPHER_CTX_Ptr ctx(EVP_CIPHER_CTX_new());
103
104    EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
105    EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
106    EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag));
107
108    std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
109    ArrayEraser out_eraser(out_tmp.get(), len);
110    uint8_t* out_pos = out_tmp.get();
111    int out_len;
112
113    EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
114    out_pos += out_len;
115    if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
116        ALOGD("Failed to decrypt blob; ciphertext or tag is likely corrupted");
117        return ResponseCode::SYSTEM_ERROR;
118    }
119    out_pos += out_len;
120    if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
121        ALOGD("Encrypted plaintext is the wrong size, expected %zu, got %zd", len,
122              out_pos - out_tmp.get());
123        return ResponseCode::SYSTEM_ERROR;
124    }
125
126    std::copy(out_tmp.get(), out_pos, out);
127
128    return ResponseCode::NO_ERROR;
129}
130
131}  // namespace
132
133Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
134           BlobType type) {
135    memset(&mBlob, 0, sizeof(mBlob));
136    if (valueLength > kValueSize) {
137        valueLength = kValueSize;
138        ALOGW("Provided blob length too large");
139    }
140    if (infoLength + valueLength > kValueSize) {
141        infoLength = kValueSize - valueLength;
142        ALOGW("Provided info length too large");
143    }
144    mBlob.length = valueLength;
145    memcpy(mBlob.value, value, valueLength);
146
147    mBlob.info = infoLength;
148    memcpy(mBlob.value + valueLength, info, infoLength);
149
150    mBlob.version = CURRENT_BLOB_VERSION;
151    mBlob.type = uint8_t(type);
152
153    if (type == TYPE_MASTER_KEY) {
154        mBlob.flags = KEYSTORE_FLAG_ENCRYPTED;
155    } else {
156        mBlob.flags = KEYSTORE_FLAG_NONE;
157    }
158}
159
160Blob::Blob(blobv3 b) {
161    mBlob = b;
162}
163
164Blob::Blob() {
165    memset(&mBlob, 0, sizeof(mBlob));
166}
167
168bool Blob::isEncrypted() const {
169    if (mBlob.version < 2) {
170        return true;
171    }
172
173    return mBlob.flags & KEYSTORE_FLAG_ENCRYPTED;
174}
175
176bool Blob::isSuperEncrypted() const {
177    return mBlob.flags & KEYSTORE_FLAG_SUPER_ENCRYPTED;
178}
179
180bool Blob::isCriticalToDeviceEncryption() const {
181    return mBlob.flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
182}
183
184inline uint8_t setFlag(uint8_t flags, bool set, KeyStoreFlag flag) {
185    return set ? (flags | flag) : (flags & ~flag);
186}
187
188void Blob::setEncrypted(bool encrypted) {
189    mBlob.flags = setFlag(mBlob.flags, encrypted, KEYSTORE_FLAG_ENCRYPTED);
190}
191
192void Blob::setSuperEncrypted(bool superEncrypted) {
193    mBlob.flags = setFlag(mBlob.flags, superEncrypted, KEYSTORE_FLAG_SUPER_ENCRYPTED);
194}
195
196void Blob::setCriticalToDeviceEncryption(bool critical) {
197    mBlob.flags = setFlag(mBlob.flags, critical, KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
198}
199
200void Blob::setFallback(bool fallback) {
201    if (fallback) {
202        mBlob.flags |= KEYSTORE_FLAG_FALLBACK;
203    } else {
204        mBlob.flags &= ~KEYSTORE_FLAG_FALLBACK;
205    }
206}
207
208ResponseCode Blob::writeBlob(const std::string& filename, const uint8_t* aes_key, State state,
209                             Entropy* entropy) {
210    ALOGV("writing blob %s", filename.c_str());
211
212    const size_t dataLength = mBlob.length;
213    mBlob.length = htonl(mBlob.length);
214
215    if (isEncrypted() || isSuperEncrypted()) {
216        if (state != STATE_NO_ERROR) {
217            ALOGD("couldn't insert encrypted blob while not unlocked");
218            return ResponseCode::LOCKED;
219        }
220
221        memset(mBlob.initialization_vector, 0, AES_BLOCK_SIZE);
222        if (!entropy->generate_random_data(mBlob.initialization_vector, kGcmIvSizeBytes)) {
223            ALOGW("Could not read random data for: %s", filename.c_str());
224            return ResponseCode::SYSTEM_ERROR;
225        }
226
227        auto rc = AES_gcm_encrypt(mBlob.value /* in */, mBlob.value /* out */, dataLength, aes_key,
228                                  mBlob.initialization_vector, mBlob.aead_tag);
229        if (rc != ResponseCode::NO_ERROR) return rc;
230    }
231
232    size_t fileLength = offsetof(blobv3, value) + dataLength + mBlob.info;
233
234    const char* tmpFileName = ".tmp";
235    int out =
236        TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
237    if (out < 0) {
238        ALOGW("could not open file: %s: %s", tmpFileName, strerror(errno));
239        return ResponseCode::SYSTEM_ERROR;
240    }
241
242    const size_t writtenBytes = writeFully(out, (uint8_t*)&mBlob, fileLength);
243    if (close(out) != 0) {
244        return ResponseCode::SYSTEM_ERROR;
245    }
246    if (writtenBytes != fileLength) {
247        ALOGW("blob not fully written %zu != %zu", writtenBytes, fileLength);
248        unlink(tmpFileName);
249        return ResponseCode::SYSTEM_ERROR;
250    }
251    if (rename(tmpFileName, filename.c_str()) == -1) {
252        ALOGW("could not rename blob to %s: %s", filename.c_str(), strerror(errno));
253        return ResponseCode::SYSTEM_ERROR;
254    }
255    return ResponseCode::NO_ERROR;
256}
257
258ResponseCode Blob::readBlob(const std::string& filename, const uint8_t* aes_key, State state) {
259    ALOGV("reading blob %s", filename.c_str());
260    const int in = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY));
261    if (in < 0) {
262        return (errno == ENOENT) ? ResponseCode::KEY_NOT_FOUND : ResponseCode::SYSTEM_ERROR;
263    }
264
265    // fileLength may be less than sizeof(mBlob)
266    const size_t fileLength = readFully(in, (uint8_t*)&mBlob, sizeof(mBlob));
267    if (close(in) != 0) {
268        return ResponseCode::SYSTEM_ERROR;
269    }
270
271    if (fileLength == 0) {
272        return ResponseCode::VALUE_CORRUPTED;
273    }
274
275    if ((isEncrypted() || isSuperEncrypted())) {
276        if (state == STATE_LOCKED) return ResponseCode::LOCKED;
277        if (state == STATE_UNINITIALIZED) return ResponseCode::UNINITIALIZED;
278    }
279
280    if (fileLength < offsetof(blobv3, value)) return ResponseCode::VALUE_CORRUPTED;
281
282    if (mBlob.version == 3) {
283        const ssize_t encryptedLength = ntohl(mBlob.length);
284
285        if (isEncrypted() || isSuperEncrypted()) {
286            auto rc = AES_gcm_decrypt(mBlob.value /* in */, mBlob.value /* out */, encryptedLength,
287                                      aes_key, mBlob.initialization_vector, mBlob.aead_tag);
288            if (rc != ResponseCode::NO_ERROR) return rc;
289        }
290    } else if (mBlob.version < 3) {
291        blobv2& blob = reinterpret_cast<blobv2&>(mBlob);
292        const size_t headerLength = offsetof(blobv2, encrypted);
293        const ssize_t encryptedLength = fileLength - headerLength - blob.info;
294        if (encryptedLength < 0) return ResponseCode::VALUE_CORRUPTED;
295
296        if (isEncrypted() || isSuperEncrypted()) {
297            if (encryptedLength % AES_BLOCK_SIZE != 0) {
298                return ResponseCode::VALUE_CORRUPTED;
299            }
300
301            AES_KEY key;
302            AES_set_decrypt_key(aes_key, kAesKeySize * 8, &key);
303            AES_cbc_encrypt(blob.encrypted, blob.encrypted, encryptedLength, &key, blob.vector,
304                            AES_DECRYPT);
305            key = {};  // clear key
306
307            uint8_t computedDigest[MD5_DIGEST_LENGTH];
308            ssize_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
309            MD5(blob.digested, digestedLength, computedDigest);
310            if (memcmp(blob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
311                return ResponseCode::VALUE_CORRUPTED;
312            }
313        }
314    }
315
316    const ssize_t maxValueLength = fileLength - offsetof(blobv3, value) - mBlob.info;
317    mBlob.length = ntohl(mBlob.length);
318    if (mBlob.length < 0 || mBlob.length > maxValueLength ||
319        mBlob.length + mBlob.info + AES_BLOCK_SIZE > static_cast<ssize_t>(sizeof(mBlob.value))) {
320        return ResponseCode::VALUE_CORRUPTED;
321    }
322
323    if (mBlob.info != 0 && mBlob.version < 3) {
324        // move info from after padding to after data
325        memmove(mBlob.value + mBlob.length, mBlob.value + maxValueLength, mBlob.info);
326    }
327
328    return ResponseCode::NO_ERROR;
329}
330