From e9dc30a7c79da47af6c2423f282d7bab7a47e494 Mon Sep 17 00:00:00 2001 From: Cassandra Comar Date: Sun, 23 Apr 2023 00:15:49 -0400 Subject: [PATCH 1/6] pull attest. server url from res/values/strings this allows the attestation server url to be overriden at AOSP/GrapheneOS build time. --- .../auditor/AttestationActivity.java | 11 +++++++---- .../attestation/auditor/RemoteVerifyJob.java | 19 ++++++++++++++----- .../attestation/auditor/SubmitSampleJob.java | 7 +++++-- app/src/main/res/values/strings.xml | 1 + 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/app/attestation/auditor/AttestationActivity.java b/app/src/main/java/app/attestation/auditor/AttestationActivity.java index c84cec43a..97c9430b0 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationActivity.java +++ b/app/src/main/java/app/attestation/auditor/AttestationActivity.java @@ -52,8 +52,6 @@ public class AttestationActivity extends AppCompatActivity { private static final String TAG = "AttestationActivity"; - private static final String TUTORIAL_URL = "https://" + RemoteVerifyJob.DOMAIN + "/tutorial"; - private static final String STATE_AUDITEE_PAIRING = "auditee_pairing"; private static final String STATE_AUDITEE_SERIALIZED_ATTESTATION = "auditee_serialized_attestation"; private static final String STATE_AUDITOR_CHALLENGE = "auditor_challenge"; @@ -114,7 +112,8 @@ private enum Stage { stage = Stage.None; Log.d(TAG, "account: " + contents); final String[] values = contents.split(" "); - if (values.length < 4 || !RemoteVerifyJob.DOMAIN.equals(values[0])) { + final String domain = getString(R.string.url); + if (values.length < 4 || !domain.equals(values[0])) { snackbar.setText(R.string.scanned_invalid_account_qr_code).show(); return; } @@ -507,6 +506,10 @@ public boolean onPrepareOptionsMenu(final Menu menu) { return true; } + final String tutorial_url() { + return "https://" + getString(R.string.url) + "/tutorial"; + } + @Override @SuppressLint("InlinedApi") public boolean onOptionsItemSelected(final MenuItem item) { @@ -584,7 +587,7 @@ public boolean onOptionsItemSelected(final MenuItem item) { } return true; } else if (itemId == R.id.action_help) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(TUTORIAL_URL))); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(tutorial_url()))); return true; } return super.onOptionsItemSelected(item); diff --git a/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java b/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java index fc4a66e99..25aef0d1d 100644 --- a/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java +++ b/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java @@ -39,9 +39,6 @@ public class RemoteVerifyJob extends JobService { private static final String TAG = "RemoteVerifyJob"; private static final int PERIODIC_JOB_ID = 0; private static final int FIRST_RUN_JOB_ID = 1; - static final String DOMAIN = "attestation.app"; - private static final String CHALLENGE_URL = "https://" + DOMAIN + "/challenge"; - private static final String VERIFY_URL = "https://" + DOMAIN + "/verify"; private static final int CONNECT_TIMEOUT = 60000; private static final int READ_TIMEOUT = 60000; private static final int DEFAULT_INTERVAL = 4 * 60 * 60; @@ -127,6 +124,18 @@ static void cancel(final Context context) { scheduler.cancel(FIRST_RUN_JOB_ID); } + final String domain() { + return getString(R.string.url); + } + + final String challenge_url() { + return "https://" + domain() + "/challenge"; + } + + final String verify_url() { + return "https://" + domain() + "/verify"; + } + @Override public boolean onStartJob(final JobParameters params) { task = executor.submit(() -> { @@ -135,7 +144,7 @@ public boolean onStartJob(final JobParameters params) { HttpURLConnection connection = null; String exceptionMessage = null; try { - connection = (HttpURLConnection) new URL(CHALLENGE_URL).openConnection(); + connection = (HttpURLConnection) new URL(challenge_url()).openConnection(); connection.setConnectTimeout(CONNECT_TIMEOUT); connection.setReadTimeout(READ_TIMEOUT); connection.setRequestMethod("POST"); @@ -158,7 +167,7 @@ public boolean onStartJob(final JobParameters params) { final AttestationResult result = AttestationProtocol.generateSerialized( context, challengeMessage, Long.toString(userId), STATE_PREFIX); - connection = (HttpURLConnection) new URL(VERIFY_URL).openConnection(); + connection = (HttpURLConnection) new URL(verify_url()).openConnection(); connection.setConnectTimeout(CONNECT_TIMEOUT); connection.setReadTimeout(READ_TIMEOUT); connection.setDoOutput(true); diff --git a/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java b/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java index f2a7bb696..63c42ff26 100644 --- a/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java +++ b/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java @@ -41,7 +41,6 @@ public class SubmitSampleJob extends JobService { private static final String TAG = "SubmitSampleJob"; private static final int JOB_ID = 2; - private static final String SUBMIT_URL = "https://" + RemoteVerifyJob.DOMAIN + "/submit"; private static final int CONNECT_TIMEOUT = 60000; private static final int READ_TIMEOUT = 60000; private static final int ESTIMATED_DOWNLOAD_BYTES = 4 * 1024; @@ -70,12 +69,16 @@ static void schedule(final Context context) { } } + final String submit_url() { + return "https://" + getString(R.string.url) + "/submit"; + } + @Override public boolean onStartJob(final JobParameters params) { task = executor.submit(() -> { HttpURLConnection connection = null; try { - connection = (HttpURLConnection) new URL(SUBMIT_URL).openConnection(); + connection = (HttpURLConnection) new URL(submit_url()).openConnection(); connection.setConnectTimeout(CONNECT_TIMEOUT); connection.setReadTimeout(READ_TIMEOUT); connection.setDoOutput(true); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 30b3a932e..bfe36db98 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,6 @@ Auditor + attestation.app Two devices with Android 10 or higher are needed to perform verification:\n\n- The device to be verified (Auditee), which needs to be one of the supported devices.\n\n- An Android device to perform the verification (Auditor).\n\nThe verification process requires sending data between the devices by scanning QR codes. Device is not one of the supported models. Camera permission is required to scan QR codes. From e998314f1b407b7825af406ce618cc02ed40815b Mon Sep 17 00:00:00 2001 From: Cassandra Comar Date: Sun, 23 Apr 2023 09:29:33 -0400 Subject: [PATCH 2/6] dynamically look up device info and fingerprint when the fingerprint override resource is set at build time, dynamically look up the device info for the current device, checking that the AVB fingerprint matches the one the Auditor app was built with. uses a singleton to allow lazily-initialized lookups of the device name and override fingerprint in a static context. --- .../app/attestation/AttestationContext.java | 25 +++ .../auditor/AttestationActivity.java | 1 + .../auditor/AttestationProtocol.java | 155 +++++++++++++++++- app/src/main/res/values/strings.xml | 1 + 4 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/app/attestation/AttestationContext.java diff --git a/app/src/main/java/app/attestation/AttestationContext.java b/app/src/main/java/app/attestation/AttestationContext.java new file mode 100644 index 000000000..3c4d861a2 --- /dev/null +++ b/app/src/main/java/app/attestation/AttestationContext.java @@ -0,0 +1,25 @@ +package app.attestation.auditor; + +import android.content.Context; + +/// singleton so lazy-initialized statics can access Resources. +public class AttestationContext { + private static AttestationContext mInstance; + private Context context; + + static AttestationContext getInstance() { + if (mInstance == null) synchronized(AttestationContext.class) { + mInstance = new AttestationContext(); + } + + return mInstance; + } + + void initialize(Context context) { + this.context = context; + } + + Context activityContext() { + return context; + } +} diff --git a/app/src/main/java/app/attestation/auditor/AttestationActivity.java b/app/src/main/java/app/attestation/auditor/AttestationActivity.java index 97c9430b0..1d72ba05a 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationActivity.java +++ b/app/src/main/java/app/attestation/auditor/AttestationActivity.java @@ -247,6 +247,7 @@ private static boolean potentialSupportedAuditee() { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + AttestationContext.getInstance().initialize(AttestationActivity.this); binding = ActivityAttestationBinding.inflate(getLayoutInflater()); View rootView = binding.getRoot(); diff --git a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java index 27ebad3f4..03a3afb70 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java +++ b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java @@ -261,6 +261,149 @@ private static class DeviceInfo { final boolean enforceStrongBox; final int osName; + /// returns a map that provides dynamic lookup of attestationVersion, keymasterVersion, etc., based on the device name + /// -- lazily initialized to ensure the main activity has started first + private static final ImmutableMap deviceMap() { + final Resources res = AttestationContext.getInstance().activityContext().getResources(); + return ImmutableMap + .builder() + .put(deviceName(res, R.string.device_huawei), + new DeviceInfo(R.string.device_huawei, 2, 3, false, true, false, R.string.os)) + .put(deviceName(res, R.string.device_huawei_honor_7a_pro), + new DeviceInfo(R.string.device_huawei_honor_7a_pro, 2, 3, false, true, false, R.string.os)) + .put(deviceName(res, R.string.device_nokia), + new DeviceInfo(R.string.device_nokia, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_nokia_3_1), + new DeviceInfo(R.string.device_nokia_3_1, 2, 3, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_nokia_7_1), + new DeviceInfo(R.string.device_nokia_7_1, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_oneplus_6_a6003), + new DeviceInfo(R.string.device_oneplus_6_a6003, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_oneplus_6t_a6013), + new DeviceInfo(R.string.device_oneplus_6t_a6013, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_oneplus_7_pro_gm1913), + new DeviceInfo(R.string.device_oneplus_7_pro_gm1913, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_pixel_2), + new DeviceInfo(R.string.device_pixel_2, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_pixel_2_xl), + new DeviceInfo(R.string.device_pixel_2_xl, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_pixel_3_generic), + new DeviceInfo(R.string.device_pixel_3_generic, 3, 4, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_3a_generic), + new DeviceInfo(R.string.device_pixel_3a_generic, 3, 4, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_4_generic), + new DeviceInfo(R.string.device_pixel_4_generic, 3, 4, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_4a), + new DeviceInfo(R.string.device_pixel_4a, 3, 4, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_5_generic), + new DeviceInfo(R.string.device_pixel_5_generic, 3, 4, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_5a), + new DeviceInfo(R.string.device_pixel_5a, 3, 4, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_6), + new DeviceInfo(R.string.device_pixel_6, 100, 100, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_6_pro), + new DeviceInfo(R.string.device_pixel_6_pro, 100, 100, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_6a), + new DeviceInfo(R.string.device_pixel_6a, 100, 100, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_pixel_7), + new DeviceInfo(R.string.device_pixel_7, 200, 200, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_pixel_7_pro), + new DeviceInfo(R.string.device_pixel_7_pro, 200, 200, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_a705fn), + new DeviceInfo(R.string.device_sm_a705fn, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_g960f), + new DeviceInfo(R.string.device_sm_g960f, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_g960_na), + new DeviceInfo(R.string.device_sm_g960_na, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_g9600), + new DeviceInfo(R.string.device_sm_g9600, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_g965f), + new DeviceInfo(R.string.device_sm_g965f, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_g965_msm), + new DeviceInfo(R.string.device_sm_g965_msm, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_g970f), + new DeviceInfo(R.string.device_sm_g970f, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_g975f), + new DeviceInfo(R.string.device_sm_g975f, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_j260a), + new DeviceInfo(R.string.device_sm_j260a, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_j260f), + new DeviceInfo(R.string.device_sm_j260f, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_j260t1), + new DeviceInfo(R.string.device_sm_j260t1, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_j337a), + new DeviceInfo(R.string.device_sm_j337a, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_j337t), + new DeviceInfo(R.string.device_sm_j337t, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_j720f), + new DeviceInfo(R.string.device_sm_j720f, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_j737t1), + new DeviceInfo(R.string.device_sm_j737t1, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_m205f), + new DeviceInfo(R.string.device_sm_m205f, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_n960f), + new DeviceInfo(R.string.device_sm_n960f, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_n960u), + new DeviceInfo(R.string.device_sm_n960u, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_n970f), + new DeviceInfo(R.string.device_sm_n970f, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_n970u), + new DeviceInfo(R.string.device_sm_n970u, 3, 4, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_sm_n975u), + new DeviceInfo(R.string.device_sm_n975u, 3, 4, false /* uses new API */, true, true, R.string.os)) + .put(deviceName(res, R.string.device_sm_s367vl), + new DeviceInfo(R.string.device_sm_s367vl, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_t510), + new DeviceInfo(R.string.device_sm_t510, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sm_t835), + new DeviceInfo(R.string.device_sm_t835, 1, 2, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_sony_xperia_xa2), + new DeviceInfo(R.string.device_sony_xperia_xa2, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sony_xperia_xz1), + new DeviceInfo(R.string.device_sony_xperia_xz1, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sony_xperia_xz2), + new DeviceInfo(R.string.device_sony_xperia_xz2, 2, 3, false, true, false, R.string.os)) + .put(deviceName(res, R.string.device_sony_xperia_xz2_compact), + new DeviceInfo(R.string.device_sony_xperia_xz2_compact, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_blackberry_key2), + new DeviceInfo(R.string.device_blackberry_key2, 2, 3, true, true, false, R.string.os)) + .put(deviceName(res, R.string.device_bq_aquaris_x2_pro), + new DeviceInfo(R.string.device_bq_aquaris_x2_pro, 2, 3, true, false, false, R.string.os)) + .put(deviceName(res, R.string.device_xiaomi_mi_a2), + new DeviceInfo(R.string.device_xiaomi_mi_a2, 2, 3, true, false, false, R.string.os)) + .put(deviceName(res, R.string.device_xiaomi_mi_a2_lite), + new DeviceInfo(R.string.device_xiaomi_mi_a2_lite, 2, 3, true, false, false, R.string.os)) + .put(deviceName(res, R.string.device_xiaomi_mi_9), + new DeviceInfo(R.string.device_xiaomi_mi_9, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_htc), + new DeviceInfo(R.string.device_htc, 2, 3, true, false, false, R.string.os)) + .put(deviceName(res, R.string.device_moto_g7), + new DeviceInfo(R.string.device_moto_g7, 3, 4, false /* uses new API */, true, false, R.string.os)) + .put(deviceName(res, R.string.device_motorola_one_vision), + new DeviceInfo(R.string.device_motorola_one_vision, 2, 3, false, true, false, R.string.os)) + .put(deviceName(res, R.string.device_vivo_1807), + new DeviceInfo(R.string.device_vivo_1807, 2, 3, true, false, false, R.string.os)) + .put(deviceName(res, R.string.device_revvl_2), + new DeviceInfo(R.string.device_revvl_2, 2, 3, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_oppo_cph1831), + new DeviceInfo(R.string.device_oppo_cph1831, 2, 3, true, false, false, R.string.os)) + .put(deviceName(res, R.string.device_oppo_cph1903), + new DeviceInfo(R.string.device_oppo_cph1903, 2, 3, true, false, false, R.string.os)) + .put(deviceName(res, R.string.device_oppo_cph1909), + new DeviceInfo(R.string.device_oppo_cph1909, 2, 3, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_lg_q710al), + new DeviceInfo(R.string.device_lg_q710al, 2, 3, false, false, false, R.string.os)) + .put(deviceName(res, R.string.device_lm_q720), + new DeviceInfo(R.string.device_lm_q720, 3, 4, false /* uses new API */, false, false, R.string.os)) + .put(deviceName(res, R.string.device_rmx1941), + new DeviceInfo(R.string.device_rmx1941, 2, 3, false, true, false, R.string.os)) + .build(); + } + + private static final String deviceName(final Resources res, final int device) { + return res.getString(device); + } + DeviceInfo(final int name, final int attestationVersion, final int keymasterVersion, final boolean rollbackResistant, final boolean perUserEncryption, final boolean enforceStrongBox, final int osName) { @@ -709,7 +852,17 @@ private static Verified verifyStateless(final Certificate[] certificates, final String verifiedBootKey = BaseEncoding.base16().encode(rootOfTrust.getVerifiedBootKey()); final DeviceInfo device; if (verifiedBootState == RootOfTrust.KM_VERIFIED_BOOT_SELF_SIGNED) { - if (attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { + final Resources res = AttestationContext.getInstance().activityContext().getResources(); + final String overridenFingerprint = res.getString(R.string.avb_fingerprint_override); + if (verifiedBootKey.equals(overridenFingerprint)) { + // the fingerprint has been overriden at build time so we need to dynamically look up the device characteristics + DeviceInfo partialDevice = DeviceInfo.deviceMap().get(res.getString(R.string.device)); + if(attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { + device = new DeviceInfo(partialDevice.name, partialDevice.attestationVersion, partialDevice.keymasterVersion, partialDevice.rollbackResistant, partialDevice.perUserEncryption, true, partialDevice.osName); + } else { + device = new DeviceInfo(partialDevice.name, partialDevice.attestationVersion, partialDevice.keymasterVersion, partialDevice.rollbackResistant, partialDevice.perUserEncryption, false, partialDevice.osName); + } + } else if (attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { device = fingerprintsStrongBoxCustomOS.get(verifiedBootKey); } else { device = fingerprintsCustomOS.get(verifiedBootKey); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bfe36db98..5063fb0ac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ Auditor attestation.app + NOT OVERRIDEN Two devices with Android 10 or higher are needed to perform verification:\n\n- The device to be verified (Auditee), which needs to be one of the supported devices.\n\n- An Android device to perform the verification (Auditor).\n\nThe verification process requires sending data between the devices by scanning QR codes. Device is not one of the supported models. Camera permission is required to scan QR codes. From d65b9b168d0c810399f29fda8f9578b594917966 Mon Sep 17 00:00:00 2001 From: Cassandra Comar Date: Sun, 23 Apr 2023 10:23:09 -0400 Subject: [PATCH 3/6] use new properties for device name and os name --- .../auditor/AttestationProtocol.java | 132 +++++++++--------- app/src/main/res/values/strings.xml | 2 + 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java index 03a3afb70..aa7a0c23d 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java +++ b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java @@ -268,135 +268,135 @@ private static final ImmutableMap deviceMap() { return ImmutableMap .builder() .put(deviceName(res, R.string.device_huawei), - new DeviceInfo(R.string.device_huawei, 2, 3, false, true, false, R.string.os)) + new DeviceInfo(R.string.device_huawei, 2, 3, false, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_huawei_honor_7a_pro), - new DeviceInfo(R.string.device_huawei_honor_7a_pro, 2, 3, false, true, false, R.string.os)) + new DeviceInfo(R.string.device_huawei_honor_7a_pro, 2, 3, false, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_nokia), - new DeviceInfo(R.string.device_nokia, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_nokia, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_nokia_3_1), - new DeviceInfo(R.string.device_nokia_3_1, 2, 3, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_nokia_3_1, 2, 3, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_nokia_7_1), - new DeviceInfo(R.string.device_nokia_7_1, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_nokia_7_1, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_oneplus_6_a6003), - new DeviceInfo(R.string.device_oneplus_6_a6003, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_oneplus_6_a6003, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_oneplus_6t_a6013), - new DeviceInfo(R.string.device_oneplus_6t_a6013, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_oneplus_6t_a6013, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_oneplus_7_pro_gm1913), - new DeviceInfo(R.string.device_oneplus_7_pro_gm1913, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_oneplus_7_pro_gm1913, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_2), - new DeviceInfo(R.string.device_pixel_2, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_pixel_2, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_2_xl), - new DeviceInfo(R.string.device_pixel_2_xl, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_pixel_2_xl, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_3_generic), - new DeviceInfo(R.string.device_pixel_3_generic, 3, 4, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_3_generic, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_3a_generic), - new DeviceInfo(R.string.device_pixel_3a_generic, 3, 4, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_3a_generic, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_4_generic), - new DeviceInfo(R.string.device_pixel_4_generic, 3, 4, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_4_generic, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_4a), - new DeviceInfo(R.string.device_pixel_4a, 3, 4, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_4a, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_5_generic), - new DeviceInfo(R.string.device_pixel_5_generic, 3, 4, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_5_generic, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_5a), - new DeviceInfo(R.string.device_pixel_5a, 3, 4, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_5a, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_6), - new DeviceInfo(R.string.device_pixel_6, 100, 100, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_6, 100, 100, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_6_pro), - new DeviceInfo(R.string.device_pixel_6_pro, 100, 100, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_6_pro, 100, 100, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_6a), - new DeviceInfo(R.string.device_pixel_6a, 100, 100, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_pixel_6a, 100, 100, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_7), - new DeviceInfo(R.string.device_pixel_7, 200, 200, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_pixel_7, 200, 200, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_pixel_7_pro), - new DeviceInfo(R.string.device_pixel_7_pro, 200, 200, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_pixel_7_pro, 200, 200, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_a705fn), - new DeviceInfo(R.string.device_sm_a705fn, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_sm_a705fn, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_g960f), - new DeviceInfo(R.string.device_sm_g960f, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_g960f, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_g960_na), - new DeviceInfo(R.string.device_sm_g960_na, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_g960_na, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_g9600), - new DeviceInfo(R.string.device_sm_g9600, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_g9600, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_g965f), - new DeviceInfo(R.string.device_sm_g965f, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_g965f, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_g965_msm), - new DeviceInfo(R.string.device_sm_g965_msm, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_g965_msm, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_g970f), - new DeviceInfo(R.string.device_sm_g970f, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_sm_g970f, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_g975f), - new DeviceInfo(R.string.device_sm_g975f, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_sm_g975f, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_j260a), - new DeviceInfo(R.string.device_sm_j260a, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_j260a, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_j260f), - new DeviceInfo(R.string.device_sm_j260f, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_j260f, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_j260t1), - new DeviceInfo(R.string.device_sm_j260t1, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_j260t1, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_j337a), - new DeviceInfo(R.string.device_sm_j337a, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_j337a, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_j337t), - new DeviceInfo(R.string.device_sm_j337t, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_j337t, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_j720f), - new DeviceInfo(R.string.device_sm_j720f, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_j720f, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_j737t1), - new DeviceInfo(R.string.device_sm_j737t1, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_j737t1, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_m205f), - new DeviceInfo(R.string.device_sm_m205f, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_m205f, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_n960f), - new DeviceInfo(R.string.device_sm_n960f, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_n960f, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_n960u), - new DeviceInfo(R.string.device_sm_n960u, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_n960u, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_n970f), - new DeviceInfo(R.string.device_sm_n970f, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_sm_n970f, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_n970u), - new DeviceInfo(R.string.device_sm_n970u, 3, 4, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_sm_n970u, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_n975u), - new DeviceInfo(R.string.device_sm_n975u, 3, 4, false /* uses new API */, true, true, R.string.os)) + new DeviceInfo(R.string.device_sm_n975u, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_s367vl), - new DeviceInfo(R.string.device_sm_s367vl, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_s367vl, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_t510), - new DeviceInfo(R.string.device_sm_t510, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_sm_t510, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sm_t835), - new DeviceInfo(R.string.device_sm_t835, 1, 2, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_sm_t835, 1, 2, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sony_xperia_xa2), - new DeviceInfo(R.string.device_sony_xperia_xa2, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_sony_xperia_xa2, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sony_xperia_xz1), - new DeviceInfo(R.string.device_sony_xperia_xz1, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_sony_xperia_xz1, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sony_xperia_xz2), - new DeviceInfo(R.string.device_sony_xperia_xz2, 2, 3, false, true, false, R.string.os)) + new DeviceInfo(R.string.device_sony_xperia_xz2, 2, 3, false, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_sony_xperia_xz2_compact), - new DeviceInfo(R.string.device_sony_xperia_xz2_compact, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_sony_xperia_xz2_compact, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_blackberry_key2), - new DeviceInfo(R.string.device_blackberry_key2, 2, 3, true, true, false, R.string.os)) + new DeviceInfo(R.string.device_blackberry_key2, 2, 3, true, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_bq_aquaris_x2_pro), - new DeviceInfo(R.string.device_bq_aquaris_x2_pro, 2, 3, true, false, false, R.string.os)) + new DeviceInfo(R.string.device_bq_aquaris_x2_pro, 2, 3, true, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_xiaomi_mi_a2), - new DeviceInfo(R.string.device_xiaomi_mi_a2, 2, 3, true, false, false, R.string.os)) + new DeviceInfo(R.string.device_xiaomi_mi_a2, 2, 3, true, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_xiaomi_mi_a2_lite), - new DeviceInfo(R.string.device_xiaomi_mi_a2_lite, 2, 3, true, false, false, R.string.os)) + new DeviceInfo(R.string.device_xiaomi_mi_a2_lite, 2, 3, true, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_xiaomi_mi_9), - new DeviceInfo(R.string.device_xiaomi_mi_9, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_xiaomi_mi_9, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_htc), - new DeviceInfo(R.string.device_htc, 2, 3, true, false, false, R.string.os)) + new DeviceInfo(R.string.device_htc, 2, 3, true, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_moto_g7), - new DeviceInfo(R.string.device_moto_g7, 3, 4, false /* uses new API */, true, false, R.string.os)) + new DeviceInfo(R.string.device_moto_g7, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_motorola_one_vision), - new DeviceInfo(R.string.device_motorola_one_vision, 2, 3, false, true, false, R.string.os)) + new DeviceInfo(R.string.device_motorola_one_vision, 2, 3, false, true, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_vivo_1807), - new DeviceInfo(R.string.device_vivo_1807, 2, 3, true, false, false, R.string.os)) + new DeviceInfo(R.string.device_vivo_1807, 2, 3, true, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_revvl_2), - new DeviceInfo(R.string.device_revvl_2, 2, 3, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_revvl_2, 2, 3, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_oppo_cph1831), - new DeviceInfo(R.string.device_oppo_cph1831, 2, 3, true, false, false, R.string.os)) + new DeviceInfo(R.string.device_oppo_cph1831, 2, 3, true, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_oppo_cph1903), - new DeviceInfo(R.string.device_oppo_cph1903, 2, 3, true, false, false, R.string.os)) + new DeviceInfo(R.string.device_oppo_cph1903, 2, 3, true, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_oppo_cph1909), - new DeviceInfo(R.string.device_oppo_cph1909, 2, 3, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_oppo_cph1909, 2, 3, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_lg_q710al), - new DeviceInfo(R.string.device_lg_q710al, 2, 3, false, false, false, R.string.os)) + new DeviceInfo(R.string.device_lg_q710al, 2, 3, false, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_lm_q720), - new DeviceInfo(R.string.device_lm_q720, 3, 4, false /* uses new API */, false, false, R.string.os)) + new DeviceInfo(R.string.device_lm_q720, 3, 4, false /* uses new API */, false, false, R.string.os_name_override)) .put(deviceName(res, R.string.device_rmx1941), - new DeviceInfo(R.string.device_rmx1941, 2, 3, false, true, false, R.string.os)) + new DeviceInfo(R.string.device_rmx1941, 2, 3, false, true, false, R.string.os_name_override)) .build(); } @@ -856,7 +856,7 @@ private static Verified verifyStateless(final Certificate[] certificates, final String overridenFingerprint = res.getString(R.string.avb_fingerprint_override); if (verifiedBootKey.equals(overridenFingerprint)) { // the fingerprint has been overriden at build time so we need to dynamically look up the device characteristics - DeviceInfo partialDevice = DeviceInfo.deviceMap().get(res.getString(R.string.device)); + DeviceInfo partialDevice = DeviceInfo.deviceMap().get(res.getString(R.string.device_name)); if(attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { device = new DeviceInfo(partialDevice.name, partialDevice.attestationVersion, partialDevice.keymasterVersion, partialDevice.rollbackResistant, partialDevice.perUserEncryption, true, partialDevice.osName); } else { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5063fb0ac..59beb927e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,6 +2,8 @@ Auditor attestation.app NOT OVERRIDEN + UNUSED + UNUSED Two devices with Android 10 or higher are needed to perform verification:\n\n- The device to be verified (Auditee), which needs to be one of the supported devices.\n\n- An Android device to perform the verification (Auditor).\n\nThe verification process requires sending data between the devices by scanning QR codes. Device is not one of the supported models. Camera permission is required to scan QR codes. From ed79aa3c45aa6b731ac06fdf881cf444e9281862 Mon Sep 17 00:00:00 2001 From: Cassandra Comar Date: Wed, 26 Apr 2023 11:44:57 -0400 Subject: [PATCH 4/6] remove AttestationContext and fix naming we're actually already passing the Context around so there's no need for a singleton. --- .../app/attestation/AttestationContext.java | 25 ------------------- .../auditor/AttestationActivity.java | 9 +++---- .../auditor/AttestationProtocol.java | 16 ++++++------ .../attestation/auditor/RemoteVerifyJob.java | 10 ++++---- .../attestation/auditor/SubmitSampleJob.java | 6 ++--- app/src/main/res/values/strings.xml | 2 +- 6 files changed, 21 insertions(+), 47 deletions(-) delete mode 100644 app/src/main/java/app/attestation/AttestationContext.java diff --git a/app/src/main/java/app/attestation/AttestationContext.java b/app/src/main/java/app/attestation/AttestationContext.java deleted file mode 100644 index 3c4d861a2..000000000 --- a/app/src/main/java/app/attestation/AttestationContext.java +++ /dev/null @@ -1,25 +0,0 @@ -package app.attestation.auditor; - -import android.content.Context; - -/// singleton so lazy-initialized statics can access Resources. -public class AttestationContext { - private static AttestationContext mInstance; - private Context context; - - static AttestationContext getInstance() { - if (mInstance == null) synchronized(AttestationContext.class) { - mInstance = new AttestationContext(); - } - - return mInstance; - } - - void initialize(Context context) { - this.context = context; - } - - Context activityContext() { - return context; - } -} diff --git a/app/src/main/java/app/attestation/auditor/AttestationActivity.java b/app/src/main/java/app/attestation/auditor/AttestationActivity.java index 1d72ba05a..13a3bebca 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationActivity.java +++ b/app/src/main/java/app/attestation/auditor/AttestationActivity.java @@ -112,7 +112,7 @@ private enum Stage { stage = Stage.None; Log.d(TAG, "account: " + contents); final String[] values = contents.split(" "); - final String domain = getString(R.string.url); + final String domain = getString(R.string.base_domain); if (values.length < 4 || !domain.equals(values[0])) { snackbar.setText(R.string.scanned_invalid_account_qr_code).show(); return; @@ -247,7 +247,6 @@ private static boolean potentialSupportedAuditee() { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - AttestationContext.getInstance().initialize(AttestationActivity.this); binding = ActivityAttestationBinding.inflate(getLayoutInflater()); View rootView = binding.getRoot(); @@ -507,8 +506,8 @@ public boolean onPrepareOptionsMenu(final Menu menu) { return true; } - final String tutorial_url() { - return "https://" + getString(R.string.url) + "/tutorial"; + final String tutorialUrl() { + return "https://" + getString(R.string.base_domain) + "/tutorial"; } @Override @@ -588,7 +587,7 @@ public boolean onOptionsItemSelected(final MenuItem item) { } return true; } else if (itemId == R.id.action_help) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(tutorial_url()))); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(tutorialUrl()))); return true; } return super.onOptionsItemSelected(item); diff --git a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java index aa7a0c23d..7a26920a0 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java +++ b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java @@ -262,9 +262,8 @@ private static class DeviceInfo { final int osName; /// returns a map that provides dynamic lookup of attestationVersion, keymasterVersion, etc., based on the device name - /// -- lazily initialized to ensure the main activity has started first - private static final ImmutableMap deviceMap() { - final Resources res = AttestationContext.getInstance().activityContext().getResources(); + /// -- lazily initialized to ensure app context is available. + private static final ImmutableMap deviceMap(final Resources res) { return ImmutableMap .builder() .put(deviceName(res, R.string.device_huawei), @@ -769,7 +768,7 @@ private static X509Certificate generateCertificate(final Resources resources, fi private static Verified verifyStateless(final Certificate[] certificates, final byte[] challenge, final boolean hasPersistentKey, final Certificate root0, - final Certificate root1, final Certificate root2) throws GeneralSecurityException { + final Certificate root1, final Certificate root2, final Resources res) throws GeneralSecurityException { verifyCertificateSignatures(certificates, hasPersistentKey); @@ -852,11 +851,10 @@ private static Verified verifyStateless(final Certificate[] certificates, final String verifiedBootKey = BaseEncoding.base16().encode(rootOfTrust.getVerifiedBootKey()); final DeviceInfo device; if (verifiedBootState == RootOfTrust.KM_VERIFIED_BOOT_SELF_SIGNED) { - final Resources res = AttestationContext.getInstance().activityContext().getResources(); final String overridenFingerprint = res.getString(R.string.avb_fingerprint_override); if (verifiedBootKey.equals(overridenFingerprint)) { // the fingerprint has been overriden at build time so we need to dynamically look up the device characteristics - DeviceInfo partialDevice = DeviceInfo.deviceMap().get(res.getString(R.string.device_name)); + DeviceInfo partialDevice = DeviceInfo.deviceMap(res).get(res.getString(R.string.device_name)); if(attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { device = new DeviceInfo(partialDevice.name, partialDevice.attestationVersion, partialDevice.keymasterVersion, partialDevice.rollbackResistant, partialDevice.perUserEncryption, true, partialDevice.osName); } else { @@ -1196,7 +1194,8 @@ private static VerificationResult verify(final Context context, final byte[] fin final Verified verified = verifyStateless(attestationCertificates, challenge, hasPersistentKey, generateCertificate(context.getResources(), R.raw.google_root_0), generateCertificate(context.getResources(), R.raw.google_root_1), - generateCertificate(context.getResources(), R.raw.google_root_2)); + generateCertificate(context.getResources(), R.raw.google_root_2), + context.getResources()); final StringBuilder teeEnforced = new StringBuilder(); final StringBuilder history = new StringBuilder(); @@ -1662,7 +1661,8 @@ static AttestationResult generateSerialized(final Context context, final byte[] final Verified verified = verifyStateless(attestationCertificates, challenge, hasPersistentKey, generateCertificate(context.getResources(), R.raw.google_root_0), generateCertificate(context.getResources(), R.raw.google_root_1), - generateCertificate(context.getResources(), R.raw.google_root_2)); + generateCertificate(context.getResources(), R.raw.google_root_2), + context.getResources()); // OS-enforced checks and information diff --git a/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java b/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java index 25aef0d1d..59e08a930 100644 --- a/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java +++ b/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java @@ -125,14 +125,14 @@ static void cancel(final Context context) { } final String domain() { - return getString(R.string.url); + return getString(R.string.base_domain); } - final String challenge_url() { + final String challengeUrl() { return "https://" + domain() + "/challenge"; } - final String verify_url() { + final String verifyUrl() { return "https://" + domain() + "/verify"; } @@ -144,7 +144,7 @@ public boolean onStartJob(final JobParameters params) { HttpURLConnection connection = null; String exceptionMessage = null; try { - connection = (HttpURLConnection) new URL(challenge_url()).openConnection(); + connection = (HttpURLConnection) new URL(challengeUrl()).openConnection(); connection.setConnectTimeout(CONNECT_TIMEOUT); connection.setReadTimeout(READ_TIMEOUT); connection.setRequestMethod("POST"); @@ -167,7 +167,7 @@ public boolean onStartJob(final JobParameters params) { final AttestationResult result = AttestationProtocol.generateSerialized( context, challengeMessage, Long.toString(userId), STATE_PREFIX); - connection = (HttpURLConnection) new URL(verify_url()).openConnection(); + connection = (HttpURLConnection) new URL(verifyUrl()).openConnection(); connection.setConnectTimeout(CONNECT_TIMEOUT); connection.setReadTimeout(READ_TIMEOUT); connection.setDoOutput(true); diff --git a/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java b/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java index 63c42ff26..e8d81e3b0 100644 --- a/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java +++ b/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java @@ -69,8 +69,8 @@ static void schedule(final Context context) { } } - final String submit_url() { - return "https://" + getString(R.string.url) + "/submit"; + final String submitUrl() { + return "https://" + getString(R.string.base_domain) + "/submit"; } @Override @@ -78,7 +78,7 @@ public boolean onStartJob(final JobParameters params) { task = executor.submit(() -> { HttpURLConnection connection = null; try { - connection = (HttpURLConnection) new URL(submit_url()).openConnection(); + connection = (HttpURLConnection) new URL(submitUrl()).openConnection(); connection.setConnectTimeout(CONNECT_TIMEOUT); connection.setReadTimeout(READ_TIMEOUT); connection.setDoOutput(true); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 59beb927e..b07ac73aa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ Auditor - attestation.app + attestation.app NOT OVERRIDEN UNUSED UNUSED From 68446c431f7fb2cb85d83549715a5154ae97271e Mon Sep 17 00:00:00 2001 From: Cassandra Comar Date: Thu, 27 Apr 2023 17:56:02 -0400 Subject: [PATCH 5/6] swap to xml maps for fingerprints this allows multiple devices to be configured the same even when they have different AVB fingerprints, and therefore should be able to validate each other. --- app/build.gradle.kts | 9 + .../auditor/ImmutableMapParserTest.java | 60 ++ .../auditor/AttestationActivity.java | 4 +- .../auditor/AttestationProtocol.java | 824 +++++++----------- .../auditor/ImmutableMapParser.java | 58 ++ .../attestation/auditor/RemoteVerifyJob.java | 10 +- .../attestation/auditor/SubmitSampleJob.java | 4 +- app/src/main/res/values/strings.xml | 3 - .../main/res/xml/fingerprints_graphene.xml | 207 +++++ .../xml/fingerprints_graphene_strongbox.xml | 183 ++++ app/src/main/res/xml/fingerprints_stock.xml | 783 +++++++++++++++++ .../res/xml/fingerprints_stock_strongbox.xml | 159 ++++ gradle/verification-metadata.xml | 395 +++++++++ 13 files changed, 2189 insertions(+), 510 deletions(-) create mode 100644 app/src/androidTest/java/app/attestation/auditor/ImmutableMapParserTest.java create mode 100644 app/src/main/java/app/attestation/auditor/ImmutableMapParser.java create mode 100644 app/src/main/res/xml/fingerprints_graphene.xml create mode 100644 app/src/main/res/xml/fingerprints_graphene_strongbox.xml create mode 100644 app/src/main/res/xml/fingerprints_stock.xml create mode 100644 app/src/main/res/xml/fingerprints_stock_strongbox.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 624211c10..fa6114bb3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -102,6 +102,9 @@ android { androidResources { noCompress += listOf("dex") } + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } } dependencies { @@ -121,4 +124,10 @@ dependencies { implementation("androidx.camera:camera-camera2:$cameraVersion") implementation("androidx.camera:camera-lifecycle:$cameraVersion") implementation("androidx.camera:camera-view:$cameraVersion") + + // unit tests + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test:runner:1.5.2") + androidTestImplementation("androidx.test:rules:1.5.0") + } diff --git a/app/src/androidTest/java/app/attestation/auditor/ImmutableMapParserTest.java b/app/src/androidTest/java/app/attestation/auditor/ImmutableMapParserTest.java new file mode 100644 index 000000000..e333055cd --- /dev/null +++ b/app/src/androidTest/java/app/attestation/auditor/ImmutableMapParserTest.java @@ -0,0 +1,60 @@ +package app.attestation.auditor; + +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import org.xmlpull.v1.XmlPullParserException; + +import android.app.Application; +import android.content.Context; +import androidx.test.platform.app.InstrumentationRegistry; + +public class ImmutableMapParserTest { + Context context; + + @Before + public void setUp() { + context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + } + + @Test + public void parseGrapheneResourceMap() throws IOException, XmlPullParserException { + var map = ImmutableMapParser.getImmutableMapResource(context, R.xml.fingerprints_graphene, "fingerprint", + "deviceInfo", + new AttestationProtocol.DeviceInfoParser()); + assertTrue( + map.get("B094E48B27C6E15661223CEFF539CF35E481DEB4E3250331E973AC2C15CAD6CD").name == R.string.device_pixel_2); + } + + @Test + public void parseGrapheneStrongBoxResourceMap() throws IOException, XmlPullParserException { + var map = ImmutableMapParser.getImmutableMapResource(context, R.xml.fingerprints_graphene_strongbox, + "fingerprint", + "deviceInfo", + new AttestationProtocol.DeviceInfoParser()); + assertTrue( + map.get("0F9A9CC8ADE73064A54A35C5509E77994E3AA37B6FB889DD53AF82C3C570C5CF").name == R.string.device_pixel_3); + } + + @Test + public void parseStockResourceMap() throws IOException, XmlPullParserException { + var map = ImmutableMapParser.getImmutableMapResource(context, R.xml.fingerprints_stock, "fingerprint", + "deviceInfo", + new AttestationProtocol.DeviceInfoParser()); + assertTrue( + map.get("5341E6B2646979A70E57653007A1F310169421EC9BDD9F1A5648F75ADE005AF1").name == R.string.device_huawei); + } + + @Test + public void parseStockStrongBoxResourceMap() throws IOException, XmlPullParserException { + var map = ImmutableMapParser.getImmutableMapResource(context, R.xml.fingerprints_stock_strongbox, + "fingerprint", + "deviceInfo", + new AttestationProtocol.DeviceInfoParser()); + assertTrue( + map.get("61FDA12B32ED84214A9CF13D1AFFB7AA80BD8A268A861ED4BB7A15170F1AB00C").name == R.string.device_pixel_3_generic); + } +} diff --git a/app/src/main/java/app/attestation/auditor/AttestationActivity.java b/app/src/main/java/app/attestation/auditor/AttestationActivity.java index 13a3bebca..31e81083f 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationActivity.java +++ b/app/src/main/java/app/attestation/auditor/AttestationActivity.java @@ -506,8 +506,8 @@ public boolean onPrepareOptionsMenu(final Menu menu) { return true; } - final String tutorialUrl() { - return "https://" + getString(R.string.base_domain) + "/tutorial"; + private String tutorialUrl() { + return getString(R.string.base_domain) + "/tutorial"; } @Override diff --git a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java index 7a26920a0..470d5a59f 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java +++ b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java @@ -10,6 +10,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.XmlResourceParser; import android.os.Build; import android.os.UserManager; import android.provider.Settings; @@ -30,6 +31,9 @@ import com.google.common.io.ByteStreams; import com.google.common.primitives.Bytes; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -81,7 +85,7 @@ import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK; import static androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS; -class AttestationProtocol { +public class AttestationProtocol { private static final String TAG = "AttestationProtocol"; // Developer previews set osVersion to 0 as a placeholder value. @@ -132,29 +136,41 @@ class AttestationProtocol { // byte[] challenge index (length: CHALLENGE_LENGTH) // byte[] challenge (length: CHALLENGE_LENGTH) // - // The challenge index is randomly generated by Auditor and used for all future challenge - // messages from that Auditor. It's used on the Auditee as an index to choose the correct - // persistent key to satisfy the Auditor, rather than only supporting pairing with one. In - // theory, the Auditor could authenticate to the Auditee, but this app already provides a - // better way to do that by doing the same process in reverse for a supported device. + // The challenge index is randomly generated by Auditor and used for all future + // challenge + // messages from that Auditor. It's used on the Auditee as an index to choose + // the correct + // persistent key to satisfy the Auditor, rather than only supporting pairing + // with one. In + // theory, the Auditor could authenticate to the Auditee, but this app already + // provides a + // better way to do that by doing the same process in reverse for a supported + // device. // - // The challenge is randomly generated by the Auditor and serves the security function of - // enforcing that the results are fresh. It's returned inside the attestation certificate - // which has a signature from the device's provisioned key (not usable by the OS) and the - // outer signature from the hardware-backed key generated for the initial pairing. + // The challenge is randomly generated by the Auditor and serves the security + // function of + // enforcing that the results are fresh. It's returned inside the attestation + // certificate + // which has a signature from the device's provisioned key (not usable by the + // OS) and the + // outer signature from the hardware-backed key generated for the initial + // pairing. // // Attestation message: // - // For backwards compatibility the Auditor device sends its maximum supported version, and + // For backwards compatibility the Auditor device sends its maximum supported + // version, and // the Auditee uses the highest version it supports. // - // Compression is done with raw DEFLATE (no zlib wrapper) with a preset dictionary generated from + // Compression is done with raw DEFLATE (no zlib wrapper) with a preset + // dictionary generated from // sample certificates. // // signed message { // byte version = min(maxVersion, PROTOCOL_VERSION) // short compressedChainLength - // byte[] compressedChain { [short encodedCertificateLength, byte[] encodedCertificate] } + // byte[] compressedChain { [short encodedCertificateLength, byte[] + // encodedCertificate] } // byte[] fingerprint (length: FINGERPRINT_LENGTH) // int osEnforcedFlags // } @@ -164,41 +180,65 @@ class AttestationProtocol { // // n/a // - // For each audit, the Auditee generates a fresh hardware-backed key with key attestation - // using the provided challenge. It reports back the certificate chain to be verified by the - // Auditor. The public key certificate of the generated key is signed by a key provisioned on - // the device (not usable by the OS) chaining up to an intermediate and the Google root. The - // certificate contains the key attestation metadata including the important fields with the - // lock state, verified boot state, the verified boot public key fingerprint and the OS + // For each audit, the Auditee generates a fresh hardware-backed key with key + // attestation + // using the provided challenge. It reports back the certificate chain to be + // verified by the + // Auditor. The public key certificate of the generated key is signed by a key + // provisioned on + // the device (not usable by the OS) chaining up to an intermediate and the + // Google root. The + // certificate contains the key attestation metadata including the important + // fields with the + // lock state, verified boot state, the verified boot public key fingerprint and + // the OS // version / patch level: // // https://developer.android.com/training/articles/security-key-attestation.html#certificate_schema // - // The Auditee keeps the first hardware-backed key generated for a challenge index and uses it - // to sign all future attestations. The fingerprint of the persistent key is included in the - // attestation message for the Auditor to find the corresponding pinning data. Other keys are + // The Auditee keeps the first hardware-backed key generated for a challenge + // index and uses it + // to sign all future attestations. The fingerprint of the persistent key is + // included in the + // attestation message for the Auditor to find the corresponding pinning data. + // Other keys are // never actually used, only generated for fresh key attestation data. // - // The OS can use the persistent generated hardware-backed key for signing but cannot obtain - // the private key. The key isn't be usable if verified boot fails or the OS is downgraded and - // the keys are protected against replay attacks via the Replay Protected Memory Block. - // Devices launching with Android P or later can provide a StrongBox Keymaster to support - // storing the keys in a dedicated hardware security module to substantially reduce the attack - // surface for obtaining the keys. StrongBox is paired with the TEE and the TEE corroborates - // the validity of the keys and attestation. The Pixel 3 and 3 XL are the first devices with a + // The OS can use the persistent generated hardware-backed key for signing but + // cannot obtain + // the private key. The key isn't be usable if verified boot fails or the OS is + // downgraded and + // the keys are protected against replay attacks via the Replay Protected Memory + // Block. + // Devices launching with Android P or later can provide a StrongBox Keymaster + // to support + // storing the keys in a dedicated hardware security module to substantially + // reduce the attack + // surface for obtaining the keys. StrongBox is paired with the TEE and the TEE + // corroborates + // the validity of the keys and attestation. The Pixel 3 and 3 XL are the first + // devices with a // StrongBox implementation via the Titan M security chip. // // https://android-developers.googleblog.com/2018/10/building-titan-better-security-through.html // - // The attestation message also includes osEnforcedFlags with data obtained at the OS level, - // which is vulnerable to tampering by an attacker with control over the OS. However, the OS - // did get verified by verified boot so without a verified boot bypass they would need to keep - // exploiting it after booting. The bootloader / TEE verified OS version / OS patch level are - // a useful mitigation as they reveal that the OS isn't upgraded even if an attacker has root. + // The attestation message also includes osEnforcedFlags with data obtained at + // the OS level, + // which is vulnerable to tampering by an attacker with control over the OS. + // However, the OS + // did get verified by verified boot so without a verified boot bypass they + // would need to keep + // exploiting it after booting. The bootloader / TEE verified OS version / OS + // patch level are + // a useful mitigation as they reveal that the OS isn't upgraded even if an + // attacker has root. // - // The Auditor saves the initial certificate chain, using the initial certificate to verify - // the outer signature and the rest of the chain for pinning the expected chain. It enforces - // downgrade protection for the OS version/patch (bootloader/TEE enforced) and app version (OS + // The Auditor saves the initial certificate chain, using the initial + // certificate to verify + // the outer signature and the rest of the chain for pinning the expected chain. + // It enforces + // downgrade protection for the OS version/patch (bootloader/TEE enforced) and + // app version (OS // enforced) by keeping them updated. private static final byte PROTOCOL_VERSION = 4; private static final byte PROTOCOL_VERSION_MINIMUM = 4; @@ -218,8 +258,7 @@ class AttestationProtocol { private static final int OS_ENFORCED_FLAGS_DEVICE_ADMIN_NON_SYSTEM = 1 << 7; private static final int OS_ENFORCED_FLAGS_OEM_UNLOCK_ALLOWED = 1 << 8; private static final int OS_ENFORCED_FLAGS_SYSTEM_USER = 1 << 9; - private static final int OS_ENFORCED_FLAGS_ALL = - OS_ENFORCED_FLAGS_USER_PROFILE_SECURE | + private static final int OS_ENFORCED_FLAGS_ALL = OS_ENFORCED_FLAGS_USER_PROFILE_SECURE | OS_ENFORCED_FLAGS_ACCESSIBILITY | OS_ENFORCED_FLAGS_DEVICE_ADMIN | OS_ENFORCED_FLAGS_ADB_ENABLED | @@ -233,12 +272,9 @@ class AttestationProtocol { private static final String AUDITOR_APP_PACKAGE_NAME_RELEASE = "app.attestation.auditor"; private static final String AUDITOR_APP_PACKAGE_NAME_PLAY = "app.attestation.auditor.play"; private static final String AUDITOR_APP_PACKAGE_NAME_DEBUG = "app.attestation.auditor.debug"; - private static final String AUDITOR_APP_SIGNATURE_DIGEST_RELEASE = - "990E04F0864B19F14F84E0E432F7A393F297AB105A22C1E1B10B442A4A62C42C"; - private static final String AUDITOR_APP_SIGNATURE_DIGEST_PLAY = - "075335BD7B54C965222B5284D2A1FDEF1198AE45EC7B09A4934287A0E3A243C7"; - private static final String AUDITOR_APP_SIGNATURE_DIGEST_DEBUG = - "17727D8B61D55A864936B1A7B4A2554A15151F32EBCF44CDAA6E6C3258231890"; + private static final String AUDITOR_APP_SIGNATURE_DIGEST_RELEASE = "990E04F0864B19F14F84E0E432F7A393F297AB105A22C1E1B10B442A4A62C42C"; + private static final String AUDITOR_APP_SIGNATURE_DIGEST_PLAY = "075335BD7B54C965222B5284D2A1FDEF1198AE45EC7B09A4934287A0E3A243C7"; + private static final String AUDITOR_APP_SIGNATURE_DIGEST_DEBUG = "17727D8B61D55A864936B1A7B4A2554A15151F32EBCF44CDAA6E6C3258231890"; private static final byte AUDITOR_APP_VARIANT_RELEASE = 0; private static final byte AUDITOR_APP_VARIANT_PLAY = 1; private static final byte AUDITOR_APP_VARIANT_DEBUG = 2; @@ -251,157 +287,108 @@ class AttestationProtocol { // Split displayed fingerprint into groups of 4 characters private static final int FINGERPRINT_SPLIT_INTERVAL = 4; - private static class DeviceInfo { - final int name; - final int attestationVersion; - final int keymasterVersion; - final boolean rollbackResistant; - final boolean perUserEncryption; - // enforce using StrongBox for new pairings - final boolean enforceStrongBox; - final int osName; + public static class DeviceInfoParser implements XmlMapElemParser { + + private static ImmutableMap getDeviceMap(Context context, int resMap) + throws IOException, XmlPullParserException { + return ImmutableMapParser.getImmutableMapResource(context, resMap, "fingerprint", "deviceInfo", + new DeviceInfoParser()); + } + + @Override + public String parseKey(Context context, XmlResourceParser parser) { + if (parser.getName().equals("fingerprint")) { + try { + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.TEXT) { + eventType = parser.next(); + } + + var key = parser.getText(); + Log.d("AttestationProtocol", "key: " + key); + return key; + } catch (IOException | XmlPullParserException e) { + throw new IllegalArgumentException("Invalid data in xml resource map: ", e); + } + } else { + throw new IllegalStateException("Trying to parse a key from an invalid node: " + parser.getName()); + } + } + + @Override + public DeviceInfo parseValue(Context context, XmlResourceParser parser) { + int name = 0, osName = 0, attestationVersion = 0, keymasterVersion = 0; + boolean rollbackResistant = false, perUserEncryption = false, enforceStrongBox = false; + + if (parser.getName().equals("deviceInfo")) { + try { + int eventType; + String tagName = "deviceInfo"; + + do { + eventType = parser.next(); + if (eventType == XmlPullParser.TEXT) { + Log.d("AttestationProtocol", "found tag text: " + parser.getText()); + switch (tagName) { + case "name": + name = context.getResources().getIdentifier(parser.getText(), "string", + context.getPackageName()); + break; + case "attestationVersion": + attestationVersion = Integer.parseInt(parser.getText()); + break; + case "keymasterVersion": + keymasterVersion = Integer.parseInt(parser.getText()); + break; + case "rollbackResistant": + rollbackResistant = Boolean.parseBoolean(parser.getText()); + break; + case "perUserEncryption": + perUserEncryption = Boolean.parseBoolean(parser.getText()); + break; + case "enforceStrongBox": + enforceStrongBox = Boolean.parseBoolean(parser.getText()); + break; + case "osName": + Log.d("AttestationProtocol", "osName: " + parser.getText()); + osName = context.getResources().getIdentifier(parser.getText(), "string", + context.getPackageName()); + break; + } + } else if (eventType == XmlPullParser.START_TAG || eventType == XmlPullParser.END_TAG) { + tagName = parser.getName(); + } + } while (eventType != XmlPullParser.END_TAG || !tagName.equals("deviceInfo")); + Log.d("AttestationProtocol", "finished parsing DeviceInfo"); + } catch (IOException | XmlPullParserException e) { + throw new IllegalArgumentException("Invalid data in xml resource map: ", e); + } + } else { + throw new IllegalStateException("Trying to parse DeviceInfo from an invalid node: " + parser.getName()); + } - /// returns a map that provides dynamic lookup of attestationVersion, keymasterVersion, etc., based on the device name - /// -- lazily initialized to ensure app context is available. - private static final ImmutableMap deviceMap(final Resources res) { - return ImmutableMap - .builder() - .put(deviceName(res, R.string.device_huawei), - new DeviceInfo(R.string.device_huawei, 2, 3, false, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_huawei_honor_7a_pro), - new DeviceInfo(R.string.device_huawei_honor_7a_pro, 2, 3, false, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_nokia), - new DeviceInfo(R.string.device_nokia, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_nokia_3_1), - new DeviceInfo(R.string.device_nokia_3_1, 2, 3, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_nokia_7_1), - new DeviceInfo(R.string.device_nokia_7_1, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_oneplus_6_a6003), - new DeviceInfo(R.string.device_oneplus_6_a6003, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_oneplus_6t_a6013), - new DeviceInfo(R.string.device_oneplus_6t_a6013, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_oneplus_7_pro_gm1913), - new DeviceInfo(R.string.device_oneplus_7_pro_gm1913, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_2), - new DeviceInfo(R.string.device_pixel_2, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_2_xl), - new DeviceInfo(R.string.device_pixel_2_xl, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_3_generic), - new DeviceInfo(R.string.device_pixel_3_generic, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_3a_generic), - new DeviceInfo(R.string.device_pixel_3a_generic, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_4_generic), - new DeviceInfo(R.string.device_pixel_4_generic, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_4a), - new DeviceInfo(R.string.device_pixel_4a, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_5_generic), - new DeviceInfo(R.string.device_pixel_5_generic, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_5a), - new DeviceInfo(R.string.device_pixel_5a, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_6), - new DeviceInfo(R.string.device_pixel_6, 100, 100, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_6_pro), - new DeviceInfo(R.string.device_pixel_6_pro, 100, 100, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_6a), - new DeviceInfo(R.string.device_pixel_6a, 100, 100, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_7), - new DeviceInfo(R.string.device_pixel_7, 200, 200, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_pixel_7_pro), - new DeviceInfo(R.string.device_pixel_7_pro, 200, 200, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_a705fn), - new DeviceInfo(R.string.device_sm_a705fn, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_g960f), - new DeviceInfo(R.string.device_sm_g960f, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_g960_na), - new DeviceInfo(R.string.device_sm_g960_na, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_g9600), - new DeviceInfo(R.string.device_sm_g9600, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_g965f), - new DeviceInfo(R.string.device_sm_g965f, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_g965_msm), - new DeviceInfo(R.string.device_sm_g965_msm, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_g970f), - new DeviceInfo(R.string.device_sm_g970f, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_g975f), - new DeviceInfo(R.string.device_sm_g975f, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_j260a), - new DeviceInfo(R.string.device_sm_j260a, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_j260f), - new DeviceInfo(R.string.device_sm_j260f, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_j260t1), - new DeviceInfo(R.string.device_sm_j260t1, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_j337a), - new DeviceInfo(R.string.device_sm_j337a, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_j337t), - new DeviceInfo(R.string.device_sm_j337t, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_j720f), - new DeviceInfo(R.string.device_sm_j720f, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_j737t1), - new DeviceInfo(R.string.device_sm_j737t1, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_m205f), - new DeviceInfo(R.string.device_sm_m205f, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_n960f), - new DeviceInfo(R.string.device_sm_n960f, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_n960u), - new DeviceInfo(R.string.device_sm_n960u, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_n970f), - new DeviceInfo(R.string.device_sm_n970f, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_n970u), - new DeviceInfo(R.string.device_sm_n970u, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_n975u), - new DeviceInfo(R.string.device_sm_n975u, 3, 4, false /* uses new API */, true, true, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_s367vl), - new DeviceInfo(R.string.device_sm_s367vl, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_t510), - new DeviceInfo(R.string.device_sm_t510, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sm_t835), - new DeviceInfo(R.string.device_sm_t835, 1, 2, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sony_xperia_xa2), - new DeviceInfo(R.string.device_sony_xperia_xa2, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sony_xperia_xz1), - new DeviceInfo(R.string.device_sony_xperia_xz1, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sony_xperia_xz2), - new DeviceInfo(R.string.device_sony_xperia_xz2, 2, 3, false, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_sony_xperia_xz2_compact), - new DeviceInfo(R.string.device_sony_xperia_xz2_compact, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_blackberry_key2), - new DeviceInfo(R.string.device_blackberry_key2, 2, 3, true, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_bq_aquaris_x2_pro), - new DeviceInfo(R.string.device_bq_aquaris_x2_pro, 2, 3, true, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_xiaomi_mi_a2), - new DeviceInfo(R.string.device_xiaomi_mi_a2, 2, 3, true, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_xiaomi_mi_a2_lite), - new DeviceInfo(R.string.device_xiaomi_mi_a2_lite, 2, 3, true, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_xiaomi_mi_9), - new DeviceInfo(R.string.device_xiaomi_mi_9, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_htc), - new DeviceInfo(R.string.device_htc, 2, 3, true, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_moto_g7), - new DeviceInfo(R.string.device_moto_g7, 3, 4, false /* uses new API */, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_motorola_one_vision), - new DeviceInfo(R.string.device_motorola_one_vision, 2, 3, false, true, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_vivo_1807), - new DeviceInfo(R.string.device_vivo_1807, 2, 3, true, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_revvl_2), - new DeviceInfo(R.string.device_revvl_2, 2, 3, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_oppo_cph1831), - new DeviceInfo(R.string.device_oppo_cph1831, 2, 3, true, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_oppo_cph1903), - new DeviceInfo(R.string.device_oppo_cph1903, 2, 3, true, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_oppo_cph1909), - new DeviceInfo(R.string.device_oppo_cph1909, 2, 3, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_lg_q710al), - new DeviceInfo(R.string.device_lg_q710al, 2, 3, false, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_lm_q720), - new DeviceInfo(R.string.device_lm_q720, 3, 4, false /* uses new API */, false, false, R.string.os_name_override)) - .put(deviceName(res, R.string.device_rmx1941), - new DeviceInfo(R.string.device_rmx1941, 2, 3, false, true, false, R.string.os_name_override)) - .build(); - } - - private static final String deviceName(final Resources res, final int device) { - return res.getString(device); - } + var deviceInfo = new DeviceInfo(name, attestationVersion, keymasterVersion, rollbackResistant, + perUserEncryption, + enforceStrongBox, osName); + Log.d("AttestationProtocol", + "DeviceInfo { name: " + deviceInfo.name + ", attestationVersion: " + attestationVersion + + ", keymasterVersion: " + keymasterVersion + ", rollbackResistant: " + rollbackResistant + + ", perUserEncryption: " + perUserEncryption + ", enforceStrongBox: " + enforceStrongBox + + ", osName: " + osName + " }"); + return deviceInfo; + } + + } + + public static class DeviceInfo { + public final int name; + public final int attestationVersion; + public final int keymasterVersion; + public final boolean rollbackResistant; + public final boolean perUserEncryption; + // enforce using StrongBox for new pairings + public final boolean enforceStrongBox; + public final int osName; DeviceInfo(final int name, final int attestationVersion, final int keymasterVersion, final boolean rollbackResistant, final boolean perUserEncryption, @@ -435,7 +422,8 @@ private static final String deviceName(final Resources res, final int device) { "SM-N970U", "SM-N975U").contains(Build.MODEL); - // Pixel 6, Pixel 6 Pro and Pixel 6a forgot to declare the attest key feature when it shipped in Android 12 + // Pixel 6, Pixel 6 Pro and Pixel 6a forgot to declare the attest key feature + // when it shipped in Android 12 private static final boolean alwaysHasAttestKey = ImmutableSet.of( "Pixel 6", "Pixel 6 Pro", @@ -450,242 +438,6 @@ private static final String deviceName(final Resources res, final int device) { R.string.device_sm_n975u, R.string.device_sm_t510); - private static final ImmutableMap fingerprintsCustomOS = ImmutableMap - .builder() - // GrapheneOS - .put("B094E48B27C6E15661223CEFF539CF35E481DEB4E3250331E973AC2C15CAD6CD", - new DeviceInfo(R.string.device_pixel_2, 2, 3, true, true, false, R.string.os_graphene)) - .put("B6851E9B9C0EBB7185420BD0E79D20A84CB15AB0B018505EFFAA4A72B9D9DAC7", - new DeviceInfo(R.string.device_pixel_2_xl, 2, 3, true, true, false, R.string.os_graphene)) - .put("0F9A9CC8ADE73064A54A35C5509E77994E3AA37B6FB889DD53AF82C3C570C5CF", - new DeviceInfo(R.string.device_pixel_3, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("06DD526EE9B1CB92AA19D9835B68B4FF1A48A3AD31D813F27C9A7D6C271E9451", - new DeviceInfo(R.string.device_pixel_3_xl, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("8FF8B9B4F831114963669E04EA4F849F33F3744686A0B33B833682746645ABC8", - new DeviceInfo(R.string.device_pixel_3a, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("91943FAA75DCB6392AE87DA18CA57D072BFFB80BC30F8FAFC7FFE13D76C5736E", - new DeviceInfo(R.string.device_pixel_3a_xl, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("80EF268700EE42686F779A47B4A155FE1FFC2EEDF836B4803CAAB8FA61439746", - new DeviceInfo(R.string.device_pixel_4, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("3F15FDCB82847FED97427CE00563B8F9FF34627070DE5FDB17ACA7849AB98CC8", - new DeviceInfo(R.string.device_pixel_4_xl, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("9F2454A1657B1B5AD7F2336B39A2611F7A40B2E0DDFD0D6553A359605928DF29", - new DeviceInfo(R.string.device_pixel_4a, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("DCEC2D053D3EC4F1C9BE414AA07E4D7D7CBD12040AD2F8831C994A83A0536866", - new DeviceInfo(R.string.device_pixel_4a_5g, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("36A99EAB7907E4FB12A70E3C41C456BCBE46C13413FBFE2436ADEE2B2B61120F", - new DeviceInfo(R.string.device_pixel_5, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("0ABDDEDA03B6CE10548C95E0BEA196FAA539866F929BCDF7ECA84B4203952514", - new DeviceInfo(R.string.device_pixel_5a, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("F0A890375D1405E62EBFD87E8D3F475F948EF031BBF9DDD516D5F600A23677E8", - new DeviceInfo(R.string.device_pixel_6, 100, 100, false /* uses new API */, true, true, R.string.os_graphene)) - .put("439B76524D94C40652CE1BF0D8243773C634D2F99BA3160D8D02AA5E29FF925C", - new DeviceInfo(R.string.device_pixel_6_pro, 100, 100, false /* uses new API */, true, true, R.string.os_graphene)) - .put("08C860350A9600692D10C8512F7B8E80707757468E8FBFEEA2A870C0A83D6031", - new DeviceInfo(R.string.device_pixel_6a, 100, 100, false /* uses new API */, true, true, R.string.os_graphene)) - .put("3EFE5392BE3AC38AFB894D13DE639E521675E62571A8A9B3EF9FC8C44FD17FA1", - new DeviceInfo(R.string.device_pixel_7, 200, 200, false /* uses new API */, true, false, R.string.os_graphene)) - .put("BC1C0DD95664604382BB888412026422742EB333071EA0B2D19036217D49182F", - new DeviceInfo(R.string.device_pixel_7_pro, 200, 200, false /* uses new API */, true, false, R.string.os_graphene)) - .build(); - private static final ImmutableMap fingerprintsStock = ImmutableMap - .builder() - .put("5341E6B2646979A70E57653007A1F310169421EC9BDD9F1A5648F75ADE005AF1", - new DeviceInfo(R.string.device_huawei, 2, 3, false, true, false, R.string.os_stock)) - .put("7E2E8CC82A77CA74554457E5DF3A3ED82E7032B3182D17FE17919BC6E989FF09", - new DeviceInfo(R.string.device_huawei_honor_7a_pro, 2, 3, false, true, false, R.string.os_stock)) - .put("DFC2920C81E136FDD2A510478FDA137B262DC51D449EDD7D0BDB554745725CFE", - new DeviceInfo(R.string.device_nokia, 2, 3, true, true, false, R.string.os_stock)) - .put("4D790FA0A5FE81D6B352B90AFE430684D9BC817518CD24C50E6343395F7C51F2", - new DeviceInfo(R.string.device_nokia_3_1, 2, 3, false, false, false, R.string.os_stock)) - .put("893A17FD918235DB2865F7F6439EB0134A45B766AA452E0675BAC6CFB5A773AA", - new DeviceInfo(R.string.device_nokia_7_1, 2, 3, true, true, false, R.string.os_stock)) - .put("6101853DFF451FAE5B137DF914D5E6C15C659337F2C405AC50B513A159071958", - new DeviceInfo(R.string.device_oneplus_6_a6003, 2, 3, true, true, false, R.string.os_stock)) - .put("1B90B7D1449D697FB2732A7D2DFA405D587254593F5137F7B6E64F7A0CE03BFD", - new DeviceInfo(R.string.device_oneplus_6t_a6013, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("4B9201B11685BE6710E2B2BA8482F444E237E0C8A3D1F7F447FE29C37CECC559", - new DeviceInfo(R.string.device_oneplus_7_pro_gm1913, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("1962B0538579FFCE9AC9F507C46AFE3B92055BAC7146462283C85C500BE78D82", - new DeviceInfo(R.string.device_pixel_2, 2, 3, true, true, false, R.string.os_stock)) - .put("171616EAEF26009FC46DC6D89F3D24217E926C81A67CE65D2E3A9DC27040C7AB", - new DeviceInfo(R.string.device_pixel_2_xl, 2, 3, true, true, false, R.string.os_stock)) - .put("61FDA12B32ED84214A9CF13D1AFFB7AA80BD8A268A861ED4BB7A15170F1AB00C", - new DeviceInfo(R.string.device_pixel_3_generic, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("E75B86C52C7496255A95FB1E2B1C044BFA9D5FE34DD1E4EEBD752EEF0EA89875", - new DeviceInfo(R.string.device_pixel_3a_generic, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("AE6316B4753C61F5855B95B9B98484AF784F2E83648D0FCC8107FCA752CAEA34", - new DeviceInfo(R.string.device_pixel_4_generic, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("879CD3F18EA76E244D4D4AC3BCB9C337C13B4667190B19035AFE2536550050F1", - new DeviceInfo(R.string.device_pixel_4a, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("88265D85BA9E1E2F6036A259D880D2741031ACA445840137395B6D541C0FC7FC", - new DeviceInfo(R.string.device_pixel_5_generic, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("1DD694CE00BF131AD61CEB576B7DCC41CF7F9B2C418F4C12B2B8F3E9A1EA911D", - new DeviceInfo(R.string.device_pixel_5a, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("0F6E75C80183B5DEC074B0054D4271E99389EBE4B136B0819DE1F150BA0FF9D7", - new DeviceInfo(R.string.device_pixel_6, 100, 100, false /* uses new API */, true, true, R.string.os_stock)) - .put("42ED1BCA352FABD428F34E8FCEE62776F4CB2C66E06F82E5A59FF4495267BFC2", - new DeviceInfo(R.string.device_pixel_6_pro, 100, 100, false /* uses new API */, true, true, R.string.os_stock)) - .put("9AC4174153D45E4545B0F49E22FE63273999B6AC1CB6949C3A9F03EC8807EEE9", - new DeviceInfo(R.string.device_pixel_6a, 100, 100, false /* uses new API */, true, true, R.string.os_stock)) - .put("8B2C4CD539F5075E8E7CF212ADB3DB0413FBD77D321199C73D5A473C51F2E10D", - new DeviceInfo(R.string.device_pixel_7, 200, 200, false /* uses new API */, true, false, R.string.os_stock)) - .put("26AC4C60BEB1E378357CAD0C3061347AF8DF6FBABBB0D8CEA2445855EE01E368", - new DeviceInfo(R.string.device_pixel_7_pro, 200, 200, false /* uses new API */, true, false, R.string.os_stock)) - .put("72376CAACF11726D4922585732429FB97D0D1DD69F0D2E0770B9E61D14ADDE65", - new DeviceInfo(R.string.device_sm_a705fn, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("33D9484FD512E610BCF00C502827F3D55A415088F276C6506657215E622FA770", - new DeviceInfo(R.string.device_sm_g960f, 1, 2, false, false, false, R.string.os_stock)) - .put("266869F7CF2FB56008EFC4BE8946C8F84190577F9CA688F59C72DD585E696488", - new DeviceInfo(R.string.device_sm_g960_na, 1, 2, false, false, false, R.string.os_stock)) - .put("12E8460A7BAF709F3B6CF41C7E5A37C6EB4D11CB36CF7F61F7793C8DCDC3C2E4", - new DeviceInfo(R.string.device_sm_g9600, 1, 2, false, false, false, R.string.os_stock)) - .put("D1C53B7A931909EC37F1939B14621C6E4FD19BF9079D195F86B3CEA47CD1F92D", - new DeviceInfo(R.string.device_sm_g965f, 1, 2, false, false, false, R.string.os_stock)) - .put("A4A544C2CFBAEAA88C12360C2E4B44C29722FC8DBB81392A6C1FAEDB7BF63010", - new DeviceInfo(R.string.device_sm_g965_msm, 1, 2, false, false, false, R.string.os_stock)) - .put("9D77474FA4FEA6F0B28636222FBCEE2BB1E6FF9856C736C85B8EA6E3467F2BBA", - new DeviceInfo(R.string.device_sm_g970f, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("08B2B5C6EC8F54C00C505756E1EF516BB4537B2F02D640410D287A43FCF92E3F", - new DeviceInfo(R.string.device_sm_g975f, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("F0FC0AF47D3FE4F27D79CF629AD6AC42AA1EEDE0A29C0AE109A91BBD1E7CD76D", - new DeviceInfo(R.string.device_sm_j260a, 1, 2, false, false, false, R.string.os_stock)) - .put("410102030405060708090001020304050607080900010203040506070809005A", - new DeviceInfo(R.string.device_sm_j260f, 1, 2, false, false, false, R.string.os_stock)) - .put("D6B902D9E77DFC0FB3627FFEFA6D05405932EBB3A6ED077874B5E2A0CCBDB632", - new DeviceInfo(R.string.device_sm_j260t1, 1, 2, false, false, false, R.string.os_stock)) - .put("4558C1AFB30D1B46CB93F85462BC7D7FCF70B0103B9DBB0FE96DD828F43F29FC", - new DeviceInfo(R.string.device_sm_j337a, 1, 2, false, false, false, R.string.os_stock)) - .put("45E3AB5D61A03915AE10BF0465B186CB5D9A2FB6A46BEFAA76E4483BBA5A358D", - new DeviceInfo(R.string.device_sm_j337t, 1, 2, false, false, false, R.string.os_stock)) - .put("D95279A8F2E832FD68D919DBF33CFE159D5A1179686DB0BD2D7BBBF2382C4DD3", - new DeviceInfo(R.string.device_sm_j720f, 1, 2, false, false, false, R.string.os_stock)) - .put("BB053A5F64D3E3F17C4611340FF2BBE2F605B832A9FA412B2C87F2A163ECE2FB", - new DeviceInfo(R.string.device_sm_j737t1, 1, 2, false, false, false, R.string.os_stock)) - .put("4E0570011025D01386D057B2B382969F804DCD19E001344535CF0CFDB8AD7CFE", - new DeviceInfo(R.string.device_sm_m205f, 1, 2, false, false, false, R.string.os_stock)) - .put("2A7E4954C9F703F3AC805AC660EA1727B981DB39B1E0F41E4013FA2586D3DF7F", - new DeviceInfo(R.string.device_sm_n960f, 1, 2, false, false, false, R.string.os_stock)) - .put("173ACFA8AE9EDE7BBD998F45A49231F3A4BDDF0779345732E309446B46B5641B", - new DeviceInfo(R.string.device_sm_n960u, 1, 2, false, false, false, R.string.os_stock)) - .put("E94BC43B97F98CD10C22CD9D8469DBE621116ECFA624FE291A1D53CF3CD685D1", - new DeviceInfo(R.string.device_sm_n970f, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("466011C44BBF883DB38CF96617ED35C796CE2552C5357F9230258329E943DB70", - new DeviceInfo(R.string.device_sm_n970u, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("52946676088007755EB586B3E3F3E8D3821BE5DF73513E6C13640507976420E6", - new DeviceInfo(R.string.device_sm_n975u, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("F3688C02D9676DEDB6909CADE364C271901FD66EA4F691AEB8B8921195E469C5", - new DeviceInfo(R.string.device_sm_s367vl, 1, 2, false, false, false, R.string.os_stock)) - .put("106592D051E54388C6E601DFD61D59EB1674A8B93216C65C5B3E1830B73D3B82", - new DeviceInfo(R.string.device_sm_t510, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("87790149AED63553B768456AAB6DAAD5678CD87BDEB2BF3649467085349C34E0", - new DeviceInfo(R.string.device_sm_t835, 1, 2, false, false, false, R.string.os_stock)) - .put("4285AD64745CC79B4499817F264DC16BF2AF5163AF6C328964F39E61EC84693E", - new DeviceInfo(R.string.device_sony_xperia_xa2, 2, 3, true, true, false, R.string.os_stock)) - .put("54A9F21E9CFAD3A2D028517EF333A658302417DB7FB75E0A109A019646CC5F39", - new DeviceInfo(R.string.device_sony_xperia_xz1, 2, 3, true, true, false, R.string.os_stock)) - .put("BC3B5E121974113939B8A2FE758F9B923F1D195F038D2FD1C04929F886E83BB5", - new DeviceInfo(R.string.device_sony_xperia_xz2, 2, 3, false, true, false, R.string.os_stock)) - .put("94B8B4E3260B4BF8211A02CF2F3DE257A127CFFB2E4047D5580A752A5E253DE0", - new DeviceInfo(R.string.device_sony_xperia_xz2_compact, 2, 3, true, true, false, R.string.os_stock)) - .put("728800FEBB119ADD74519618AFEDB715E1C39FE08A4DE37D249BF54ACF1CE00F", - new DeviceInfo(R.string.device_blackberry_key2, 2, 3, true, true, false, R.string.os_stock)) - .put("1194659B40EA291245E54A3C4EC4AA5B7077BD244D65C7DD8C0A2DBB9DB1FB35", - new DeviceInfo(R.string.device_bq_aquaris_x2_pro, 2, 3, true, false, false, R.string.os_stock)) - .put("A9C6758D509600D0EB94FA8D2BF6EE7A6A6097F0CCEF94A755DDE065AA1AA1B0", - new DeviceInfo(R.string.device_xiaomi_mi_a2, 2, 3, true, false, false, R.string.os_stock)) - .put("6FA710B639848C9D47378937A1AFB1B6A52DDA738BEB6657E2AE70A15B40541A", - new DeviceInfo(R.string.device_xiaomi_mi_a2_lite, 2, 3, true, false, false, R.string.os_stock)) - .put("84BC8445A29B5444A2D1629C9774C8626DAFF3574D865EC5067A78FAEC96B013", - new DeviceInfo(R.string.device_xiaomi_mi_9, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("1CC39488D2F85DEE0A8E0903CDC4124CFDF2BE2531ED6060B678057ED2CB89B4", - new DeviceInfo(R.string.device_htc, 2, 3, true, false, false, R.string.os_stock)) - .put("80BAB060807CFFA45D4747DF1AD706FEE3AE3F645F80CF14871DDBE27E14C30B", - new DeviceInfo(R.string.device_moto_g7, 3, 4, false /* uses new API */, true, false, R.string.os_stock)) - .put("C2224571C9CD5C89200A7311B1E37AA9CF751E2E19753E8D3702BCA00BE1D42C", - new DeviceInfo(R.string.device_motorola_one_vision, 2, 3, false, true, false, R.string.os_stock)) - .put("1F6D98D1B0E1F1CE1C872BD36C668F9DFDBE0D47594789E1540DF4E6198F657D", - new DeviceInfo(R.string.device_vivo_1807, 2, 3, true, false, false, R.string.os_stock)) - .put("C55635636999E9D0A0588D24402256B7F9F3AEE07B4F7E4E003F09FF0190AFAE", - new DeviceInfo(R.string.device_revvl_2, 2, 3, false, false, false, R.string.os_stock)) - .put("341C50D577DC5F3D5B46E8BFA22C22D1E5FC7D86D4D860E70B89222A7CBFC893", - new DeviceInfo(R.string.device_oppo_cph1831, 2, 3, true, false, false, R.string.os_stock)) - .put("41BF0A26BB3AFDCCCC40F7B685083522EB5BF1C492F0EC4847F351265313CB07", - new DeviceInfo(R.string.device_oppo_cph1903, 2, 3, true, false, false, R.string.os_stock)) - .put("7E19E217072BE6CB7A4C6F673FD3FB62DC51B3E204E7475838747947A3920DD8", - new DeviceInfo(R.string.device_oppo_cph1909, 2, 3, false, false, false, R.string.os_stock)) - .put("0D5F986943D0CE0D4F9783C27EEBE175BE359927DB8B6546B667279A81133C3C", - new DeviceInfo(R.string.device_lg_q710al, 2, 3, false, false, false, R.string.os_stock)) - .put("D20078F2AF2A7D3ECA3064018CB8BD47FBCA6EE61ABB41BA909D3C529CB802F4", - new DeviceInfo(R.string.device_lm_q720, 3, 4, false /* uses new API */, false, false, R.string.os_stock)) - .put("54EC644C21FD8229E3B0066513337A8E2C8EF3098A3F974B6A1CFE456A683DAE", - new DeviceInfo(R.string.device_rmx1941, 2, 3, false, true, false, R.string.os_stock)) - .build(); - - private static final ImmutableMap fingerprintsStrongBoxCustomOS = ImmutableMap - .builder() - // GrapheneOS - .put("0F9A9CC8ADE73064A54A35C5509E77994E3AA37B6FB889DD53AF82C3C570C5CF", - new DeviceInfo(R.string.device_pixel_3, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("06DD526EE9B1CB92AA19D9835B68B4FF1A48A3AD31D813F27C9A7D6C271E9451", - new DeviceInfo(R.string.device_pixel_3_xl, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("73D6C63A07610404FE16A4E07DD24E41A70D331E9D3EF7BBA2D087E4761EB63A", - new DeviceInfo(R.string.device_pixel_3a, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("3F36E3482E1FF82986576552CB4FD08AF09F8B09D3832314341E04C42D2919A4", - new DeviceInfo(R.string.device_pixel_3a_xl, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("80EF268700EE42686F779A47B4A155FE1FFC2EEDF836B4803CAAB8FA61439746", - new DeviceInfo(R.string.device_pixel_4, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("3F15FDCB82847FED97427CE00563B8F9FF34627070DE5FDB17ACA7849AB98CC8", - new DeviceInfo(R.string.device_pixel_4_xl, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("9F2454A1657B1B5AD7F2336B39A2611F7A40B2E0DDFD0D6553A359605928DF29", - new DeviceInfo(R.string.device_pixel_4a, 3, 4, false /* uses new API */, true, true, R.string.os_graphene)) - .put("DCEC2D053D3EC4F1C9BE414AA07E4D7D7CBD12040AD2F8831C994A83A0536866", - new DeviceInfo(R.string.device_pixel_4a_5g, 4, 41, false /* uses new API */, true, true, R.string.os_graphene)) - .put("36A99EAB7907E4FB12A70E3C41C456BCBE46C13413FBFE2436ADEE2B2B61120F", - new DeviceInfo(R.string.device_pixel_5, 4, 41, false /* uses new API */, true, true, R.string.os_graphene)) - .put("0ABDDEDA03B6CE10548C95E0BEA196FAA539866F929BCDF7ECA84B4203952514", - new DeviceInfo(R.string.device_pixel_5a, 4, 41, false /* uses new API */, true, true, R.string.os_graphene)) - .put("F0A890375D1405E62EBFD87E8D3F475F948EF031BBF9DDD516D5F600A23677E8", - new DeviceInfo(R.string.device_pixel_6, 100, 100, false /* uses new API */, true, true, R.string.os_graphene)) - .put("439B76524D94C40652CE1BF0D8243773C634D2F99BA3160D8D02AA5E29FF925C", - new DeviceInfo(R.string.device_pixel_6_pro, 100, 100, false /* uses new API */, true, true, R.string.os_graphene)) - .put("08C860350A9600692D10C8512F7B8E80707757468E8FBFEEA2A870C0A83D6031", - new DeviceInfo(R.string.device_pixel_6a, 100, 100, false /* uses new API */, true, true, R.string.os_graphene)) - .put("3EFE5392BE3AC38AFB894D13DE639E521675E62571A8A9B3EF9FC8C44FD17FA1", - new DeviceInfo(R.string.device_pixel_7, 100, 100, false /* uses new API */, true, false, R.string.os_graphene)) - .put("BC1C0DD95664604382BB888412026422742EB333071EA0B2D19036217D49182F", - new DeviceInfo(R.string.device_pixel_7_pro, 100, 100, false /* uses new API */, true, false, R.string.os_graphene)) - .build(); - private static final ImmutableMap fingerprintsStrongBoxStock = ImmutableMap - .builder() - .put("61FDA12B32ED84214A9CF13D1AFFB7AA80BD8A268A861ED4BB7A15170F1AB00C", - new DeviceInfo(R.string.device_pixel_3_generic, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("8CA89AF1A6DAA74B00810849356DE929CFC4498EF36AF964757BDE8A113BF46D", - new DeviceInfo(R.string.device_pixel_3a_generic, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("AE6316B4753C61F5855B95B9B98484AF784F2E83648D0FCC8107FCA752CAEA34", - new DeviceInfo(R.string.device_pixel_4_generic, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("879CD3F18EA76E244D4D4AC3BCB9C337C13B4667190B19035AFE2536550050F1", - new DeviceInfo(R.string.device_pixel_4a, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("88265D85BA9E1E2F6036A259D880D2741031ACA445840137395B6D541C0FC7FC", - new DeviceInfo(R.string.device_pixel_5_generic, 4, 41, false /* uses new API */, true, true, R.string.os_stock)) - .put("1DD694CE00BF131AD61CEB576B7DCC41CF7F9B2C418F4C12B2B8F3E9A1EA911D", - new DeviceInfo(R.string.device_pixel_5a, 4, 41, false /* uses new API */, true, true, R.string.os_stock)) - .put("0F6E75C80183B5DEC074B0054D4271E99389EBE4B136B0819DE1F150BA0FF9D7", - new DeviceInfo(R.string.device_pixel_6, 100, 100, false /* uses new API */, true, true, R.string.os_stock)) - .put("42ED1BCA352FABD428F34E8FCEE62776F4CB2C66E06F82E5A59FF4495267BFC2", - new DeviceInfo(R.string.device_pixel_6_pro, 100, 100, false /* uses new API */, true, true, R.string.os_stock)) - .put("9AC4174153D45E4545B0F49E22FE63273999B6AC1CB6949C3A9F03EC8807EEE9", - new DeviceInfo(R.string.device_pixel_6a, 100, 100, false /* uses new API */, true, true, R.string.os_stock)) - .put("8B2C4CD539F5075E8E7CF212ADB3DB0413FBD77D321199C73D5A473C51F2E10D", - new DeviceInfo(R.string.device_pixel_7, 100, 100, false /* uses new API */, true, false, R.string.os_stock)) - .put("26AC4C60BEB1E378357CAD0C3061347AF8DF6FBABBB0D8CEA2445855EE01E368", - new DeviceInfo(R.string.device_pixel_7_pro, 100, 100, false /* uses new API */, true, false, R.string.os_stock)) - .put("3D3DEB132A89551D0A700D230BABAE4E3E80E3C7926ACDD7BAEDF9B57AD316D0", - new DeviceInfo(R.string.device_sm_n970u, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .put("9AC63842137D92C119A1B1BE2C9270B9EBB6083BBE6350B7823571942B5869F0", - new DeviceInfo(R.string.device_sm_n975u, 3, 4, false /* uses new API */, true, true, R.string.os_stock)) - .build(); - private static byte[] getChallengeIndex(final Context context) { final SharedPreferences global = PreferenceManager.getDefaultSharedPreferences(context); final String challengeIndexSerialized = global.getString(KEY_CHALLENGE_INDEX, null); @@ -708,7 +460,7 @@ private static byte[] getChallenge() { } static byte[] getChallengeMessage(final Context context) { - return Bytes.concat(new byte[]{PROTOCOL_VERSION}, getChallengeIndex(context), getChallenge()); + return Bytes.concat(new byte[] { PROTOCOL_VERSION }, getChallengeIndex(context), getChallenge()); } private static byte[] getFingerprint(final Certificate certificate) @@ -766,9 +518,69 @@ private static X509Certificate generateCertificate(final Resources resources, fi } } - private static Verified verifyStateless(final Certificate[] certificates, + private static Verified verifyStateless(Context context, + final Certificate[] certificates, final byte[] challenge, final boolean hasPersistentKey, final Certificate root0, - final Certificate root1, final Certificate root2, final Resources res) throws GeneralSecurityException { + final Certificate root1, final Certificate root2) throws GeneralSecurityException { + + ImmutableMap fingerprintsGraphene, fingerprintsGrapheneStrongBox, fingerprintsStock, + fingerprintsStockStrongBox, fingerprintsCustomOs, fingerprintsCustomOsStrongBox; + try { + fingerprintsGraphene = DeviceInfoParser.getDeviceMap(context, + R.xml.fingerprints_graphene); + } catch (IOException | XmlPullParserException e) { + throw new GeneralSecurityException("failed to parse GrapheneOS fingerprints"); + } + + try { + fingerprintsGrapheneStrongBox = DeviceInfoParser.getDeviceMap(context, + R.xml.fingerprints_graphene_strongbox); + } catch (IOException | XmlPullParserException e) { + throw new GeneralSecurityException("failed to parse GrapheneOS StrongBox fingerprints"); + } + + try { + fingerprintsStock = DeviceInfoParser.getDeviceMap(context, + R.xml.fingerprints_stock); + } catch (IOException | XmlPullParserException e) { + throw new GeneralSecurityException("failed to parse Stock fingerprints"); + } + + try { + fingerprintsStockStrongBox = DeviceInfoParser.getDeviceMap(context, + R.xml.fingerprints_stock_strongbox); + } catch (IOException | XmlPullParserException e) { + throw new GeneralSecurityException("failed to parse Stock StrongBox fingerprints"); + } + + Resources res = context.getResources(); + + int custom_os_fingerprints_res = res.getIdentifier("os_custom_fingerprints_res", "string", + context.getPackageName()); + if (custom_os_fingerprints_res == 0) { + fingerprintsCustomOs = ImmutableMap.builder().build(); + } else { + try { + fingerprintsCustomOs = DeviceInfoParser.getDeviceMap(context, custom_os_fingerprints_res); + } catch (IOException | XmlPullParserException e) { + Log.e("AttestationProtocol", "failed to parse provided fingerprints for custom OS", e); + fingerprintsCustomOs = ImmutableMap.builder().build(); + } + } + + int custom_os_fingerprints_strongbox_res = res.getIdentifier("os_custom_fingerprints_strongbox_res", "string", + context.getPackageName()); + if (custom_os_fingerprints_res == 0) { + fingerprintsCustomOsStrongBox = ImmutableMap.builder().build(); + } else { + try { + fingerprintsCustomOsStrongBox = DeviceInfoParser.getDeviceMap(context, + custom_os_fingerprints_strongbox_res); + } catch (IOException | XmlPullParserException e) { + Log.e("AttestationProtocol", "failed to parse provided StrongBox fingerprints for custom OS", e); + fingerprintsCustomOsStrongBox = ImmutableMap.builder().build(); + } + } verifyCertificateSignatures(certificates, hasPersistentKey); @@ -823,7 +635,8 @@ private static Verified verifyStateless(final Certificate[] certificates, appVariant = AUDITOR_APP_VARIANT_PLAY; } else if (AUDITOR_APP_PACKAGE_NAME_DEBUG.equals(info.getPackageName())) { if (!BuildConfig.DEBUG) { - throw new GeneralSecurityException("Auditor debug builds are only trusted by other Auditor debug builds"); + throw new GeneralSecurityException( + "Auditor debug builds are only trusted by other Auditor debug builds"); } if (!AUDITOR_APP_SIGNATURE_DIGEST_DEBUG.equals(signatureDigest)) { throw new GeneralSecurityException("invalid Auditor app signing key"); @@ -851,23 +664,22 @@ private static Verified verifyStateless(final Certificate[] certificates, final String verifiedBootKey = BaseEncoding.base16().encode(rootOfTrust.getVerifiedBootKey()); final DeviceInfo device; if (verifiedBootState == RootOfTrust.KM_VERIFIED_BOOT_SELF_SIGNED) { - final String overridenFingerprint = res.getString(R.string.avb_fingerprint_override); - if (verifiedBootKey.equals(overridenFingerprint)) { - // the fingerprint has been overriden at build time so we need to dynamically look up the device characteristics - DeviceInfo partialDevice = DeviceInfo.deviceMap(res).get(res.getString(R.string.device_name)); - if(attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { - device = new DeviceInfo(partialDevice.name, partialDevice.attestationVersion, partialDevice.keymasterVersion, partialDevice.rollbackResistant, partialDevice.perUserEncryption, true, partialDevice.osName); + if (attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { + if (fingerprintsGrapheneStrongBox.containsKey(verifiedBootKey)) { + device = fingerprintsGrapheneStrongBox.get(verifiedBootKey); } else { - device = new DeviceInfo(partialDevice.name, partialDevice.attestationVersion, partialDevice.keymasterVersion, partialDevice.rollbackResistant, partialDevice.perUserEncryption, false, partialDevice.osName); + device = fingerprintsCustomOsStrongBox.get(verifiedBootKey); } - } else if (attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { - device = fingerprintsStrongBoxCustomOS.get(verifiedBootKey); } else { - device = fingerprintsCustomOS.get(verifiedBootKey); + if (fingerprintsGraphene.containsKey(verifiedBootKey)) { + device = fingerprintsGraphene.get(verifiedBootKey); + } else { + device = fingerprintsCustomOs.get(verifiedBootKey); + } } } else if (verifiedBootState == RootOfTrust.KM_VERIFIED_BOOT_VERIFIED) { if (attestationSecurityLevel == Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { - device = fingerprintsStrongBoxStock.get(verifiedBootKey); + device = fingerprintsStockStrongBox.get(verifiedBootKey); } else { device = fingerprintsStock.get(verifiedBootKey); } @@ -930,12 +742,14 @@ private static Verified verifyStateless(final Certificate[] certificates, final int attestationVersion = attestation.getAttestationVersion(); Log.d(TAG, "attestationVersion: " + attestationVersion); if (attestationVersion < device.attestationVersion) { - throw new GeneralSecurityException("attestation version " + attestationVersion + " below " + device.attestationVersion); + throw new GeneralSecurityException( + "attestation version " + attestationVersion + " below " + device.attestationVersion); } final int keymasterVersion = attestation.getKeymasterVersion(); Log.d(TAG, "keymasterVersion: " + keymasterVersion); if (keymasterVersion < device.keymasterVersion) { - throw new GeneralSecurityException("keymaster version " + keymasterVersion + " below " + device.keymasterVersion); + throw new GeneralSecurityException( + "keymaster version " + keymasterVersion + " below " + device.keymasterVersion); } final byte[] verifiedBootHash = rootOfTrust.getVerifiedBootHash(); @@ -991,7 +805,8 @@ private static Verified verifyStateless(final Certificate[] certificates, throw new GeneralSecurityException("attest key challenge does not match"); } - if (!attestation1.getSoftwareEnforced().getAttestationApplicationId().equals(attestationApplicationId)) { + if (!attestation1.getSoftwareEnforced().getAttestationApplicationId() + .equals(attestationApplicationId)) { throw new GeneralSecurityException("attest key application does not match"); } @@ -1023,7 +838,8 @@ private static Verified verifyStateless(final Certificate[] certificates, } attestKey = true; - } catch (final Attestation.KeyDescriptionMissingException e) {} + } catch (final Attestation.KeyDescriptionMissingException e) { + } for (int i = 2; i < certificates.length; i++) { try { @@ -1040,9 +856,12 @@ private static Verified verifyStateless(final Certificate[] certificates, device.enforceStrongBox); } - // Only checks expiry beyond the initial certificate for the initial pairing since the - // certificates are short lived when remote provisioning is in use and we prevent rotation by - // using the attest key feature to provide permanent pairing-specific certificate chains in + // Only checks expiry beyond the initial certificate for the initial pairing + // since the + // certificates are short lived when remote provisioning is in use and we + // prevent rotation by + // using the attest key feature to provide permanent pairing-specific + // certificate chains in // order to pin them. private static void verifyCertificateSignatures(final Certificate[] certChain, final boolean hasPersistentKey) throws GeneralSecurityException { @@ -1116,9 +935,9 @@ private static void appendVerifiedInformation(final Context context, } else { final String osVersion = String.format(Locale.US, "%06d", verified.osVersion); builder.append(context.getString(R.string.os_version, - Integer.parseInt(osVersion.substring(0, 2)) + "." + - Integer.parseInt(osVersion.substring(2, 4)) + "." + - Integer.parseInt(osVersion.substring(4, 6)))); + Integer.parseInt(osVersion.substring(0, 2)) + "." + + Integer.parseInt(osVersion.substring(2, 4)) + "." + + Integer.parseInt(osVersion.substring(4, 6)))); } builder.append(context.getString(R.string.os_patch_level, formatPatchLevel(verified.osPatchLevel))); @@ -1132,7 +951,7 @@ private static void appendVerifiedInformation(final Context context, } builder.append(context.getString(R.string.verified_boot_key_hash, - verified.verifiedBootKey)); + verified.verifiedBootKey)); if (verified.verifiedBootHash != null) { builder.append(context.getString(R.string.verified_boot_hash, @@ -1181,21 +1000,19 @@ private static VerificationResult verify(final Context context, final byte[] fin final byte[] currentFingerprint = getFingerprint(attestationCertificates[0]); final boolean hasPersistentKey = !Arrays.equals(currentFingerprint, fingerprint); - final SharedPreferences preferences = - context.getSharedPreferences(PREFERENCES_DEVICE_PREFIX + fingerprintHex, - Context.MODE_PRIVATE); + final SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_DEVICE_PREFIX + fingerprintHex, + Context.MODE_PRIVATE); if (hasPersistentKey && !preferences.contains(KEY_PINNED_CERTIFICATE_LENGTH)) { throw new GeneralSecurityException( "Pairing data for this Auditee is missing. Cannot perform paired attestation.\n" + - "\nEither the initial pairing was incomplete or the device is compromised.\n" + - "\nIf the initial pairing was simply not completed, clear the pairing data on either the Auditee or the Auditor via the menu and try again.\n"); + "\nEither the initial pairing was incomplete or the device is compromised.\n" + + "\nIf the initial pairing was simply not completed, clear the pairing data on either the Auditee or the Auditor via the menu and try again.\n"); } - final Verified verified = verifyStateless(attestationCertificates, challenge, hasPersistentKey, + final Verified verified = verifyStateless(context, attestationCertificates, challenge, hasPersistentKey, generateCertificate(context.getResources(), R.raw.google_root_0), generateCertificate(context.getResources(), R.raw.google_root_1), - generateCertificate(context.getResources(), R.raw.google_root_2), - context.getResources()); + generateCertificate(context.getResources(), R.raw.google_root_2)); final StringBuilder teeEnforced = new StringBuilder(); final StringBuilder history = new StringBuilder(); @@ -1211,7 +1028,8 @@ private static VerificationResult verify(final Context context, final byte[] fin chainOffset = 1; pinOffset = 0; attestKeyMigration = true; - } else if (ALLOW_ATTEST_KEY_DOWNGRADE && attestationCertificates.length == 4 && preferences.getInt(KEY_PINNED_CERTIFICATE_LENGTH, 0) == 5) { + } else if (ALLOW_ATTEST_KEY_DOWNGRADE && attestationCertificates.length == 4 + && preferences.getInt(KEY_PINNED_CERTIFICATE_LENGTH, 0) == 5) { // temporarily work around attest key breakage by allowing not using it chainOffset = 0; pinOffset = 1; @@ -1223,13 +1041,15 @@ private static VerificationResult verify(final Context context, final byte[] fin pinOffset = 0; } for (int i = 1 + chainOffset; i < attestationCertificates.length; i++) { - final byte[] b = BaseEncoding.base64().decode(preferences.getString(KEY_PINNED_CERTIFICATE + (i - chainOffset + pinOffset), "")); + final byte[] b = BaseEncoding.base64() + .decode(preferences.getString(KEY_PINNED_CERTIFICATE + (i - chainOffset + pinOffset), "")); if (!Arrays.equals(attestationCertificates[i].getEncoded(), b)) { throw new GeneralSecurityException("certificate chain mismatch"); } } - final byte[] persistentCertificateEncoded = BaseEncoding.base64().decode(preferences.getString(KEY_PINNED_CERTIFICATE + "0", "")); + final byte[] persistentCertificateEncoded = BaseEncoding.base64() + .decode(preferences.getString(KEY_PINNED_CERTIFICATE + "0", "")); final Certificate persistentCertificate = generateCertificate( new ByteArrayInputStream(persistentCertificateEncoded)); if (!Arrays.equals(fingerprint, getFingerprint(persistentCertificate))) { @@ -1262,7 +1082,8 @@ private static VerificationResult verify(final Context context, final byte[] fin if (verified.appVariant < pinnedAppVariant) { throw new GeneralSecurityException("App version downgraded"); } - if (verified.securityLevel != preferences.getInt(KEY_PINNED_SECURITY_LEVEL, Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT)) { + if (verified.securityLevel != preferences.getInt(KEY_PINNED_SECURITY_LEVEL, + Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT)) { throw new GeneralSecurityException("Security level mismatch"); } @@ -1288,8 +1109,10 @@ private static VerificationResult verify(final Context context, final byte[] fin } else { verifySignature(attestationCertificates[0].getPublicKey(), signedMessage, signature); - if (PREFER_STRONGBOX && verified.enforceStrongBox && verified.securityLevel != Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { - throw new GeneralSecurityException("non-StrongBox security level for initial pairing with StrongBox device"); + if (PREFER_STRONGBOX && verified.enforceStrongBox + && verified.securityLevel != Attestation.KM_SECURITY_LEVEL_STRONG_BOX) { + throw new GeneralSecurityException( + "non-StrongBox security level for initial pairing with StrongBox device"); } final SharedPreferences.Editor editor = preferences.edit(); @@ -1364,7 +1187,8 @@ private static VerificationResult verify(final Context context, final byte[] fin osEnforced.append(context.getString(R.string.system_user, toYesNoString(context, systemUser))); - return new VerificationResult(hasPersistentKey, teeEnforced.toString(), osEnforced.toString(), history.toString()); + return new VerificationResult(hasPersistentKey, teeEnforced.toString(), osEnforced.toString(), + history.toString()); } private static Certificate[] decodeChain(final byte[] dictionary, final byte[] compressedChain) @@ -1511,8 +1335,8 @@ static KeyGenParameterSpec.Builder getKeyBuilder(final String alias, final int p } @TargetApi(31) - static void generateAttestKey(final String alias, final byte[] challenge, final boolean useStrongBox) throws - GeneralSecurityException, IOException { + static void generateAttestKey(final String alias, final byte[] challenge, final boolean useStrongBox) + throws GeneralSecurityException, IOException { generateKeyPair(getKeyBuilder(alias, KeyProperties.PURPOSE_ATTEST_KEY, useStrongBox, challenge, false).build()); } @@ -1568,19 +1392,19 @@ static AttestationResult generateSerialized(final Context context, final byte[] index = BaseEncoding.base16().encode(challengeIndex); } - final String attestKeystoreAlias = - statePrefix + KEYSTORE_ALIAS_ATTEST_PREFIX + index; - final String persistentKeystoreAlias = - statePrefix + KEYSTORE_ALIAS_PERSISTENT_PREFIX + index; + final String attestKeystoreAlias = statePrefix + KEYSTORE_ALIAS_ATTEST_PREFIX + index; + final String persistentKeystoreAlias = statePrefix + KEYSTORE_ALIAS_PERSISTENT_PREFIX + index; final PackageManager pm = context.getPackageManager(); - // generate a new key for fresh attestation results unless the persistent key is not yet created + // generate a new key for fresh attestation results unless the persistent key is + // not yet created final boolean hasPersistentKey = keyStore.containsAlias(persistentKeystoreAlias); final String attestationKeystoreAlias; final boolean useStrongBox; @SuppressLint("InlinedApi") - final boolean canUseAttestKey = (alwaysHasAttestKey || pm.hasSystemFeature(PackageManager.FEATURE_KEYSTORE_APP_ATTEST_KEY)) + final boolean canUseAttestKey = (alwaysHasAttestKey + || pm.hasSystemFeature(PackageManager.FEATURE_KEYSTORE_APP_ATTEST_KEY)) && USE_ATTEST_KEY; boolean useAttestKey; if (hasPersistentKey) { @@ -1594,8 +1418,7 @@ static AttestationResult generateSerialized(final Context context, final byte[] final KeyInfo keyinfo = factory.getKeySpec(key, KeyInfo.class); useStrongBox = keyinfo.getSecurityLevel() == KeyProperties.SECURITY_LEVEL_STRONGBOX; } else { - final X509Certificate persistent = - (X509Certificate) getCertificate(keyStore, persistentKeystoreAlias); + final X509Certificate persistent = (X509Certificate) getCertificate(keyStore, persistentKeystoreAlias); final String dn = persistent.getIssuerX500Principal().getName(X500Principal.RFC1779); useStrongBox = dn.contains("StrongBox"); } @@ -1630,7 +1453,8 @@ static AttestationResult generateSerialized(final Context context, final byte[] } generateKeyPair(builder.build()); } catch (final IOException e) { - // try without using attest key when already paired due to Pixel 6 / Pixel 6 Pro / Pixel 6a upgrade bug + // try without using attest key when already paired due to Pixel 6 / Pixel 6 Pro + // / Pixel 6a upgrade bug if (hasPersistentKey) { useAttestKey = false; final KeyGenParameterSpec.Builder builder = getKeyBuilder(attestationKeystoreAlias, @@ -1643,8 +1467,7 @@ static AttestationResult generateSerialized(final Context context, final byte[] } try { - final byte[] fingerprint = - getFingerprint(getCertificate(keyStore, persistentKeystoreAlias)); + final byte[] fingerprint = getFingerprint(getCertificate(keyStore, persistentKeystoreAlias)); final Certificate[] attestationCertificates; @@ -1657,12 +1480,12 @@ static AttestationResult generateSerialized(final Context context, final byte[] attestationCertificates = getCertificateChain(keyStore, attestationKeystoreAlias); } - // sanity check on the device being verified before sending it off to the verifying device - final Verified verified = verifyStateless(attestationCertificates, challenge, hasPersistentKey, + // sanity check on the device being verified before sending it off to the + // verifying device + final Verified verified = verifyStateless(context, attestationCertificates, challenge, hasPersistentKey, generateCertificate(context.getResources(), R.raw.google_root_0), generateCertificate(context.getResources(), R.raw.google_root_1), - generateCertificate(context.getResources(), R.raw.google_root_2), - context.getResources()); + generateCertificate(context.getResources(), R.raw.google_root_2)); // OS-enforced checks and information @@ -1711,8 +1534,7 @@ static AttestationResult generateSerialized(final Context context, final byte[] final boolean addUsersWhenLocked = Settings.Global.getInt(context.getContentResolver(), ADD_USERS_WHEN_LOCKED, 0) != 0; - final String denyNewUsbValue = - SystemProperties.get("persist.security.deny_new_usb", "disabled"); + final String denyNewUsbValue = SystemProperties.get("persist.security.deny_new_usb", "disabled"); final boolean denyNewUsb = !denyNewUsbValue.equals("disabled"); final String oemUnlockAllowedValue = SystemProperties.get("sys.oem_unlock_allowed", "0"); @@ -1804,12 +1626,18 @@ static AttestationResult generateSerialized(final Context context, final byte[] static void generateKeyPair(final KeyGenParameterSpec spec) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, IOException { - // Handle RuntimeExceptions caused by a broken keystore. A common issue involves users - // unlocking the device and wiping the encrypted TEE attestation keys from the persist - // partition. Additionally, some non-CTS compliant devices or operating systems have a - // non-existent or broken implementation. No one has reported these uncaught exceptions, - // presumably because they know their device or OS is broken, but the crash reports are - // being spammed to the Google Play error collection and causing it to think the app is + // Handle RuntimeExceptions caused by a broken keystore. A common issue involves + // users + // unlocking the device and wiping the encrypted TEE attestation keys from the + // persist + // partition. Additionally, some non-CTS compliant devices or operating systems + // have a + // non-existent or broken implementation. No one has reported these uncaught + // exceptions, + // presumably because they know their device or OS is broken, but the crash + // reports are + // being spammed to the Google Play error collection and causing it to think the + // app is // unreliable. try { final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, diff --git a/app/src/main/java/app/attestation/auditor/ImmutableMapParser.java b/app/src/main/java/app/attestation/auditor/ImmutableMapParser.java new file mode 100644 index 000000000..cc43a988c --- /dev/null +++ b/app/src/main/java/app/attestation/auditor/ImmutableMapParser.java @@ -0,0 +1,58 @@ +package app.attestation.auditor; + +import java.io.IOException; + +import com.google.common.collect.ImmutableMap; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.content.res.XmlResourceParser; +import android.util.Log; + +interface XmlMapElemParser { + public K parseKey(Context context, XmlResourceParser parser); + + public V parseValue(Context context, XmlResourceParser parser); +} + +public class ImmutableMapParser { + public static ImmutableMap getImmutableMapResource(Context context, int hashMapResId, + String keyTagName, String valueTagName, XmlMapElemParser mapElemParser) + throws XmlPullParserException, IOException, IllegalArgumentException { + ImmutableMap.Builder map = null; + XmlResourceParser parser = context.getResources().getXml(hashMapResId); + + K key = null; + V value = null; + + int eventType = parser.getEventType(); + + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_DOCUMENT) { + Log.d("ImmutableMapParser", "Start document"); + } else if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("map")) { + Log.d("ImmutableMapParser", "parsing map"); + map = ImmutableMap.builder(); + } else if (parser.getName().equals("entry")) { + Log.d("ImmutableMapParser", "parsing entry"); + } else if (parser.getName().equals(keyTagName)) { + key = mapElemParser.parseKey(context, parser); + } else if (parser.getName().equals(valueTagName)) { + value = mapElemParser.parseValue(context, parser); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("entry")) { + map.put(key, value); + key = null; + value = null; + } + } + eventType = parser.next(); + } + + return map.build(); + } +} diff --git a/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java b/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java index 59e08a930..f1f629a1d 100644 --- a/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java +++ b/app/src/main/java/app/attestation/auditor/RemoteVerifyJob.java @@ -124,16 +124,16 @@ static void cancel(final Context context) { scheduler.cancel(FIRST_RUN_JOB_ID); } - final String domain() { + private String domain() { return getString(R.string.base_domain); } - final String challengeUrl() { - return "https://" + domain() + "/challenge"; + private String challengeUrl() { + return domain() + "/challenge"; } - final String verifyUrl() { - return "https://" + domain() + "/verify"; + private String verifyUrl() { + return domain() + "/verify"; } @Override diff --git a/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java b/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java index e8d81e3b0..985cef07c 100644 --- a/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java +++ b/app/src/main/java/app/attestation/auditor/SubmitSampleJob.java @@ -69,8 +69,8 @@ static void schedule(final Context context) { } } - final String submitUrl() { - return "https://" + getString(R.string.base_domain) + "/submit"; + private String submitUrl() { + return getString(R.string.base_domain) + "/submit"; } @Override diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b07ac73aa..b5948c3f9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,9 +1,6 @@ Auditor attestation.app - NOT OVERRIDEN - UNUSED - UNUSED Two devices with Android 10 or higher are needed to perform verification:\n\n- The device to be verified (Auditee), which needs to be one of the supported devices.\n\n- An Android device to perform the verification (Auditor).\n\nThe verification process requires sending data between the devices by scanning QR codes. Device is not one of the supported models. Camera permission is required to scan QR codes. diff --git a/app/src/main/res/xml/fingerprints_graphene.xml b/app/src/main/res/xml/fingerprints_graphene.xml new file mode 100644 index 000000000..cec6a668a --- /dev/null +++ b/app/src/main/res/xml/fingerprints_graphene.xml @@ -0,0 +1,207 @@ + + + + B094E48B27C6E15661223CEFF539CF35E481DEB4E3250331E973AC2C15CAD6CD + + device_pixel_2 + 2 + 3 + true + true + false + os_graphene + + + + B6851E9B9C0EBB7185420BD0E79D20A84CB15AB0B018505EFFAA4A72B9D9DAC7 + + device_pixel_2_xl + 2 + 3 + true + true + false + os_graphene + + + + 0F9A9CC8ADE73064A54A35C5509E77994E3AA37B6FB889DD53AF82C3C570C5CF + + device_pixel_3 + 3 + 4 + false + true + true + os_graphene + + + + 06DD526EE9B1CB92AA19D9835B68B4FF1A48A3AD31D813F27C9A7D6C271E9451 + + device_pixel_3_xl + 3 + 4 + false + true + true + os_graphene + + + + 8FF8B9B4F831114963669E04EA4F849F33F3744686A0B33B833682746645ABC8 + + device_pixel_3a + 3 + 4 + false + true + true + os_graphene + + + + 91943FAA75DCB6392AE87DA18CA57D072BFFB80BC30F8FAFC7FFE13D76C5736E + + device_pixel_3a_xl + 3 + 4 + false + true + true + os_graphene + + + + 80EF268700EE42686F779A47B4A155FE1FFC2EEDF836B4803CAAB8FA61439746 + + device_pixel_4 + 3 + 4 + false + true + true + os_graphene + + + + 3F15FDCB82847FED97427CE00563B8F9FF34627070DE5FDB17ACA7849AB98CC8 + + device_pixel_4_xl + 3 + 4 + false + true + true + os_graphene + + + + 9F2454A1657B1B5AD7F2336B39A2611F7A40B2E0DDFD0D6553A359605928DF29 + + device_pixel_4a + 3 + 4 + false + true + true + os_graphene + + + + DCEC2D053D3EC4F1C9BE414AA07E4D7D7CBD12040AD2F8831C994A83A0536866 + + device_pixel_4a_5g + 3 + 4 + false + true + true + os_graphene + + + + 36A99EAB7907E4FB12A70E3C41C456BCBE46C13413FBFE2436ADEE2B2B61120F + + device_pixel_5 + 3 + 4 + false + true + true + os_graphene + + + + 0ABDDEDA03B6CE10548C95E0BEA196FAA539866F929BCDF7ECA84B4203952514 + + device_pixel_5a + 3 + 4 + false + true + true + os_graphene + + + + F0A890375D1405E62EBFD87E8D3F475F948EF031BBF9DDD516D5F600A23677E8 + + device_pixel_6 + 100 + 100 + false + true + true + os_graphene + + + + 439B76524D94C40652CE1BF0D8243773C634D2F99BA3160D8D02AA5E29FF925C + + device_pixel_6_pro + 100 + 100 + false + true + true + os_graphene + + + + 08C860350A9600692D10C8512F7B8E80707757468E8FBFEEA2A870C0A83D6031 + + device_pixel_6a + 100 + 100 + false + true + true + os_graphene + + + + 3EFE5392BE3AC38AFB894D13DE639E521675E62571A8A9B3EF9FC8C44FD17FA1 + + device_pixel_7 + 200 + 200 + false + true + false + os_graphene + + + + BC1C0DD95664604382BB888412026422742EB333071EA0B2D19036217D49182F + + device_pixel_7_pro + 200 + 200 + false + true + false + os_graphene + + + diff --git a/app/src/main/res/xml/fingerprints_graphene_strongbox.xml b/app/src/main/res/xml/fingerprints_graphene_strongbox.xml new file mode 100644 index 000000000..43f3fdfe5 --- /dev/null +++ b/app/src/main/res/xml/fingerprints_graphene_strongbox.xml @@ -0,0 +1,183 @@ + + + + 0F9A9CC8ADE73064A54A35C5509E77994E3AA37B6FB889DD53AF82C3C570C5CF + + device_pixel_3 + 3 + 4 + false + true + true + os_graphene + + + + 06DD526EE9B1CB92AA19D9835B68B4FF1A48A3AD31D813F27C9A7D6C271E9451 + + device_pixel_3_xl + 3 + 4 + false + true + true + os_graphene + + + + 73D6C63A07610404FE16A4E07DD24E41A70D331E9D3EF7BBA2D087E4761EB63A + + device_pixel_3a + 3 + 4 + false + true + true + os_graphene + + + + 3F36E3482E1FF82986576552CB4FD08AF09F8B09D3832314341E04C42D2919A4 + + device_pixel_3a_xl + 3 + 4 + false + true + true + os_graphene + + + + 80EF268700EE42686F779A47B4A155FE1FFC2EEDF836B4803CAAB8FA61439746 + + device_pixel_4 + 3 + 4 + false + true + true + os_graphene + + + + 3F15FDCB82847FED97427CE00563B8F9FF34627070DE5FDB17ACA7849AB98CC8 + + device_pixel_4_xl + 3 + 4 + false + true + true + os_graphene + + + + 9F2454A1657B1B5AD7F2336B39A2611F7A40B2E0DDFD0D6553A359605928DF29 + + device_pixel_4a + 3 + 4 + false + true + true + os_graphene + + + + DCEC2D053D3EC4F1C9BE414AA07E4D7D7CBD12040AD2F8831C994A83A0536866 + + device_pixel_4a_5g + 4 + 41 + false + true + true + os_graphene + + + + 36A99EAB7907E4FB12A70E3C41C456BCBE46C13413FBFE2436ADEE2B2B61120F + + device_pixel_5 + 4 + 41 + false + true + true + os_graphene + + + + 0ABDDEDA03B6CE10548C95E0BEA196FAA539866F929BCDF7ECA84B4203952514 + + device_pixel_5a + 4 + 41 + false + true + true + os_graphene + + + + F0A890375D1405E62EBFD87E8D3F475F948EF031BBF9DDD516D5F600A23677E8 + + device_pixel_6 + 100 + 100 + false + true + true + os_graphene + + + + 439B76524D94C40652CE1BF0D8243773C634D2F99BA3160D8D02AA5E29FF925C + + device_pixel_6_pro + 100 + 100 + false + true + true + os_graphene + + + + 08C860350A9600692D10C8512F7B8E80707757468E8FBFEEA2A870C0A83D6031 + + device_pixel_6a + 100 + 100 + false + true + true + os_graphene + + + + 3EFE5392BE3AC38AFB894D13DE639E521675E62571A8A9B3EF9FC8C44FD17FA1 + + device_pixel_7 + 100 + 100 + false + true + false + os_graphene + + + + BC1C0DD95664604382BB888412026422742EB333071EA0B2D19036217D49182F + + device_pixel_7_pro + 100 + 100 + false + true + false + os_graphene + + + diff --git a/app/src/main/res/xml/fingerprints_stock.xml b/app/src/main/res/xml/fingerprints_stock.xml new file mode 100644 index 000000000..047abfeb2 --- /dev/null +++ b/app/src/main/res/xml/fingerprints_stock.xml @@ -0,0 +1,783 @@ + + + + 5341E6B2646979A70E57653007A1F310169421EC9BDD9F1A5648F75ADE005AF1 + + device_huawei + 2 + 3 + false + true + false + os_stock + + + + 7E2E8CC82A77CA74554457E5DF3A3ED82E7032B3182D17FE17919BC6E989FF09 + + device_huawei_honor_7a_pro + 2 + 3 + false + true + false + os_stock + + + + DFC2920C81E136FDD2A510478FDA137B262DC51D449EDD7D0BDB554745725CFE + + device_nokia + 2 + 3 + true + true + false + os_stock + + + + 4D790FA0A5FE81D6B352B90AFE430684D9BC817518CD24C50E6343395F7C51F2 + + device_nokia_3_1 + 2 + 3 + false + false + false + os_stock + + + + 893A17FD918235DB2865F7F6439EB0134A45B766AA452E0675BAC6CFB5A773AA + + device_nokia_7_1 + 2 + 3 + true + true + false + os_stock + + + + 6101853DFF451FAE5B137DF914D5E6C15C659337F2C405AC50B513A159071958 + + device_oneplus_6_a6003 + 2 + 3 + true + true + false + os_stock + + + + 1B90B7D1449D697FB2732A7D2DFA405D587254593F5137F7B6E64F7A0CE03BFD + + device_oneplus_6t_a6013 + 3 + 4 + false + true + false + os_stock + + + + 4B9201B11685BE6710E2B2BA8482F444E237E0C8A3D1F7F447FE29C37CECC559 + + device_oneplus_7_pro_gm1913 + 3 + 4 + false + true + false + os_stock + + + + 1962B0538579FFCE9AC9F507C46AFE3B92055BAC7146462283C85C500BE78D82 + + device_pixel_2 + 2 + 3 + true + true + false + os_stock + + + + 171616EAEF26009FC46DC6D89F3D24217E926C81A67CE65D2E3A9DC27040C7AB + + device_pixel_2_xl + 2 + 3 + true + true + false + os_stock + + + + 61FDA12B32ED84214A9CF13D1AFFB7AA80BD8A268A861ED4BB7A15170F1AB00C + + device_pixel_3_generic + 3 + 4 + false + true + true + os_stock + + + + E75B86C52C7496255A95FB1E2B1C044BFA9D5FE34DD1E4EEBD752EEF0EA89875 + + device_pixel_3a_generic + 3 + 4 + false + true + true + os_stock + + + + AE6316B4753C61F5855B95B9B98484AF784F2E83648D0FCC8107FCA752CAEA34 + + device_pixel_4_generic + 3 + 4 + false + true + true + os_stock + + + + 879CD3F18EA76E244D4D4AC3BCB9C337C13B4667190B19035AFE2536550050F1 + + device_pixel_4a + 3 + 4 + false + true + true + os_stock + + + + 88265D85BA9E1E2F6036A259D880D2741031ACA445840137395B6D541C0FC7FC + + device_pixel_5_generic + 3 + 4 + false + true + true + os_stock + + + + 1DD694CE00BF131AD61CEB576B7DCC41CF7F9B2C418F4C12B2B8F3E9A1EA911D + + device_pixel_5a + 3 + 4 + false + true + true + os_stock + + + + 0F6E75C80183B5DEC074B0054D4271E99389EBE4B136B0819DE1F150BA0FF9D7 + + device_pixel_6 + 100 + 100 + false + true + true + os_stock + + + + 42ED1BCA352FABD428F34E8FCEE62776F4CB2C66E06F82E5A59FF4495267BFC2 + + device_pixel_6_pro + 100 + 100 + false + true + true + os_stock + + + + 9AC4174153D45E4545B0F49E22FE63273999B6AC1CB6949C3A9F03EC8807EEE9 + + device_pixel_6a + 100 + 100 + false + true + true + os_stock + + + + 8B2C4CD539F5075E8E7CF212ADB3DB0413FBD77D321199C73D5A473C51F2E10D + + device_pixel_7 + 200 + 200 + false + true + false + os_stock + + + + 26AC4C60BEB1E378357CAD0C3061347AF8DF6FBABBB0D8CEA2445855EE01E368 + + device_pixel_7_pro + 200 + 200 + false + true + false + os_stock + + + + 72376CAACF11726D4922585732429FB97D0D1DD69F0D2E0770B9E61D14ADDE65 + + device_sm_a705fn + 3 + 4 + false + true + false + os_stock + + + + 33D9484FD512E610BCF00C502827F3D55A415088F276C6506657215E622FA770 + + device_sm_g960f + 1 + 2 + false + false + false + os_stock + + + + 266869F7CF2FB56008EFC4BE8946C8F84190577F9CA688F59C72DD585E696488 + + device_sm_g960_na + 1 + 2 + false + false + false + os_stock + + + + 12E8460A7BAF709F3B6CF41C7E5A37C6EB4D11CB36CF7F61F7793C8DCDC3C2E4 + + device_sm_g9600 + 1 + 2 + false + false + false + os_stock + + + + D1C53B7A931909EC37F1939B14621C6E4FD19BF9079D195F86B3CEA47CD1F92D + + device_sm_g965f + 1 + 2 + false + false + false + os_stock + + + + A4A544C2CFBAEAA88C12360C2E4B44C29722FC8DBB81392A6C1FAEDB7BF63010 + + device_sm_g965_msm + 1 + 2 + false + false + false + os_stock + + + + 9D77474FA4FEA6F0B28636222FBCEE2BB1E6FF9856C736C85B8EA6E3467F2BBA + + device_sm_g970f + 3 + 4 + false + true + false + os_stock + + + + 08B2B5C6EC8F54C00C505756E1EF516BB4537B2F02D640410D287A43FCF92E3F + + device_sm_g975f + 3 + 4 + false + true + false + os_stock + + + + F0FC0AF47D3FE4F27D79CF629AD6AC42AA1EEDE0A29C0AE109A91BBD1E7CD76D + + device_sm_j260a + 1 + 2 + false + false + false + os_stock + + + + 410102030405060708090001020304050607080900010203040506070809005A + + device_sm_j260f + 1 + 2 + false + false + false + os_stock + + + + D6B902D9E77DFC0FB3627FFEFA6D05405932EBB3A6ED077874B5E2A0CCBDB632 + + device_sm_j260t1 + 1 + 2 + false + false + false + os_stock + + + + 4558C1AFB30D1B46CB93F85462BC7D7FCF70B0103B9DBB0FE96DD828F43F29FC + + device_sm_j337a + 1 + 2 + false + false + false + os_stock + + + + 45E3AB5D61A03915AE10BF0465B186CB5D9A2FB6A46BEFAA76E4483BBA5A358D + + device_sm_j337t + 1 + 2 + false + false + false + os_stock + + + + D95279A8F2E832FD68D919DBF33CFE159D5A1179686DB0BD2D7BBBF2382C4DD3 + + device_sm_j720f + 1 + 2 + false + false + false + os_stock + + + + BB053A5F64D3E3F17C4611340FF2BBE2F605B832A9FA412B2C87F2A163ECE2FB + + device_sm_j737t1 + 1 + 2 + false + false + false + os_stock + + + + 4E0570011025D01386D057B2B382969F804DCD19E001344535CF0CFDB8AD7CFE + + device_sm_m205f + 1 + 2 + false + false + false + os_stock + + + + 2A7E4954C9F703F3AC805AC660EA1727B981DB39B1E0F41E4013FA2586D3DF7F + + device_sm_n960f + 1 + 2 + false + false + false + os_stock + + + + 173ACFA8AE9EDE7BBD998F45A49231F3A4BDDF0779345732E309446B46B5641B + + device_sm_n960u + 1 + 2 + false + false + false + os_stock + + + + E94BC43B97F98CD10C22CD9D8469DBE621116ECFA624FE291A1D53CF3CD685D1 + + device_sm_n970f + 3 + 4 + false + true + false + os_stock + + + + 466011C44BBF883DB38CF96617ED35C796CE2552C5357F9230258329E943DB70 + + device_sm_n970u + 3 + 4 + false + true + true + os_stock + + + + 52946676088007755EB586B3E3F3E8D3821BE5DF73513E6C13640507976420E6 + + device_sm_n975u + 3 + 4 + false + true + true + os_stock + + + + F3688C02D9676DEDB6909CADE364C271901FD66EA4F691AEB8B8921195E469C5 + + device_sm_s367vl + 1 + 2 + false + false + false + os_stock + + + + 106592D051E54388C6E601DFD61D59EB1674A8B93216C65C5B3E1830B73D3B82 + + device_sm_t510 + 3 + 4 + false + true + false + os_stock + + + + 87790149AED63553B768456AAB6DAAD5678CD87BDEB2BF3649467085349C34E0 + + device_sm_t835 + 1 + 2 + false + false + false + os_stock + + + + 4285AD64745CC79B4499817F264DC16BF2AF5163AF6C328964F39E61EC84693E + + device_sony_xperia_xa2 + 2 + 3 + true + true + false + os_stock + + + + 54A9F21E9CFAD3A2D028517EF333A658302417DB7FB75E0A109A019646CC5F39 + + device_sony_xperia_xz1 + 2 + 3 + true + true + false + os_stock + + + + BC3B5E121974113939B8A2FE758F9B923F1D195F038D2FD1C04929F886E83BB5 + + device_sony_xperia_xz2 + 2 + 3 + false + true + false + os_stock + + + + 94B8B4E3260B4BF8211A02CF2F3DE257A127CFFB2E4047D5580A752A5E253DE0 + + device_sony_xperia_xz2_compact + 2 + 3 + true + true + false + os_stock + + + + 728800FEBB119ADD74519618AFEDB715E1C39FE08A4DE37D249BF54ACF1CE00F + + device_blackberry_key2 + 2 + 3 + true + true + false + os_stock + + + + 1194659B40EA291245E54A3C4EC4AA5B7077BD244D65C7DD8C0A2DBB9DB1FB35 + + device_bq_aquaris_x2_pro + 2 + 3 + true + false + false + os_stock + + + + A9C6758D509600D0EB94FA8D2BF6EE7A6A6097F0CCEF94A755DDE065AA1AA1B0 + + device_xiaomi_mi_a2 + 2 + 3 + true + false + false + os_stock + + + + 6FA710B639848C9D47378937A1AFB1B6A52DDA738BEB6657E2AE70A15B40541A + + device_xiaomi_mi_a2_lite + 2 + 3 + true + false + false + os_stock + + + + 84BC8445A29B5444A2D1629C9774C8626DAFF3574D865EC5067A78FAEC96B013 + + device_xiaomi_mi_9 + 3 + 4 + false + true + false + os_stock + + + + 1CC39488D2F85DEE0A8E0903CDC4124CFDF2BE2531ED6060B678057ED2CB89B4 + + device_htc + 2 + 3 + true + false + false + os_stock + + + + 80BAB060807CFFA45D4747DF1AD706FEE3AE3F645F80CF14871DDBE27E14C30B + + device_moto_g7 + 3 + 4 + false + true + false + os_stock + + + + C2224571C9CD5C89200A7311B1E37AA9CF751E2E19753E8D3702BCA00BE1D42C + + device_motorola_one_vision + 2 + 3 + false + true + false + os_stock + + + + 1F6D98D1B0E1F1CE1C872BD36C668F9DFDBE0D47594789E1540DF4E6198F657D + + device_vivo_1807 + 2 + 3 + true + false + false + os_stock + + + + C55635636999E9D0A0588D24402256B7F9F3AEE07B4F7E4E003F09FF0190AFAE + + device_revvl_2 + 2 + 3 + false + false + false + os_stock + + + + 341C50D577DC5F3D5B46E8BFA22C22D1E5FC7D86D4D860E70B89222A7CBFC893 + + device_oppo_cph1831 + 2 + 3 + true + false + false + os_stock + + + + 41BF0A26BB3AFDCCCC40F7B685083522EB5BF1C492F0EC4847F351265313CB07 + + device_oppo_cph1903 + 2 + 3 + true + false + false + os_stock + + + + 7E19E217072BE6CB7A4C6F673FD3FB62DC51B3E204E7475838747947A3920DD8 + + device_oppo_cph1909 + 2 + 3 + false + false + false + os_stock + + + + 0D5F986943D0CE0D4F9783C27EEBE175BE359927DB8B6546B667279A81133C3C + + device_lg_q710al + 2 + 3 + false + false + false + os_stock + + + + D20078F2AF2A7D3ECA3064018CB8BD47FBCA6EE61ABB41BA909D3C529CB802F4 + + device_lm_q720 + 3 + 4 + false + false + false + os_stock + + + + 54EC644C21FD8229E3B0066513337A8E2C8EF3098A3F974B6A1CFE456A683DAE + + device_rmx1941 + 2 + 3 + false + true + false + os_stock + + + diff --git a/app/src/main/res/xml/fingerprints_stock_strongbox.xml b/app/src/main/res/xml/fingerprints_stock_strongbox.xml new file mode 100644 index 000000000..93d84aa14 --- /dev/null +++ b/app/src/main/res/xml/fingerprints_stock_strongbox.xml @@ -0,0 +1,159 @@ + + + + 61FDA12B32ED84214A9CF13D1AFFB7AA80BD8A268A861ED4BB7A15170F1AB00C + + device_pixel_3_generic + 3 + 4 + false + true + true + os_stock + + + + 8CA89AF1A6DAA74B00810849356DE929CFC4498EF36AF964757BDE8A113BF46D + + device_pixel_3a_generic + 3 + 4 + false + true + true + os_stock + + + + AE6316B4753C61F5855B95B9B98484AF784F2E83648D0FCC8107FCA752CAEA34 + + device_pixel_4_generic + 3 + 4 + false + true + true + os_stock + + + + 879CD3F18EA76E244D4D4AC3BCB9C337C13B4667190B19035AFE2536550050F1 + + device_pixel_4a + 3 + 4 + false + true + true + os_stock + + + + 88265D85BA9E1E2F6036A259D880D2741031ACA445840137395B6D541C0FC7FC + + device_pixel_5_generic + 4 + 41 + false + true + true + os_stock + + + + 1DD694CE00BF131AD61CEB576B7DCC41CF7F9B2C418F4C12B2B8F3E9A1EA911D + + device_pixel_5a + 4 + 41 + false + true + true + os_stock + + + + 0F6E75C80183B5DEC074B0054D4271E99389EBE4B136B0819DE1F150BA0FF9D7 + + device_pixel_6 + 100 + 100 + false + true + true + os_stock + + + + 42ED1BCA352FABD428F34E8FCEE62776F4CB2C66E06F82E5A59FF4495267BFC2 + + device_pixel_6_pro + 100 + 100 + false + true + true + os_stock + + + + 9AC4174153D45E4545B0F49E22FE63273999B6AC1CB6949C3A9F03EC8807EEE9 + + device_pixel_6a + 100 + 100 + false + true + true + os_stock + + + + 8B2C4CD539F5075E8E7CF212ADB3DB0413FBD77D321199C73D5A473C51F2E10D + + device_pixel_7 + 100 + 100 + false + true + false + os_stock + + + + 26AC4C60BEB1E378357CAD0C3061347AF8DF6FBABBB0D8CEA2445855EE01E368 + + device_pixel_7_pro + 100 + 100 + false + true + false + os_stock + + + + 3D3DEB132A89551D0A700D230BABAE4E3E80E3C7926ACDD7BAEDF9B57AD316D0 + + device_sm_n970u + 3 + 4 + false + true + true + os_stock + + + + 9AC63842137D92C119A1B1BE2C9270B9EBB6083BBE6350B7823571942B5869F0 + + device_sm_n975u + 3 + 4 + false + true + true + os_stock + + + diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 5a97d0faa..3f19dbf4b 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -37,6 +37,22 @@ + + + + + + + + + + + + + + + + @@ -51,6 +67,9 @@ + + + @@ -80,6 +99,9 @@ + + + @@ -272,6 +294,14 @@ + + + + + + + + @@ -280,6 +310,14 @@ + + + + + + + + @@ -288,6 +326,22 @@ + + + + + + + + + + + + + + + + @@ -386,6 +440,14 @@ + + + + + + + + @@ -394,6 +456,14 @@ + + + + + + + + @@ -407,6 +477,14 @@ + + + + + + + + @@ -423,6 +501,14 @@ + + + + + + + + @@ -436,6 +522,14 @@ + + + + + + + + @@ -449,6 +543,14 @@ + + + + + + + + @@ -457,6 +559,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -582,6 +708,9 @@ + + + @@ -594,6 +723,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -682,6 +851,11 @@ + + + + + @@ -914,6 +1088,14 @@ + + + + + + + + @@ -994,6 +1176,14 @@ + + + + + + + + @@ -1002,6 +1192,14 @@ + + + + + + + + @@ -1010,6 +1208,14 @@ + + + + + + + + @@ -1018,6 +1224,14 @@ + + + + + + + + @@ -1026,6 +1240,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -1034,6 +1272,14 @@ + + + + + + + + @@ -1042,6 +1288,14 @@ + + + + + + + + @@ -1074,6 +1328,14 @@ + + + + + + + + @@ -1110,6 +1372,14 @@ + + + + + + + + @@ -1331,6 +1601,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1339,6 +1641,14 @@ + + + + + + + + @@ -1360,6 +1670,11 @@ + + + + + @@ -1472,6 +1787,14 @@ + + + + + + + + @@ -1671,6 +1994,14 @@ + + + + + + + + @@ -1930,6 +2261,19 @@ + + + + + + + + + + + + + @@ -2277,6 +2621,21 @@ + + + + + + + + + + + + + + + @@ -2285,6 +2644,19 @@ + + + + + + + + + + + + + @@ -2298,6 +2670,24 @@ + + + + + + + + + + + + + + + + + + @@ -2314,6 +2704,11 @@ + + + + + From 58a662671efe28402deab333cb883677df9935a6 Mon Sep 17 00:00:00 2001 From: Cassandra Comar Date: Fri, 28 Apr 2023 07:38:07 -0400 Subject: [PATCH 6/6] lower access modifiers. they got increased when I was moving the tests around to figure out what would make them actually function. --- .../auditor/AttestationProtocol.java | 20 +++++++++---------- .../auditor/ImmutableMapParser.java | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java index 470d5a59f..67f98f163 100644 --- a/app/src/main/java/app/attestation/auditor/AttestationProtocol.java +++ b/app/src/main/java/app/attestation/auditor/AttestationProtocol.java @@ -85,7 +85,7 @@ import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK; import static androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS; -public class AttestationProtocol { +class AttestationProtocol { private static final String TAG = "AttestationProtocol"; // Developer previews set osVersion to 0 as a placeholder value. @@ -287,7 +287,7 @@ public class AttestationProtocol { // Split displayed fingerprint into groups of 4 characters private static final int FINGERPRINT_SPLIT_INTERVAL = 4; - public static class DeviceInfoParser implements XmlMapElemParser { + static class DeviceInfoParser implements XmlMapElemParser { private static ImmutableMap getDeviceMap(Context context, int resMap) throws IOException, XmlPullParserException { @@ -380,15 +380,15 @@ public DeviceInfo parseValue(Context context, XmlResourceParser parser) { } - public static class DeviceInfo { - public final int name; - public final int attestationVersion; - public final int keymasterVersion; - public final boolean rollbackResistant; - public final boolean perUserEncryption; + static class DeviceInfo { + final int name; + final int attestationVersion; + final int keymasterVersion; + final boolean rollbackResistant; + final boolean perUserEncryption; // enforce using StrongBox for new pairings - public final boolean enforceStrongBox; - public final int osName; + final boolean enforceStrongBox; + final int osName; DeviceInfo(final int name, final int attestationVersion, final int keymasterVersion, final boolean rollbackResistant, final boolean perUserEncryption, diff --git a/app/src/main/java/app/attestation/auditor/ImmutableMapParser.java b/app/src/main/java/app/attestation/auditor/ImmutableMapParser.java index cc43a988c..31e077f98 100644 --- a/app/src/main/java/app/attestation/auditor/ImmutableMapParser.java +++ b/app/src/main/java/app/attestation/auditor/ImmutableMapParser.java @@ -17,7 +17,7 @@ interface XmlMapElemParser { public V parseValue(Context context, XmlResourceParser parser); } -public class ImmutableMapParser { +class ImmutableMapParser { public static ImmutableMap getImmutableMapResource(Context context, int hashMapResId, String keyTagName, String valueTagName, XmlMapElemParser mapElemParser) throws XmlPullParserException, IOException, IllegalArgumentException {