• 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.ByteArrayOutputStream;
30 import java.io.IOException;
31 import java.time.Instant;
32 import java.util.Set;
33 
34 import org.bouncycastle.asn1.ASN1Encodable;
35 import org.bouncycastle.asn1.ASN1Sequence;
36 import org.bouncycastle.util.encoders.Base64;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.junit.runners.JUnit4;
40 
41 /** Test for {@link AuthorizationList}. */
42 @RunWith(JUnit4.class)
43 public class AuthorizationListTest {
44 
45   // Generated from certificate with RSA Algorithm and StrongBox Security Level
46   private static final String SW_ENFORCED_EXTENSION_DATA =
47       "MIIBzb+FPQgCBgFr9iKgzL+FRYIBuwSCAbcwggGzMYIBizAMBAdhbmRyb2lkAgEdMBkEFGNvbS5hbmRyb2lkLmtleWNo"
48           + "YWluAgEdMBkEFGNvbS5hbmRyb2lkLnNldHRpbmdzAgEdMBkEFGNvbS5xdGkuZGlhZ3NlcnZpY2VzAgEdMBoEFW"
49           + "NvbS5hbmRyb2lkLmR5bnN5c3RlbQIBHTAdBBhjb20uYW5kcm9pZC5pbnB1dGRldmljZXMCAR0wHwQaY29tLmFu"
50           + "ZHJvaWQubG9jYWx0cmFuc3BvcnQCAR0wHwQaY29tLmFuZHJvaWQubG9jYXRpb24uZnVzZWQCAR0wHwQaY29tLm"
51           + "FuZHJvaWQuc2VydmVyLnRlbGVjb20CAR0wIAQbY29tLmFuZHJvaWQud2FsbHBhcGVyYmFja3VwAgEdMCEEHGNv"
52           + "bS5nb29nbGUuU1NSZXN0YXJ0RGV0ZWN0b3ICAR0wIgQdY29tLmdvb2dsZS5hbmRyb2lkLmhpZGRlbm1lbnUCAQ"
53           + "EwIwQeY29tLmFuZHJvaWQucHJvdmlkZXJzLnNldHRpbmdzAgEdMSIEIDAao8sIETRQHEXxQiq8ZsJCJP1d7V/c"
54           + "jxfmlxdv2Gaq";
55   private static final String TEE_ENFORCED_EXTENSION_DATA =
56       "MIGwoQgxBgIBAgIBA6IDAgEBowQCAggApQUxAwIBBKYIMQYCAQMCAQW/gUgFAgMBAAG/g3cCBQC/hT4DAgEAv4VATDBK"
57           + "BCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoBAgQgco2xJ08fHPFXHeQ4CwSKVUrEo4Dnb1"
58           + "NVCDUpCEqTeAG/hUEDAgEAv4VCBQIDAxSzv4VOBgIEATQV8b+FTwYCBAE0Few=";
59   private static final int ATTESTATION_VERSION = 3;
60 
61   // Some enum values, complete list can be found at:
62   // https://source.android.com/security/keystore/tags
63   private static final int PURPOSE_SIGN = 2;
64   private static final int PURPOSE_VERIFY = 3;
65   private static final int ALGORITHM_RSA = 1;
66   private static final int DIGEST_SHA_2_256 = 4;
67   private static final int PADDING_RSA_PSS = 3;
68   private static final int PADDING_RSA_1_5_SIGN = 5;
69   private static final int ORIGIN_GENERATED = 0;
70 
71   // 2019-07-15T14:56:32.972Z
72   private static final Instant EXPECTED_SW_CREATION_DATETIME = Instant.ofEpochMilli(1563202592972L);
73   private static final byte[] EXPECTED_SW_ATTESTATION_APPLICATION_ID_BYTES =
74       Base64.decode(
75           "MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXljaGFpbgIBHTAZBBRjb20uYW5kcm9pZC5z"
76               + "ZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNlcwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW"
77               + "0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZpY2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNw"
78               + "b3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxvY2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci"
79               + "50ZWxlY29tAgEdMCAEG2NvbS5hbmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNT"
80               + "UmVzdGFydERldGVjdG9yAgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS"
81               + "5hbmRyb2lkLnByb3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcX"
82               + "b9hmqg==");
83   private static final ImmutableSet<Integer> EXPECTED_TEE_PURPOSE =
84       ImmutableSet.of(PURPOSE_SIGN, PURPOSE_VERIFY);
85   private static final Integer EXPECTED_TEE_ALGORITHM = ALGORITHM_RSA;
86   private static final Integer EXPECTED_TEE_KEY_SIZE = 2048;
87   private static final ImmutableSet<Integer> EXPECTED_TEE_DIGEST =
88       ImmutableSet.of(DIGEST_SHA_2_256);
89   private static final ImmutableSet<Integer> EXPECTED_TEE_PADDING =
90       ImmutableSet.of(PADDING_RSA_PSS, PADDING_RSA_1_5_SIGN);
91   private static final Long EXPECTED_TEE_RSA_PUBLIC_COMPONENT = 65537L;
92   private static final Integer EXPECTED_TEE_ORIGIN = ORIGIN_GENERATED;
93   private static final Integer EXPECTED_TEE_OS_VERSION = 0;
94   private static final Integer EXPECTED_TEE_OS_PATCH_LEVEL = 201907;
95   private static final Integer EXPECTED_TEE_VENDOR_PATCH_LEVEL = 20190705;
96   private static final Integer EXPECTED_TEE_BOOT_PATCH_LEVEL = 20190700;
97 
getEncodableAuthorizationList(String extensionData)98   private static ASN1Encodable[] getEncodableAuthorizationList(String extensionData)
99       throws IOException {
100     byte[] extensionDataBytes = Base64.decode(extensionData);
101     return ((ASN1Sequence) ASN1Sequence.fromByteArray(extensionDataBytes)).toArray();
102   }
103 
104   @Test
testCanParseAuthorizationListFromSwEnforced()105   public void testCanParseAuthorizationListFromSwEnforced() throws IOException {
106     AuthorizationList authorizationList =
107         AuthorizationList.createAuthorizationList(
108             getEncodableAuthorizationList(SW_ENFORCED_EXTENSION_DATA), ATTESTATION_VERSION);
109 
110     assertThat(authorizationList.creationDateTime).hasValue(EXPECTED_SW_CREATION_DATETIME);
111     assertThat(authorizationList.rootOfTrust).isEmpty();
112     assertThat(authorizationList.attestationApplicationId).isPresent();
113     assertThat(authorizationList.attestationApplicationIdBytes)
114         .hasValue(EXPECTED_SW_ATTESTATION_APPLICATION_ID_BYTES);
115     assertThat(authorizationList.individualAttestation).isFalse();
116   }
117 
118   @Test
testCanParseAuthorizationListFromTeeEnforced()119   public void testCanParseAuthorizationListFromTeeEnforced() throws IOException {
120     AuthorizationList authorizationList =
121         AuthorizationList.createAuthorizationList(
122             getEncodableAuthorizationList(TEE_ENFORCED_EXTENSION_DATA), ATTESTATION_VERSION);
123 
124     assertThat(authorizationList.purpose).hasValue(EXPECTED_TEE_PURPOSE);
125     assertThat(authorizationList.algorithm).hasValue(EXPECTED_TEE_ALGORITHM);
126     assertThat(authorizationList.keySize).hasValue(EXPECTED_TEE_KEY_SIZE);
127     assertThat(authorizationList.digest).hasValue(EXPECTED_TEE_DIGEST);
128     assertThat(authorizationList.padding).hasValue(EXPECTED_TEE_PADDING);
129     assertThat(authorizationList.rsaPublicExponent).hasValue(EXPECTED_TEE_RSA_PUBLIC_COMPONENT);
130     assertThat(authorizationList.noAuthRequired).isTrue();
131     assertThat(authorizationList.origin).hasValue(EXPECTED_TEE_ORIGIN);
132     assertThat(authorizationList.rootOfTrust).isPresent();
133     assertThat(authorizationList.osVersion).hasValue(EXPECTED_TEE_OS_VERSION);
134     assertThat(authorizationList.osPatchLevel).hasValue(EXPECTED_TEE_OS_PATCH_LEVEL);
135     assertThat(authorizationList.vendorPatchLevel).hasValue(EXPECTED_TEE_VENDOR_PATCH_LEVEL);
136     assertThat(authorizationList.bootPatchLevel).hasValue(EXPECTED_TEE_BOOT_PATCH_LEVEL);
137     assertThat(authorizationList.individualAttestation).isFalse();
138   }
139 
140   @Test
testUserAuthTypeToEnum()141   public void testUserAuthTypeToEnum() {
142     assertThat(userAuthTypeToEnum(0L)).isEqualTo(Set.of(USER_AUTH_TYPE_NONE));
143     assertThat(userAuthTypeToEnum(1L)).isEqualTo(Set.of(PASSWORD));
144     assertThat(userAuthTypeToEnum(2L)).isEqualTo(Set.of(FINGERPRINT));
145     assertThat(userAuthTypeToEnum(3L)).isEqualTo(Set.of(PASSWORD, FINGERPRINT));
146     assertThat(userAuthTypeToEnum(UINT32_MAX)).isEqualTo(Set.of(PASSWORD, FINGERPRINT, USER_AUTH_TYPE_ANY));
147 
148 
149     try {
150       userAuthTypeToEnum(4L);
151       fail();
152     } catch (IllegalArgumentException expected) {
153       assertThat(expected).hasMessageThat().contains("Invalid User Auth Type.");
154     }
155   }
156 
157   private static final String EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION =
158       "MIH0oQgxBgIBAgIBA6IDAgEBowQCAggApQUxAwIBBKYIMQYCAQMCAQW/gUgFAgMBAAG/g3cCBQC/hT4DAgEAv4VATDBK"
159           + "BCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoBAgQgEvR7Lf1t9nD6P2qyUmgiQ0mG+RixYn"
160           + "glj2TaAMZmHn2/hUEFAgMBrbC/hUIFAgMDFRi/hUYIBAZnb29nbGW/hUcHBAVzYXJnb7+FSAcEBXNhcmdvv4VM"
161           + "CAQGR29vZ2xlv4VNCgQIUGl4ZWwgM2G/hU4GAgQBND1lv4VPBgIEATQ9Zb+FUAIFAA==";
162 
163   @Test
testCanParseIndividualAttestation()164   public void testCanParseIndividualAttestation() throws IOException {
165     AuthorizationList authorizationList =
166         AuthorizationList.createAuthorizationList(
167             getEncodableAuthorizationList(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION),
168             ATTESTATION_VERSION);
169 
170     assertThat(authorizationList.individualAttestation).isTrue();
171   }
172 
173   @Test
testCreateAndParse()174   public void testCreateAndParse() throws IOException {
175     AuthorizationList authorizationList =
176         AuthorizationList.createAuthorizationList(
177             getEncodableAuthorizationList(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION),
178             ATTESTATION_VERSION);
179     ASN1Sequence seq = authorizationList.toAsn1Sequence();
180     assertThat(seq.getEncoded("DER"))
181         .isEqualTo(Base64.decode(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION));
182   }
183 }
184