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