Home | History | Annotate | Download | only in accounts
      1 package com.android.server.accounts;
      2 
      3 import android.annotation.NonNull;
      4 import android.annotation.Nullable;
      5 import android.os.Bundle;
      6 import android.os.Parcel;
      7 import android.util.Log;
      8 
      9 import com.android.internal.util.Preconditions;
     10 
     11 import java.security.GeneralSecurityException;
     12 import java.security.NoSuchAlgorithmException;
     13 
     14 import javax.crypto.Cipher;
     15 import javax.crypto.KeyGenerator;
     16 import javax.crypto.Mac;
     17 import javax.crypto.SecretKey;
     18 import javax.crypto.spec.IvParameterSpec;
     19 
     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";
     30     private static final String KEY_IV = "iv";
     31     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.
     37     private final SecretKey mEncryptionKey;
     38     private final SecretKey mMacKey;
     39 
     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);
     49         mEncryptionKey = kgen.generateKey();
     50         // Use a different key for mac-ing than encryption/decryption.
     51         kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
     52         mMacKey = kgen.generateKey();
     53     }
     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);
     60         byte[] clearBytes = parcel.marshall();
     61         parcel.recycle();
     62 
     63         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 
     69         Bundle encryptedBundle = new Bundle();
     70         encryptedBundle.putByteArray(KEY_CIPHER, encryptedBytes);
     71         encryptedBundle.putByteArray(KEY_MAC, mac);
     72         encryptedBundle.putByteArray(KEY_IV, iv);
     73 
     74         return encryptedBundle;
     75     }
     76 
     77     @Nullable
     78     /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
     79         Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
     80         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!");
     85             return null;
     86         }
     87 
     88         IvParameterSpec ivSpec = new IvParameterSpec(iv);
     89         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
     90         cipher.init(Cipher.DECRYPT_MODE, mEncryptionKey, ivSpec);
     91         byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
     92 
     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 
    102     private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] iv, @Nullable byte[] macArray)
    103             throws GeneralSecurityException {
    104         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         }
    111         return constantTimeArrayEquals(macArray, createMac(cipherArray, iv));
    112     }
    113 
    114     @NonNull
    115     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();
    121     }
    122 
    123     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;
    135     }
    136 }
    137