Home | History | Annotate | Download | only in keystore
      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