blob: 2ade673523acf25376cd25cfa0994e395e994e37 [file] [log] [blame]
Sandra Kwan78812282015-11-04 11:19:47 -08001package com.android.server.accounts;
2
3import android.annotation.NonNull;
4import android.annotation.Nullable;
5import android.os.Bundle;
6import android.os.Parcel;
7import android.util.Log;
8
9import com.android.internal.util.Preconditions;
10
11import java.security.GeneralSecurityException;
12import java.security.NoSuchAlgorithmException;
Sandra Kwan78812282015-11-04 11:19:47 -080013
14import javax.crypto.Cipher;
15import javax.crypto.KeyGenerator;
16import javax.crypto.Mac;
17import javax.crypto.SecretKey;
18import javax.crypto.spec.IvParameterSpec;
Sandra Kwan78812282015-11-04 11:19:47 -080019
20/**
21 * A crypto helper for encrypting and decrypting bundle with in-memory symmetric
22 * key for {@link AccountManagerService}.
23 */
24/* default */ class CryptoHelper {
25 private static final String TAG = "Account";
26
27 private static final String KEY_CIPHER = "cipher";
28 private static final String KEY_MAC = "mac";
29 private static final String KEY_ALGORITHM = "AES";
Carlos Valdivia766b2832016-04-06 17:42:51 -070030 private static final String KEY_IV = "iv";
Sandra Kwan78812282015-11-04 11:19:47 -080031 private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
32 private static final String MAC_ALGORITHM = "HMACSHA256";
33 private static final int IV_LENGTH = 16;
34
35 private static CryptoHelper sInstance;
36 // Keys used for encrypting and decrypting data returned in a Bundle.
Carlos Valdivia766b2832016-04-06 17:42:51 -070037 private final SecretKey mEncryptionKey;
38 private final SecretKey mMacKey;
Sandra Kwan78812282015-11-04 11:19:47 -080039
40 /* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
41 if (sInstance == null) {
42 sInstance = new CryptoHelper();
43 }
44 return sInstance;
45 }
46
47 private CryptoHelper() throws NoSuchAlgorithmException {
48 KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
Carlos Valdivia766b2832016-04-06 17:42:51 -070049 mEncryptionKey = kgen.generateKey();
50 // Use a different key for mac-ing than encryption/decryption.
Sandra Kwan78812282015-11-04 11:19:47 -080051 kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
Carlos Valdivia766b2832016-04-06 17:42:51 -070052 mMacKey = kgen.generateKey();
Sandra Kwan78812282015-11-04 11:19:47 -080053 }
54
55 @NonNull
56 /* default */ Bundle encryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
57 Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
58 Parcel parcel = Parcel.obtain();
59 bundle.writeToParcel(parcel, 0);
Carlos Valdivia766b2832016-04-06 17:42:51 -070060 byte[] clearBytes = parcel.marshall();
Sandra Kwan78812282015-11-04 11:19:47 -080061 parcel.recycle();
62
Carlos Valdivia766b2832016-04-06 17:42:51 -070063 Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
64 cipher.init(Cipher.ENCRYPT_MODE, mEncryptionKey);
65 byte[] encryptedBytes = cipher.doFinal(clearBytes);
66 byte[] iv = cipher.getIV();
67 byte[] mac = createMac(encryptedBytes, iv);
68
Sandra Kwan78812282015-11-04 11:19:47 -080069 Bundle encryptedBundle = new Bundle();
Carlos Valdivia766b2832016-04-06 17:42:51 -070070 encryptedBundle.putByteArray(KEY_CIPHER, encryptedBytes);
Sandra Kwan78812282015-11-04 11:19:47 -080071 encryptedBundle.putByteArray(KEY_MAC, mac);
Carlos Valdivia766b2832016-04-06 17:42:51 -070072 encryptedBundle.putByteArray(KEY_IV, iv);
Sandra Kwan78812282015-11-04 11:19:47 -080073
74 return encryptedBundle;
75 }
76
77 @Nullable
78 /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
79 Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
Carlos Valdivia766b2832016-04-06 17:42:51 -070080 byte[] iv = bundle.getByteArray(KEY_IV);
81 byte[] encryptedBytes = bundle.getByteArray(KEY_CIPHER);
82 byte[] mac = bundle.getByteArray(KEY_MAC);
83 if (!verifyMac(encryptedBytes, iv, mac)) {
84 Log.w(TAG, "Escrow mac mismatched!");
Sandra Kwan78812282015-11-04 11:19:47 -080085 return null;
86 }
87
Carlos Valdivia766b2832016-04-06 17:42:51 -070088 IvParameterSpec ivSpec = new IvParameterSpec(iv);
Sandra Kwan78812282015-11-04 11:19:47 -080089 Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
Carlos Valdivia766b2832016-04-06 17:42:51 -070090 cipher.init(Cipher.DECRYPT_MODE, mEncryptionKey, ivSpec);
91 byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
Sandra Kwan78812282015-11-04 11:19:47 -080092
93 Parcel decryptedParcel = Parcel.obtain();
94 decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
95 decryptedParcel.setDataPosition(0);
96 Bundle decryptedBundle = new Bundle();
97 decryptedBundle.readFromParcel(decryptedParcel);
98 decryptedParcel.recycle();
99 return decryptedBundle;
100 }
101
Carlos Valdivia766b2832016-04-06 17:42:51 -0700102 private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] iv, @Nullable byte[] macArray)
Sandra Kwan78812282015-11-04 11:19:47 -0800103 throws GeneralSecurityException {
Sandra Kwan78812282015-11-04 11:19:47 -0800104 if (cipherArray == null || cipherArray.length == 0 || macArray == null
105 || macArray.length == 0) {
106 if (Log.isLoggable(TAG, Log.VERBOSE)) {
107 Log.v(TAG, "Cipher or MAC is empty!");
108 }
109 return false;
110 }
Carlos Valdivia766b2832016-04-06 17:42:51 -0700111 return constantTimeArrayEquals(macArray, createMac(cipherArray, iv));
Sandra Kwan78812282015-11-04 11:19:47 -0800112 }
113
114 @NonNull
Carlos Valdivia766b2832016-04-06 17:42:51 -0700115 private byte[] createMac(@NonNull byte[] cipher, @NonNull byte[] iv) throws GeneralSecurityException {
116 Mac mac = Mac.getInstance(MAC_ALGORITHM);
117 mac.init(mMacKey);
118 mac.update(cipher);
119 mac.update(iv);
120 return mac.doFinal();
Sandra Kwan78812282015-11-04 11:19:47 -0800121 }
122
Carlos Valdivia766b2832016-04-06 17:42:51 -0700123 private static boolean constantTimeArrayEquals(byte[] a, byte[] b) {
124 if (a == null || b == null) {
125 return a == b;
126 }
127 if (a.length != b.length) {
128 return false;
129 }
130 boolean isEqual = true;
131 for (int i = 0; i < b.length; i++) {
132 isEqual &= (a[i] == b[i]);
133 }
134 return isEqual;
Sandra Kwan78812282015-11-04 11:19:47 -0800135 }
136}