bc_components/
private_keys.rs

1use anyhow::Result;
2use bc_ur::prelude::*;
3
4use crate::{
5    Decrypter, Digest, EncapsulationPrivateKey, Reference, ReferenceProvider,
6    Signature, Signer, SigningPrivateKey, tags,
7};
8
9/// A container for an entity's private cryptographic keys.
10///
11/// `PrivateKeys` combines a signing key for creating digital signatures with an
12/// encapsulation key for decrypting messages, providing a complete private key
13/// package for cryptographic operations.
14///
15/// This type is typically used in conjunction with its public counterpart,
16/// `PublicKeys`, to enable secure communication between entities. The private
17/// keys remain with the owner, while the corresponding public keys can be
18/// freely shared.
19///
20/// # Components
21///
22/// * `signing_private_key` - A private key used for creating digital
23///   signatures. Can be Schnorr, ECDSA, Ed25519, or SSH-based, depending on the
24///   security needs.
25///
26/// * `encapsulation_private_key` - A private key used for decrypting messages
27///   that were encrypted using the corresponding public key. Can be X25519 or
28///   ML-KEM based.
29///
30/// # Security
31///
32/// This struct contains highly sensitive cryptographic material and should be
33/// handled with appropriate security measures:
34///
35/// - Minimize serialization and storage of private keys
36/// - Ensure secure memory handling and proper zeroization
37/// - Apply access controls and encryption when at rest
38/// - Consider using hardware security modules for production systems
39///
40/// # Examples
41///
42/// ```
43/// use bc_components::{Signer, Verifier, keypair};
44///
45/// // Generate a new key pair with default schemes
46/// let (private_keys, public_keys) = keypair();
47///
48/// // Sign a message using the private keys
49/// let message = b"Hello, world!";
50/// let signature = private_keys.sign(message).unwrap();
51///
52/// // Verify the signature using the corresponding public keys
53/// assert!(public_keys.verify(&signature, message));
54/// ```
55#[derive(Debug, Clone, PartialEq, Eq, Hash)]
56pub struct PrivateKeys {
57    signing_private_key: SigningPrivateKey,
58    encapsulation_private_key: EncapsulationPrivateKey,
59}
60
61impl PrivateKeys {
62    /// Restores a `PrivateKeys` from a `SigningPrivateKey` and an
63    /// `EncapsulationPrivateKey`.
64    pub fn with_keys(
65        signing_private_key: SigningPrivateKey,
66        encapsulation_private_key: EncapsulationPrivateKey,
67    ) -> Self {
68        Self { signing_private_key, encapsulation_private_key }
69    }
70
71    /// Returns the `SigningPrivateKey` of this `PrivateKeys`.
72    pub fn signing_private_key(&self) -> &SigningPrivateKey {
73        &self.signing_private_key
74    }
75
76    /// Returns the `EncapsulationPrivateKey` of this `PrivateKeys`.
77    pub fn enapsulation_private_key(&self) -> &EncapsulationPrivateKey {
78        &self.encapsulation_private_key
79    }
80}
81
82/// A trait for types that can provide a complete set of private cryptographic
83/// keys.
84///
85/// Types implementing this trait can be used as a source of `PrivateKeys`,
86/// which contain both signing and encryption private keys. This trait is
87/// particularly useful for key management systems, wallets, or other components
88/// that need to generate or access cryptographic key material.
89///
90/// # Examples
91///
92/// ```
93/// use bc_components::{PrivateKeyBase, PrivateKeysProvider};
94///
95/// // Create a provider of private keys
96/// let key_base = PrivateKeyBase::new();
97///
98/// // Get the private keys from the provider
99/// let private_keys = key_base.private_keys();
100/// ```
101pub trait PrivateKeysProvider {
102    /// Returns a complete set of private keys for cryptographic operations.
103    ///
104    /// The returned `PrivateKeys` instance contains both signing and encryption
105    /// private keys that can be used for various cryptographic operations.
106    ///
107    /// # Returns
108    ///
109    /// A `PrivateKeys` instance containing the complete set of private keys.
110    fn private_keys(&self) -> PrivateKeys;
111}
112
113impl PrivateKeysProvider for PrivateKeys {
114    fn private_keys(&self) -> PrivateKeys { self.clone() }
115}
116
117impl ReferenceProvider for PrivateKeys {
118    fn reference(&self) -> Reference {
119        Reference::from_digest(Digest::from_image(
120            self.tagged_cbor().to_cbor_data(),
121        ))
122    }
123}
124
125impl AsRef<PrivateKeys> for PrivateKeys {
126    fn as_ref(&self) -> &PrivateKeys { self }
127}
128
129impl AsRef<SigningPrivateKey> for PrivateKeys {
130    fn as_ref(&self) -> &SigningPrivateKey { &self.signing_private_key }
131}
132
133impl AsRef<EncapsulationPrivateKey> for PrivateKeys {
134    fn as_ref(&self) -> &EncapsulationPrivateKey {
135        &self.encapsulation_private_key
136    }
137}
138
139impl CBORTagged for PrivateKeys {
140    fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_PRIVATE_KEYS]) }
141}
142
143impl From<PrivateKeys> for CBOR {
144    fn from(value: PrivateKeys) -> Self { value.tagged_cbor() }
145}
146
147impl CBORTaggedEncodable for PrivateKeys {
148    fn untagged_cbor(&self) -> CBOR {
149        let signing_key_cbor: CBOR = self.signing_private_key.clone().into();
150        let encapsulation_key_cbor: CBOR =
151            self.encapsulation_private_key.clone().into();
152        vec![signing_key_cbor, encapsulation_key_cbor].into()
153    }
154}
155
156impl TryFrom<CBOR> for PrivateKeys {
157    type Error = dcbor::Error;
158
159    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
160        Self::from_tagged_cbor(cbor)
161    }
162}
163
164impl CBORTaggedDecodable for PrivateKeys {
165    fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
166        match untagged_cbor.as_case() {
167            CBORCase::Array(elements) => {
168                if elements.len() != 2 {
169                    return Err("PrivateKeys must have two elements".into());
170                }
171
172                let signing_private_key =
173                    SigningPrivateKey::try_from(elements[0].clone())?;
174                let encapsulation_private_key =
175                    EncapsulationPrivateKey::try_from(elements[1].clone())?;
176                Ok(Self::with_keys(
177                    signing_private_key,
178                    encapsulation_private_key,
179                ))
180            }
181            _ => Err("PrivateKeys must be an array".into()),
182        }
183    }
184}
185
186impl Signer for PrivateKeys {
187    fn sign_with_options(
188        &self,
189        message: &dyn AsRef<[u8]>,
190        options: Option<crate::SigningOptions>,
191    ) -> Result<Signature> {
192        self.signing_private_key.sign_with_options(message, options)
193    }
194}
195
196impl Decrypter for PrivateKeys {
197    fn encapsulation_private_key(&self) -> EncapsulationPrivateKey {
198        self.encapsulation_private_key.clone()
199    }
200}
201
202impl std::fmt::Display for PrivateKeys {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        write!(f, "PrivateKeys({})", self.reference().ref_hex_short())
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use bc_ur::{URDecodable, UREncodable};
211    use dcbor::prelude::*;
212    use hex_literal::hex;
213
214    use crate::{
215        PrivateKeyBase, PrivateKeys, PrivateKeysProvider, ReferenceProvider,
216    };
217
218    const SEED: [u8; 16] = hex!("59f2293a5bce7d4de59e71b4207ac5d2");
219
220    #[test]
221    fn test_private_keys() {
222        crate::register_tags();
223
224        let private_key_base = PrivateKeyBase::from_data(SEED);
225        let private_keys = private_key_base.private_keys();
226
227        let cbor = CBOR::from(private_keys.clone());
228        println!("{}", cbor.diagnostic_annotated());
229
230        let private_keys_2 = PrivateKeys::try_from(cbor.clone()).unwrap();
231        assert_eq!(private_keys, private_keys_2);
232
233        let cbor_2 = CBOR::from(private_keys_2);
234        assert_eq!(cbor, cbor_2);
235
236        let ur = private_keys.ur_string();
237        assert_eq!(
238            ur,
239            "ur:crypto-prvkeys/lftansgohdcxmdahoxgepeethhvaeotkbadnssnnihsflokkfwbwryzoyasgwtfpgdrhssmhhehttansgehdcxktzmlslflpnbfzfsencspklkdygactnlykgmclrnbdmwgwgdrsqdjswkfrldjylpmtdpskfx"
240        );
241        assert_eq!(PrivateKeys::from_ur_string(&ur).unwrap(), private_keys);
242
243        assert_eq!(format!("{}", private_keys), "PrivateKeys(fa742ac8)");
244        assert_eq!(
245            format!("{}", private_keys.reference()),
246            "Reference(fa742ac8)"
247        );
248    }
249}