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

Commit c99762b2 authored by Guojing Yuan's avatar Guojing Yuan Committed by Android (Google) Code Review
Browse files

Merge "[AVF] Make patch level diff flexible by a requirement" into main

parents 58b11ca4 fe8e1b60
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -322,6 +322,10 @@ public class AttestationVerificationManager {
    /** Requirements bundle parameter for a challenge. */
    public static final String PARAM_CHALLENGE = "localbinding.challenge";

    /** Requirements bundle parameter for max patch level diff (int) for a peer device. **/
    public static final String PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS =
            "param_max_patch_level_diff_months";

    /** @hide */
    public static String localBindingTypeToString(@LocalBindingType int localBindingType) {
        final String text;
+3 −0
Original line number Diff line number Diff line
@@ -2,3 +2,6 @@

dlm@google.com
dkrahn@google.com
guojing@google.com
raphk@google.com
yukl@google.com
 No newline at end of file
+1 −2
Original line number Diff line number Diff line
@@ -53,9 +53,8 @@ public class AttestationVerifier {
     *
     * @param remoteAttestation the full certificate chain containing attestation extension.
     * @param attestationChallenge attestation challenge for authentication.
     * @return true if attestation is successfully verified; false otherwise.
     * @return 1 if attestation is successfully verified; 0 otherwise.
     */
    @NonNull
    public int verifyAttestation(
            @NonNull byte[] remoteAttestation,
            @NonNull byte[] attestationChallenge
+26 −24
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.security;

import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE;
import static android.security.attestationverification.AttestationVerificationManager.PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS;
import static android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
@@ -174,8 +175,8 @@ class AttestationVerificationPeerDeviceVerifier {

        MyDumpData dumpData = new MyDumpData();

        int result =
                verifyAttestationInternal(localBindingType, requirements, attestation, dumpData);
        int result = verifyAttestationInternal(localBindingType, requirements, attestation,
                dumpData);
        dumpData.mResult = result;
        mDumpLogger.logAttempt(dumpData);
        return result;
@@ -222,7 +223,8 @@ class AttestationVerificationPeerDeviceVerifier {
            final var attestationExtension = fromCertificate(leafCertificate);

            // Second: verify if the attestation satisfies the "peer device" profile.
            if (!checkAttestationForPeerDeviceProfile(attestationExtension, dumpData)) {
            if (!checkAttestationForPeerDeviceProfile(requirements, attestationExtension,
                    dumpData)) {
                failed = true;
            }

@@ -400,6 +402,7 @@ class AttestationVerificationPeerDeviceVerifier {
    }

    private boolean checkAttestationForPeerDeviceProfile(
            @NonNull Bundle requirements,
            @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes,
            MyDumpData dumpData) {
        boolean result = true;
@@ -461,30 +464,37 @@ class AttestationVerificationPeerDeviceVerifier {
            result = false;
        }

        // Patch level integer YYYYMM is expected to be within 1 year of today.
        if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel())) {
        int maxPatchLevelDiffMonths = requirements.getInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS,
                MAX_PATCH_AGE_MONTHS);

        // Patch level integer YYYYMM is expected to be within maxPatchLevelDiffMonths of today.
        if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel(),
                maxPatchLevelDiffMonths)) {
            debugVerboseLog("OS patch level is not within valid range.");
            result = false;
        } else {
            dumpData.mOsPatchLevelInRange = true;
        }

        // Patch level integer YYYYMMDD is expected to be within 1 year of today.
        if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
        // Patch level integer YYYYMMDD is expected to be within maxPatchLevelDiffMonths of today.
        if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel(),
                maxPatchLevelDiffMonths)) {
            debugVerboseLog("Boot patch level is not within valid range.");
            result = false;
        } else {
            dumpData.mKeyBootPatchLevelInRange = true;
        }

        if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel())) {
        if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel(),
                maxPatchLevelDiffMonths)) {
            debugVerboseLog("Vendor patch level is not within valid range.");
            result = false;
        } else {
            dumpData.mKeyVendorPatchLevelInRange = true;
        }

        if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
        if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel(),
                maxPatchLevelDiffMonths)) {
            debugVerboseLog("Boot patch level is not within valid range.");
            result = false;
        } else {
@@ -525,7 +535,7 @@ class AttestationVerificationPeerDeviceVerifier {
     * is not enough. Therefore, we also confirm the patch level for the remote and local device are
     * similar.
     */
    private boolean isValidPatchLevel(int patchLevel) {
    private boolean isValidPatchLevel(int patchLevel, int maxPatchLevelDiffMonths) {
        LocalDate currentDate = mTestSystemDate != null
                ? mTestSystemDate : LocalDate.now(ZoneId.systemDefault());

@@ -543,7 +553,9 @@ class AttestationVerificationPeerDeviceVerifier {
            return false;
        }

        // Check local patch date is not in last year of system clock.
        // Check local patch date is not in last year of system clock. If the local patch already
        // has a year's worth of bugs and vulnerabilities, it has no security meanings to check the
        // remote patch level.
        if (ChronoUnit.MONTHS.between(localPatchDate, currentDate) > MAX_PATCH_AGE_MONTHS) {
            return true;
        }
@@ -559,19 +571,9 @@ class AttestationVerificationPeerDeviceVerifier {
        int patchMonth = Integer.parseInt(remoteDeviceDateStr.substring(4, 6));
        LocalDate remotePatchDate = LocalDate.of(patchYear, patchMonth, 1);

        // Check patch dates are within 1 year of each other
        boolean IsRemotePatchWithinOneYearOfLocalPatch;
        if (remotePatchDate.compareTo(localPatchDate) > 0) {
            IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between(
                    localPatchDate, remotePatchDate) <= MAX_PATCH_AGE_MONTHS;
        } else if (remotePatchDate.compareTo(localPatchDate) < 0) {
            IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between(
                    remotePatchDate, localPatchDate) <= MAX_PATCH_AGE_MONTHS;
        } else {
            IsRemotePatchWithinOneYearOfLocalPatch = true;
        }

        return IsRemotePatchWithinOneYearOfLocalPatch;
        // Check patch dates are within the max patch level diff of each other
        return Math.abs(ChronoUnit.MONTHS.between(localPatchDate, remotePatchDate))
                <= maxPatchLevelDiffMonths;
    }

    /**
+36 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
import android.security.attestationverification.AttestationVerificationManager.PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS
import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
@@ -161,6 +162,41 @@ class AttestationVerificationPeerDeviceVerifierTest {
        assertThat(result).isEqualTo(RESULT_FAILURE)
    }

    @Test
    fun verifyAttestation_returnsSuccessPatchDataWithinMaxPatchDiff() {
        val verifier = AttestationVerificationPeerDeviceVerifier(
            context, dumpLogger, trustAnchors, false, LocalDate.of(2023, 3, 1),
            LocalDate.of(2023, 2, 1)
        )
        val challengeRequirements = Bundle()
        challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
        challengeRequirements.putInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS, 24)

        val result = verifier.verifyAttestation(
            TYPE_CHALLENGE, challengeRequirements,
            TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
        )
        assertThat(result).isEqualTo(RESULT_SUCCESS)
    }

    @Test
    fun verifyAttestation_returnsFailurePatchDataNotWithinMaxPatchDiff() {
        val verifier = AttestationVerificationPeerDeviceVerifier(
            context, dumpLogger, trustAnchors, false, LocalDate.of(2024, 10, 1),
            LocalDate.of(2024, 9, 1)
        )
        val challengeRequirements = Bundle()
        challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
        challengeRequirements.putInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS, 24)

        val result = verifier.verifyAttestation(
            TYPE_CHALLENGE, challengeRequirements,
            // The patch date of this file is early 2022
            TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
        )
        assertThat(result).isEqualTo(RESULT_FAILURE)
    }

    @Test
    fun verifyAttestation_returnsFailureTrustedAnchorEmpty() {
        val verifier = AttestationVerificationPeerDeviceVerifier(