Created
January 2, 2021 16:52
-
-
Save xperylab/1436d02fe7bffa7ea96ec0e9116fa5f0 to your computer and use it in GitHub Desktop.
Decrypt Signal databases
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from Crypto.Cipher import AES | |
from struct import unpack | |
import os | |
import plistlib | |
from base64 import b64encode | |
signal_enc_db_name = 'signal.sqlite' | |
keychain_plist_path = 'keychain_decrypted.plist' | |
out_path = signal | |
#Extract key from keychain | |
#Accept plist file GK format or ios_keychain_decrypter plist file | |
with open(keychain_plist_path,'rb') as f : | |
plist = plistlib.load(f) | |
def getSignalKeyFromKeychain(): | |
for d in plist: | |
for dd in plist[d]: | |
if type(dd) == dict: | |
if 'acct' in dd: | |
if 'GRDBDatabaseCipherKeySpec' in str(dd['acct']) or str(b64encode(b'GRDBDatabaseCipherKeySpec')) in str(dd['acct']): | |
return dd['v_Data'] | |
return None | |
key = getSignalKeyFromKeychain() | |
os.makedirs(out_path, exist_ok = True) | |
#Decrypt Main DB | |
enc_db_size = os.path.getsize(signal_enc_db_name) | |
with open('signal.sqlite','rb') as tmp: | |
signal_header_size = 0x20 | |
default_page_size=0x1000 | |
page_size = default_page_size | |
header_size = signal_header_size | |
header = tmp.read(header_size) | |
salt_sz = 0x10 | |
hmac_sz = 0x40 | |
reserved_sz = salt_sz + hmac_sz | |
max_page = int(enc_db_size / default_page_size) | |
def decrypt_page(page_offset): | |
if page_offset == 0: | |
page_data = tmp.read(page_size - header_size) | |
else: | |
page_data = tmp.read(page_size) | |
iv = page_data[-reserved_sz:-reserved_sz+salt_sz] | |
decryption_suite = AES.new(key[:32], AES.MODE_CBC, iv) | |
plain_text = decryption_suite.decrypt(page_data[:-reserved_sz]) | |
return plain_text | |
with open(os.path.join(out_path,'signal-decrypted.sqlite'),'wb') as decrypted: | |
decrypted.write(header) | |
for page in range(0,max_page): | |
decrypted.write(decrypt_page(page)) | |
decrypted.write(b'\x00'*reserved_sz) | |
# Decrypt WAL File | |
enc_db_size = os.path.getsize(signal_enc_db_name+'-'+'wal') | |
with open(signal_enc_db_name+'-'+'wal','rb') as tmp: | |
wal_header_size = 32 | |
wal_page_header_size = 24 | |
signal_header_size = 0x20 | |
wal_header = tmp.read(wal_header_size) | |
page_size = unpack('>I',wal_header[8:12])[0] | |
salt_sz = 0x10 | |
hmac_sz = 0x40 | |
reserved_sz = salt_sz + hmac_sz | |
max_page = int((enc_db_size - wal_header_size) / (page_size + wal_page_header_size)) | |
def decrypt_page(page_offset): | |
page_header = tmp.read(wal_page_header_size) | |
page_number = unpack('>I',page_header[:4])[0] | |
plain_text = b'' | |
if page_number == 0: | |
plain_text += tmp.read(signal_header_size) | |
page_data = tmp.read(page_size - signal_header_size) | |
else: | |
page_data = tmp.read(page_size) | |
iv = page_data[-reserved_sz:-reserved_sz+salt_sz] | |
decryption_suite = AES.new(key[:32], AES.MODE_CBC, iv) | |
plain_text += decryption_suite.decrypt(page_data[:-reserved_sz]) | |
return plain_text | |
with open(os.path.join(out_path,'signal-decrypted-wal.sqlite'),'wb') as decrypted: | |
decrypted.write(header) | |
for page in range(0,max_page): | |
decrypted.write(decrypt_page(page)) | |
decrypted.write(b'\x00'*reserved_sz) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment