blob: 2b59b7483521d3c1211dceb5afd75e90244e7356 [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;
13import java.security.SecureRandom;
14import java.util.Arrays;
15
16import javax.crypto.Cipher;
17import javax.crypto.KeyGenerator;
18import javax.crypto.Mac;
19import javax.crypto.SecretKey;
20import javax.crypto.spec.IvParameterSpec;
21import javax.crypto.spec.SecretKeySpec;
22
23/**
24 * A crypto helper for encrypting and decrypting bundle with in-memory symmetric
25 * key for {@link AccountManagerService}.
26 */
27/* default */ class CryptoHelper {
28 private static final String TAG = "Account";
29
30 private static final String KEY_CIPHER = "cipher";
31 private static final String KEY_MAC = "mac";
32 private static final String KEY_ALGORITHM = "AES";
33 private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
34 private static final String MAC_ALGORITHM = "HMACSHA256";
35 private static final int IV_LENGTH = 16;
36
37 private static CryptoHelper sInstance;
38 // Keys used for encrypting and decrypting data returned in a Bundle.
39 private final SecretKeySpec mCipherKeySpec;
40 private final SecretKeySpec mMacKeySpec;
41 private final IvParameterSpec mIv;
42
43 /* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
44 if (sInstance == null) {
45 sInstance = new CryptoHelper();
46 }
47 return sInstance;
48 }
49
50 private CryptoHelper() throws NoSuchAlgorithmException {
51 KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
52 SecretKey skey = kgen.generateKey();
53 mCipherKeySpec = new SecretKeySpec(skey.getEncoded(), KEY_ALGORITHM);
54
55 kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
56 skey = kgen.generateKey();
57 mMacKeySpec = new SecretKeySpec(skey.getEncoded(), MAC_ALGORITHM);
58
59 // Create random iv
60 byte[] iv = new byte[IV_LENGTH];
61 SecureRandom secureRandom = new SecureRandom();
62 secureRandom.nextBytes(iv);
63 mIv = new IvParameterSpec(iv);
64 }
65
66 @NonNull
67 /* default */ Bundle encryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
68 Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
69 Parcel parcel = Parcel.obtain();
70 bundle.writeToParcel(parcel, 0);
71 byte[] bytes = parcel.marshall();
72 parcel.recycle();
73
74 Bundle encryptedBundle = new Bundle();
75
76 byte[] cipher = encrypt(bytes);
77 byte[] mac = createMac(cipher);
78
79 encryptedBundle.putByteArray(KEY_CIPHER, cipher);
80 encryptedBundle.putByteArray(KEY_MAC, mac);
81
82 return encryptedBundle;
83 }
84
85 @Nullable
86 /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
87 Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
88 byte[] cipherArray = bundle.getByteArray(KEY_CIPHER);
89 byte[] macArray = bundle.getByteArray(KEY_MAC);
90
91 if (!verifyMac(cipherArray, macArray)) {
92 if (Log.isLoggable(TAG, Log.VERBOSE)) {
93 Log.v(TAG, "Escrow mac mismatched!");
94 }
95 return null;
96 }
97
98 Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
99 cipher.init(Cipher.DECRYPT_MODE, mCipherKeySpec, mIv);
100 byte[] decryptedBytes = cipher.doFinal(cipherArray);
101
102 Parcel decryptedParcel = Parcel.obtain();
103 decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
104 decryptedParcel.setDataPosition(0);
105 Bundle decryptedBundle = new Bundle();
106 decryptedBundle.readFromParcel(decryptedParcel);
107 decryptedParcel.recycle();
108 return decryptedBundle;
109 }
110
111 private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] macArray)
112 throws GeneralSecurityException {
113
114 if (cipherArray == null || cipherArray.length == 0 || macArray == null
115 || macArray.length == 0) {
116 if (Log.isLoggable(TAG, Log.VERBOSE)) {
117 Log.v(TAG, "Cipher or MAC is empty!");
118 }
119 return false;
120 }
121 Mac mac = Mac.getInstance(MAC_ALGORITHM);
122 mac.init(mMacKeySpec);
123 mac.update(cipherArray);
124 return Arrays.equals(macArray, mac.doFinal());
125 }
126
127 @NonNull
128 private byte[] encrypt(@NonNull byte[] data) throws GeneralSecurityException {
129 Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
130 cipher.init(Cipher.ENCRYPT_MODE, mCipherKeySpec, mIv);
131 return cipher.doFinal(data);
132 }
133
134 @NonNull
135 private byte[] createMac(@NonNull byte[] cipher) throws GeneralSecurityException {
136 Mac mac = Mac.getInstance(MAC_ALGORITHM);
137 mac.init(mMacKeySpec);
138 return mac.doFinal(cipher);
139 }
140}