Hello,
My name is Abe, and I'm one of the developers behind osvauld, an open-source credentials manager for developers (github http://github.com/osvauld || website http://osvauld.com). We're building our UI as a Chrome extension and utilizing WASM and Sequoia PGP for cryptographic operations (wasm repo https://github.com/osvauld/crypto_primitives). We'd greatly appreciate your expertise on a couple of key management challenges we're facing. Our current Setup: *Initial Key Generation:* When a user first logs in, they set up a passphrase. We use this passphrase to secure their certificate within the extension's local storage: ``` let (cert, _revocation) = CertBuilder::new() .add_userid("someone@example.org") .set_cipher_suite(CipherSuite::Cv25519) // This specifies ECC keys with Curve25519 .add_subkey(flags, None, None) .set_password(Some(passphrase)) .generate()?; ```
*Logging In:* Now when the user logs in the next time using the passphrase, passphrase and protected certificates are given to wasm it decrypts the secret key material and stores that in memory. ``` let cert = Cert::from_bytes(&private_key_bytes)?; let p = &StandardPolicy::new(); let keypair =cert.keys() .with_policy(p, None) .secret() .for_storage_encryption() .nth(0) .ok_or_else(|| "No suitable key found in Cert.")? .key() .clone() let password = Password::from(password); let decrypted_keypair = keypair.decrypt_secret(&password)?; ``` *Decryption: * When user requests for decryption ``` pub fn decrypt_message( p: &dyn Policy, sk: &Keyopenpgp::packet::key::SecretParts, openpgp::packet::key::UnspecifiedRole, ciphertext: &[u8], ) -> openpgp::Result<Vec<u8>> { let helper = Helper { secret: &sk, policy: p, }; let mut decryptor = DecryptorBuilder::from_bytes(ciphertext)?.with_policy(p, None, helper)?; let mut plaintext = Cursor::new(Vec::new()); std::io::copy(&mut decryptor, &mut plaintext)?; let plaintext = plaintext.into_inner(); Ok(plaintext) }
struct Helper<'a> { secret: &'a Keyopenpgp::packet::key::SecretParts, openpgp::packet::key::UnspecifiedRole, policy: &'a dyn Policy, }
impl<'a> openpgp::parse::stream::DecryptionHelper for Helper<'a> { fn decrypt<D>( &mut self, pkesks: &[openpgp::packet::PKESK], _skesks: &[openpgp::packet::SKESK], sym_algo: Optionopenpgp::types::SymmetricAlgorithm, mut decrypt: D, ) -> openpgp::Result<Optionopenpgp::Fingerprint> where D: FnMut(openpgp::types::SymmetricAlgorithm, &openpgp::crypto::SessionKey) -> bool, { // The secret key is already decrypted. let mut pair = KeyPair::from(self.secret.clone().into_keypair()?);
pkesks[0] .decrypt(&mut pair, sym_algo) .map(|(algo, session_key)| decrypt(algo, &session_key));
Ok(None) } } ``` We need your advice on handling these scenarios:
1. Certificate Export: - We would like to provide users with the ability to export their unprotected certificate. Could you please advise us on how this can be achieved using the Sequoia PGP library? 2. Password Change: - In case a user needs to change their password, how can we implement this functionality within our current setup? - If directly changing the password is not feasible, would it be safe to use (KDF) to encrypt the unprotected certificate with the new password? 3. Session Key Management and Extension Constraints: Due to the limitations imposed by Chrome extension manifest v3, we are unable to securely store decrypted private keys in memory for extended periods. To address this, we are considering a hybrid approach: - Encrypt the decrypted certificate (secret key material) with a session key and store the encrypted version locally. - Store the session key securely on our server and fetch it using a user-specific token when needed. Our questions regarding this approach are: - Does the Sequoia PGP library provide any built-in functionality or provisions that can assist us in implementing this session key management scheme? - Could you offer any suggestions or best practices on how to securely implement this approach within the Sequoia framework? - Are there any alternative solutions or architectures you would recommend for managing session keys in our specific use case?
Thank you for your time and for the tremendous work on Sequoia PGP!
Sincerely, Abe