Fix transport encryption
Prior to this commit, some of the application data was not included during encryption. This is a breaking change, any backups made prior to this commit can no longer be restored. 1. Encrypt 'full' backup data. 2. Increase number of key generation iterations to 32767. 3. Change cipher to 'AES/CBC/PKCS5Padding'.
This commit is contained in:
parent
b16fcf5d87
commit
04543a1014
7 changed files with 254 additions and 168 deletions
|
@ -16,12 +16,13 @@ public class CipherUtil {
|
||||||
/**
|
/**
|
||||||
* The cipher algorithm.
|
* The cipher algorithm.
|
||||||
*/
|
*/
|
||||||
public static final String CIPHER_ALGORITHM = "AES/CFB/PKCS5Padding";
|
public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
|
||||||
|
|
||||||
/**.
|
/**
|
||||||
|
* .
|
||||||
* Encrypts the given payload using the provided secret key.
|
* Encrypts the given payload using the provided secret key.
|
||||||
*
|
*
|
||||||
* @param payload The payload.
|
* @param payload The payload.
|
||||||
* @param secretKey The secret key.
|
* @param secretKey The secret key.
|
||||||
* @param iv The initialization vector.
|
* @param iv The initialization vector.
|
||||||
*/
|
*/
|
||||||
|
@ -29,9 +30,22 @@ public class CipherUtil {
|
||||||
NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException,
|
NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException,
|
||||||
InvalidAlgorithmParameterException, InvalidKeyException {
|
InvalidAlgorithmParameterException, InvalidKeyException {
|
||||||
|
|
||||||
|
return startEncrypt(secretKey, iv).doFinal(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a cipher in {@link Cipher#ENCRYPT_MODE}.
|
||||||
|
*
|
||||||
|
* @param secretKey The secret key.
|
||||||
|
* @param iv The initialization vector.
|
||||||
|
* @return The initialized cipher.
|
||||||
|
*/
|
||||||
|
public static Cipher startEncrypt(SecretKey secretKey, byte[] iv) throws NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
|
||||||
return cipher.doFinal(payload);
|
return cipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,8 +59,21 @@ public class CipherUtil {
|
||||||
NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException,
|
NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException,
|
||||||
InvalidAlgorithmParameterException, InvalidKeyException {
|
InvalidAlgorithmParameterException, InvalidKeyException {
|
||||||
|
|
||||||
|
return startDecrypt(secretKey, iv).doFinal(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a cipher in {@link Cipher#DECRYPT_MODE}.
|
||||||
|
*
|
||||||
|
* @param secretKey The secret key.
|
||||||
|
* @param iv The initialization vector.
|
||||||
|
* @return The initialized cipher.
|
||||||
|
*/
|
||||||
|
public static Cipher startDecrypt(SecretKey secretKey, byte[] iv) throws NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
|
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
|
||||||
return cipher.doFinal(payload);
|
return cipher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ public class KeyGenerator {
|
||||||
/**
|
/**
|
||||||
* The number of iterations for key generation.
|
* The number of iterations for key generation.
|
||||||
*/
|
*/
|
||||||
private static final int ITERATIONS = 25;
|
private static final int ITERATIONS = 32767;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The generated key length.
|
* The generated key length.
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.stevesoltys.backup.transport.component.BackupComponent;
|
||||||
import libcore.io.IoUtils;
|
import libcore.io.IoUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
@ -44,6 +45,30 @@ public class ContentProviderBackupComponent implements BackupComponent {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelFullBackup() {
|
||||||
|
clearBackupState(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int checkFullBackupSize(long size) {
|
||||||
|
int result = TRANSPORT_OK;
|
||||||
|
|
||||||
|
if (size <= 0) {
|
||||||
|
result = TRANSPORT_PACKAGE_REJECTED;
|
||||||
|
|
||||||
|
} else if (size > configuration.getBackupSizeQuota()) {
|
||||||
|
result = TRANSPORT_QUOTA_EXCEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int clearBackupData(PackageInfo packageInfo) {
|
||||||
|
return TRANSPORT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String currentDestinationString() {
|
public String currentDestinationString() {
|
||||||
return DESTINATION_DESCRIPTION;
|
return DESTINATION_DESCRIPTION;
|
||||||
|
@ -54,85 +79,19 @@ public class ContentProviderBackupComponent implements BackupComponent {
|
||||||
return TRANSPORT_DATA_MANAGEMENT_LABEL;
|
return TRANSPORT_DATA_MANAGEMENT_LABEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int initializeDevice() {
|
|
||||||
return TRANSPORT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int clearBackupData(PackageInfo packageInfo) {
|
|
||||||
return TRANSPORT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int finishBackup() {
|
public int finishBackup() {
|
||||||
return clearBackupState(false);
|
return clearBackupState(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int performIncrementalBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
|
|
||||||
BackupDataInput backupDataInput = new BackupDataInput(data.getFileDescriptor());
|
|
||||||
|
|
||||||
try {
|
|
||||||
initializeBackupState();
|
|
||||||
backupState.setPackageIndex(backupState.getPackageIndex() + 1);
|
|
||||||
backupState.setPackageName(packageInfo.packageName);
|
|
||||||
|
|
||||||
return transferIncrementalBackupData(backupDataInput);
|
|
||||||
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Log.e(TAG, "Error reading backup input: ", ex);
|
|
||||||
return TRANSPORT_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int clearBackupState(boolean closeFile) {
|
|
||||||
|
|
||||||
if (backupState == null) {
|
|
||||||
return TRANSPORT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
IoUtils.closeQuietly(backupState.getInputFileDescriptor());
|
|
||||||
backupState.setInputFileDescriptor(null);
|
|
||||||
|
|
||||||
ZipOutputStream outputStream = backupState.getOutputStream();
|
|
||||||
|
|
||||||
if (outputStream != null) {
|
|
||||||
outputStream.closeEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backupState.getPackageIndex() == configuration.getPackageCount() || closeFile) {
|
|
||||||
if (outputStream != null) {
|
|
||||||
outputStream.finish();
|
|
||||||
outputStream.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
IoUtils.closeQuietly(backupState.getOutputFileDescriptor());
|
|
||||||
backupState = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Log.e(TAG, "Error cancelling full backup: ", ex);
|
|
||||||
return TRANSPORT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRANSPORT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getBackupQuota(String packageName, boolean fullBackup) {
|
public long getBackupQuota(String packageName, boolean fullBackup) {
|
||||||
return configuration.getBackupSizeQuota();
|
return configuration.getBackupSizeQuota();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long requestBackupTime() {
|
public int initializeDevice() {
|
||||||
return 0;
|
return TRANSPORT_OK;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long requestFullBackupTime() {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -152,6 +111,9 @@ public class ContentProviderBackupComponent implements BackupComponent {
|
||||||
backupState.setInputStream(new FileInputStream(fileDescriptor.getFileDescriptor()));
|
backupState.setInputStream(new FileInputStream(fileDescriptor.getFileDescriptor()));
|
||||||
backupState.setBytesTransferred(0);
|
backupState.setBytesTransferred(0);
|
||||||
|
|
||||||
|
Cipher cipher = CipherUtil.startEncrypt(backupState.getSecretKey(), backupState.getSalt());
|
||||||
|
backupState.setCipher(cipher);
|
||||||
|
|
||||||
ZipEntry zipEntry = new ZipEntry(configuration.getFullBackupDirectory() + backupState.getPackageName());
|
ZipEntry zipEntry = new ZipEntry(configuration.getFullBackupDirectory() + backupState.getPackageName());
|
||||||
backupState.getOutputStream().putNextEntry(zipEntry);
|
backupState.getOutputStream().putNextEntry(zipEntry);
|
||||||
|
|
||||||
|
@ -165,17 +127,30 @@ public class ContentProviderBackupComponent implements BackupComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int checkFullBackupSize(long size) {
|
public int performIncrementalBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
|
||||||
int result = TRANSPORT_OK;
|
BackupDataInput backupDataInput = new BackupDataInput(data.getFileDescriptor());
|
||||||
|
|
||||||
if (size <= 0) {
|
try {
|
||||||
result = TRANSPORT_PACKAGE_REJECTED;
|
initializeBackupState();
|
||||||
|
backupState.setPackageIndex(backupState.getPackageIndex() + 1);
|
||||||
|
backupState.setPackageName(packageInfo.packageName);
|
||||||
|
|
||||||
} else if (size > configuration.getBackupSizeQuota()) {
|
return transferIncrementalBackupData(backupDataInput);
|
||||||
result = TRANSPORT_QUOTA_EXCEEDED;
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.e(TAG, "Error reading backup input: ", ex);
|
||||||
|
return TRANSPORT_ERROR;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
@Override
|
||||||
|
public long requestBackupTime() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long requestFullBackupTime() {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -196,51 +171,22 @@ public class ContentProviderBackupComponent implements BackupComponent {
|
||||||
ZipOutputStream outputStream = backupState.getOutputStream();
|
ZipOutputStream outputStream = backupState.getOutputStream();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
outputStream.write(IOUtils.readFully(inputStream, numBytes));
|
byte[] payload = IOUtils.readFully(inputStream, numBytes);
|
||||||
|
|
||||||
|
if (backupState.getCipher() != null) {
|
||||||
|
payload = backupState.getCipher().update(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.write(payload, 0, numBytes);
|
||||||
backupState.setBytesTransferred(bytesTransferred);
|
backupState.setBytesTransferred(bytesTransferred);
|
||||||
|
|
||||||
} catch (IOException ex) {
|
} catch (Exception ex) {
|
||||||
Log.e(TAG, "Error handling backup data for " + backupState.getPackageName() + ": ", ex);
|
Log.e(TAG, "Error handling backup data for " + backupState.getPackageName() + ": ", ex);
|
||||||
return TRANSPORT_ERROR;
|
return TRANSPORT_ERROR;
|
||||||
}
|
}
|
||||||
return TRANSPORT_OK;
|
return TRANSPORT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancelFullBackup() {
|
|
||||||
clearBackupState(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeBackupState() throws Exception {
|
|
||||||
if (backupState == null) {
|
|
||||||
backupState = new ContentProviderBackupState();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backupState.getOutputStream() == null) {
|
|
||||||
initializeOutputStream();
|
|
||||||
|
|
||||||
ZipEntry saltZipEntry = new ZipEntry(ContentProviderBackupConstants.SALT_FILE_PATH);
|
|
||||||
backupState.getOutputStream().putNextEntry(saltZipEntry);
|
|
||||||
backupState.getOutputStream().write(backupState.getSalt());
|
|
||||||
backupState.getOutputStream().closeEntry();
|
|
||||||
|
|
||||||
|
|
||||||
if (configuration.getPassword() != null && !configuration.getPassword().isEmpty()) {
|
|
||||||
backupState.setSecretKey(KeyGenerator.generate(configuration.getPassword(), backupState.getSalt()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeOutputStream() throws IOException {
|
|
||||||
ContentResolver contentResolver = configuration.getContext().getContentResolver();
|
|
||||||
ParcelFileDescriptor outputFileDescriptor = contentResolver.openFileDescriptor(configuration.getUri(), "w");
|
|
||||||
backupState.setOutputFileDescriptor(outputFileDescriptor);
|
|
||||||
|
|
||||||
FileOutputStream fileOutputStream = new FileOutputStream(outputFileDescriptor.getFileDescriptor());
|
|
||||||
ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
|
|
||||||
backupState.setOutputStream(zipOutputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int transferIncrementalBackupData(BackupDataInput backupDataInput) throws IOException {
|
private int transferIncrementalBackupData(BackupDataInput backupDataInput) throws IOException {
|
||||||
ZipOutputStream outputStream = backupState.getOutputStream();
|
ZipOutputStream outputStream = backupState.getOutputStream();
|
||||||
|
|
||||||
|
@ -285,4 +231,73 @@ public class ContentProviderBackupComponent implements BackupComponent {
|
||||||
|
|
||||||
return TRANSPORT_OK;
|
return TRANSPORT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeBackupState() throws Exception {
|
||||||
|
if (backupState == null) {
|
||||||
|
backupState = new ContentProviderBackupState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backupState.getOutputStream() == null) {
|
||||||
|
initializeOutputStream();
|
||||||
|
|
||||||
|
ZipEntry saltZipEntry = new ZipEntry(ContentProviderBackupConstants.SALT_FILE_PATH);
|
||||||
|
backupState.getOutputStream().putNextEntry(saltZipEntry);
|
||||||
|
backupState.getOutputStream().write(backupState.getSalt());
|
||||||
|
backupState.getOutputStream().closeEntry();
|
||||||
|
|
||||||
|
if (configuration.getPassword() != null && !configuration.getPassword().isEmpty()) {
|
||||||
|
backupState.setSecretKey(KeyGenerator.generate(configuration.getPassword(), backupState.getSalt()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeOutputStream() throws IOException {
|
||||||
|
ContentResolver contentResolver = configuration.getContext().getContentResolver();
|
||||||
|
ParcelFileDescriptor outputFileDescriptor = contentResolver.openFileDescriptor(configuration.getUri(), "w");
|
||||||
|
backupState.setOutputFileDescriptor(outputFileDescriptor);
|
||||||
|
|
||||||
|
FileOutputStream fileOutputStream = new FileOutputStream(outputFileDescriptor.getFileDescriptor());
|
||||||
|
ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
|
||||||
|
backupState.setOutputStream(zipOutputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int clearBackupState(boolean closeFile) {
|
||||||
|
|
||||||
|
if (backupState == null) {
|
||||||
|
return TRANSPORT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
IoUtils.closeQuietly(backupState.getInputFileDescriptor());
|
||||||
|
backupState.setInputFileDescriptor(null);
|
||||||
|
|
||||||
|
ZipOutputStream outputStream = backupState.getOutputStream();
|
||||||
|
|
||||||
|
if (outputStream != null) {
|
||||||
|
|
||||||
|
if (backupState.getCipher() != null) {
|
||||||
|
outputStream.write(backupState.getCipher().doFinal());
|
||||||
|
backupState.setCipher(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backupState.getPackageIndex() == configuration.getPackageCount() || closeFile) {
|
||||||
|
if (outputStream != null) {
|
||||||
|
outputStream.finish();
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
IoUtils.closeQuietly(backupState.getOutputFileDescriptor());
|
||||||
|
backupState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.e(TAG, "Error cancelling full backup: ", ex);
|
||||||
|
return TRANSPORT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRANSPORT_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.stevesoltys.backup.transport.component.provider;
|
||||||
|
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
@ -22,6 +23,8 @@ class ContentProviderBackupState {
|
||||||
|
|
||||||
private ZipOutputStream outputStream;
|
private ZipOutputStream outputStream;
|
||||||
|
|
||||||
|
private Cipher cipher;
|
||||||
|
|
||||||
private long bytesTransferred;
|
private long bytesTransferred;
|
||||||
|
|
||||||
private String packageName;
|
private String packageName;
|
||||||
|
@ -45,6 +48,14 @@ class ContentProviderBackupState {
|
||||||
this.bytesTransferred = bytesTransferred;
|
this.bytesTransferred = bytesTransferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cipher getCipher() {
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCipher(Cipher cipher) {
|
||||||
|
this.cipher = cipher;
|
||||||
|
}
|
||||||
|
|
||||||
ParcelFileDescriptor getInputFileDescriptor() {
|
ParcelFileDescriptor getInputFileDescriptor() {
|
||||||
return inputFileDescriptor;
|
return inputFileDescriptor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import libcore.io.Streams;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -166,29 +167,6 @@ public class ContentProviderRestoreComponent implements RestoreComponent {
|
||||||
return TRANSPORT_OK;
|
return TRANSPORT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ParcelFileDescriptor buildInputFileDescriptor() throws FileNotFoundException {
|
|
||||||
ContentResolver contentResolver = configuration.getContext().getContentResolver();
|
|
||||||
return contentResolver.openFileDescriptor(configuration.getUri(), "r");
|
|
||||||
}
|
|
||||||
|
|
||||||
private ZipInputStream buildInputStream(ParcelFileDescriptor inputFileDescriptor) throws FileNotFoundException {
|
|
||||||
FileInputStream fileInputStream = new FileInputStream(inputFileDescriptor.getFileDescriptor());
|
|
||||||
return new ZipInputStream(fileInputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ZipEntry> seekToEntry(ZipInputStream inputStream, String entryPath) throws IOException {
|
|
||||||
ZipEntry zipEntry;
|
|
||||||
while ((zipEntry = inputStream.getNextEntry()) != null) {
|
|
||||||
|
|
||||||
if (zipEntry.getName().startsWith(entryPath)) {
|
|
||||||
return Optional.of(zipEntry);
|
|
||||||
}
|
|
||||||
inputStream.closeEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] readBackupData(ZipInputStream inputStream) throws Exception {
|
private byte[] readBackupData(ZipInputStream inputStream) throws Exception {
|
||||||
byte[] backupData = Streams.readFullyNoClose(inputStream);
|
byte[] backupData = Streams.readFullyNoClose(inputStream);
|
||||||
SecretKey secretKey = restoreState.getSecretKey();
|
SecretKey secretKey = restoreState.getSecretKey();
|
||||||
|
@ -248,7 +226,28 @@ public class ContentProviderRestoreComponent implements RestoreComponent {
|
||||||
|
|
||||||
if (bytesRead <= 0) {
|
if (bytesRead <= 0) {
|
||||||
bytesRead = NO_MORE_DATA;
|
bytesRead = NO_MORE_DATA;
|
||||||
|
|
||||||
|
if (restoreState.getCipher() != null) {
|
||||||
|
buffer = restoreState.getCipher().doFinal();
|
||||||
|
bytesRead = buffer.length;
|
||||||
|
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
restoreState.setCipher(null);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (restoreState.getSecretKey() != null) {
|
||||||
|
SecretKey secretKey = restoreState.getSecretKey();
|
||||||
|
byte[] salt = restoreState.getSalt();
|
||||||
|
|
||||||
|
if (restoreState.getCipher() == null) {
|
||||||
|
restoreState.setCipher(CipherUtil.startDecrypt(secretKey, salt));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = restoreState.getCipher().update(Arrays.copyOfRange(buffer, 0, bytesRead));
|
||||||
|
bytesRead = buffer.length;
|
||||||
|
}
|
||||||
|
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,4 +304,27 @@ public class ContentProviderRestoreComponent implements RestoreComponent {
|
||||||
IoUtils.closeQuietly(restoreState.getInputFileDescriptor());
|
IoUtils.closeQuietly(restoreState.getInputFileDescriptor());
|
||||||
restoreState = null;
|
restoreState = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ParcelFileDescriptor buildInputFileDescriptor() throws FileNotFoundException {
|
||||||
|
ContentResolver contentResolver = configuration.getContext().getContentResolver();
|
||||||
|
return contentResolver.openFileDescriptor(configuration.getUri(), "r");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZipInputStream buildInputStream(ParcelFileDescriptor inputFileDescriptor) throws FileNotFoundException {
|
||||||
|
FileInputStream fileInputStream = new FileInputStream(inputFileDescriptor.getFileDescriptor());
|
||||||
|
return new ZipInputStream(fileInputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ZipEntry> seekToEntry(ZipInputStream inputStream, String entryPath) throws IOException {
|
||||||
|
ZipEntry zipEntry;
|
||||||
|
while ((zipEntry = inputStream.getNextEntry()) != null) {
|
||||||
|
|
||||||
|
if (zipEntry.getName().startsWith(entryPath)) {
|
||||||
|
return Optional.of(zipEntry);
|
||||||
|
}
|
||||||
|
inputStream.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.stevesoltys.backup.transport.component.provider;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
|
@ -23,16 +24,26 @@ class ContentProviderRestoreState {
|
||||||
|
|
||||||
private ZipInputStream inputStream;
|
private ZipInputStream inputStream;
|
||||||
|
|
||||||
|
private Cipher cipher;
|
||||||
|
|
||||||
private byte[] salt;
|
private byte[] salt;
|
||||||
|
|
||||||
private SecretKey secretKey;
|
private SecretKey secretKey;
|
||||||
|
|
||||||
private List<ZipEntry> zipEntries;
|
private List<ZipEntry> zipEntries;
|
||||||
|
|
||||||
|
Cipher getCipher() {
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
|
||||||
ParcelFileDescriptor getInputFileDescriptor() {
|
ParcelFileDescriptor getInputFileDescriptor() {
|
||||||
return inputFileDescriptor;
|
return inputFileDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCipher(Cipher cipher) {
|
||||||
|
this.cipher = cipher;
|
||||||
|
}
|
||||||
|
|
||||||
void setInputFileDescriptor(ParcelFileDescriptor inputFileDescriptor) {
|
void setInputFileDescriptor(ParcelFileDescriptor inputFileDescriptor) {
|
||||||
this.inputFileDescriptor = inputFileDescriptor;
|
this.inputFileDescriptor = inputFileDescriptor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,21 @@ import com.stevesoltys.backup.transport.component.BackupComponent;
|
||||||
*/
|
*/
|
||||||
public class StubBackupComponent implements BackupComponent {
|
public class StubBackupComponent implements BackupComponent {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelFullBackup() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int checkFullBackupSize(long size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int clearBackupData(PackageInfo packageInfo) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String currentDestinationString() {
|
public String currentDestinationString() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -19,23 +34,18 @@ public class StubBackupComponent implements BackupComponent {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int initializeDevice() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int clearBackupData(PackageInfo packageInfo) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int finishBackup() {
|
public int finishBackup() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int performIncrementalBackup(PackageInfo targetPackage, ParcelFileDescriptor data) {
|
public long getBackupQuota(String packageName, boolean fullBackup) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int initializeDevice() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,22 +55,7 @@ public class StubBackupComponent implements BackupComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int checkFullBackupSize(long size) {
|
public int performIncrementalBackup(PackageInfo targetPackage, ParcelFileDescriptor data) {
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int sendBackupData(int numBytes) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancelFullBackup() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getBackupQuota(String packageName, boolean fullBackup) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,4 +68,9 @@ public class StubBackupComponent implements BackupComponent {
|
||||||
public long requestFullBackupTime() {
|
public long requestFullBackupTime() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendBackupData(int numBytes) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue