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)
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.