freedomofpress / securedrop

GitHub repository for the SecureDrop whistleblower platform. Do not submit tips here!
https://securedrop.org/
Other
3.63k stars 686 forks source link

Update to OpenPGP RFC 9580 / Sequoia 2.0 #7225

Open legoktm opened 3 months ago

legoktm commented 3 months ago

Description

OpenPGP has a new RFC dubbed "crypto refresh" that updates a lot of things to modern practices, there's a good summary at https://proton.me/blog/openpgp-crypto-refresh

Neal of Sequoia said that they'll be releasing 2.0 at the end of the year, which will include support for it. They also provided a link to https://www.openpgp.org/community/email-summit/2024/minutes/#mechanism-of-key-migration which discusses what migrations might look like.

Roughly as I understand it:

The elephant in the room is that we're still using GPG in places like the SecureDrop Workstation (https://github.com/freedomofpress/securedrop-workstation/issues/812) and Tails Workstation (no issue). Because of the schism, it seems likely that we need to fully leave GPG behind and embrace Sequoia/other OpenPGP-based tools.

legoktm commented 3 months ago

Neal of Sequoia said that they'll be releasing 2.0 at the end of the year

Forgot to mention that https://gitlab.com/sequoia-pgp/sequoia/-/tree/crypto-refresh exists, it was a small diff to get code to compile:

diff --git a/redwood/Cargo.toml b/redwood/Cargo.toml
index 33f77aeff..a53f69d36 100644
--- a/redwood/Cargo.toml
+++ b/redwood/Cargo.toml
@@ -11,7 +11,7 @@ crate-type = ["cdylib"]
 [dependencies]
 anyhow = "1.0"
 pyo3 = { version = "0.18.0", features = ["extension-module"] }
-sequoia-openpgp = { version = "1.21.1", default-features = false, features = ["crypto-openssl", "compression"]}
+sequoia-openpgp = { git = "https://gitlab.com/sequoia-pgp/sequoia/", branch = "crypto-refresh", default-features = false, features = ["crypto-openssl", "compression"]}
 thiserror = "1.0.31"

 [dev-dependencies]
diff --git a/redwood/src/decryption.rs b/redwood/src/decryption.rs
index 8175063e3..bfdbc165b 100644
--- a/redwood/src/decryption.rs
+++ b/redwood/src/decryption.rs
@@ -41,26 +41,28 @@ impl<'a> DecryptionHelper for Helper<'a> {
         mut decrypt: D,
     ) -> sequoia_openpgp::Result<Option<sequoia_openpgp::Fingerprint>>
     where
-        D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool,
+        D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool,
     {
         let key = secret_key_from_cert(self.secret)?;

         for pkesk in pkesks {
             // Note: this check won't work for messages encrypted with --throw-keyids,
             // but we don't generate any messages that use it.
-            if pkesk.recipient() == &key.keyid() {
-                // Decrypt the secret key with the specified passphrase.
-                let mut pair = key
-                    .clone()
-                    .decrypt_secret(self.passphrase)?
-                    .into_keypair()?;
-                pkesk
-                    .decrypt(&mut pair, sym_algo)
-                    .map(|(algo, session_key)| decrypt(algo, &session_key));
-                // Return the fingerprint of the key we decrypted with, this is used in
-                // conjunction with the intended recipient subpacket (see "Intended Recipient Fingerprint"
-                // in RFC 4880bis) to prevent Surreptitious Forwarding.
-                return Ok(Some(key.fingerprint()));
+            if let Some(recipient) = pkesk.recipient() {
+                if recipient == key.key_handle() {
+                    // Decrypt the secret key with the specified passphrase.
+                    let mut pair = key
+                        .clone()
+                        .decrypt_secret(self.passphrase)?
+                        .into_keypair()?;
+                    pkesk
+                        .decrypt(&mut pair, sym_algo)
+                        .map(|(algo, session_key)| decrypt(algo, &session_key));
+                    // Return the fingerprint of the key we decrypted with, this is used in
+                    // conjunction with the intended recipient subpacket (see "Intended Recipient Fingerprint"
+                    // in RFC 4880bis) to prevent Surreptitious Forwarding.
+                    return Ok(Some(key.fingerprint()));
+                }
             }
         }

Tests don't pass yet (maybe I mis-ported the key handle stuff):

test tests::test_multiple_recipients ... FAILED

---- tests::test_multiple_recipients stdout ----
/tmp/.tmpZauY30/message.asc
thread 'tests::test_multiple_recipients' panicked at redwood/src/lib.rs:376:18:
called `Result::unwrap()` on an `Err` value: OpenPgp(no matching pkesk, wrong secret key provided?)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I don't think we need to do anything on this now, we can pick this up once Sequoia 2.0 is closer.

cfm commented 3 months ago
  • New sources will get V6 key pairs, older sources will have V4 keys and we can generate V6 for them on login for future messages, but we need to retain the V4 key to decrypt older messages.

The elephant in the room is that we're still using GPG in places like the SecureDrop Workstation (freedomofpress/securedrop-workstation#812) and Tails Workstation (no issue). Because of the schism, it seems likely that we need to fully leave GPG behind and embrace Sequoia/other OpenPGP-based tools.

I think this is right. We should also try to make https://github.com/freedomofpress/securedrop-workstation-docs/blob/8734793c1d024c779c2ee65b26c64045db5c0e03/docs/general/known_issues.rst?plain=1#L48-L50 not true: i.e., support a set of keys both at Workstation provisioning and Client runtime.