Merge "Improve CryptoHelper" into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2cde05c3bb
@@ -10,15 +10,12 @@ import com.android.internal.util.Preconditions;
|
|||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.KeyGenerator;
|
import javax.crypto.KeyGenerator;
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A crypto helper for encrypting and decrypting bundle with in-memory symmetric
|
* A crypto helper for encrypting and decrypting bundle with in-memory symmetric
|
||||||
@@ -30,15 +27,15 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
private static final String KEY_CIPHER = "cipher";
|
private static final String KEY_CIPHER = "cipher";
|
||||||
private static final String KEY_MAC = "mac";
|
private static final String KEY_MAC = "mac";
|
||||||
private static final String KEY_ALGORITHM = "AES";
|
private static final String KEY_ALGORITHM = "AES";
|
||||||
|
private static final String KEY_IV = "iv";
|
||||||
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
|
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
|
||||||
private static final String MAC_ALGORITHM = "HMACSHA256";
|
private static final String MAC_ALGORITHM = "HMACSHA256";
|
||||||
private static final int IV_LENGTH = 16;
|
private static final int IV_LENGTH = 16;
|
||||||
|
|
||||||
private static CryptoHelper sInstance;
|
private static CryptoHelper sInstance;
|
||||||
// Keys used for encrypting and decrypting data returned in a Bundle.
|
// Keys used for encrypting and decrypting data returned in a Bundle.
|
||||||
private final SecretKeySpec mCipherKeySpec;
|
private final SecretKey mEncryptionKey;
|
||||||
private final SecretKeySpec mMacKeySpec;
|
private final SecretKey mMacKey;
|
||||||
private final IvParameterSpec mIv;
|
|
||||||
|
|
||||||
/* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
|
/* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
|
||||||
if (sInstance == null) {
|
if (sInstance == null) {
|
||||||
@@ -49,18 +46,10 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
|
|
||||||
private CryptoHelper() throws NoSuchAlgorithmException {
|
private CryptoHelper() throws NoSuchAlgorithmException {
|
||||||
KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
|
KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
|
||||||
SecretKey skey = kgen.generateKey();
|
mEncryptionKey = kgen.generateKey();
|
||||||
mCipherKeySpec = new SecretKeySpec(skey.getEncoded(), KEY_ALGORITHM);
|
// Use a different key for mac-ing than encryption/decryption.
|
||||||
|
|
||||||
kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
|
kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
|
||||||
skey = kgen.generateKey();
|
mMacKey = kgen.generateKey();
|
||||||
mMacKeySpec = new SecretKeySpec(skey.getEncoded(), MAC_ALGORITHM);
|
|
||||||
|
|
||||||
// Create random iv
|
|
||||||
byte[] iv = new byte[IV_LENGTH];
|
|
||||||
SecureRandom secureRandom = new SecureRandom();
|
|
||||||
secureRandom.nextBytes(iv);
|
|
||||||
mIv = new IvParameterSpec(iv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -68,16 +57,19 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
|
Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
|
||||||
Parcel parcel = Parcel.obtain();
|
Parcel parcel = Parcel.obtain();
|
||||||
bundle.writeToParcel(parcel, 0);
|
bundle.writeToParcel(parcel, 0);
|
||||||
byte[] bytes = parcel.marshall();
|
byte[] clearBytes = parcel.marshall();
|
||||||
parcel.recycle();
|
parcel.recycle();
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, mEncryptionKey);
|
||||||
|
byte[] encryptedBytes = cipher.doFinal(clearBytes);
|
||||||
|
byte[] iv = cipher.getIV();
|
||||||
|
byte[] mac = createMac(encryptedBytes, iv);
|
||||||
|
|
||||||
Bundle encryptedBundle = new Bundle();
|
Bundle encryptedBundle = new Bundle();
|
||||||
|
encryptedBundle.putByteArray(KEY_CIPHER, encryptedBytes);
|
||||||
byte[] cipher = encrypt(bytes);
|
|
||||||
byte[] mac = createMac(cipher);
|
|
||||||
|
|
||||||
encryptedBundle.putByteArray(KEY_CIPHER, cipher);
|
|
||||||
encryptedBundle.putByteArray(KEY_MAC, mac);
|
encryptedBundle.putByteArray(KEY_MAC, mac);
|
||||||
|
encryptedBundle.putByteArray(KEY_IV, iv);
|
||||||
|
|
||||||
return encryptedBundle;
|
return encryptedBundle;
|
||||||
}
|
}
|
||||||
@@ -85,19 +77,18 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
@Nullable
|
@Nullable
|
||||||
/* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
|
/* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
|
||||||
Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
|
Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
|
||||||
byte[] cipherArray = bundle.getByteArray(KEY_CIPHER);
|
byte[] iv = bundle.getByteArray(KEY_IV);
|
||||||
byte[] macArray = bundle.getByteArray(KEY_MAC);
|
byte[] encryptedBytes = bundle.getByteArray(KEY_CIPHER);
|
||||||
|
byte[] mac = bundle.getByteArray(KEY_MAC);
|
||||||
if (!verifyMac(cipherArray, macArray)) {
|
if (!verifyMac(encryptedBytes, iv, mac)) {
|
||||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
Log.w(TAG, "Escrow mac mismatched!");
|
||||||
Log.v(TAG, "Escrow mac mismatched!");
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||||
cipher.init(Cipher.DECRYPT_MODE, mCipherKeySpec, mIv);
|
cipher.init(Cipher.DECRYPT_MODE, mEncryptionKey, ivSpec);
|
||||||
byte[] decryptedBytes = cipher.doFinal(cipherArray);
|
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
|
||||||
|
|
||||||
Parcel decryptedParcel = Parcel.obtain();
|
Parcel decryptedParcel = Parcel.obtain();
|
||||||
decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
|
decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
|
||||||
@@ -108,9 +99,8 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
return decryptedBundle;
|
return decryptedBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] macArray)
|
private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] iv, @Nullable byte[] macArray)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
|
|
||||||
if (cipherArray == null || cipherArray.length == 0 || macArray == null
|
if (cipherArray == null || cipherArray.length == 0 || macArray == null
|
||||||
|| macArray.length == 0) {
|
|| macArray.length == 0) {
|
||||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
@@ -118,23 +108,29 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Mac mac = Mac.getInstance(MAC_ALGORITHM);
|
return constantTimeArrayEquals(macArray, createMac(cipherArray, iv));
|
||||||
mac.init(mMacKeySpec);
|
|
||||||
mac.update(cipherArray);
|
|
||||||
return Arrays.equals(macArray, mac.doFinal());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private byte[] encrypt(@NonNull byte[] data) throws GeneralSecurityException {
|
private byte[] createMac(@NonNull byte[] cipher, @NonNull byte[] iv) throws GeneralSecurityException {
|
||||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
Mac mac = Mac.getInstance(MAC_ALGORITHM);
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, mCipherKeySpec, mIv);
|
mac.init(mMacKey);
|
||||||
return cipher.doFinal(data);
|
mac.update(cipher);
|
||||||
|
mac.update(iv);
|
||||||
|
return mac.doFinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
private static boolean constantTimeArrayEquals(byte[] a, byte[] b) {
|
||||||
private byte[] createMac(@NonNull byte[] cipher) throws GeneralSecurityException {
|
if (a == null || b == null) {
|
||||||
Mac mac = Mac.getInstance(MAC_ALGORITHM);
|
return a == b;
|
||||||
mac.init(mMacKeySpec);
|
}
|
||||||
return mac.doFinal(cipher);
|
if (a.length != b.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean isEqual = true;
|
||||||
|
for (int i = 0; i < b.length; i++) {
|
||||||
|
isEqual &= (a[i] == b[i]);
|
||||||
|
}
|
||||||
|
return isEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user