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 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 58 Blob::Blob(blob b) { 59 mBlob = b; 60 } 61 62 Blob::Blob() { 63 memset(&mBlob, 0, sizeof(mBlob)); 64 } 65 66 bool Blob::isEncrypted() const { 67 if (mBlob.version < 2) { 68 return true; 69 } 70 71 return mBlob.flags & KEYSTORE_FLAG_ENCRYPTED; 72 } 73 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 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 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 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