bc_components/
private_keys.rs

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