Loading services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +10 −77 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ import com.android.server.backup.restore.PerformUnifiedRestoreTask; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.BackupManagerMonitorUtils; import com.android.server.backup.utils.BackupObserverUtils; import com.android.server.backup.utils.PasswordUtils; import com.android.server.power.BatterySaverPolicy.ServiceType; import libcore.io.IoUtils; Loading @@ -121,11 +122,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.ArrayList; Loading @@ -139,10 +136,6 @@ import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; public class RefactoredBackupManagerService implements BackupManagerServiceInterface { public static final String TAG = "BackupManagerService"; Loading Loading @@ -669,12 +662,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter private File mPasswordVersionFile; private byte[] mPasswordSalt; // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys public static final int PBKDF2_HASH_ROUNDS = 10000; private static final int PBKDF2_KEY_SIZE = 256; // bits public static final int PBKDF2_SALT_SIZE = 512; // bits public static final String ENCRYPTION_ALGORITHM_NAME = "AES-256"; // Keep a log of all the apps we've ever backed up, and what the // dataset tokens are for both the current backup dataset and // the ancestral dataset. Loading Loading @@ -1169,62 +1156,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } public SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) { return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds); } private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) { try { SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); return keyFactory.generateSecret(ks); } catch (InvalidKeySpecException e) { Slog.e(TAG, "Invalid key spec for PBKDF2!"); } catch (NoSuchAlgorithmException e) { Slog.e(TAG, "PBKDF2 unavailable!"); } return null; } private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) { SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds); if (key != null) { return byteArrayToHex(key.getEncoded()); } return null; } public String byteArrayToHex(byte[] data) { StringBuilder buf = new StringBuilder(data.length * 2); for (int i = 0; i < data.length; i++) { buf.append(Byte.toHexString(data[i], true)); } return buf.toString(); } public byte[] hexToByteArray(String digits) { final int bytes = digits.length() / 2; if (2 * bytes != digits.length()) { throw new IllegalArgumentException("Hex string must have an even number of digits"); } byte[] result = new byte[bytes]; for (int i = 0; i < digits.length(); i += 2) { result[i / 2] = (byte) Integer.parseInt(digits.substring(i, i + 2), 16); } return result; } public byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) { char[] mkAsChar = new char[pwBytes.length]; for (int i = 0; i < pwBytes.length; i++) { mkAsChar[i] = (char) pwBytes[i]; } Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds); return checksum.getEncoded(); } // Used for generating random salts or passwords public byte[] randomBytes(int bits) { byte[] array = new byte[bits / 8]; Loading @@ -1241,7 +1172,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } else { // hash the stated current pw and compare to the stored one if (candidatePw != null && candidatePw.length() > 0) { String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, String currentPwHash = PasswordUtils.buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds); if (mPasswordHash.equalsIgnoreCase(currentPwHash)) { // candidate hash matches the stored hash -- the password matches Loading @@ -1262,9 +1194,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // If the supplied pw doesn't hash to the the saved one, fail. The password // might be caught in the legacy crypto mismatch; verify that too. if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS) && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, currentPw, PBKDF2_HASH_ROUNDS))) { currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS))) { return false; } Loading Loading @@ -1304,8 +1236,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter try { // Okay, build the hash of the new backup password byte[] salt = randomBytes(PBKDF2_SALT_SIZE); String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS); byte[] salt = randomBytes(PasswordUtils.PBKDF2_SALT_SIZE); String newPwHash = PasswordUtils.buildPasswordHash(PBKDF_CURRENT, newPw, salt, PasswordUtils.PBKDF2_HASH_ROUNDS); OutputStream pwf = null, buffer = null; DataOutputStream out = null; Loading Loading @@ -1344,9 +1277,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter public boolean backupPasswordMatches(String currentPw) { if (hasBackupPassword()) { final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION); if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS) && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, currentPw, PBKDF2_HASH_ROUNDS))) { currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS))) { if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); return false; } Loading services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java +15 −13 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.KeyValueAdbBackupEngine; import com.android.server.backup.RefactoredBackupManagerService; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.PasswordUtils; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; Loading Loading @@ -119,7 +120,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor for (String pkgName : pkgNames) { if (!set.containsKey(pkgName)) { try { PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(pkgName, PackageInfo info = backupManagerService.getPackageManager().getPackageInfo( pkgName, PackageManager.GET_SIGNATURES); set.put(pkgName, info); } catch (NameNotFoundException e) { Loading @@ -134,17 +136,17 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor OutputStream ofstream) throws Exception { // User key will be used to encrypt the master key. byte[] newUserSalt = backupManagerService .randomBytes(RefactoredBackupManagerService.PBKDF2_SALT_SIZE); SecretKey userKey = backupManagerService .randomBytes(PasswordUtils.PBKDF2_SALT_SIZE); SecretKey userKey = PasswordUtils .buildPasswordKey(RefactoredBackupManagerService.PBKDF_CURRENT, mEncryptPassword, newUserSalt, RefactoredBackupManagerService.PBKDF2_HASH_ROUNDS); PasswordUtils.PBKDF2_HASH_ROUNDS); // the master key is random for each backup byte[] masterPw = new byte[256 / 8]; backupManagerService.getRng().nextBytes(masterPw); byte[] checksumSalt = backupManagerService .randomBytes(RefactoredBackupManagerService.PBKDF2_SALT_SIZE); .randomBytes(PasswordUtils.PBKDF2_SALT_SIZE); // primary encryption of the datastream with the random key Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); Loading @@ -153,16 +155,16 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor OutputStream finalOutput = new CipherOutputStream(ofstream, c); // line 4: name of encryption algorithm headerbuf.append(RefactoredBackupManagerService.ENCRYPTION_ALGORITHM_NAME); headerbuf.append(PasswordUtils.ENCRYPTION_ALGORITHM_NAME); headerbuf.append('\n'); // line 5: user password salt [hex] headerbuf.append(backupManagerService.byteArrayToHex(newUserSalt)); headerbuf.append(PasswordUtils.byteArrayToHex(newUserSalt)); headerbuf.append('\n'); // line 6: master key checksum salt [hex] headerbuf.append(backupManagerService.byteArrayToHex(checksumSalt)); headerbuf.append(PasswordUtils.byteArrayToHex(checksumSalt)); headerbuf.append('\n'); // line 7: number of PBKDF2 rounds used [decimal] headerbuf.append(RefactoredBackupManagerService.PBKDF2_HASH_ROUNDS); headerbuf.append(PasswordUtils.PBKDF2_HASH_ROUNDS); headerbuf.append('\n'); // line 8: IV of the user key [hex] Loading @@ -170,7 +172,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor mkC.init(Cipher.ENCRYPT_MODE, userKey); byte[] IV = mkC.getIV(); headerbuf.append(backupManagerService.byteArrayToHex(IV)); headerbuf.append(PasswordUtils.byteArrayToHex(IV)); headerbuf.append('\n'); // line 9: master IV + key blob, encrypted by the user key [hex]. Blob format: Loading @@ -185,10 +187,10 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor // stated number of PBKDF2 rounds IV = c.getIV(); byte[] mk = masterKeySpec.getEncoded(); byte[] checksum = backupManagerService byte[] checksum = PasswordUtils .makeKeyChecksum(RefactoredBackupManagerService.PBKDF_CURRENT, masterKeySpec.getEncoded(), checksumSalt, RefactoredBackupManagerService.PBKDF2_HASH_ROUNDS); checksumSalt, PasswordUtils.PBKDF2_HASH_ROUNDS); ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length + checksum.length + 3); Loading @@ -201,7 +203,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor mkOut.write(checksum); mkOut.flush(); byte[] encryptedMk = mkC.doFinal(blob.toByteArray()); headerbuf.append(backupManagerService.byteArrayToHex(encryptedMk)); headerbuf.append(PasswordUtils.byteArrayToHex(encryptedMk)); headerbuf.append('\n'); return finalOutput; Loading services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +13 −10 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.server.backup.PackageManagerBackupAgent; import com.android.server.backup.RefactoredBackupManagerService; import com.android.server.backup.fullbackup.FullBackupObbConnection; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.PasswordUtils; import java.io.ByteArrayInputStream; import java.io.DataInputStream; Loading Loading @@ -315,15 +316,15 @@ public class PerformAdbRestoreTask implements Runnable { try { Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKey userKey = backupManagerService SecretKey userKey = PasswordUtils .buildPasswordKey(algorithm, mDecryptPassword, userSalt, rounds); byte[] IV = backupManagerService.hexToByteArray(userIvHex); byte[] IV = PasswordUtils.hexToByteArray(userIvHex); IvParameterSpec ivSpec = new IvParameterSpec(IV); c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(userKey.getEncoded(), "AES"), ivSpec); byte[] mkCipher = backupManagerService.hexToByteArray(masterKeyBlobHex); byte[] mkCipher = PasswordUtils.hexToByteArray(masterKeyBlobHex); byte[] mkBlob = c.doFinal(mkCipher); // first, the master key IV Loading @@ -342,7 +343,7 @@ public class PerformAdbRestoreTask implements Runnable { offset, offset + len); // now validate the decrypted master key against the checksum byte[] calculatedCk = backupManagerService.makeKeyChecksum(algorithm, mk, ckSalt, byte[] calculatedCk = PasswordUtils.makeKeyChecksum(algorithm, mk, ckSalt, rounds); if (Arrays.equals(calculatedCk, mkChecksum)) { ivSpec = new IvParameterSpec(IV); Loading Loading @@ -392,13 +393,13 @@ public class PerformAdbRestoreTask implements Runnable { InputStream rawInStream) { InputStream result = null; try { if (encryptionName.equals(RefactoredBackupManagerService.ENCRYPTION_ALGORITHM_NAME)) { if (encryptionName.equals(PasswordUtils.ENCRYPTION_ALGORITHM_NAME)) { String userSaltHex = readHeaderLine(rawInStream); // 5 byte[] userSalt = backupManagerService.hexToByteArray(userSaltHex); byte[] userSalt = PasswordUtils.hexToByteArray(userSaltHex); String ckSaltHex = readHeaderLine(rawInStream); // 6 byte[] ckSalt = backupManagerService.hexToByteArray(ckSaltHex); byte[] ckSalt = PasswordUtils.hexToByteArray(ckSaltHex); int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 String userIvHex = readHeaderLine(rawInStream); // 8 Loading Loading @@ -557,7 +558,8 @@ public class PerformAdbRestoreTask implements Runnable { } try { mTargetApp = backupManagerService.getPackageManager().getApplicationInfo( mTargetApp = backupManagerService.getPackageManager().getApplicationInfo( pkg, 0); // If we haven't sent any data to this app yet, we probably Loading Loading @@ -836,7 +838,8 @@ public class PerformAdbRestoreTask implements Runnable { if (RefactoredBackupManagerService.DEBUG) { Slog.d(RefactoredBackupManagerService.TAG, "Killing host process"); } backupManagerService.getActivityManager().killApplicationProcess(app.processName, backupManagerService.getActivityManager().killApplicationProcess( app.processName, app.uid); } else { if (RefactoredBackupManagerService.DEBUG) { Loading services/backup/java/com/android/server/backup/utils/PasswordUtils.java 0 → 100644 +120 −0 Original line number Diff line number Diff line package com.android.server.backup.utils; import android.util.Slog; import com.android.server.backup.RefactoredBackupManagerService; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; /** * Passwords related utility methods. */ public class PasswordUtils { // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys public static final int PBKDF2_HASH_ROUNDS = 10000; private static final int PBKDF2_KEY_SIZE = 256; // bits public static final int PBKDF2_SALT_SIZE = 512; // bits public static final String ENCRYPTION_ALGORITHM_NAME = "AES-256"; /** * Creates {@link SecretKey} instance from given parameters. * * @param algorithm - key generation algorithm. * @param pw - password. * @param salt - salt. * @param rounds - number of rounds to run in key generation. * @return {@link SecretKey} instance or null in case of an error. */ public static SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) { return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds); } /** * Generates {@link SecretKey} instance from given parameters and returns it's hex * representation. * * @param algorithm - key generation algorithm. * @param pw - password. * @param salt - salt. * @param rounds - number of rounds to run in key generation. * @return Hex representation of the generated key, or null if generation failed. */ public static String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) { SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds); if (key != null) { return byteArrayToHex(key.getEncoded()); } return null; } /** * Creates hex string representation of the byte array. */ public static String byteArrayToHex(byte[] data) { StringBuilder buf = new StringBuilder(data.length * 2); for (int i = 0; i < data.length; i++) { buf.append(Byte.toHexString(data[i], true)); } return buf.toString(); } /** * Creates byte array from it's hex string representation. */ public static byte[] hexToByteArray(String digits) { final int bytes = digits.length() / 2; if (2 * bytes != digits.length()) { throw new IllegalArgumentException("Hex string must have an even number of digits"); } byte[] result = new byte[bytes]; for (int i = 0; i < digits.length(); i += 2) { result[i / 2] = (byte) Integer.parseInt(digits.substring(i, i + 2), 16); } return result; } /** * Generates {@link SecretKey} instance from given parameters and returns it's checksum. * * Current implementation returns the key in its primary encoding format. * * @param algorithm - key generation algorithm. * @param pwBytes - password. * @param salt - salt. * @param rounds - number of rounds to run in key generation. * @return Hex representation of the generated key, or null if generation failed. */ public static byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) { char[] mkAsChar = new char[pwBytes.length]; for (int i = 0; i < pwBytes.length; i++) { mkAsChar[i] = (char) pwBytes[i]; } Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds); return checksum.getEncoded(); } private static SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) { try { SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); return keyFactory.generateSecret(ks); } catch (InvalidKeySpecException e) { Slog.e(RefactoredBackupManagerService.TAG, "Invalid key spec for PBKDF2!"); } catch (NoSuchAlgorithmException e) { Slog.e(RefactoredBackupManagerService.TAG, "PBKDF2 unavailable!"); } return null; } } Loading
services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +10 −77 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ import com.android.server.backup.restore.PerformUnifiedRestoreTask; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.BackupManagerMonitorUtils; import com.android.server.backup.utils.BackupObserverUtils; import com.android.server.backup.utils.PasswordUtils; import com.android.server.power.BatterySaverPolicy.ServiceType; import libcore.io.IoUtils; Loading @@ -121,11 +122,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.ArrayList; Loading @@ -139,10 +136,6 @@ import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; public class RefactoredBackupManagerService implements BackupManagerServiceInterface { public static final String TAG = "BackupManagerService"; Loading Loading @@ -669,12 +662,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter private File mPasswordVersionFile; private byte[] mPasswordSalt; // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys public static final int PBKDF2_HASH_ROUNDS = 10000; private static final int PBKDF2_KEY_SIZE = 256; // bits public static final int PBKDF2_SALT_SIZE = 512; // bits public static final String ENCRYPTION_ALGORITHM_NAME = "AES-256"; // Keep a log of all the apps we've ever backed up, and what the // dataset tokens are for both the current backup dataset and // the ancestral dataset. Loading Loading @@ -1169,62 +1156,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } public SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) { return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds); } private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) { try { SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); return keyFactory.generateSecret(ks); } catch (InvalidKeySpecException e) { Slog.e(TAG, "Invalid key spec for PBKDF2!"); } catch (NoSuchAlgorithmException e) { Slog.e(TAG, "PBKDF2 unavailable!"); } return null; } private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) { SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds); if (key != null) { return byteArrayToHex(key.getEncoded()); } return null; } public String byteArrayToHex(byte[] data) { StringBuilder buf = new StringBuilder(data.length * 2); for (int i = 0; i < data.length; i++) { buf.append(Byte.toHexString(data[i], true)); } return buf.toString(); } public byte[] hexToByteArray(String digits) { final int bytes = digits.length() / 2; if (2 * bytes != digits.length()) { throw new IllegalArgumentException("Hex string must have an even number of digits"); } byte[] result = new byte[bytes]; for (int i = 0; i < digits.length(); i += 2) { result[i / 2] = (byte) Integer.parseInt(digits.substring(i, i + 2), 16); } return result; } public byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) { char[] mkAsChar = new char[pwBytes.length]; for (int i = 0; i < pwBytes.length; i++) { mkAsChar[i] = (char) pwBytes[i]; } Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds); return checksum.getEncoded(); } // Used for generating random salts or passwords public byte[] randomBytes(int bits) { byte[] array = new byte[bits / 8]; Loading @@ -1241,7 +1172,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } else { // hash the stated current pw and compare to the stored one if (candidatePw != null && candidatePw.length() > 0) { String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, String currentPwHash = PasswordUtils.buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds); if (mPasswordHash.equalsIgnoreCase(currentPwHash)) { // candidate hash matches the stored hash -- the password matches Loading @@ -1262,9 +1194,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // If the supplied pw doesn't hash to the the saved one, fail. The password // might be caught in the legacy crypto mismatch; verify that too. if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS) && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, currentPw, PBKDF2_HASH_ROUNDS))) { currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS))) { return false; } Loading Loading @@ -1304,8 +1236,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter try { // Okay, build the hash of the new backup password byte[] salt = randomBytes(PBKDF2_SALT_SIZE); String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS); byte[] salt = randomBytes(PasswordUtils.PBKDF2_SALT_SIZE); String newPwHash = PasswordUtils.buildPasswordHash(PBKDF_CURRENT, newPw, salt, PasswordUtils.PBKDF2_HASH_ROUNDS); OutputStream pwf = null, buffer = null; DataOutputStream out = null; Loading Loading @@ -1344,9 +1277,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter public boolean backupPasswordMatches(String currentPw) { if (hasBackupPassword()) { final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION); if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS) && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, currentPw, PBKDF2_HASH_ROUNDS))) { currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS))) { if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); return false; } Loading
services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java +15 −13 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.KeyValueAdbBackupEngine; import com.android.server.backup.RefactoredBackupManagerService; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.PasswordUtils; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; Loading Loading @@ -119,7 +120,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor for (String pkgName : pkgNames) { if (!set.containsKey(pkgName)) { try { PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(pkgName, PackageInfo info = backupManagerService.getPackageManager().getPackageInfo( pkgName, PackageManager.GET_SIGNATURES); set.put(pkgName, info); } catch (NameNotFoundException e) { Loading @@ -134,17 +136,17 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor OutputStream ofstream) throws Exception { // User key will be used to encrypt the master key. byte[] newUserSalt = backupManagerService .randomBytes(RefactoredBackupManagerService.PBKDF2_SALT_SIZE); SecretKey userKey = backupManagerService .randomBytes(PasswordUtils.PBKDF2_SALT_SIZE); SecretKey userKey = PasswordUtils .buildPasswordKey(RefactoredBackupManagerService.PBKDF_CURRENT, mEncryptPassword, newUserSalt, RefactoredBackupManagerService.PBKDF2_HASH_ROUNDS); PasswordUtils.PBKDF2_HASH_ROUNDS); // the master key is random for each backup byte[] masterPw = new byte[256 / 8]; backupManagerService.getRng().nextBytes(masterPw); byte[] checksumSalt = backupManagerService .randomBytes(RefactoredBackupManagerService.PBKDF2_SALT_SIZE); .randomBytes(PasswordUtils.PBKDF2_SALT_SIZE); // primary encryption of the datastream with the random key Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); Loading @@ -153,16 +155,16 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor OutputStream finalOutput = new CipherOutputStream(ofstream, c); // line 4: name of encryption algorithm headerbuf.append(RefactoredBackupManagerService.ENCRYPTION_ALGORITHM_NAME); headerbuf.append(PasswordUtils.ENCRYPTION_ALGORITHM_NAME); headerbuf.append('\n'); // line 5: user password salt [hex] headerbuf.append(backupManagerService.byteArrayToHex(newUserSalt)); headerbuf.append(PasswordUtils.byteArrayToHex(newUserSalt)); headerbuf.append('\n'); // line 6: master key checksum salt [hex] headerbuf.append(backupManagerService.byteArrayToHex(checksumSalt)); headerbuf.append(PasswordUtils.byteArrayToHex(checksumSalt)); headerbuf.append('\n'); // line 7: number of PBKDF2 rounds used [decimal] headerbuf.append(RefactoredBackupManagerService.PBKDF2_HASH_ROUNDS); headerbuf.append(PasswordUtils.PBKDF2_HASH_ROUNDS); headerbuf.append('\n'); // line 8: IV of the user key [hex] Loading @@ -170,7 +172,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor mkC.init(Cipher.ENCRYPT_MODE, userKey); byte[] IV = mkC.getIV(); headerbuf.append(backupManagerService.byteArrayToHex(IV)); headerbuf.append(PasswordUtils.byteArrayToHex(IV)); headerbuf.append('\n'); // line 9: master IV + key blob, encrypted by the user key [hex]. Blob format: Loading @@ -185,10 +187,10 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor // stated number of PBKDF2 rounds IV = c.getIV(); byte[] mk = masterKeySpec.getEncoded(); byte[] checksum = backupManagerService byte[] checksum = PasswordUtils .makeKeyChecksum(RefactoredBackupManagerService.PBKDF_CURRENT, masterKeySpec.getEncoded(), checksumSalt, RefactoredBackupManagerService.PBKDF2_HASH_ROUNDS); checksumSalt, PasswordUtils.PBKDF2_HASH_ROUNDS); ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length + checksum.length + 3); Loading @@ -201,7 +203,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor mkOut.write(checksum); mkOut.flush(); byte[] encryptedMk = mkC.doFinal(blob.toByteArray()); headerbuf.append(backupManagerService.byteArrayToHex(encryptedMk)); headerbuf.append(PasswordUtils.byteArrayToHex(encryptedMk)); headerbuf.append('\n'); return finalOutput; Loading
services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +13 −10 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.server.backup.PackageManagerBackupAgent; import com.android.server.backup.RefactoredBackupManagerService; import com.android.server.backup.fullbackup.FullBackupObbConnection; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.PasswordUtils; import java.io.ByteArrayInputStream; import java.io.DataInputStream; Loading Loading @@ -315,15 +316,15 @@ public class PerformAdbRestoreTask implements Runnable { try { Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKey userKey = backupManagerService SecretKey userKey = PasswordUtils .buildPasswordKey(algorithm, mDecryptPassword, userSalt, rounds); byte[] IV = backupManagerService.hexToByteArray(userIvHex); byte[] IV = PasswordUtils.hexToByteArray(userIvHex); IvParameterSpec ivSpec = new IvParameterSpec(IV); c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(userKey.getEncoded(), "AES"), ivSpec); byte[] mkCipher = backupManagerService.hexToByteArray(masterKeyBlobHex); byte[] mkCipher = PasswordUtils.hexToByteArray(masterKeyBlobHex); byte[] mkBlob = c.doFinal(mkCipher); // first, the master key IV Loading @@ -342,7 +343,7 @@ public class PerformAdbRestoreTask implements Runnable { offset, offset + len); // now validate the decrypted master key against the checksum byte[] calculatedCk = backupManagerService.makeKeyChecksum(algorithm, mk, ckSalt, byte[] calculatedCk = PasswordUtils.makeKeyChecksum(algorithm, mk, ckSalt, rounds); if (Arrays.equals(calculatedCk, mkChecksum)) { ivSpec = new IvParameterSpec(IV); Loading Loading @@ -392,13 +393,13 @@ public class PerformAdbRestoreTask implements Runnable { InputStream rawInStream) { InputStream result = null; try { if (encryptionName.equals(RefactoredBackupManagerService.ENCRYPTION_ALGORITHM_NAME)) { if (encryptionName.equals(PasswordUtils.ENCRYPTION_ALGORITHM_NAME)) { String userSaltHex = readHeaderLine(rawInStream); // 5 byte[] userSalt = backupManagerService.hexToByteArray(userSaltHex); byte[] userSalt = PasswordUtils.hexToByteArray(userSaltHex); String ckSaltHex = readHeaderLine(rawInStream); // 6 byte[] ckSalt = backupManagerService.hexToByteArray(ckSaltHex); byte[] ckSalt = PasswordUtils.hexToByteArray(ckSaltHex); int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 String userIvHex = readHeaderLine(rawInStream); // 8 Loading Loading @@ -557,7 +558,8 @@ public class PerformAdbRestoreTask implements Runnable { } try { mTargetApp = backupManagerService.getPackageManager().getApplicationInfo( mTargetApp = backupManagerService.getPackageManager().getApplicationInfo( pkg, 0); // If we haven't sent any data to this app yet, we probably Loading Loading @@ -836,7 +838,8 @@ public class PerformAdbRestoreTask implements Runnable { if (RefactoredBackupManagerService.DEBUG) { Slog.d(RefactoredBackupManagerService.TAG, "Killing host process"); } backupManagerService.getActivityManager().killApplicationProcess(app.processName, backupManagerService.getActivityManager().killApplicationProcess( app.processName, app.uid); } else { if (RefactoredBackupManagerService.DEBUG) { Loading
services/backup/java/com/android/server/backup/utils/PasswordUtils.java 0 → 100644 +120 −0 Original line number Diff line number Diff line package com.android.server.backup.utils; import android.util.Slog; import com.android.server.backup.RefactoredBackupManagerService; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; /** * Passwords related utility methods. */ public class PasswordUtils { // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys public static final int PBKDF2_HASH_ROUNDS = 10000; private static final int PBKDF2_KEY_SIZE = 256; // bits public static final int PBKDF2_SALT_SIZE = 512; // bits public static final String ENCRYPTION_ALGORITHM_NAME = "AES-256"; /** * Creates {@link SecretKey} instance from given parameters. * * @param algorithm - key generation algorithm. * @param pw - password. * @param salt - salt. * @param rounds - number of rounds to run in key generation. * @return {@link SecretKey} instance or null in case of an error. */ public static SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) { return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds); } /** * Generates {@link SecretKey} instance from given parameters and returns it's hex * representation. * * @param algorithm - key generation algorithm. * @param pw - password. * @param salt - salt. * @param rounds - number of rounds to run in key generation. * @return Hex representation of the generated key, or null if generation failed. */ public static String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) { SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds); if (key != null) { return byteArrayToHex(key.getEncoded()); } return null; } /** * Creates hex string representation of the byte array. */ public static String byteArrayToHex(byte[] data) { StringBuilder buf = new StringBuilder(data.length * 2); for (int i = 0; i < data.length; i++) { buf.append(Byte.toHexString(data[i], true)); } return buf.toString(); } /** * Creates byte array from it's hex string representation. */ public static byte[] hexToByteArray(String digits) { final int bytes = digits.length() / 2; if (2 * bytes != digits.length()) { throw new IllegalArgumentException("Hex string must have an even number of digits"); } byte[] result = new byte[bytes]; for (int i = 0; i < digits.length(); i += 2) { result[i / 2] = (byte) Integer.parseInt(digits.substring(i, i + 2), 16); } return result; } /** * Generates {@link SecretKey} instance from given parameters and returns it's checksum. * * Current implementation returns the key in its primary encoding format. * * @param algorithm - key generation algorithm. * @param pwBytes - password. * @param salt - salt. * @param rounds - number of rounds to run in key generation. * @return Hex representation of the generated key, or null if generation failed. */ public static byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) { char[] mkAsChar = new char[pwBytes.length]; for (int i = 0; i < pwBytes.length; i++) { mkAsChar[i] = (char) pwBytes[i]; } Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds); return checksum.getEncoded(); } private static SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) { try { SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); return keyFactory.generateSecret(ks); } catch (InvalidKeySpecException e) { Slog.e(RefactoredBackupManagerService.TAG, "Invalid key spec for PBKDF2!"); } catch (NoSuchAlgorithmException e) { Slog.e(RefactoredBackupManagerService.TAG, "PBKDF2 unavailable!"); } return null; } }