bc_components/
private_keys.rs

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