CryptoHelper.java revision 7881228736fd5f3f4ecf25d4808dc004c03b54d1
17881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanpackage com.android.server.accounts;
27881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
37881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport android.annotation.NonNull;
47881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport android.annotation.Nullable;
57881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport android.os.Bundle;
67881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport android.os.Parcel;
77881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport android.util.Log;
87881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
97881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport com.android.internal.util.Preconditions;
107881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
117881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport java.security.GeneralSecurityException;
127881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport java.security.NoSuchAlgorithmException;
137881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport java.security.SecureRandom;
147881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport java.util.Arrays;
157881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
167881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.Cipher;
177881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.KeyGenerator;
187881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.Mac;
197881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.SecretKey;
207881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.spec.IvParameterSpec;
217881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.spec.SecretKeySpec;
227881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
237881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan/**
247881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan * A crypto helper for encrypting and decrypting bundle with in-memory symmetric
257881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan * key for {@link AccountManagerService}.
267881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan */
277881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan/* default */ class CryptoHelper {
287881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String TAG = "Account";
297881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
307881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String KEY_CIPHER = "cipher";
317881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String KEY_MAC = "mac";
327881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String KEY_ALGORITHM = "AES";
337881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
347881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String MAC_ALGORITHM = "HMACSHA256";
357881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final int IV_LENGTH = 16;
367881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
377881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static CryptoHelper sInstance;
387881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    // Keys used for encrypting and decrypting data returned in a Bundle.
397881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private final SecretKeySpec mCipherKeySpec;
407881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private final SecretKeySpec mMacKeySpec;
417881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private final IvParameterSpec mIv;
427881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
437881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    /* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
447881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        if (sInstance == null) {
457881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            sInstance = new CryptoHelper();
467881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        }
477881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return sInstance;
487881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
497881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
507881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private CryptoHelper() throws NoSuchAlgorithmException {
517881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
527881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        SecretKey skey = kgen.generateKey();
537881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        mCipherKeySpec = new SecretKeySpec(skey.getEncoded(), KEY_ALGORITHM);
547881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
557881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
567881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        skey = kgen.generateKey();
577881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        mMacKeySpec = new SecretKeySpec(skey.getEncoded(), MAC_ALGORITHM);
587881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
597881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        // Create random iv
607881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        byte[] iv = new byte[IV_LENGTH];
617881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        SecureRandom secureRandom = new SecureRandom();
627881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        secureRandom.nextBytes(iv);
637881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        mIv = new IvParameterSpec(iv);
647881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
657881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
667881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    @NonNull
677881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    /* default */ Bundle encryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
687881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
697881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Parcel parcel = Parcel.obtain();
707881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        bundle.writeToParcel(parcel, 0);
717881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        byte[] bytes = parcel.marshall();
727881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        parcel.recycle();
737881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
747881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Bundle encryptedBundle = new Bundle();
757881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
767881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        byte[] cipher = encrypt(bytes);
777881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        byte[] mac = createMac(cipher);
787881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
797881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        encryptedBundle.putByteArray(KEY_CIPHER, cipher);
807881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        encryptedBundle.putByteArray(KEY_MAC, mac);
817881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
827881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return encryptedBundle;
837881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
847881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
857881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    @Nullable
867881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
877881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
887881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        byte[] cipherArray = bundle.getByteArray(KEY_CIPHER);
897881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        byte[] macArray = bundle.getByteArray(KEY_MAC);
907881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
917881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        if (!verifyMac(cipherArray, macArray)) {
927881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            if (Log.isLoggable(TAG, Log.VERBOSE)) {
937881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan                Log.v(TAG, "Escrow mac mismatched!");
947881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            }
957881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            return null;
967881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        }
977881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
987881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
997881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        cipher.init(Cipher.DECRYPT_MODE, mCipherKeySpec, mIv);
1007881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        byte[] decryptedBytes = cipher.doFinal(cipherArray);
1017881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
1027881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Parcel decryptedParcel = Parcel.obtain();
1037881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
1047881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        decryptedParcel.setDataPosition(0);
1057881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Bundle decryptedBundle = new Bundle();
1067881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        decryptedBundle.readFromParcel(decryptedParcel);
1077881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        decryptedParcel.recycle();
1087881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return decryptedBundle;
1097881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
1107881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
1117881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] macArray)
1127881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            throws GeneralSecurityException {
1137881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
1147881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        if (cipherArray == null || cipherArray.length == 0 || macArray == null
1157881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan                || macArray.length == 0) {
1167881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            if (Log.isLoggable(TAG, Log.VERBOSE)) {
1177881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan                Log.v(TAG, "Cipher or MAC is empty!");
1187881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            }
1197881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            return false;
1207881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        }
1217881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Mac mac = Mac.getInstance(MAC_ALGORITHM);
1227881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        mac.init(mMacKeySpec);
1237881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        mac.update(cipherArray);
1247881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return Arrays.equals(macArray, mac.doFinal());
1257881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
1267881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
1277881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    @NonNull
1287881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private byte[] encrypt(@NonNull byte[] data) throws GeneralSecurityException {
1297881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
1307881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        cipher.init(Cipher.ENCRYPT_MODE, mCipherKeySpec, mIv);
1317881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return cipher.doFinal(data);
1327881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
1337881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
1347881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    @NonNull
1357881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private byte[] createMac(@NonNull byte[] cipher) throws GeneralSecurityException {
1367881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Mac mac = Mac.getInstance(MAC_ALGORITHM);
1377881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        mac.init(mMacKeySpec);
1387881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return mac.doFinal(cipher);
1397881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
1407881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan}
141