• 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 com.android.internal.util.Preconditions;
20 
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.DataInputStream;
24 import java.io.DataOutputStream;
25 import java.io.IOException;
26 import java.security.InvalidAlgorithmParameterException;
27 import java.security.InvalidKeyException;
28 import java.security.NoSuchAlgorithmException;
29 
30 import javax.crypto.BadPaddingException;
31 import javax.crypto.Cipher;
32 import javax.crypto.IllegalBlockSizeException;
33 import javax.crypto.NoSuchPaddingException;
34 import javax.crypto.spec.IvParameterSpec;
35 
36 /**
37  * Holds the data necessary to complete a reboot escrow of the Synthetic Password.
38  */
39 class RebootEscrowData {
40     /**
41      * This is the current version of the escrow data format. This should be incremented if the
42      * format on disk is changed.
43      */
44     private static final int CURRENT_VERSION = 1;
45 
46     /** The algorithm used for the encryption of the key blob. */
47     private static final String CIPHER_ALGO = "AES/GCM/NoPadding";
48 
RebootEscrowData(byte spVersion, byte[] iv, byte[] syntheticPassword, byte[] blob, RebootEscrowKey key)49     private RebootEscrowData(byte spVersion, byte[] iv, byte[] syntheticPassword, byte[] blob,
50             RebootEscrowKey key) {
51         mSpVersion = spVersion;
52         mIv = iv;
53         mSyntheticPassword = syntheticPassword;
54         mBlob = blob;
55         mKey = key;
56     }
57 
58     private final byte mSpVersion;
59     private final byte[] mIv;
60     private final byte[] mSyntheticPassword;
61     private final byte[] mBlob;
62     private final RebootEscrowKey mKey;
63 
getSpVersion()64     public byte getSpVersion() {
65         return mSpVersion;
66     }
67 
getIv()68     public byte[] getIv() {
69         return mIv;
70     }
71 
getSyntheticPassword()72     public byte[] getSyntheticPassword() {
73         return mSyntheticPassword;
74     }
75 
getBlob()76     public byte[] getBlob() {
77         return mBlob;
78     }
79 
getKey()80     public RebootEscrowKey getKey() {
81         return mKey;
82     }
83 
fromEncryptedData(RebootEscrowKey key, byte[] blob)84     static RebootEscrowData fromEncryptedData(RebootEscrowKey key, byte[] blob)
85             throws IOException {
86         Preconditions.checkNotNull(key);
87         Preconditions.checkNotNull(blob);
88 
89         DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob));
90         int version = dis.readInt();
91         if (version != CURRENT_VERSION) {
92             throw new IOException("Unsupported version " + version);
93         }
94 
95         byte spVersion = dis.readByte();
96 
97         int ivSize = dis.readInt();
98         if (ivSize < 0 || ivSize > 32) {
99             throw new IOException("IV out of range: " + ivSize);
100         }
101         byte[] iv = new byte[ivSize];
102         dis.readFully(iv);
103 
104         int cipherTextSize = dis.readInt();
105         if (cipherTextSize < 0) {
106             throw new IOException("Invalid cipher text size: " + cipherTextSize);
107         }
108 
109         byte[] cipherText = new byte[cipherTextSize];
110         dis.readFully(cipherText);
111 
112         final byte[] syntheticPassword;
113         try {
114             Cipher c = Cipher.getInstance(CIPHER_ALGO);
115             c.init(Cipher.DECRYPT_MODE, key.getKey(), new IvParameterSpec(iv));
116             syntheticPassword = c.doFinal(cipherText);
117         } catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
118                 | IllegalBlockSizeException | NoSuchPaddingException
119                 | InvalidAlgorithmParameterException e) {
120             throw new IOException("Could not decrypt ciphertext", e);
121         }
122 
123         return new RebootEscrowData(spVersion, iv, syntheticPassword, blob, key);
124     }
125 
fromSyntheticPassword(RebootEscrowKey key, byte spVersion, byte[] syntheticPassword)126     static RebootEscrowData fromSyntheticPassword(RebootEscrowKey key, byte spVersion,
127             byte[] syntheticPassword)
128             throws IOException {
129         Preconditions.checkNotNull(syntheticPassword);
130 
131         ByteArrayOutputStream bos = new ByteArrayOutputStream();
132         DataOutputStream dos = new DataOutputStream(bos);
133 
134         final byte[] cipherText;
135         final byte[] iv;
136         try {
137             Cipher cipher = Cipher.getInstance(CIPHER_ALGO);
138             cipher.init(Cipher.ENCRYPT_MODE, key.getKey());
139             cipherText = cipher.doFinal(syntheticPassword);
140             iv = cipher.getIV();
141         } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException
142                 | NoSuchPaddingException | InvalidKeyException e) {
143             throw new IOException("Could not encrypt reboot escrow data", e);
144         }
145 
146         dos.writeInt(CURRENT_VERSION);
147         dos.writeByte(spVersion);
148         dos.writeInt(iv.length);
149         dos.write(iv);
150         dos.writeInt(cipherText.length);
151         dos.write(cipherText);
152 
153         return new RebootEscrowData(spVersion, iv, syntheticPassword, bos.toByteArray(),
154                 key);
155     }
156 }
157