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
31Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
32           BlobType type) {
33    memset(&mBlob, 0, sizeof(mBlob));
34    if (valueLength > VALUE_SIZE) {
35        valueLength = VALUE_SIZE;
36        ALOGW("Provided blob length too large");
37    }
38    if (infoLength + valueLength > VALUE_SIZE) {
39        infoLength = VALUE_SIZE - valueLength;
40        ALOGW("Provided info length too large");
41    }
42    mBlob.length = valueLength;
43    memcpy(mBlob.value, value, valueLength);
44
45    mBlob.info = infoLength;
46    memcpy(mBlob.value + valueLength, info, infoLength);
47
48    mBlob.version = CURRENT_BLOB_VERSION;
49    mBlob.type = uint8_t(type);
50
51    if (type == TYPE_MASTER_KEY) {
52        mBlob.flags = KEYSTORE_FLAG_ENCRYPTED;
53    } else {
54        mBlob.flags = KEYSTORE_FLAG_NONE;
55    }
56}
57
58Blob::Blob(blob b) {
59    mBlob = b;
60}
61
62Blob::Blob() {
63    memset(&mBlob, 0, sizeof(mBlob));
64}
65
66bool Blob::isEncrypted() const {
67    if (mBlob.version < 2) {
68        return true;
69    }
70
71    return mBlob.flags & KEYSTORE_FLAG_ENCRYPTED;
72}
73
74void Blob::setEncrypted(bool encrypted) {
75    if (encrypted) {
76        mBlob.flags |= KEYSTORE_FLAG_ENCRYPTED;
77    } else {
78        mBlob.flags &= ~KEYSTORE_FLAG_ENCRYPTED;
79    }
80}
81
82void Blob::setFallback(bool fallback) {
83    if (fallback) {
84        mBlob.flags |= KEYSTORE_FLAG_FALLBACK;
85    } else {
86        mBlob.flags &= ~KEYSTORE_FLAG_FALLBACK;
87    }
88}
89
90ResponseCode Blob::writeBlob(const char* filename, AES_KEY* aes_key, State state,
91                             Entropy* entropy) {
92    ALOGV("writing blob %s", filename);
93    if (isEncrypted()) {
94        if (state != STATE_NO_ERROR) {
95            ALOGD("couldn't insert encrypted blob while not unlocked");
96            return LOCKED;
97        }
98
99        if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) {
100            ALOGW("Could not read random data for: %s", filename);
101            return SYSTEM_ERROR;
102        }
103    }
104
105    // data includes the value and the value's length
106    size_t dataLength = mBlob.length + sizeof(mBlob.length);
107    // pad data to the AES_BLOCK_SIZE
108    size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE);
109    // encrypted data includes the digest value
110    size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH;
111    // move info after space for padding
112    memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info);
113    // zero padding area
114    memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength);
115
116    mBlob.length = htonl(mBlob.length);
117
118    if (isEncrypted()) {
119        MD5(mBlob.digested, digestedLength, mBlob.digest);
120
121        uint8_t vector[AES_BLOCK_SIZE];
122        memcpy(vector, mBlob.vector, AES_BLOCK_SIZE);
123        AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, vector,
124                        AES_ENCRYPT);
125    }
126
127    size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
128    size_t fileLength = encryptedLength + headerLength + mBlob.info;
129
130    const char* tmpFileName = ".tmp";
131    int out =
132        TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
133    if (out < 0) {
134        ALOGW("could not open file: %s: %s", tmpFileName, strerror(errno));
135        return SYSTEM_ERROR;
136    }
137    size_t writtenBytes = writeFully(out, (uint8_t*)&mBlob, fileLength);
138    if (close(out) != 0) {
139        return SYSTEM_ERROR;
140    }
141    if (writtenBytes != fileLength) {
142        ALOGW("blob not fully written %zu != %zu", writtenBytes, fileLength);
143        unlink(tmpFileName);
144        return SYSTEM_ERROR;
145    }
146    if (rename(tmpFileName, filename) == -1) {
147        ALOGW("could not rename blob to %s: %s", filename, strerror(errno));
148        return SYSTEM_ERROR;
149    }
150    return NO_ERROR;
151}
152
153ResponseCode Blob::readBlob(const char* filename, AES_KEY* aes_key, State state) {
154    ALOGV("reading blob %s", filename);
155    int in = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
156    if (in < 0) {
157        return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR;
158    }
159    // fileLength may be less than sizeof(mBlob) since the in
160    // memory version has extra padding to tolerate rounding up to
161    // the AES_BLOCK_SIZE
162    size_t fileLength = readFully(in, (uint8_t*)&mBlob, sizeof(mBlob));
163    if (close(in) != 0) {
164        return SYSTEM_ERROR;
165    }
166
167    if (fileLength == 0) {
168        return VALUE_CORRUPTED;
169    }
170
171    if (isEncrypted() && (state != STATE_NO_ERROR)) {
172        return LOCKED;
173    }
174
175    size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
176    if (fileLength < headerLength) {
177        return VALUE_CORRUPTED;
178    }
179
180    ssize_t encryptedLength = fileLength - (headerLength + mBlob.info);
181    if (encryptedLength < 0) {
182        return VALUE_CORRUPTED;
183    }
184
185    ssize_t digestedLength;
186    if (isEncrypted()) {
187        if (encryptedLength % AES_BLOCK_SIZE != 0) {
188            return VALUE_CORRUPTED;
189        }
190
191        AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, mBlob.vector,
192                        AES_DECRYPT);
193        digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
194        uint8_t computedDigest[MD5_DIGEST_LENGTH];
195        MD5(mBlob.digested, digestedLength, computedDigest);
196        if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
197            return VALUE_CORRUPTED;
198        }
199    } else {
200        digestedLength = encryptedLength;
201    }
202
203    ssize_t maxValueLength = digestedLength - sizeof(mBlob.length);
204    mBlob.length = ntohl(mBlob.length);
205    if (mBlob.length < 0 || mBlob.length > maxValueLength) {
206        return VALUE_CORRUPTED;
207    }
208    if (mBlob.info != 0) {
209        // move info from after padding to after data
210        memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info);
211    }
212    return ::NO_ERROR;
213}
214