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

Commit 686e088

Browse files
committed
Merge pull request #98 from google/acz1
Parse and display block mode and key size
2 parents c2e1fb2 + dc508a9 commit 686e088

File tree

7 files changed

+301
-62
lines changed

7 files changed

+301
-62
lines changed

u2f-gae-demo/src/soy/card.soy

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@
2626
<div class="androidAttestationLabel">Android Attestation <button class="toggleAttestationButton">show</button><br/></div>
2727
<div class="androidAttestation">
2828
<ul>
29-
<li><span class="label">Keymaster Version: </span><span class="keymasterVersion"></span><br/>
30-
<li><span class="label">Attestation Challenge: </span><span class="challenge"></span><br/>
29+
<li><span class="label">Cert Chain Verified: </span><span class="chainVerified">false</span><br/>
30+
<li><span class="label">Keymaster Version: </span><span class="keymasterVersion">none</span><br/>
31+
<li><span class="label">Attestation Challenge: </span><span class="challenge">none</span><br/>
3132
<li><span class="label">Software Enforced: </span>
3233
<div class="softwareEnforced">
3334
<ul>
34-
<li><span class="label">Purpose: </span> <span class="purpose"></span>
35-
<li><span class="label">Algorithm: </span> <span class="algorithm"></span>
35+
<li><span class="label">Purpose: </span> <span class="purpose">none</span>
36+
<li><span class="label">Algorithm: </span> <span class="algorithm">none</span>
37+
<li><span class="label">Key Size: </span> <span class="keysize">none</span>
38+
<li><span class="label">Block Mode: </span> <span class="blockmode">none</span>
3639
</ul>
3740
</div>
3841
<li><span class="label">TEE Enforced: </span>

u2f-gae-demo/war/js/u2fdemo.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,22 @@ function tokenToDom(token) {
3333
card.querySelector('.challenge').textContent
3434
= token.android_attestation.attestation_challenge;
3535

36-
card.querySelector('.softwareEnforced .algorithm').textContent
37-
= token.android_attestation.software_encoded.algorithm;
36+
if (token.android_attestation.software_encoded.algorithm) {
37+
card.querySelector('.softwareEnforced .algorithm').textContent
38+
= token.android_attestation.software_encoded.algorithm;
39+
}
3840
if (token.android_attestation.software_encoded.purpose) {
3941
card.querySelector('.softwareEnforced .purpose').textContent
4042
= token.android_attestation.software_encoded.purpose.join(', ');
4143
}
44+
if (token.android_attestation.software_encoded.keysize) {
45+
card.querySelector('.softwareEnforced .keysize').textContent
46+
= token.android_attestation.software_encoded.keysize;
47+
}
48+
if (token.android_attestation.software_encoded.blockmode) {
49+
card.querySelector('.softwareEnforced .blockmode').textContent
50+
= token.android_attestation.software_encoded.blockmode.join(', ');
51+
}
4252

4353
card.querySelector('.teeEnforced').textContent
4454
= JSON.stringify(token.android_attestation.tee_encoded, null, 2);

u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestation.java

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class AndroidKeyStoreAttestation {
3434
// Tags for Authorization List
3535
private static final int AUTHZ_PURPOSE_TAG = 1;
3636
private static final int AUTHZ_ALGORITHM_TAG = 2;
37+
private static final int AUTHZ_KEY_SIZE_TAG = 3;
38+
private static final int AUTHZ_BLOCK_MODE_TAG = 4;
3739

3840
private final int keymasterVersion;
3941
private final byte[] attestationChallenge;
@@ -298,12 +300,48 @@ private static byte[] getAttestationChallenge(ASN1Sequence keyDescriptionSequenc
298300
return X509ExtensionParsingUtil.getByteArray(asn1Encodable);
299301
}
300302

301-
private static List<Purpose> getPurpose(
303+
private static List<Purpose> getPurpose(HashMap<Integer, ASN1Primitive> taggedObjects)
304+
throws CertificateParsingException {
305+
return getListFromTaggedObjectSet(taggedObjects, AUTHZ_PURPOSE_TAG, Purpose.class);
306+
}
307+
308+
private static Algorithm getAlgorithm(HashMap<Integer, ASN1Primitive> taggedObjects)
309+
throws CertificateParsingException {
310+
ASN1Primitive asn1Primitive = taggedObjects.get(AUTHZ_ALGORITHM_TAG);
311+
if (asn1Primitive == null) {
312+
// No algorithm found
313+
return null;
314+
}
315+
return Algorithm.fromValue(X509ExtensionParsingUtil.getInt(asn1Primitive));
316+
}
317+
318+
private static Integer getKeySize(HashMap<Integer, ASN1Primitive> taggedObjects)
319+
throws CertificateParsingException {
320+
ASN1Primitive asn1Primitive = taggedObjects.get(AUTHZ_KEY_SIZE_TAG);
321+
if (asn1Primitive == null) {
322+
// No key size found
323+
return null;
324+
}
325+
return X509ExtensionParsingUtil.getInt(asn1Primitive);
326+
}
327+
328+
private static List<BlockMode> getBlockMode(
302329
HashMap<Integer, ASN1Primitive> softwareEnforcedTaggedObjects)
303330
throws CertificateParsingException {
304-
ASN1Primitive asn1Primitive = softwareEnforcedTaggedObjects.get(AUTHZ_PURPOSE_TAG);
331+
return getListFromTaggedObjectSet(
332+
softwareEnforcedTaggedObjects, AUTHZ_BLOCK_MODE_TAG, BlockMode.class);
333+
}
334+
335+
// TODO(aczeskis): There is a cleaner way of doing this in Java 8. In Java 8, we can make Purpose
336+
// & BlockMode implement an interface (so the function could call .fromInt() on the
337+
// parameterized type). Unfortunately, Java 7 does not allow interfaces to have static methods!
338+
// This decision was fixed in Java 8.
339+
private static <T> List<T> getListFromTaggedObjectSet(
340+
HashMap<Integer, ASN1Primitive> taggedObjects, int tag, Class<T> type)
341+
throws CertificateParsingException {
342+
ASN1Primitive asn1Primitive = taggedObjects.get(tag);
305343
if (asn1Primitive == null) {
306-
// No purpose found
344+
// No tagged object mode found
307345
return null;
308346
}
309347

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

314352
ASN1Set set = (ASN1Set) asn1Primitive;
315-
List<Purpose> purpose = new ArrayList<Purpose>();
353+
List<T> list = new ArrayList<T>();
316354
for (ASN1Encodable asn1Encodable : set.toArray()) {
317-
purpose.add(Purpose.fromValue(X509ExtensionParsingUtil.getInt(asn1Encodable)));
355+
list.add(buildTypeFromInt(X509ExtensionParsingUtil.getInt(asn1Encodable), type));
318356
}
319357

320-
return purpose;
358+
return list;
321359
}
322360

323-
private static Algorithm getAlgorithm(
324-
HashMap<Integer, ASN1Primitive> softwareEnforcedTaggedObjects)
361+
@SuppressWarnings("unchecked")
362+
private static <T> T buildTypeFromInt(int value, Class<T> type)
325363
throws CertificateParsingException {
326-
ASN1Primitive asn1Primitive = softwareEnforcedTaggedObjects.get(AUTHZ_ALGORITHM_TAG);
327-
if (asn1Primitive == null) {
328-
// No algorithm found
329-
return null;
364+
if (type == Purpose.class) {
365+
return (T) Purpose.fromValue(value);
366+
} else if (type == BlockMode.class) {
367+
return (T) BlockMode.fromValue(value);
368+
} else {
369+
throw new CertificateParsingException("Cannot build type " + type.getSimpleName());
330370
}
331-
return Algorithm.fromValue(X509ExtensionParsingUtil.getInt(asn1Primitive));
332371
}
333372

334373
private static AuthorizationList extractAuthorizationList(ASN1Sequence authorizationSequence)
335374
throws CertificateParsingException {
336-
HashMap<Integer, ASN1Primitive> softwareEnforcedTaggedObjects =
375+
HashMap<Integer, ASN1Primitive> taggedObjects =
337376
X509ExtensionParsingUtil.extractTaggedObjects(authorizationSequence);
338377

339378
return new AuthorizationList.Builder()
340-
.setPurpose(getPurpose(softwareEnforcedTaggedObjects))
341-
.setAlgorithm(getAlgorithm(softwareEnforcedTaggedObjects))
379+
.setPurpose(getPurpose(taggedObjects))
380+
.setAlgorithm(getAlgorithm(taggedObjects))
381+
.setKeySize(getKeySize(taggedObjects))
382+
.setBlockMode(getBlockMode(taggedObjects))
342383
.build();
343384
}
344385
}

u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.google.gson.JsonObject;
66
import com.google.gson.JsonPrimitive;
77

8+
import java.util.ArrayList;
89
import java.util.List;
910
import java.util.Objects;
1011

@@ -14,29 +15,45 @@
1415
public class AuthorizationList {
1516
private final List<Purpose> purposeList;
1617
private final Algorithm algorithm;
18+
private final Integer keySize;
19+
private final List<BlockMode> blockModeList;
1720

1821
@VisibleForTesting
1922
public static final String JSON_ALGORITHM_KEY = "algorithm";
20-
2123
@VisibleForTesting
2224
public static final String JSON_PURPOSE_KEY = "purpose";
25+
@VisibleForTesting
26+
public static final String JSON_KEY_SIZE_KEY = "keysize";
27+
@VisibleForTesting
28+
public static final String JSON_BLOCK_MODE_KEY = "blockmode";
2329

24-
protected AuthorizationList(List<Purpose> purpose, Algorithm algorithm) {
25-
this.purposeList = purpose;
30+
protected AuthorizationList(List<Purpose> purposeList, Algorithm algorithm, Integer keySize,
31+
List<BlockMode> blockModeList) {
32+
this.purposeList = purposeList;
2633
this.algorithm = algorithm;
34+
this.keySize = keySize;
35+
this.blockModeList = blockModeList;
2736
}
2837

29-
public List<Purpose> getPurpose() {
38+
public List<Purpose> getPurposeList() {
3039
return purposeList;
3140
}
3241

3342
public Algorithm getAlgorithm() {
3443
return algorithm;
3544
}
3645

46+
public Integer getKeySize() {
47+
return keySize;
48+
}
49+
50+
public List<BlockMode> getBlockModeList() {
51+
return blockModeList;
52+
}
53+
3754
@Override
3855
public int hashCode() {
39-
return Objects.hash(purposeList, algorithm);
56+
return Objects.hash(purposeList, algorithm, keySize, blockModeList);
4057
}
4158

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

5168
AuthorizationList other = (AuthorizationList) obj;
5269
return Objects.equals(algorithm, other.algorithm)
53-
&& Objects.equals(purposeList, other.purposeList);
70+
&& areEqualIgnoringOrder(purposeList, other.purposeList)
71+
&& Objects.equals(keySize, other.keySize)
72+
&& areEqualIgnoringOrder(blockModeList, other.blockModeList);
5473
}
5574

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

6180
if (purposeList != null) {
62-
stringRepresentation.append("\n purpose: ");
81+
stringRepresentation.append("\n purpose list: ");
6382
stringRepresentation.append(purposeList);
6483
}
6584

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

90+
if (keySize != null) {
91+
stringRepresentation.append("\n key size: ");
92+
stringRepresentation.append(keySize);
93+
}
94+
95+
if (blockModeList != null) {
96+
stringRepresentation.append("\n block mode list: ");
97+
stringRepresentation.append(blockModeList);
98+
}
99+
71100
stringRepresentation.append("\n]");
72101

73102
return stringRepresentation.toString();
@@ -77,28 +106,44 @@ public JsonObject toJson() {
77106
JsonObject json = new JsonObject();
78107
if (purposeList != null) {
79108
JsonArray purposeJsonArray = new JsonArray();
80-
for (Purpose p : purposeList) {
81-
purposeJsonArray.add(new JsonPrimitive(p.toString()));
109+
for (Purpose purpose : purposeList) {
110+
purposeJsonArray.add(new JsonPrimitive(purpose.toString()));
82111
}
83112
json.add(JSON_PURPOSE_KEY, purposeJsonArray);
84113
}
85114
if (algorithm != null) {
86115
json.addProperty(JSON_ALGORITHM_KEY, algorithm.toString());
87116
}
117+
if (keySize != null) {
118+
json.addProperty(JSON_KEY_SIZE_KEY, keySize);
119+
}
120+
if (blockModeList != null) {
121+
JsonArray blockModeJsonArray = new JsonArray();
122+
for (BlockMode blockMode : blockModeList) {
123+
blockModeJsonArray.add(new JsonPrimitive(blockMode.toString()));
124+
}
125+
json.add(JSON_BLOCK_MODE_KEY, blockModeJsonArray);
126+
}
88127
return json;
89128
}
90129

91130
public static class Builder {
92131
private List<Purpose> purpose;
93132
private Algorithm algorithm;
133+
private Integer keySize;
134+
private List<BlockMode> blockMode;
94135

95136
public Builder() {
96137
this.purpose = null;
97138
this.algorithm = null;
139+
this.keySize = null;
140+
this.blockMode = null;
98141
}
99142

100143
public Builder setPurpose(List<Purpose> purpose) {
101-
this.purpose = purpose;
144+
if (purpose != null) {
145+
this.purpose = new ArrayList<Purpose>(purpose);
146+
}
102147
return this;
103148
}
104149

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

155+
public Builder setKeySize(Integer keySize) {
156+
this.keySize = keySize;
157+
return this;
158+
}
159+
160+
public Builder setBlockMode(List<BlockMode> blockMode) {
161+
if (blockMode != null) {
162+
this.blockMode = new ArrayList<BlockMode>(blockMode);
163+
}
164+
return this;
165+
}
166+
110167
public AuthorizationList build() {
111-
return new AuthorizationList(this.purpose, this.algorithm);
168+
return new AuthorizationList(this.purpose, this.algorithm, this.keySize, this.blockMode);
169+
}
170+
}
171+
172+
private <T> boolean areEqualIgnoringOrder(List<T> list1, List<T> list2) {
173+
if (list1 != null && list2 != null) {
174+
return list1.containsAll(list2) && list2.containsAll(list1);
112175
}
176+
177+
return list1 == null && list2 == null;
113178
}
114179
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.google.u2f.server.impl.attestation.android;
2+
3+
import java.security.cert.CertificateParsingException;
4+
5+
/**
6+
* Keymaster block mode values as taken from: keymaster_defs.h / KeymasterDefs.java
7+
*/
8+
public enum BlockMode {
9+
KM_MODE_ECB(1, "ecb"),
10+
KM_MODE_CBC(2, "cbc"),
11+
KM_MODE_CTR(3, "ctr"),
12+
KM_MODE_GCM(32, "gcm");
13+
14+
private final int value;
15+
private final String description;
16+
17+
public static BlockMode fromValue(int value) throws CertificateParsingException {
18+
for (BlockMode mode : BlockMode.values()) {
19+
if (mode.getValue() == value) {
20+
return mode;
21+
}
22+
}
23+
24+
throw new CertificateParsingException("Invalid block mode value: " + value);
25+
}
26+
27+
public static BlockMode fromString(String string) throws CertificateParsingException {
28+
for (BlockMode mode : BlockMode.values()) {
29+
if (mode.toString().equals(string)) {
30+
return mode;
31+
}
32+
}
33+
34+
throw new CertificateParsingException("Invalid block mode string: " + string);
35+
}
36+
37+
private BlockMode(int value, String description) {
38+
this.value = value;
39+
this.description = description;
40+
}
41+
42+
public int getValue() {
43+
return value;
44+
}
45+
46+
@Override
47+
public String toString() {
48+
return description;
49+
}
50+
}

0 commit comments

Comments
 (0)