• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.DataInputStream;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 import java.util.Objects;
25 
26 import javax.crypto.SecretKey;
27 
28 /**
29  * Holds the data necessary to complete a reboot escrow of the Synthetic Password.
30  */
31 class RebootEscrowData {
32     /**
33      * This is the current version of the escrow data format. This should be incremented if the
34      * format on disk is changed.
35      */
36     private static final int CURRENT_VERSION = 2;
37 
38     /**
39     * This is the legacy version of the escrow data format for R builds. The escrow data is only
40     * encrypted by the escrow key, without additional wrap of another key from keystore.
41     */
42     private static final int LEGACY_SINGLE_ENCRYPTED_VERSION = 1;
43 
RebootEscrowData(byte spVersion, byte[] syntheticPassword, byte[] blob, RebootEscrowKey key)44     private RebootEscrowData(byte spVersion, byte[] syntheticPassword, byte[] blob,
45             RebootEscrowKey key) {
46         mSpVersion = spVersion;
47         mSyntheticPassword = syntheticPassword;
48         mBlob = blob;
49         mKey = key;
50     }
51 
52     private final byte mSpVersion;
53     private final byte[] mSyntheticPassword;
54     private final byte[] mBlob;
55     private final RebootEscrowKey mKey;
56 
getSpVersion()57     public byte getSpVersion() {
58         return mSpVersion;
59     }
60 
getSyntheticPassword()61     public byte[] getSyntheticPassword() {
62         return mSyntheticPassword;
63     }
64 
getBlob()65     public byte[] getBlob() {
66         return mBlob;
67     }
68 
getKey()69     public RebootEscrowKey getKey() {
70         return mKey;
71     }
72 
decryptBlobCurrentVersion(SecretKey kk, RebootEscrowKey ks, DataInputStream dis)73     private static byte[] decryptBlobCurrentVersion(SecretKey kk, RebootEscrowKey ks,
74             DataInputStream dis) throws IOException {
75         if (kk == null) {
76             throw new IOException("Failed to find wrapper key in keystore, cannot decrypt the"
77                     + " escrow data");
78         }
79 
80         // Decrypt the blob with the key from keystore first, then decrypt again with the reboot
81         // escrow key.
82         byte[] ksEncryptedBlob = AesEncryptionUtil.decrypt(kk, dis);
83         return AesEncryptionUtil.decrypt(ks.getKey(), ksEncryptedBlob);
84     }
85 
fromEncryptedData(RebootEscrowKey ks, byte[] blob, SecretKey kk)86     static RebootEscrowData fromEncryptedData(RebootEscrowKey ks, byte[] blob, SecretKey kk)
87             throws IOException {
88         Objects.requireNonNull(ks);
89         Objects.requireNonNull(blob);
90 
91         DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob));
92         int version = dis.readInt();
93         byte spVersion = dis.readByte();
94         switch (version) {
95             case CURRENT_VERSION: {
96                 byte[] syntheticPassword = decryptBlobCurrentVersion(kk, ks, dis);
97                 return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
98             }
99             case LEGACY_SINGLE_ENCRYPTED_VERSION: {
100                 // Decrypt the blob with the escrow key directly.
101                 byte[] syntheticPassword = AesEncryptionUtil.decrypt(ks.getKey(), dis);
102                 return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
103             }
104             default:
105                 throw new IOException("Unsupported version " + version);
106         }
107     }
108 
fromSyntheticPassword(RebootEscrowKey ks, byte spVersion, byte[] syntheticPassword, SecretKey kk)109     static RebootEscrowData fromSyntheticPassword(RebootEscrowKey ks, byte spVersion,
110             byte[] syntheticPassword, SecretKey kk)
111             throws IOException {
112         Objects.requireNonNull(syntheticPassword);
113 
114         // Encrypt synthetic password with the escrow key first; then encrypt the blob again with
115         // the key from keystore.
116         byte[] ksEncryptedBlob = AesEncryptionUtil.encrypt(ks.getKey(), syntheticPassword);
117         byte[] kkEncryptedBlob = AesEncryptionUtil.encrypt(kk, ksEncryptedBlob);
118 
119         ByteArrayOutputStream bos = new ByteArrayOutputStream();
120         DataOutputStream dos = new DataOutputStream(bos);
121 
122         dos.writeInt(CURRENT_VERSION);
123         dos.writeByte(spVersion);
124         dos.write(kkEncryptedBlob);
125 
126         return new RebootEscrowData(spVersion, syntheticPassword, bos.toByteArray(), ks);
127     }
128 }
129