• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2019, The Android Open Source Project, Inc.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *   http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.google.android.attestation;
17 
18 import static com.google.android.attestation.AuthorizationList.UserAuthType.FINGERPRINT;
19 import static com.google.android.attestation.AuthorizationList.UserAuthType.PASSWORD;
20 import static com.google.android.attestation.AuthorizationList.UserAuthType.USER_AUTH_TYPE_ANY;
21 import static com.google.android.attestation.AuthorizationList.UserAuthType.USER_AUTH_TYPE_NONE;
22 import static com.google.android.attestation.AuthorizationList.userAuthTypeToEnum;
23 import static com.google.android.attestation.Constants.UINT32_MAX;
24 import static com.google.common.truth.Truth.assertThat;
25 import static com.google.common.truth.Truth8.assertThat;
26 import static org.junit.Assert.fail;
27 
28 import com.google.common.collect.ImmutableSet;
29 import java.io.IOException;
30 import java.time.Instant;
31 
32 import org.bouncycastle.asn1.ASN1Encodable;
33 import org.bouncycastle.asn1.ASN1Sequence;
34 import org.bouncycastle.util.encoders.Base64;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.junit.runners.JUnit4;
38 
39 /** Test for {@link AuthorizationList}. */
40 @RunWith(JUnit4.class)
41 public class AuthorizationListTest {
42 
43   // Generated from certificate with RSA Algorithm and StrongBox Security Level
44   private static final String SW_ENFORCED_EXTENSION_DATA =
45       "MIIBzb+FPQgCBgFr9iKgzL+FRYIBuwSCAbcwggGzMYIBizAMBAdhbmRyb2lkAgEdMBkEFGNvbS5hbmRyb2lkLmtleWNo"
46           + "YWluAgEdMBkEFGNvbS5hbmRyb2lkLnNldHRpbmdzAgEdMBkEFGNvbS5xdGkuZGlhZ3NlcnZpY2VzAgEdMBoEFW"
47           + "NvbS5hbmRyb2lkLmR5bnN5c3RlbQIBHTAdBBhjb20uYW5kcm9pZC5pbnB1dGRldmljZXMCAR0wHwQaY29tLmFu"
48           + "ZHJvaWQubG9jYWx0cmFuc3BvcnQCAR0wHwQaY29tLmFuZHJvaWQubG9jYXRpb24uZnVzZWQCAR0wHwQaY29tLm"
49           + "FuZHJvaWQuc2VydmVyLnRlbGVjb20CAR0wIAQbY29tLmFuZHJvaWQud2FsbHBhcGVyYmFja3VwAgEdMCEEHGNv"
50           + "bS5nb29nbGUuU1NSZXN0YXJ0RGV0ZWN0b3ICAR0wIgQdY29tLmdvb2dsZS5hbmRyb2lkLmhpZGRlbm1lbnUCAQ"
51           + "EwIwQeY29tLmFuZHJvaWQucHJvdmlkZXJzLnNldHRpbmdzAgEdMSIEIDAao8sIETRQHEXxQiq8ZsJCJP1d7V/c"
52           + "jxfmlxdv2Gaq";
53   private static final String TEE_ENFORCED_EXTENSION_DATA =
54       "MIGwoQgxBgIBAgIBA6IDAgEBowQCAggApQUxAwIBBKYIMQYCAQMCAQW/gUgFAgMBAAG/g3cCBQC/hT4DAgEAv4VATDBK"
55           + "BCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoBAgQgco2xJ08fHPFXHeQ4CwSKVUrEo4Dnb1"
56           + "NVCDUpCEqTeAG/hUEDAgEAv4VCBQIDAxSzv4VOBgIEATQV8b+FTwYCBAE0Few=";
57   private static final int ATTESTATION_VERSION = 3;
58 
59   // Some enum values, complete list can be found at:
60   // https://source.android.com/security/keystore/tags
61   private static final int PURPOSE_SIGN = 2;
62   private static final int PURPOSE_VERIFY = 3;
63   private static final int ALGORITHM_RSA = 1;
64   private static final int DIGEST_SHA_2_256 = 4;
65   private static final int PADDING_RSA_PSS = 3;
66   private static final int PADDING_RSA_1_5_SIGN = 5;
67   private static final int ORIGIN_GENERATED = 0;
68 
69   // 2019-07-15T14:56:32.972Z
70   private static final Instant EXPECTED_SW_CREATION_DATETIME = Instant.ofEpochMilli(1563202592972L);
71   private static final byte[] EXPECTED_SW_ATTESTATION_APPLICATION_ID_BYTES =
72       Base64.decode(
73           "MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXljaGFpbgIBHTAZBBRjb20uYW5kcm9pZC5z"
74               + "ZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNlcwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW"
75               + "0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZpY2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNw"
76               + "b3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxvY2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci"
77               + "50ZWxlY29tAgEdMCAEG2NvbS5hbmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNT"
78               + "UmVzdGFydERldGVjdG9yAgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS"
79               + "5hbmRyb2lkLnByb3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcX"
80               + "b9hmqg==");
81   private static final ImmutableSet<Integer> EXPECTED_TEE_PURPOSE =
82       ImmutableSet.of(PURPOSE_SIGN, PURPOSE_VERIFY);
83   private static final Integer EXPECTED_TEE_ALGORITHM = ALGORITHM_RSA;
84   private static final Integer EXPECTED_TEE_KEY_SIZE = 2048;
85   private static final ImmutableSet<Integer> EXPECTED_TEE_DIGEST =
86       ImmutableSet.of(DIGEST_SHA_2_256);
87   private static final ImmutableSet<Integer> EXPECTED_TEE_PADDING =
88       ImmutableSet.of(PADDING_RSA_PSS, PADDING_RSA_1_5_SIGN);
89   private static final Long EXPECTED_TEE_RSA_PUBLIC_COMPONENT = 65537L;
90   private static final Integer EXPECTED_TEE_ORIGIN = ORIGIN_GENERATED;
91   private static final Integer EXPECTED_TEE_OS_VERSION = 0;
92   private static final Integer EXPECTED_TEE_OS_PATCH_LEVEL = 201907;
93   private static final Integer EXPECTED_TEE_VENDOR_PATCH_LEVEL = 20190705;
94   private static final Integer EXPECTED_TEE_BOOT_PATCH_LEVEL = 20190700;
95 
getEncodableAuthorizationList(String extensionData)96   private static ASN1Encodable[] getEncodableAuthorizationList(String extensionData)
97       throws IOException {
98     byte[] extensionDataBytes = Base64.decode(extensionData);
99     return ((ASN1Sequence) ASN1Sequence.fromByteArray(extensionDataBytes)).toArray();
100   }
101 
102   @Test
testCanParseAuthorizationListFromSwEnforced()103   public void testCanParseAuthorizationListFromSwEnforced() throws IOException {
104     AuthorizationList authorizationList =
105         AuthorizationList.createAuthorizationList(
106             getEncodableAuthorizationList(SW_ENFORCED_EXTENSION_DATA), ATTESTATION_VERSION);
107 
108     assertThat(authorizationList.creationDateTime).hasValue(EXPECTED_SW_CREATION_DATETIME);
109     assertThat(authorizationList.rootOfTrust).isEmpty();
110     assertThat(authorizationList.attestationApplicationId).isPresent();
111     assertThat(authorizationList.attestationApplicationIdBytes)
112         .hasValue(EXPECTED_SW_ATTESTATION_APPLICATION_ID_BYTES);
113     assertThat(authorizationList.individualAttestation).isFalse();
114     assertThat(authorizationList.identityCredentialKey).isFalse();
115   }
116 
117   @Test
testCanParseAuthorizationListFromTeeEnforced()118   public void testCanParseAuthorizationListFromTeeEnforced() throws IOException {
119     AuthorizationList authorizationList =
120         AuthorizationList.createAuthorizationList(
121             getEncodableAuthorizationList(TEE_ENFORCED_EXTENSION_DATA), ATTESTATION_VERSION);
122 
123     assertThat(authorizationList.purpose).hasValue(EXPECTED_TEE_PURPOSE);
124     assertThat(authorizationList.algorithm).hasValue(EXPECTED_TEE_ALGORITHM);
125     assertThat(authorizationList.keySize).hasValue(EXPECTED_TEE_KEY_SIZE);
126     assertThat(authorizationList.digest).hasValue(EXPECTED_TEE_DIGEST);
127     assertThat(authorizationList.padding).hasValue(EXPECTED_TEE_PADDING);
128     assertThat(authorizationList.rsaPublicExponent).hasValue(EXPECTED_TEE_RSA_PUBLIC_COMPONENT);
129     assertThat(authorizationList.noAuthRequired).isTrue();
130     assertThat(authorizationList.origin).hasValue(EXPECTED_TEE_ORIGIN);
131     assertThat(authorizationList.rootOfTrust).isPresent();
132     assertThat(authorizationList.osVersion).hasValue(EXPECTED_TEE_OS_VERSION);
133     assertThat(authorizationList.osPatchLevel).hasValue(EXPECTED_TEE_OS_PATCH_LEVEL);
134     assertThat(authorizationList.vendorPatchLevel).hasValue(EXPECTED_TEE_VENDOR_PATCH_LEVEL);
135     assertThat(authorizationList.bootPatchLevel).hasValue(EXPECTED_TEE_BOOT_PATCH_LEVEL);
136     assertThat(authorizationList.individualAttestation).isFalse();
137     assertThat(authorizationList.identityCredentialKey).isFalse();
138   }
139 
140   @Test
testUserAuthTypeToEnum()141   public void testUserAuthTypeToEnum() {
142     assertThat(userAuthTypeToEnum(0L)).isEqualTo(ImmutableSet.of(USER_AUTH_TYPE_NONE));
143     assertThat(userAuthTypeToEnum(1L)).isEqualTo(ImmutableSet.of(PASSWORD));
144     assertThat(userAuthTypeToEnum(2L)).isEqualTo(ImmutableSet.of(FINGERPRINT));
145     assertThat(userAuthTypeToEnum(3L)).isEqualTo(ImmutableSet.of(PASSWORD, FINGERPRINT));
146     assertThat(userAuthTypeToEnum(UINT32_MAX)).isEqualTo(ImmutableSet.of(PASSWORD, FINGERPRINT,
147             USER_AUTH_TYPE_ANY));
148 
149 
150     try {
151       userAuthTypeToEnum(4L);
152       fail();
153     } catch (IllegalArgumentException expected) {
154       assertThat(expected).hasMessageThat().contains("Invalid User Auth Type.");
155     }
156   }
157 
158   private static final String EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION =
159       "MIH0oQgxBgIBAgIBA6IDAgEBowQCAggApQUxAwIBBKYIMQYCAQMCAQW/gUgFAgMBAAG/g3cCBQC/hT4DAgEAv4VATDBK"
160           + "BCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoBAgQgEvR7Lf1t9nD6P2qyUmgiQ0mG+RixYn"
161           + "glj2TaAMZmHn2/hUEFAgMBrbC/hUIFAgMDFRi/hUYIBAZnb29nbGW/hUcHBAVzYXJnb7+FSAcEBXNhcmdvv4VM"
162           + "CAQGR29vZ2xlv4VNCgQIUGl4ZWwgM2G/hU4GAgQBND1lv4VPBgIEATQ9Zb+FUAIFAA==";
163 
164   @Test
testCanParseIndividualAttestation()165   public void testCanParseIndividualAttestation() throws IOException {
166     AuthorizationList authorizationList =
167         AuthorizationList.createAuthorizationList(
168             getEncodableAuthorizationList(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION),
169             ATTESTATION_VERSION);
170 
171     assertThat(authorizationList.individualAttestation).isTrue();
172   }
173 
174   private static final String EXTENTION_DATA_WITH_ID_CREDENTIAL_KEY =
175           "MIH0oQgxBgIBAgIBA6IDAgEBowQCAggApQUxAwIBBKYIMQYCAQMCAQW/" +
176           "gUgFAgMBAAG/g3cCBQC/hT4DAgEAv4VATDBKBCAAAAAAAAAAAAAAAAAA" +
177           "AAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoBAgQgEvR7Lf1t9nD6P2qyUmgi" +
178           "Q0mG+RixYnglj2TaAMZmHn2/hUEFAgMBrbC/hUIFAgMDFRi/hUYIBAZn" +
179           "b29nbGW/hUcHBAVzYXJnb7+FSAcEBXNhcmdvv4VMCAQGR29vZ2xlv4VN" +
180           "CgQIUGl4ZWwgM2G/hU4GAgQBND1lv4VPBgIEATQ9Zb+FUQIFAA==";
181 
182   @Test
testCanParseIdentityCredentialTag()183   public void testCanParseIdentityCredentialTag() throws IOException {
184     AuthorizationList authorizationList =
185             AuthorizationList.createAuthorizationList(
186                     getEncodableAuthorizationList(EXTENTION_DATA_WITH_ID_CREDENTIAL_KEY),
187                     ATTESTATION_VERSION);
188 
189     assertThat(authorizationList.identityCredentialKey).isTrue();
190   }
191 
192   @Test
testCreateAndParse()193   public void testCreateAndParse() throws IOException {
194     AuthorizationList authorizationList =
195         AuthorizationList.createAuthorizationList(
196             getEncodableAuthorizationList(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION),
197             ATTESTATION_VERSION);
198     ASN1Sequence seq = authorizationList.toAsn1Sequence();
199     assertThat(seq.getEncoded("DER"))
200         .isEqualTo(Base64.decode(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION));
201   }
202 }
203