Identity: Update for changes to ISO 18013-5.
Key derivation for session encryption and MACing now involves mixing in SessionTranscriptBytes. Update docs, default implementation, and VTS tests to reflect this. Also, the standard changed such that instead of DeviceAuthentication being MACed or signed, it's instead DeviceAuthenticationBytes which is defined as #6.24(bstr .cbor DeviceAuthentication). The same also for ReaderAuthentication, now ReaderAuthenticationBytes is the CBOR which is signed by the reader. Also update the URL for CDDL since it's now a published RFC. Bug: 159482543 Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Change-Id: I73fc7eb48ffb71e00a8b54849266ed814295fa39
This commit is contained in:
parent
780f8c860d
commit
2e4533e5c1
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