Skip to content

Instantly share code, notes, and snippets.

@drzraf
Last active March 21, 2025 03:05
Show Gist options
  • Save drzraf/0ceee4c979a9b7fd8faa3b5edd2c3b61 to your computer and use it in GitHub Desktop.
Save drzraf/0ceee4c979a9b7fd8faa3b5edd2c3b61 to your computer and use it in GitHub Desktop.
extract/decrypt signal-desktop (on mac/linux) sqlcipher database encrypted key
#!/usr/bin/php
<?php
/**
* extract/decrypt signal-desktop (on mac/linux) sqlcipher database encrypted key
* see https://github.com/signalapp/Signal-Desktop/issues/7005
* drzraf, 2025
*/
error_reporting(-1);
$errors = [];
$opts = getopt('p:k:');
$pwd = $opts['p'] ?? false; // password provided from the command-line (otherwise extracted using secret-tool)
$enckey = $opts['k'] ?? false; // key provided from the command-line (otherwise extracted from .config/Signal/config.json)
// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/os_crypt/sync/os_crypt_linux.cc#42
const SALT = 'saltysalt';
// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/os_crypt/sync/os_crypt_linux.cc#57
$iv = ' ';
if (! $pwd) {
$out;
exec('secret-tool lookup application Signal', $out);
$pwd = $out[0]; // base64-encoded, but don't decode it
if (! $pwd) {
// default password (?)
$pwd = '';
}
$derived_pwd = hash_pbkdf2('sha1', $pwd, SALT, 1, 16, true);
}
// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/os_crypt/sync/os_crypt_linux.cc#52
if (hash_pbkdf2('sha1', '', SALT, 1, 16, true) !== hex2bin('d0d0ec9c7d77d43ac54187fa4818d17f')) {
$errors[] = "PHP crypto configuration error";
}
if (! $enckey) {
$enckey = json_decode(file_get_contents(getenv('HOME') . '/.config/Signal/config.json'), true)['encryptedKey'];
}
// validation
$rawenckey = hex2bin($enckey);
if (strlen($rawenckey) != 83 )
$errors[] = "decoded enckey is " . strlen($rawenckey) . " characters rather than 83";
if (substr($rawenckey, 0, 3) != "v11")
$errors[] = "decoded enckey doesnt start with the v11 header";
if ($errors) {
print_r($errors);
die;
}
// skip the 3 bytes "v1x" header
$input = substr($rawenckey, 3);
// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/crypto/aes_cbc.cc#17
$mode = strlen($derived_pwd) == 16 ? 'aes-128-cbc' : 'aes-256-cbc';
//printf("full enc = $enckey %s\n", strlen($enckey));
printf("iv = '%s' [%d]\n", $iv, strlen($iv));
printf("input = '%s' [%d]\n", $input, strlen($input));
printf("pwd = '%s' [%d]\n", $pwd, strlen($pwd));
printf("pwd (der)= $derived_pwd [%d] (%s\n", strlen($derived_pwd), bin2hex($derived_pwd));
printf("mode = $mode\n");
$x = openssl_decrypt($input, $mode, $derived_pwd, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
// should match `signalbackup-tools --showdesktopkey`
print(PHP_EOL . "sqlcipher key = " . $x . PHP_EOL);
// None of these worked
// printf("$ openssl enc -d -$mode -base64 -iv %s -nosalt -K %s -in -<<<%s\n", bin2hex($iv), bin2hex($derived_pwd), bin2hex($input));
// printf("$ openssl enc -d -$mode -base64 -iv %s -salt -S %s -md sha1 -iter 1 -pbkdf2 -k %s -A -in -<<<%s\n", SALT, bin2hex($iv), bin2hex($pwd), bin2hex($input));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment