Search code examples
rjsonencryptionjsonlite

R: Encrypt string via cyphr, sending to JSON and then converting from JS back to string causes issue


I want to encrypt a string, and then format it to JSON, so I can send it to a database/the frontend safely. However, when retrieving encrypted data from the database, I need to go from JSON back to an R-string, which is an issue:

key <- cyphr::key_sodium(sha256(charToRaw("password123")))
encrypted <- cyphr::encrypt_string("my secret string", key)
encrypted_raw <- rawToChar(encrypted)
json_encrypted <- jsonlite::toJSON(encrypted_raw)
json_encrypted #This variable is then send to the database and retrieved again
json_decrypted <- jsonlite::fromJSON(json_encrypted)
decrypted_raw <- charToRaw(json_decrypted)
cyphr::decrypt_string(decrypted_raw, key)
#Expected output: "my secret string"
#Received output: "Error in decrypt(unpack(msg), key()) : Failed to decrypt"

It seems like it is a problem with the jsonlite package: If I remove this step, it works:

key <- cyphr::key_sodium(sha256(charToRaw("password123")))
encrypted <- cyphr::encrypt_string("my secret string", key)
encrypted_raw <- rawToChar(encrypted)
decrypted_raw <- charToRaw(encrypted_raw)
cyphr::decrypt_string(decrypted_raw, key)
#Expected output: "my secret string"
#Received output: "my secret string"

How can I encrypt a string, pass it to JSON, and then retrieve it from JSON and decrypt it afterwards? Thanks


Solution

  • jsonlite::toJSON() does accept raw input. By default it is encoded as base64. Thus, when reimporting it, you need to convert the base64 string to raw with base64enc::base64decode(). After that you should have no problem decoding your secret.

    library(jsonlite)
    library(sodium)
    library(cyphr)
    library(base64enc)
    
    key <- "password123" |> 
      charToRaw() |> 
      sha256() |> 
      key_sodium()
    
    sec <- "my secret string" |> 
      encrypt_string(key) 
    
    sec |> 
      toJSON() |> 
      fromJSON() |> 
      base64decode() |> 
      decrypt_string(key)
    #> [1] "my secret string"