bc_envelope/extension/
secret.rs

1use anyhow::{Result, bail};
2use bc_components::{EncryptedKey, KeyDerivationMethod, SymmetricKey};
3use known_values;
4
5use crate::{Envelope, Error};
6
7impl Envelope {
8    pub fn lock_subject(
9        &self,
10        method: KeyDerivationMethod,
11        secret: impl AsRef<[u8]>,
12    ) -> Result<Self> {
13        let content_key = SymmetricKey::new();
14        // Lock the content key using the specified derivation method
15        let encrypted_key = EncryptedKey::lock(method, secret, &content_key)?;
16        // Add a hasSecret assertion with the EncryptedKey
17        Ok(self
18            .encrypt_subject(&content_key)
19            .expect("Encrypt subject")
20            .add_assertion(known_values::HAS_SECRET, encrypted_key))
21    }
22
23    pub fn unlock_subject(&self, secret: impl AsRef<[u8]>) -> Result<Self> {
24        // Find and attempt to unlock each EncryptedKey in hasSecret assertions
25        for assertion in
26            self.assertions_with_predicate(known_values::HAS_SECRET)
27        {
28            let obj = assertion.as_object().unwrap();
29            if !obj.is_obscured() {
30                let encrypted_key = obj.extract_subject::<EncryptedKey>()?;
31                if let Ok(content_key) = encrypted_key.unlock(secret.as_ref()) {
32                    return self.decrypt_subject(&content_key);
33                }
34            }
35        }
36        // No matching secret unlock succeeded
37        bail!(Error::UnknownSecret)
38    }
39
40    pub fn is_locked_with_password(&self) -> bool {
41        // Check if the envelope has a hasSecret assertion with a password-based
42        // key derivation method
43        self.assertions_with_predicate(known_values::HAS_SECRET)
44            .iter()
45            .any(|assertion| {
46                let obj = assertion.as_object().unwrap();
47                if let Ok(encrypted_key) = obj.extract_subject::<EncryptedKey>()
48                {
49                    encrypted_key.is_password_based()
50                } else {
51                    false
52                }
53            })
54    }
55
56    pub fn is_locked_with_ssh_agent(&self) -> bool {
57        // Check if the envelope has a hasSecret assertion with an SSH agent
58        // key derivation method
59        self.assertions_with_predicate(known_values::HAS_SECRET)
60            .iter()
61            .any(|assertion| {
62                let obj = assertion.as_object().unwrap();
63                if let Ok(encrypted_key) = obj.extract_subject::<EncryptedKey>()
64                {
65                    encrypted_key.is_ssh_agent()
66                } else {
67                    false
68                }
69            })
70    }
71
72    pub fn add_secret(
73        &self,
74        method: KeyDerivationMethod,
75        secret: impl AsRef<[u8]>,
76        content_key: &SymmetricKey,
77    ) -> Result<Self> {
78        // Lock the content key using the specified derivation method
79        let encrypted_key = EncryptedKey::lock(method, secret, content_key)?;
80        // Add a hasSecret assertion with the EncryptedKey
81        Ok(self.add_assertion(known_values::HAS_SECRET, encrypted_key))
82    }
83}
84
85impl Envelope {
86    pub fn lock(
87        &self,
88        method: KeyDerivationMethod,
89        secret: impl AsRef<[u8]>,
90    ) -> Result<Self> {
91        self.wrap().lock_subject(method, secret)
92    }
93
94    pub fn unlock(&self, secret: impl AsRef<[u8]>) -> Result<Self> {
95        self.unlock_subject(secret)?.try_unwrap()
96    }
97}