Skip to content
This repository has been archived by the owner on Nov 9, 2023. It is now read-only.

Commit

Permalink
Merge pull request #98 from google/acz1
Browse files Browse the repository at this point in the history
Parse and display block mode and key size
  • Loading branch information
leshi committed Feb 10, 2016
2 parents c2e1fb2 + dc508a9 commit 686e088
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 62 deletions.
11 changes: 7 additions & 4 deletions u2f-gae-demo/src/soy/card.soy
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@
<div class="androidAttestationLabel">Android Attestation <button class="toggleAttestationButton">show</button><br/></div>
<div class="androidAttestation">
<ul>
<li><span class="label">Keymaster Version: </span><span class="keymasterVersion"></span><br/>
<li><span class="label">Attestation Challenge: </span><span class="challenge"></span><br/>
<li><span class="label">Cert Chain Verified: </span><span class="chainVerified">false</span><br/>
<li><span class="label">Keymaster Version: </span><span class="keymasterVersion">none</span><br/>
<li><span class="label">Attestation Challenge: </span><span class="challenge">none</span><br/>
<li><span class="label">Software Enforced: </span>
<div class="softwareEnforced">
<ul>
<li><span class="label">Purpose: </span> <span class="purpose"></span>
<li><span class="label">Algorithm: </span> <span class="algorithm"></span>
<li><span class="label">Purpose: </span> <span class="purpose">none</span>
<li><span class="label">Algorithm: </span> <span class="algorithm">none</span>
<li><span class="label">Key Size: </span> <span class="keysize">none</span>
<li><span class="label">Block Mode: </span> <span class="blockmode">none</span>
</ul>
</div>
<li><span class="label">TEE Enforced: </span>
Expand Down
14 changes: 12 additions & 2 deletions u2f-gae-demo/war/js/u2fdemo.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,22 @@ function tokenToDom(token) {
card.querySelector('.challenge').textContent
= token.android_attestation.attestation_challenge;

card.querySelector('.softwareEnforced .algorithm').textContent
= token.android_attestation.software_encoded.algorithm;
if (token.android_attestation.software_encoded.algorithm) {
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(', ');
}
if (token.android_attestation.software_encoded.keysize) {
card.querySelector('.softwareEnforced .keysize').textContent
= token.android_attestation.software_encoded.keysize;
}
if (token.android_attestation.software_encoded.blockmode) {
card.querySelector('.softwareEnforced .blockmode').textContent
= token.android_attestation.software_encoded.blockmode.join(', ');
}

card.querySelector('.teeEnforced').textContent
= JSON.stringify(token.android_attestation.tee_encoded, null, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public class AndroidKeyStoreAttestation {
// Tags for Authorization List
private static final int AUTHZ_PURPOSE_TAG = 1;
private static final int AUTHZ_ALGORITHM_TAG = 2;
private static final int AUTHZ_KEY_SIZE_TAG = 3;
private static final int AUTHZ_BLOCK_MODE_TAG = 4;

private final int keymasterVersion;
private final byte[] attestationChallenge;
Expand Down Expand Up @@ -298,12 +300,48 @@ private static byte[] getAttestationChallenge(ASN1Sequence keyDescriptionSequenc
return X509ExtensionParsingUtil.getByteArray(asn1Encodable);
}

private static List<Purpose> getPurpose(
private static List<Purpose> getPurpose(HashMap<Integer, ASN1Primitive> taggedObjects)
throws CertificateParsingException {
return getListFromTaggedObjectSet(taggedObjects, AUTHZ_PURPOSE_TAG, Purpose.class);
}

private static Algorithm getAlgorithm(HashMap<Integer, ASN1Primitive> taggedObjects)
throws CertificateParsingException {
ASN1Primitive asn1Primitive = taggedObjects.get(AUTHZ_ALGORITHM_TAG);
if (asn1Primitive == null) {
// No algorithm found
return null;
}
return Algorithm.fromValue(X509ExtensionParsingUtil.getInt(asn1Primitive));
}

private static Integer getKeySize(HashMap<Integer, ASN1Primitive> taggedObjects)
throws CertificateParsingException {
ASN1Primitive asn1Primitive = taggedObjects.get(AUTHZ_KEY_SIZE_TAG);
if (asn1Primitive == null) {
// No key size found
return null;
}
return X509ExtensionParsingUtil.getInt(asn1Primitive);
}

private static List<BlockMode> getBlockMode(
HashMap<Integer, ASN1Primitive> softwareEnforcedTaggedObjects)
throws CertificateParsingException {
ASN1Primitive asn1Primitive = softwareEnforcedTaggedObjects.get(AUTHZ_PURPOSE_TAG);
return getListFromTaggedObjectSet(
softwareEnforcedTaggedObjects, AUTHZ_BLOCK_MODE_TAG, BlockMode.class);
}

// TODO(aczeskis): There is a cleaner way of doing this in Java 8. In Java 8, we can make Purpose
// & BlockMode implement an interface (so the function could call .fromInt() on the
// parameterized type). Unfortunately, Java 7 does not allow interfaces to have static methods!
// This decision was fixed in Java 8.
private static <T> List<T> getListFromTaggedObjectSet(
HashMap<Integer, ASN1Primitive> taggedObjects, int tag, Class<T> type)
throws CertificateParsingException {
ASN1Primitive asn1Primitive = taggedObjects.get(tag);
if (asn1Primitive == null) {
// No purpose found
// No tagged object mode found
return null;
}

Expand All @@ -312,33 +350,36 @@ private static List<Purpose> getPurpose(
}

ASN1Set set = (ASN1Set) asn1Primitive;
List<Purpose> purpose = new ArrayList<Purpose>();
List<T> list = new ArrayList<T>();
for (ASN1Encodable asn1Encodable : set.toArray()) {
purpose.add(Purpose.fromValue(X509ExtensionParsingUtil.getInt(asn1Encodable)));
list.add(buildTypeFromInt(X509ExtensionParsingUtil.getInt(asn1Encodable), type));
}

return purpose;
return list;
}

private static Algorithm getAlgorithm(
HashMap<Integer, ASN1Primitive> softwareEnforcedTaggedObjects)
@SuppressWarnings("unchecked")
private static <T> T buildTypeFromInt(int value, Class<T> type)
throws CertificateParsingException {
ASN1Primitive asn1Primitive = softwareEnforcedTaggedObjects.get(AUTHZ_ALGORITHM_TAG);
if (asn1Primitive == null) {
// No algorithm found
return null;
if (type == Purpose.class) {
return (T) Purpose.fromValue(value);
} else if (type == BlockMode.class) {
return (T) BlockMode.fromValue(value);
} else {
throw new CertificateParsingException("Cannot build type " + type.getSimpleName());
}
return Algorithm.fromValue(X509ExtensionParsingUtil.getInt(asn1Primitive));
}

private static AuthorizationList extractAuthorizationList(ASN1Sequence authorizationSequence)
throws CertificateParsingException {
HashMap<Integer, ASN1Primitive> softwareEnforcedTaggedObjects =
HashMap<Integer, ASN1Primitive> taggedObjects =
X509ExtensionParsingUtil.extractTaggedObjects(authorizationSequence);

return new AuthorizationList.Builder()
.setPurpose(getPurpose(softwareEnforcedTaggedObjects))
.setAlgorithm(getAlgorithm(softwareEnforcedTaggedObjects))
.setPurpose(getPurpose(taggedObjects))
.setAlgorithm(getAlgorithm(taggedObjects))
.setKeySize(getKeySize(taggedObjects))
.setBlockMode(getBlockMode(taggedObjects))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

Expand All @@ -14,29 +15,45 @@
public class AuthorizationList {
private final List<Purpose> purposeList;
private final Algorithm algorithm;
private final Integer keySize;
private final List<BlockMode> blockModeList;

@VisibleForTesting
public static final String JSON_ALGORITHM_KEY = "algorithm";

@VisibleForTesting
public static final String JSON_PURPOSE_KEY = "purpose";
@VisibleForTesting
public static final String JSON_KEY_SIZE_KEY = "keysize";
@VisibleForTesting
public static final String JSON_BLOCK_MODE_KEY = "blockmode";

protected AuthorizationList(List<Purpose> purpose, Algorithm algorithm) {
this.purposeList = purpose;
protected AuthorizationList(List<Purpose> purposeList, Algorithm algorithm, Integer keySize,
List<BlockMode> blockModeList) {
this.purposeList = purposeList;
this.algorithm = algorithm;
this.keySize = keySize;
this.blockModeList = blockModeList;
}

public List<Purpose> getPurpose() {
public List<Purpose> getPurposeList() {
return purposeList;
}

public Algorithm getAlgorithm() {
return algorithm;
}

public Integer getKeySize() {
return keySize;
}

public List<BlockMode> getBlockModeList() {
return blockModeList;
}

@Override
public int hashCode() {
return Objects.hash(purposeList, algorithm);
return Objects.hash(purposeList, algorithm, keySize, blockModeList);
}

@Override
Expand All @@ -50,7 +67,9 @@ public boolean equals(Object obj) {

AuthorizationList other = (AuthorizationList) obj;
return Objects.equals(algorithm, other.algorithm)
&& Objects.equals(purposeList, other.purposeList);
&& areEqualIgnoringOrder(purposeList, other.purposeList)
&& Objects.equals(keySize, other.keySize)
&& areEqualIgnoringOrder(blockModeList, other.blockModeList);
}

@Override
Expand All @@ -59,7 +78,7 @@ public String toString() {
stringRepresentation.append("[");

if (purposeList != null) {
stringRepresentation.append("\n purpose: ");
stringRepresentation.append("\n purpose list: ");
stringRepresentation.append(purposeList);
}

Expand All @@ -68,6 +87,16 @@ public String toString() {
stringRepresentation.append(algorithm);
}

if (keySize != null) {
stringRepresentation.append("\n key size: ");
stringRepresentation.append(keySize);
}

if (blockModeList != null) {
stringRepresentation.append("\n block mode list: ");
stringRepresentation.append(blockModeList);
}

stringRepresentation.append("\n]");

return stringRepresentation.toString();
Expand All @@ -77,28 +106,44 @@ public JsonObject toJson() {
JsonObject json = new JsonObject();
if (purposeList != null) {
JsonArray purposeJsonArray = new JsonArray();
for (Purpose p : purposeList) {
purposeJsonArray.add(new JsonPrimitive(p.toString()));
for (Purpose purpose : purposeList) {
purposeJsonArray.add(new JsonPrimitive(purpose.toString()));
}
json.add(JSON_PURPOSE_KEY, purposeJsonArray);
}
if (algorithm != null) {
json.addProperty(JSON_ALGORITHM_KEY, algorithm.toString());
}
if (keySize != null) {
json.addProperty(JSON_KEY_SIZE_KEY, keySize);
}
if (blockModeList != null) {
JsonArray blockModeJsonArray = new JsonArray();
for (BlockMode blockMode : blockModeList) {
blockModeJsonArray.add(new JsonPrimitive(blockMode.toString()));
}
json.add(JSON_BLOCK_MODE_KEY, blockModeJsonArray);
}
return json;
}

public static class Builder {
private List<Purpose> purpose;
private Algorithm algorithm;
private Integer keySize;
private List<BlockMode> blockMode;

public Builder() {
this.purpose = null;
this.algorithm = null;
this.keySize = null;
this.blockMode = null;
}

public Builder setPurpose(List<Purpose> purpose) {
this.purpose = purpose;
if (purpose != null) {
this.purpose = new ArrayList<Purpose>(purpose);
}
return this;
}

Expand All @@ -107,8 +152,28 @@ public Builder setAlgorithm(Algorithm algorithm) {
return this;
}

public Builder setKeySize(Integer keySize) {
this.keySize = keySize;
return this;
}

public Builder setBlockMode(List<BlockMode> blockMode) {
if (blockMode != null) {
this.blockMode = new ArrayList<BlockMode>(blockMode);
}
return this;
}

public AuthorizationList build() {
return new AuthorizationList(this.purpose, this.algorithm);
return new AuthorizationList(this.purpose, this.algorithm, this.keySize, this.blockMode);
}
}

private <T> boolean areEqualIgnoringOrder(List<T> list1, List<T> list2) {
if (list1 != null && list2 != null) {
return list1.containsAll(list2) && list2.containsAll(list1);
}

return list1 == null && list2 == null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.google.u2f.server.impl.attestation.android;

import java.security.cert.CertificateParsingException;

/**
* Keymaster block mode values as taken from: keymaster_defs.h / KeymasterDefs.java
*/
public enum BlockMode {
KM_MODE_ECB(1, "ecb"),
KM_MODE_CBC(2, "cbc"),
KM_MODE_CTR(3, "ctr"),
KM_MODE_GCM(32, "gcm");

private final int value;
private final String description;

public static BlockMode fromValue(int value) throws CertificateParsingException {
for (BlockMode mode : BlockMode.values()) {
if (mode.getValue() == value) {
return mode;
}
}

throw new CertificateParsingException("Invalid block mode value: " + value);
}

public static BlockMode fromString(String string) throws CertificateParsingException {
for (BlockMode mode : BlockMode.values()) {
if (mode.toString().equals(string)) {
return mode;
}
}

throw new CertificateParsingException("Invalid block mode string: " + string);
}

private BlockMode(int value, String description) {
this.value = value;
this.description = description;
}

public int getValue() {
return value;
}

@Override
public String toString() {
return description;
}
}
Loading

0 comments on commit 686e088

Please sign in to comment.