From a53db4679778f6cb067f98e290db26daab06df02 Mon Sep 17 00:00:00 2001 From: Alexei Czeskis Date: Tue, 9 Feb 2016 13:05:03 -0800 Subject: [PATCH] Makes the software enforced section more pretty Splits up the various software enforced properties into their own items instead of just displaying the JSON strigified representation. --- u2f-gae-demo/src/soy/card.soy | 7 +- u2f-gae-demo/war/js/u2fdemo.js | 10 +- .../android/AuthorizationList.java | 34 +++++-- .../impl/attestation/android/Purpose.java | 10 ++ .../android/AuthorizationListTest.java | 92 +++++++++++++++++++ 5 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AuthorizationListTest.java diff --git a/u2f-gae-demo/src/soy/card.soy b/u2f-gae-demo/src/soy/card.soy index 364d15a..f9fa969 100644 --- a/u2f-gae-demo/src/soy/card.soy +++ b/u2f-gae-demo/src/soy/card.soy @@ -29,7 +29,12 @@
  • Keymaster Version:
  • Attestation Challenge:
  • Software Enforced: -
    +
    +
      +
    • Purpose: +
    • Algorithm: +
    +
  • TEE Enforced: diff --git a/u2f-gae-demo/war/js/u2fdemo.js b/u2f-gae-demo/war/js/u2fdemo.js index 9c7cca2..124f5ea 100644 --- a/u2f-gae-demo/war/js/u2fdemo.js +++ b/u2f-gae-demo/war/js/u2fdemo.js @@ -33,8 +33,14 @@ function tokenToDom(token) { = token.android_attestation.keymaster_version; card.querySelector('.challenge').textContent = token.android_attestation.attestation_challenge; - card.querySelector('.softwareEnforced').textContent - = JSON.stringify(token.android_attestation.software_encoded, null, 2); + + card.querySelector('.softwareEnforced .algorithm').textContent + = token.android_attestation.software_encoded.algorithm; + if (token.android_attestation.software_encoded.purpose) { + card.querySelector('.softwareEnforced .purpose').textContent + = token.android_attestation.software_encoded.purpose.join(', '); + } + card.querySelector('.teeEnforced').textContent = JSON.stringify(token.android_attestation.tee_encoded, null, 2); } diff --git a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java index 294e7e8..45c4fdb 100644 --- a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java +++ b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java @@ -1,6 +1,9 @@ package com.google.u2f.server.impl.attestation.android; +import com.google.common.annotations.VisibleForTesting; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import java.util.List; import java.util.Objects; @@ -9,16 +12,22 @@ * Authorization List that describes a Keymaster key */ public class AuthorizationList { - private final List purpose; + private final List purposeList; private final Algorithm algorithm; + @VisibleForTesting + public static final String JSON_ALGORITHM_KEY = "algorithm"; + + @VisibleForTesting + public static final String JSON_PURPOSE_KEY = "purpose"; + protected AuthorizationList(List purpose, Algorithm algorithm) { - this.purpose = purpose; + this.purposeList = purpose; this.algorithm = algorithm; } public List getPurpose() { - return purpose; + return purposeList; } public Algorithm getAlgorithm() { @@ -27,7 +36,7 @@ public Algorithm getAlgorithm() { @Override public int hashCode() { - return Objects.hash(purpose, algorithm); + return Objects.hash(purposeList, algorithm); } @Override @@ -40,7 +49,8 @@ public boolean equals(Object obj) { return false; AuthorizationList other = (AuthorizationList) obj; - return Objects.equals(algorithm, other.algorithm) && Objects.equals(purpose, other.purpose); + return Objects.equals(algorithm, other.algorithm) + && Objects.equals(purposeList, other.purposeList); } @Override @@ -48,9 +58,9 @@ public String toString() { StringBuilder stringRepresentation = new StringBuilder(); stringRepresentation.append("["); - if (purpose != null) { + if (purposeList != null) { stringRepresentation.append("\n purpose: "); - stringRepresentation.append(purpose); + stringRepresentation.append(purposeList); } if (algorithm != null) { @@ -65,11 +75,15 @@ public String toString() { public JsonObject toJson() { JsonObject json = new JsonObject(); - if (purpose != null) { - json.addProperty("purpose", purpose.toString()); + if (purposeList != null) { + JsonArray purposeJsonArray = new JsonArray(); + for (Purpose p : purposeList) { + purposeJsonArray.add(new JsonPrimitive(p.toString())); + } + json.add(JSON_PURPOSE_KEY, purposeJsonArray); } if (algorithm != null) { - json.addProperty("algorithm", algorithm.toString()); + json.addProperty(JSON_ALGORITHM_KEY, algorithm.toString()); } return json; } diff --git a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/Purpose.java b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/Purpose.java index df7cc16..75d9396 100644 --- a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/Purpose.java +++ b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/Purpose.java @@ -24,6 +24,16 @@ public static Purpose fromValue(int value) throws CertificateParsingException { throw new CertificateParsingException("Invalid purpose value: " + value); } + public static Purpose fromString(String string) throws CertificateParsingException { + for (Purpose purpose : Purpose.values()) { + if (purpose.toString().equals(string)) { + return purpose; + } + } + + throw new CertificateParsingException("Invalid purpose value: " + string); + } + private Purpose(int value, String description) { this.value = value; this.description = description; diff --git a/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AuthorizationListTest.java b/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AuthorizationListTest.java new file mode 100644 index 0000000..ffaf09d --- /dev/null +++ b/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AuthorizationListTest.java @@ -0,0 +1,92 @@ +package com.google.u2f.server.impl.attestation.android; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * Unit tests for {@link AuthorizationList} + */ +@RunWith(JUnit4.class) +public class AuthorizationListTest { + private static final List EMPTY_PURPOSE = new ArrayList(); + private static final List ONE_PURPOSE = Arrays.asList(Purpose.KM_PURPOSE_SIGN); + private static final List TWO_PURPOSES = + Arrays.asList(Purpose.KM_PURPOSE_SIGN, Purpose.KM_PURPOSE_VERIFY); + + @Test + public void toJson_nullValues() throws Exception { + JsonObject json = new AuthorizationList(null, null).toJson(); + + assertFalse(json.has(AuthorizationList.JSON_ALGORITHM_KEY)); + assertFalse(json.has(AuthorizationList.JSON_PURPOSE_KEY)); + } + + @Test + public void toJson_emptyPurpose() throws Exception { + AuthorizationList authorizationList = + new AuthorizationList(EMPTY_PURPOSE, Algorithm.KM_ALGORITHM_EC); + JsonObject json = authorizationList.toJson(); + + assertEquals( + Algorithm.KM_ALGORITHM_EC.toString(), + json.get(AuthorizationList.JSON_ALGORITHM_KEY).getAsString()); + List extractedPurpose = extractPurposeListFromJsonArray( + json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray()); + + assertTrue(EMPTY_PURPOSE.containsAll(extractedPurpose)); + assertTrue(extractedPurpose.containsAll(EMPTY_PURPOSE)); + } + + @Test + public void toJson_onePurpose() throws Exception { + AuthorizationList authorizationList = + new AuthorizationList(ONE_PURPOSE, Algorithm.KM_ALGORITHM_HMAC); + JsonObject json = authorizationList.toJson(); + + assertEquals( + Algorithm.KM_ALGORITHM_HMAC.toString(), + json.get(AuthorizationList.JSON_ALGORITHM_KEY).getAsString()); + List extractedPurpose = extractPurposeListFromJsonArray( + json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray()); + + assertTrue(ONE_PURPOSE.containsAll(extractedPurpose)); + assertTrue(extractedPurpose.containsAll(ONE_PURPOSE)); + } + + @Test + public void toJson_twoPurposes() throws Exception { + JsonObject json = new AuthorizationList(TWO_PURPOSES, Algorithm.KM_ALGORITHM_RSA).toJson(); + + assertEquals( + Algorithm.KM_ALGORITHM_RSA.toString(), + json.get(AuthorizationList.JSON_ALGORITHM_KEY).getAsString()); + List extractedPurpose = extractPurposeListFromJsonArray( + json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray()); + + assertTrue(TWO_PURPOSES.containsAll(extractedPurpose)); + assertTrue(extractedPurpose.containsAll(TWO_PURPOSES)); + } + + private List extractPurposeListFromJsonArray(JsonArray array) throws Exception { + Iterator iterator = array.iterator(); + List result = new ArrayList(); + while (iterator.hasNext()) { + result.add(Purpose.fromString(iterator.next().getAsString())); + } + return result; + } +}