blob: 3da88c2f5a6b54dc69b231e4500a21ea7a5beb2a [file] [log] [blame]
Shawn Willden6507c272016-01-05 22:51:48 -07001/*
2 * Copyright (C) 2016 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 "user_state.h"
20
21#include <dirent.h>
22#include <fcntl.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <sys/stat.h>
26
27#include <openssl/evp.h>
28
29#include <cutils/log.h>
30
31#include "blob.h"
32#include "keystore_utils.h"
33
34UserState::UserState(uid_t userId) : mUserId(userId), mRetry(MAX_RETRY) {
35 asprintf(&mUserDir, "user_%u", mUserId);
36 asprintf(&mMasterKeyFile, "%s/.masterkey", mUserDir);
37}
38
39UserState::~UserState() {
40 free(mUserDir);
41 free(mMasterKeyFile);
42}
43
44bool UserState::initialize() {
45 if ((mkdir(mUserDir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) && (errno != EEXIST)) {
46 ALOGE("Could not create directory '%s'", mUserDir);
47 return false;
48 }
49
50 if (access(mMasterKeyFile, R_OK) == 0) {
51 setState(STATE_LOCKED);
52 } else {
53 setState(STATE_UNINITIALIZED);
54 }
55
56 return true;
57}
58
59void UserState::setState(State state) {
60 mState = state;
61 if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) {
62 mRetry = MAX_RETRY;
63 }
64}
65
66void UserState::zeroizeMasterKeysInMemory() {
67 memset(mMasterKey, 0, sizeof(mMasterKey));
68 memset(mSalt, 0, sizeof(mSalt));
69 memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption));
70 memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption));
71}
72
73bool UserState::deleteMasterKey() {
74 setState(STATE_UNINITIALIZED);
75 zeroizeMasterKeysInMemory();
76 return unlink(mMasterKeyFile) == 0 || errno == ENOENT;
77}
78
79ResponseCode UserState::initialize(const android::String8& pw, Entropy* entropy) {
80 if (!generateMasterKey(entropy)) {
81 return SYSTEM_ERROR;
82 }
83 ResponseCode response = writeMasterKey(pw, entropy);
84 if (response != NO_ERROR) {
85 return response;
86 }
87 setupMasterKeys();
88 return ::NO_ERROR;
89}
90
91ResponseCode UserState::copyMasterKey(UserState* src) {
92 if (mState != STATE_UNINITIALIZED) {
93 return ::SYSTEM_ERROR;
94 }
95 if (src->getState() != STATE_NO_ERROR) {
96 return ::SYSTEM_ERROR;
97 }
98 memcpy(mMasterKey, src->mMasterKey, MASTER_KEY_SIZE_BYTES);
99 setupMasterKeys();
100 return copyMasterKeyFile(src);
101}
102
103ResponseCode UserState::copyMasterKeyFile(UserState* src) {
104 /* Copy the master key file to the new user. Unfortunately we don't have the src user's
105 * password so we cannot generate a new file with a new salt.
106 */
107 int in = TEMP_FAILURE_RETRY(open(src->getMasterKeyFileName(), O_RDONLY));
108 if (in < 0) {
109 return ::SYSTEM_ERROR;
110 }
111 blob rawBlob;
112 size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
113 if (close(in) != 0) {
114 return ::SYSTEM_ERROR;
115 }
116 int out =
117 TEMP_FAILURE_RETRY(open(mMasterKeyFile, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
118 if (out < 0) {
119 return ::SYSTEM_ERROR;
120 }
121 size_t outLength = writeFully(out, (uint8_t*)&rawBlob, length);
122 if (close(out) != 0) {
123 return ::SYSTEM_ERROR;
124 }
125 if (outLength != length) {
126 ALOGW("blob not fully written %zu != %zu", outLength, length);
127 unlink(mMasterKeyFile);
128 return ::SYSTEM_ERROR;
129 }
130 return ::NO_ERROR;
131}
132
133ResponseCode UserState::writeMasterKey(const android::String8& pw, Entropy* entropy) {
134 uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
135 generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt);
136 AES_KEY passwordAesKey;
137 AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
138 Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt), TYPE_MASTER_KEY);
139 return masterKeyBlob.writeBlob(mMasterKeyFile, &passwordAesKey, STATE_NO_ERROR, entropy);
140}
141
142ResponseCode UserState::readMasterKey(const android::String8& pw, Entropy* entropy) {
143 int in = TEMP_FAILURE_RETRY(open(mMasterKeyFile, O_RDONLY));
144 if (in < 0) {
145 return SYSTEM_ERROR;
146 }
147
148 // We read the raw blob to just to get the salt to generate the AES key, then we create the Blob
149 // to use with decryptBlob
150 blob rawBlob;
151 size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
152 if (close(in) != 0) {
153 return SYSTEM_ERROR;
154 }
155 // find salt at EOF if present, otherwise we have an old file
156 uint8_t* salt;
157 if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) {
158 salt = (uint8_t*)&rawBlob + length - SALT_SIZE;
159 } else {
160 salt = NULL;
161 }
162 uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
163 generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt);
164 AES_KEY passwordAesKey;
165 AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
166 Blob masterKeyBlob(rawBlob);
167 ResponseCode response = masterKeyBlob.readBlob(mMasterKeyFile, &passwordAesKey, STATE_NO_ERROR);
168 if (response == SYSTEM_ERROR) {
169 return response;
170 }
171 if (response == NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) {
172 // If salt was missing, generate one and write a new master key file with the salt.
173 if (salt == NULL) {
174 if (!generateSalt(entropy)) {
175 return SYSTEM_ERROR;
176 }
177 response = writeMasterKey(pw, entropy);
178 }
179 if (response == NO_ERROR) {
180 memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES);
181 setupMasterKeys();
182 }
183 return response;
184 }
185 if (mRetry <= 0) {
186 reset();
187 return UNINITIALIZED;
188 }
189 --mRetry;
190 switch (mRetry) {
191 case 0:
192 return WRONG_PASSWORD_0;
193 case 1:
194 return WRONG_PASSWORD_1;
195 case 2:
196 return WRONG_PASSWORD_2;
197 case 3:
198 return WRONG_PASSWORD_3;
199 default:
200 return WRONG_PASSWORD_3;
201 }
202}
203
204bool UserState::reset() {
205 DIR* dir = opendir(getUserDirName());
206 if (!dir) {
207 // If the directory doesn't exist then nothing to do.
208 if (errno == ENOENT) {
209 return true;
210 }
211 ALOGW("couldn't open user directory: %s", strerror(errno));
212 return false;
213 }
214
215 struct dirent* file;
216 while ((file = readdir(dir)) != NULL) {
217 // skip . and ..
218 if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
219 continue;
220 }
221
222 unlinkat(dirfd(dir), file->d_name, 0);
223 }
224 closedir(dir);
225 return true;
226}
227
228void UserState::generateKeyFromPassword(uint8_t* key, ssize_t keySize, const android::String8& pw,
229 uint8_t* salt) {
230 size_t saltSize;
231 if (salt != NULL) {
232 saltSize = SALT_SIZE;
233 } else {
234 // Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
235 salt = (uint8_t*)"keystore";
236 // sizeof = 9, not strlen = 8
237 saltSize = sizeof("keystore");
238 }
239
240 PKCS5_PBKDF2_HMAC_SHA1(reinterpret_cast<const char*>(pw.string()), pw.length(), salt, saltSize,
241 8192, keySize, key);
242}
243
244bool UserState::generateSalt(Entropy* entropy) {
245 return entropy->generate_random_data(mSalt, sizeof(mSalt));
246}
247
248bool UserState::generateMasterKey(Entropy* entropy) {
249 if (!entropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) {
250 return false;
251 }
252 if (!generateSalt(entropy)) {
253 return false;
254 }
255 return true;
256}
257
258void UserState::setupMasterKeys() {
259 AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption);
260 AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption);
261 setState(STATE_NO_ERROR);
262}