Skip to content

Commit

Permalink
sqlcipher decryption (#146)
Browse files Browse the repository at this point in the history
* Add hint about using kwallet to retreive password for encrypted key

* Explain get_key function

* supply help for getting password depending on safeStorageBackend

possible values for safeStorageBackend are taken from https://www.electronjs.org/docs/latest/api/safe-storage

* Run password retreival for gnome and kde similarly to darwin

for this, make get_password more general and include error messages that are the same for all three systems in get_password

Code smell: error message in get_password is very similar to error message in the case of missing safeStorageBackend

---------

Co-authored-by: belamu <[email protected]>
  • Loading branch information
belamu and belamu authored Nov 4, 2024
1 parent 4dd484a commit d5d81a0
Showing 1 changed file with 67 additions and 8 deletions.
75 changes: 67 additions & 8 deletions sigexport/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,27 @@
from Crypto.Util.Padding import unpad
from typer import colors, secho

PASSWORD_CMD_DARWIN = ["security", "find-generic-password", "-ws", "Signal Safe Storage"]
PASSWORD_CMD_GNOME = ["secret-tool", "lookup", "application", "Signal"]
PASSWARD_CMD_KDE = ["kwallet-query", "kdewallet", "-f",
"Chromium Keys", "-r", "Chromium Safe Storage"]


def get_key(file: Path, password: Optional[str]) -> str:
"""Get key for decrypting database.
Retreives key depending on key encryption software.
If it cannot be decrypted, print an explanation message.
Args:
file: Signal config json file path
password: password that user could have supplied to decrypt key
Returns:
(decrypted) password
Raises:
non-specified exception if no decrypted password is available.
"""
with open(file, encoding="utf-8") as f:
data = json.loads(f.read())
if "key" in data:
Expand All @@ -28,25 +47,65 @@ def get_key(file: Path, password: Optional[str]) -> str:
fg=colors.RED,
)
if sys.platform == "darwin":
pw = get_password()
if password:
return decrypt(password, encrypyed_key, b"v10", 1003)
pw = get_password(PASSWORD_CMD_DARWIN, "macOS") # may raise error
return decrypt(pw, encrypyed_key, b"v10", 1003)
else:
else: # linux
if password:
return decrypt(password, encrypyed_key, b"v11", 1)
elif "safeStorageBackend" in data:
if data["safeStorageBackend"] == "gnome_libsecret":
pw = get_password(PASSWORD_CMD_GNOME, "gnome") # may raise error
pw = get_password(PASSWORD_CMD_KDE, "KDE") # may raise error
return decrypt(password, encrypyed_key, b"v11", 1)
elif data["safeStorageBackend"] in [
"gnome_libsecret", "kwallet", "kwallet5", "kwallet6"]:
pw = get_password(PASSWORD_CMD_KDE, "KDE") # may raise error
return decrypt(password, encrypyed_key, b"v11", 1)
else:
secho("Your Signal data key is encrypted, and requires a password.")
secho(f"The safe storage backend is {data['safeStorageBackend']}")
secho("If you know some Python and know how to retreive passwords "
"from this backend, please contribute a PR!")
else:
secho("Your Signal data key is encrypted, and requires a password.")
secho("On Gnome, you can try to get this with this command:")
secho("secret-tool lookup application Signal\n", fg=colors.BLUE)
secho("Then please rerun sigexport as follows:")
secho(f"No safe storage backend is specified.")
secho("On gnome, you can usually retreive the password with the command")
secho(" ".join(PASSWORD_CMD_GNOME) + "\n", fg=colors.BLUE)
secho("On KDE, you can usually retreive the password with the command")
secho(" ".join(PASSWORD_CMD_KDE) + "\n", fg=colors.BLUE)
secho("If you have found your password, please rerun sigexport as follows:")
secho("sigexport --password=PASSWORD_FROM_COMMAND ...", fg=colors.BLUE)
secho("No Signal decryption key found", fg=colors.RED)
else:
secho("No Signal decryption key found", fg=colors.RED)
raise


def get_password() -> str:
cmd = ["security", "find-generic-password", "-ws", "Signal Safe Storage"]
p = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8") # NoQA: S603
def get_password(cmd: list[str], system: str) -> Optional[str]:
"""Call external tool to get key password.
Args:
cmd: shell command as list of words
system: Name of the system we are on, for help message.
Returns:
password if found, None otherwise
Raises:
nondescript error: if no password was found
"""
p = subprocess.run( # NoQA: S603
cmd, capture_output=True, text=True, encoding="utf-8")
if p.returncode != 0:
secho("Your Signal data key is encrypted, and requires a password.")
secho(f"Usually on {system}, you can try to get it with this command:")
secho(" ".join(cmd) + "\n", fg=colors.BLUE)
secho("But this failed with errorcode "
f"{p.returncode} and error {p.stdout} {p.stderr}")
secho("If you have found your password, please rerun sigexport as follows:")
secho("sigexport --password=PASSWORD_FROM_COMMAND ...", fg=colors.BLUE)
secho("No Signal decryption key found", fg=colors.RED)
raise
pw = p.stdout
return pw.strip()

Expand Down

0 comments on commit d5d81a0

Please sign in to comment.