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

Commit f45b3cac authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update Identity Credential VTS tests." into rvc-dev

parents cda23ea3 d16d0fa6
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;
@@ -341,28 +343,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(
@@ -370,8 +350,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 "
@@ -478,9 +460,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