blob: 237d896243ac5715c7b3f866348c92c307517c13 [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
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
Shawn Willdend5a24e62017-02-28 13:53:24 -070074bool Blob::isSuperEncrypted() const {
75 return mBlob.flags & KEYSTORE_FLAG_SUPER_ENCRYPTED;
76}
77
Rubin Xu67899de2017-04-21 19:15:13 +010078bool Blob::isCriticalToDeviceEncryption() const {
79 return mBlob.flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
80}
81
Shawn Willdend5a24e62017-02-28 13:53:24 -070082inline uint8_t setFlag(uint8_t flags, bool set, KeyStoreFlag flag) {
83 return set ? (flags | flag) : (flags & ~flag);
84}
85
Shawn Willdenc1d1fee2016-01-26 22:44:56 -070086void Blob::setEncrypted(bool encrypted) {
Shawn Willdend5a24e62017-02-28 13:53:24 -070087 mBlob.flags = setFlag(mBlob.flags, encrypted, KEYSTORE_FLAG_ENCRYPTED);
88}
89
Rubin Xu67899de2017-04-21 19:15:13 +010090void Blob::setSuperEncrypted(bool superEncrypted) {
91 mBlob.flags = setFlag(mBlob.flags, superEncrypted, KEYSTORE_FLAG_SUPER_ENCRYPTED);
92}
Rubin Xu48477952017-04-19 14:16:57 +010093
Rubin Xu67899de2017-04-21 19:15:13 +010094void Blob::setCriticalToDeviceEncryption(bool critical) {
95 mBlob.flags = setFlag(mBlob.flags, critical, KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
Shawn Willdenc1d1fee2016-01-26 22:44:56 -070096}
97
98void Blob::setFallback(bool fallback) {
99 if (fallback) {
100 mBlob.flags |= KEYSTORE_FLAG_FALLBACK;
101 } else {
102 mBlob.flags &= ~KEYSTORE_FLAG_FALLBACK;
103 }
104}
105
106ResponseCode Blob::writeBlob(const char* filename, AES_KEY* aes_key, State state,
107 Entropy* entropy) {
108 ALOGV("writing blob %s", filename);
Shawn Willdend5a24e62017-02-28 13:53:24 -0700109 if (isEncrypted() || isSuperEncrypted()) {
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700110 if (state != STATE_NO_ERROR) {
111 ALOGD("couldn't insert encrypted blob while not unlocked");
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100112 return ResponseCode::LOCKED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700113 }
114
115 if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) {
116 ALOGW("Could not read random data for: %s", filename);
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100117 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700118 }
119 }
120
121 // data includes the value and the value's length
122 size_t dataLength = mBlob.length + sizeof(mBlob.length);
123 // pad data to the AES_BLOCK_SIZE
124 size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE);
125 // encrypted data includes the digest value
126 size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH;
127 // move info after space for padding
128 memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info);
129 // zero padding area
130 memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength);
131
132 mBlob.length = htonl(mBlob.length);
133
Shawn Willdend5a24e62017-02-28 13:53:24 -0700134 if (isEncrypted() || isSuperEncrypted()) {
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700135 MD5(mBlob.digested, digestedLength, mBlob.digest);
136
137 uint8_t vector[AES_BLOCK_SIZE];
138 memcpy(vector, mBlob.vector, AES_BLOCK_SIZE);
139 AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, vector,
140 AES_ENCRYPT);
141 }
142
143 size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
144 size_t fileLength = encryptedLength + headerLength + mBlob.info;
145
146 const char* tmpFileName = ".tmp";
147 int out =
148 TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
149 if (out < 0) {
150 ALOGW("could not open file: %s: %s", tmpFileName, strerror(errno));
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100151 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700152 }
153 size_t writtenBytes = writeFully(out, (uint8_t*)&mBlob, fileLength);
154 if (close(out) != 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100155 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700156 }
157 if (writtenBytes != fileLength) {
158 ALOGW("blob not fully written %zu != %zu", writtenBytes, fileLength);
159 unlink(tmpFileName);
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100160 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700161 }
162 if (rename(tmpFileName, filename) == -1) {
163 ALOGW("could not rename blob to %s: %s", filename, strerror(errno));
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100164 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700165 }
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100166 return ResponseCode::NO_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700167}
168
169ResponseCode Blob::readBlob(const char* filename, AES_KEY* aes_key, State state) {
170 ALOGV("reading blob %s", filename);
171 int in = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
172 if (in < 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100173 return (errno == ENOENT) ? ResponseCode::KEY_NOT_FOUND : ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700174 }
175 // fileLength may be less than sizeof(mBlob) since the in
176 // memory version has extra padding to tolerate rounding up to
177 // the AES_BLOCK_SIZE
178 size_t fileLength = readFully(in, (uint8_t*)&mBlob, sizeof(mBlob));
179 if (close(in) != 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100180 return ResponseCode::SYSTEM_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700181 }
182
183 if (fileLength == 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100184 return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700185 }
186
Shawn Willdend5a24e62017-02-28 13:53:24 -0700187 if ((isEncrypted() || isSuperEncrypted()) && (state != STATE_NO_ERROR)) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100188 return ResponseCode::LOCKED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700189 }
190
191 size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
192 if (fileLength < headerLength) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100193 return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700194 }
195
196 ssize_t encryptedLength = fileLength - (headerLength + mBlob.info);
197 if (encryptedLength < 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100198 return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700199 }
200
201 ssize_t digestedLength;
Shawn Willden45c112a2017-04-17 09:02:42 -0600202 if (isEncrypted() || isSuperEncrypted()) {
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700203 if (encryptedLength % AES_BLOCK_SIZE != 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100204 return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700205 }
206
207 AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, mBlob.vector,
208 AES_DECRYPT);
209 digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
210 uint8_t computedDigest[MD5_DIGEST_LENGTH];
211 MD5(mBlob.digested, digestedLength, computedDigest);
212 if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100213 return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700214 }
215 } else {
216 digestedLength = encryptedLength;
217 }
218
219 ssize_t maxValueLength = digestedLength - sizeof(mBlob.length);
220 mBlob.length = ntohl(mBlob.length);
221 if (mBlob.length < 0 || mBlob.length > maxValueLength) {
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100222 return ResponseCode::VALUE_CORRUPTED;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700223 }
224 if (mBlob.info != 0) {
225 // move info from after padding to after data
226 memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info);
227 }
Janis Danisevskisc7a9fa22016-10-13 18:43:45 +0100228 return ResponseCode::NO_ERROR;
Shawn Willdenc1d1fee2016-01-26 22:44:56 -0700229}