• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
setEncrypted(bool encrypted)74 void Blob::setEncrypted(bool encrypted) {
75     if (encrypted) {
76         mBlob.flags |= KEYSTORE_FLAG_ENCRYPTED;
77     } else {
78         mBlob.flags &= ~KEYSTORE_FLAG_ENCRYPTED;
79     }
80 }
81 
setFallback(bool fallback)82 void Blob::setFallback(bool fallback) {
83     if (fallback) {
84         mBlob.flags |= KEYSTORE_FLAG_FALLBACK;
85     } else {
86         mBlob.flags &= ~KEYSTORE_FLAG_FALLBACK;
87     }
88 }
89 
writeBlob(const char * filename,AES_KEY * aes_key,State state,Entropy * entropy)90 ResponseCode 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 
readBlob(const char * filename,AES_KEY * aes_key,State state)153 ResponseCode 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