Credits: https://community.home-assistant.io/t/get-files-from-password-protected-snapshot/245066/16
Usage:
python3 decrypt.py -i backup_name.tar -p PASSWORD
#!/usr/bin/env python3 | |
import sys | |
import getopt | |
import hashlib | |
import tarfile | |
import glob | |
import os | |
import shutil | |
from pathlib import Path | |
from cryptography.hazmat.backends import default_backend | |
from cryptography.hazmat.primitives.ciphers import ( | |
Cipher, | |
algorithms, | |
modes, | |
) | |
def _password_to_key(password): | |
password = password.encode() | |
for _ in range(100): | |
password = hashlib.sha256(password).digest() | |
return password[:16] | |
def _generate_iv(key, salt): | |
temp_iv = key + salt | |
for _ in range(100): | |
temp_iv = hashlib.sha256(temp_iv).digest() | |
return temp_iv[:16] | |
class SecureTarFile: | |
def __init__(self, filename, password): | |
self._file = None | |
self._name = Path(filename) | |
self._tar = None | |
self._tar_mode = "r|gz" | |
self._aes = None | |
self._key = _password_to_key(password) | |
self._decrypt = None | |
def __enter__(self): | |
self._file = self._name.open("rb") | |
cbc_rand = self._file.read(16) | |
self._aes = Cipher( | |
algorithms.AES(self._key), | |
modes.CBC(_generate_iv(self._key, cbc_rand)), | |
backend=default_backend(), | |
) | |
self._decrypt = self._aes.decryptor() | |
self._tar = tarfile.open(fileobj=self, mode=self._tar_mode) | |
return self._tar | |
def __exit__(self, exc_type, exc_value, traceback): | |
if self._tar: | |
self._tar.close() | |
if self._file: | |
self._file.close() | |
def read(self, size = 0): | |
return self._decrypt.update(self._file.read(size)) | |
@property | |
def path(self): | |
return self._name | |
@property | |
def size(self): | |
if not self._name.is_file(): | |
return 0 | |
return round(self._name.stat().st_size / 1_048_576, 2) # calc mbyte | |
def _extract_tar(filename): | |
_dirname = '.'.join(filename.split('.')[:-1]) | |
try: | |
shutil.rmtree('_dirname') | |
except FileNotFoundError: | |
pass | |
print(f'Extracting {filename}...') | |
_tar = tarfile.open(name=filename, mode="r") | |
_tar.extractall(path=_dirname) | |
return _dirname | |
def _extract_secure_tar(filename, password): | |
_dirname = '.'.join(filename.split('.')[:-2]) | |
print(f'Extracting secure tar {filename.split("/")[-1]}...') | |
try: | |
with SecureTarFile(filename, password) as _tar: | |
_tar.extractall(path=_dirname) | |
except tarfile.ReadError: | |
print("Unable to extract SecureTar - maybe your password is wrong or the tar is not password encrypted?") | |
sys.exit(5) | |
return _dirname | |
def print_usage(): | |
print(f'{sys.argv[0]} -i <inputfile> -p <password>') | |
def main(): | |
_inputfile = None | |
_password=None | |
try: | |
opts, args = getopt.getopt(sys.argv[1:],"hi:p:") | |
except getopt.GetoptError: | |
print_usage() | |
sys.exit(2) | |
for opt, arg in opts: | |
if opt == '-h': | |
print_usage() | |
sys.exit() | |
elif opt in ("-i"): | |
_inputfile = arg | |
elif opt in ("-p"): | |
_password = arg | |
if not _inputfile: | |
print ("Missing inputfile") | |
print_usage() | |
sys.exit(3) | |
if not _password: | |
print ("Missing password") | |
print_usage() | |
sys.exit(4) | |
_dirname = _extract_tar(_inputfile) | |
for _secure_tar in glob.glob(f'{_dirname}/*.tar.gz'): | |
_extract_secure_tar(_secure_tar, _password) | |
os.remove(_secure_tar) | |
print("Done") | |
if __name__ == "__main__": | |
main() |
Credits: https://community.home-assistant.io/t/get-files-from-password-protected-snapshot/245066/16
Usage:
python3 decrypt.py -i backup_name.tar -p PASSWORD