| // Copyright 2023 Google LLC | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | //! Credential types used in deserialization. | 
 | //! | 
 | //! While simple implementations are provided to get started with, there is likely opportunity for | 
 | //! efficiency gains with implementations tailored to suit (e.g. caching a few hot credentials | 
 | //! rather than reading from disk every time, etc). | 
 |  | 
 | use crate::MetadataKey; | 
 |  | 
 | use crate::credential::v0::{V0DiscoveryCredential, V0ProtocolVersion}; | 
 |  | 
 | use core::convert::Infallible; | 
 | use core::fmt::Debug; | 
 | use crypto_provider::{CryptoProvider, CryptoRng}; | 
 |  | 
 | pub mod book; | 
 | pub mod source; | 
 | #[cfg(test)] | 
 | pub mod tests; | 
 | pub mod v0; | 
 | pub mod v1; | 
 |  | 
 | /// Information about a credential as supplied by the caller. | 
 | #[derive(Clone)] | 
 | pub struct MatchableCredential<V: ProtocolVersion, M: MatchedCredential> { | 
 |     /// The discovery credential/cryptographic information associated | 
 |     /// with this particular credential which is used for discovering | 
 |     /// advertisements/advertisement sections generated via the | 
 |     /// paired sender credential. | 
 |     pub discovery_credential: V::DiscoveryCredential, | 
 |     /// The data which will be yielded back to the caller upon a successful | 
 |     /// identity-match against this credential. | 
 |     pub match_data: M, | 
 | } | 
 |  | 
 | impl<V: ProtocolVersion, M: MatchedCredential> MatchableCredential<V, M> { | 
 |     /// De-structures this credential into the pairing of a discovery | 
 |     /// credential and some matched credential data. | 
 |     pub fn into_pair(self) -> (V::DiscoveryCredential, M) { | 
 |         (self.discovery_credential, self.match_data) | 
 |     } | 
 |     /// Views this credential as a (borrowed) discovery-credential | 
 |     /// combined with some matched credential data | 
 |     /// (which is copied - see documentation on [`MatchedCredential`]) | 
 |     pub fn as_pair(&self) -> (&V::DiscoveryCredential, ReferencedMatchedCredential<M>) { | 
 |         (&self.discovery_credential, ReferencedMatchedCredential::from(&self.match_data)) | 
 |     } | 
 | } | 
 |  | 
 | /// The portion of a credential's data to be bundled with the advertisement content it was used to | 
 | /// decrypt. At a minimum, this includes any encrypted identity-specific metadata. | 
 | /// | 
 | /// As it is `Debug` and `Eq`, implementors should not hold any cryptographic secrets to avoid | 
 | /// accidental logging, timing side channels on comparison, etc, or should use custom impls of | 
 | /// those traits rather than deriving them. | 
 | /// | 
 | /// Instances of `MatchedCredential` may be cloned whenever advertisement content is | 
 | /// successfully associated with a credential (see [`crate::WithMatchedCredential`]). As a | 
 | /// result, it's recommended to use matched-credentials which reference | 
 | /// some underlying match-data, but don't necessarily own it. | 
 | /// See [`ReferencedMatchedCredential`] for the most common case of shared references. | 
 | pub trait MatchedCredential: Debug + PartialEq + Eq + Clone { | 
 |     /// The type returned for successful calls to [`Self::fetch_encrypted_metadata`]. | 
 |     type EncryptedMetadata: AsRef<[u8]>; | 
 |  | 
 |     /// The type of errors for [`Self::fetch_encrypted_metadata`]. | 
 |     type EncryptedMetadataFetchError: Debug; | 
 |  | 
 |     /// Attempts to obtain the (AES-GCM)-encrypted metadata bytes for the credential, | 
 |     /// with possible failure based on the availability of the underlying data (i.e: | 
 |     /// failing disk reads.) | 
 |     /// | 
 |     /// If your implementation does not maintain any encrypted metadata for each credential, | 
 |     /// you may simply return an empty byte-array from this method. | 
 |     /// | 
 |     /// If your method for obtaining metadata cannot fail, use | 
 |     /// the `core::convert::Infallible` type for the error type | 
 |     /// [`Self::EncryptedMetadataFetchError`]. | 
 |     fn fetch_encrypted_metadata( | 
 |         &self, | 
 |     ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError>; | 
 | } | 
 |  | 
 | /// [`MatchedCredential`] wrapper around a shared reference to a [`MatchedCredential`]. | 
 | /// This is done instead of providing a blanket impl of [`MatchedCredential`] for | 
 | /// reference types to allow for downstream crates to impl [`MatchedCredential`] on | 
 | /// specific reference types. | 
 | #[derive(Clone, Debug, PartialEq, Eq)] | 
 | pub struct ReferencedMatchedCredential<'a, M: MatchedCredential> { | 
 |     wrapped: &'a M, | 
 | } | 
 |  | 
 | impl<'a, M: MatchedCredential> From<&'a M> for ReferencedMatchedCredential<'a, M> { | 
 |     fn from(wrapped: &'a M) -> Self { | 
 |         Self { wrapped } | 
 |     } | 
 | } | 
 |  | 
 | impl<'a, M: MatchedCredential> AsRef<M> for ReferencedMatchedCredential<'a, M> { | 
 |     fn as_ref(&self) -> &M { | 
 |         self.wrapped | 
 |     } | 
 | } | 
 |  | 
 | impl<'a, M: MatchedCredential> MatchedCredential for ReferencedMatchedCredential<'a, M> { | 
 |     type EncryptedMetadata = <M as MatchedCredential>::EncryptedMetadata; | 
 |     type EncryptedMetadataFetchError = <M as MatchedCredential>::EncryptedMetadataFetchError; | 
 |     fn fetch_encrypted_metadata( | 
 |         &self, | 
 |     ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> { | 
 |         self.wrapped.fetch_encrypted_metadata() | 
 |     } | 
 | } | 
 |  | 
 | /// A simple implementation of [`MatchedCredential`] where all match-data | 
 | /// is contained in the encrypted metadata byte-field. | 
 | #[derive(Debug, PartialEq, Eq, Clone)] | 
 | pub struct MetadataMatchedCredential<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> { | 
 |     encrypted_metadata: A, | 
 | } | 
 |  | 
 | #[cfg(any(test, feature = "alloc"))] | 
 | impl MetadataMatchedCredential<alloc::vec::Vec<u8>> { | 
 |     /// Builds a [`MetadataMatchedCredential`] whose contents are given | 
 |     /// as plaintext to be encrypted using AES-GCM against the given | 
 |     /// broadcast crypto-material. | 
 |     pub fn encrypt_from_plaintext<V, B, C>(broadcast_cm: &B, plaintext_metadata: &[u8]) -> Self | 
 |     where | 
 |         V: ProtocolVersion, | 
 |         B: BroadcastCryptoMaterial<V>, | 
 |         C: CryptoProvider, | 
 |     { | 
 |         let encrypted_metadata = broadcast_cm.encrypt_metadata::<C>(plaintext_metadata); | 
 |         Self { encrypted_metadata } | 
 |     } | 
 | } | 
 |  | 
 | impl<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> MetadataMatchedCredential<A> { | 
 |     /// Builds a new [`MetadataMatchedCredential`] with the given | 
 |     /// encrypted metadata. | 
 |     pub fn new(encrypted_metadata: A) -> Self { | 
 |         Self { encrypted_metadata } | 
 |     } | 
 | } | 
 |  | 
 | impl<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> MatchedCredential | 
 |     for MetadataMatchedCredential<A> | 
 | { | 
 |     type EncryptedMetadata = A; | 
 |     type EncryptedMetadataFetchError = Infallible; | 
 |     fn fetch_encrypted_metadata(&self) -> Result<Self::EncryptedMetadata, Infallible> { | 
 |         Ok(self.encrypted_metadata.clone()) | 
 |     } | 
 | } | 
 |  | 
 | /// Trivial implementation of [`MatchedCredential`] which consists of no match-data. | 
 | /// Suitable for usage scenarios where the decoded advertisement contents matter, | 
 | /// but not necessarily which devices generated the contents. | 
 | /// | 
 | /// Attempting to obtain the encrypted metadata from this type of credential | 
 | /// will always yield an empty byte-array. | 
 | #[derive(Default, Debug, PartialEq, Eq, Clone)] | 
 | pub struct EmptyMatchedCredential; | 
 |  | 
 | impl MatchedCredential for EmptyMatchedCredential { | 
 |     type EncryptedMetadata = [u8; 0]; | 
 |     type EncryptedMetadataFetchError = Infallible; | 
 |     fn fetch_encrypted_metadata( | 
 |         &self, | 
 |     ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> { | 
 |         Ok([0u8; 0]) | 
 |     } | 
 | } | 
 |  | 
 | #[cfg(any(test, feature = "devtools"))] | 
 | /// A [`MatchedCredential`] which consists only of the `key_seed` in the crypto-material | 
 | /// for the credential. Note that this is unique per-credential by construction, | 
 | /// and so this provides natural match-data for credentials in settings where | 
 | /// there may not be any other information available. | 
 | /// | 
 | /// Since this matched-credential type contains cryptographic information mirroring | 
 | /// a credential's crypto-material, this structure is not suitable for production | 
 | /// usage outside of unit tests and dev-tools. | 
 | /// | 
 | /// Additionally, note that the metadata on this particular kind of matched credential | 
 | /// is deliberately made inaccessible. This is done because a key-seed representation | 
 | /// is only suitable in very limited circumstances where no other meaningful | 
 | /// identifying information is available, such as that which is contained in metadata. | 
 | /// Attempting to obtain the encrypted metadata from this type of matched credential | 
 | /// will always yield an empty byte-array. | 
 | #[derive(Default, Debug, PartialEq, Eq, Clone)] | 
 | pub struct KeySeedMatchedCredential { | 
 |     key_seed: [u8; 32], | 
 | } | 
 |  | 
 | #[cfg(any(test, feature = "devtools"))] | 
 | impl From<[u8; 32]> for KeySeedMatchedCredential { | 
 |     fn from(key_seed: [u8; 32]) -> Self { | 
 |         Self { key_seed } | 
 |     } | 
 | } | 
 | #[cfg(any(test, feature = "devtools"))] | 
 | impl From<KeySeedMatchedCredential> for [u8; 32] { | 
 |     fn from(matched: KeySeedMatchedCredential) -> Self { | 
 |         matched.key_seed | 
 |     } | 
 | } | 
 |  | 
 | #[cfg(any(test, feature = "devtools"))] | 
 | impl MatchedCredential for KeySeedMatchedCredential { | 
 |     type EncryptedMetadata = [u8; 0]; | 
 |     type EncryptedMetadataFetchError = Infallible; | 
 |     fn fetch_encrypted_metadata( | 
 |         &self, | 
 |     ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> { | 
 |         Ok([0u8; 0]) | 
 |     } | 
 | } | 
 |  | 
 | /// Error returned when metadata decryption fails. | 
 | #[derive(Debug, Eq, PartialEq)] | 
 | pub struct MetadataDecryptionError; | 
 |  | 
 | /// Seal for protocol versions to enforce totality. | 
 | pub(crate) mod protocol_version_seal { | 
 |     /// Internal-only supertrait of protocol versions | 
 |     /// for the purpose of sealing the trait. | 
 |     pub trait ProtocolVersionSeal: Clone {} | 
 | } | 
 |  | 
 | /// Marker trait for protocol versions (V0/V1) | 
 | /// and associated data about them. | 
 | pub trait ProtocolVersion: protocol_version_seal::ProtocolVersionSeal { | 
 |     /// The discovery credential type for this protocol version, which | 
 |     /// is the minimal amount of cryptographic materials that we need | 
 |     /// in order to discover advertisements/sections which make | 
 |     /// use of the sender-paired version of the credential. | 
 |     type DiscoveryCredential: DiscoveryCryptoMaterial<Self> + Clone; | 
 |  | 
 |     /// The native-length metadata key for this protocol version | 
 |     /// [i.e: if V0, a 14-byte metadata key, or if V1, a 16-byte | 
 |     /// metadata key.] | 
 |     type MetadataKey: Clone + AsRef<[u8]>; | 
 |  | 
 |     /// Computes the metadata nonce for this version from the given key-seed. | 
 |     fn metadata_nonce_from_key_seed<C: CryptoProvider>(key_seed: &[u8; 32]) -> [u8; 12]; | 
 |  | 
 |     /// Expands the passed metadata key (if needed) to a 16-byte metadata key | 
 |     /// which may be used for metadata encryption/decryption | 
 |     fn expand_metadata_key<C: CryptoProvider>(metadata_key: Self::MetadataKey) -> MetadataKey; | 
 |  | 
 |     /// Generates a random metadata key using the given cryptographically-secure Rng | 
 |     fn gen_random_metadata_key<R: CryptoRng>(rng: &mut R) -> Self::MetadataKey; | 
 |  | 
 |     #[cfg(any(test, feature = "alloc"))] | 
 |     /// Decrypt the given metadata using the given metadata nonce and version-specific | 
 |     /// metadata key. Returns [`MetadataDecryptionError`] in the case that | 
 |     /// the decryption operation failed. | 
 |     fn decrypt_metadata<C: CryptoProvider>( | 
 |         metadata_nonce: [u8; 12], | 
 |         metadata_key: Self::MetadataKey, | 
 |         encrypted_metadata: &[u8], | 
 |     ) -> Result<alloc::vec::Vec<u8>, MetadataDecryptionError> { | 
 |         use crypto_provider::{ | 
 |             aead::{Aead, AeadInit}, | 
 |             aes::Aes128Key, | 
 |         }; | 
 |  | 
 |         let metadata_key = Self::expand_metadata_key::<C>(metadata_key); | 
 |         let metadata_key = Aes128Key::from(metadata_key.0); | 
 |         let aead = <<C as CryptoProvider>::Aes128Gcm as AeadInit<Aes128Key>>::new(&metadata_key); | 
 |         // No additional authenticated data for encrypted metadata. | 
 |         aead.decrypt(encrypted_metadata, &[], &metadata_nonce).map_err(|_| MetadataDecryptionError) | 
 |     } | 
 | } | 
 |  | 
 | /// Trait for structures which provide cryptographic | 
 | /// materials for discovery in a particular protocol version. | 
 | /// See [`crate::credential::v0::V0DiscoveryCryptoMaterial`] | 
 | /// and [`crate::credential::v1::V1DiscoveryCryptoMaterial`] | 
 | /// for V0 and V1 specializations. | 
 | pub trait DiscoveryCryptoMaterial<V: ProtocolVersion> { | 
 |     /// Constructs or copies the metadata nonce used for decryption of associated credential | 
 |     /// metadata for the identity represented via this crypto material. | 
 |     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]; | 
 | } | 
 |  | 
 | /// Cryptographic materials necessary for broadcasting encrypted | 
 | /// advertisement contents with the given protocol version. | 
 | pub trait BroadcastCryptoMaterial<V: ProtocolVersion> { | 
 |     /// Yields a copy of the key seed to be used to derive other key materials used | 
 |     /// in the encryption of broadcasted advertisement contents. | 
 |     fn key_seed(&self) -> [u8; 32]; | 
 |  | 
 |     /// Yields a copy of the metadata-key (size dependent on protocol version) | 
 |     /// to tag advertisement contents sent with this broadcast crypto-material. | 
 |     fn metadata_key(&self) -> V::MetadataKey; | 
 |  | 
 |     /// Yields the 16-byte expanded metadata key, suitable for metadata encryption. | 
 |     fn expanded_metadata_key<C: CryptoProvider>(&self) -> MetadataKey { | 
 |         V::expand_metadata_key::<C>(self.metadata_key()) | 
 |     } | 
 |  | 
 |     /// Constructs the metadata nonce used for encryption of associated credential | 
 |     /// metadata for the identity represented via this crypto material. | 
 |     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] { | 
 |         V::metadata_nonce_from_key_seed::<C>(&self.key_seed()) | 
 |     } | 
 |  | 
 |     /// Derives a V0 discovery credential from this V0 broadcast crypto-material | 
 |     /// which may be used to discover v0 advertisements broadcasted with this credential.` | 
 |     fn derive_v0_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential | 
 |     where | 
 |         V: V0ProtocolVersion, | 
 |     { | 
 |         let key_seed = self.key_seed(); | 
 |         let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed); | 
 |         let metadata_key_hmac = | 
 |             hkdf.legacy_metadata_key_hmac_key().calculate_hmac(self.metadata_key().as_ref()); | 
 |         V0DiscoveryCredential::new(key_seed, metadata_key_hmac) | 
 |     } | 
 |  | 
 |     #[cfg(any(test, feature = "alloc"))] | 
 |     /// Encrypts the given plaintext metadata bytes to allow that metadata | 
 |     /// to be shared with receiving devices. | 
 |     fn encrypt_metadata<C: CryptoProvider>( | 
 |         &self, | 
 |         plaintext_metadata: &[u8], | 
 |     ) -> alloc::vec::Vec<u8> { | 
 |         use crypto_provider::{ | 
 |             aead::{Aead, AeadInit}, | 
 |             aes::Aes128Key, | 
 |         }; | 
 |         let plaintext_metadata_key = self.expanded_metadata_key::<C>(); | 
 |         let plaintext_metadata_key = Aes128Key::from(plaintext_metadata_key.0); | 
 |  | 
 |         let aead = | 
 |             <<C as CryptoProvider>::Aes128Gcm as AeadInit<Aes128Key>>::new(&plaintext_metadata_key); | 
 |         // No additional authenticated data for encrypted metadata. | 
 |         aead.encrypt(plaintext_metadata, &[], &self.metadata_nonce::<C>()) | 
 |             .expect("Metadata encryption should be infallible") | 
 |     } | 
 | } | 
 |  | 
 | /// Concrete implementation of [`BroadcastCryptoMaterial<V>`] for | 
 | /// a particular protocol version which keeps the key seed | 
 | /// and the metadata key contiguous in memory. | 
 | /// | 
 | /// Broadcast crypto-material specified in this way will only | 
 | /// be usable for (unsigned) advertisement content broadcasts | 
 | /// in the given protocol version. | 
 | /// | 
 | /// For more flexible expression of broadcast credentials, | 
 | /// feel free to directly implement one or more of the | 
 | /// [`BroadcastCryptoMaterial`] and/or | 
 | /// [`crate::credential::v1::SignedBroadcastCryptoMaterial`] | 
 | /// traits on your own struct, dependent on the details | 
 | /// of your own broadcast credentials. | 
 | pub struct SimpleBroadcastCryptoMaterial<V: ProtocolVersion> { | 
 |     key_seed: [u8; 32], | 
 |     metadata_key: V::MetadataKey, | 
 | } | 
 |  | 
 | impl<V: ProtocolVersion> SimpleBroadcastCryptoMaterial<V> { | 
 |     /// Builds some simple broadcast crypto-materials out of | 
 |     /// the provided key-seed and version-specific metadata-key. | 
 |     pub fn new(key_seed: [u8; 32], metadata_key: V::MetadataKey) -> Self { | 
 |         Self { key_seed, metadata_key } | 
 |     } | 
 | } | 
 |  | 
 | impl<V: ProtocolVersion> BroadcastCryptoMaterial<V> for SimpleBroadcastCryptoMaterial<V> { | 
 |     fn key_seed(&self) -> [u8; 32] { | 
 |         self.key_seed | 
 |     } | 
 |     fn metadata_key(&self) -> V::MetadataKey { | 
 |         self.metadata_key.clone() | 
 |     } | 
 | } |