bc_components/
public_keys.rs

1use bc_ur::prelude::*;
2
3use crate::{
4    Digest, EncapsulationPublicKey, Encrypter, Reference, ReferenceProvider,
5    Signature, SigningPublicKey, Verifier, tags,
6};
7
8/// A container for an entity's public cryptographic keys.
9///
10/// `PublicKeys` combines a verification key for checking digital signatures
11/// with an encapsulation key for encrypting messages, providing a complete
12/// public key package for secure communication with an entity.
13///
14/// This type is designed to be freely shared across networks and systems,
15/// allowing others to securely communicate with the key owner, who holds the
16/// corresponding `PrivateKeys` instance.
17///
18/// # Components
19///
20/// * `signing_public_key` - A public key used for verifying digital signatures.
21///   Can verify signatures created by the corresponding private key, which may
22///   be Schnorr, ECDSA, Ed25519, or SSH-based.
23///
24/// * `encapsulation_public_key` - A public key used for encrypting messages
25///   that can only be decrypted by the holder of the corresponding private key.
26///   Can be X25519 or ML-KEM based.
27///
28/// # Use Cases
29///
30/// * Verifying the authenticity of signed messages or content
31/// * Encrypting data for secure transmission to the key owner
32/// * Identity verification in distributed systems
33/// * Establishing secure communication channels
34///
35/// # Examples
36///
37/// ```
38/// use bc_components::{EncapsulationPublicKey, keypair};
39///
40/// // Generate a key pair
41/// let (private_keys, public_keys) = keypair();
42///
43/// // Get the encapsulation public key
44/// let enc_pub_key = public_keys.enapsulation_public_key();
45///
46/// // The public key can be used for key encapsulation
47/// // The resulting shared secret is only accessible to the
48/// // holder of the corresponding private key
49/// ```
50#[derive(Clone, PartialEq, Eq, Debug, Hash)]
51pub struct PublicKeys {
52    signing_public_key: SigningPublicKey,
53    encapsulation_public_key: EncapsulationPublicKey,
54}
55
56impl PublicKeys {
57    /// Restores a `PublicKeys` from a `SigningPublicKey` and an
58    /// `EncapsulationPublicKey`.
59    pub fn new(
60        signing_public_key: SigningPublicKey,
61        encapsulation_public_key: EncapsulationPublicKey,
62    ) -> Self {
63        Self { signing_public_key, encapsulation_public_key }
64    }
65
66    /// Returns the `SigningPublicKey` of this `PublicKeys`.
67    pub fn signing_public_key(&self) -> &SigningPublicKey {
68        &self.signing_public_key
69    }
70
71    /// Returns the `EncapsulationPublicKey` of this `PublicKeys`.
72    pub fn enapsulation_public_key(&self) -> &EncapsulationPublicKey {
73        &self.encapsulation_public_key
74    }
75}
76
77/// A trait for types that can provide a complete set of public cryptographic
78/// keys.
79///
80/// Types implementing this trait can be used as a source of `PublicKeys`,
81/// which contain both verification and encryption public keys. This trait is
82/// particularly useful for key management systems, wallets, identity systems,
83/// or any component that needs to provide public keys for cryptographic
84/// operations.
85///
86/// # Examples
87///
88/// ```ignore
89/// # // Requires secp256k1 feature (enabled by default)
90/// use bc_components::{PrivateKeyBase, PublicKeysProvider};
91///
92/// // Create a provider of public keys (in this case, a private key base
93/// // that can derive the corresponding public keys)
94/// let key_base = PrivateKeyBase::new();
95///
96/// // Get the public keys from the provider
97/// let public_keys = key_base.public_keys();
98///
99/// // These public keys can be shared with others for secure communication
100/// ```
101pub trait PublicKeysProvider {
102    /// Returns a complete set of public keys for cryptographic operations.
103    ///
104    /// The returned `PublicKeys` instance contains both verification and
105    /// encryption public keys that can be used by other parties to securely
106    /// communicate with the key owner.
107    ///
108    /// # Returns
109    ///
110    /// A `PublicKeys` instance containing the complete set of public keys.
111    fn public_keys(&self) -> PublicKeys;
112}
113
114impl PublicKeysProvider for PublicKeys {
115    fn public_keys(&self) -> PublicKeys { self.clone() }
116}
117
118impl ReferenceProvider for PublicKeys {
119    fn reference(&self) -> Reference {
120        Reference::from_digest(Digest::from_image(
121            self.tagged_cbor().to_cbor_data(),
122        ))
123    }
124}
125
126impl AsRef<PublicKeys> for PublicKeys {
127    fn as_ref(&self) -> &PublicKeys { self }
128}
129
130impl AsRef<SigningPublicKey> for PublicKeys {
131    fn as_ref(&self) -> &SigningPublicKey { &self.signing_public_key }
132}
133
134impl AsRef<EncapsulationPublicKey> for PublicKeys {
135    fn as_ref(&self) -> &EncapsulationPublicKey {
136        &self.encapsulation_public_key
137    }
138}
139
140impl CBORTagged for PublicKeys {
141    fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_PUBLIC_KEYS]) }
142}
143
144impl From<PublicKeys> for CBOR {
145    fn from(value: PublicKeys) -> Self { value.tagged_cbor() }
146}
147
148impl CBORTaggedEncodable for PublicKeys {
149    fn untagged_cbor(&self) -> CBOR {
150        #[cfg(any(
151            feature = "secp256k1",
152            feature = "ed25519",
153            feature = "ssh",
154            feature = "pqcrypto"
155        ))]
156        {
157            let _signing_key_cbor: CBOR =
158                self.signing_public_key.clone().into();
159            let _encapsulation_key_cbor: CBOR =
160                self.encapsulation_public_key.clone().into();
161            vec![_signing_key_cbor, _encapsulation_key_cbor].into()
162        }
163        #[cfg(not(any(
164            feature = "secp256k1",
165            feature = "ed25519",
166            feature = "ssh",
167            feature = "pqcrypto"
168        )))]
169        {
170            match self.signing_public_key {}
171        }
172    }
173}
174
175impl TryFrom<CBOR> for PublicKeys {
176    type Error = dcbor::Error;
177
178    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
179        Self::from_tagged_cbor(cbor)
180    }
181}
182
183impl CBORTaggedDecodable for PublicKeys {
184    fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
185        match untagged_cbor.as_case() {
186            CBORCase::Array(elements) => {
187                if elements.len() != 2 {
188                    return Err("PublicKeys must have two elements".into());
189                }
190
191                let signing_public_key =
192                    SigningPublicKey::try_from(elements[0].clone())?;
193                let encapsulation_public_key =
194                    EncapsulationPublicKey::try_from(elements[1].clone())?;
195                Ok(Self::new(signing_public_key, encapsulation_public_key))
196            }
197            _ => Err("PublicKeys must be an array".into()),
198        }
199    }
200}
201
202impl Verifier for PublicKeys {
203    fn verify(&self, signature: &Signature, message: &dyn AsRef<[u8]>) -> bool {
204        self.signing_public_key.verify(signature, message)
205    }
206}
207
208impl Encrypter for PublicKeys {
209    fn encapsulation_public_key(&self) -> EncapsulationPublicKey {
210        self.encapsulation_public_key.clone()
211    }
212}
213
214impl std::fmt::Display for PublicKeys {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        write!(
217            f,
218            "PublicKeys({}, {}, {})",
219            self.reference().ref_hex_short(),
220            self.signing_public_key,
221            self.encapsulation_public_key
222        )
223    }
224}
225
226#[cfg(test)]
227#[cfg(feature = "secp256k1")]
228mod tests {
229    use bc_ur::{URDecodable, UREncodable};
230    use dcbor::prelude::*;
231    use hex_literal::hex;
232
233    use crate::{
234        PrivateKeyBase, PublicKeys, PublicKeysProvider, ReferenceProvider,
235    };
236
237    const SEED: [u8; 16] = hex!("59f2293a5bce7d4de59e71b4207ac5d2");
238
239    #[test]
240    #[cfg(feature = "secp256k1")]
241    fn test_private_key_base() {
242        crate::register_tags();
243        let private_key_base = PrivateKeyBase::from_data(SEED);
244        let public_keys = private_key_base.public_keys();
245
246        let cbor = CBOR::from(public_keys.clone());
247
248        let public_keys_2 = PublicKeys::try_from(cbor.clone()).unwrap();
249        assert_eq!(public_keys, public_keys_2);
250
251        let cbor_2 = CBOR::from(public_keys_2);
252        assert_eq!(cbor, cbor_2);
253
254        let ur = public_keys.ur_string();
255        assert_eq!(
256            ur,
257            "ur:crypto-pubkeys/lftanshfhdcxzcgtcpytvsgafsondpjkbkoxaopsnniycawpnbnlwsgtregdfhgynyjksrgafmcstansgrhdcxlnfnwfzstovlrdfeuoghvwwyuesbcltsmetbgeurpfoyswfrzojlwdenjzckvadnrndtgsya"
258        );
259        assert_eq!(PublicKeys::from_ur_string(&ur).unwrap(), public_keys);
260
261        assert_eq!(
262            format!("{}", public_keys),
263            "PublicKeys(c9ede672, SigningPublicKey(7efa2ea1, SchnorrPublicKey(b4df96ce)), EncapsulationPublicKey(bacae62f, X25519PublicKey(bacae62f)))"
264        );
265        assert_eq!(
266            format!("{}", public_keys.reference()),
267            "Reference(c9ede672)"
268        );
269    }
270}