Skip to main content

bc_xid/
key.rs

1use std::collections::HashSet;
2
3use bc_components::{
4    EncapsulationPublicKey, KeyDerivationMethod, PrivateKeys,
5    PrivateKeysProvider, PublicKeys, PublicKeysProvider, Reference,
6    ReferenceProvider, Salt, SigningPublicKey, URI, Verifier,
7};
8use bc_envelope::{PrivateKeyBase, prelude::*};
9use known_values::{ENDPOINT, NICKNAME, PRIVATE_KEY};
10
11use super::Permissions;
12use crate::{Error, HasNickname, HasPermissions, Privilege, Result};
13
14/// Private key data that can be either decrypted or encrypted.
15#[derive(Debug, Clone)]
16pub enum PrivateKeyData {
17    /// Decrypted private keys that can be used for signing/decryption.
18    Decrypted(PrivateKeys),
19
20    /// Encrypted private key envelope that cannot be used without decryption.
21    /// This preserves the encrypted assertion when a document is loaded
22    /// without the decryption password.
23    ///
24    /// Note: Envelope uses internal reference counting (Rc/Arc) so cloning
25    /// is cheap - no need for additional wrapper.
26    Encrypted(Envelope),
27}
28impl PartialEq for PrivateKeyData {
29    fn eq(&self, other: &Self) -> bool {
30        match (self, other) {
31            (Self::Decrypted(a), Self::Decrypted(b)) => a == b,
32            (Self::Encrypted(a), Self::Encrypted(b)) => {
33                // Compare envelopes by their UR string representation
34                a.ur_string() == b.ur_string()
35            }
36            _ => false,
37        }
38    }
39}
40
41impl Eq for PrivateKeyData {}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct Key {
45    public_keys: PublicKeys,
46    private_keys: Option<(PrivateKeyData, Salt)>,
47    nickname: String,
48    endpoints: HashSet<URI>,
49    permissions: Permissions,
50}
51impl Verifier for Key {
52    fn verify(
53        &self,
54        signature: &bc_components::Signature,
55        message: &dyn AsRef<[u8]>,
56    ) -> bool {
57        self.public_keys.verify(signature, message)
58    }
59}
60
61impl PublicKeysProvider for Key {
62    fn public_keys(&self) -> PublicKeys { self.public_keys.clone() }
63}
64
65impl std::hash::Hash for Key {
66    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
67        self.public_keys.hash(state);
68    }
69}
70
71impl Key {
72    pub fn new(public_keys: impl AsRef<PublicKeys>) -> Self {
73        Self {
74            public_keys: public_keys.as_ref().clone(),
75            private_keys: None,
76            nickname: String::new(),
77            endpoints: HashSet::new(),
78            permissions: Permissions::new(),
79        }
80    }
81
82    pub fn new_allow_all(public_keys: impl AsRef<PublicKeys>) -> Self {
83        Self {
84            public_keys: public_keys.as_ref().clone(),
85            private_keys: None,
86            nickname: String::new(),
87            endpoints: HashSet::new(),
88            permissions: Permissions::new_allow_all(),
89        }
90    }
91
92    pub fn new_with_private_keys(
93        private_keys: PrivateKeys,
94        public_keys: PublicKeys,
95    ) -> Self {
96        let salt = Salt::new_with_len(32).unwrap();
97        Self {
98            public_keys,
99            private_keys: Some((PrivateKeyData::Decrypted(private_keys), salt)),
100            nickname: String::new(),
101            endpoints: HashSet::new(),
102            permissions: Permissions::new_allow_all(),
103        }
104    }
105
106    pub fn new_with_private_key_base(private_key_base: PrivateKeyBase) -> Self {
107        let private_keys = private_key_base.private_keys();
108        let public_keys = private_key_base.public_keys();
109        Self::new_with_private_keys(private_keys, public_keys)
110    }
111
112    pub fn public_keys(&self) -> &PublicKeys { &self.public_keys }
113
114    pub fn private_keys(&self) -> Option<&PrivateKeys> {
115        self.private_keys.as_ref().and_then(|(data, _)| match data {
116            PrivateKeyData::Decrypted(keys) => Some(keys),
117            PrivateKeyData::Encrypted(_) => None,
118        })
119    }
120
121    pub fn has_private_keys(&self) -> bool {
122        matches!(
123            self.private_keys.as_ref(),
124            Some((PrivateKeyData::Decrypted(_), _))
125        )
126    }
127
128    pub fn has_encrypted_private_keys(&self) -> bool {
129        matches!(
130            self.private_keys.as_ref(),
131            Some((PrivateKeyData::Encrypted(_), _))
132        )
133    }
134
135    pub fn private_key_salt(&self) -> Option<&Salt> {
136        self.private_keys.as_ref().map(|(_, salt)| salt)
137    }
138
139    /// Extract the private key data as an Envelope, optionally decrypting it.
140    ///
141    /// # Returns
142    ///
143    /// - `Ok(None)` if no private key is present
144    /// - `Ok(Some(Envelope))` containing:
145    ///   - Decrypted `PrivateKeys` if unencrypted
146    ///   - Decrypted `PrivateKeys` if encrypted and correct password provided
147    ///   - Encrypted envelope if encrypted and no password provided
148    /// - `Err(...)` if encrypted and wrong password provided
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use bc_components::PrivateKeyBase;
154    /// use bc_envelope::prelude::*;
155    /// use bc_xid::Key;
156    ///
157    /// // Unencrypted key
158    /// let prvkey_base = PrivateKeyBase::new();
159    /// let key = Key::new_with_private_key_base(prvkey_base.clone());
160    /// let envelope = key.private_key_envelope(None).unwrap().unwrap();
161    /// // Returns envelope containing PrivateKeys
162    ///
163    /// // Encrypted key without password
164    /// // Returns the encrypted envelope as-is
165    ///
166    /// // Encrypted key with correct password
167    /// // Returns envelope containing decrypted PrivateKeys
168    /// ```
169    pub fn private_key_envelope(
170        &self,
171        password: Option<&str>,
172    ) -> Result<Option<Envelope>> {
173        match &self.private_keys {
174            None => Ok(None),
175            Some((PrivateKeyData::Decrypted(private_keys), _)) => {
176                // Unencrypted key - return as envelope
177                Ok(Some(Envelope::new(private_keys.clone())))
178            }
179            Some((PrivateKeyData::Encrypted(encrypted_envelope), _)) => {
180                if let Some(pwd) = password {
181                    // Try to decrypt with provided password
182                    match encrypted_envelope.clone().unlock_subject(pwd) {
183                        Ok(decrypted) => {
184                            // Successfully decrypted
185                            Ok(Some(decrypted))
186                        }
187                        Err(_) => {
188                            // Wrong password
189                            Err(Error::InvalidPassword)
190                        }
191                    }
192                } else {
193                    // No password provided, return encrypted envelope as-is
194                    Ok(Some(encrypted_envelope.clone()))
195                }
196            }
197        }
198    }
199
200    pub fn signing_public_key(&self) -> &SigningPublicKey {
201        self.public_keys.signing_public_key()
202    }
203
204    pub fn encapsulation_public_key(&self) -> &EncapsulationPublicKey {
205        self.public_keys.enapsulation_public_key()
206    }
207
208    pub fn endpoints(&self) -> &HashSet<URI> { &self.endpoints }
209
210    pub fn endpoints_mut(&mut self) -> &mut HashSet<URI> { &mut self.endpoints }
211
212    pub fn add_endpoint(&mut self, endpoint: URI) {
213        self.endpoints.insert(endpoint);
214    }
215
216    pub fn permissions(&self) -> &Permissions { &self.permissions }
217
218    pub fn permissions_mut(&mut self) -> &mut Permissions {
219        &mut self.permissions
220    }
221
222    pub fn add_permission(&mut self, privilege: Privilege) {
223        self.permissions.add_allow(privilege);
224    }
225}
226
227impl HasNickname for Key {
228    fn nickname(&self) -> &str { &self.nickname }
229
230    fn set_nickname(&mut self, nickname: impl Into<String>) {
231        self.nickname = nickname.into();
232    }
233}
234
235impl HasPermissions for Key {
236    fn permissions(&self) -> &Permissions { &self.permissions }
237
238    fn permissions_mut(&mut self) -> &mut Permissions { &mut self.permissions }
239}
240
241/// Options for handling private keys in envelopes.
242#[derive(Clone, Debug, PartialEq, Eq, Default)]
243pub enum XIDPrivateKeyOptions {
244    /// Omit the private key from the envelope (default).
245    #[default]
246    Omit,
247
248    /// Include the private key in plaintext (with salt for decorrelation).
249    Include,
250
251    /// Include the private key assertion but elide it (maintains digest tree).
252    Elide,
253
254    /// Include the private key encrypted with a password using the specified
255    /// key derivation method.
256    Encrypt {
257        method: KeyDerivationMethod,
258        password: Vec<u8>,
259    },
260}
261
262impl Key {
263    fn private_key_assertion_envelope(&self) -> Envelope {
264        let (private_key_data, salt) = self.private_keys.clone().unwrap();
265        match private_key_data {
266            PrivateKeyData::Decrypted(private_keys) => {
267                Envelope::new_assertion(PRIVATE_KEY, private_keys)
268                    .add_salt_instance(salt)
269            }
270            PrivateKeyData::Encrypted(encrypted_envelope) => {
271                // Already encrypted, just wrap with privateKey predicate and
272                // salt
273                Envelope::new_assertion(PRIVATE_KEY, encrypted_envelope)
274                    .add_salt_instance(salt)
275            }
276        }
277    }
278
279    fn extract_optional_private_key_with_password(
280        envelope: &Envelope,
281        password: Option<&[u8]>,
282    ) -> Result<Option<(PrivateKeyData, Salt)>> {
283        if let Some(private_key_assertion) =
284            envelope.optional_assertion_with_predicate(PRIVATE_KEY)?
285        {
286            let private_key_object =
287                private_key_assertion.subject().try_object()?;
288
289            // Extract the salt (always present)
290            let salt = private_key_assertion
291                .extract_object_for_predicate::<Salt>(known_values::SALT)?;
292
293            // Check if the private key object is locked with a password
294            if private_key_object.is_locked_with_password() {
295                // Need a password to decrypt
296                if let Some(pwd) = password {
297                    // Try to unlock with the password
298                    match private_key_object.unlock_subject(pwd) {
299                        Ok(decrypted) => {
300                            // Successfully decrypted, extract the private key
301                            let private_keys_cbor =
302                                decrypted.subject().try_leaf()?;
303                            let private_keys =
304                                PrivateKeys::try_from(private_keys_cbor)?;
305                            return Ok(Some((
306                                PrivateKeyData::Decrypted(private_keys),
307                                salt,
308                            )));
309                        }
310                        Err(_) => {
311                            // Wrong password or decryption failed
312                            // Store the encrypted envelope for later
313                            return Ok(Some((
314                                PrivateKeyData::Encrypted(
315                                    private_key_object.clone(),
316                                ),
317                                salt,
318                            )));
319                        }
320                    }
321                } else {
322                    // No password provided, store encrypted envelope
323                    return Ok(Some((
324                        PrivateKeyData::Encrypted(private_key_object.clone()),
325                        salt,
326                    )));
327                }
328            }
329
330            // Extract plaintext private key
331            let private_keys_cbor = private_key_object.try_leaf()?;
332            let private_keys = PrivateKeys::try_from(private_keys_cbor)?;
333            return Ok(Some((PrivateKeyData::Decrypted(private_keys), salt)));
334        }
335        Ok(None)
336    }
337
338    pub fn into_envelope_opt(
339        self,
340        private_key_options: XIDPrivateKeyOptions,
341    ) -> Envelope {
342        let mut envelope = Envelope::new(self.public_keys().clone());
343        if let Some((private_key_data, _)) = &self.private_keys {
344            match private_key_data {
345                PrivateKeyData::Encrypted(_) => {
346                    // Always preserve encrypted keys, regardless of options
347                    let assertion_envelope =
348                        self.private_key_assertion_envelope();
349                    envelope = envelope
350                        .add_assertion_envelope(assertion_envelope)
351                        .unwrap();
352                }
353                PrivateKeyData::Decrypted(_) => {
354                    // For decrypted keys, respect the private_key_options
355                    match private_key_options {
356                        XIDPrivateKeyOptions::Include => {
357                            let assertion_envelope =
358                                self.private_key_assertion_envelope();
359                            envelope = envelope
360                                .add_assertion_envelope(assertion_envelope)
361                                .unwrap();
362                        }
363                        XIDPrivateKeyOptions::Elide => {
364                            let assertion_envelope =
365                                self.private_key_assertion_envelope().elide();
366                            envelope = envelope
367                                .add_assertion_envelope(assertion_envelope)
368                                .unwrap();
369                        }
370                        XIDPrivateKeyOptions::Encrypt { method, password } => {
371                            let (private_keys, salt) =
372                                self.private_keys.clone().unwrap();
373
374                            match private_keys {
375                                PrivateKeyData::Decrypted(keys) => {
376                                    // Create an envelope with just the private
377                                    // keys
378                                    let private_keys_envelope =
379                                        Envelope::new(keys);
380
381                                    // Encrypt it using lock_subject
382                                    let encrypted = private_keys_envelope
383                                        .lock_subject(method, password)
384                                        .expect(
385                                            "Failed to encrypt private key",
386                                        );
387
388                                    // Create the privateKey assertion with the
389                                    // encrypted envelope
390                                    let assertion_envelope =
391                                        Envelope::new_assertion(
392                                            PRIVATE_KEY,
393                                            encrypted,
394                                        )
395                                        .add_salt_instance(salt);
396
397                                    envelope = envelope
398                                        .add_assertion_envelope(
399                                            assertion_envelope,
400                                        )
401                                        .unwrap();
402                                }
403                                PrivateKeyData::Encrypted(
404                                    encrypted_envelope,
405                                ) => {
406                                    // Already encrypted - we can't re-encrypt
407                                    // without
408                                    // decrypting first. Just preserve the
409                                    // existing
410                                    // encrypted envelope.
411                                    let assertion_envelope =
412                                        Envelope::new_assertion(
413                                            PRIVATE_KEY,
414                                            encrypted_envelope,
415                                        )
416                                        .add_salt_instance(salt);
417
418                                    envelope = envelope
419                                        .add_assertion_envelope(
420                                            assertion_envelope,
421                                        )
422                                        .unwrap();
423                                }
424                            }
425                        }
426                        XIDPrivateKeyOptions::Omit => {
427                            // Omit decrypted private keys
428                        }
429                    }
430                }
431            }
432        }
433
434        envelope = envelope.add_nonempty_string_assertion(
435            known_values::NICKNAME,
436            self.nickname,
437        );
438
439        envelope = self
440            .endpoints
441            .into_iter()
442            .fold(envelope, |envelope, endpoint| {
443                envelope.add_assertion(ENDPOINT, endpoint)
444            });
445
446        self.permissions.add_to_envelope(envelope)
447    }
448}
449
450impl EnvelopeEncodable for Key {
451    fn into_envelope(self) -> Envelope {
452        self.into_envelope_opt(XIDPrivateKeyOptions::Omit)
453    }
454}
455
456impl TryFrom<&Envelope> for Key {
457    type Error = Error;
458
459    fn try_from(envelope: &Envelope) -> Result<Self> {
460        Self::try_from_envelope(envelope, None)
461    }
462}
463
464impl TryFrom<Envelope> for Key {
465    type Error = Error;
466
467    fn try_from(envelope: Envelope) -> Result<Self> { Key::try_from(&envelope) }
468}
469
470impl Key {
471    /// Try to extract a `Key` from an envelope, optionally providing a
472    /// password to decrypt an encrypted private key.
473    ///
474    /// If the private key is encrypted and no password is provided, the `Key`
475    /// will be created without the private key (it will be `None`).
476    pub fn try_from_envelope(
477        envelope: &Envelope,
478        password: Option<&[u8]>,
479    ) -> Result<Self> {
480        let public_keys = PublicKeys::try_from(envelope.subject().try_leaf()?)?;
481        let private_keys = Key::extract_optional_private_key_with_password(
482            envelope, password,
483        )?;
484
485        let nickname = envelope.extract_object_for_predicate_with_default(
486            NICKNAME,
487            String::new(),
488        )?;
489
490        let mut endpoints = HashSet::new();
491        for assertion in envelope.assertions_with_predicate(ENDPOINT) {
492            let endpoint =
493                URI::try_from(assertion.try_object()?.subject().try_leaf()?)?;
494            endpoints.insert(endpoint);
495        }
496        let permissions = Permissions::try_from_envelope(envelope)?;
497        Ok(Self {
498            public_keys,
499            private_keys,
500            nickname,
501            endpoints,
502            permissions,
503        })
504    }
505}
506
507impl ReferenceProvider for &Key {
508    fn reference(&self) -> Reference { self.public_keys.reference() }
509}