ropensci / cyphr

:shipit: Humane encryption
https://docs.ropensci.org/cyphr
Other
93 stars 10 forks source link

Deterministic encryption #56

Open krlmlr opened 5 months ago

krlmlr commented 5 months ago

Thank you for this package!

With the "data encryption" workflow in https://docs.ropensci.org/cyphr/articles/data.html, is there a way to ensure deterministic encryption (i.e., same input leads to same output)? Currently:

key <- cyphr::key_sodium(raw(32))
cyphr::encrypt_data(raw(3), key)
#>  [1] 17 79 70 f4 cd cf 44 04 75 92 51 af 15 43 d8 d9 41 5a ae af b1 48 1a 34 83
#> [26] a6 b0 d6 2e b3 55 58 d8 24 71 ec 14 87 c2 23 9e 4f b9
cyphr::encrypt_data(raw(3), key)
#>  [1] 6e 1d 2c f8 ca e4 51 3c 73 98 49 ab 68 c0 a9 72 ec bd 23 1d 45 9e e8 9f 4b
#> [26] 68 d4 74 fc b4 58 a4 61 b8 3a 4d 36 ec 74 53 49 c0 f9

Created on 2024-01-18 with reprex v2.0.2

richfitz commented 5 months ago

cyphr just wraps sodium and openssl. With the former we can have deterministic encryption for symmetric keys if you pass the nonce argument through with suitable random data. I don't think we can do this with public/private key-based methods though.

If this is important to you, then I'd be happy to review a PR that added it as an option that was fairly insensitive to the backend, but also reasonably secure by default.

I presume this is something where you want to hash the encrypted results to see if they're different or not? This is something we did briefly consider a few years ago. If you have a different use case I'd also be curious what it is.

krlmlr commented 5 months ago

Thanks. Yes, I wanted to encrypt a file and not change the file if the plaintext is unchanged.

If stability is hard, perhaps cyphr could offer a helper that safely decrypts to check plaintext equality before overwriting an existing file? This should be optional to avoid wasting cycles if the user knows that the plaintext is likely to have changed. For my use case, I already hand-rolled it.