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 Kwan
147881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.Cipher;
157881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.KeyGenerator;
167881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.Mac;
177881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.SecretKey;
187881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwanimport javax.crypto.spec.IvParameterSpec;
197881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
207881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan/**
217881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan * A crypto helper for encrypting and decrypting bundle with in-memory symmetric
227881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan * key for {@link AccountManagerService}.
237881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan */
247881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan/* default */ class CryptoHelper {
257881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String TAG = "Account";
267881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
277881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String KEY_CIPHER = "cipher";
287881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String KEY_MAC = "mac";
297881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String KEY_ALGORITHM = "AES";
30766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia    private static final String KEY_IV = "iv";
317881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
327881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final String MAC_ALGORITHM = "HMACSHA256";
337881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static final int IV_LENGTH = 16;
347881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
357881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private static CryptoHelper sInstance;
367881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    // Keys used for encrypting and decrypting data returned in a Bundle.
37766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia    private final SecretKey mEncryptionKey;
38766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia    private final SecretKey mMacKey;
397881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
407881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    /* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
417881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        if (sInstance == null) {
427881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            sInstance = new CryptoHelper();
437881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        }
447881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return sInstance;
457881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
467881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
477881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    private CryptoHelper() throws NoSuchAlgorithmException {
487881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
49766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        mEncryptionKey = kgen.generateKey();
50766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        // Use a different key for mac-ing than encryption/decryption.
517881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
52766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        mMacKey = kgen.generateKey();
537881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
547881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
557881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    @NonNull
567881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    /* default */ Bundle encryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
577881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
587881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Parcel parcel = Parcel.obtain();
597881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        bundle.writeToParcel(parcel, 0);
60766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        byte[] clearBytes = parcel.marshall();
617881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        parcel.recycle();
627881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
63766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
64766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        cipher.init(Cipher.ENCRYPT_MODE, mEncryptionKey);
65766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        byte[] encryptedBytes = cipher.doFinal(clearBytes);
66766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        byte[] iv = cipher.getIV();
67766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        byte[] mac = createMac(encryptedBytes, iv);
687881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
69766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        Bundle encryptedBundle = new Bundle();
70766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        encryptedBundle.putByteArray(KEY_CIPHER, encryptedBytes);
717881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        encryptedBundle.putByteArray(KEY_MAC, mac);
72766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        encryptedBundle.putByteArray(KEY_IV, iv);
737881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
747881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return encryptedBundle;
757881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
767881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
777881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    @Nullable
787881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
797881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
80766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        byte[] iv = bundle.getByteArray(KEY_IV);
81766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        byte[] encryptedBytes = bundle.getByteArray(KEY_CIPHER);
82766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        byte[] mac = bundle.getByteArray(KEY_MAC);
83766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        if (!verifyMac(encryptedBytes, iv, mac)) {
84766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia            Log.w(TAG, "Escrow mac mismatched!");
857881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            return null;
867881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        }
877881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
88766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        IvParameterSpec ivSpec = new IvParameterSpec(iv);
897881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
90766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        cipher.init(Cipher.DECRYPT_MODE, mEncryptionKey, ivSpec);
91766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
927881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
937881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Parcel decryptedParcel = Parcel.obtain();
947881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
957881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        decryptedParcel.setDataPosition(0);
967881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        Bundle decryptedBundle = new Bundle();
977881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        decryptedBundle.readFromParcel(decryptedParcel);
987881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        decryptedParcel.recycle();
997881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        return decryptedBundle;
1007881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
1017881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
102766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia    private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] iv, @Nullable byte[] macArray)
1037881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            throws GeneralSecurityException {
1047881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        if (cipherArray == null || cipherArray.length == 0 || macArray == null
1057881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan                || macArray.length == 0) {
1067881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            if (Log.isLoggable(TAG, Log.VERBOSE)) {
1077881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan                Log.v(TAG, "Cipher or MAC is empty!");
1087881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            }
1097881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan            return false;
1107881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan        }
111766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        return constantTimeArrayEquals(macArray, createMac(cipherArray, iv));
1127881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
1137881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
1147881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    @NonNull
115766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia    private byte[] createMac(@NonNull byte[] cipher, @NonNull byte[] iv) throws GeneralSecurityException {
116766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        Mac mac = Mac.getInstance(MAC_ALGORITHM);
117766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        mac.init(mMacKey);
118766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        mac.update(cipher);
119766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        mac.update(iv);
120766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        return mac.doFinal();
1217881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
1227881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan
123766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia    private static boolean constantTimeArrayEquals(byte[] a, byte[] b) {
124766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        if (a == null || b == null) {
125766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia            return a == b;
126766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        }
127766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        if (a.length != b.length) {
128766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia            return false;
129766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        }
130766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        boolean isEqual = true;
131766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        for (int i = 0; i < b.length; i++) {
132766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia            isEqual &= (a[i] == b[i]);
133766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        }
134766b28329326628eaf1ef8009ebd5d611369c490Carlos Valdivia        return isEqual;
1357881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan    }
1367881228736fd5f3f4ecf25d4808dc004c03b54d1Sandra Kwan}
137