Package: pytibade
py-tibade: Python Titanium Backup DecryptAuthor: Tet Woo LeeThis is a Python/pycryptodome-based implementation ofTitaniumBackupDecrypt,
which was originally authored by Brian T. Hafer. It will decrypt backups made
byTitanium Backup for Android.An alternative Python implementation is isTiBUdecryptercreated by David O'Rourke.InstallationThis is available on both PyPi:pip install pytibade
pytibade --versionand conda:conda create -n pytibade -c twlee79 pytibade
cona activate pytibade
pytibade --versionUsagepositional arguments:
  inputfiles            Input file(s), either a list of filenames or as an
                        unexpanded glob wildcards that is expanded internally

optional arguments:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  -s SUFFIX, --suffix SUFFIX
                        Suffix to add to base filename of decrypted files,
                        appended to stem before any extensions (default:
                        -decrypted)
  -m MATCH_BASENAME, --match_basename MATCH_BASENAME
                        Regular expression to match base filename of a file,
                        applied to filename without directory; first capturing
                        group should be base filename; an error is produced if
                        files don't match this pattern, which can be ignored
                        with the `-c` option (default:
                        (^.*-[0-9\-]+)[.]([a-z]{3})([.a-z]{0,4}))
  -p PASSPHRASE, --passphrase PASSPHRASE
                        Passphrase used to encrypt the backup files, if not
                        present then user will be prompted to enter one; same
                        passphrase used for all files. (default: None)
  -c, --continue        Continue processing next file even if error
                        encountered; by default script will stop on first
                        error (default: False)
  -v, --verbose         Increase logging verbosity, available levels 1 to 3
                        with `-v` to `-vvv` (default: 0)Simplest usage:pytibade com.my.favourite.app-20200426-111127.tar.gzThe script will then prompt for the passphrase.The script can be used to decrypt a batch of files by specifying multipleinputfilesor wildcards ininputfiles. The samepassphraseis used
for all files. The--continueoption is useful to persistently attempt to
decrypt all specified files despite earlier failures (useful, for example, if
there is a mixture of encrypted and unencrypted files).Titanium Backup File FormatInformation SourceThis information was originally taken from a post on Christian Egger's G+ pagethat has now disappeared.
It has been reformatted a little for markdown byDavid O'Rourke.File Format"TB_ARMOR_V1" '\n'
pass_hmac_key '\n'
pass_hmac_result '\n'
public_key '\n'
enc_privkey_spec '\n'
enc_sesskey_spec '\n'
dataExplanation of formatEach of the 5 "variables" (pass_hmac_key,pass_hmac_result,public_key,enc_privkey_spec,enc_sesskey_spec) is stored in
Base64 format without linewraps (of course) and can be decoded with:Base64.decode(pass_hmac_key, Base64.NO_WRAP)Then the user-supplied passphrase (String) can be verified as follows:Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(pass_hmac_key, "HmacSHA1"));
byte[] sigBytes = mac.doFinal(passphrase.getBytes("UTF-8"));
boolean passphraseMatches = Arrays.equals(sigBytes, pass_hmac_result);Then the passphrase is independently hashed with SHA-1. We append [twelve]0x00bytes to the 160-bit result to constitute the 256-bit AES key which is used to
decryptenc_privkey_spec(with an IV of [sixteen]0x00bytes). [Decrypt
using AES-256 in CBC mode and perform PKCS7 unpadding with block size 16.]Then we build the KeyPair object as follows:KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey2 = keyFactory.generatePrivate(
    new PKCS8EncodedKeySpec(privateKey)
);
PublicKey public_key2 = keyFactory.generatePublic(
    new X509EncodedKeySpec(public_key)
);
KeyPair keyPair = new KeyPair(public_key2, 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(enc_sesskey_spec); 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 a0x00IV. [Remember
to PKCS7 unpad with block size 16].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.AuthorTet Woo LeeCreated2020-04-26Copyright© 2020 Tet Woo LeeLicenseGPLv3Dependenciespycryptodome, tested with v3.8.2Change logversion 1.0.dev1 2020-04-26Working version