mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 22:04:26 +00:00
Merge "Port IdentityCredential HAL to AIDL." am: 6a83338df9
Change-Id: Idf1e9ce02d150cde887c26e82c2a9c91196540f3
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
// This file is autogenerated by hidl-gen -Landroidbp.
|
||||
|
||||
hidl_interface {
|
||||
name: "android.hardware.identity@1.0",
|
||||
root: "android.hardware",
|
||||
vndk: {
|
||||
enabled: true,
|
||||
},
|
||||
srcs: [
|
||||
"types.hal",
|
||||
"IIdentityCredential.hal",
|
||||
"IIdentityCredentialStore.hal",
|
||||
"IWritableIdentityCredential.hal",
|
||||
],
|
||||
interfaces: [
|
||||
"android.hardware.keymaster@4.0",
|
||||
"android.hidl.base@1.0",
|
||||
],
|
||||
gen_java: false,
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2019 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.
|
||||
//
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.identity@1.0-service.example",
|
||||
init_rc: ["android.hardware.identity@1.0-service.example.rc"],
|
||||
vendor: true,
|
||||
relative_install_path: "hw",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
],
|
||||
srcs: [
|
||||
"service.cpp",
|
||||
"IdentityCredential.cpp",
|
||||
"IdentityCredentialStore.cpp",
|
||||
"WritableIdentityCredential.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.identity@1.0",
|
||||
"android.hardware.identity-support-lib",
|
||||
"android.hardware.keymaster@4.0",
|
||||
"libcppbor",
|
||||
"libcrypto",
|
||||
"libbase",
|
||||
"libhidlbase",
|
||||
"liblog",
|
||||
"libutils",
|
||||
],
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019, 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 ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
|
||||
#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
|
||||
|
||||
#include <android/hardware/identity/1.0/IIdentityCredential.h>
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cppbor/cppbor.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
namespace implementation {
|
||||
|
||||
using ::std::map;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using ::android::hardware::identity::V1_0::IIdentityCredential;
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
|
||||
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
|
||||
|
||||
using MapStringToVectorOfStrings = map<string, vector<string>>;
|
||||
|
||||
class IdentityCredential : public IIdentityCredential {
|
||||
public:
|
||||
IdentityCredential(const hidl_vec<uint8_t>& credentialData)
|
||||
: credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
|
||||
|
||||
// Parses and decrypts credentialData_, return false on failure. Must be
|
||||
// called right after construction.
|
||||
ResultCode initialize();
|
||||
|
||||
// Methods from ::android::hardware::identity::IIdentityCredential follow.
|
||||
|
||||
Return<void> deleteCredential(deleteCredential_cb _hidl_cb) override;
|
||||
Return<void> createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) override;
|
||||
|
||||
Return<void> setReaderEphemeralPublicKey(const hidl_vec<uint8_t>& publicKey,
|
||||
setReaderEphemeralPublicKey_cb _hidl_cb) override;
|
||||
|
||||
Return<void> createAuthChallenge(createAuthChallenge_cb _hidl_cb) override;
|
||||
|
||||
Return<void> startRetrieval(const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
|
||||
const HardwareAuthToken& authToken,
|
||||
const hidl_vec<uint8_t>& itemsRequest,
|
||||
const hidl_vec<uint8_t>& sessionTranscript,
|
||||
const hidl_vec<uint8_t>& readerSignature,
|
||||
const hidl_vec<uint16_t>& requestCounts,
|
||||
startRetrieval_cb _hidl_cb) override;
|
||||
Return<void> startRetrieveEntryValue(const hidl_string& nameSpace, const hidl_string& name,
|
||||
uint32_t entrySize,
|
||||
const hidl_vec<uint16_t>& accessControlProfileIds,
|
||||
startRetrieveEntryValue_cb _hidl_cb) override;
|
||||
Return<void> retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
|
||||
retrieveEntryValue_cb _hidl_cb) override;
|
||||
Return<void> finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
|
||||
finishRetrieval_cb _hidl_cb) override;
|
||||
|
||||
Return<void> generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) override;
|
||||
|
||||
private:
|
||||
// Set by constructor
|
||||
vector<uint8_t> credentialData_;
|
||||
int numStartRetrievalCalls_;
|
||||
|
||||
// Set by initialize()
|
||||
string docType_;
|
||||
bool testCredential_;
|
||||
vector<uint8_t> storageKey_;
|
||||
vector<uint8_t> credentialPrivKey_;
|
||||
|
||||
// Set by createEphemeralKeyPair()
|
||||
vector<uint8_t> ephemeralPublicKey_;
|
||||
|
||||
// Set by setReaderEphemeralPublicKey()
|
||||
vector<uint8_t> readerPublicKey_;
|
||||
|
||||
// Set by createAuthChallenge()
|
||||
uint64_t authChallenge_;
|
||||
|
||||
// Set at startRetrieval() time.
|
||||
map<uint16_t, ResultCode> profileIdToAccessCheckResult_;
|
||||
vector<uint8_t> sessionTranscript_;
|
||||
std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
|
||||
vector<uint8_t> itemsRequest_;
|
||||
vector<uint16_t> requestCountsRemaining_;
|
||||
MapStringToVectorOfStrings requestedNameSpacesAndNames_;
|
||||
cppbor::Map deviceNameSpacesMap_;
|
||||
cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
|
||||
|
||||
// Set at startRetrieveEntryValue() time.
|
||||
string currentNameSpace_;
|
||||
string currentName_;
|
||||
size_t entryRemainingBytes_;
|
||||
vector<uint8_t> entryValue_;
|
||||
vector<uint8_t> entryAdditionalData_;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019, 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 "IdentityCredentialStore"
|
||||
|
||||
#include "IdentityCredentialStore.h"
|
||||
#include "IdentityCredential.h"
|
||||
#include "WritableIdentityCredential.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
namespace implementation {
|
||||
|
||||
// Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
|
||||
|
||||
Return<void> IdentityCredentialStore::getHardwareInformation(getHardwareInformation_cb _hidl_cb) {
|
||||
_hidl_cb(support::resultOK(), "IdentityCredential Reference Implementation", "Google",
|
||||
kGcmChunkSize, false /* isDirectAccess */, {} /* supportedDocTypes */);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> IdentityCredentialStore::createCredential(const hidl_string& docType,
|
||||
bool testCredential,
|
||||
createCredential_cb _hidl_cb) {
|
||||
auto writable_credential = new WritableIdentityCredential(docType, testCredential);
|
||||
if (!writable_credential->initialize()) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED,
|
||||
"Error initializing WritableIdentityCredential"),
|
||||
writable_credential);
|
||||
return Void();
|
||||
}
|
||||
_hidl_cb(support::resultOK(), writable_credential);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> IdentityCredentialStore::getCredential(const hidl_vec<uint8_t>& credentialData,
|
||||
getCredential_cb _hidl_cb) {
|
||||
auto credential = new IdentityCredential(credentialData);
|
||||
// We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
|
||||
auto ret = credential->initialize();
|
||||
if (ret != ResultCode::OK) {
|
||||
_hidl_cb(support::result(ret, "Error initializing IdentityCredential"), credential);
|
||||
return Void();
|
||||
}
|
||||
_hidl_cb(support::resultOK(), credential);
|
||||
return Void();
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019, 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 ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
|
||||
#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
|
||||
|
||||
#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
|
||||
class IdentityCredentialStore : public IIdentityCredentialStore {
|
||||
public:
|
||||
IdentityCredentialStore() {}
|
||||
|
||||
// The GCM chunk size used by this implementation is 64 KiB.
|
||||
static constexpr size_t kGcmChunkSize = 64 * 1024;
|
||||
|
||||
// Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
|
||||
Return<void> getHardwareInformation(getHardwareInformation_cb _hidl_cb) override;
|
||||
Return<void> createCredential(const hidl_string& docType, bool testCredential,
|
||||
createCredential_cb _hidl_cb) override;
|
||||
Return<void> getCredential(const hidl_vec<uint8_t>& credentialData,
|
||||
getCredential_cb _hidl_cb) override;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019, 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 ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
|
||||
#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
|
||||
|
||||
#include <android/hardware/identity/1.0/IWritableIdentityCredential.h>
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <cppbor.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
namespace implementation {
|
||||
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
|
||||
|
||||
class WritableIdentityCredential : public IWritableIdentityCredential {
|
||||
public:
|
||||
WritableIdentityCredential(const hidl_string& docType, bool testCredential)
|
||||
: docType_(docType), testCredential_(testCredential) {}
|
||||
|
||||
// Creates the Credential Key. Returns false on failure. Must be called
|
||||
// right after construction.
|
||||
bool initialize();
|
||||
|
||||
// Methods from ::android::hardware::identity::IWritableIdentityCredential
|
||||
// follow.
|
||||
Return<void> getAttestationCertificate(const hidl_vec<uint8_t>& attestationApplicationId,
|
||||
const hidl_vec<uint8_t>& attestationChallenge,
|
||||
getAttestationCertificate_cb _hidl_cb) override;
|
||||
|
||||
Return<void> startPersonalization(uint16_t accessControlProfileCount,
|
||||
const hidl_vec<uint16_t>& entryCounts,
|
||||
startPersonalization_cb _hidl_cb) override;
|
||||
|
||||
Return<void> addAccessControlProfile(uint16_t id, const hidl_vec<uint8_t>& readerCertificate,
|
||||
bool userAuthenticationRequired, uint64_t timeoutMillis,
|
||||
uint64_t secureUserId,
|
||||
addAccessControlProfile_cb _hidl_cb) override;
|
||||
|
||||
Return<void> beginAddEntry(const hidl_vec<uint16_t>& accessControlProfileIds,
|
||||
const hidl_string& nameSpace, const hidl_string& name,
|
||||
uint32_t entrySize, beginAddEntry_cb _hidl_cb) override;
|
||||
|
||||
Return<void> addEntryValue(const hidl_vec<uint8_t>& content,
|
||||
addEntryValue_cb _hidl_cb) override;
|
||||
|
||||
Return<void> finishAddingEntries(finishAddingEntries_cb _hidl_cb) override;
|
||||
|
||||
private:
|
||||
string docType_;
|
||||
bool testCredential_;
|
||||
|
||||
// These are set in initialize().
|
||||
vector<uint8_t> storageKey_;
|
||||
vector<uint8_t> credentialPrivKey_;
|
||||
vector<uint8_t> credentialPubKey_;
|
||||
|
||||
// These fields are initialized during startPersonalization()
|
||||
size_t numAccessControlProfileRemaining_;
|
||||
vector<uint16_t> remainingEntryCounts_;
|
||||
cppbor::Array signedDataAccessControlProfiles_;
|
||||
cppbor::Map signedDataNamespaces_;
|
||||
cppbor::Array signedDataCurrentNamespace_;
|
||||
|
||||
// These fields are initialized during beginAddEntry()
|
||||
size_t entryRemainingBytes_;
|
||||
vector<uint8_t> entryAdditionalData_;
|
||||
string entryNameSpace_;
|
||||
string entryName_;
|
||||
vector<uint16_t> entryAccessControlProfileIds_;
|
||||
vector<uint8_t> entryBytes_;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
|
||||
@@ -1,3 +0,0 @@
|
||||
service vendor.identity-1-0 /vendor/bin/hw/android.hardware.identity@1.0-service.example
|
||||
class hal
|
||||
user nobody
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019, 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 "android.hardware.identity@1.0-service"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
|
||||
#include "IdentityCredentialStore.h"
|
||||
|
||||
using android::hardware::joinRpcThreadpool;
|
||||
using android::hardware::identity::implementation::IdentityCredentialStore;
|
||||
|
||||
int main(int /* argc */, char* argv[]) {
|
||||
::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
|
||||
|
||||
::android::base::InitLogging(argv, &android::base::StderrLogger);
|
||||
|
||||
auto identity_store = new IdentityCredentialStore();
|
||||
auto status = identity_store->registerAsService();
|
||||
if (status != android::OK) {
|
||||
LOG(FATAL) << "Could not register service for IdentityCredentialStore 1.0 (" << status
|
||||
<< ")";
|
||||
}
|
||||
joinRpcThreadpool();
|
||||
return -1; // Should never get here.
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
package android.hardware.identity@1.0;
|
||||
|
||||
/**
|
||||
* The ResultCode enumeration is used to convey the status of an operation.
|
||||
*/
|
||||
enum ResultCode : int32_t {
|
||||
/**
|
||||
* Success.
|
||||
*/
|
||||
OK = 0,
|
||||
|
||||
/**
|
||||
* The operation failed. This is used as a generic catch-all for errors that don't belong
|
||||
* in other categories, including memory/resource allocation failures and I/O errors.
|
||||
*/
|
||||
FAILED = 1,
|
||||
|
||||
/**
|
||||
* The passed data was invalid. This is a generic catch all for errors that don't belong
|
||||
* in other categories related to parameter validation.
|
||||
*/
|
||||
INVALID_DATA = 2,
|
||||
|
||||
/**
|
||||
* The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
|
||||
*/
|
||||
INVALID_AUTH_TOKEN = 3,
|
||||
|
||||
/**
|
||||
* The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
|
||||
* the requirements described in the documentation for that method.
|
||||
*/
|
||||
INVALID_ITEMS_REQUEST_MESSAGE = 4,
|
||||
|
||||
/**
|
||||
* The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
|
||||
* doesn't contain an embedded certificate chain, or the signature failed to
|
||||
* validate.
|
||||
*/
|
||||
READER_SIGNATURE_CHECK_FAILED = 5,
|
||||
|
||||
/**
|
||||
* The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
|
||||
* key returned by createEphemeralPublicKey().
|
||||
*/
|
||||
EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 6,
|
||||
|
||||
/**
|
||||
* An access condition related to user authentication was not satisfied.
|
||||
*/
|
||||
USER_AUTHENTICATION_FAILED = 7,
|
||||
|
||||
/**
|
||||
* An access condition related to reader authentication was not satisfied.
|
||||
*/
|
||||
READER_AUTHENTICATION_FAILED = 8,
|
||||
|
||||
/**
|
||||
* The request data element has no access control profiles associated so it cannot be accessed.
|
||||
*/
|
||||
NO_ACCESS_CONTROL_PROFILES = 9,
|
||||
|
||||
/**
|
||||
* The requested data element is not in the provided non-empty itemsRequest message.
|
||||
*/
|
||||
NOT_IN_REQUEST_MESSAGE = 10,
|
||||
|
||||
/**
|
||||
* The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
|
||||
*/
|
||||
SESSION_TRANSCRIPT_MISMATCH = 11,
|
||||
};
|
||||
|
||||
/**
|
||||
* A result has a ResultCode and corresponding textual message.
|
||||
*/
|
||||
struct Result {
|
||||
/**
|
||||
* The result code.
|
||||
*
|
||||
* Implementations must not use values not defined in the ResultCode enumeration.
|
||||
*/
|
||||
ResultCode code;
|
||||
|
||||
/**
|
||||
* A human-readable message in English conveying more detail about a failure.
|
||||
*
|
||||
* If code is ResultCode::OK this field must be set to the empty string.
|
||||
*/
|
||||
string message;
|
||||
};
|
||||
|
||||
struct SecureAccessControlProfile {
|
||||
/**
|
||||
* id is a numeric identifier that must be unique within the context of a Credential and may be
|
||||
* used to reference the profile.
|
||||
*/
|
||||
uint16_t id;
|
||||
|
||||
/**
|
||||
* readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
|
||||
* of certificates) that must be used to authenticate requests. For details about how
|
||||
* this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
|
||||
*/
|
||||
vec<uint8_t> readerCertificate;
|
||||
|
||||
/**
|
||||
* if true, the user is required to authenticate to allow requests. Required authentication
|
||||
* fressness is specified by timeout below.
|
||||
*
|
||||
*/
|
||||
bool userAuthenticationRequired;
|
||||
|
||||
/**
|
||||
* Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
|
||||
* above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired
|
||||
* is true and timout is zero then authentication is required for each reader session.
|
||||
*
|
||||
* If userAuthenticationRequired is false, timeout must be zero.
|
||||
*/
|
||||
uint64_t timeoutMillis;
|
||||
|
||||
/**
|
||||
* secureUserId must be non-zero if userAuthenticationRequired is true.
|
||||
* It is not related to any Android user ID or UID, but is created in the
|
||||
* Gatekeeper application in the secure environment.
|
||||
*/
|
||||
uint64_t secureUserId;
|
||||
|
||||
/**
|
||||
* The mac is used to authenticate the access control profile. It contains:
|
||||
*
|
||||
* AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
|
||||
*
|
||||
* where AccessControlProfile is the CBOR map:
|
||||
*
|
||||
* AccessControlProfile = {
|
||||
* "id": uint,
|
||||
* ? "readerCertificate" : bstr,
|
||||
* ? (
|
||||
* "userAuthenticationRequired" : bool,
|
||||
* "timeoutMillis" : uint,
|
||||
* "secureUserId" : uint
|
||||
* )
|
||||
* }
|
||||
*/
|
||||
vec<uint8_t> mac;
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
swillden@google.com
|
||||
zeuthen@google.com
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2019 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.
|
||||
//
|
||||
|
||||
cc_test {
|
||||
name: "VtsHalIdentityCredentialTargetTest",
|
||||
defaults: ["VtsHalTargetTestDefaults"],
|
||||
srcs: [
|
||||
"VtsHalIdentityCredentialTargetTest.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.identity@1.0",
|
||||
"android.hardware.identity-support-lib",
|
||||
"android.hardware.keymaster@4.0",
|
||||
"libcppbor",
|
||||
],
|
||||
shared_libs: [
|
||||
"libcrypto",
|
||||
],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"vts-core",
|
||||
],
|
||||
}
|
||||
21
identity/aidl/Android.bp
Normal file
21
identity/aidl/Android.bp
Normal file
@@ -0,0 +1,21 @@
|
||||
aidl_interface {
|
||||
name: "android.hardware.identity",
|
||||
vendor_available: true,
|
||||
srcs: [
|
||||
"android/hardware/identity/*.aidl",
|
||||
],
|
||||
imports: [
|
||||
"android.hardware.keymaster",
|
||||
],
|
||||
stability: "vintf",
|
||||
backend: {
|
||||
java: {
|
||||
platform_apis: true,
|
||||
},
|
||||
ndk: {
|
||||
vndk: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
27
identity/aidl/android/hardware/identity/Certificate.aidl
Normal file
27
identity/aidl/android/hardware/identity/Certificate.aidl
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
package android.hardware.identity;
|
||||
|
||||
@VintfStability
|
||||
parcelable Certificate {
|
||||
/**
|
||||
* encodedCertificate contains the bytes of a DER-encoded X.509 certificate.
|
||||
*
|
||||
* If there is no certificate, this array is empty.
|
||||
*/
|
||||
byte[] encodedCertificate;
|
||||
}
|
||||
39
identity/aidl/android/hardware/identity/CipherSuite.aidl
Normal file
39
identity/aidl/android/hardware/identity/CipherSuite.aidl
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
package android.hardware.identity;
|
||||
|
||||
/**
|
||||
* Cipher suites that can be used for communication between holder and reader devices.
|
||||
*/
|
||||
@VintfStability
|
||||
@Backing(type="int")
|
||||
enum CipherSuite {
|
||||
/**
|
||||
* Specifies that the cipher suite that will be used to secure communications between the reader
|
||||
* is:
|
||||
*
|
||||
* - ECDHE with HKDF-SHA-256 for key agreement.
|
||||
* - AES-256 with GCM block mode for authenticated encryption (nonces are incremented by
|
||||
* one for every message).
|
||||
* - ECDSA with SHA-256 for signing (used for signing session transcripts to defeat
|
||||
* man-in-the-middle attacks), signing keys are not ephemeral.
|
||||
*
|
||||
* At present this is the only supported cipher suite and it is mandatory for all
|
||||
* implementations to support it.
|
||||
*/
|
||||
CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1,
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
package android.hardware.identity;
|
||||
|
||||
@VintfStability
|
||||
parcelable HardwareInformation {
|
||||
/**
|
||||
* credentialStoreName is the name of the credential store implementation.
|
||||
*/
|
||||
@utf8InCpp String credentialStoreName;
|
||||
|
||||
/**
|
||||
* credentialStoreAuthorName is the name of the credential store author.
|
||||
*/
|
||||
@utf8InCpp String credentialStoreAuthorName;
|
||||
|
||||
/**
|
||||
* dataChunkSize is the size of data chunks to be used when sending and recieving data
|
||||
* entries. All data chunks for a data item must be this size except for the last.
|
||||
*/
|
||||
int dataChunkSize;
|
||||
|
||||
/**
|
||||
* isDirectAccess specifies whether the provisioned credential is available through
|
||||
* direct access. Credentials provisioned in credential stores with this set
|
||||
* to true, should use reader authentication on all data elements.
|
||||
*/
|
||||
boolean isDirectAccess;
|
||||
|
||||
/**
|
||||
* supportedDocTypes if empty, then any document type is supported, otherwise
|
||||
* only the document types returned are supported.
|
||||
*
|
||||
* Document types are defined in the relevant standard for the document, for example for the
|
||||
* for Mobile Driving License as defined by ISO 18013-5 the document type is defined to
|
||||
* be "org.iso.18013.5.1.mDL".
|
||||
*
|
||||
*/
|
||||
@utf8InCpp String[] supportedDocTypes;
|
||||
}
|
||||
@@ -14,10 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.identity@1.0;
|
||||
package android.hardware.identity;
|
||||
|
||||
import android.hardware.keymaster@4.0::HardwareAuthToken;
|
||||
import android.hardware.identity.Certificate;
|
||||
import android.hardware.identity.SecureAccessControlProfile;
|
||||
import android.hardware.keymaster.HardwareAuthToken;
|
||||
|
||||
@VintfStability
|
||||
interface IIdentityCredential {
|
||||
/**
|
||||
* Delete a credential.
|
||||
@@ -35,10 +38,9 @@ interface IIdentityCredential {
|
||||
* After this method has been called, the persistent storage used for credentialData should
|
||||
* be deleted.
|
||||
*
|
||||
* @return proofOfDeletionSignature is a COSE_Sign1 signature described above.
|
||||
* @return a COSE_Sign1 signature described above.
|
||||
*/
|
||||
deleteCredential()
|
||||
generates(Result result, vec<uint8_t> proofOfDeletionSignature);
|
||||
byte[] deleteCredential();
|
||||
|
||||
/**
|
||||
* Creates an ephemeral EC key pair, for use in establishing a seceure session with a reader.
|
||||
@@ -46,39 +48,33 @@ interface IIdentityCredential {
|
||||
* with the reader. The reason for generating the key pair in the secure environment is so that
|
||||
* the secure environment knows what public key to expect to find in the session transcript.
|
||||
*
|
||||
* This method may only be called once per instance. If called more than once, FAILED
|
||||
* This method may only be called once per instance. If called more than once, STATUS_FAILED
|
||||
* will be returned.
|
||||
*
|
||||
* @return result is OK on success or FAILED if an error occurred.
|
||||
*
|
||||
* @return keyPair contains the unencrypted key-pair in PKCS#8 format.
|
||||
* @return the unencrypted key-pair in PKCS#8 format.
|
||||
*/
|
||||
createEphemeralKeyPair() generates (Result result, vec<uint8_t> keyPair);
|
||||
byte[] createEphemeralKeyPair();
|
||||
|
||||
/**
|
||||
* Sets the public part of the reader's ephemeral key pair.
|
||||
*
|
||||
* This method may only be called once per instance. If called more than once, FAILED
|
||||
* This method may only be called once per instance. If called more than once, STATUS_FAILED
|
||||
* will be returned.
|
||||
*
|
||||
* @param publicKey contains the reader's ephemeral public key, in uncompressed form.
|
||||
*
|
||||
* @return result is OK on success or FAILED if an error occurred.
|
||||
*/
|
||||
setReaderEphemeralPublicKey(vec<uint8_t> publicKey) generates (Result result);
|
||||
void setReaderEphemeralPublicKey(in byte[] publicKey);
|
||||
|
||||
/**
|
||||
* Creates a challenge value to be used for proving successful user authentication. This
|
||||
* is included in the authToken passed to the startRetrieval() method.
|
||||
*
|
||||
* This method may only be called once per instance. If called more than once, FAILED
|
||||
* This method may only be called once per instance. If called more than once, STATUS_FAILED
|
||||
* will be returned.
|
||||
*
|
||||
* @return result is OK on success or FAILED if an error occurred.
|
||||
*
|
||||
* @return challenge on success, is a non-zero number.
|
||||
* @return challenge, a non-zero number.
|
||||
*/
|
||||
createAuthChallenge() generates (Result result, uint64_t challenge);
|
||||
long createAuthChallenge();
|
||||
|
||||
/**
|
||||
* Start an entry retrieval process.
|
||||
@@ -92,12 +88,12 @@ interface IIdentityCredential {
|
||||
* startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(),
|
||||
* then finally finishRetrieval()) but if this is done, the sessionTranscript parameter
|
||||
* must be identical for each startRetrieval() invocation. If this is not the case, this call
|
||||
* fails with the SESSION_TRANSCRIPT_MISMATCH error.
|
||||
* fails with the STATUS_SESSION_TRANSCRIPT_MISMATCH error.
|
||||
*
|
||||
* If the provided authToken is not valid this method fails with INVALID_AUTH_TOKEN.
|
||||
* If the provided authToken is not valid this method fails with STATUS_INVALID_AUTH_TOKEN.
|
||||
*
|
||||
* Each of the provided accessControlProfiles is checked in this call. If they are not
|
||||
* all valid, the call fails with INVALID_DATA.
|
||||
* all valid, the call fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* For the itemsRequest parameter, the content can be defined in the way appropriate for
|
||||
* the credential, but there are three requirements that must be met to work with this HAL:
|
||||
@@ -108,7 +104,7 @@ interface IIdentityCredential {
|
||||
* the example below.
|
||||
*
|
||||
* If these requirements are not met the startRetrieval() call fails with
|
||||
* INVALID_ITEMS_REQUEST_MESSAGE.
|
||||
* STATUS_INVALID_ITEMS_REQUEST_MESSAGE.
|
||||
*
|
||||
* Here's an example of ItemsRequest CBOR which conforms to this requirement:
|
||||
*
|
||||
@@ -156,17 +152,18 @@ interface IIdentityCredential {
|
||||
* 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described
|
||||
* in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element
|
||||
* and there may be more (and if so, each certificate must be signed by its successor).
|
||||
* This is checked and if the check fails the call fails with READER_SIGNATURE_CHECK_FAILED.
|
||||
* This is checked and if the check fails the call fails with
|
||||
* STATUS_READER_SIGNATURE_CHECK_FAILED.
|
||||
*
|
||||
* The SessionTranscript CBOR is conveyed in the sessionTranscript parameter. It
|
||||
* is permissible for this to be empty in which case the readerSignature parameter
|
||||
* must also be empty. If this is not the case, the call fails with FAILED.
|
||||
* must also be empty. If this is not the case, the call fails with STATUS_FAILED.
|
||||
*
|
||||
* If the SessionTranscript CBOR is not empty, the X and Y coordinates of the public
|
||||
* part of the key-pair previously generated by createEphemeralKeyPair() must appear
|
||||
* somewhere in the bytes of DeviceEngagement structure. Both X and Y should be in
|
||||
* uncompressed form. If this is not satisfied, the call fails with
|
||||
* EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
|
||||
* STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
|
||||
*
|
||||
* @param accessControlProfiles
|
||||
* Access control profiles that are required to retrieve the entries that are going to be
|
||||
@@ -196,16 +193,11 @@ interface IIdentityCredential {
|
||||
* will succeed (i.e. that the access control profile checks will succeed). This means that
|
||||
* it's the responsibility of the caller to determine which access control checks will fail
|
||||
* and remove the corresponding requests from the counts.
|
||||
*
|
||||
* @return result is OK on success. If an error occurs one of the values described above
|
||||
* will be returned.
|
||||
*/
|
||||
startRetrieval(vec<SecureAccessControlProfile> accessControlProfiles,
|
||||
HardwareAuthToken authToken,
|
||||
vec<uint8_t> itemsRequest,
|
||||
vec<uint8_t> sessionTranscript,
|
||||
vec<uint8_t> readerSignature,
|
||||
vec<uint16_t> requestCounts) generates(Result result);
|
||||
void startRetrieval(in SecureAccessControlProfile[] accessControlProfiles,
|
||||
in HardwareAuthToken authToken,
|
||||
in byte[] itemsRequest,
|
||||
in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
|
||||
|
||||
/**
|
||||
* Starts retrieving an entry, subject to access control requirements. Entries must be
|
||||
@@ -213,15 +205,15 @@ interface IIdentityCredential {
|
||||
*
|
||||
* If the requestData parameter as passed to startRetrieval() was non-empty
|
||||
* this method must only be called with entries specified in that field. If this
|
||||
* requirement is not met, the call fails with NOT_IN_REQUEST_MESSAGE.
|
||||
* requirement is not met, the call fails with STATUS_NOT_IN_REQUEST_MESSAGE.
|
||||
*
|
||||
* If nameSpace or name is empty this call fails with INVALID_DATA.
|
||||
* If nameSpace or name is empty this call fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* Each access control profile for the entry is checked. If user authentication
|
||||
* is required and the supplied auth token doesn't provide it the call fails
|
||||
* with USER_AUTHENTICATION_FAILED. If reader authentication is required and
|
||||
* with STATUS_USER_AUTHENTICATION_FAILED. If reader authentication is required and
|
||||
* a suitable reader certificate chain isn't presented, the call fails with
|
||||
* READER_AUTHENTICATION_FAILED.
|
||||
* STATUS_READER_AUTHENTICATION_FAILED.
|
||||
*
|
||||
* It is permissible to keep retrieving values if an access control check fails.
|
||||
*
|
||||
@@ -231,38 +223,29 @@ interface IIdentityCredential {
|
||||
*
|
||||
* @param entrySize is the size of the entry value, if it's a text string or a byte string.
|
||||
* It must be zero if the entry value is an integer or boolean. If this requirement
|
||||
* is not met the call fails with INVALID_DATA.
|
||||
* is not met the call fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* @param accessControlProfileIds specifies the set of access control profiles that can
|
||||
* authorize access to the provisioned element. If an identifier of a profile
|
||||
* is given and this profile wasn't passed to startRetrieval() this call fails
|
||||
* with INVALID_DATA.
|
||||
*
|
||||
* @return result is OK on success. Otherwise one of INVALID_DATA, FAILED,
|
||||
* USER_AUTHENTICATION_FAILED, READER_AUTHENTICATION_FAILED.
|
||||
* with STATUS_INVALID_DATA.
|
||||
*/
|
||||
startRetrieveEntryValue(string nameSpace, string name, uint32_t entrySize,
|
||||
vec<uint16_t> accessControlProfileIds)
|
||||
generates (Result result);
|
||||
|
||||
void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name,
|
||||
in int entrySize, in int[] accessControlProfileIds);
|
||||
|
||||
/**
|
||||
* Retrieves an entry value, or part of one, if the entry value is larger than gcmChunkSize.
|
||||
* May only be called after startRetrieveEntry().
|
||||
*
|
||||
* If the passed in data is not authentic, can't be decrypted, is of the wrong size, or can't
|
||||
* be decoded, this call fails with INVALID_DATA.
|
||||
* be decoded, this call fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* @param encryptedContent contains the encrypted and MACed content.
|
||||
*
|
||||
* @return result is OK on success, INVALID_DATA, or FAILED if an error occurred.
|
||||
*
|
||||
* @return content is the entry value as CBOR, or part of the entry value in the case the
|
||||
* @return the entry value as CBOR, or part of the entry value in the case the
|
||||
* content exceeds gcmChunkSize in length.
|
||||
*/
|
||||
retrieveEntryValue(vec<uint8_t> encryptedContent)
|
||||
generates (Result result, vec<uint8_t> content);
|
||||
|
||||
byte[] retrieveEntryValue(in byte[] encryptedContent);
|
||||
|
||||
/**
|
||||
* End retrieval of data, optionally returning a message authentication code over the
|
||||
@@ -273,11 +256,9 @@ interface IIdentityCredential {
|
||||
*
|
||||
* @param signingKeyBlob is either empty or a signingKeyBlob (see generateSigningKeyPair(),
|
||||
* below) containing the signing key to use to sign the data retrieved. If this
|
||||
* is not in the right format the call fails with INVALID_DATA.
|
||||
* is not in the right format the call fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
|
||||
*
|
||||
* @return mac is empty if signingKeyBlob or the sessionTranscript passed to
|
||||
* @param out mac is empty if signingKeyBlob or the sessionTranscript passed to
|
||||
* startRetrieval() is empty. Otherwise it is a COSE_Mac0 with empty payload
|
||||
* and the detached content is set to DeviceAuthentication as defined below.
|
||||
* The key used for the MAC operation is EMacKey and is derived as follows:
|
||||
@@ -321,23 +302,17 @@ interface IIdentityCredential {
|
||||
* DataItemValue = any
|
||||
*
|
||||
*
|
||||
* @return deviceNameSpaces the bytes of DeviceNameSpaces.
|
||||
* @param out deviceNameSpaces the bytes of DeviceNameSpaces.
|
||||
*/
|
||||
finishRetrieval(vec<uint8_t> signingKeyBlob)
|
||||
generates(Result result, vec<uint8_t> mac, vec<uint8_t> deviceNameSpaces);
|
||||
|
||||
void finishRetrieval(in byte[] signingKeyBlob, out byte[] mac, out byte[] deviceNameSpaces);
|
||||
|
||||
/**
|
||||
* Generate a key pair to be used for signing session data and retrieved data items.
|
||||
*
|
||||
* @return result is OK on success or FAILED if an error occurred.
|
||||
* @param out signingKeyBlob contains an encrypted copy of the newly-generated private
|
||||
* signing key.
|
||||
*
|
||||
* @return signingKeyBlob contains an encrypted copy of the newly-generated private signing key.
|
||||
*
|
||||
* @return signingKeyCertificate contains an X.509 certificate for the new signing key, signed
|
||||
* by the credential key.
|
||||
* @return an X.509 certificate for the new signing key, signed by the credential key.
|
||||
*/
|
||||
generateSigningKeyPair()
|
||||
generates(Result result, vec<uint8_t> signingKeyBlob,
|
||||
vec<uint8_t> signingKeyCertificate);
|
||||
};
|
||||
Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
|
||||
}
|
||||
@@ -14,10 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.identity@1.0;
|
||||
package android.hardware.identity;
|
||||
|
||||
import IWritableIdentityCredential;
|
||||
import IIdentityCredential;
|
||||
import android.hardware.identity.IIdentityCredential;
|
||||
import android.hardware.identity.IWritableIdentityCredential;
|
||||
import android.hardware.identity.HardwareInformation;
|
||||
import android.hardware.identity.CipherSuite;
|
||||
|
||||
/**
|
||||
* IIdentityCredentialStore provides an interface to a secure store for user identity documents.
|
||||
@@ -98,37 +100,90 @@ import IIdentityCredential;
|
||||
* define appropriate encodings, those are used. For example, X.509 certificates. Where new
|
||||
* encodings are needed, CBOR is used. CBOR maps are described in CDDL notation
|
||||
* (https://tools.ietf.org/html/draft-ietf-cbor-cddl-06).
|
||||
*
|
||||
* All binder calls in the HAL may return a ServiceSpecificException with statuses from the
|
||||
* STATUS_* integers defined in this interface. Each method states which status can be returned
|
||||
* and under which circumstances.
|
||||
*/
|
||||
@VintfStability
|
||||
interface IIdentityCredentialStore {
|
||||
/**
|
||||
* Success.
|
||||
*/
|
||||
const int STATUS_OK = 0;
|
||||
|
||||
/**
|
||||
* The operation failed. This is used as a generic catch-all for errors that don't belong
|
||||
* in other categories, including memory/resource allocation failures and I/O errors.
|
||||
*/
|
||||
const int STATUS_FAILED = 1;
|
||||
|
||||
/**
|
||||
* Unsupported cipher suite.
|
||||
*/
|
||||
const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2;
|
||||
|
||||
/**
|
||||
* The passed data was invalid. This is a generic catch all for errors that don't belong
|
||||
* in other categories related to parameter validation.
|
||||
*/
|
||||
const int STATUS_INVALID_DATA = 3;
|
||||
|
||||
/**
|
||||
* The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
|
||||
*/
|
||||
const int STATUS_INVALID_AUTH_TOKEN = 4;
|
||||
|
||||
/**
|
||||
* The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
|
||||
* the requirements described in the documentation for that method.
|
||||
*/
|
||||
const int STATUS_INVALID_ITEMS_REQUEST_MESSAGE = 5;
|
||||
|
||||
/**
|
||||
* The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
|
||||
* doesn't contain an embedded certificate chain, or the signature failed to
|
||||
* validate.
|
||||
*/
|
||||
const int STATUS_READER_SIGNATURE_CHECK_FAILED = 6;
|
||||
|
||||
/**
|
||||
* The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
|
||||
* key returned by createEphemeralPublicKey().
|
||||
*/
|
||||
const int STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 7;
|
||||
|
||||
/**
|
||||
* An access condition related to user authentication was not satisfied.
|
||||
*/
|
||||
const int STATUS_USER_AUTHENTICATION_FAILED = 8;
|
||||
|
||||
/**
|
||||
* An access condition related to reader authentication was not satisfied.
|
||||
*/
|
||||
const int STATUS_READER_AUTHENTICATION_FAILED = 9;
|
||||
|
||||
/**
|
||||
* The request data element has no access control profiles associated so it cannot be accessed.
|
||||
*/
|
||||
const int STATUS_NO_ACCESS_CONTROL_PROFILES = 10;
|
||||
|
||||
/**
|
||||
* The requested data element is not in the provided non-empty itemsRequest message.
|
||||
*/
|
||||
const int STATUS_NOT_IN_REQUEST_MESSAGE = 11;
|
||||
|
||||
/**
|
||||
* The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
|
||||
*/
|
||||
const int STATUS_SESSION_TRANSCRIPT_MISMATCH = 12;
|
||||
|
||||
/**
|
||||
* Returns information about hardware.
|
||||
*
|
||||
* The isDirectAccess output parameter indicates whether this credential store
|
||||
* implementation is for direct access. Credentials provisioned in credential
|
||||
* stores with this set to true, should use reader authentication on all data elements.
|
||||
*
|
||||
* @return result is OK on success, FAILED if an error occurred.
|
||||
*
|
||||
* @return credentialStoreName the name of the credential store implementation.
|
||||
*
|
||||
* @return credentialStoreAuthorName the name of the credential store author.
|
||||
*
|
||||
* @return dataChunkSize the maximum size of data chunks.
|
||||
*
|
||||
* @return isDirectAccess whether the provisioned credential is available through
|
||||
* direct access.
|
||||
*
|
||||
* @return supportedDocTypes if empty, then any document type is supported, otherwise
|
||||
* only the document types returned are supported.
|
||||
* @return a HardwareInformation with information about the hardware.
|
||||
*/
|
||||
getHardwareInformation()
|
||||
generates(Result result,
|
||||
string credentialStoreName,
|
||||
string credentialStoreAuthorName,
|
||||
uint32_t dataChunkSize,
|
||||
bool isDirectAccess,
|
||||
vec<string> supportedDocTypes);
|
||||
HardwareInformation getHardwareInformation();
|
||||
|
||||
/**
|
||||
* createCredential creates a new Credential. When a Credential is created, two cryptographic
|
||||
@@ -147,16 +202,14 @@ interface IIdentityCredentialStore {
|
||||
* all-zeros hardware-bound key (HBK) and must set the test bit in the
|
||||
* personalizationReceipt (see finishAddingEntries(), in IWritableIdentityCredential).
|
||||
*
|
||||
* @return result is OK on success, FAILED if an error occurred.
|
||||
*
|
||||
* @return writableCredential is an IWritableIdentityCredential HIDL interface that provides
|
||||
* operations to provision a credential.
|
||||
* @return an IWritableIdentityCredential interface that provides operations to
|
||||
* provision a credential.
|
||||
*/
|
||||
createCredential(string docType, bool testCredential)
|
||||
generates(Result result, IWritableIdentityCredential writableCredential);
|
||||
IWritableIdentityCredential createCredential(in @utf8InCpp String docType,
|
||||
in boolean testCredential);
|
||||
|
||||
/**
|
||||
* getCredential retrieves an IIdentityCredential HIDL interface which allows use of a stored
|
||||
* getCredential retrieves an IIdentityCredential interface which allows use of a stored
|
||||
* Credential.
|
||||
*
|
||||
* The cipher suite used to communicate with the remote verifier must also be specified. Currently
|
||||
@@ -170,16 +223,17 @@ interface IIdentityCredentialStore {
|
||||
*
|
||||
* Support for other cipher suites may be added in a future version of this HAL.
|
||||
*
|
||||
* This method fails with STATUS_INVALID_DATA if the passed in credentialData cannot be
|
||||
* decoded or decrypted.
|
||||
*
|
||||
* @param cipherSuite is the cipher suite to use.
|
||||
*
|
||||
* @param credentialData is a CBOR-encoded structure containing metadata about the credential
|
||||
* and an encrypted byte array that contains data used to secure the credential. See the
|
||||
* return argument of the same name in finishAddingEntries(), in IWritableIdentityCredential.
|
||||
* return argument of the same name in finishAddingEntries(), in
|
||||
* IWritableIdentityCredential.
|
||||
*
|
||||
* @return result is OK on success or INVALID_DATA if the passed in credentialData
|
||||
* cannot be decoded or decrypted.
|
||||
*
|
||||
* @return credential is an IIdentityCredential HIDL interface that provides operations on the
|
||||
* Credential.
|
||||
* @return an IIdentityCredential HIDL interface that provides operations on the Credential.
|
||||
*/
|
||||
getCredential(vec<uint8_t> credentialData)
|
||||
generates (Result result, IIdentityCredential credential);
|
||||
};
|
||||
IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData);
|
||||
}
|
||||
@@ -14,12 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.identity@1.0;
|
||||
package android.hardware.identity;
|
||||
|
||||
import android.hardware.identity.Certificate;
|
||||
import android.hardware.identity.SecureAccessControlProfile;
|
||||
|
||||
/**
|
||||
* IWritableIdentityCredential is used to personalize a new identity credential. Credentials cannot
|
||||
* be updated or modified after creation; any changes require deletion and re-creation.
|
||||
*/
|
||||
@VintfStability
|
||||
interface IWritableIdentityCredential {
|
||||
/**
|
||||
* Gets the certificate chain for credentialKey which can be used to prove the hardware
|
||||
@@ -69,31 +73,23 @@ interface IWritableIdentityCredential {
|
||||
*
|
||||
* @param attestationChallenge a challenge set by the issuer to ensure freshness.
|
||||
*
|
||||
* @return result is OK on success, FAILED if an error occurred.
|
||||
*
|
||||
* @return certificateChain is the X.509 certificate chain for the credentialKey
|
||||
* @return the X.509 certificate chain for the credentialKey
|
||||
*/
|
||||
getAttestationCertificate(vec<uint8_t> attestationApplicationId,
|
||||
vec<uint8_t> attestationChallenge)
|
||||
generates(Result result, vec<vec<uint8_t>> certificateChain);
|
||||
Certificate[] getAttestationCertificate(in byte[] attestationApplicationId, in byte[] attestationChallenge);
|
||||
|
||||
/**
|
||||
* Start the personalization process.
|
||||
*
|
||||
* startPersonalization must not be called more than once.
|
||||
*
|
||||
* @param accessControlProfileCount specifies the number of access control profiles that will be
|
||||
* provisioned with addAccessControlProfile().
|
||||
* @param accessControlProfileCount specifies the number of access control profiles that will
|
||||
* be provisioned with addAccessControlProfile().
|
||||
*
|
||||
* @param entryCounts specifies the number of data entries that will be provisioned with
|
||||
* beginAddEntry() and addEntry(). Each item in the array specifies how many entries
|
||||
* will be added for each name space.
|
||||
*
|
||||
* @return result is OK on success, FAILED if an error occurred.
|
||||
*
|
||||
*/
|
||||
startPersonalization(uint16_t accessControlProfileCount, vec<uint16_t> entryCounts)
|
||||
generates(Result result);
|
||||
void startPersonalization(in int accessControlProfileCount, in int[] entryCounts);
|
||||
|
||||
/**
|
||||
* Add an access control profile, which defines the requirements or retrieval of one or more
|
||||
@@ -103,15 +99,15 @@ interface IWritableIdentityCredential {
|
||||
*
|
||||
* This method must be called exactly as many times as specified in the startPersonalization()
|
||||
* accessControlProfileCount parameter. If this is requirement is not met, the method fails
|
||||
* with INVALID_DATA.
|
||||
* with STATUS_INVALID_DATA.
|
||||
*
|
||||
* @param id a numeric identifier that must be unique within the context of a Credential and may
|
||||
* be used to reference the profile. If this is not satisfied the call fails with
|
||||
* INVALID_DATA.
|
||||
* STATUS_INVALID_DATA.
|
||||
*
|
||||
* @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of certificates)
|
||||
* that must be used to authenticate requests (see the readerSignature parameter in
|
||||
* IIdentityCredential.startRetrieval).
|
||||
* @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of
|
||||
* certificates) that must be used to authenticate requests (see the readerSignature
|
||||
* parameter in IIdentityCredential.startRetrieval).
|
||||
*
|
||||
* @param userAuthenticationRequired if true, specifies that the user is required to
|
||||
* authenticate to allow requests. Required authentication freshness is specified by
|
||||
@@ -121,22 +117,18 @@ interface IWritableIdentityCredential {
|
||||
* authentication (see userAuthenticationRequired above) is valid, if
|
||||
* userAuthenticationRequired is true. If the timout is zero then authentication is
|
||||
* required for each reader session. If userAuthenticationRequired is false, the timeout
|
||||
* must be zero. If this requirement is not met the call fails with INVALID_DATA.
|
||||
* must be zero. If this requirement is not met the call fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* @param secureUserId must be non-zero if userAuthenticationRequired is true. It is not
|
||||
* related to any Android user ID or UID, but is created in the Gatekeeper application
|
||||
* in the secure environment. If this requirement is not met the call fails with
|
||||
* INVALID_DATA.
|
||||
* STATUS_INVALID_DATA.
|
||||
*
|
||||
* @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
|
||||
*
|
||||
* @return secureAccessControlProfile is a structure with the passed-in data and MAC created
|
||||
* with storageKey for authenticating the data at a later point in time.
|
||||
* @return a structure with the passed-in data and MAC created with storageKey for authenticating
|
||||
* the data at a later point in time.
|
||||
*/
|
||||
addAccessControlProfile(uint16_t id, vec<uint8_t> readerCertificate,
|
||||
bool userAuthenticationRequired, uint64_t timeoutMillis,
|
||||
uint64_t secureUserId)
|
||||
generates(Result result, SecureAccessControlProfile secureAccessControlProfile);
|
||||
SecureAccessControlProfile addAccessControlProfile(in int id, in Certificate readerCertificate,
|
||||
in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
|
||||
|
||||
/**
|
||||
* Begins the process of adding an entry to the credential. All access control profiles must be
|
||||
@@ -145,7 +137,7 @@ interface IWritableIdentityCredential {
|
||||
*
|
||||
* This method must be called exactly as many times as the sum of the items in the entryCounts
|
||||
* parameter specified in the startPersonalization(), and must be followed by one or more calls
|
||||
* to addEntryValue(). If this requirement is not met the method fails with INVALID_DATA.
|
||||
* to addEntryValue(). If this requirement is not met the method fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* @param accessControlProfileIds specifies the set of access control profiles that can
|
||||
* authorize access to the provisioned element.
|
||||
@@ -155,13 +147,10 @@ interface IWritableIdentityCredential {
|
||||
* @param name is the name of the element.
|
||||
*
|
||||
* @param entrySize is the size of the entry value. If this requirement
|
||||
* is not met this method fails with INVALID_DATA.
|
||||
*
|
||||
* @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
|
||||
* is not met this method fails with STATUS_INVALID_DATA.
|
||||
*/
|
||||
beginAddEntry(vec<uint16_t> accessControlProfileIds, string nameSpace,
|
||||
string name, uint32_t entrySize)
|
||||
generates(Result result);
|
||||
void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace,
|
||||
in @utf8InCpp String name, in int entrySize);
|
||||
|
||||
/**
|
||||
* Continues the process of adding an entry, providing a value or part of a value.
|
||||
@@ -171,18 +160,13 @@ interface IWritableIdentityCredential {
|
||||
* (see IIdentityCredentialStore.getHardwareInformation()), the caller must provide the
|
||||
* value in chunks. All chunks must be exactly gcmChunkSize except the last and the sum of all
|
||||
* chunk sizes must equal the value of the beginAddEntry() entrySize argument. If this
|
||||
* requirement is not met the call fails with INVALID_DATA.
|
||||
* requirement is not met the call fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* @param content is the entry value, encoded as CBOR. In the case the content exceeds gcmChunkSize,
|
||||
* this may be partial content up to gcmChunkSize bytes long.
|
||||
*
|
||||
* @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
|
||||
*
|
||||
* @return encryptedContent contains the encrypted and MACed content. For directly-available
|
||||
* credentials the contents are implementation-defined but must not exceed 32 bytes in
|
||||
* length.
|
||||
*
|
||||
* For other credentials, encryptedContent contains:
|
||||
* @return the encrypted and MACed content. For directly-available credentials the contents are
|
||||
* implementation-defined. For other credentials, the result contains
|
||||
*
|
||||
* AES-GCM-ENC(storageKey, R, Data, AdditionalData)
|
||||
*
|
||||
@@ -196,18 +180,15 @@ interface IWritableIdentityCredential {
|
||||
* "AccessControlProfileIds" : [ + uint ],
|
||||
* }
|
||||
*/
|
||||
addEntryValue(vec<uint8_t> content)
|
||||
generates(Result result, vec<uint8_t> encryptedContent);
|
||||
byte[] addEntryValue(in byte[] content);
|
||||
|
||||
/**
|
||||
* Finishes adding entries and returns a signature that an issuing authority may use to validate
|
||||
* that all data was provisioned correctly.
|
||||
* Finishes adding entries and returns a signature that an issuing authority may use to
|
||||
* validate that all data was provisioned correctly.
|
||||
*
|
||||
* After this method is called, the IWritableIdentityCredential is no longer usable.
|
||||
*
|
||||
* @return result is OK on success or FAILED if an error occurred.
|
||||
*
|
||||
* @return credentialData is a CBOR-encoded structure (in CDDL notation):
|
||||
* @param out credentialData is a CBOR-encoded structure (in CDDL notation):
|
||||
*
|
||||
* CredentialData = [
|
||||
* tstr, ; docType, an optional name that identifies the type of credential
|
||||
@@ -230,10 +211,10 @@ interface IWritableIdentityCredential {
|
||||
* bstr ; credentialPrivKey, the private key for credentialKey
|
||||
* ]
|
||||
*
|
||||
* @return proofOfProvisioningSignature proves to the IA that the credential was imported into the
|
||||
* secure hardware without alteration or error. When the final addEntry() call is made
|
||||
* (when the number of provisioned entries equals the sum of the items in
|
||||
* startPersonalization() entryCounts parameter), it a COSE_Sign1 structure
|
||||
* @param out proofOfProvisioningSignature proves to the IA that the credential was imported
|
||||
* into the secure hardware without alteration or error. When the final addEntry() call is
|
||||
* made (when the number of provisioned entries equals the sum of the items in
|
||||
* startPersonalization() entryCounts parameter), a COSE_Sign1 structure
|
||||
* signed by CredentialKey with payload set to the ProofOfProvisioning CBOR below:
|
||||
*
|
||||
* ProofOfProvisioning = [
|
||||
@@ -266,7 +247,6 @@ interface IWritableIdentityCredential {
|
||||
* "accessControlProfiles" : [ * uint ],
|
||||
* }
|
||||
*/
|
||||
finishAddingEntries()
|
||||
generates(Result result, vec<uint8_t> credentialData,
|
||||
vec<uint8_t> proofOfProvisioningSignature);
|
||||
};
|
||||
void finishAddingEntries(out byte[] credentialData,
|
||||
out byte[] proofOfProvisioningSignature);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
package android.hardware.identity;
|
||||
|
||||
import android.hardware.identity.Certificate;
|
||||
|
||||
@VintfStability
|
||||
parcelable SecureAccessControlProfile {
|
||||
/**
|
||||
* id is a numeric identifier that must be unique within the context of a Credential and may be
|
||||
* used to reference the profile.
|
||||
*/
|
||||
int id;
|
||||
|
||||
/**
|
||||
* readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
|
||||
* of certificates) that must be used to authenticate requests. For details about how
|
||||
* this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
|
||||
*/
|
||||
Certificate readerCertificate;
|
||||
|
||||
/**
|
||||
* if true, the user is required to authenticate to allow requests. Required authentication
|
||||
* fressness is specified by timeout below.
|
||||
*
|
||||
*/
|
||||
boolean userAuthenticationRequired;
|
||||
|
||||
/**
|
||||
* Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
|
||||
* above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired
|
||||
* is true and timout is zero then authentication is required for each reader session.
|
||||
*
|
||||
* If userAuthenticationRequired is false, timeout must be zero.
|
||||
*/
|
||||
long timeoutMillis;
|
||||
|
||||
/**
|
||||
* secureUserId must be non-zero if userAuthenticationRequired is true.
|
||||
* It is not related to any Android user ID or UID, but is created in the
|
||||
* Gatekeeper application in the secure environment.
|
||||
*/
|
||||
long secureUserId;
|
||||
|
||||
/**
|
||||
* The mac is used to authenticate the access control profile. It contains:
|
||||
*
|
||||
* AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
|
||||
*
|
||||
* where AccessControlProfile is the CBOR map:
|
||||
*
|
||||
* AccessControlProfile = {
|
||||
* "id": uint,
|
||||
* ? "readerCertificate" : bstr,
|
||||
* ? (
|
||||
* "userAuthenticationRequired" : bool,
|
||||
* "timeoutMillis" : uint,
|
||||
* "secureUserId" : uint
|
||||
* )
|
||||
* }
|
||||
*/
|
||||
byte[] mac;
|
||||
}
|
||||
|
||||
29
identity/aidl/default/Android.bp
Normal file
29
identity/aidl/default/Android.bp
Normal file
@@ -0,0 +1,29 @@
|
||||
cc_binary {
|
||||
name: "android.hardware.identity-service.example",
|
||||
relative_install_path: "hw",
|
||||
init_rc: ["identity-default.rc"],
|
||||
vintf_fragments: ["identity-default.xml"],
|
||||
vendor: true,
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libcppbor",
|
||||
"libcrypto",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"android.hardware.identity-support-lib",
|
||||
"android.hardware.identity-ndk_platform",
|
||||
"android.hardware.keymaster-ndk_platform",
|
||||
],
|
||||
srcs: [
|
||||
"IdentityCredential.cpp",
|
||||
"IdentityCredentialStore.cpp",
|
||||
"WritableIdentityCredential.cpp",
|
||||
"Util.cpp",
|
||||
"service.cpp",
|
||||
],
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "IdentityCredential.h"
|
||||
#include "IdentityCredentialStore.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
@@ -28,71 +29,24 @@
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
namespace implementation {
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::android::hardware::keymaster::V4_0::Timestamp;
|
||||
using ::aidl::android::hardware::keymaster::Timestamp;
|
||||
using ::std::optional;
|
||||
|
||||
Return<void> IdentityCredential::deleteCredential(deleteCredential_cb _hidl_cb) {
|
||||
cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
|
||||
vector<uint8_t> proofOfDeletion = array.encode();
|
||||
using namespace ::android::hardware::identity;
|
||||
|
||||
optional<vector<uint8_t>> proofOfDeletionSignature =
|
||||
support::coseSignEcDsa(credentialPrivKey_,
|
||||
proofOfDeletion, // payload
|
||||
{}, // additionalData
|
||||
{}); // certificateChain
|
||||
if (!proofOfDeletionSignature) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {});
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(support::resultOK(), proofOfDeletionSignature.value());
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> IdentityCredential::createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) {
|
||||
optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
|
||||
if (!keyPair) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error creating ephemeral key pair"), {});
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Stash public key of this key-pair for later check in startRetrieval().
|
||||
optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
|
||||
if (!publicKey) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED,
|
||||
"Error getting public part of ephemeral key pair"),
|
||||
{});
|
||||
return Void();
|
||||
}
|
||||
ephemeralPublicKey_ = publicKey.value();
|
||||
|
||||
_hidl_cb(support::resultOK(), keyPair.value());
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> IdentityCredential::setReaderEphemeralPublicKey(
|
||||
const hidl_vec<uint8_t>& publicKey, setReaderEphemeralPublicKey_cb _hidl_cb) {
|
||||
readerPublicKey_ = publicKey;
|
||||
_hidl_cb(support::resultOK());
|
||||
return Void();
|
||||
}
|
||||
|
||||
ResultCode IdentityCredential::initialize() {
|
||||
int IdentityCredential::initialize() {
|
||||
auto [item, _, message] = cppbor::parse(credentialData_);
|
||||
if (item == nullptr) {
|
||||
LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
|
||||
return ResultCode::INVALID_DATA;
|
||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
||||
}
|
||||
|
||||
const cppbor::Array* arrayItem = item->asArray();
|
||||
if (arrayItem == nullptr || arrayItem->size() != 3) {
|
||||
LOG(ERROR) << "CredentialData is not an array with three elements";
|
||||
return ResultCode::INVALID_DATA;
|
||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
||||
}
|
||||
|
||||
const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
|
||||
@@ -103,7 +57,7 @@ ResultCode IdentityCredential::initialize() {
|
||||
if (docTypeItem == nullptr || testCredentialItem == nullptr ||
|
||||
encryptedCredentialKeysItem == nullptr) {
|
||||
LOG(ERROR) << "CredentialData unexpected item types";
|
||||
return ResultCode::INVALID_DATA;
|
||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
||||
}
|
||||
|
||||
docType_ = docTypeItem->value();
|
||||
@@ -113,7 +67,7 @@ ResultCode IdentityCredential::initialize() {
|
||||
if (testCredential_) {
|
||||
hardwareBoundKey = support::getTestHardwareBoundKey();
|
||||
} else {
|
||||
hardwareBoundKey = support::getHardwareBoundKey();
|
||||
hardwareBoundKey = getHardwareBoundKey();
|
||||
}
|
||||
|
||||
const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
|
||||
@@ -122,39 +76,83 @@ ResultCode IdentityCredential::initialize() {
|
||||
support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
|
||||
if (!decryptedCredentialKeys) {
|
||||
LOG(ERROR) << "Error decrypting CredentialKeys";
|
||||
return ResultCode::INVALID_DATA;
|
||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
||||
}
|
||||
|
||||
auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
|
||||
if (dckItem == nullptr) {
|
||||
LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
|
||||
return ResultCode::INVALID_DATA;
|
||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
||||
}
|
||||
const cppbor::Array* dckArrayItem = dckItem->asArray();
|
||||
if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
|
||||
LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
|
||||
return ResultCode::INVALID_DATA;
|
||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
||||
}
|
||||
const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
|
||||
const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
|
||||
if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
|
||||
LOG(ERROR) << "CredentialKeys unexpected item types";
|
||||
return ResultCode::INVALID_DATA;
|
||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
||||
}
|
||||
storageKey_ = storageKeyItem->value();
|
||||
credentialPrivKey_ = credentialPrivKeyItem->value();
|
||||
|
||||
return ResultCode::OK;
|
||||
return IIdentityCredentialStore::STATUS_OK;
|
||||
}
|
||||
|
||||
Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hidl_cb) {
|
||||
ndk::ScopedAStatus IdentityCredential::deleteCredential(
|
||||
vector<int8_t>* outProofOfDeletionSignature) {
|
||||
cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
|
||||
vector<uint8_t> proofOfDeletion = array.encode();
|
||||
|
||||
optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
|
||||
proofOfDeletion, // payload
|
||||
{}, // additionalData
|
||||
{}); // certificateChain
|
||||
if (!signature) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
|
||||
}
|
||||
|
||||
*outProofOfDeletionSignature = byteStringToSigned(signature.value());
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<int8_t>* outKeyPair) {
|
||||
optional<vector<uint8_t>> kp = support::createEcKeyPair();
|
||||
if (!kp) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
|
||||
}
|
||||
|
||||
// Stash public key of this key-pair for later check in startRetrieval().
|
||||
optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
|
||||
if (!publicKey) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error getting public part of ephemeral key pair"));
|
||||
}
|
||||
ephemeralPublicKey_ = publicKey.value();
|
||||
|
||||
*outKeyPair = byteStringToSigned(kp.value());
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
|
||||
const vector<int8_t>& publicKey) {
|
||||
readerPublicKey_ = byteStringToUnsigned(publicKey);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
|
||||
uint64_t challenge = 0;
|
||||
while (challenge == 0) {
|
||||
optional<vector<uint8_t>> bytes = support::getRandom(8);
|
||||
if (!bytes) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error getting random data for challenge"),
|
||||
0);
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error getting random data for challenge"));
|
||||
}
|
||||
|
||||
challenge = 0;
|
||||
@@ -163,17 +161,16 @@ Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hid
|
||||
}
|
||||
}
|
||||
|
||||
authChallenge_ = challenge;
|
||||
_hidl_cb(support::resultOK(), challenge);
|
||||
return Void();
|
||||
*outChallenge = challenge;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
|
||||
// ahead of time.
|
||||
bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
|
||||
const vector<uint8_t>& readerCertificateChain) {
|
||||
optional<vector<uint8_t>> acpPubKey =
|
||||
support::certificateChainGetTopMostKey(profile.readerCertificate);
|
||||
optional<vector<uint8_t>> acpPubKey = support::certificateChainGetTopMostKey(
|
||||
byteStringToUnsigned(profile.readerCertificate.encodedCertificate));
|
||||
if (!acpPubKey) {
|
||||
LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
|
||||
return false;
|
||||
@@ -202,7 +199,9 @@ bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
|
||||
Timestamp clockGetTime() {
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
||||
return time.tv_sec * 1000 + time.tv_nsec / 1000000;
|
||||
Timestamp ts;
|
||||
ts.milliSeconds = time.tv_sec * 1000 + time.tv_nsec / 1000000;
|
||||
return ts;
|
||||
}
|
||||
|
||||
bool checkUserAuthentication(const SecureAccessControlProfile& profile,
|
||||
@@ -219,7 +218,7 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authToken.challenge != authChallenge) {
|
||||
if (authToken.challenge != int64_t(authChallenge)) {
|
||||
LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
|
||||
return false;
|
||||
}
|
||||
@@ -239,42 +238,44 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile,
|
||||
// implementation should never be used on a real device.
|
||||
//
|
||||
Timestamp now = clockGetTime();
|
||||
if (authToken.timestamp > now) {
|
||||
LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp
|
||||
<< ") is in the future (now: " << now << ")";
|
||||
if (authToken.timestamp.milliSeconds > now.milliSeconds) {
|
||||
LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
|
||||
<< ") is in the future (now: " << now.milliSeconds << ")";
|
||||
return false;
|
||||
}
|
||||
if (now > authToken.timestamp + profile.timeoutMillis) {
|
||||
LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp << " + "
|
||||
if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
|
||||
LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
|
||||
<< profile.timeoutMillis << " = "
|
||||
<< (authToken.timestamp + profile.timeoutMillis)
|
||||
<< ") is in the past (now: " << now << ")";
|
||||
<< (authToken.timestamp.milliSeconds + profile.timeoutMillis)
|
||||
<< ") is in the past (now: " << now.milliSeconds << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Return<void> IdentityCredential::startRetrieval(
|
||||
const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
|
||||
const HardwareAuthToken& authToken, const hidl_vec<uint8_t>& itemsRequest,
|
||||
const hidl_vec<uint8_t>& sessionTranscript, const hidl_vec<uint8_t>& readerSignature,
|
||||
const hidl_vec<uint16_t>& requestCounts, startRetrieval_cb _hidl_cb) {
|
||||
ndk::ScopedAStatus IdentityCredential::startRetrieval(
|
||||
const vector<SecureAccessControlProfile>& accessControlProfiles,
|
||||
const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS,
|
||||
const vector<int8_t>& sessionTranscriptS, const vector<int8_t>& readerSignatureS,
|
||||
const vector<int32_t>& requestCounts) {
|
||||
auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS);
|
||||
auto itemsRequest = byteStringToUnsigned(itemsRequestS);
|
||||
auto readerSignature = byteStringToUnsigned(readerSignatureS);
|
||||
|
||||
if (sessionTranscript.size() > 0) {
|
||||
auto [item, _, message] = cppbor::parse(sessionTranscript);
|
||||
if (item == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"SessionTranscript contains invalid CBOR"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"SessionTranscript contains invalid CBOR"));
|
||||
}
|
||||
sessionTranscriptItem_ = std::move(item);
|
||||
}
|
||||
if (numStartRetrievalCalls_ > 0) {
|
||||
if (sessionTranscript_ != vector<uint8_t>(sessionTranscript)) {
|
||||
_hidl_cb(support::result(
|
||||
ResultCode::SESSION_TRANSCRIPT_MISMATCH,
|
||||
if (sessionTranscript_ != sessionTranscript) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
|
||||
"Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
|
||||
return Void();
|
||||
}
|
||||
}
|
||||
sessionTranscript_ = sessionTranscript;
|
||||
@@ -285,23 +286,23 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
if (readerSignature.size() > 0) {
|
||||
readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
|
||||
if (!readerCertificateChain) {
|
||||
_hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
|
||||
"Unable to get reader certificate chain from COSE_Sign1"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
|
||||
"Unable to get reader certificate chain from COSE_Sign1"));
|
||||
}
|
||||
|
||||
if (!support::certificateChainValidate(readerCertificateChain.value())) {
|
||||
_hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
|
||||
"Error validating reader certificate chain"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
|
||||
"Error validating reader certificate chain"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> readerPublicKey =
|
||||
support::certificateChainGetTopMostKey(readerCertificateChain.value());
|
||||
if (!readerPublicKey) {
|
||||
_hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
|
||||
"Unable to get public key from reader certificate chain"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
|
||||
"Unable to get public key from reader certificate chain"));
|
||||
}
|
||||
|
||||
const vector<uint8_t>& itemsRequestBytes = itemsRequest;
|
||||
@@ -313,9 +314,9 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
if (!support::coseCheckEcDsaSignature(readerSignature,
|
||||
dataThatWasSigned, // detached content
|
||||
readerPublicKey.value())) {
|
||||
_hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
|
||||
"readerSignature check failed"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
|
||||
"readerSignature check failed"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,39 +335,39 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
if (sessionTranscript.size() > 0) {
|
||||
const cppbor::Array* array = sessionTranscriptItem_->asArray();
|
||||
if (array == nullptr || array->size() != 2) {
|
||||
_hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"SessionTranscript is not an array with two items"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"SessionTranscript is not an array with two items"));
|
||||
}
|
||||
const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
|
||||
if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
|
||||
_hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"First item in SessionTranscript array is not a "
|
||||
"semantic with value 24"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"First item in SessionTranscript array is not a "
|
||||
"semantic with value 24"));
|
||||
}
|
||||
const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
|
||||
if (encodedDE == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Child of semantic in first item in SessionTranscript "
|
||||
"array is not a bstr"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Child of semantic in first item in SessionTranscript "
|
||||
"array is not a bstr"));
|
||||
}
|
||||
const vector<uint8_t>& bytesDE = encodedDE->value();
|
||||
|
||||
auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
|
||||
if (!getXYSuccess) {
|
||||
_hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Error extracting X and Y from ePub"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Error extracting X and Y from ePub"));
|
||||
}
|
||||
if (sessionTranscript.size() > 0 &&
|
||||
!(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
|
||||
memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
|
||||
_hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Did not find ephemeral public key's X and Y coordinates in "
|
||||
"SessionTranscript (make sure leading zeroes are not used)"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
||||
"Did not find ephemeral public key's X and Y coordinates in "
|
||||
"SessionTranscript (make sure leading zeroes are not used)"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,17 +379,17 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
// 1. The content must be a CBOR-encoded structure.
|
||||
auto [item, _, message] = cppbor::parse(itemsRequest);
|
||||
if (item == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"Error decoding CBOR in itemsRequest: %s", message.c_str()));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"Error decoding CBOR in itemsRequest"));
|
||||
}
|
||||
|
||||
// 2. The CBOR structure must be a map.
|
||||
const cppbor::Map* map = item->asMap();
|
||||
if (map == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"itemsRequest is not a CBOR map"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"itemsRequest is not a CBOR map"));
|
||||
}
|
||||
|
||||
// 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
|
||||
@@ -435,9 +436,9 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
}
|
||||
}
|
||||
if (nsMap == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"No nameSpaces map in top-most map"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"No nameSpaces map in top-most map"));
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < nsMap->size(); n++) {
|
||||
@@ -445,9 +446,9 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
|
||||
const cppbor::Map* nsInnerMap = nsValueItem->asMap();
|
||||
if (nsKey == nullptr || nsInnerMap == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"Type mismatch in nameSpaces map"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"Type mismatch in nameSpaces map"));
|
||||
}
|
||||
string requestedNamespace = nsKey->value();
|
||||
vector<string> requestedKeys;
|
||||
@@ -458,9 +459,9 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
const cppbor::Bool* intentToRetainItem =
|
||||
(simple != nullptr) ? simple->asBool() : nullptr;
|
||||
if (nameItem == nullptr || intentToRetainItem == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"Type mismatch in value in nameSpaces map"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
|
||||
"Type mismatch in value in nameSpaces map"));
|
||||
}
|
||||
requestedKeys.push_back(nameItem->value());
|
||||
}
|
||||
@@ -471,20 +472,20 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
// Finally, validate all the access control profiles in the requestData.
|
||||
bool haveAuthToken = (authToken.mac.size() > 0);
|
||||
for (const auto& profile : accessControlProfiles) {
|
||||
if (!support::secureAccessControlProfileCheckMac(profile, storageKey_)) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"Error checking MAC for profile with id %d", int(profile.id)));
|
||||
return Void();
|
||||
if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Error checking MAC for profile"));
|
||||
}
|
||||
ResultCode accessControlCheck = ResultCode::OK;
|
||||
int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
|
||||
if (profile.userAuthenticationRequired) {
|
||||
if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
|
||||
accessControlCheck = ResultCode::USER_AUTHENTICATION_FAILED;
|
||||
accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
|
||||
}
|
||||
} else if (profile.readerCertificate.size() > 0) {
|
||||
} else if (profile.readerCertificate.encodedCertificate.size() > 0) {
|
||||
if (!readerCertificateChain ||
|
||||
!checkReaderAuthentication(profile, readerCertificateChain.value())) {
|
||||
accessControlCheck = ResultCode::READER_AUTHENTICATION_FAILED;
|
||||
accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
|
||||
}
|
||||
}
|
||||
profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
|
||||
@@ -499,26 +500,25 @@ Return<void> IdentityCredential::startRetrieval(
|
||||
itemsRequest_ = itemsRequest;
|
||||
|
||||
numStartRetrievalCalls_ += 1;
|
||||
_hidl_cb(support::resultOK());
|
||||
return Void();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
Return<void> IdentityCredential::startRetrieveEntryValue(
|
||||
const hidl_string& nameSpace, const hidl_string& name, uint32_t entrySize,
|
||||
const hidl_vec<uint16_t>& accessControlProfileIds, startRetrieveEntryValue_cb _hidl_cb) {
|
||||
ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
|
||||
const string& nameSpace, const string& name, int32_t entrySize,
|
||||
const vector<int32_t>& accessControlProfileIds) {
|
||||
if (name.empty()) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA, "Name cannot be empty"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
|
||||
}
|
||||
if (nameSpace.empty()) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA, "Name space cannot be empty"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
|
||||
}
|
||||
|
||||
if (requestCountsRemaining_.size() == 0) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"No more name spaces left to go through"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"No more name spaces left to go through"));
|
||||
}
|
||||
|
||||
if (currentNameSpace_ == "") {
|
||||
@@ -529,19 +529,18 @@ Return<void> IdentityCredential::startRetrieveEntryValue(
|
||||
if (nameSpace == currentNameSpace_) {
|
||||
// Same namespace.
|
||||
if (requestCountsRemaining_[0] == 0) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"No more entries to be retrieved in current name space"));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"No more entries to be retrieved in current name space"));
|
||||
}
|
||||
requestCountsRemaining_[0] -= 1;
|
||||
} else {
|
||||
// New namespace.
|
||||
if (requestCountsRemaining_[0] != 0) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"Moved to new name space but %d entries need to be retrieved "
|
||||
"in current name space",
|
||||
int(requestCountsRemaining_[0])));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Moved to new name space but one or more entries need to be retrieved "
|
||||
"in current name space"));
|
||||
}
|
||||
if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
|
||||
deviceNameSpacesMap_.add(currentNameSpace_,
|
||||
@@ -558,18 +557,15 @@ Return<void> IdentityCredential::startRetrieveEntryValue(
|
||||
if (itemsRequest_.size() > 0) {
|
||||
const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
|
||||
if (it == requestedNameSpacesAndNames_.end()) {
|
||||
_hidl_cb(support::result(ResultCode::NOT_IN_REQUEST_MESSAGE,
|
||||
"Name space '%s' was not requested in startRetrieval",
|
||||
nameSpace.c_str()));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
|
||||
"Name space was not requested in startRetrieval"));
|
||||
}
|
||||
const auto& dataItemNames = it->second;
|
||||
if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
|
||||
_hidl_cb(support::result(
|
||||
ResultCode::NOT_IN_REQUEST_MESSAGE,
|
||||
"Data item name '%s' in name space '%s' was not requested in startRetrieval",
|
||||
name.c_str(), nameSpace.c_str()));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
|
||||
"Data item name in name space was not requested in startRetrieval"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,44 +575,44 @@ Return<void> IdentityCredential::startRetrieveEntryValue(
|
||||
//
|
||||
// If an item is configured without any profiles, access is denied.
|
||||
//
|
||||
ResultCode accessControl = ResultCode::NO_ACCESS_CONTROL_PROFILES;
|
||||
int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
|
||||
for (auto id : accessControlProfileIds) {
|
||||
auto search = profileIdToAccessCheckResult_.find(id);
|
||||
if (search == profileIdToAccessCheckResult_.end()) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"Requested entry with unvalidated profile id %d", (int(id))));
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Requested entry with unvalidated profile id"));
|
||||
}
|
||||
ResultCode accessControlForProfile = search->second;
|
||||
if (accessControlForProfile == ResultCode::OK) {
|
||||
accessControl = ResultCode::OK;
|
||||
int accessControlForProfile = search->second;
|
||||
if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
|
||||
accessControl = IIdentityCredentialStore::STATUS_OK;
|
||||
break;
|
||||
}
|
||||
accessControl = accessControlForProfile;
|
||||
}
|
||||
if (accessControl != ResultCode::OK) {
|
||||
_hidl_cb(support::result(accessControl, "Access control check failed"));
|
||||
return Void();
|
||||
if (accessControl != IIdentityCredentialStore::STATUS_OK) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
int(accessControl), "Access control check failed"));
|
||||
}
|
||||
|
||||
entryAdditionalData_ =
|
||||
support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
|
||||
entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
|
||||
|
||||
currentName_ = name;
|
||||
entryRemainingBytes_ = entrySize;
|
||||
entryValue_.resize(0);
|
||||
|
||||
_hidl_cb(support::resultOK());
|
||||
return Void();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
|
||||
retrieveEntryValue_cb _hidl_cb) {
|
||||
ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>& encryptedContentS,
|
||||
vector<int8_t>* outContent) {
|
||||
auto encryptedContent = byteStringToUnsigned(encryptedContentS);
|
||||
|
||||
optional<vector<uint8_t>> content =
|
||||
support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
|
||||
if (!content) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting data"), {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
|
||||
}
|
||||
|
||||
size_t chunkSize = content.value().size();
|
||||
@@ -624,23 +620,17 @@ Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& enc
|
||||
if (chunkSize > entryRemainingBytes_) {
|
||||
LOG(ERROR) << "Retrieved chunk of size " << chunkSize
|
||||
<< " is bigger than remaining space of size " << entryRemainingBytes_;
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"Retrieved chunk of size %zd is bigger than remaining space "
|
||||
"of size %zd",
|
||||
chunkSize, entryRemainingBytes_),
|
||||
{});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Retrieved chunk is bigger than remaining space"));
|
||||
}
|
||||
|
||||
entryRemainingBytes_ -= chunkSize;
|
||||
if (entryRemainingBytes_ > 0) {
|
||||
if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"Retrieved non-final chunk of size %zd but expected "
|
||||
"kGcmChunkSize which is %zd",
|
||||
chunkSize, IdentityCredentialStore::kGcmChunkSize),
|
||||
{});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Retrieved non-final chunk of size which isn't kGcmChunkSize"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,18 +639,22 @@ Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& enc
|
||||
if (entryRemainingBytes_ == 0) {
|
||||
auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
|
||||
if (entryValueItem == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA, "Retrieved data invalid CBOR"), {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Retrieved data which is invalid CBOR"));
|
||||
}
|
||||
currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
|
||||
}
|
||||
|
||||
_hidl_cb(support::resultOK(), content.value());
|
||||
return Void();
|
||||
*outContent = byteStringToSigned(content.value());
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
|
||||
finishRetrieval_cb _hidl_cb) {
|
||||
ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector<int8_t>& signingKeyBlobS,
|
||||
vector<int8_t>* outMac,
|
||||
vector<int8_t>* outDeviceNameSpaces) {
|
||||
auto signingKeyBlob = byteStringToUnsigned(signingKeyBlobS);
|
||||
|
||||
if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
|
||||
deviceNameSpacesMap_.add(currentNameSpace_,
|
||||
std::move(currentNameSpaceDeviceNameSpacesMap_));
|
||||
@@ -682,40 +676,42 @@ Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signin
|
||||
optional<vector<uint8_t>> signingKey =
|
||||
support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
|
||||
if (!signingKey) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting signingKeyBlob"),
|
||||
{}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Error decrypting signingKeyBlob"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> sharedSecret =
|
||||
support::ecdh(readerPublicKey_, signingKey.value());
|
||||
if (!sharedSecret) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error doing ECDH"), {}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH"));
|
||||
}
|
||||
|
||||
vector<uint8_t> salt = {0x00};
|
||||
vector<uint8_t> info = {};
|
||||
optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
|
||||
if (!derivedKey) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error deriving key from shared secret"),
|
||||
{}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error deriving key from shared secret"));
|
||||
}
|
||||
|
||||
mac = support::coseMac0(derivedKey.value(), {}, // payload
|
||||
encodedDeviceAuthentication); // additionalData
|
||||
if (!mac) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error MACing data"), {}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
|
||||
}
|
||||
}
|
||||
|
||||
_hidl_cb(support::resultOK(), mac.value_or(vector<uint8_t>({})), encodedDeviceNameSpaces);
|
||||
return Void();
|
||||
*outMac = byteStringToSigned(mac.value_or(vector<uint8_t>({})));
|
||||
*outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) {
|
||||
ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
|
||||
vector<int8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
|
||||
string serialDecimal = "0"; // TODO: set serial to something unique
|
||||
string issuer = "Android Open Source Project";
|
||||
string subject = "Android IdentityCredential Reference Implementation";
|
||||
@@ -724,50 +720,49 @@ Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_c
|
||||
|
||||
optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
|
||||
if (!signingKeyPKCS8) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> signingPublicKey =
|
||||
support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
|
||||
if (!signingPublicKey) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of signingKey"), {},
|
||||
{});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error getting public part of signingKey"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
|
||||
if (!signingKey) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error getting private part of signingKey"),
|
||||
{}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error getting private part of signingKey"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
|
||||
signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
|
||||
validityNotBefore, validityNotAfter);
|
||||
if (!certificate) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> nonce = support::getRandom(12);
|
||||
if (!nonce) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error getting random"), {}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
|
||||
}
|
||||
vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
|
||||
optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
|
||||
storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
|
||||
if (!encryptedSigningKey) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error encrypting signingKey"), {}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
|
||||
}
|
||||
_hidl_cb(support::resultOK(), encryptedSigningKey.value(), certificate.value());
|
||||
return Void();
|
||||
*outSigningKeyBlob = byteStringToSigned(encryptedSigningKey.value());
|
||||
*outSigningKeyCertificate = Certificate();
|
||||
outSigningKeyCertificate->encodedCertificate = byteStringToSigned(certificate.value());
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl::android::hardware::identity
|
||||
109
identity/aidl/default/IdentityCredential.h
Normal file
109
identity/aidl/default/IdentityCredential.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2019, 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 ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
|
||||
#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
|
||||
|
||||
#include <aidl/android/hardware/identity/BnIdentityCredential.h>
|
||||
#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cppbor/cppbor.h>
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::aidl::android::hardware::keymaster::HardwareAuthToken;
|
||||
using ::std::map;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
using MapStringToVectorOfStrings = map<string, vector<string>>;
|
||||
|
||||
class IdentityCredential : public BnIdentityCredential {
|
||||
public:
|
||||
IdentityCredential(const vector<uint8_t>& credentialData)
|
||||
: credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
|
||||
|
||||
// Parses and decrypts credentialData_, return a status code from
|
||||
// IIdentityCredentialStore. Must be called right after construction.
|
||||
int initialize();
|
||||
|
||||
// Methods from IIdentityCredential follow.
|
||||
ndk::ScopedAStatus deleteCredential(vector<int8_t>* outProofOfDeletionSignature) override;
|
||||
ndk::ScopedAStatus createEphemeralKeyPair(vector<int8_t>* outKeyPair) override;
|
||||
ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<int8_t>& publicKey) override;
|
||||
ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
|
||||
ndk::ScopedAStatus startRetrieval(
|
||||
const vector<SecureAccessControlProfile>& accessControlProfiles,
|
||||
const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequest,
|
||||
const vector<int8_t>& sessionTranscript, const vector<int8_t>& readerSignature,
|
||||
const vector<int32_t>& requestCounts) override;
|
||||
ndk::ScopedAStatus startRetrieveEntryValue(
|
||||
const string& nameSpace, const string& name, int32_t entrySize,
|
||||
const vector<int32_t>& accessControlProfileIds) override;
|
||||
ndk::ScopedAStatus retrieveEntryValue(const vector<int8_t>& encryptedContent,
|
||||
vector<int8_t>* outContent) override;
|
||||
ndk::ScopedAStatus finishRetrieval(const vector<int8_t>& signingKeyBlob, vector<int8_t>* outMac,
|
||||
vector<int8_t>* outDeviceNameSpaces) override;
|
||||
ndk::ScopedAStatus generateSigningKeyPair(vector<int8_t>* outSigningKeyBlob,
|
||||
Certificate* outSigningKeyCertificate) override;
|
||||
|
||||
private:
|
||||
// Set by constructor
|
||||
vector<uint8_t> credentialData_;
|
||||
int numStartRetrievalCalls_;
|
||||
|
||||
// Set by initialize()
|
||||
string docType_;
|
||||
bool testCredential_;
|
||||
vector<uint8_t> storageKey_;
|
||||
vector<uint8_t> credentialPrivKey_;
|
||||
|
||||
// Set by createEphemeralKeyPair()
|
||||
vector<uint8_t> ephemeralPublicKey_;
|
||||
|
||||
// Set by setReaderEphemeralPublicKey()
|
||||
vector<uint8_t> readerPublicKey_;
|
||||
|
||||
// Set by createAuthChallenge()
|
||||
uint64_t authChallenge_;
|
||||
|
||||
// Set at startRetrieval() time.
|
||||
map<int32_t, int> profileIdToAccessCheckResult_;
|
||||
vector<uint8_t> sessionTranscript_;
|
||||
std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
|
||||
vector<uint8_t> itemsRequest_;
|
||||
vector<int32_t> requestCountsRemaining_;
|
||||
MapStringToVectorOfStrings requestedNameSpacesAndNames_;
|
||||
cppbor::Map deviceNameSpacesMap_;
|
||||
cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
|
||||
|
||||
// Set at startRetrieveEntryValue() time.
|
||||
string currentNameSpace_;
|
||||
string currentName_;
|
||||
size_t entryRemainingBytes_;
|
||||
vector<uint8_t> entryValue_;
|
||||
vector<uint8_t> entryAdditionalData_;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
|
||||
74
identity/aidl/default/IdentityCredentialStore.cpp
Normal file
74
identity/aidl/default/IdentityCredentialStore.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2019, 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 "IdentityCredentialStore"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "IdentityCredential.h"
|
||||
#include "IdentityCredentialStore.h"
|
||||
#include "WritableIdentityCredential.h"
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation(
|
||||
HardwareInformation* hardwareInformation) {
|
||||
HardwareInformation hw;
|
||||
hw.credentialStoreName = "Identity Credential Reference Implementation";
|
||||
hw.credentialStoreAuthorName = "Google";
|
||||
hw.dataChunkSize = kGcmChunkSize;
|
||||
hw.isDirectAccess = false;
|
||||
hw.supportedDocTypes = {};
|
||||
*hardwareInformation = hw;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredentialStore::createCredential(
|
||||
const string& docType, bool testCredential,
|
||||
shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
|
||||
shared_ptr<WritableIdentityCredential> wc =
|
||||
ndk::SharedRefBase::make<WritableIdentityCredential>(docType, testCredential);
|
||||
if (!wc->initialize()) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error initializing WritableIdentityCredential"));
|
||||
}
|
||||
*outWritableCredential = wc;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredentialStore::getCredential(
|
||||
CipherSuite cipherSuite, const vector<int8_t>& credentialData,
|
||||
shared_ptr<IIdentityCredential>* outCredential) {
|
||||
// We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
|
||||
if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED,
|
||||
"Unsupported cipher suite"));
|
||||
}
|
||||
|
||||
vector<uint8_t> data = vector<uint8_t>(credentialData.begin(), credentialData.end());
|
||||
shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(data);
|
||||
auto ret = credential->initialize();
|
||||
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
int(ret), "Error initializing IdentityCredential"));
|
||||
}
|
||||
*outCredential = credential;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
48
identity/aidl/default/IdentityCredentialStore.h
Normal file
48
identity/aidl/default/IdentityCredentialStore.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2019, 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 ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
|
||||
#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
|
||||
|
||||
#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::std::shared_ptr;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
class IdentityCredentialStore : public BnIdentityCredentialStore {
|
||||
public:
|
||||
IdentityCredentialStore() {}
|
||||
|
||||
// The GCM chunk size used by this implementation is 64 KiB.
|
||||
static constexpr size_t kGcmChunkSize = 64 * 1024;
|
||||
|
||||
// Methods from IIdentityCredentialStore follow.
|
||||
ndk::ScopedAStatus getHardwareInformation(HardwareInformation* hardwareInformation) override;
|
||||
|
||||
ndk::ScopedAStatus createCredential(
|
||||
const string& docType, bool testCredential,
|
||||
shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
|
||||
|
||||
ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<int8_t>& credentialData,
|
||||
shared_ptr<IIdentityCredential>* outCredential) override;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
|
||||
117
identity/aidl/default/Util.cpp
Normal file
117
identity/aidl/default/Util.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2019, 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 "Util"
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using namespace ::android::hardware::identity;
|
||||
|
||||
// This is not a very random HBK but that's OK because this is the SW
|
||||
// implementation where it can't be kept secret.
|
||||
vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
|
||||
const vector<uint8_t>& getHardwareBoundKey() {
|
||||
return hardwareBoundKey;
|
||||
}
|
||||
|
||||
vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value) {
|
||||
return vector<uint8_t>(value.begin(), value.end());
|
||||
}
|
||||
|
||||
vector<int8_t> byteStringToSigned(const vector<uint8_t>& value) {
|
||||
return vector<int8_t>(value.begin(), value.end());
|
||||
}
|
||||
|
||||
vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
|
||||
cppbor::Map map;
|
||||
map.add("id", profile.id);
|
||||
|
||||
if (profile.readerCertificate.encodedCertificate.size() > 0) {
|
||||
map.add("readerCertificate",
|
||||
cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
|
||||
}
|
||||
|
||||
if (profile.userAuthenticationRequired) {
|
||||
map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
|
||||
map.add("timeoutMillis", profile.timeoutMillis);
|
||||
map.add("secureUserId", profile.secureUserId);
|
||||
}
|
||||
|
||||
return map.encode();
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
|
||||
const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
|
||||
vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
|
||||
|
||||
optional<vector<uint8_t>> nonce = support::getRandom(12);
|
||||
if (!nonce) {
|
||||
return {};
|
||||
}
|
||||
optional<vector<uint8_t>> macO =
|
||||
support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
|
||||
if (!macO) {
|
||||
return {};
|
||||
}
|
||||
return macO.value();
|
||||
}
|
||||
|
||||
bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
|
||||
const vector<uint8_t>& storageKey) {
|
||||
vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
|
||||
|
||||
if (profile.mac.size() < support::kAesGcmIvSize) {
|
||||
return false;
|
||||
}
|
||||
vector<uint8_t> nonce =
|
||||
vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize);
|
||||
optional<vector<uint8_t>> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData);
|
||||
if (!mac) {
|
||||
return false;
|
||||
}
|
||||
if (mac.value() != byteStringToUnsigned(profile.mac)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
|
||||
const vector<int32_t> accessControlProfileIds) {
|
||||
cppbor::Map map;
|
||||
map.add("Namespace", nameSpace);
|
||||
map.add("Name", name);
|
||||
|
||||
cppbor::Array acpIds;
|
||||
for (auto id : accessControlProfileIds) {
|
||||
acpIds.add(id);
|
||||
}
|
||||
map.add("AccessControlProfileIds", std::move(acpIds));
|
||||
return map.encode();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
58
identity/aidl/default/Util.h
Normal file
58
identity/aidl/default/Util.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2019, 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 ANDROID_HARDWARE_IDENTITY_UTIL_H
|
||||
#define ANDROID_HARDWARE_IDENTITY_UTIL_H
|
||||
|
||||
#include <aidl/android/hardware/identity/BnIdentityCredential.h>
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cppbor/cppbor.h>
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::std::optional;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
// Returns the hardware-bound AES-128 key.
|
||||
const vector<uint8_t>& getHardwareBoundKey();
|
||||
|
||||
// Calculates the MAC for |profile| using |storageKey|.
|
||||
optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
|
||||
const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
|
||||
|
||||
// Checks authenticity of the MAC in |profile| using |storageKey|.
|
||||
bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
|
||||
const vector<uint8_t>& storageKey);
|
||||
|
||||
// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
|
||||
vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
|
||||
const vector<int32_t> accessControlProfileIds);
|
||||
|
||||
vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value);
|
||||
|
||||
vector<int8_t> byteStringToSigned(const vector<uint8_t>& value);
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
#endif // ANDROID_HARDWARE_IDENTITY_UTIL_H
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
#define LOG_TAG "WritableIdentityCredential"
|
||||
|
||||
#include "WritableIdentityCredential.h"
|
||||
#include "IdentityCredentialStore.h"
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
@@ -26,12 +23,315 @@
|
||||
#include <cppbor/cppbor.h>
|
||||
#include <cppbor/cppbor_parse.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
namespace implementation {
|
||||
#include "IdentityCredentialStore.h"
|
||||
#include "Util.h"
|
||||
#include "WritableIdentityCredential.h"
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::std::optional;
|
||||
using namespace ::android::hardware::identity;
|
||||
|
||||
bool WritableIdentityCredential::initialize() {
|
||||
optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
|
||||
if (!keyPair) {
|
||||
LOG(ERROR) << "Error creating credentialKey";
|
||||
return false;
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
|
||||
if (!pubKey) {
|
||||
LOG(ERROR) << "Error getting public part of credentialKey";
|
||||
return false;
|
||||
}
|
||||
credentialPubKey_ = pubKey.value();
|
||||
|
||||
optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
|
||||
if (!privKey) {
|
||||
LOG(ERROR) << "Error getting private part of credentialKey";
|
||||
return false;
|
||||
}
|
||||
credentialPrivKey_ = privKey.value();
|
||||
|
||||
optional<vector<uint8_t>> random = support::getRandom(16);
|
||||
if (!random) {
|
||||
LOG(ERROR) << "Error creating storageKey";
|
||||
return false;
|
||||
}
|
||||
storageKey_ = random.value();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: use |attestationApplicationId| and |attestationChallenge| and also
|
||||
// ensure the returned certificate chain satisfy the requirements listed in
|
||||
// the docs for IWritableIdentityCredential::getAttestationCertificate()
|
||||
//
|
||||
ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
|
||||
const vector<int8_t>& /*attestationApplicationId*/,
|
||||
const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* outCertificateChain) {
|
||||
// For now, we dynamically generate an attestion key on each and every
|
||||
// request and use that to sign CredentialKey. In a real implementation this
|
||||
// would look very differently.
|
||||
optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
|
||||
if (!attestationKeyPair) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> attestationPubKey =
|
||||
support::ecKeyPairGetPublicKey(attestationKeyPair.value());
|
||||
if (!attestationPubKey) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error getting public part of attestationKey"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> attestationPrivKey =
|
||||
support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
|
||||
if (!attestationPrivKey) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error getting private part of attestationKey"));
|
||||
}
|
||||
|
||||
string serialDecimal;
|
||||
string issuer;
|
||||
string subject;
|
||||
time_t validityNotBefore = time(nullptr);
|
||||
time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
|
||||
|
||||
// First create a certificate for |credentialPubKey| which is signed by
|
||||
// |attestationPrivKey|.
|
||||
//
|
||||
serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
|
||||
issuer = "Android Open Source Project";
|
||||
subject = "Android IdentityCredential CredentialKey";
|
||||
optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
|
||||
credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
|
||||
validityNotBefore, validityNotAfter);
|
||||
if (!credentialPubKeyCertificate) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error creating certificate for credentialPubKey"));
|
||||
}
|
||||
|
||||
// This is followed by a certificate for |attestationPubKey| self-signed by
|
||||
// |attestationPrivKey|.
|
||||
serialDecimal = "0"; // TODO: set serial
|
||||
issuer = "Android Open Source Project";
|
||||
subject = "Android IdentityCredential AttestationKey";
|
||||
optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
|
||||
attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
|
||||
validityNotBefore, validityNotAfter);
|
||||
if (!attestationKeyCertificate) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error creating certificate for attestationPubKey"));
|
||||
}
|
||||
|
||||
// Concatenate the certificates to form the chain.
|
||||
vector<uint8_t> certificateChain;
|
||||
certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
|
||||
credentialPubKeyCertificate.value().end());
|
||||
certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
|
||||
attestationKeyCertificate.value().end());
|
||||
|
||||
optional<vector<vector<uint8_t>>> splitCertChain =
|
||||
support::certificateChainSplit(certificateChain);
|
||||
if (!splitCertChain) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain"));
|
||||
}
|
||||
*outCertificateChain = vector<Certificate>();
|
||||
for (const vector<uint8_t>& cert : splitCertChain.value()) {
|
||||
Certificate c = Certificate();
|
||||
c.encodedCertificate = byteStringToSigned(cert);
|
||||
outCertificateChain->push_back(std::move(c));
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
|
||||
int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
|
||||
numAccessControlProfileRemaining_ = accessControlProfileCount;
|
||||
remainingEntryCounts_ = entryCounts;
|
||||
entryNameSpace_ = "";
|
||||
|
||||
signedDataAccessControlProfiles_ = cppbor::Array();
|
||||
signedDataNamespaces_ = cppbor::Map();
|
||||
signedDataCurrentNamespace_ = cppbor::Array();
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
|
||||
int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
|
||||
int64_t timeoutMillis, int64_t secureUserId,
|
||||
SecureAccessControlProfile* outSecureAccessControlProfile) {
|
||||
SecureAccessControlProfile profile;
|
||||
|
||||
if (numAccessControlProfileRemaining_ == 0) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"numAccessControlProfileRemaining_ is 0 and expected non-zero"));
|
||||
}
|
||||
|
||||
// Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
|
||||
// be zero.
|
||||
if (!userAuthenticationRequired && timeoutMillis != 0) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"userAuthenticationRequired is false but timeout is non-zero"));
|
||||
}
|
||||
|
||||
profile.id = id;
|
||||
profile.readerCertificate = readerCertificate;
|
||||
profile.userAuthenticationRequired = userAuthenticationRequired;
|
||||
profile.timeoutMillis = timeoutMillis;
|
||||
profile.secureUserId = secureUserId;
|
||||
optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
|
||||
if (!mac) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
|
||||
}
|
||||
profile.mac = byteStringToSigned(mac.value());
|
||||
|
||||
cppbor::Map profileMap;
|
||||
profileMap.add("id", profile.id);
|
||||
if (profile.readerCertificate.encodedCertificate.size() > 0) {
|
||||
profileMap.add(
|
||||
"readerCertificate",
|
||||
cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
|
||||
}
|
||||
if (profile.userAuthenticationRequired) {
|
||||
profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
|
||||
profileMap.add("timeoutMillis", profile.timeoutMillis);
|
||||
}
|
||||
signedDataAccessControlProfiles_.add(std::move(profileMap));
|
||||
|
||||
numAccessControlProfileRemaining_--;
|
||||
|
||||
*outSecureAccessControlProfile = profile;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
|
||||
const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
|
||||
int32_t entrySize) {
|
||||
if (numAccessControlProfileRemaining_ != 0) {
|
||||
LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
|
||||
<< " and expected zero";
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"numAccessControlProfileRemaining_ is not zero"));
|
||||
}
|
||||
|
||||
if (remainingEntryCounts_.size() == 0) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
|
||||
}
|
||||
|
||||
// Handle initial beginEntry() call.
|
||||
if (entryNameSpace_ == "") {
|
||||
entryNameSpace_ = nameSpace;
|
||||
}
|
||||
|
||||
// If the namespace changed...
|
||||
if (nameSpace != entryNameSpace_) {
|
||||
// Then check that all entries in the previous namespace have been added..
|
||||
if (remainingEntryCounts_[0] != 0) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"New namespace but a non-zero number of entries remain to be added"));
|
||||
}
|
||||
remainingEntryCounts_.erase(remainingEntryCounts_.begin());
|
||||
|
||||
if (signedDataCurrentNamespace_.size() > 0) {
|
||||
signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
|
||||
signedDataCurrentNamespace_ = cppbor::Array();
|
||||
}
|
||||
} else {
|
||||
// Same namespace...
|
||||
if (remainingEntryCounts_[0] == 0) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Same namespace but no entries remain to be added"));
|
||||
}
|
||||
remainingEntryCounts_[0] -= 1;
|
||||
}
|
||||
|
||||
entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
|
||||
|
||||
entryRemainingBytes_ = entrySize;
|
||||
entryNameSpace_ = nameSpace;
|
||||
entryName_ = name;
|
||||
entryAccessControlProfileIds_ = accessControlProfileIds;
|
||||
entryBytes_.resize(0);
|
||||
// LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<int8_t>& contentS,
|
||||
vector<int8_t>* outEncryptedContent) {
|
||||
auto content = byteStringToUnsigned(contentS);
|
||||
size_t contentSize = content.size();
|
||||
|
||||
if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Passed in chunk of is bigger than kGcmChunkSize"));
|
||||
}
|
||||
if (contentSize > entryRemainingBytes_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Passed in chunk is bigger than remaining space"));
|
||||
}
|
||||
|
||||
entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
|
||||
entryRemainingBytes_ -= contentSize;
|
||||
if (entryRemainingBytes_ > 0) {
|
||||
if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA,
|
||||
"Retrieved non-final chunk which isn't kGcmChunkSize"));
|
||||
}
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> nonce = support::getRandom(12);
|
||||
if (!nonce) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
|
||||
}
|
||||
optional<vector<uint8_t>> encryptedContent =
|
||||
support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
|
||||
if (!encryptedContent) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
|
||||
}
|
||||
|
||||
if (entryRemainingBytes_ == 0) {
|
||||
// TODO: ideally do do this without parsing the data (but still validate data is valid
|
||||
// CBOR).
|
||||
auto [item, _, message] = cppbor::parse(entryBytes_);
|
||||
if (item == nullptr) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
|
||||
}
|
||||
cppbor::Map entryMap;
|
||||
entryMap.add("name", entryName_);
|
||||
entryMap.add("value", std::move(item));
|
||||
cppbor::Array profileIdArray;
|
||||
for (auto id : entryAccessControlProfileIds_) {
|
||||
profileIdArray.add(id);
|
||||
}
|
||||
entryMap.add("accessControlProfiles", std::move(profileIdArray));
|
||||
signedDataCurrentNamespace_.add(std::move(entryMap));
|
||||
}
|
||||
|
||||
*outEncryptedContent = byteStringToSigned(encryptedContent.value());
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
|
||||
// |credentialPrivKey|.
|
||||
@@ -77,325 +377,8 @@ bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const strin
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WritableIdentityCredential::initialize() {
|
||||
optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
|
||||
if (!keyPair) {
|
||||
LOG(ERROR) << "Error creating credentialKey";
|
||||
return false;
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
|
||||
if (!pubKey) {
|
||||
LOG(ERROR) << "Error getting public part of credentialKey";
|
||||
return false;
|
||||
}
|
||||
credentialPubKey_ = pubKey.value();
|
||||
|
||||
optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
|
||||
if (!privKey) {
|
||||
LOG(ERROR) << "Error getting private part of credentialKey";
|
||||
return false;
|
||||
}
|
||||
credentialPrivKey_ = privKey.value();
|
||||
|
||||
optional<vector<uint8_t>> random = support::getRandom(16);
|
||||
if (!random) {
|
||||
LOG(ERROR) << "Error creating storageKey";
|
||||
return false;
|
||||
}
|
||||
storageKey_ = random.value();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: use |attestationApplicationId| and |attestationChallenge| and also
|
||||
// ensure the returned certificate chain satisfy the requirements listed in
|
||||
// the docs for IWritableIdentityCredential::getAttestationCertificate()
|
||||
//
|
||||
Return<void> WritableIdentityCredential::getAttestationCertificate(
|
||||
const hidl_vec<uint8_t>& /* attestationApplicationId */,
|
||||
const hidl_vec<uint8_t>& /* attestationChallenge */,
|
||||
getAttestationCertificate_cb _hidl_cb) {
|
||||
// For now, we dynamically generate an attestion key on each and every
|
||||
// request and use that to sign CredentialKey. In a real implementation this
|
||||
// would look very differently.
|
||||
optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
|
||||
if (!attestationKeyPair) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error creating attestationKey"), {});
|
||||
return Void();
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> attestationPubKey =
|
||||
support::ecKeyPairGetPublicKey(attestationKeyPair.value());
|
||||
if (!attestationPubKey) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of attestationKey"),
|
||||
{});
|
||||
return Void();
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> attestationPrivKey =
|
||||
support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
|
||||
if (!attestationPrivKey) {
|
||||
_hidl_cb(
|
||||
support::result(ResultCode::FAILED, "Error getting private part of attestationKey"),
|
||||
{});
|
||||
return Void();
|
||||
}
|
||||
|
||||
string serialDecimal;
|
||||
string issuer;
|
||||
string subject;
|
||||
time_t validityNotBefore = time(nullptr);
|
||||
time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
|
||||
|
||||
// First create a certificate for |credentialPubKey| which is signed by
|
||||
// |attestationPrivKey|.
|
||||
//
|
||||
serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
|
||||
issuer = "Android Open Source Project";
|
||||
subject = "Android IdentityCredential CredentialKey";
|
||||
optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
|
||||
credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
|
||||
validityNotBefore, validityNotAfter);
|
||||
if (!credentialPubKeyCertificate) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED,
|
||||
"Error creating certificate for credentialPubKey"),
|
||||
{});
|
||||
return Void();
|
||||
}
|
||||
|
||||
// This is followed by a certificate for |attestationPubKey| self-signed by
|
||||
// |attestationPrivKey|.
|
||||
serialDecimal = "0"; // TODO: set serial
|
||||
issuer = "Android Open Source Project";
|
||||
subject = "Android IdentityCredential AttestationKey";
|
||||
optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
|
||||
attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
|
||||
validityNotBefore, validityNotAfter);
|
||||
if (!attestationKeyCertificate) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED,
|
||||
"Error creating certificate for attestationPubKey"),
|
||||
{});
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Concatenate the certificates to form the chain.
|
||||
vector<uint8_t> certificateChain;
|
||||
certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
|
||||
credentialPubKeyCertificate.value().end());
|
||||
certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
|
||||
attestationKeyCertificate.value().end());
|
||||
|
||||
optional<vector<vector<uint8_t>>> splitCertChain =
|
||||
support::certificateChainSplit(certificateChain);
|
||||
if (!splitCertChain) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error splitting certificate chain"), {});
|
||||
return Void();
|
||||
}
|
||||
hidl_vec<hidl_vec<uint8_t>> ret;
|
||||
ret.resize(splitCertChain.value().size());
|
||||
std::copy(splitCertChain.value().begin(), splitCertChain.value().end(), ret.begin());
|
||||
_hidl_cb(support::resultOK(), ret);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> WritableIdentityCredential::startPersonalization(uint16_t accessControlProfileCount,
|
||||
const hidl_vec<uint16_t>& entryCounts,
|
||||
startPersonalization_cb _hidl_cb) {
|
||||
numAccessControlProfileRemaining_ = accessControlProfileCount;
|
||||
remainingEntryCounts_ = entryCounts;
|
||||
entryNameSpace_ = "";
|
||||
|
||||
signedDataAccessControlProfiles_ = cppbor::Array();
|
||||
signedDataNamespaces_ = cppbor::Map();
|
||||
signedDataCurrentNamespace_ = cppbor::Array();
|
||||
|
||||
_hidl_cb(support::resultOK());
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> WritableIdentityCredential::addAccessControlProfile(
|
||||
uint16_t id, const hidl_vec<uint8_t>& readerCertificate, bool userAuthenticationRequired,
|
||||
uint64_t timeoutMillis, uint64_t secureUserId, addAccessControlProfile_cb _hidl_cb) {
|
||||
SecureAccessControlProfile profile;
|
||||
|
||||
if (numAccessControlProfileRemaining_ == 0) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"numAccessControlProfileRemaining_ is 0 and expected non-zero"),
|
||||
profile);
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
|
||||
// be zero.
|
||||
if (!userAuthenticationRequired && timeoutMillis != 0) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"userAuthenticationRequired is false but timeout is non-zero"),
|
||||
profile);
|
||||
return Void();
|
||||
}
|
||||
|
||||
profile.id = id;
|
||||
profile.readerCertificate = readerCertificate;
|
||||
profile.userAuthenticationRequired = userAuthenticationRequired;
|
||||
profile.timeoutMillis = timeoutMillis;
|
||||
profile.secureUserId = secureUserId;
|
||||
optional<vector<uint8_t>> mac =
|
||||
support::secureAccessControlProfileCalcMac(profile, storageKey_);
|
||||
if (!mac) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error calculating MAC for profile"), profile);
|
||||
return Void();
|
||||
}
|
||||
profile.mac = mac.value();
|
||||
|
||||
cppbor::Map profileMap;
|
||||
profileMap.add("id", profile.id);
|
||||
if (profile.readerCertificate.size() > 0) {
|
||||
profileMap.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
|
||||
}
|
||||
if (profile.userAuthenticationRequired) {
|
||||
profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
|
||||
profileMap.add("timeoutMillis", profile.timeoutMillis);
|
||||
}
|
||||
signedDataAccessControlProfiles_.add(std::move(profileMap));
|
||||
|
||||
numAccessControlProfileRemaining_--;
|
||||
|
||||
_hidl_cb(support::resultOK(), profile);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> WritableIdentityCredential::beginAddEntry(
|
||||
const hidl_vec<uint16_t>& accessControlProfileIds, const hidl_string& nameSpace,
|
||||
const hidl_string& name, uint32_t entrySize, beginAddEntry_cb _hidl_cb) {
|
||||
if (numAccessControlProfileRemaining_ != 0) {
|
||||
LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
|
||||
<< " and expected zero";
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"numAccessControlProfileRemaining_ is %zd and expected zero",
|
||||
numAccessControlProfileRemaining_));
|
||||
return Void();
|
||||
}
|
||||
|
||||
if (remainingEntryCounts_.size() == 0) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA, "No more namespaces to add to"));
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Handle initial beginEntry() call.
|
||||
if (entryNameSpace_ == "") {
|
||||
entryNameSpace_ = nameSpace;
|
||||
}
|
||||
|
||||
// If the namespace changed...
|
||||
if (nameSpace != entryNameSpace_) {
|
||||
// Then check that all entries in the previous namespace have been added..
|
||||
if (remainingEntryCounts_[0] != 0) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"New namespace but %d entries remain to be added",
|
||||
int(remainingEntryCounts_[0])));
|
||||
return Void();
|
||||
}
|
||||
remainingEntryCounts_.erase(remainingEntryCounts_.begin());
|
||||
|
||||
if (signedDataCurrentNamespace_.size() > 0) {
|
||||
signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
|
||||
signedDataCurrentNamespace_ = cppbor::Array();
|
||||
}
|
||||
} else {
|
||||
// Same namespace...
|
||||
if (remainingEntryCounts_[0] == 0) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"Same namespace but no entries remain to be added"));
|
||||
return Void();
|
||||
}
|
||||
remainingEntryCounts_[0] -= 1;
|
||||
}
|
||||
|
||||
entryAdditionalData_ =
|
||||
support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
|
||||
|
||||
entryRemainingBytes_ = entrySize;
|
||||
entryNameSpace_ = nameSpace;
|
||||
entryName_ = name;
|
||||
entryAccessControlProfileIds_ = accessControlProfileIds;
|
||||
entryBytes_.resize(0);
|
||||
// LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
|
||||
|
||||
_hidl_cb(support::resultOK());
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& content,
|
||||
addEntryValue_cb _hidl_cb) {
|
||||
size_t contentSize = content.size();
|
||||
|
||||
if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
|
||||
_hidl_cb(support::result(
|
||||
ResultCode::INVALID_DATA,
|
||||
"Passed in chunk of size %zd is bigger than kGcmChunkSize which is %zd",
|
||||
contentSize, IdentityCredentialStore::kGcmChunkSize),
|
||||
{});
|
||||
return Void();
|
||||
}
|
||||
if (contentSize > entryRemainingBytes_) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"Passed in chunk of size %zd is bigger than remaining space "
|
||||
"of size %zd",
|
||||
contentSize, entryRemainingBytes_),
|
||||
{});
|
||||
return Void();
|
||||
}
|
||||
|
||||
entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
|
||||
entryRemainingBytes_ -= contentSize;
|
||||
if (entryRemainingBytes_ > 0) {
|
||||
if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA,
|
||||
"Retrieved non-final chunk of size %zd but expected "
|
||||
"kGcmChunkSize which is %zd",
|
||||
contentSize, IdentityCredentialStore::kGcmChunkSize),
|
||||
{});
|
||||
return Void();
|
||||
}
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> nonce = support::getRandom(12);
|
||||
if (!nonce) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error getting nonce"), {});
|
||||
return Void();
|
||||
}
|
||||
optional<vector<uint8_t>> encryptedContent =
|
||||
support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
|
||||
if (!encryptedContent) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error encrypting content"), {});
|
||||
return Void();
|
||||
}
|
||||
|
||||
if (entryRemainingBytes_ == 0) {
|
||||
// TODO: ideally do do this without parsing the data (but still validate data is valid
|
||||
// CBOR).
|
||||
auto [item, _, message] = cppbor::parse(entryBytes_);
|
||||
if (item == nullptr) {
|
||||
_hidl_cb(support::result(ResultCode::INVALID_DATA, "Data is not valid CBOR"), {});
|
||||
return Void();
|
||||
}
|
||||
cppbor::Map entryMap;
|
||||
entryMap.add("name", entryName_);
|
||||
entryMap.add("value", std::move(item));
|
||||
cppbor::Array profileIdArray;
|
||||
for (auto id : entryAccessControlProfileIds_) {
|
||||
profileIdArray.add(id);
|
||||
}
|
||||
entryMap.add("accessControlProfiles", std::move(profileIdArray));
|
||||
signedDataCurrentNamespace_.add(std::move(entryMap));
|
||||
}
|
||||
|
||||
_hidl_cb(support::resultOK(), encryptedContent.value());
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries_cb _hidl_cb) {
|
||||
ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
|
||||
vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) {
|
||||
if (signedDataCurrentNamespace_.size() > 0) {
|
||||
signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
|
||||
}
|
||||
@@ -412,29 +395,27 @@ Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries
|
||||
{}, // additionalData
|
||||
{}); // certificateChain
|
||||
if (!signature) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
|
||||
}
|
||||
|
||||
vector<uint8_t> credentialKeys;
|
||||
if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialKeys"), {}, {});
|
||||
return Void();
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
|
||||
}
|
||||
|
||||
vector<uint8_t> credentialData;
|
||||
if (!generateCredentialData(testCredential_ ? support::getTestHardwareBoundKey()
|
||||
: support::getHardwareBoundKey(),
|
||||
docType_, testCredential_, credentialKeys, credentialData)) {
|
||||
_hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialData"), {}, {});
|
||||
return Void();
|
||||
if (!generateCredentialData(
|
||||
testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
|
||||
docType_, testCredential_, credentialKeys, credentialData)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
|
||||
}
|
||||
|
||||
_hidl_cb(support::resultOK(), credentialData, signature.value());
|
||||
return Void();
|
||||
*outCredentialData = byteStringToSigned(credentialData);
|
||||
*outProofOfProvisioningSignature = byteStringToSigned(signature.value());
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl::android::hardware::identity
|
||||
90
identity/aidl/default/WritableIdentityCredential.h
Normal file
90
identity/aidl/default/WritableIdentityCredential.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2019, 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 ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
|
||||
#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
|
||||
|
||||
#include <aidl/android/hardware/identity/BnWritableIdentityCredential.h>
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <cppbor.h>
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
class WritableIdentityCredential : public BnWritableIdentityCredential {
|
||||
public:
|
||||
WritableIdentityCredential(const string& docType, bool testCredential)
|
||||
: docType_(docType), testCredential_(testCredential) {}
|
||||
|
||||
// Creates the Credential Key. Returns false on failure. Must be called
|
||||
// right after construction.
|
||||
bool initialize();
|
||||
|
||||
// Methods from IWritableIdentityCredential follow.
|
||||
ndk::ScopedAStatus getAttestationCertificate(const vector<int8_t>& attestationApplicationId,
|
||||
const vector<int8_t>& attestationChallenge,
|
||||
vector<Certificate>* outCertificateChain) override;
|
||||
|
||||
ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount,
|
||||
const vector<int32_t>& entryCounts) override;
|
||||
|
||||
ndk::ScopedAStatus addAccessControlProfile(
|
||||
int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
|
||||
int64_t timeoutMillis, int64_t secureUserId,
|
||||
SecureAccessControlProfile* outSecureAccessControlProfile) override;
|
||||
|
||||
ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds,
|
||||
const string& nameSpace, const string& name,
|
||||
int32_t entrySize) override;
|
||||
|
||||
ndk::ScopedAStatus addEntryValue(const vector<int8_t>& content,
|
||||
vector<int8_t>* outEncryptedContent) override;
|
||||
|
||||
ndk::ScopedAStatus finishAddingEntries(
|
||||
vector<int8_t>* outCredentialData,
|
||||
vector<int8_t>* outProofOfProvisioningSignature) override;
|
||||
|
||||
// private:
|
||||
string docType_;
|
||||
bool testCredential_;
|
||||
|
||||
// These are set in initialize().
|
||||
vector<uint8_t> storageKey_;
|
||||
vector<uint8_t> credentialPrivKey_;
|
||||
vector<uint8_t> credentialPubKey_;
|
||||
|
||||
// These fields are initialized during startPersonalization()
|
||||
size_t numAccessControlProfileRemaining_;
|
||||
vector<int32_t> remainingEntryCounts_;
|
||||
cppbor::Array signedDataAccessControlProfiles_;
|
||||
cppbor::Map signedDataNamespaces_;
|
||||
cppbor::Array signedDataCurrentNamespace_;
|
||||
|
||||
// These fields are initialized during beginAddEntry()
|
||||
size_t entryRemainingBytes_;
|
||||
vector<uint8_t> entryAdditionalData_;
|
||||
string entryNameSpace_;
|
||||
string entryName_;
|
||||
vector<int32_t> entryAccessControlProfileIds_;
|
||||
vector<uint8_t> entryBytes_;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
|
||||
3
identity/aidl/default/identity-default.rc
Normal file
3
identity/aidl/default/identity-default.rc
Normal file
@@ -0,0 +1,3 @@
|
||||
service vendor.identity-default /vendor/bin/hw/android.hardware.identity-service.example
|
||||
class hal
|
||||
user nobody
|
||||
9
identity/aidl/default/identity-default.xml
Normal file
9
identity/aidl/default/identity-default.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.identity</name>
|
||||
<interface>
|
||||
<name>IIdentityCredentialStore</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
||||
39
identity/aidl/default/service.cpp
Normal file
39
identity/aidl/default/service.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2019, 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 "android.hardware.identity-service"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
|
||||
#include "IdentityCredentialStore.h"
|
||||
|
||||
using aidl::android::hardware::identity::IdentityCredentialStore;
|
||||
|
||||
int main() {
|
||||
ABinderProcess_setThreadPoolMaxThreadCount(0);
|
||||
std::shared_ptr<IdentityCredentialStore> store =
|
||||
ndk::SharedRefBase::make<IdentityCredentialStore>();
|
||||
|
||||
const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default";
|
||||
LOG(INFO) << "instance: " << instance;
|
||||
binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str());
|
||||
CHECK(status == STATUS_OK);
|
||||
|
||||
ABinderProcess_joinThreadPool();
|
||||
return EXIT_FAILURE; // should not reach
|
||||
}
|
||||
21
identity/aidl/vts/Android.bp
Normal file
21
identity/aidl/vts/Android.bp
Normal file
@@ -0,0 +1,21 @@
|
||||
cc_test {
|
||||
name: "VtsHalIdentityTargetTest",
|
||||
defaults: [
|
||||
"VtsHalTargetTestDefaults",
|
||||
"use_libaidlvintf_gtest_helper_static",
|
||||
],
|
||||
srcs: ["VtsHalIdentityTargetTest.cpp"],
|
||||
shared_libs: [
|
||||
"libbinder",
|
||||
"libcppbor",
|
||||
"android.hardware.identity-support-lib",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.identity-cpp",
|
||||
"android.hardware.keymaster-cpp",
|
||||
],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"vts-core",
|
||||
],
|
||||
}
|
||||
@@ -13,62 +13,56 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#define LOG_TAG "VtsHalIdentityTargetTest"
|
||||
|
||||
#define LOG_TAG "IdentityCredentialHidlHalTest"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <aidl/Gtest.h>
|
||||
#include <aidl/Vintf.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/1.0/types.h>
|
||||
#include <android/hardware/identity/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <binder/ProcessState.h>
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hidl/GtestPrinter.h>
|
||||
#include <hidl/ServiceManagement.h>
|
||||
#include <future>
|
||||
#include <map>
|
||||
|
||||
namespace android::hardware::identity {
|
||||
|
||||
using std::map;
|
||||
using std::optional;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
namespace test {
|
||||
using ::android::sp;
|
||||
using ::android::String16;
|
||||
using ::android::binder::Status;
|
||||
|
||||
using ::android::hardware::identity::V1_0::IIdentityCredential;
|
||||
using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
|
||||
using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
|
||||
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
|
||||
using ::android::hardware::keymaster::HardwareAuthToken;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test Data.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct TestEntryData {
|
||||
TestEntryData(string nameSpace, string name, vector<uint16_t> profileIds)
|
||||
TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
|
||||
: nameSpace(nameSpace), name(name), profileIds(profileIds) {}
|
||||
|
||||
TestEntryData(string nameSpace, string name, const string& value, vector<uint16_t> profileIds)
|
||||
TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
|
||||
: TestEntryData(nameSpace, name, profileIds) {
|
||||
valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
|
||||
}
|
||||
TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
|
||||
vector<uint16_t> profileIds)
|
||||
vector<int32_t> profileIds)
|
||||
: TestEntryData(nameSpace, name, profileIds) {
|
||||
valueCbor = cppbor::Bstr(value).encode();
|
||||
}
|
||||
TestEntryData(string nameSpace, string name, bool value, vector<uint16_t> profileIds)
|
||||
TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
|
||||
: TestEntryData(nameSpace, name, profileIds) {
|
||||
valueCbor = cppbor::Bool(value).encode();
|
||||
}
|
||||
TestEntryData(string nameSpace, string name, int64_t value, vector<uint16_t> profileIds)
|
||||
TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
|
||||
: TestEntryData(nameSpace, name, profileIds) {
|
||||
if (value >= 0) {
|
||||
valueCbor = cppbor::Uint(value).encode();
|
||||
@@ -80,63 +74,38 @@ struct TestEntryData {
|
||||
string nameSpace;
|
||||
string name;
|
||||
vector<uint8_t> valueCbor;
|
||||
vector<uint16_t> profileIds;
|
||||
vector<int32_t> profileIds;
|
||||
};
|
||||
|
||||
struct TestProfile {
|
||||
uint16_t id;
|
||||
hidl_vec<uint8_t> readerCertificate;
|
||||
vector<uint8_t> readerCertificate;
|
||||
bool userAuthenticationRequired;
|
||||
uint64_t timeoutMillis;
|
||||
};
|
||||
|
||||
/************************************
|
||||
* TEST DATA FOR AUTHENTICATION
|
||||
************************************/
|
||||
// Test authentication token for user authentication
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
class IdentityCredentialStoreHidlTest : public ::testing::TestWithParam<std::string> {
|
||||
class IdentityAidl : public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
string serviceName = GetParam();
|
||||
ASSERT_FALSE(serviceName.empty());
|
||||
credentialStore_ = IIdentityCredentialStore::getService(serviceName);
|
||||
credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
|
||||
String16(GetParam().c_str()));
|
||||
ASSERT_NE(credentialStore_, nullptr);
|
||||
|
||||
credentialStore_->getHardwareInformation(
|
||||
[&](const Result& result, const hidl_string& credentialStoreName,
|
||||
const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
|
||||
bool /* isDirectAccess */,
|
||||
const hidl_vec<hidl_string> /* supportedDocTypes */) {
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_GT(credentialStoreName.size(), 0u);
|
||||
ASSERT_GT(credentialStoreAuthorName.size(), 0u);
|
||||
ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
|
||||
dataChunkSize_ = chunkSize;
|
||||
});
|
||||
}
|
||||
virtual void TearDown() override {}
|
||||
|
||||
uint32_t dataChunkSize_ = 0;
|
||||
|
||||
sp<IIdentityCredentialStore> credentialStore_;
|
||||
};
|
||||
|
||||
TEST_P(IdentityCredentialStoreHidlTest, HardwareConfiguration) {
|
||||
credentialStore_->getHardwareInformation(
|
||||
[&](const Result& result, const hidl_string& credentialStoreName,
|
||||
const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
|
||||
bool /* isDirectAccess */, const hidl_vec<hidl_string> /* supportedDocTypes */) {
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_GT(credentialStoreName.size(), 0u);
|
||||
ASSERT_GT(credentialStoreAuthorName.size(), 0u);
|
||||
ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
|
||||
});
|
||||
TEST_P(IdentityAidl, hardwareInformation) {
|
||||
HardwareInformation info;
|
||||
ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
|
||||
ASSERT_GT(info.credentialStoreName.size(), 0);
|
||||
ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
|
||||
ASSERT_GE(info.dataChunkSize, 256);
|
||||
}
|
||||
|
||||
TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
||||
// First, generate a key-pair for the reader since its public key will be
|
||||
// part of the request data.
|
||||
optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
|
||||
@@ -168,75 +137,59 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
// Profile 1 (no authentication)
|
||||
{1, {}, false, 0}};
|
||||
|
||||
HardwareAuthToken authToken = {};
|
||||
HardwareAuthToken authToken;
|
||||
|
||||
// Here's the actual test data:
|
||||
const vector<TestEntryData> testEntries = {
|
||||
{"PersonalData", "Last name", string("Turing"), vector<uint16_t>{0, 1}},
|
||||
{"PersonalData", "Birth date", string("19120623"), vector<uint16_t>{0, 1}},
|
||||
{"PersonalData", "First name", string("Alan"), vector<uint16_t>{0, 1}},
|
||||
{"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
|
||||
{"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
|
||||
{"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
|
||||
{"PersonalData", "Home address", string("Maida Vale, London, England"),
|
||||
vector<uint16_t>{0}},
|
||||
{"Image", "Portrait image", portraitImage, vector<uint16_t>{0, 1}},
|
||||
vector<int32_t>{0}},
|
||||
{"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
|
||||
};
|
||||
const vector<uint16_t> testEntriesEntryCounts = {static_cast<uint16_t>(testEntries.size() - 1),
|
||||
1u};
|
||||
const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
|
||||
1u};
|
||||
HardwareInformation hwInfo;
|
||||
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
|
||||
|
||||
string cborPretty;
|
||||
sp<IWritableIdentityCredential> writableCredential;
|
||||
|
||||
hidl_vec<uint8_t> empty{0};
|
||||
|
||||
string docType = "org.iso.18013-5.2019.mdl";
|
||||
bool testCredential = true;
|
||||
Result result;
|
||||
credentialStore_->createCredential(
|
||||
docType, testCredential,
|
||||
[&](const Result& _result, const sp<IWritableIdentityCredential>& _writableCredential) {
|
||||
result = _result;
|
||||
writableCredential = _writableCredential;
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &writableCredential)
|
||||
.isOk());
|
||||
ASSERT_NE(writableCredential, nullptr);
|
||||
|
||||
string challenge = "attestationChallenge";
|
||||
// TODO: set it to something random and check it's in the cert chain
|
||||
vector<uint8_t> attestationApplicationId = {};
|
||||
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
|
||||
vector<uint8_t> attestationCertificate;
|
||||
writableCredential->getAttestationCertificate(
|
||||
attestationApplicationId, attestationChallenge,
|
||||
[&](const Result& _result, const hidl_vec<hidl_vec<uint8_t>>& _splitCertChain) {
|
||||
result = _result;
|
||||
vector<vector<uint8_t>> splitCerts;
|
||||
std::copy(_splitCertChain.begin(), _splitCertChain.end(),
|
||||
std::back_inserter(splitCerts));
|
||||
attestationCertificate = support::certificateChainJoin(splitCerts);
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
vector<Certificate> attestationCertificates;
|
||||
ASSERT_TRUE(writableCredential
|
||||
->getAttestationCertificate(attestationApplicationId, attestationChallenge,
|
||||
&attestationCertificates)
|
||||
.isOk());
|
||||
ASSERT_GE(attestationCertificates.size(), 2);
|
||||
|
||||
writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts,
|
||||
[&](const Result& _result) { result = _result; });
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(
|
||||
writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
|
||||
.isOk());
|
||||
|
||||
vector<SecureAccessControlProfile> returnedSecureProfiles;
|
||||
for (const auto& testProfile : testProfiles) {
|
||||
SecureAccessControlProfile profile;
|
||||
writableCredential->addAccessControlProfile(
|
||||
testProfile.id, testProfile.readerCertificate,
|
||||
testProfile.userAuthenticationRequired, testProfile.timeoutMillis,
|
||||
0, // secureUserId
|
||||
[&](const Result& _result, const SecureAccessControlProfile& _profile) {
|
||||
result = _result;
|
||||
profile = _profile;
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
Certificate cert;
|
||||
cert.encodedCertificate = testProfile.readerCertificate;
|
||||
ASSERT_TRUE(writableCredential
|
||||
->addAccessControlProfile(testProfile.id, cert,
|
||||
testProfile.userAuthenticationRequired,
|
||||
testProfile.timeoutMillis,
|
||||
0, // secureUserId
|
||||
&profile)
|
||||
.isOk());
|
||||
ASSERT_EQ(testProfile.id, profile.id);
|
||||
ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate);
|
||||
ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
|
||||
ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
|
||||
ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
|
||||
ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
|
||||
@@ -248,38 +201,28 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
|
||||
|
||||
for (const auto& entry : testEntries) {
|
||||
vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize_);
|
||||
vector<vector<uint8_t>> chunks =
|
||||
support::chunkVector(entry.valueCbor, hwInfo.dataChunkSize);
|
||||
|
||||
writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
|
||||
entry.valueCbor.size(),
|
||||
[&](const Result& _result) { result = _result; });
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(writableCredential
|
||||
->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
|
||||
entry.valueCbor.size())
|
||||
.isOk());
|
||||
|
||||
vector<vector<uint8_t>> encryptedChunks;
|
||||
for (const auto& chunk : chunks) {
|
||||
writableCredential->addEntryValue(
|
||||
chunk, [&](const Result& result, hidl_vec<uint8_t> encryptedContent) {
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_GT(encryptedContent.size(), 0u);
|
||||
encryptedChunks.push_back(encryptedContent);
|
||||
});
|
||||
vector<uint8_t> encryptedChunk;
|
||||
ASSERT_TRUE(writableCredential->addEntryValue(chunk, &encryptedChunk).isOk());
|
||||
encryptedChunks.push_back(encryptedChunk);
|
||||
}
|
||||
encryptedBlobs[&entry] = encryptedChunks;
|
||||
}
|
||||
|
||||
vector<uint8_t> credentialData;
|
||||
vector<uint8_t> proofOfProvisioningSignature;
|
||||
writableCredential->finishAddingEntries(
|
||||
[&](const Result& _result, const hidl_vec<uint8_t>& _credentialData,
|
||||
const hidl_vec<uint8_t>& _proofOfProvisioningSignature) {
|
||||
result = _result;
|
||||
credentialData = _credentialData;
|
||||
proofOfProvisioningSignature = _proofOfProvisioningSignature;
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(
|
||||
writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
|
||||
.isOk());
|
||||
|
||||
optional<vector<uint8_t>> proofOfProvisioning =
|
||||
support::coseSignGetPayload(proofOfProvisioningSignature);
|
||||
@@ -334,7 +277,7 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
cborPretty);
|
||||
|
||||
optional<vector<uint8_t>> credentialPubKey =
|
||||
support::certificateChainGetTopMostKey(attestationCertificate);
|
||||
support::certificateChainGetTopMostKey(attestationCertificates[0].encodedCertificate);
|
||||
ASSERT_TRUE(credentialPubKey);
|
||||
EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
|
||||
{}, // Additional data
|
||||
@@ -344,32 +287,21 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
// Now that the credential has been provisioned, read it back and check the
|
||||
// correct data is returned.
|
||||
sp<IIdentityCredential> credential;
|
||||
credentialStore_->getCredential(
|
||||
credentialData, [&](const Result& _result, const sp<IIdentityCredential>& _credential) {
|
||||
result = _result;
|
||||
credential = _credential;
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(credentialStore_
|
||||
->getCredential(
|
||||
CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
|
||||
credentialData, &credential)
|
||||
.isOk());
|
||||
ASSERT_NE(credential, nullptr);
|
||||
|
||||
optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
|
||||
ASSERT_TRUE(readerEphemeralKeyPair);
|
||||
optional<vector<uint8_t>> readerEphemeralPublicKey =
|
||||
support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
|
||||
credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value(),
|
||||
[&](const Result& _result) { result = _result; });
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
|
||||
|
||||
vector<uint8_t> ephemeralKeyPair;
|
||||
credential->createEphemeralKeyPair(
|
||||
[&](const Result& _result, const hidl_vec<uint8_t>& _ephemeralKeyPair) {
|
||||
result = _result;
|
||||
ephemeralKeyPair = _ephemeralKeyPair;
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
|
||||
optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
|
||||
|
||||
// Calculate requestData field and sign it with the reader key.
|
||||
@@ -420,19 +352,17 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
readerCertificate.value());
|
||||
ASSERT_TRUE(readerSignature);
|
||||
|
||||
credential->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
|
||||
sessionTranscriptBytes, readerSignature.value(),
|
||||
testEntriesEntryCounts,
|
||||
[&](const Result& _result) { result = _result; });
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(credential
|
||||
->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
|
||||
sessionTranscriptBytes, readerSignature.value(),
|
||||
testEntriesEntryCounts)
|
||||
.isOk());
|
||||
|
||||
for (const auto& entry : testEntries) {
|
||||
credential->startRetrieveEntryValue(entry.nameSpace, entry.name, entry.valueCbor.size(),
|
||||
entry.profileIds,
|
||||
[&](const Result& _result) { result = _result; });
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(credential
|
||||
->startRetrieveEntryValue(entry.nameSpace, entry.name,
|
||||
entry.valueCbor.size(), entry.profileIds)
|
||||
.isOk());
|
||||
|
||||
auto it = encryptedBlobs.find(&entry);
|
||||
ASSERT_NE(it, encryptedBlobs.end());
|
||||
@@ -441,13 +371,7 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
vector<uint8_t> content;
|
||||
for (const auto& encryptedChunk : encryptedChunks) {
|
||||
vector<uint8_t> chunk;
|
||||
credential->retrieveEntryValue(
|
||||
encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& _chunk) {
|
||||
result = _result;
|
||||
chunk = _chunk;
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
|
||||
content.insert(content.end(), chunk.begin(), chunk.end());
|
||||
}
|
||||
EXPECT_EQ(content, entry.valueCbor);
|
||||
@@ -455,28 +379,12 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
|
||||
// Generate the key that will be used to sign AuthenticatedData.
|
||||
vector<uint8_t> signingKeyBlob;
|
||||
vector<uint8_t> signingKeyCertificate;
|
||||
credential->generateSigningKeyPair([&](const Result& _result,
|
||||
const hidl_vec<uint8_t> _signingKeyBlob,
|
||||
const hidl_vec<uint8_t> _signingKeyCertificate) {
|
||||
result = _result;
|
||||
signingKeyBlob = _signingKeyBlob;
|
||||
signingKeyCertificate = _signingKeyCertificate;
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
Certificate signingKeyCertificate;
|
||||
ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
|
||||
|
||||
vector<uint8_t> mac;
|
||||
vector<uint8_t> deviceNameSpacesBytes;
|
||||
credential->finishRetrieval(signingKeyBlob,
|
||||
[&](const Result& _result, const hidl_vec<uint8_t> _mac,
|
||||
const hidl_vec<uint8_t> _deviceNameSpacesBytes) {
|
||||
result = _result;
|
||||
mac = _mac;
|
||||
deviceNameSpacesBytes = _deviceNameSpacesBytes;
|
||||
});
|
||||
EXPECT_EQ("", result.message);
|
||||
ASSERT_EQ(ResultCode::OK, result.code);
|
||||
ASSERT_TRUE(credential->finishRetrieval(signingKeyBlob, &mac, &deviceNameSpacesBytes).isOk());
|
||||
cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
|
||||
ASSERT_EQ(
|
||||
"{\n"
|
||||
@@ -501,7 +409,7 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
|
||||
vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
|
||||
optional<vector<uint8_t>> signingPublicKey =
|
||||
support::certificateChainGetTopMostKey(signingKeyCertificate);
|
||||
support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
|
||||
EXPECT_TRUE(signingPublicKey);
|
||||
|
||||
// Derive the key used for MACing.
|
||||
@@ -521,12 +429,18 @@ TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
|
||||
EXPECT_EQ(mac, calculatedMac);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(PerInstance, IdentityCredentialStoreHidlTest,
|
||||
testing::ValuesIn(android::hardware::getAllHalInstanceNames(
|
||||
IIdentityCredentialStore::descriptor)),
|
||||
android::hardware::PrintInstanceNameToString);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
Identity, IdentityAidl,
|
||||
testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
|
||||
android::PrintInstanceNameToString);
|
||||
// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
|
||||
// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
|
||||
|
||||
} // namespace test
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace android::hardware::identity
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
|
||||
::android::ProcessState::self()->startThreadPool();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -23,7 +23,6 @@ cc_library {
|
||||
"include",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.identity@1.0",
|
||||
"libcrypto",
|
||||
"libbase",
|
||||
"libhidlbase",
|
||||
@@ -41,7 +40,6 @@ cc_test {
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.identity-support-lib",
|
||||
"android.hardware.identity@1.0",
|
||||
"libcrypto",
|
||||
"libbase",
|
||||
"libhidlbase",
|
||||
|
||||
@@ -18,12 +18,11 @@
|
||||
#define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include <android/hardware/identity/1.0/types.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
@@ -34,10 +33,6 @@ using ::std::string;
|
||||
using ::std::tuple;
|
||||
using ::std::vector;
|
||||
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Miscellaneous utilities.
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -257,22 +252,12 @@ optional<vector<uint8_t>> coseSignGetX5Chain(const vector<uint8_t>& signatureCos
|
||||
optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint8_t>& data,
|
||||
const vector<uint8_t>& detachedContent);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Platform abstraction.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Returns the hardware-bound AES-128 key.
|
||||
const vector<uint8_t>& getHardwareBoundKey();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Utility functions specific to IdentityCredential.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Returns a reference to a Result with code OK and empty message.
|
||||
const Result& resultOK();
|
||||
|
||||
// Returns a new Result with the given code and message.
|
||||
Result result(ResultCode code, const char* format, ...) __attribute__((format(printf, 2, 3)));
|
||||
// Returns the testing AES-128 key where all bits are set to 0.
|
||||
const vector<uint8_t>& getTestHardwareBoundKey();
|
||||
|
||||
// Splits the given bytestring into chunks. If the given vector is smaller or equal to
|
||||
// |maxChunkSize| a vector with |content| as the only element is returned. Otherwise
|
||||
@@ -280,21 +265,6 @@ Result result(ResultCode code, const char* format, ...) __attribute__((format(pr
|
||||
// may be smaller than |maxChunkSize|.
|
||||
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize);
|
||||
|
||||
// Calculates the MAC for |profile| using |storageKey|.
|
||||
optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
|
||||
const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
|
||||
|
||||
// Checks authenticity of the MAC in |profile| using |storageKey|.
|
||||
bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
|
||||
const vector<uint8_t>& storageKey);
|
||||
|
||||
// Returns the testing AES-128 key where all bits are set to 0.
|
||||
const vector<uint8_t>& getTestHardwareBoundKey();
|
||||
|
||||
// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
|
||||
vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
|
||||
const vector<uint16_t> accessControlProfileIds);
|
||||
|
||||
} // namespace support
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
|
||||
@@ -1681,37 +1681,10 @@ optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint
|
||||
return array.encode();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Platform abstraction.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// This is not a very random HBK but that's OK because this is the SW
|
||||
// implementation where it can't be kept secret.
|
||||
vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
|
||||
const vector<uint8_t>& getHardwareBoundKey() {
|
||||
return hardwareBoundKey;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Utility functions specific to IdentityCredential.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Result okResult{ResultCode::OK, ""};
|
||||
|
||||
const Result& resultOK() {
|
||||
return okResult;
|
||||
}
|
||||
|
||||
Result result(ResultCode code, const char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
string str;
|
||||
android::base::StringAppendV(&str, format, ap);
|
||||
va_end(ap);
|
||||
return Result{code, str};
|
||||
}
|
||||
|
||||
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) {
|
||||
vector<vector<uint8_t>> ret;
|
||||
|
||||
@@ -1738,56 +1711,6 @@ vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxCh
|
||||
return ret;
|
||||
}
|
||||
|
||||
vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
|
||||
cppbor::Map map;
|
||||
map.add("id", profile.id);
|
||||
|
||||
if (profile.readerCertificate.size() > 0) {
|
||||
map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
|
||||
}
|
||||
|
||||
if (profile.userAuthenticationRequired) {
|
||||
map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
|
||||
map.add("timeoutMillis", profile.timeoutMillis);
|
||||
map.add("secureUserId", profile.secureUserId);
|
||||
}
|
||||
|
||||
return map.encode();
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
|
||||
const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
|
||||
vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
|
||||
|
||||
optional<vector<uint8_t>> nonce = getRandom(12);
|
||||
if (!nonce) {
|
||||
return {};
|
||||
}
|
||||
optional<vector<uint8_t>> macO = encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
|
||||
if (!macO) {
|
||||
return {};
|
||||
}
|
||||
return macO.value();
|
||||
}
|
||||
|
||||
bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
|
||||
const vector<uint8_t>& storageKey) {
|
||||
vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
|
||||
|
||||
if (profile.mac.size() < kAesGcmIvSize) {
|
||||
return false;
|
||||
}
|
||||
vector<uint8_t> nonce =
|
||||
vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + kAesGcmIvSize);
|
||||
optional<vector<uint8_t>> mac = encryptAes128Gcm(storageKey, nonce, {}, cborData);
|
||||
if (!mac) {
|
||||
return false;
|
||||
}
|
||||
if (mac.value() != vector<uint8_t>(profile.mac)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<uint8_t> testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
@@ -1795,20 +1718,6 @@ const vector<uint8_t>& getTestHardwareBoundKey() {
|
||||
return testHardwareBoundKey;
|
||||
}
|
||||
|
||||
vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
|
||||
const vector<uint16_t> accessControlProfileIds) {
|
||||
cppbor::Map map;
|
||||
map.add("Namespace", nameSpace);
|
||||
map.add("Name", name);
|
||||
|
||||
cppbor::Array acpIds;
|
||||
for (auto id : accessControlProfileIds) {
|
||||
acpIds.add(id);
|
||||
}
|
||||
map.add("AccessControlProfileIds", std::move(acpIds));
|
||||
return map.encode();
|
||||
}
|
||||
|
||||
} // namespace support
|
||||
} // namespace identity
|
||||
} // namespace hardware
|
||||
|
||||
18
keymaster/aidl/Android.bp
Normal file
18
keymaster/aidl/Android.bp
Normal file
@@ -0,0 +1,18 @@
|
||||
aidl_interface {
|
||||
name: "android.hardware.keymaster",
|
||||
vendor_available: true,
|
||||
srcs: [
|
||||
"android/hardware/keymaster/*.aidl",
|
||||
],
|
||||
stability: "vintf",
|
||||
backend: {
|
||||
java: {
|
||||
platform_apis: true,
|
||||
},
|
||||
ndk: {
|
||||
vndk: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
package android.hardware.keymaster;
|
||||
|
||||
import android.hardware.keymaster.Timestamp;
|
||||
import android.hardware.keymaster.HardwareAuthenticatorType;
|
||||
|
||||
/**
|
||||
* HardwareAuthToken is used to prove successful user authentication, to unlock the use of a key.
|
||||
*
|
||||
* HardwareAuthTokens are produced by other secure environment applications, notably GateKeeper and
|
||||
* Fingerprint, in response to successful user authentication events. These tokens are passed to
|
||||
* begin(), update(), and finish() to prove that authentication occurred. See those methods for
|
||||
* more details. It is up to the caller to determine which of the generated auth tokens is
|
||||
* appropriate for a given key operation.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable HardwareAuthToken {
|
||||
|
||||
/**
|
||||
* challenge is a value that's used to enable authentication tokens to authorize specific
|
||||
* events. The primary use case for challenge is to authorize an IKeymasterDevice cryptographic
|
||||
* operation, for keys that require authentication per operation. See begin() for details.
|
||||
*/
|
||||
long challenge;
|
||||
|
||||
/**
|
||||
* userId is the a "secure" user ID. It is not related to any Android user ID or UID, but is
|
||||
* created in the Gatekeeper application in the secure environment.
|
||||
*/
|
||||
long userId;
|
||||
|
||||
/**
|
||||
* authenticatorId is the a "secure" user ID. It is not related to any Android user ID or UID,
|
||||
* but is created in an authentication application in the secure environment, such as the
|
||||
* Fingerprint application.
|
||||
*/
|
||||
long authenticatorId; // Secure authenticator ID.
|
||||
|
||||
/**
|
||||
* authenticatorType describes the type of authentication that took place, e.g. password or
|
||||
* fingerprint.
|
||||
*/
|
||||
HardwareAuthenticatorType authenticatorType;
|
||||
|
||||
/**
|
||||
* timestamp indicates when the user authentication took place, in milliseconds since some
|
||||
* starting point (generally the most recent device boot) which all of the applications within
|
||||
* one secure environment must agree upon. This timestamp is used to determine whether or not
|
||||
* the authentication occurred recently enough to unlock a key (see Tag::AUTH_TIMEOUT).
|
||||
*/
|
||||
Timestamp timestamp;
|
||||
|
||||
/**
|
||||
* MACs are computed with a backward-compatible method, used by Keymaster 3.0, Gatekeeper 1.0
|
||||
* and Fingerprint 1.0, as well as pre-treble HALs.
|
||||
*
|
||||
* The MAC is Constants::AUTH_TOKEN_MAC_LENGTH bytes in length and is computed as follows:
|
||||
*
|
||||
* HMAC_SHA256(
|
||||
* H, 0 || challenge || user_id || authenticator_id || authenticator_type || timestamp)
|
||||
*
|
||||
* where ``||'' represents concatenation, the leading zero is a single byte, and all integers
|
||||
* are represented as unsigned values, the full width of the type. The challenge, userId and
|
||||
* authenticatorId values are in machine order, but authenticatorType and timestamp are in
|
||||
* network order (big-endian). This odd construction is compatible with the hw_auth_token_t
|
||||
* structure,
|
||||
*
|
||||
* Note that mac is a vec rather than an array, not because it's actually variable-length but
|
||||
* because it could be empty. As documented in the IKeymasterDevice::begin,
|
||||
* IKeymasterDevice::update and IKeymasterDevice::finish doc comments, an empty mac indicates
|
||||
* that this auth token is empty.
|
||||
*/
|
||||
byte[] mac;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
package android.hardware.keymaster;
|
||||
|
||||
/**
|
||||
* Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to
|
||||
* authentiate the user, and in KeyCharacteristics to specify the allowable mechanisms for
|
||||
* authenticating to activate a key.
|
||||
*/
|
||||
@VintfStability
|
||||
@Backing(type="int")
|
||||
enum HardwareAuthenticatorType {
|
||||
NONE = 0,
|
||||
PASSWORD = 1 << 0,
|
||||
FINGERPRINT = 1 << 1,
|
||||
// Additional entries must be powers of 2.
|
||||
ANY = 0xFFFFFFFF,
|
||||
}
|
||||
22
keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
Normal file
22
keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
package android.hardware.keymaster;
|
||||
|
||||
@VintfStability
|
||||
parcelable Timestamp {
|
||||
long milliSeconds;
|
||||
}
|
||||
Reference in New Issue
Block a user