/TitaniumBackupDecrypt

Decrypts encrypted archive files from Titanium Backup for Android.

Primary LanguagePHPMIT LicenseMIT

TitaniumBackupDecrypt

Decrypts encrypted archive files from Titanium Backup for Android.

If you use this program, please let me know via a message on Github. Thanks!
https://github.com/bhafer/TitaniumBackupDecrypt

=====================

Dependencies:
     http://phpseclib.sourceforge.net/ :: Install via PEAR with:
         pear channel-discover phpseclib.sourceforge.net
         pear install phpseclib/Crypt_AES phpseclib/Crypt_RSA

Usage:
     php TitaniumBackupDecrypt <archive-file>
          Will check for TB_ARCHIVE_PASSWORD environment variable or else prompt for password.
            --OR--
     php TitaniumBackupDecrypt <archive-file> <password>
Where archive-file is a file of one of the following types:
     .tar.bz2
     .tar.gz
     .tar.lzop
     .tar
     .xml.bz2
     .xml.gz
     .xml.lzop
     .xml

Based on the file format specification information from Titanium Backup at:
     https://plus.google.com/+ChristianEgger/posts/MQBmYhKDex5

=====================

Snapshot of https://plus.google.com/+ChristianEgger/posts/MQBmYhKDex5 ::

So, the file format for encrypted data backups is as follows:

"TB_ARMOR_V1" '\n' passphraseHmacKey '\n' passphraseHmacResult '\n' publicKey '\n' encryptedPrivateKey '\n' encryptedSessionKey '\n' Data

Each of the 5 "variables" (passphraseHmacKey, passphraseHmacResult, publicKey, encryptedPrivateKey, encryptedSessionKey) is stored in Base64 format without linewraps (of course) and can be decoded with: Base64.decode(passphraseHmacKey, Base64.NO_WRAP)

Then the user-supplied passphrase (String) can be verified as follows:
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(passphraseHmacKey, "HmacSHA1"));
byte[] sigBytes = mac.doFinal(passphrase.getBytes("UTF-8"));
boolean passphraseMatches = Arrays.equals(sigBytes, passphraseHmacResult);

Then the passphrase is independently hashed with SHA-1. We append [twelve] 0x00 bytes to the 160-bit result to constitute the 256-bit AES key which is used to decrypt "encryptedPrivateKey" (with an IV of [sixteen] 0x00 bytes). [Decrypt using AES-256 in CBC mode and perform PKCS5 unpadding. Note that AES-256 is equivalent to Rijndael-128 using a 256 bit key.]

Then we build the KeyPair object as follows:
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey2 = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
PublicKey publicKey2 = keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
KeyPair keyPair = new KeyPair(publicKey2, privateKey2);

Then we decrypt the session key as follows:
Cipher rsaDecrypt = Cipher.getInstance("RSA/NONE/PKCS1Padding"); rsaDecrypt.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CipherOutputStream cos = new CipherOutputStream(baos, rsaDecrypt);
cos.write(encryptedSessionKey);
cos.close();
byte[] sessionKey = baos.toByteArray();

And finally, we decrypt the data itself with the session key (which can be either a 128-bit, 192-bit or 256-bit key) and with a 0x00 IV.

While the "zero" IV is suboptimal from a security standpoint, it allows files to be encoded faster - because every little bit counts, especially when we store backups with LZO compression.

=====================

Docker usage: (until a full image is built)

First, build your own image with Docker

docker build -t TitaniumBackupDecrypt .

Then, run as a standalone container.

You MUST mount the directory that holds TitaniumBackup files into the container.

Example for Windows hosts

docker run --rm -ti -v C:\Users\Example\TitaniumBackup:/app /app/encrypted-backup.tar.gz

Example for Linux hosts

docker run --rm -ti -v /home/example/TitaniumBackup:/app /app/encrypted-backup.tar.gz