Commit e4f2fa54 authored by Shawn Willden's avatar Shawn Willden Committed by Gerrit Code Review
Browse files

Merge "Refactor keystore."

parents 93c57ca1 6507c27a
......@@ -27,7 +27,18 @@ ifeq ($(USE_32_BIT_KEYSTORE), true)
LOCAL_MULTILIB := 32
endif
LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
LOCAL_SRC_FILES := keystore.cpp keyblob_utils.cpp operation.cpp auth_token_table.cpp
LOCAL_SRC_FILES := \
auth_token_table.cpp \
blob.cpp \
entropy.cpp \
key_store_service.cpp \
keyblob_utils.cpp \
keystore.cpp \
keystore_main.cpp \
keystore_utils.cpp \
operation.cpp \
permissions.cpp \
user_state.cpp
LOCAL_SHARED_LIBRARIES := \
libbinder \
libcutils \
......
......@@ -20,8 +20,8 @@
#include <hardware/hw_auth_token.h>
#include <keymaster/authorization_set.h>
#ifndef SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H
#define SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H
#ifndef KEYSTORE_AUTH_TOKEN_TABLE_H_
#define KEYSTORE_AUTH_TOKEN_TABLE_H_
namespace keymaster {
......@@ -160,4 +160,4 @@ class AuthTokenTable {
} // namespace keymaster
#endif // SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H
#endif // KEYSTORE_AUTH_TOKEN_TABLE_H_
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "keystore"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <cutils/log.h>
#include "blob.h"
#include "entropy.h"
#include "keystore_utils.h"
Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
BlobType type) {
memset(&mBlob, 0, sizeof(mBlob));
if (valueLength > VALUE_SIZE) {
valueLength = VALUE_SIZE;
ALOGW("Provided blob length too large");
}
if (infoLength + valueLength > VALUE_SIZE) {
infoLength = VALUE_SIZE - valueLength;
ALOGW("Provided info length too large");
}
mBlob.length = valueLength;
memcpy(mBlob.value, value, valueLength);
mBlob.info = infoLength;
memcpy(mBlob.value + valueLength, info, infoLength);
mBlob.version = CURRENT_BLOB_VERSION;
mBlob.type = uint8_t(type);
if (type == TYPE_MASTER_KEY) {
mBlob.flags = KEYSTORE_FLAG_ENCRYPTED;
} else {
mBlob.flags = KEYSTORE_FLAG_NONE;
}
}
Blob::Blob(blob b) {
mBlob = b;
}
Blob::Blob() {
memset(&mBlob, 0, sizeof(mBlob));
}
bool Blob::isEncrypted() const {
if (mBlob.version < 2) {
return true;
}
return mBlob.flags & KEYSTORE_FLAG_ENCRYPTED;
}
void Blob::setEncrypted(bool encrypted) {
if (encrypted) {
mBlob.flags |= KEYSTORE_FLAG_ENCRYPTED;
} else {
mBlob.flags &= ~KEYSTORE_FLAG_ENCRYPTED;
}
}
void Blob::setFallback(bool fallback) {
if (fallback) {
mBlob.flags |= KEYSTORE_FLAG_FALLBACK;
} else {
mBlob.flags &= ~KEYSTORE_FLAG_FALLBACK;
}
}
ResponseCode Blob::writeBlob(const char* filename, AES_KEY* aes_key, State state,
Entropy* entropy) {
ALOGV("writing blob %s", filename);
if (isEncrypted()) {
if (state != STATE_NO_ERROR) {
ALOGD("couldn't insert encrypted blob while not unlocked");
return LOCKED;
}
if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) {
ALOGW("Could not read random data for: %s", filename);
return SYSTEM_ERROR;
}
}
// data includes the value and the value's length
size_t dataLength = mBlob.length + sizeof(mBlob.length);
// pad data to the AES_BLOCK_SIZE
size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE);
// encrypted data includes the digest value
size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH;
// move info after space for padding
memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info);
// zero padding area
memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength);
mBlob.length = htonl(mBlob.length);
if (isEncrypted()) {
MD5(mBlob.digested, digestedLength, mBlob.digest);
uint8_t vector[AES_BLOCK_SIZE];
memcpy(vector, mBlob.vector, AES_BLOCK_SIZE);
AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, vector,
AES_ENCRYPT);
}
size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
size_t fileLength = encryptedLength + headerLength + mBlob.info;
const char* tmpFileName = ".tmp";
int out =
TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
if (out < 0) {
ALOGW("could not open file: %s: %s", tmpFileName, strerror(errno));
return SYSTEM_ERROR;
}
size_t writtenBytes = writeFully(out, (uint8_t*)&mBlob, fileLength);
if (close(out) != 0) {
return SYSTEM_ERROR;
}
if (writtenBytes != fileLength) {
ALOGW("blob not fully written %zu != %zu", writtenBytes, fileLength);
unlink(tmpFileName);
return SYSTEM_ERROR;
}
if (rename(tmpFileName, filename) == -1) {
ALOGW("could not rename blob to %s: %s", filename, strerror(errno));
return SYSTEM_ERROR;
}
return NO_ERROR;
}
ResponseCode Blob::readBlob(const char* filename, AES_KEY* aes_key, State state) {
ALOGV("reading blob %s", filename);
int in = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
if (in < 0) {
return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR;
}
// fileLength may be less than sizeof(mBlob) since the in
// memory version has extra padding to tolerate rounding up to
// the AES_BLOCK_SIZE
size_t fileLength = readFully(in, (uint8_t*)&mBlob, sizeof(mBlob));
if (close(in) != 0) {
return SYSTEM_ERROR;
}
if (fileLength == 0) {
return VALUE_CORRUPTED;
}
if (isEncrypted() && (state != STATE_NO_ERROR)) {
return LOCKED;
}
size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
if (fileLength < headerLength) {
return VALUE_CORRUPTED;
}
ssize_t encryptedLength = fileLength - (headerLength + mBlob.info);
if (encryptedLength < 0) {
return VALUE_CORRUPTED;
}
ssize_t digestedLength;
if (isEncrypted()) {
if (encryptedLength % AES_BLOCK_SIZE != 0) {
return VALUE_CORRUPTED;
}
AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, mBlob.vector,
AES_DECRYPT);
digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
uint8_t computedDigest[MD5_DIGEST_LENGTH];
MD5(mBlob.digested, digestedLength, computedDigest);
if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
return VALUE_CORRUPTED;
}
} else {
digestedLength = encryptedLength;
}
ssize_t maxValueLength = digestedLength - sizeof(mBlob.length);
mBlob.length = ntohl(mBlob.length);
if (mBlob.length < 0 || mBlob.length > maxValueLength) {
return VALUE_CORRUPTED;
}
if (mBlob.info != 0) {
// move info from after padding to after data
memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info);
}
return ::NO_ERROR;
}
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KEYSTORE_BLOB_H_
#define KEYSTORE_BLOB_H_
#include <stdint.h>
#include <openssl/aes.h>
#include <openssl/md5.h>
#include <keystore/keystore.h>
#define VALUE_SIZE 32768
/* Here is the file format. There are two parts in blob.value, the secret and
* the description. The secret is stored in ciphertext, and its original size
* can be found in blob.length. The description is stored after the secret in
* plaintext, and its size is specified in blob.info. The total size of the two
* parts must be no more than VALUE_SIZE bytes. The first field is the version,
* the second is the blob's type, and the third byte is flags. Fields other
* than blob.info, blob.length, and blob.value are modified by encryptBlob()
* and decryptBlob(). Thus they should not be accessed from outside. */
/* ** Note to future implementors of encryption: **
* Currently this is the construction:
* metadata || Enc(MD5(data) || data)
*
* This should be the construction used for encrypting if re-implementing:
*
* Derive independent keys for encryption and MAC:
* Kenc = AES_encrypt(masterKey, "Encrypt")
* Kmac = AES_encrypt(masterKey, "MAC")
*
* Store this:
* metadata || AES_CTR_encrypt(Kenc, rand_IV, data) ||
* HMAC(Kmac, metadata || Enc(data))
*/
struct __attribute__((packed)) blob {
uint8_t version;
uint8_t type;
uint8_t flags;
uint8_t info;
uint8_t vector[AES_BLOCK_SIZE];
uint8_t encrypted[0]; // Marks offset to encrypted data.
uint8_t digest[MD5_DIGEST_LENGTH];
uint8_t digested[0]; // Marks offset to digested data.
int32_t length; // in network byte order when encrypted
uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE];
};
static const uint8_t CURRENT_BLOB_VERSION = 2;
typedef enum {
TYPE_ANY = 0, // meta type that matches anything
TYPE_GENERIC = 1,
TYPE_MASTER_KEY = 2,
TYPE_KEY_PAIR = 3,
TYPE_KEYMASTER_10 = 4,
} BlobType;
class Entropy;
class Blob {
public:
Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
BlobType type);
Blob(blob b);
Blob();
const uint8_t* getValue() const { return mBlob.value; }
int32_t getLength() const { return mBlob.length; }
const uint8_t* getInfo() const { return mBlob.value + mBlob.length; }
uint8_t getInfoLength() const { return mBlob.info; }
uint8_t getVersion() const { return mBlob.version; }
bool isEncrypted() const;
void setEncrypted(bool encrypted);
bool isFallback() const { return mBlob.flags & KEYSTORE_FLAG_FALLBACK; }
void setFallback(bool fallback);
void setVersion(uint8_t version) { mBlob.version = version; }
BlobType getType() const { return BlobType(mBlob.type); }
void setType(BlobType type) { mBlob.type = uint8_t(type); }
ResponseCode writeBlob(const char* filename, AES_KEY* aes_key, State state, Entropy* entropy);
ResponseCode readBlob(const char* filename, AES_KEY* aes_key, State state);
private:
struct blob mBlob;
};
#endif // KEYSTORE_BLOB_H_
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "keystore"
#include "entropy.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <cutils/log.h>
#include "keystore_utils.h"
Entropy::~Entropy() {
if (mRandom >= 0) {
close(mRandom);
}
}
bool Entropy::open() {
const char* randomDevice = "/dev/urandom";
mRandom = TEMP_FAILURE_RETRY(::open(randomDevice, O_RDONLY));
if (mRandom < 0) {
ALOGE("open: %s: %s", randomDevice, strerror(errno));
return false;
}
return true;
}
bool Entropy::generate_random_data(uint8_t* data, size_t size) const {
return (readFully(mRandom, data, size) == size);
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KEYSTORE_ENTROPY_H_
#define KEYSTORE_ENTROPY_H_
#include <stdint.h>
class Entropy {
public:
Entropy() : mRandom(-1) {}
~Entropy();
bool open();
bool generate_random_data(uint8_t* data, size_t size) const;
private:
int mRandom;
};
#endif // KEYSTORE_ENTROPY_H_
This diff is collapsed.
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KEYSTORE_KEYSTORE_SERVICE_H_
#define KEYSTORE_KEYSTORE_SERVICE_H_
#include <keystore/IKeystoreService.h>
#include "auth_token_table.h"
#include "keystore.h"
#include "keystore_keymaster_enforcement.h"
#include "operation.h"
#include "permissions.h"
namespace android {
class KeyStoreService : public BnKeystoreService, public IBinder::DeathRecipient {
public:
KeyStoreService(KeyStore* keyStore) : mKeyStore(keyStore), mOperationMap(this) {}
void binderDied(const wp<IBinder>& who);
int32_t getState(int32_t userId);
int32_t get(const String16& name, uint8_t** item, size_t* itemLength);
int32_t insert(const String16& name, const uint8_t* item, size_t itemLength, int targetUid,
int32_t flags);
int32_t del(const String16& name, int targetUid);
int32_t exist(const String16& name, int targetUid);
int32_t list(const String16& prefix, int targetUid, Vector<String16>* matches);
int32_t reset();
int32_t onUserPasswordChanged(int32_t userId, const String16& password);
int32_t onUserAdded(int32_t userId, int32_t parentId);
int32_t onUserRemoved(int32_t userId);
int32_t lock(int32_t userId);
int32_t unlock(int32_t userId, const String16& pw);
bool isEmpty(int32_t userId);
int32_t generate(const String16& name, int32_t targetUid, int32_t keyType, int32_t keySize,
int32_t flags, Vector<sp<KeystoreArg>>* args);
int32_t import(const String16& name, const uint8_t* data, size_t length, int targetUid,
int32_t flags);
int32_t sign(const String16& name, const uint8_t* data, size_t length, uint8_t** out,
size_t* outLength);
int32_t verify(const String16& name, const uint8_t* data, size_t dataLength,
const uint8_t* signature, size_t signatureLength);
/*
* TODO: The abstraction between things stored in hardware and regular blobs
* of data stored on the filesystem should be moved down to keystore itself.
* Unfortunately the Java code that calls this has naming conventions that it
* knows about. Ideally keystore shouldn't be used to store random blobs of
* data.
*
* Until that happens, it's necessary to have a separate "get_pubkey" and
* "del_key" since the Java code doesn't really communicate what it's
* intentions are.
*/
int32_t get_pubkey(const String16& name, uint8_t** pubkey, size_t* pubkeyLength);
int32_t grant(const String16& name, int32_t granteeUid);
int32_t ungrant(const String16& name, int32_t granteeUid);
int64_t getmtime(const String16& name);
int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
int32_t destUid);
int32_t is_hardware_backed(const String16& keyType);
int32_t clear_uid(int64_t targetUid64);
int32_t addRngEntropy(const uint8_t* data, size_t dataLength);
int32_t generateKey(const String16& name, const KeymasterArguments& params,
const uint8_t* entropy, size_t entropyLength, int uid, int flags,
KeyCharacteristics* outCharacteristics);
int32_t getKeyCharacteristics(const String16& name, const keymaster_blob_t* clientId,
const keymaster_blob_t* appData,
KeyCharacteristics* outCharacteristics);
int32_t importKey(const String16& name, const KeymasterArguments& params,
keymaster_key_format_t format, const uint8_t* keyData, size_t keyLength,
int uid, int flags, KeyCharacteristics* outCharacteristics);
void exportKey(const String16& name, keymaster_key_format_t format,
const keymaster_blob_t* clientId, const keymaster_blob_t* appData,
ExportResult* result);
void begin(const sp<IBinder>& appToken, const String16& name, keymaster_purpose_t purpose,
bool pruneable, const KeymasterArguments& params, const uint8_t* entropy,
size_t entropyLength, OperationResult* result);
void update(const sp<IBinder>& token, const KeymasterArguments& params, const uint8_t* data,
size_t dataLength, OperationResult* result);
void finish(const sp<IBinder>& token, const KeymasterArguments& params,
const uint8_t* signature, size_t signatureLength, const uint8_t* entropy,
size_t entropyLength, OperationResult* result);
int32_t abort(const sp<IBinder>& token);
bool isOperationAuthorized(const sp<IBinder>& token);
int32_t addAuthToken(const uint8_t* token, size_t length);
private:
static const int32_t UID_SELF = -1;
/**
* Prune the oldest pruneable operation.
*/
bool pruneOperation();
/**
* Get the effective target uid for a binder operation that takes an
* optional uid as the target.
*/
uid_t getEffectiveUid(int32_t targetUid);
/**
* Check if the caller of the current binder method has the required
* permission and if acting on other uids the grants to do so.
*/
bool checkBinderPermission(perm_t permission, int32_t targetUid = UID_SELF);
/**
* Check if the caller of the current binder method has the required
* permission and the target uid is the caller or the caller is system.
*/
bool checkBinderPermissionSelfOrSystem(perm_t permission, int32_t targetUid);
/**
* Check if the caller of the current binder method has the required
* permission or the target of the operation is the caller's uid. This is
* for operation where the permission is only for cross-uid activity and all
* uids are allowed to act on their own (ie: clearing all entries for a
* given uid).
*/
bool checkBinderPermissionOrSelfTarget(perm_t permission, int32_t targetUid);
/**
* Helper method to check that the caller has the required permission as
* well as the keystore is in the unlocked state if checkUnlocked is true.
*
* Returns NO_ERROR on success, PERMISSION_DENIED on a permission error and
* otherwise the state of keystore when not unlocked and checkUnlocked is
* true.
*/
int32_t checkBinderPermissionAndKeystoreState(perm_t permission, int32_t targetUid = -1,
bool checkUnlocked = true);
bool isKeystoreUnlocked(State state);
bool isKeyTypeSupported(const keymaster1_device_t* device, keymaster_keypair_t keyType);
/**
* Check that all keymaster_key_param_t's provided by the application are
* allowed. Any parameter that keystore adds itself should be disallowed here.
*/
bool checkAllowedOperationParams(const std::vector<keymaster_key_param_t>& params);
keymaster_error_t getOperationCharacteristics(const keymaster_key_blob_t& key,
const keymaster1_device_t* dev,
const std::vector<keymaster_key_param_t>& params,
keymaster_key_characteristics_t* out);
/**
* Get the auth token for this operation from the auth token table.
*
* Returns ::NO_ERROR if the auth token was set or none was required.
* ::OP_AUTH_NEEDED if it is a per op authorization, no
* authorization token exists for that operation and
* failOnTokenMissing is false.
* KM_ERROR_KEY_USER_NOT_AUTHENTICATED if there is no valid auth
* token for the operation
*/
int32_t getAuthToken(const keymaster_key_characteristics_t* characteristics,
keymaster_operation_handle_t handle, keymaster_purpose_t purpose,
const hw_auth_token_t** authToken, bool failOnTokenMissing = true);
void addAuthToParams(std::vector<keymaster_key_param_t>* params, const hw_auth_token_t* token);
/**
* Add the auth token for the operation to the param list if the operation
* requires authorization. Uses the cached result in the OperationMap if available
* otherwise gets the token from the AuthTokenTable and caches the result.
*
* Returns ::NO_ERROR if the auth token was added or not needed.
* KM_ERROR_KEY_USER_NOT_AUTHENTICATED if the operation is not
* authenticated.
* KM_ERROR_INVALID_OPERATION_HANDLE if token is not a valid
* operation token.
*/
int32_t addOperationAuthTokenIfNeeded(sp<IBinder> token,
std::vector<keymaster_key_param_t>* params);
/**
* Translate a result value to a legacy return value. All keystore errors are
* preserved and keymaster errors become SYSTEM_ERRORs
*/
int32_t translateResultToLegacyResult(int32_t result);
keymaster_key_param_t* getKeyAlgorithm(keymaster_key_characteristics_t* characteristics);
void addLegacyBeginParams(const String16& name, std::vector<keymaster_key_param_t>& params);
int32_t doLegacySignVerify(const String16& name, const uint8_t* data, size_t length,
uint8_t** out, size_t* outLength, const uint8_t* signature,
size_t signatureLength, keymaster_purpose_t purpose);
::KeyStore* mKeyStore;
OperationMap mOperationMap;
keymaster::AuthTokenTable mAuthTokenTable;
KeystoreKeymasterEnforcement enforcement_policy;
};
}; // namespace android
#endif // KEYSTORE_KEYSTORE_SERVICE_H_
This diff is collapsed.
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KEYSTORE_KEYSTORE_H_
#define KEYSTORE_KEYSTORE_H_
#include "user_state.h"
#include <hardware/keymaster1.h>
#include <utils/Vector.h>
#include "blob.h"
typedef struct {
uint32_t uid;
const uint8_t* filename;
} grant_t;
class KeyStore {
public:
KeyStore(Entropy* entropy, keymaster1_device_t* device, keymaster1_device_t* fallback);
~KeyStore();
/**
* Depending on the hardware keymaster version is this may return a
* keymaster0_device_t* cast to a keymaster1_device_t*. All methods from
* keymaster0 are safe to call, calls to keymaster1_device_t methods should
* be guarded by a check on the device's version.
*/
keymaster1_device_t* getDevice() const { return mDevice; }
keymaster1_device_t* getFallbackDevice() const { return mFallbackDevice; }
keymaster1_device_t* getDeviceForBlob(const Blob& blob) const {
return blob.isFallback() ? mFallbackDevice : mDevice;
}
ResponseCode initialize();
State getState(uid_t userId) { return getUserState(userId)->getState(); }
ResponseCode initializeUser(const android::String8& pw, uid_t userId);
ResponseCode copyMasterKey(uid_t srcUser, uid_t dstUser);
ResponseCode writeMasterKey(const android::String8& pw, uid_t userId);
ResponseCode readMasterKey(const android::String8& pw, uid_t userId);
android::String8 getKeyName(const android::String8& keyName);
android::String8 getKeyNameForUid(const android::String8& keyName, uid_t uid);
android::String8 getKeyNameForUidWithDir(const android::String8& keyName, uid_t uid);
/*
* Delete entries owned by userId. If keepUnencryptedEntries is true
* then only encrypted entries will be removed, otherwise all entries will
* be removed.
*/
void resetUser(uid_t userId, bool keepUnenryptedEntries);
bool isEmpty(uid_t userId) const;
void lock(uid_t userId);
ResponseCode get(const char* filename, Blob* keyBlob, const BlobType type, uid_t userId);
ResponseCode put(const char* filename, Blob* keyBlob, uid_t userId);
ResponseCode del(const char* filename, const BlobType type, uid_t userId);
ResponseCode list(const android::String8& prefix, android::Vector<android::String16>* matches,
uid_t userId);
void addGrant(const char* filename, uid_t granteeUid);
bool removeGrant(const char* filename, uid_t granteeUid);
bool hasGrant(const char* filename, const uid_t uid) const {
return getGrant(filename, uid) != NULL;
}
ResponseCode importKey(const uint8_t* key, size_t keyLen, const char* filename, uid_t userId,
int32_t flags);
bool isHardwareBacked(const android::String16& keyType) const;
ResponseCode getKeyForName(Blob* keyBlob, const android::String8& keyName, const uid_t uid,
const BlobType type);
/**
* Returns any existing UserState or creates it if it doesn't exist.
*/
UserState* getUserState(uid_t userId);
/**
* Returns any existing UserState or creates it if it doesn't exist.
*/
UserState* getUserStateByUid(uid_t uid);
/**
* Returns NULL if the UserState doesn't already exist.
*/
const UserState* getUserState(uid_t userId) const;
/**
* Returns NULL if the UserState doesn't already exist.
*/
const UserState* getUserStateByUid(uid_t uid) const;
private:
static const char* sOldMasterKey;
static const char* sMetaDataFile;
static const android::String16 sRSAKeyType;
static const android::String16 sECKeyType;
Entropy* mEntropy;
keymaster1_device_t* mDevice;
keymaster1_device_t* mFallbackDevice;
android::Vector<UserState*> mMasterKeys;
android::Vector<grant_t*> mGrants;
typedef struct { uint32_t version; } keystore_metadata_t;
keystore_metadata_t mMetaData;
const grant_t* getGrant(const char* filename, uid_t uid) const;
/**
* Upgrade the key from the current version to whatever is newest.
*/
bool upgradeBlob(const char* filename, Blob* blob, const uint8_t oldVersion,
const BlobType type, uid_t uid);
/**
* Takes a blob that is an PEM-encoded RSA key as a byte array and converts it to a DER-encoded
* PKCS#8 for import into a keymaster. Then it overwrites the original blob with the new blob
* format that is returned from the keymaster.
*/
ResponseCode importBlobAsKey(Blob* blob, const char* filename, uid_t uid);
void readMetaData();
void writeMetaData();
bool upgradeKeystore();
};
#endif // KEYSTORE_KEYSTORE_H_
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "keystore"
#include <keymaster/soft_keymaster_device.h>
#include <keymaster/soft_keymaster_logger.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <cutils/log.h>
#include "entropy.h"
#include "key_store_service.h"
#include "keystore.h"
#include "permissions.h"
/* KeyStore is a secured storage for key-value pairs. In this implementation,
* each file stores one key-value pair. Keys are encoded in file names, and
* values are encrypted with checksums. The encryption key is protected by a
* user-defined password. To keep things simple, buffers are always larger than
* the maximum space we needed, so boundary checks on buffers are omitted. */
using keymaster::SoftKeymasterDevice;
static int keymaster0_device_initialize(const hw_module_t* mod, keymaster1_device_t** dev) {
assert(mod->module_api_version < KEYMASTER_MODULE_API_VERSION_1_0);
ALOGI("Found keymaster0 module %s, version %x", mod->name, mod->module_api_version);
UniquePtr<SoftKeymasterDevice> soft_keymaster(new SoftKeymasterDevice);
keymaster0_device_t* km0_device = NULL;
keymaster_error_t error = KM_ERROR_OK;
int rc = keymaster0_open(mod, &km0_device);
if (rc) {
ALOGE("Error opening keystore keymaster0 device.");
goto err;
}
if (km0_device->flags & KEYMASTER_SOFTWARE_ONLY) {
ALOGI("Keymaster0 module is software-only. Using SoftKeymasterDevice instead.");
km0_device->common.close(&km0_device->common);
km0_device = NULL;
// SoftKeymasterDevice will be deleted by keymaster_device_release()
*dev = soft_keymaster.release()->keymaster_device();
return 0;
}
ALOGE("Wrapping keymaster0 module %s with SoftKeymasterDevice", mod->name);
error = soft_keymaster->SetHardwareDevice(km0_device);
km0_device = NULL; // SoftKeymasterDevice has taken ownership.
if (error != KM_ERROR_OK) {
ALOGE("Got error %d from SetHardwareDevice", error);
rc = error;
goto err;
}
// SoftKeymasterDevice will be deleted by keymaster_device_release()
*dev = soft_keymaster.release()->keymaster_device();
return 0;
err:
if (km0_device)
km0_device->common.close(&km0_device->common);
*dev = NULL;
return rc;
}
static int keymaster1_device_initialize(const hw_module_t* mod, keymaster1_device_t** dev) {
assert(mod->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0);
ALOGI("Found keymaster1 module %s, version %x", mod->name, mod->module_api_version);
UniquePtr<SoftKeymasterDevice> soft_keymaster(new SoftKeymasterDevice);
keymaster1_device_t* km1_device = NULL;
keymaster_error_t error = KM_ERROR_OK;
int rc = keymaster1_open(mod, &km1_device);
if (rc) {
ALOGE("Error %d opening keystore keymaster1 device", rc);
goto err;
}
error = soft_keymaster->SetHardwareDevice(km1_device);
km1_device = NULL; // SoftKeymasterDevice has taken ownership.
if (error != KM_ERROR_OK) {
ALOGE("Got error %d from SetHardwareDevice", error);
rc = error;
goto err;
}
if (!soft_keymaster->Keymaster1DeviceIsGood()) {
ALOGI("Keymaster1 module is incomplete, using SoftKeymasterDevice wrapper");
// SoftKeymasterDevice will be deleted by keymaster_device_release()
*dev = soft_keymaster.release()->keymaster_device();
return 0;
} else {
ALOGI("Keymaster1 module is good, destroying wrapper and re-opening");
soft_keymaster.reset(NULL);
rc = keymaster1_open(mod, &km1_device);
if (rc) {
ALOGE("Error %d re-opening keystore keymaster1 device.", rc);
goto err;
}
*dev = km1_device;
return 0;
}
err:
if (km1_device)
km1_device->common.close(&km1_device->common);
*dev = NULL;
return rc;
}
static int keymaster_device_initialize(keymaster1_device_t** dev) {
const hw_module_t* mod;
int rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
if (rc) {
ALOGI("Could not find any keystore module, using software-only implementation.");
// SoftKeymasterDevice will be deleted by keymaster_device_release()
*dev = (new SoftKeymasterDevice)->keymaster_device();
return 0;
}
if (mod->module_api_version < KEYMASTER_MODULE_API_VERSION_1_0) {
return keymaster0_device_initialize(mod, dev);
} else {
return keymaster1_device_initialize(mod, dev);
}
}
// softkeymaster_logger appears not to be used in keystore, but it installs itself as the
// logger used by SoftKeymasterDevice.
static keymaster::SoftKeymasterLogger softkeymaster_logger;
static int fallback_keymaster_device_initialize(keymaster1_device_t** dev) {
*dev = (new SoftKeymasterDevice)->keymaster_device();
// SoftKeymasterDevice will be deleted by keymaster_device_release()
return 0;
}
static void keymaster_device_release(keymaster1_device_t* dev) {
dev->common.close(&dev->common);
}
int main(int argc, char* argv[]) {
if (argc < 2) {
ALOGE("A directory must be specified!");
return 1;
}
if (chdir(argv[1]) == -1) {
ALOGE("chdir: %s: %s", argv[1], strerror(errno));
return 1;
}
Entropy entropy;
if (!entropy.open()) {
return 1;
}
keymaster1_device_t* dev;
if (keymaster_device_initialize(&dev)) {
ALOGE("keystore keymaster could not be initialized; exiting");
return 1;
}
keymaster1_device_t* fallback;
if (fallback_keymaster_device_initialize(&fallback)) {
ALOGE("software keymaster could not be initialized; exiting");
return 1;
}
if (configure_selinux() == -1) {
return -1;
}
KeyStore keyStore(&entropy, dev, fallback);
keyStore.initialize();
android::sp<android::IServiceManager> sm = android::defaultServiceManager();
android::sp<android::KeyStoreService> service = new android::KeyStoreService(&keyStore);
android::status_t ret = sm->addService(android::String16("android.security.keystore"), service);
if (ret != android::OK) {
ALOGE("Couldn't register binder service!");
return -1;
}
/*
* We're the only thread in existence, so we're just going to process
* Binder transaction as a single-threaded program.
*/
android::IPCThreadState::self()->joinThreadPool();
keymaster_device_release(dev);
return 1;
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "keystore"
#include "keystore_utils.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <cutils/log.h>
#include <private/android_filesystem_config.h>
#include <keymaster/android_keymaster_utils.h>
size_t readFully(int fd, uint8_t* data, size_t size) {
size_t remaining = size;
while (remaining > 0) {
ssize_t n = TEMP_FAILURE_RETRY(read(fd, data, remaining));
if (n <= 0) {
return size - remaining;
}
data += n;
remaining -= n;
}
return size;
}
size_t writeFully(int fd, uint8_t* data, size_t size) {
size_t remaining = size;
while (remaining > 0) {
ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, remaining));
if (n < 0) {
ALOGW("write failed: %s", strerror(errno));
return size - remaining;
}
data += n;
remaining -= n;
}
return size;
}
void add_legacy_key_authorizations(int keyType, std::vector<keymaster_key_param_t>* params) {
params->push_back(keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN));
params->push_back(keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY));
params->push_back(keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_ENCRYPT));
params->push_back(keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_DECRYPT));
params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE));
if (keyType == EVP_PKEY_RSA) {
params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN));
params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT));
params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_RSA_PSS));
params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_RSA_OAEP));
}
params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE));
params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_MD5));
params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA1));
params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA_2_224));
params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA_2_256));
params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA_2_384));
params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA_2_512));
params->push_back(keymaster_param_bool(KM_TAG_ALL_USERS));
params->push_back(keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED));
params->push_back(keymaster_param_date(KM_TAG_ORIGINATION_EXPIRE_DATETIME, LLONG_MAX));
params->push_back(keymaster_param_date(KM_TAG_USAGE_EXPIRE_DATETIME, LLONG_MAX));
params->push_back(keymaster_param_date(KM_TAG_ACTIVE_DATETIME, 0));
uint64_t now = keymaster::java_time(time(NULL));
params->push_back(keymaster_param_date(KM_TAG_CREATION_DATETIME, now));
}
uid_t get_app_id(uid_t uid) {
return uid % AID_USER;
}
uid_t get_user_id(uid_t uid) {
return uid / AID_USER;
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KEYSTORE_KEYSTORE_UTILS_H_
#define KEYSTORE_KEYSTORE_UTILS_H_
#include <stdint.h>
#include <vector>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <hardware/keymaster_defs.h>
#include <UniquePtr.h>
size_t readFully(int fd, uint8_t* data, size_t size);
size_t writeFully(int fd, uint8_t* data, size_t size);
void add_legacy_key_authorizations(int keyType, std::vector<keymaster_key_param_t>* params);
/**
* Returns the app ID (in the Android multi-user sense) for the current
* UNIX UID.
*/
uid_t get_app_id(uid_t uid);
/**
* Returns the user ID (in the Android multi-user sense) for the current
* UNIX UID.
*/
uid_t get_user_id(uid_t uid);
struct EVP_PKEY_Delete {
void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
};
typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY;
struct PKCS8_PRIV_KEY_INFO_Delete {
void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
};
typedef UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> Unique_PKCS8_PRIV_KEY_INFO;
#endif // KEYSTORE_KEYSTORE_UTILS_H_
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "keystore"
#include "permissions.h"
#include <cutils/log.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
#include "keystore_utils.h"
/* perm_labels associcated with keystore_key SELinux class verbs. */
const char* perm_labels[] = {
"get_state", "get", "insert", "delete", "exist", "list",
"reset", "password", "lock", "unlock", "is_empty", "sign",
"verify", "grant", "duplicate", "clear_uid", "add_auth", "user_changed",
};
struct user_euid {
uid_t uid;
uid_t euid;
};
user_euid user_euids[] = {
{AID_VPN, AID_SYSTEM}, {AID_WIFI, AID_SYSTEM}, {AID_ROOT, AID_SYSTEM},
};
struct user_perm {
uid_t uid;
perm_t perms;
};
static user_perm user_perms[] = {
{AID_SYSTEM, static_cast<perm_t>((uint32_t)(~0))},
{AID_VPN, static_cast<perm_t>(P_GET | P_SIGN | P_VERIFY)},
{AID_WIFI, static_cast<perm_t>(P_GET | P_SIGN | P_VERIFY)},
{AID_ROOT, static_cast<perm_t>(P_GET)},
};
static const perm_t DEFAULT_PERMS = static_cast<perm_t>(P_GET_STATE | P_GET | P_INSERT | P_DELETE |
P_EXIST | P_LIST | P_SIGN | P_VERIFY);
struct audit_data {
pid_t pid;
uid_t uid;
};
const char* get_perm_label(perm_t perm) {
unsigned int index = ffs(perm);
if (index > 0 && index <= (sizeof(perm_labels) / sizeof(perm_labels[0]))) {
return perm_labels[index - 1];
} else {
ALOGE("Keystore: Failed to retrieve permission label.\n");
abort();
}
}
static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len) {
struct audit_data* ad = reinterpret_cast<struct audit_data*>(data);
if (!ad) {
ALOGE("No keystore audit data");
return 0;
}
snprintf(buf, len, "pid=%d uid=%d", ad->pid, ad->uid);
return 0;
}
static char* tctx;
static int ks_is_selinux_enabled;
int configure_selinux() {
ks_is_selinux_enabled = is_selinux_enabled();
if (ks_is_selinux_enabled) {
union selinux_callback cb;
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
cb.func_log = selinux_log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
if (getcon(&tctx) != 0) {
ALOGE("SELinux: Could not acquire target context. Aborting keystore.\n");
return -1;
}
} else {
ALOGI("SELinux: Keystore SELinux is disabled.\n");
}
return 0;
}
static bool keystore_selinux_check_access(uid_t uid, perm_t perm, pid_t spid) {
if (!ks_is_selinux_enabled) {
return true;
}
audit_data ad;
char* sctx = NULL;
const char* selinux_class = "keystore_key";
const char* str_perm = get_perm_label(perm);
if (!str_perm) {
return false;
}
if (getpidcon(spid, &sctx) != 0) {
ALOGE("SELinux: Failed to get source pid context.\n");
return false;
}
ad.pid = spid;
ad.uid = uid;
bool allowed = selinux_check_access(sctx, tctx, selinux_class, str_perm,
reinterpret_cast<void*>(&ad)) == 0;
freecon(sctx);
return allowed;
}
/**
* Returns the UID that the callingUid should act as. This is here for
* legacy support of the WiFi and VPN systems and should be removed
* when WiFi can operate in its own namespace.
*/
uid_t get_keystore_euid(uid_t uid) {
for (size_t i = 0; i < sizeof(user_euids) / sizeof(user_euids[0]); i++) {
struct user_euid user = user_euids[i];
if (user.uid == uid) {
return user.euid;
}
}
return uid;
}
bool has_permission(uid_t uid, perm_t perm, pid_t spid) {
// All system users are equivalent for multi-user support.
if (get_app_id(uid) == AID_SYSTEM) {
uid = AID_SYSTEM;
}
for (size_t i = 0; i < sizeof(user_perms) / sizeof(user_perms[0]); i++) {
struct user_perm user = user_perms[i];
if (user.uid == uid) {
return (user.perms & perm) && keystore_selinux_check_access(uid, perm, spid);
}
}
return (DEFAULT_PERMS & perm) && keystore_selinux_check_access(uid, perm, spid);
}
/**
* Returns true if the callingUid is allowed to interact in the targetUid's
* namespace.
*/
bool is_granted_to(uid_t callingUid, uid_t targetUid) {
if (callingUid == targetUid) {
return true;
}
for (size_t i = 0; i < sizeof(user_euids) / sizeof(user_euids[0]); i++) {
struct user_euid user = user_euids[i];
if (user.euid == callingUid && user.uid == targetUid) {
return true;
}
}
return false;
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KEYSTORE_PERMISSIONS_H_
#define KEYSTORE_PERMISSIONS_H_
#include <unistd.h>
/* Here are the permissions, actions, users, and the main function. */
enum perm_t {
P_GET_STATE = 1 << 0,
P_GET = 1 << 1,
P_INSERT = 1 << 2,
P_DELETE = 1 << 3,
P_EXIST = 1 << 4,
P_LIST = 1 << 5,
P_RESET = 1 << 6,
P_PASSWORD = 1 << 7,
P_LOCK = 1 << 8,
P_UNLOCK = 1 << 9,
P_IS_EMPTY = 1 << 10,
P_SIGN = 1 << 11,
P_VERIFY = 1 << 12,
P_GRANT = 1 << 13,
P_DUPLICATE = 1 << 14,
P_CLEAR_UID = 1 << 15,
P_ADD_AUTH = 1 << 16,
P_USER_CHANGED = 1 << 17,
};
const char* get_perm_label(perm_t perm);
/**
* Returns the UID that the callingUid should act as. This is here for
* legacy support of the WiFi and VPN systems and should be removed
* when WiFi can operate in its own namespace.
*/
uid_t get_keystore_euid(uid_t uid);
bool has_permission(uid_t uid, perm_t perm, pid_t spid);
/**
* Returns true if the callingUid is allowed to interact in the targetUid's
* namespace.
*/
bool is_granted_to(uid_t callingUid, uid_t targetUid);
int configure_selinux();
#endif // KEYSTORE_PERMISSIONS_H_
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "keystore"
#include "user_state.h"
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <openssl/evp.h>
#include <cutils/log.h>
#include "blob.h"
#include "keystore_utils.h"
UserState::UserState(uid_t userId) : mUserId(userId), mRetry(MAX_RETRY) {
asprintf(&mUserDir, "user_%u", mUserId);
asprintf(&mMasterKeyFile, "%s/.masterkey", mUserDir);
}
UserState::~UserState() {
free(mUserDir);
free(mMasterKeyFile);
}
bool UserState::initialize() {
if ((mkdir(mUserDir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) && (errno != EEXIST)) {
ALOGE("Could not create directory '%s'", mUserDir);
return false;
}
if (access(mMasterKeyFile, R_OK) == 0) {
setState(STATE_LOCKED);
} else {
setState(STATE_UNINITIALIZED);
}
return true;
}
void UserState::setState(State state) {
mState = state;
if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) {
mRetry = MAX_RETRY;
}
}
void UserState::zeroizeMasterKeysInMemory() {
memset(mMasterKey, 0, sizeof(mMasterKey));
memset(mSalt, 0, sizeof(mSalt));
memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption));
memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption));
}
bool UserState::deleteMasterKey() {
setState(STATE_UNINITIALIZED);
zeroizeMasterKeysInMemory();
return unlink(mMasterKeyFile) == 0 || errno == ENOENT;
}
ResponseCode UserState::initialize(const android::String8& pw, Entropy* entropy) {
if (!generateMasterKey(entropy)) {
return SYSTEM_ERROR;
}
ResponseCode response = writeMasterKey(pw, entropy);
if (response != NO_ERROR) {
return response;
}
setupMasterKeys();
return ::NO_ERROR;
}
ResponseCode UserState::copyMasterKey(UserState* src) {
if (mState != STATE_UNINITIALIZED) {
return ::SYSTEM_ERROR;
}
if (src->getState() != STATE_NO_ERROR) {
return ::SYSTEM_ERROR;
}
memcpy(mMasterKey, src->mMasterKey, MASTER_KEY_SIZE_BYTES);
setupMasterKeys();
return copyMasterKeyFile(src);
}
ResponseCode UserState::copyMasterKeyFile(UserState* src) {
/* Copy the master key file to the new user. Unfortunately we don't have the src user's
* password so we cannot generate a new file with a new salt.
*/
int in = TEMP_FAILURE_RETRY(open(src->getMasterKeyFileName(), O_RDONLY));
if (in < 0) {
return ::SYSTEM_ERROR;
}
blob rawBlob;
size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
if (close(in) != 0) {
return ::SYSTEM_ERROR;
}
int out =
TEMP_FAILURE_RETRY(open(mMasterKeyFile, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
if (out < 0) {
return ::SYSTEM_ERROR;
}
size_t outLength = writeFully(out, (uint8_t*)&rawBlob, length);
if (close(out) != 0) {
return ::SYSTEM_ERROR;
}
if (outLength != length) {
ALOGW("blob not fully written %zu != %zu", outLength, length);
unlink(mMasterKeyFile);
return ::SYSTEM_ERROR;
}
return ::NO_ERROR;
}
ResponseCode UserState::writeMasterKey(const android::String8& pw, Entropy* entropy) {
uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt);
AES_KEY passwordAesKey;
AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt), TYPE_MASTER_KEY);
return masterKeyBlob.writeBlob(mMasterKeyFile, &passwordAesKey, STATE_NO_ERROR, entropy);
}
ResponseCode UserState::readMasterKey(const android::String8& pw, Entropy* entropy) {
int in = TEMP_FAILURE_RETRY(open(mMasterKeyFile, O_RDONLY));
if (in < 0) {
return SYSTEM_ERROR;
}
// We read the raw blob to just to get the salt to generate the AES key, then we create the Blob
// to use with decryptBlob
blob rawBlob;
size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
if (close(in) != 0) {
return SYSTEM_ERROR;
}
// find salt at EOF if present, otherwise we have an old file
uint8_t* salt;
if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) {
salt = (uint8_t*)&rawBlob + length - SALT_SIZE;
} else {
salt = NULL;
}
uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt);
AES_KEY passwordAesKey;
AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
Blob masterKeyBlob(rawBlob);
ResponseCode response = masterKeyBlob.readBlob(mMasterKeyFile, &passwordAesKey, STATE_NO_ERROR);
if (response == SYSTEM_ERROR) {
return response;
}
if (response == NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) {
// If salt was missing, generate one and write a new master key file with the salt.
if (salt == NULL) {
if (!generateSalt(entropy)) {
return SYSTEM_ERROR;
}
response = writeMasterKey(pw, entropy);
}
if (response == NO_ERROR) {
memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES);
setupMasterKeys();
}
return response;
}
if (mRetry <= 0) {
reset();
return UNINITIALIZED;
}
--mRetry;
switch (mRetry) {
case 0:
return WRONG_PASSWORD_0;
case 1:
return WRONG_PASSWORD_1;
case 2:
return WRONG_PASSWORD_2;
case 3:
return WRONG_PASSWORD_3;
default:
return WRONG_PASSWORD_3;
}
}
bool UserState::reset() {
DIR* dir = opendir(getUserDirName());
if (!dir) {
// If the directory doesn't exist then nothing to do.
if (errno == ENOENT) {
return true;
}
ALOGW("couldn't open user directory: %s", strerror(errno));
return false;
}
struct dirent* file;
while ((file = readdir(dir)) != NULL) {
// skip . and ..
if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
continue;
}
unlinkat(dirfd(dir), file->d_name, 0);
}
closedir(dir);
return true;
}
void UserState::generateKeyFromPassword(uint8_t* key, ssize_t keySize, const android::String8& pw,
uint8_t* salt) {
size_t saltSize;
if (salt != NULL) {
saltSize = SALT_SIZE;
} else {
// Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
salt = (uint8_t*)"keystore";
// sizeof = 9, not strlen = 8
saltSize = sizeof("keystore");
}
PKCS5_PBKDF2_HMAC_SHA1(reinterpret_cast<const char*>(pw.string()), pw.length(), salt, saltSize,
8192, keySize, key);
}
bool UserState::generateSalt(Entropy* entropy) {
return entropy->generate_random_data(mSalt, sizeof(mSalt));
}
bool UserState::generateMasterKey(Entropy* entropy) {
if (!entropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) {
return false;
}
if (!generateSalt(entropy)) {
return false;
}
return true;
}
void UserState::setupMasterKeys() {
AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption);
AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption);
setState(STATE_NO_ERROR);
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KEYSTORE_USER_STATE_H_
#define KEYSTORE_USER_STATE_H_
#include <sys/types.h>
#include <openssl/aes.h>
#include <utils/String8.h>
#include <keystore/keystore.h>
#include "entropy.h"
class UserState {
public:
UserState(uid_t userId);
~UserState();
bool initialize();
uid_t getUserId() const { return mUserId; }
const char* getUserDirName() const { return mUserDir; }
const char* getMasterKeyFileName() const { return mMasterKeyFile; }
void setState(State state);
State getState() const { return mState; }
int8_t getRetry() const { return mRetry; }
void zeroizeMasterKeysInMemory();
bool deleteMasterKey();
ResponseCode initialize(const android::String8& pw, Entropy* entropy);
ResponseCode copyMasterKey(UserState* src);
ResponseCode copyMasterKeyFile(UserState* src);
ResponseCode writeMasterKey(const android::String8& pw, Entropy* entropy);
ResponseCode readMasterKey(const android::String8& pw, Entropy* entropy);
AES_KEY* getEncryptionKey() { return &mMasterKeyEncryption; }
AES_KEY* getDecryptionKey() { return &mMasterKeyDecryption; }
bool reset();
private:
static const int MASTER_KEY_SIZE_BYTES = 16;
static const int MASTER_KEY_SIZE_BITS = MASTER_KEY_SIZE_BYTES * 8;
static const int MAX_RETRY = 4;
static const size_t SALT_SIZE = 16;
void generateKeyFromPassword(uint8_t* key, ssize_t keySize, const android::String8& pw,
uint8_t* salt);
bool generateSalt(Entropy* entropy);
bool generateMasterKey(Entropy* entropy);
void setupMasterKeys();
uid_t mUserId;
char* mUserDir;
char* mMasterKeyFile;
State mState;
int8_t mRetry;
uint8_t mMasterKey[MASTER_KEY_SIZE_BYTES];
uint8_t mSalt[SALT_SIZE];
AES_KEY mMasterKeyEncryption;
AES_KEY mMasterKeyDecryption;
};
#endif // KEYSTORE_USER_STATE_H_
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment