Skip to content

Commit

Permalink
Support and use TAG_ALLOW_WHILE_ON_BODY
Browse files Browse the repository at this point in the history
There are three changes in this CL:
1. Persist all characteristics provided at the time of key creation.
   We do this to avoid device-specific keymaster implementations
   stripping keys they are not aware of.
2. Add an onDeviceOffBody API method that will be called whenever a
   wearable device is detected to have been removed.
3. Check whether a key was created with TAG_ALLOW_WHILE_ON_BODY and
   the device has gone off-body since the last auth event when
   deciding whether it can be used.

BUG: 30701680
BUG: 28911985
Change-Id: I6be3af3dee8e576fe713dfdd726502d8b333f224
  • Loading branch information
Tucker Sylvestro committed Oct 5, 2016
1 parent 37ca417 commit 0ab28b7
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 31 deletions.
26 changes: 26 additions & 0 deletions keystore/IKeystoreService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,24 @@ class BpKeystoreService: public BpInterface<IKeystoreService>
return ret;
}

virtual int32_t onDeviceOffBody()
{
Parcel data, reply;
data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
status_t status = remote()->transact(BnKeystoreService::ON_DEVICE_OFF_BODY, data, &reply);
if (status != NO_ERROR) {
ALOGD("onDeviceOffBody() could not contact remote: %d\n", status);
return -1;
}
int32_t err = reply.readExceptionCode();
int32_t ret = reply.readInt32();
if (err < 0) {
ALOGD("onDeviceOffBody() caught exception %d\n", err);
return -1;
}
return ret;
}

};

IMPLEMENT_META_INTERFACE(KeystoreService, "android.security.IKeystoreService");
Expand Down Expand Up @@ -1862,6 +1880,14 @@ status_t BnKeystoreService::onTransact(

return NO_ERROR;
}
case ON_DEVICE_OFF_BODY: {
CHECK_INTERFACE(IKeystoreService, data, reply);
int32_t ret = onDeviceOffBody();
reply->writeNoException();
reply->writeInt32(ret);

return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
Expand Down
11 changes: 11 additions & 0 deletions keystore/auth_token_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ AuthTokenTable::Error AuthTokenTable::FindTimedAuthorization(const std::vector<u
if (static_cast<int64_t>(newest_match->time_received()) + timeout < static_cast<int64_t>(now))
return AUTH_TOKEN_EXPIRED;

if (key_info.GetTagValue(TAG_ALLOW_WHILE_ON_BODY)) {
if (static_cast<int64_t>(newest_match->time_received()) <
static_cast<int64_t>(last_off_body_)) {
return AUTH_TOKEN_EXPIRED;
}
}

newest_match->UpdateLastUse(now);
*found = newest_match->token();
return OK;
Expand All @@ -155,6 +162,10 @@ void AuthTokenTable::RemoveEntriesSupersededBy(const Entry& entry) {
entries_.end());
}

void AuthTokenTable::onDeviceOffBody() {
last_off_body_ = clock_function_();
}

void AuthTokenTable::Clear() {
entries_.clear();
}
Expand Down
9 changes: 8 additions & 1 deletion keystore/auth_token_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ time_t clock_gettime_raw();
class AuthTokenTable {
public:
AuthTokenTable(size_t max_entries = 32, time_t (*clock_function)() = clock_gettime_raw)
: max_entries_(max_entries), clock_function_(clock_function) {}
: max_entries_(max_entries), last_off_body_(clock_function()), clock_function_(clock_function) {}

enum Error {
OK,
Expand Down Expand Up @@ -95,6 +95,12 @@ class AuthTokenTable {
*/
void MarkCompleted(const keymaster_operation_handle_t op_handle);

/**
* Update the last_off_body_ timestamp so that tokens which remain authorized only so long as
* the device stays on body can be revoked.
*/
void onDeviceOffBody();

void Clear();

size_t size() { return entries_.size(); }
Expand Down Expand Up @@ -155,6 +161,7 @@ class AuthTokenTable {

std::vector<Entry> entries_;
size_t max_entries_;
time_t last_off_body_;
time_t (*clock_function_)();
};

Expand Down
1 change: 1 addition & 0 deletions keystore/blob.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ typedef enum {
TYPE_MASTER_KEY = 2,
TYPE_KEY_PAIR = 3,
TYPE_KEYMASTER_10 = 4,
TYPE_KEY_CHARACTERISTICS = 5,
} BlobType;

class Entropy;
Expand Down
2 changes: 2 additions & 0 deletions keystore/include/keystore/IKeystoreService.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class IKeystoreService: public IInterface {
ON_USER_ADDED = IBinder::FIRST_CALL_TRANSACTION + 33,
ON_USER_REMOVED = IBinder::FIRST_CALL_TRANSACTION + 34,
ATTEST_KEY = IBinder::FIRST_CALL_TRANSACTION + 35,
ON_DEVICE_OFF_BODY = IBinder::FIRST_CALL_TRANSACTION + 36,
};

DECLARE_META_INTERFACE(KeystoreService);
Expand Down Expand Up @@ -248,6 +249,7 @@ class IKeystoreService: public IInterface {
virtual int32_t attestKey(const String16& name, const KeymasterArguments& params,
KeymasterCertificateChain* outChain) = 0;

virtual int32_t onDeviceOffBody() = 0;
};

// ----------------------------------------------------------------------------
Expand Down
124 changes: 108 additions & 16 deletions keystore/key_store_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ int32_t KeyStoreService::insert(const String16& name, const uint8_t* item, size_
}

String8 name8(name);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_GENERIC));

Blob keyBlob(item, itemLength, NULL, 0, ::TYPE_GENERIC);
keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
Expand All @@ -111,8 +111,16 @@ int32_t KeyStoreService::del(const String16& name, int targetUid) {
return ::PERMISSION_DENIED;
}
String8 name8(name);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
return mKeyStore->del(filename.string(), ::TYPE_ANY, get_user_id(targetUid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_ANY));
int32_t result = mKeyStore->del(filename.string(), ::TYPE_ANY, get_user_id(targetUid));
if (result != ::NO_ERROR) {
return result;
}

// Also delete any characteristics files
String8 chrFilename(mKeyStore->getKeyNameForUidWithDir(
name8, targetUid, ::TYPE_KEY_CHARACTERISTICS));
return mKeyStore->del(chrFilename.string(), ::TYPE_KEY_CHARACTERISTICS, get_user_id(targetUid));
}

int32_t KeyStoreService::exist(const String16& name, int targetUid) {
Expand All @@ -122,7 +130,7 @@ int32_t KeyStoreService::exist(const String16& name, int targetUid) {
}

String8 name8(name);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_ANY));

if (access(filename.string(), R_OK) == -1) {
return (errno != ENOENT) ? ::SYSTEM_ERROR : ::KEY_NOT_FOUND;
Expand All @@ -136,7 +144,7 @@ int32_t KeyStoreService::list(const String16& prefix, int targetUid, Vector<Stri
return ::PERMISSION_DENIED;
}
const String8 prefix8(prefix);
String8 filename(mKeyStore->getKeyNameForUid(prefix8, targetUid));
String8 filename(mKeyStore->getKeyNameForUid(prefix8, targetUid, TYPE_ANY));

if (mKeyStore->list(filename, matches, get_user_id(targetUid)) != ::NO_ERROR) {
return ::SYSTEM_ERROR;
Expand Down Expand Up @@ -425,7 +433,7 @@ int32_t KeyStoreService::grant(const String16& name, int32_t granteeUid) {
}

String8 name8(name);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, callingUid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, callingUid, ::TYPE_ANY));

if (access(filename.string(), R_OK) == -1) {
return (errno != ENOENT) ? ::SYSTEM_ERROR : ::KEY_NOT_FOUND;
Expand All @@ -443,7 +451,7 @@ int32_t KeyStoreService::ungrant(const String16& name, int32_t granteeUid) {
}

String8 name8(name);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, callingUid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, callingUid, ::TYPE_ANY));

if (access(filename.string(), R_OK) == -1) {
return (errno != ENOENT) ? ::SYSTEM_ERROR : ::KEY_NOT_FOUND;
Expand All @@ -460,7 +468,7 @@ int64_t KeyStoreService::getmtime(const String16& name, int32_t uid) {
}

String8 name8(name);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_ANY));

if (access(filename.string(), R_OK) == -1) {
ALOGW("could not access %s for getmtime", filename.string());
Expand All @@ -484,6 +492,7 @@ int64_t KeyStoreService::getmtime(const String16& name, int32_t uid) {
return static_cast<int64_t>(s.st_mtime);
}

// TODO(tuckeris): This is dead code, remove it. Don't bother copying over key characteristics here
int32_t KeyStoreService::duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
int32_t destUid) {
uid_t callingUid = IPCThreadState::self()->getCallingUid();
Expand Down Expand Up @@ -525,10 +534,10 @@ int32_t KeyStoreService::duplicate(const String16& srcKey, int32_t srcUid, const
}

String8 source8(srcKey);
String8 sourceFile(mKeyStore->getKeyNameForUidWithDir(source8, srcUid));
String8 sourceFile(mKeyStore->getKeyNameForUidWithDir(source8, srcUid, ::TYPE_ANY));

String8 target8(destKey);
String8 targetFile(mKeyStore->getKeyNameForUidWithDir(target8, destUid));
String8 targetFile(mKeyStore->getKeyNameForUidWithDir(target8, destUid, ::TYPE_ANY));

if (access(targetFile.string(), W_OK) != -1 || errno != ENOENT) {
ALOGD("destination already exists: %s", targetFile.string());
Expand Down Expand Up @@ -563,8 +572,13 @@ int32_t KeyStoreService::clear_uid(int64_t targetUid64) {

for (uint32_t i = 0; i < aliases.size(); i++) {
String8 name8(aliases[i]);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_ANY));
mKeyStore->del(filename.string(), ::TYPE_ANY, get_user_id(targetUid));

// del() will fail silently if no cached characteristics are present for this alias.
String8 chr_filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid,
::TYPE_KEY_CHARACTERISTICS));
mKeyStore->del(chr_filename.string(), ::TYPE_KEY_CHARACTERISTICS, get_user_id(targetUid));
}
return ::NO_ERROR;
}
Expand Down Expand Up @@ -611,6 +625,19 @@ int32_t KeyStoreService::generateKey(const String16& name, const KeymasterArgume
if (device == NULL) {
return ::SYSTEM_ERROR;
}

// Capture characteristics before they're potentially stripped by the device
AuthorizationSet keyCharacteristics(opParams.data(), opParams.size());
if (keyCharacteristics.is_valid() != AuthorizationSet::Error::OK) {
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
}
UniquePtr<uint8_t[]> kc_buf;
kc_buf.reset(new (std::nothrow) uint8_t[keyCharacteristics.SerializedSize()]);
if (!kc_buf.get()) {
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
}
keyCharacteristics.Serialize(kc_buf.get(), kc_buf.get() + keyCharacteristics.SerializedSize());

// TODO: Seed from Linux RNG before this.
if (device->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0 &&
device->generate_key != NULL) {
Expand Down Expand Up @@ -652,16 +679,30 @@ int32_t KeyStoreService::generateKey(const String16& name, const KeymasterArgume
return rc;
}

// Write the key:
String8 name8(name);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid, ::TYPE_KEYMASTER_10));

Blob keyBlob(blob.key_material, blob.key_material_size, NULL, 0, ::TYPE_KEYMASTER_10);
keyBlob.setFallback(isFallback);
keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);

free(const_cast<uint8_t*>(blob.key_material));
rc = mKeyStore->put(filename.string(), &keyBlob, get_user_id(uid));

return mKeyStore->put(filename.string(), &keyBlob, get_user_id(uid));
if (rc != ::NO_ERROR) {
return rc;
}

// Write the characteristics:
String8 cFilename(mKeyStore->getKeyNameForUidWithDir(name8, uid, ::TYPE_KEY_CHARACTERISTICS));

Blob charBlob(kc_buf.get(), keyCharacteristics.SerializedSize(),
NULL, 0, ::TYPE_KEY_CHARACTERISTICS);
charBlob.setFallback(isFallback);
charBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);

return mKeyStore->put(cFilename.string(), &charBlob, get_user_id(uid));
}

int32_t KeyStoreService::getKeyCharacteristics(const String16& name,
Expand Down Expand Up @@ -743,6 +784,19 @@ int32_t KeyStoreService::importKey(const String16& name, const KeymasterArgument
if (device == NULL) {
return ::SYSTEM_ERROR;
}

// Capture characteristics before they're potentially stripped
AuthorizationSet keyCharacteristics(opParams.data(), opParams.size());
if (keyCharacteristics.is_valid() != AuthorizationSet::Error::OK) {
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
}
UniquePtr<uint8_t[]> kc_buf;
kc_buf.reset(new (std::nothrow) uint8_t[keyCharacteristics.SerializedSize()]);
if (!kc_buf.get()) {
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
}
keyCharacteristics.Serialize(kc_buf.get(), kc_buf.get() + keyCharacteristics.SerializedSize());

if (device->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0 &&
device->import_key != NULL) {
rc = device->import_key(device, &inParams, format, &input, &blob,
Expand All @@ -762,16 +816,30 @@ int32_t KeyStoreService::importKey(const String16& name, const KeymasterArgument
return rc;
}

// Write the key:
String8 name8(name);
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid, ::TYPE_KEYMASTER_10));

Blob keyBlob(blob.key_material, blob.key_material_size, NULL, 0, ::TYPE_KEYMASTER_10);
keyBlob.setFallback(isFallback);
keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);

free(const_cast<uint8_t*>(blob.key_material));
rc = mKeyStore->put(filename.string(), &keyBlob, get_user_id(uid));

if (rc != ::NO_ERROR) {
return rc;
}

// Write the characteristics:
String8 cFilename(mKeyStore->getKeyNameForUidWithDir(name8, uid, ::TYPE_KEY_CHARACTERISTICS));

Blob charBlob(kc_buf.get(), keyCharacteristics.SerializedSize(),
NULL, 0, ::TYPE_KEY_CHARACTERISTICS);
charBlob.setFallback(isFallback);
charBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);

return mKeyStore->put(filename.string(), &keyBlob, get_user_id(uid));
return mKeyStore->put(cFilename.string(), &charBlob, get_user_id(uid));
}

void KeyStoreService::exportKey(const String16& name, keymaster_key_format_t format,
Expand Down Expand Up @@ -864,6 +932,24 @@ void KeyStoreService::begin(const sp<IBinder>& appToken, const String16& name,
return;
}
const hw_auth_token_t* authToken = NULL;

// Merge these characteristics with the ones cached when the key was generated or imported
Blob charBlob;
AuthorizationSet persistedCharacteristics;
responseCode = mKeyStore->getKeyForName(&charBlob, name8, targetUid, TYPE_KEY_CHARACTERISTICS);
if (responseCode == ::NO_ERROR) {
const uint8_t* serializedCharacteristics = charBlob.getValue();
persistedCharacteristics.Deserialize(&serializedCharacteristics,
serializedCharacteristics + charBlob.getLength());
} else {
ALOGD("Unable to read cached characteristics for key");
}

// Replace the sw_enforced set with those persisted to disk, minus hw_enforced
persistedCharacteristics.Union(characteristics.get()->sw_enforced);
persistedCharacteristics.Difference(characteristics.get()->hw_enforced);
persistedCharacteristics.CopyToParamSet(&characteristics.get()->sw_enforced);

int32_t authResult = getAuthToken(characteristics.get(), 0, purpose, &authToken,
/*failOnTokenMissing*/ false);
// If per-operation auth is needed we need to begin the operation and
Expand Down Expand Up @@ -1161,6 +1247,12 @@ int32_t KeyStoreService::attestKey(const String16& name, const KeymasterArgument
return ::NO_ERROR;
}

int32_t KeyStoreService::onDeviceOffBody() {
// TODO(tuckeris): add permission check. This should be callable from ClockworkHome only.
mAuthTokenTable.onDeviceOffBody();
return ::NO_ERROR;
}

/**
* Prune the oldest pruneable operation.
*/
Expand Down Expand Up @@ -1560,7 +1652,7 @@ int32_t KeyStoreService::upgradeKeyBlob(const String16& name, uid_t uid,
return rc;
}

String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid));
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid, ::TYPE_KEYMASTER_10));
Blob newBlob(upgraded_key.key_material, upgraded_key.key_material_size, nullptr /* info */,
0 /* infoLength */, ::TYPE_KEYMASTER_10);
newBlob.setFallback(blob->isFallback());
Expand Down
2 changes: 2 additions & 0 deletions keystore/key_store_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class KeyStoreService : public BnKeystoreService, public IBinder::DeathRecipient
int32_t attestKey(const String16& name, const KeymasterArguments& params,
KeymasterCertificateChain* outChain) override;

int32_t onDeviceOffBody();

private:
static const int32_t UID_SELF = -1;

Expand Down
Loading

0 comments on commit 0ab28b7

Please sign in to comment.