1 /* 2 * Copyright (C) 2020 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.security.InvalidAlgorithmParameterException; 25 import java.security.InvalidKeyException; 26 import java.security.NoSuchAlgorithmException; 27 import java.util.Objects; 28 29 import javax.crypto.BadPaddingException; 30 import javax.crypto.Cipher; 31 import javax.crypto.IllegalBlockSizeException; 32 import javax.crypto.NoSuchPaddingException; 33 import javax.crypto.SecretKey; 34 import javax.crypto.spec.GCMParameterSpec; 35 36 class AesEncryptionUtil { 37 /** The algorithm used for the encryption of the key blob. */ 38 private static final String CIPHER_ALGO = "AES/GCM/NoPadding"; 39 AesEncryptionUtil()40 private AesEncryptionUtil() {} 41 decrypt(SecretKey key, DataInputStream cipherStream)42 static byte[] decrypt(SecretKey key, DataInputStream cipherStream) throws IOException { 43 Objects.requireNonNull(key); 44 Objects.requireNonNull(cipherStream); 45 46 int ivSize = cipherStream.readInt(); 47 if (ivSize < 0 || ivSize > 32) { 48 throw new IOException("IV out of range: " + ivSize); 49 } 50 byte[] iv = new byte[ivSize]; 51 cipherStream.readFully(iv); 52 53 int rawCipherTextSize = cipherStream.readInt(); 54 if (rawCipherTextSize < 0) { 55 throw new IOException("Invalid cipher text size: " + rawCipherTextSize); 56 } 57 58 byte[] rawCipherText = new byte[rawCipherTextSize]; 59 cipherStream.readFully(rawCipherText); 60 61 final byte[] plainText; 62 try { 63 Cipher c = Cipher.getInstance(CIPHER_ALGO); 64 c.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv)); 65 plainText = c.doFinal(rawCipherText); 66 } catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException 67 | IllegalBlockSizeException | NoSuchPaddingException 68 | InvalidAlgorithmParameterException e) { 69 throw new IOException("Could not decrypt cipher text", e); 70 } 71 72 return plainText; 73 } 74 decrypt(SecretKey key, byte[] cipherText)75 static byte[] decrypt(SecretKey key, byte[] cipherText) throws IOException { 76 Objects.requireNonNull(key); 77 Objects.requireNonNull(cipherText); 78 79 DataInputStream cipherStream = new DataInputStream(new ByteArrayInputStream(cipherText)); 80 return decrypt(key, cipherStream); 81 } 82 encrypt(SecretKey key, byte[] plainText)83 static byte[] encrypt(SecretKey key, byte[] plainText) throws IOException { 84 Objects.requireNonNull(key); 85 Objects.requireNonNull(plainText); 86 87 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 88 DataOutputStream dos = new DataOutputStream(bos); 89 90 final byte[] cipherText; 91 final byte[] iv; 92 try { 93 Cipher cipher = Cipher.getInstance(CIPHER_ALGO); 94 cipher.init(Cipher.ENCRYPT_MODE, key); 95 cipherText = cipher.doFinal(plainText); 96 iv = cipher.getIV(); 97 } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException 98 | NoSuchPaddingException | InvalidKeyException e) { 99 throw new IOException("Could not encrypt input data", e); 100 } 101 102 dos.writeInt(iv.length); 103 dos.write(iv); 104 dos.writeInt(cipherText.length); 105 dos.write(cipherText); 106 107 return bos.toByteArray(); 108 } 109 } 110