Merge "Identity: Update for changes to ISO 18013-5." am: 84a6118710

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1344923

Change-Id: I388758629a0edbe6f1d531df8a974e95b18791c4
This commit is contained in:
Treehugger Robot
2020-06-24 16:07:44 +00:00
committed by Automerger Merge Worker
5 changed files with 79 additions and 44 deletions

View File

@@ -151,8 +151,8 @@ interface IIdentityCredential {
* IntentToRetain = bool * IntentToRetain = bool
* *
* For the readerSignature parameter, this can either be empty or if non-empty it * For the readerSignature parameter, this can either be empty or if non-empty it
* must be a COSE_Sign1 structure with an ECDSA signature over the content of the * must be a COSE_Sign1 where the payload is the bytes of the
* CBOR conforming to the following CDDL: * ReaderAuthenticationBytes CBOR defined below:
* *
* ReaderAuthentication = [ * ReaderAuthentication = [
* "ReaderAuthentication", * "ReaderAuthentication",
@@ -164,6 +164,8 @@ interface IIdentityCredential {
* *
* ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest) * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
* *
* ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
*
* The public key corresponding to the key used to made signature, can be found in the * The public key corresponding to the key used to made signature, can be found in the
* 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described * '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 * in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element
@@ -278,7 +280,7 @@ interface IIdentityCredential {
* *
* @param out 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 * startRetrieval() is empty. Otherwise it is a COSE_Mac0 with empty payload
* and the detached content is set to DeviceAuthentication as defined below. * and the detached content is set to DeviceAuthenticationBytes as defined below.
* This code is produced by using the key agreement and key derivation function * This code is produced by using the key agreement and key derivation function
* from the ciphersuite with the authentication private key and the reader * from the ciphersuite with the authentication private key and the reader
* ephemeral public key to compute a shared message authentication code (MAC) * ephemeral public key to compute a shared message authentication code (MAC)
@@ -299,6 +301,8 @@ interface IIdentityCredential {
* *
* DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces) * DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
* *
* DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
*
* where * where
* *
* DeviceNameSpaces = { * DeviceNameSpaces = {

View File

@@ -99,7 +99,7 @@ import android.hardware.identity.CipherSuite;
* Various fields need to be encoded as precisely-specified byte arrays. Where existing standards * Various fields need to be encoded as precisely-specified byte arrays. Where existing standards
* define appropriate encodings, those are used. For example, X.509 certificates. Where new * 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 * encodings are needed, CBOR is used. CBOR maps are described in CDDL notation
* (https://tools.ietf.org/html/draft-ietf-cbor-cddl-06). * (https://tools.ietf.org/html/rfc8610).
* *
* All binder calls in the HAL may return a ServiceSpecificException with statuses from the * 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 * STATUS_* integers defined in this interface. Each method states which status can be returned

View File

@@ -39,6 +39,10 @@ using ::std::optional;
using namespace ::android::hardware::identity; using namespace ::android::hardware::identity;
int IdentityCredential::initialize() { int IdentityCredential::initialize() {
if (credentialData_.size() == 0) {
LOG(ERROR) << "CredentialData is empty";
return IIdentityCredentialStore::STATUS_INVALID_DATA;
}
auto [item, _, message] = cppbor::parse(credentialData_); auto [item, _, message] = cppbor::parse(credentialData_);
if (item == nullptr) { if (item == nullptr) {
LOG(ERROR) << "CredentialData is not valid CBOR: " << message; LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
@@ -312,13 +316,16 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
} }
const vector<uint8_t>& itemsRequestBytes = itemsRequest; const vector<uint8_t>& itemsRequestBytes = itemsRequest;
vector<uint8_t> dataThatWasSigned = cppbor::Array() vector<uint8_t> encodedReaderAuthentication =
.add("ReaderAuthentication") cppbor::Array()
.add(sessionTranscriptItem_->clone()) .add("ReaderAuthentication")
.add(cppbor::Semantic(24, itemsRequestBytes)) .add(sessionTranscriptItem_->clone())
.encode(); .add(cppbor::Semantic(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
cppbor::Semantic(24, encodedReaderAuthentication).encode();
if (!support::coseCheckEcDsaSignature(readerSignature, if (!support::coseCheckEcDsaSignature(readerSignature,
dataThatWasSigned, // detached content encodedReaderAuthenticationBytes, // detached content
readerPublicKey.value())) { readerPublicKey.value())) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED, IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
@@ -774,7 +781,7 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
array.add(sessionTranscriptItem_->clone()); array.add(sessionTranscriptItem_->clone());
array.add(docType_); array.add(docType_);
array.add(cppbor::Semantic(24, encodedDeviceNameSpaces)); array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
vector<uint8_t> encodedDeviceAuthentication = array.encode(); vector<uint8_t> deviceAuthenticationBytes = cppbor::Semantic(24, array.encode()).encode();
vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end()); vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
optional<vector<uint8_t>> signingKey = optional<vector<uint8_t>> signingKey =
@@ -792,17 +799,24 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH")); IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH"));
} }
// Mix-in SessionTranscriptBytes
vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
vector<uint8_t> sharedSecretWithSessionTranscriptBytes = sharedSecret.value();
std::copy(sessionTranscriptBytes.begin(), sessionTranscriptBytes.end(),
std::back_inserter(sharedSecretWithSessionTranscriptBytes));
vector<uint8_t> salt = {0x00}; vector<uint8_t> salt = {0x00};
vector<uint8_t> info = {}; vector<uint8_t> info = {};
optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32); optional<vector<uint8_t>> derivedKey =
support::hkdf(sharedSecretWithSessionTranscriptBytes, salt, info, 32);
if (!derivedKey) { if (!derivedKey) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, IIdentityCredentialStore::STATUS_FAILED,
"Error deriving key from shared secret")); "Error deriving key from shared secret"));
} }
mac = support::coseMac0(derivedKey.value(), {}, // payload mac = support::coseMac0(derivedKey.value(), {}, // payload
encodedDeviceAuthentication); // additionalData deviceAuthenticationBytes); // detached content
if (!mac) { if (!mac) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "Error MACing data")); IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));

View File

@@ -289,16 +289,19 @@ void ReaderAuthTests::retrieveData(const vector<uint8_t>& readerPrivateKey,
.add("Accessible by None", false))) .add("Accessible by None", false)))
.encode(); .encode();
} }
vector<uint8_t> dataToSign = cppbor::Array() vector<uint8_t> encodedReaderAuthentication =
.add("ReaderAuthentication") cppbor::Array()
.add(sessionTranscript.clone()) .add("ReaderAuthentication")
.add(cppbor::Semantic(24, itemsRequestBytes)) .add(sessionTranscript.clone())
.encode(); .add(cppbor::Semantic(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
cppbor::Semantic(24, encodedReaderAuthentication).encode();
optional<vector<uint8_t>> readerSignature = optional<vector<uint8_t>> readerSignature =
support::coseSignEcDsa(readerPrivateKey, // private key for reader support::coseSignEcDsa(readerPrivateKey, // private key for reader
{}, // content {}, // content
dataToSign, // detached content encodedReaderAuthenticationBytes, // detached content
support::certificateChainJoin(readerCertChain)); support::certificateChainJoin(readerCertChain));
ASSERT_TRUE(readerSignature); ASSERT_TRUE(readerSignature);
@@ -528,17 +531,20 @@ TEST_P(ReaderAuthTests, ephemeralKeyNotInSessionTranscript) {
.add("Accessible by C", false) .add("Accessible by C", false)
.add("Accessible by None", false))) .add("Accessible by None", false)))
.encode(); .encode();
vector<uint8_t> dataToSign = cppbor::Array() vector<uint8_t> encodedReaderAuthentication =
.add("ReaderAuthentication") cppbor::Array()
.add(sessionTranscript.clone()) .add("ReaderAuthentication")
.add(cppbor::Semantic(24, itemsRequestBytes)) .add(sessionTranscript.clone())
.encode(); .add(cppbor::Semantic(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
cppbor::Semantic(24, encodedReaderAuthentication).encode();
vector<vector<uint8_t>> readerCertChain = {cert_reader_SelfSigned_}; vector<vector<uint8_t>> readerCertChain = {cert_reader_SelfSigned_};
optional<vector<uint8_t>> readerSignature = optional<vector<uint8_t>> readerSignature =
support::coseSignEcDsa(readerPrivateKey_, // private key for reader support::coseSignEcDsa(readerPrivateKey_, // private key for reader
{}, // content {}, // content
dataToSign, // detached content encodedReaderAuthenticationBytes, // detached content
support::certificateChainJoin(readerCertChain)); support::certificateChainJoin(readerCertChain));
ASSERT_TRUE(readerSignature); ASSERT_TRUE(readerSignature);

View File

@@ -319,7 +319,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
cppbor::Array sessionTranscript = cppbor::Array() cppbor::Array sessionTranscript = cppbor::Array()
.add(cppbor::Semantic(24, deviceEngagementBytes)) .add(cppbor::Semantic(24, deviceEngagementBytes))
.add(cppbor::Semantic(24, eReaderPubBytes)); .add(cppbor::Semantic(24, eReaderPubBytes));
vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode(); vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
vector<uint8_t> itemsRequestBytes = vector<uint8_t> itemsRequestBytes =
cppbor::Map("nameSpaces", cppbor::Map("nameSpaces",
@@ -347,14 +347,17 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
" },\n" " },\n"
"}", "}",
cborPretty); cborPretty);
vector<uint8_t> dataToSign = cppbor::Array() vector<uint8_t> encodedReaderAuthentication =
.add("ReaderAuthentication") cppbor::Array()
.add(sessionTranscript.clone()) .add("ReaderAuthentication")
.add(cppbor::Semantic(24, itemsRequestBytes)) .add(sessionTranscript.clone())
.encode(); .add(cppbor::Semantic(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
cppbor::Semantic(24, encodedReaderAuthentication).encode();
optional<vector<uint8_t>> readerSignature = optional<vector<uint8_t>> readerSignature =
support::coseSignEcDsa(readerKey, {}, // content support::coseSignEcDsa(readerKey, {}, // content
dataToSign, // detached content encodedReaderAuthenticationBytes, // detached content
readerCertificate.value()); readerCertificate.value());
ASSERT_TRUE(readerSignature); ASSERT_TRUE(readerSignature);
@@ -388,7 +391,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
credential->setVerificationToken(verificationToken); credential->setVerificationToken(verificationToken);
ASSERT_TRUE(credential ASSERT_TRUE(credential
->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes, ->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
signingKeyBlob, sessionTranscriptBytes, signingKeyBlob, sessionTranscriptEncoded,
readerSignature.value(), testEntriesEntryCounts) readerSignature.value(), testEntriesEntryCounts)
.isOk()); .isOk());
@@ -432,7 +435,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
" },\n" " },\n"
"}", "}",
cborPretty); cborPretty);
// The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType, // The data that is MACed is ["DeviceAuthentication", sessionTranscript, docType,
// deviceNameSpacesBytes] so build up that structure // deviceNameSpacesBytes] so build up that structure
cppbor::Array deviceAuthentication; cppbor::Array deviceAuthentication;
deviceAuthentication.add("DeviceAuthentication"); deviceAuthentication.add("DeviceAuthentication");
@@ -441,7 +444,8 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
string docType = "org.iso.18013-5.2019.mdl"; string docType = "org.iso.18013-5.2019.mdl";
deviceAuthentication.add(docType); deviceAuthentication.add(docType);
deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes)); deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode(); vector<uint8_t> deviceAuthenticationBytes =
cppbor::Semantic(24, deviceAuthentication.encode()).encode();
// Derive the key used for MACing. // Derive the key used for MACing.
optional<vector<uint8_t>> readerEphemeralPrivateKey = optional<vector<uint8_t>> readerEphemeralPrivateKey =
@@ -449,13 +453,20 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
optional<vector<uint8_t>> sharedSecret = optional<vector<uint8_t>> sharedSecret =
support::ecdh(signingPubKey.value(), readerEphemeralPrivateKey.value()); support::ecdh(signingPubKey.value(), readerEphemeralPrivateKey.value());
ASSERT_TRUE(sharedSecret); ASSERT_TRUE(sharedSecret);
// Mix-in SessionTranscriptBytes
vector<uint8_t> sessionTranscriptBytes =
cppbor::Semantic(24, sessionTranscript.encode()).encode();
vector<uint8_t> sharedSecretWithSessionTranscriptBytes = sharedSecret.value();
std::copy(sessionTranscriptBytes.begin(), sessionTranscriptBytes.end(),
std::back_inserter(sharedSecretWithSessionTranscriptBytes));
vector<uint8_t> salt = {0x00}; vector<uint8_t> salt = {0x00};
vector<uint8_t> info = {}; vector<uint8_t> info = {};
optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32); optional<vector<uint8_t>> derivedKey =
support::hkdf(sharedSecretWithSessionTranscriptBytes, salt, info, 32);
ASSERT_TRUE(derivedKey); ASSERT_TRUE(derivedKey);
optional<vector<uint8_t>> calculatedMac = optional<vector<uint8_t>> calculatedMac =
support::coseMac0(derivedKey.value(), {}, // payload support::coseMac0(derivedKey.value(), {}, // payload
encodedDeviceAuthentication); // detached content deviceAuthenticationBytes); // detached content
ASSERT_TRUE(calculatedMac); ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac); EXPECT_EQ(mac, calculatedMac);
} }