Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17ec80b6 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Update Identity Credential VTS tests."

parents 91521ab8 ef739512
Loading
Loading
Loading
Loading
+11 −18
Original line number Diff line number Diff line
@@ -160,17 +160,10 @@ interface IIdentityCredential {
     *       ItemsRequestBytes
     *     ]
     *
     *     SessionTranscript = [
     *       DeviceEngagementBytes,
     *       EReaderKeyBytes
     *     ]
     *     SessionTranscript = any
     *
     *     DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
     *     EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
     *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
     *
     *     EReaderKey.Pub = COSE_Key    ; Ephemeral public key provided by reader
     *
     * 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
@@ -184,8 +177,12 @@ interface IIdentityCredential {
     *
     * 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
     * somewhere in the bytes of the CBOR. Each of these coordinates must appear encoded
     * with the most significant bits first and use the exact amount of bits indicated by
     * the key size of the ephemeral keys. For example, if the ephemeral key is using the
     * P-256 curve then the 32 bytes for the X coordinate encoded with the most significant
     * bits first must appear somewhere in the CBOR and ditto for the 32 bytes for the Y
     * coordinate. If this is not satisfied, the call fails with
     * STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
     *
     * @param accessControlProfiles
@@ -298,13 +295,8 @@ interface IIdentityCredential {
     *
     *        DocType = tstr
     *
     *        SessionTranscript = [
     *            DeviceEngagementBytes,
     *            EReaderKeyBytes
     *        ]
     *        SessionTranscript = any
     *
     *        DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
     *        EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
     *        DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
     *
     *    where
@@ -356,8 +348,9 @@ interface IIdentityCredential {
     *
     *  - subjectPublicKeyInfo: must contain attested public key.
     *
     * @param out signingKeyBlob contains an encrypted copy of the newly-generated private
     *     signing key.
     * @param out signingKeyBlob contains an AES-GCM-ENC(storageKey, R, signingKey, docType)
     *     where signingKey is an EC private key in uncompressed form. That is, the returned
     *     blob is an encrypted copy of the newly-generated private signing key.
     *
     * @return an X.509 certificate for the new signing key, signed by the credential key.
     */
+22 −2
Original line number Diff line number Diff line
@@ -29,9 +29,27 @@ interface IWritableIdentityCredential {
     * Gets the certificate chain for credentialKey which can be used to prove the hardware
     * characteristics to an issuing authority.  Must not be called more than once.
     *
     * The following non-optional fields for the X.509 certificate shall be set as follows:
     *
     *  - version: INTEGER 2 (means v3 certificate).
     *
     *  - serialNumber: INTEGER 1 (fixed value: same on all certs).
     *
     *  - signature: must be set to ECDSA.
     *
     *  - subject: CN shall be set to "Android Identity Credential Key".
     *
     *  - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
     *    values returned in HardwareInformation.
     *
     *  - validity: should be from current time and expire at the same time as the
     *    attestation batch certificate used.
     *
     *  - subjectPublicKeyInfo: must contain attested public key.
     *
     * The certificate chain must be generated using Keymaster Attestation
     * (see https://source.android.com/security/keystore/attestation) with the
     * following additional requirements:
     * following additional requirements on the data in the attestation extension:
     *
     *  - The attestationVersion field in the attestation extension must be at least 3.
     *
@@ -109,7 +127,8 @@ interface IWritableIdentityCredential {
     *     in Tag::ATTESTATION_APPLICATION_ID. This schema is described in
     *     https://developer.android.com/training/articles/security-key-attestation#certificate_schema_attestationid
     *
     * @param attestationChallenge a challenge set by the issuer to ensure freshness.
     * @param attestationChallenge a challenge set by the issuer to ensure freshness. If
     *    this is empty, the call fails with STATUS_INVALID_DATA.
     *
     * @return the X.509 certificate chain for the credentialKey
     */
@@ -250,6 +269,7 @@ interface IWritableIdentityCredential {
     *         CredentialKeys = [
     *              bstr,   ; storageKey, a 128-bit AES key
     *              bstr    ; credentialPrivKey, the private key for credentialKey
     *                      ; in uncompressed form
     *         ]
     *
     * @param out proofOfProvisioningSignature proves to the IA that the credential was imported
+9 −26
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge
    }

    *outChallenge = challenge;
    authChallenge_ = challenge;
    return ndk::ScopedAStatus::ok();
}

@@ -223,7 +224,8 @@ bool checkUserAuthentication(const SecureAccessControlProfile& profile,
        }

        if (authToken.challenge != int64_t(authChallenge)) {
            LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
            LOG(ERROR) << "Challenge in authToken (" << uint64_t(authToken.challenge) << ") "
                       << "doesn't match the challenge we created (" << authChallenge << ")";
            return false;
        }
        return true;
@@ -337,28 +339,6 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
    //
    // We do this by just searching for the X and Y coordinates.
    if (sessionTranscript.size() > 0) {
        const cppbor::Array* array = sessionTranscriptItem_->asArray();
        if (array == nullptr || array->size() != 2) {
            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) {
            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) {
            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) {
            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
@@ -366,8 +346,10 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
                    "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)) {
            !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
                     ePubX.size()) != nullptr &&
              memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
                     ePubY.size()) != nullptr)) {
            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                    IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
                    "Did not find ephemeral public key's X and Y coordinates in "
@@ -474,9 +456,10 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
    }

    // Validate all the access control profiles in the requestData.
    bool haveAuthToken = (authToken.mac.size() > 0);
    bool haveAuthToken = (authToken.timestamp.milliSeconds != int64_t(0));
    for (const auto& profile : accessControlProfiles) {
        if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
            LOG(ERROR) << "Error checking MAC for profile";
            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                    IIdentityCredentialStore::STATUS_INVALID_DATA,
                    "Error checking MAC for profile"));
+11 −0
Original line number Diff line number Diff line
@@ -65,6 +65,10 @@ ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
                IIdentityCredentialStore::STATUS_FAILED,
                "Error attestation certificate previously generated"));
    }
    if (attestationChallenge.empty()) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
    }

    vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
    vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
@@ -165,6 +169,13 @@ ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
                "userAuthenticationRequired is false but timeout is non-zero"));
    }

    // If |userAuthenticationRequired| is true, then |secureUserId| must be non-zero.
    if (userAuthenticationRequired && secureUserId == 0) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_INVALID_DATA,
                "userAuthenticationRequired is true but secureUserId is zero"));
    }

    profile.id = id;
    profile.readerCertificate = readerCertificate;
    profile.userAuthenticationRequired = userAuthenticationRequired;
+3 −0
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@ cc_test {
        "VtsIdentityTestUtils.cpp",
        "VtsAttestationTests.cpp",
        "VtsAttestationParserSupport.cpp",
        "UserAuthTests.cpp",
        "ReaderAuthTests.cpp",
    ],
    shared_libs: [
        "android.hardware.keymaster@4.0",
@@ -18,6 +20,7 @@ cc_test {
        "libkeymaster_portable",
        "libsoft_attestation_cert",
        "libpuresoftkeymasterdevice",
        "android.hardware.keymaster-ndk_platform",
    ],
    static_libs: [
        "libcppbor",
Loading