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/// ```
89/// use bc_components::{PrivateKeyBase, PublicKeysProvider};
90///
91/// // Create a provider of public keys (in this case, a private key base
92/// // that can derive the corresponding public keys)
93/// let key_base = PrivateKeyBase::new();
94///
95/// // Get the public keys from the provider
96/// let public_keys = key_base.public_keys();
97///
98/// // These public keys can be shared with others for secure communication
99/// ```
100pub trait PublicKeysProvider {
101    /// Returns a complete set of public keys for cryptographic operations.
102    ///
103    /// The returned `PublicKeys` instance contains both verification and
104    /// encryption public keys that can be used by other parties to securely
105    /// communicate with the key owner.
106    ///
107    /// # Returns
108    ///
109    /// A `PublicKeys` instance containing the complete set of public keys.
110    fn public_keys(&self) -> PublicKeys;
111}
112
113impl PublicKeysProvider for PublicKeys {
114    fn public_keys(&self) -> PublicKeys { self.clone() }
115}
116
117impl ReferenceProvider for PublicKeys {
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<PublicKeys> for PublicKeys {
126    fn as_ref(&self) -> &PublicKeys { self }
127}
128
129impl AsRef<SigningPublicKey> for PublicKeys {
130    fn as_ref(&self) -> &SigningPublicKey { &self.signing_public_key }
131}
132
133impl AsRef<EncapsulationPublicKey> for PublicKeys {
134    fn as_ref(&self) -> &EncapsulationPublicKey {
135        &self.encapsulation_public_key
136    }
137}
138
139impl CBORTagged for PublicKeys {
140    fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_PUBLIC_KEYS]) }
141}
142
143impl From<PublicKeys> for CBOR {
144    fn from(value: PublicKeys) -> Self { value.tagged_cbor() }
145}
146
147impl CBORTaggedEncodable for PublicKeys {
148    fn untagged_cbor(&self) -> CBOR {
149        let signing_key_cbor: CBOR = self.signing_public_key.clone().into();
150        let encapsulation_key_cbor: CBOR =
151            self.encapsulation_public_key.clone().into();
152        vec![signing_key_cbor, encapsulation_key_cbor].into()
153    }
154}
155
156impl TryFrom<CBOR> for PublicKeys {
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 PublicKeys {
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("PublicKeys must have two elements".into());
170                }
171
172                let signing_public_key =
173                    SigningPublicKey::try_from(elements[0].clone())?;
174                let encapsulation_public_key =
175                    EncapsulationPublicKey::try_from(elements[1].clone())?;
176                Ok(Self::new(signing_public_key, encapsulation_public_key))
177            }
178            _ => Err("PublicKeys must be an array".into()),
179        }
180    }
181}
182
183impl Verifier for PublicKeys {
184    fn verify(&self, signature: &Signature, message: &dyn AsRef<[u8]>) -> bool {
185        self.signing_public_key.verify(signature, message)
186    }
187}
188
189impl Encrypter for PublicKeys {
190    fn encapsulation_public_key(&self) -> EncapsulationPublicKey {
191        self.encapsulation_public_key.clone()
192    }
193}
194
195impl std::fmt::Display for PublicKeys {
196    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197        write!(f, "PublicKeys({})", self.reference().ref_hex_short())
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use bc_ur::{URDecodable, UREncodable};
204    use dcbor::prelude::*;
205    use hex_literal::hex;
206
207    use crate::{
208        PrivateKeyBase, PublicKeys, PublicKeysProvider, ReferenceProvider,
209    };
210
211    const SEED: [u8; 16] = hex!("59f2293a5bce7d4de59e71b4207ac5d2");
212
213    #[test]
214    fn test_private_key_base() {
215        crate::register_tags();
216        let private_key_base = PrivateKeyBase::from_data(SEED);
217        let public_keys = private_key_base.public_keys();
218
219        let cbor = CBOR::from(public_keys.clone());
220
221        let public_keys_2 = PublicKeys::try_from(cbor.clone()).unwrap();
222        assert_eq!(public_keys, public_keys_2);
223
224        let cbor_2 = CBOR::from(public_keys_2);
225        assert_eq!(cbor, cbor_2);
226
227        let ur = public_keys.ur_string();
228        assert_eq!(
229            ur,
230            "ur:crypto-pubkeys/lftanshfhdcxzcgtcpytvsgafsondpjkbkoxaopsnniycawpnbnlwsgtregdfhgynyjksrgafmcstansgrhdcxlnfnwfzstovlrdfeuoghvwwyuesbcltsmetbgeurpfoyswfrzojlwdenjzckvadnrndtgsya"
231        );
232        assert_eq!(PublicKeys::from_ur_string(&ur).unwrap(), public_keys);
233
234        assert_eq!(format!("{}", public_keys), "PublicKeys(c9ede672)");
235        assert_eq!(
236            format!("{}", public_keys.reference()),
237            "Reference(c9ede672)"
238        );
239    }
240}