Search code examples
pythonrencryption

How to decrypt a key encrypted using the sodium R package in python?


I have he following R functions that use the sodium R package to encrypt and decrypt an access key with a password:

encrypt_access_key <- function(access_key, password) {
  key <- sha256(charToRaw(password))
  nonce <- random(24)  # Generate a random nonce
  encrypted <- data_encrypt(charToRaw(access_key), key, nonce)
  list(
    encrypted_access_key = base64encode(encrypted),
    nonce = base64encode(nonce)
  )
}

decrypt_access_key <- function(encrypted_access_key, nonce, password) {
  key <- sha256(charToRaw(password))
  encrypted_raw <- base64decode(encrypted_access_key)
  nonce_raw <- base64decode(nonce)
  rawToChar(data_decrypt(encrypted_raw, key, nonce_raw))
} 

I want to create a python function that mimics the functionality of the decrypt_access_key function, but I have not been able to do so... In case it is useful, here is the documentation for the data_encrypt and data_decrypt functions from the sodium package: link

Edit: Here is a reproducible example

encripted_message <- encrypt_access_key("This is a hidden message", "Example123")

decrypt_access_key(encripted_message$encrypted_access_key, encripted_message$nonce, "Example123")

There is no need to replicate the functionality of encrypt_access_key, all I need is a python function that can get the string stored as encripted_message$encrypted_access_key and decrypt the message

Edit 2: Here is the reproductible example with the explicit value of the nonce and the encrypted message

decrypt_access_key(encrypted_access_key = "QG42J4fSVcY9luHaXiEhQcR85isjgUlmFc97pMHIIwuxQyZzDNpz2w==", nonce = "LpO7fBaCs1iobxDxVmOPFycTe/BCifqh", password = "Example123")

And here is one of the failed attempts to decrypt the message using python:

import base64
from Crypto.Cipher import ChaCha20_Poly1305
from Crypto.Protocol.KDF import PBKDF2
import hashlib

def decrypt_access_key(encrypted_access_key, nonce, password):
    # Decode the base64 encoded components
    encrypted_access_key_bytes = base64.b64decode(encrypted_access_key)
    nonce_bytes = base64.b64decode(nonce)
    
    # Derive the key using PBKDF2
    key = hashlib.pbkdf2_hmac('sha256', password.encode(), b'', 100000)
    
    # Decrypt the access key using ChaCha20-Poly1305
    cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce_bytes)
    decrypted = cipher.decrypt(encrypted_access_key_bytes)
    
    return decrypted.decode('utf-8')

# Example usage
encrypted_access_key = 'QG42J4fSVcY9luHaXiEhQcR85isjgUlmFc97pMHIIwuxQyZzDNpz2w=='
nonce = 'LpO7fBaCs1iobxDxVmOPFycTe/BCifqh'
password = 'Example123'

decrypted_access_key = decrypt_access_key(encrypted_access_key, nonce, password)
print(decrypted_access_key)

Solution

  • The R code for encryption derives the key from a password using SHA256 and performs authenticated encryption with SecretBox, which uses XSalsa20 and Poly1305 under the hood, see here. The result returns the (24 bytes) nonce and the concatenation of ciphertext and (16 bytes) tag separately.

    The posted Python code is not compatible with SecretBox.

    A possible NaCl/Libsodium Python port that supports SecretBox is PyNaCl. In contrast to the R library, PyNaCl returns the concatenation of nonce, ciphertext and tag as the result of the encryption, and requires the concatenation of nonce, ciphertext and tag during decryption:

    import nacl.secret
    import hashlib
    import base64
    
    key = hashlib.sha256(b'Example123').digest()
    box = nacl.secret.SecretBox(key)
    
    nonceB64 = 'LpO7fBaCs1iobxDxVmOPFycTe/BCifqh'
    ciphertextTagB64 = 'QG42J4fSVcY9luHaXiEhQcR85isjgUlmFc97pMHIIwuxQyZzDNpz2w=='
    
    nonceCiphertextTag = base64.b64decode(nonceB64) + base64.b64decode(ciphertextTagB64)
    decrypted = box.decrypt(nonceCiphertextTag)
    print(decrypted.decode('utf-8')) # This is a hidden message
    

    Note that the use of a fast digest such as SHA256 is a vulnerability. It is more secure to use a reliable key derivation function, at least PBKDF2, or even better the more modern algorithms mentioned in the Libsodium documentation (Argon2, Scrypt), see here.