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 
Blob(const uint8_t * value,size_t valueLength,const uint8_t * info,uint8_t infoLength,BlobType type)31 Blob::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 
Blob(blob b)58 Blob::Blob(blob b) {
59     mBlob = b;
60 }
61 
Blob()62 Blob::Blob() {
63     memset(&mBlob, 0, sizeof(mBlob));
64 }
65 
isEncrypted() const66 bool Blob::isEncrypted() const {
67     if (mBlob.version < 2) {
68         return true;
69     }
70 
71     return mBlob.flags & KEYSTORE_FLAG_ENCRYPTED;
72 }
73 
isSuperEncrypted() const74 bool Blob::isSuperEncrypted() const {
75     return mBlob.flags & KEYSTORE_FLAG_SUPER_ENCRYPTED;
76 }
77 
isCriticalToDeviceEncryption() const78 bool Blob::isCriticalToDeviceEncryption() const {
79     return mBlob.flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
80 }
81 
setFlag(uint8_t flags,bool set,KeyStoreFlag flag)82 inline uint8_t setFlag(uint8_t flags, bool set, KeyStoreFlag flag) {
83     return set ? (flags | flag) : (flags & ~flag);
84 }
85 
setEncrypted(bool encrypted)86 void Blob::setEncrypted(bool encrypted) {
87     mBlob.flags = setFlag(mBlob.flags, encrypted, KEYSTORE_FLAG_ENCRYPTED);
88 }
89 
setSuperEncrypted(bool superEncrypted)90 void Blob::setSuperEncrypted(bool superEncrypted) {
91     mBlob.flags = setFlag(mBlob.flags, superEncrypted, KEYSTORE_FLAG_SUPER_ENCRYPTED);
92 }
93 
setCriticalToDeviceEncryption(bool critical)94 void Blob::setCriticalToDeviceEncryption(bool critical) {
95     mBlob.flags = setFlag(mBlob.flags, critical, KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
96 }
97 
setFallback(bool fallback)98 void Blob::setFallback(bool fallback) {
99     if (fallback) {
100         mBlob.flags |= KEYSTORE_FLAG_FALLBACK;
101     } else {
102         mBlob.flags &= ~KEYSTORE_FLAG_FALLBACK;
103     }
104 }
105 
writeBlob(const char * filename,AES_KEY * aes_key,State state,Entropy * entropy)106 ResponseCode Blob::writeBlob(const char* filename, AES_KEY* aes_key, State state,
107                              Entropy* entropy) {
108     ALOGV("writing blob %s", filename);
109     if (isEncrypted() || isSuperEncrypted()) {
110         if (state != STATE_NO_ERROR) {
111             ALOGD("couldn't insert encrypted blob while not unlocked");
112             return ResponseCode::LOCKED;
113         }
114 
115         if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) {
116             ALOGW("Could not read random data for: %s", filename);
117             return ResponseCode::SYSTEM_ERROR;
118         }
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 
134     if (isEncrypted() || isSuperEncrypted()) {
135         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));
151         return ResponseCode::SYSTEM_ERROR;
152     }
153     size_t writtenBytes = writeFully(out, (uint8_t*)&mBlob, fileLength);
154     if (close(out) != 0) {
155         return ResponseCode::SYSTEM_ERROR;
156     }
157     if (writtenBytes != fileLength) {
158         ALOGW("blob not fully written %zu != %zu", writtenBytes, fileLength);
159         unlink(tmpFileName);
160         return ResponseCode::SYSTEM_ERROR;
161     }
162     if (rename(tmpFileName, filename) == -1) {
163         ALOGW("could not rename blob to %s: %s", filename, strerror(errno));
164         return ResponseCode::SYSTEM_ERROR;
165     }
166     return ResponseCode::NO_ERROR;
167 }
168 
readBlob(const char * filename,AES_KEY * aes_key,State state)169 ResponseCode 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) {
173         return (errno == ENOENT) ? ResponseCode::KEY_NOT_FOUND : ResponseCode::SYSTEM_ERROR;
174     }
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) {
180         return ResponseCode::SYSTEM_ERROR;
181     }
182 
183     if (fileLength == 0) {
184         return ResponseCode::VALUE_CORRUPTED;
185     }
186 
187     if ((isEncrypted() || isSuperEncrypted()) && (state != STATE_NO_ERROR)) {
188         return ResponseCode::LOCKED;
189     }
190 
191     size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
192     if (fileLength < headerLength) {
193         return ResponseCode::VALUE_CORRUPTED;
194     }
195 
196     ssize_t encryptedLength = fileLength - (headerLength + mBlob.info);
197     if (encryptedLength < 0) {
198         return ResponseCode::VALUE_CORRUPTED;
199     }
200 
201     ssize_t digestedLength;
202     if (isEncrypted() || isSuperEncrypted()) {
203         if (encryptedLength % AES_BLOCK_SIZE != 0) {
204             return ResponseCode::VALUE_CORRUPTED;
205         }
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) {
213             return ResponseCode::VALUE_CORRUPTED;
214         }
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) {
222         return ResponseCode::VALUE_CORRUPTED;
223     }
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     }
228     return ResponseCode::NO_ERROR;
229 }
230