1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.locksettings;
18 
19 import static org.hamcrest.CoreMatchers.is;
20 import static org.junit.Assert.assertThat;
21 
22 import android.platform.test.annotations.Presubmit;
23 
24 import androidx.test.filters.SmallTest;
25 import androidx.test.runner.AndroidJUnit4;
26 
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 
31 import java.io.ByteArrayOutputStream;
32 import java.io.DataOutputStream;
33 
34 import javax.crypto.SecretKey;
35 import javax.crypto.spec.SecretKeySpec;
36 
37 /**
38  * atest FrameworksServicesTests:RebootEscrowDataTest
39  */
40 @SmallTest
41 @Presubmit
42 @RunWith(AndroidJUnit4.class)
43 public class RebootEscrowDataTest {
44     private RebootEscrowKey mKey;
45     private SecretKey mKeyStoreEncryptionKey;
46 
47     // Hex encoding of a randomly generated AES key for test.
48     private static final byte[] TEST_AES_KEY = new byte[] {
49             0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
50             0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
51             0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
52             0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
53     };
54 
55     @Before
generateKey()56     public void generateKey() throws Exception {
57         mKey = RebootEscrowKey.generate();
58         mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
59     }
60 
getTestSp()61     private static byte[] getTestSp() {
62         byte[] testSp = new byte[10];
63         for (int i = 0; i < testSp.length; i++) {
64             testSp[i] = (byte) i;
65         }
66         return testSp;
67     }
68 
69     @Test(expected = NullPointerException.class)
fromEntries_failsOnNull()70     public void fromEntries_failsOnNull() throws Exception {
71         RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, null, mKeyStoreEncryptionKey);
72     }
73 
74     @Test(expected = NullPointerException.class)
fromEncryptedData_failsOnNullData()75     public void fromEncryptedData_failsOnNullData() throws Exception {
76         byte[] testSp = getTestSp();
77         RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp,
78                 mKeyStoreEncryptionKey);
79         RebootEscrowKey key = RebootEscrowKey.fromKeyBytes(expected.getKey().getKeyBytes());
80         RebootEscrowData.fromEncryptedData(key, null, mKeyStoreEncryptionKey);
81     }
82 
83     @Test(expected = NullPointerException.class)
fromEncryptedData_failsOnNullKey()84     public void fromEncryptedData_failsOnNullKey() throws Exception {
85         byte[] testSp = getTestSp();
86         RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp,
87                 mKeyStoreEncryptionKey);
88         RebootEscrowData.fromEncryptedData(null, expected.getBlob(), mKeyStoreEncryptionKey);
89     }
90 
91     @Test
fromEntries_loopback_success()92     public void fromEntries_loopback_success() throws Exception {
93         byte[] testSp = getTestSp();
94         RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp,
95                 mKeyStoreEncryptionKey);
96 
97         RebootEscrowKey key = RebootEscrowKey.fromKeyBytes(expected.getKey().getKeyBytes());
98         RebootEscrowData actual = RebootEscrowData.fromEncryptedData(key, expected.getBlob(),
99                 mKeyStoreEncryptionKey);
100 
101         assertThat(actual.getSpVersion(), is(expected.getSpVersion()));
102         assertThat(actual.getKey().getKeyBytes(), is(expected.getKey().getKeyBytes()));
103         assertThat(actual.getBlob(), is(expected.getBlob()));
104         assertThat(actual.getSyntheticPassword(), is(expected.getSyntheticPassword()));
105     }
106 
107     @Test
aesEncryptedBlob_loopback_success()108     public void aesEncryptedBlob_loopback_success() throws Exception {
109         byte[] testSp = getTestSp();
110         byte [] encrypted = AesEncryptionUtil.encrypt(mKeyStoreEncryptionKey, testSp);
111         byte [] decrypted = AesEncryptionUtil.decrypt(mKeyStoreEncryptionKey, encrypted);
112 
113         assertThat(decrypted, is(testSp));
114     }
115 
116     @Test
fromEncryptedData_legacyVersion_success()117     public void fromEncryptedData_legacyVersion_success() throws Exception {
118         byte[] testSp = getTestSp();
119         byte[] ksEncryptedBlob = AesEncryptionUtil.encrypt(mKey.getKey(), testSp);
120 
121         // Write a legacy blob encrypted only by k_s.
122         ByteArrayOutputStream bos = new ByteArrayOutputStream();
123         DataOutputStream dos = new DataOutputStream(bos);
124         dos.writeInt(1);
125         dos.writeByte(3);
126         dos.write(ksEncryptedBlob);
127         byte[] legacyBlob = bos.toByteArray();
128 
129         RebootEscrowData actual = RebootEscrowData.fromEncryptedData(mKey, legacyBlob, null);
130 
131         assertThat(actual.getSpVersion(), is((byte) 3));
132         assertThat(actual.getKey().getKeyBytes(), is(mKey.getKeyBytes()));
133         assertThat(actual.getSyntheticPassword(), is(testSp));
134     }
135 }
136