Update language to comply with Android's inclusive language guidance
See https://source.android.com/setup/contribute/respectful-code for reference PerformAdb{Backup,Restore}Task handles backing up to and restoring from a file. The backup data is encrypted with a randomly generated encryption key. The encryption key is encrypted with a key that is derived from a user supplied passphrase. The key that was used to encrypt and decrypt the backup data used to be called the "master key", I've renamed it to "encryption key" which better describes its purpose. Bug: 161896447 Test: `atest PerformAdbRestoreTaskTest`, `atest TarBackupReaderTest` and manually tested using adb with the following commands: `adb backup -all -apk -shared -f backup.ab` and `adb restore backup.ab`. Change-Id: I8e6b22a06b2a583440839994f0626d370bdb2e19
This commit is contained in:
@@ -24,8 +24,6 @@ import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEA
|
||||
import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
|
||||
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.app.backup.BackupManager.OperationType;
|
||||
import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
@@ -143,7 +141,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
|
||||
|
||||
private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
|
||||
OutputStream ofstream) throws Exception {
|
||||
// User key will be used to encrypt the master key.
|
||||
// User key will be used to encrypt the encryption key.
|
||||
byte[] newUserSalt = mUserBackupManagerService
|
||||
.randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
|
||||
SecretKey userKey = PasswordUtils
|
||||
@@ -151,16 +149,16 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
|
||||
newUserSalt,
|
||||
PasswordUtils.PBKDF2_HASH_ROUNDS);
|
||||
|
||||
// the master key is random for each backup
|
||||
byte[] masterPw = new byte[256 / 8];
|
||||
mUserBackupManagerService.getRng().nextBytes(masterPw);
|
||||
// the encryption key is random for each backup
|
||||
byte[] encryptionKey = new byte[256 / 8];
|
||||
mUserBackupManagerService.getRng().nextBytes(encryptionKey);
|
||||
byte[] checksumSalt = mUserBackupManagerService
|
||||
.randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
|
||||
|
||||
// primary encryption of the datastream with the random key
|
||||
// primary encryption of the datastream with the encryption key
|
||||
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES");
|
||||
c.init(Cipher.ENCRYPT_MODE, masterKeySpec);
|
||||
SecretKeySpec encryptionKeySpec = new SecretKeySpec(encryptionKey, "AES");
|
||||
c.init(Cipher.ENCRYPT_MODE, encryptionKeySpec);
|
||||
OutputStream finalOutput = new CipherOutputStream(ofstream, c);
|
||||
|
||||
// line 4: name of encryption algorithm
|
||||
@@ -169,7 +167,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
|
||||
// line 5: user password salt [hex]
|
||||
headerbuf.append(PasswordUtils.byteArrayToHex(newUserSalt));
|
||||
headerbuf.append('\n');
|
||||
// line 6: master key checksum salt [hex]
|
||||
// line 6: encryption key checksum salt [hex]
|
||||
headerbuf.append(PasswordUtils.byteArrayToHex(checksumSalt));
|
||||
headerbuf.append('\n');
|
||||
// line 7: number of PBKDF2 rounds used [decimal]
|
||||
@@ -184,21 +182,21 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
|
||||
headerbuf.append(PasswordUtils.byteArrayToHex(IV));
|
||||
headerbuf.append('\n');
|
||||
|
||||
// line 9: master IV + key blob, encrypted by the user key [hex]. Blob format:
|
||||
// line 9: encryption IV + key blob, encrypted by the user key [hex]. Blob format:
|
||||
// [byte] IV length = Niv
|
||||
// [array of Niv bytes] IV itself
|
||||
// [byte] master key length = Nmk
|
||||
// [array of Nmk bytes] master key itself
|
||||
// [byte] MK checksum hash length = Nck
|
||||
// [array of Nck bytes] master key checksum hash
|
||||
// [byte] encryption key length = Nek
|
||||
// [array of Nek bytes] encryption key itself
|
||||
// [byte] encryption key checksum hash length = Nck
|
||||
// [array of Nck bytes] encryption key checksum hash
|
||||
//
|
||||
// The checksum is the (master key + checksum salt), run through the
|
||||
// The checksum is the (encryption key + checksum salt), run through the
|
||||
// stated number of PBKDF2 rounds
|
||||
IV = c.getIV();
|
||||
byte[] mk = masterKeySpec.getEncoded();
|
||||
byte[] mk = encryptionKeySpec.getEncoded();
|
||||
byte[] checksum = PasswordUtils
|
||||
.makeKeyChecksum(PBKDF_CURRENT,
|
||||
masterKeySpec.getEncoded(),
|
||||
encryptionKeySpec.getEncoded(),
|
||||
checksumSalt, PasswordUtils.PBKDF2_HASH_ROUNDS);
|
||||
|
||||
ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
|
||||
@@ -347,15 +345,15 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
|
||||
// When line 4 is not "none", then additional header data follows:
|
||||
//
|
||||
// line 5: user password salt [hex]
|
||||
// line 6: master key checksum salt [hex]
|
||||
// line 7: number of PBKDF2 rounds to use (same for user & master) [decimal]
|
||||
// line 6: encryption key checksum salt [hex]
|
||||
// line 7: number of PBKDF2 rounds to use (same for user & encryption key) [decimal]
|
||||
// line 8: IV of the user key [hex]
|
||||
// line 9: master key blob [hex]
|
||||
// IV of the master key, master key itself, master key checksum hash
|
||||
// line 9: encryption key blob [hex]
|
||||
// IV of the encryption key, encryption key itself, encryption key checksum hash
|
||||
//
|
||||
// The master key checksum is the master key plus its checksum salt, run through
|
||||
// The encryption key checksum is the encryption key plus its checksum salt, run through
|
||||
// 10k rounds of PBKDF2. This is used to verify that the user has supplied the
|
||||
// correct password for decrypting the archive: the master key decrypted from
|
||||
// correct password for decrypting the archive: the encryption key decrypted from
|
||||
// the archive using the user-supplied password is also run through PBKDF2 in
|
||||
// this way, and if the result does not match the checksum as stored in the
|
||||
// archive, then we know that the user-supplied password does not match the
|
||||
|
||||
@@ -212,10 +212,9 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private static InputStream attemptMasterKeyDecryption(String decryptPassword, String algorithm,
|
||||
byte[] userSalt, byte[] ckSalt,
|
||||
int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
|
||||
boolean doLog) {
|
||||
private static InputStream attemptEncryptionKeyDecryption(String decryptPassword,
|
||||
String algorithm, byte[] userSalt, byte[] ckSalt, int rounds, String userIvHex,
|
||||
String encryptionKeyBlobHex, InputStream rawInStream, boolean doLog) {
|
||||
InputStream result = null;
|
||||
|
||||
try {
|
||||
@@ -228,31 +227,31 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
c.init(Cipher.DECRYPT_MODE,
|
||||
new SecretKeySpec(userKey.getEncoded(), "AES"),
|
||||
ivSpec);
|
||||
byte[] mkCipher = PasswordUtils.hexToByteArray(masterKeyBlobHex);
|
||||
byte[] mkCipher = PasswordUtils.hexToByteArray(encryptionKeyBlobHex);
|
||||
byte[] mkBlob = c.doFinal(mkCipher);
|
||||
|
||||
// first, the master key IV
|
||||
// first, the encryption key IV
|
||||
int offset = 0;
|
||||
int len = mkBlob[offset++];
|
||||
IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
|
||||
offset += len;
|
||||
// then the master key itself
|
||||
// then the encryption key itself
|
||||
len = mkBlob[offset++];
|
||||
byte[] mk = Arrays.copyOfRange(mkBlob,
|
||||
byte[] encryptionKey = Arrays.copyOfRange(mkBlob,
|
||||
offset, offset + len);
|
||||
offset += len;
|
||||
// and finally the master key checksum hash
|
||||
// and finally the encryption key checksum hash
|
||||
len = mkBlob[offset++];
|
||||
byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
|
||||
offset, offset + len);
|
||||
|
||||
// now validate the decrypted master key against the checksum
|
||||
byte[] calculatedCk = PasswordUtils.makeKeyChecksum(algorithm, mk, ckSalt,
|
||||
// now validate the decrypted encryption key against the checksum
|
||||
byte[] calculatedCk = PasswordUtils.makeKeyChecksum(algorithm, encryptionKey, ckSalt,
|
||||
rounds);
|
||||
if (Arrays.equals(calculatedCk, mkChecksum)) {
|
||||
ivSpec = new IvParameterSpec(IV);
|
||||
c.init(Cipher.DECRYPT_MODE,
|
||||
new SecretKeySpec(mk, "AES"),
|
||||
new SecretKeySpec(encryptionKey, "AES"),
|
||||
ivSpec);
|
||||
// Only if all of the above worked properly will 'result' be assigned
|
||||
result = new CipherInputStream(rawInStream, c);
|
||||
@@ -265,7 +264,7 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
}
|
||||
} catch (BadPaddingException e) {
|
||||
// This case frequently occurs when the wrong password is used to decrypt
|
||||
// the master key. Use the identical "incorrect password" log text as is
|
||||
// the encryption key. Use the identical "incorrect password" log text as is
|
||||
// used in the checksum failure log in order to avoid providing additional
|
||||
// information to an attacker.
|
||||
if (doLog) {
|
||||
@@ -273,7 +272,7 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
}
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
if (doLog) {
|
||||
Slog.w(TAG, "Invalid block size in master key");
|
||||
Slog.w(TAG, "Invalid block size in encryption key");
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
if (doLog) {
|
||||
@@ -309,15 +308,15 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
|
||||
String userIvHex = readHeaderLine(rawInStream); // 8
|
||||
|
||||
String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
|
||||
String encryptionKeyBlobHex = readHeaderLine(rawInStream); // 9
|
||||
|
||||
// decrypt the master key blob
|
||||
result = attemptMasterKeyDecryption(decryptPassword, PBKDF_CURRENT,
|
||||
userSalt, ckSalt, rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
|
||||
// decrypt the encryption key blob
|
||||
result = attemptEncryptionKeyDecryption(decryptPassword, PBKDF_CURRENT, userSalt,
|
||||
ckSalt, rounds, userIvHex, encryptionKeyBlobHex, rawInStream, false);
|
||||
if (result == null && pbkdf2Fallback) {
|
||||
result = attemptMasterKeyDecryption(
|
||||
result = attemptEncryptionKeyDecryption(
|
||||
decryptPassword, PBKDF_FALLBACK, userSalt, ckSalt,
|
||||
rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
|
||||
rounds, userIvHex, encryptionKeyBlobHex, rawInStream, true);
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
|
||||
|
||||
Reference in New Issue
Block a user