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:
commit
6223c5fbf3
5 changed files with 79 additions and 44 deletions
|
@ -151,8 +151,8 @@ interface IIdentityCredential {
|
|||
* IntentToRetain = bool
|
||||
*
|
||||
* 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
|
||||
* CBOR conforming to the following CDDL:
|
||||
* must be a COSE_Sign1 where the payload is the bytes of the
|
||||
* ReaderAuthenticationBytes CBOR defined below:
|
||||
*
|
||||
* ReaderAuthentication = [
|
||||
* "ReaderAuthentication",
|
||||
|
@ -164,6 +164,8 @@ interface IIdentityCredential {
|
|||
*
|
||||
* 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
|
||||
* '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
|
||||
|
@ -278,7 +280,7 @@ interface IIdentityCredential {
|
|||
*
|
||||
* @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.
|
||||
* and the detached content is set to DeviceAuthenticationBytes as defined below.
|
||||
* This code is produced by using the key agreement and key derivation function
|
||||
* from the ciphersuite with the authentication private key and the reader
|
||||
* ephemeral public key to compute a shared message authentication code (MAC)
|
||||
|
@ -299,6 +301,8 @@ interface IIdentityCredential {
|
|||
*
|
||||
* DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
|
||||
*
|
||||
* DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
|
||||
*
|
||||
* where
|
||||
*
|
||||
* DeviceNameSpaces = {
|
||||
|
|
|
@ -99,7 +99,7 @@ import android.hardware.identity.CipherSuite;
|
|||
* 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
|
||||
* 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
|
||||
* STATUS_* integers defined in this interface. Each method states which status can be returned
|
||||
|
|
|
@ -39,6 +39,10 @@ using ::std::optional;
|
|||
using namespace ::android::hardware::identity;
|
||||
|
||||
int IdentityCredential::initialize() {
|
||||
if (credentialData_.size() == 0) {
|
||||
LOG(ERROR) << "CredentialData is empty";
|
||||
return IIdentityCredentialStore::STATUS_INVALID_DATA;
|
||||
}
|
||||
auto [item, _, message] = cppbor::parse(credentialData_);
|
||||
if (item == nullptr) {
|
||||
LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
|
||||
|
@ -312,13 +316,16 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
|
|||
}
|
||||
|
||||
const vector<uint8_t>& itemsRequestBytes = itemsRequest;
|
||||
vector<uint8_t> dataThatWasSigned = cppbor::Array()
|
||||
.add("ReaderAuthentication")
|
||||
.add(sessionTranscriptItem_->clone())
|
||||
.add(cppbor::Semantic(24, itemsRequestBytes))
|
||||
.encode();
|
||||
vector<uint8_t> encodedReaderAuthentication =
|
||||
cppbor::Array()
|
||||
.add("ReaderAuthentication")
|
||||
.add(sessionTranscriptItem_->clone())
|
||||
.add(cppbor::Semantic(24, itemsRequestBytes))
|
||||
.encode();
|
||||
vector<uint8_t> encodedReaderAuthenticationBytes =
|
||||
cppbor::Semantic(24, encodedReaderAuthentication).encode();
|
||||
if (!support::coseCheckEcDsaSignature(readerSignature,
|
||||
dataThatWasSigned, // detached content
|
||||
encodedReaderAuthenticationBytes, // detached content
|
||||
readerPublicKey.value())) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
|
||||
|
@ -774,7 +781,7 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
|
|||
array.add(sessionTranscriptItem_->clone());
|
||||
array.add(docType_);
|
||||
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());
|
||||
optional<vector<uint8_t>> signingKey =
|
||||
|
@ -792,17 +799,24 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
|
|||
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> 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) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error deriving key from shared secret"));
|
||||
}
|
||||
|
||||
mac = support::coseMac0(derivedKey.value(), {}, // payload
|
||||
encodedDeviceAuthentication); // additionalData
|
||||
mac = support::coseMac0(derivedKey.value(), {}, // payload
|
||||
deviceAuthenticationBytes); // detached content
|
||||
if (!mac) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
|
||||
|
|
|
@ -289,16 +289,19 @@ void ReaderAuthTests::retrieveData(const vector<uint8_t>& readerPrivateKey,
|
|||
.add("Accessible by None", false)))
|
||||
.encode();
|
||||
}
|
||||
vector<uint8_t> dataToSign = cppbor::Array()
|
||||
.add("ReaderAuthentication")
|
||||
.add(sessionTranscript.clone())
|
||||
.add(cppbor::Semantic(24, itemsRequestBytes))
|
||||
.encode();
|
||||
vector<uint8_t> encodedReaderAuthentication =
|
||||
cppbor::Array()
|
||||
.add("ReaderAuthentication")
|
||||
.add(sessionTranscript.clone())
|
||||
.add(cppbor::Semantic(24, itemsRequestBytes))
|
||||
.encode();
|
||||
vector<uint8_t> encodedReaderAuthenticationBytes =
|
||||
cppbor::Semantic(24, encodedReaderAuthentication).encode();
|
||||
|
||||
optional<vector<uint8_t>> readerSignature =
|
||||
support::coseSignEcDsa(readerPrivateKey, // private key for reader
|
||||
{}, // content
|
||||
dataToSign, // detached content
|
||||
support::coseSignEcDsa(readerPrivateKey, // private key for reader
|
||||
{}, // content
|
||||
encodedReaderAuthenticationBytes, // detached content
|
||||
support::certificateChainJoin(readerCertChain));
|
||||
ASSERT_TRUE(readerSignature);
|
||||
|
||||
|
@ -528,17 +531,20 @@ TEST_P(ReaderAuthTests, ephemeralKeyNotInSessionTranscript) {
|
|||
.add("Accessible by C", false)
|
||||
.add("Accessible by None", false)))
|
||||
.encode();
|
||||
vector<uint8_t> dataToSign = cppbor::Array()
|
||||
.add("ReaderAuthentication")
|
||||
.add(sessionTranscript.clone())
|
||||
.add(cppbor::Semantic(24, itemsRequestBytes))
|
||||
.encode();
|
||||
vector<uint8_t> encodedReaderAuthentication =
|
||||
cppbor::Array()
|
||||
.add("ReaderAuthentication")
|
||||
.add(sessionTranscript.clone())
|
||||
.add(cppbor::Semantic(24, itemsRequestBytes))
|
||||
.encode();
|
||||
vector<uint8_t> encodedReaderAuthenticationBytes =
|
||||
cppbor::Semantic(24, encodedReaderAuthentication).encode();
|
||||
|
||||
vector<vector<uint8_t>> readerCertChain = {cert_reader_SelfSigned_};
|
||||
optional<vector<uint8_t>> readerSignature =
|
||||
support::coseSignEcDsa(readerPrivateKey_, // private key for reader
|
||||
{}, // content
|
||||
dataToSign, // detached content
|
||||
support::coseSignEcDsa(readerPrivateKey_, // private key for reader
|
||||
{}, // content
|
||||
encodedReaderAuthenticationBytes, // detached content
|
||||
support::certificateChainJoin(readerCertChain));
|
||||
ASSERT_TRUE(readerSignature);
|
||||
|
||||
|
|
|
@ -319,7 +319,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
|||
cppbor::Array sessionTranscript = cppbor::Array()
|
||||
.add(cppbor::Semantic(24, deviceEngagementBytes))
|
||||
.add(cppbor::Semantic(24, eReaderPubBytes));
|
||||
vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
|
||||
vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
|
||||
|
||||
vector<uint8_t> itemsRequestBytes =
|
||||
cppbor::Map("nameSpaces",
|
||||
|
@ -347,14 +347,17 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
|||
" },\n"
|
||||
"}",
|
||||
cborPretty);
|
||||
vector<uint8_t> dataToSign = cppbor::Array()
|
||||
.add("ReaderAuthentication")
|
||||
.add(sessionTranscript.clone())
|
||||
.add(cppbor::Semantic(24, itemsRequestBytes))
|
||||
.encode();
|
||||
vector<uint8_t> encodedReaderAuthentication =
|
||||
cppbor::Array()
|
||||
.add("ReaderAuthentication")
|
||||
.add(sessionTranscript.clone())
|
||||
.add(cppbor::Semantic(24, itemsRequestBytes))
|
||||
.encode();
|
||||
vector<uint8_t> encodedReaderAuthenticationBytes =
|
||||
cppbor::Semantic(24, encodedReaderAuthentication).encode();
|
||||
optional<vector<uint8_t>> readerSignature =
|
||||
support::coseSignEcDsa(readerKey, {}, // content
|
||||
dataToSign, // detached content
|
||||
support::coseSignEcDsa(readerKey, {}, // content
|
||||
encodedReaderAuthenticationBytes, // detached content
|
||||
readerCertificate.value());
|
||||
ASSERT_TRUE(readerSignature);
|
||||
|
||||
|
@ -388,7 +391,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
|||
credential->setVerificationToken(verificationToken);
|
||||
ASSERT_TRUE(credential
|
||||
->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
|
||||
signingKeyBlob, sessionTranscriptBytes,
|
||||
signingKeyBlob, sessionTranscriptEncoded,
|
||||
readerSignature.value(), testEntriesEntryCounts)
|
||||
.isOk());
|
||||
|
||||
|
@ -432,7 +435,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
|||
" },\n"
|
||||
"}",
|
||||
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
|
||||
cppbor::Array deviceAuthentication;
|
||||
deviceAuthentication.add("DeviceAuthentication");
|
||||
|
@ -441,7 +444,8 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
|||
string docType = "org.iso.18013-5.2019.mdl";
|
||||
deviceAuthentication.add(docType);
|
||||
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.
|
||||
optional<vector<uint8_t>> readerEphemeralPrivateKey =
|
||||
|
@ -449,13 +453,20 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
|
|||
optional<vector<uint8_t>> sharedSecret =
|
||||
support::ecdh(signingPubKey.value(), readerEphemeralPrivateKey.value());
|
||||
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> 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);
|
||||
optional<vector<uint8_t>> calculatedMac =
|
||||
support::coseMac0(derivedKey.value(), {}, // payload
|
||||
encodedDeviceAuthentication); // detached content
|
||||
support::coseMac0(derivedKey.value(), {}, // payload
|
||||
deviceAuthenticationBytes); // detached content
|
||||
ASSERT_TRUE(calculatedMac);
|
||||
EXPECT_EQ(mac, calculatedMac);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue