|  | // 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. | 
|  |  | 
|  | use crypto_provider::CryptoProvider; | 
|  | use np_adv::extended::salt::MultiSalt; | 
|  | use np_adv::{extended::data_elements::*, extended::serialize::*, shared_data::*}; | 
|  | use sink::Sink; | 
|  | use std::fmt::{Display, Formatter}; | 
|  |  | 
|  | /// An advertisement builder for V1 advertisements where the | 
|  | /// presence/absence of salt is determined at run-time instead of compile-time. | 
|  | pub struct BoxedAdvBuilder { | 
|  | adv_builder: Box<AdvBuilder>, | 
|  | } | 
|  |  | 
|  | impl From<AdvBuilder> for BoxedAdvBuilder { | 
|  | fn from(adv_builder: AdvBuilder) -> Self { | 
|  | let adv_builder = Box::new(adv_builder); | 
|  | BoxedAdvBuilder { adv_builder } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Error possibly generated when attempting to add a section to | 
|  | /// a BoxedAdvBuilder. | 
|  | #[derive(Debug)] | 
|  | pub enum BoxedAddSectionError { | 
|  | /// An error which was generated by the underlying AdvBuilder wrapped by the BoxedAdvBuilder | 
|  | Underlying(AddSectionError), | 
|  | /// An error generated when the boxed advertisement builder is unsalted, but the section | 
|  | /// identity requires salt. | 
|  | IdentityRequiresSaltError, | 
|  | } | 
|  |  | 
|  | impl Display for BoxedAddSectionError { | 
|  | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | 
|  | match self { | 
|  | BoxedAddSectionError::Underlying(u) => { | 
|  | write!(f, "{0}", u) | 
|  | } | 
|  | BoxedAddSectionError::IdentityRequiresSaltError => { | 
|  | write!(f, "Error generated when the BoxedAdvBuilder is unsalted, but the section identity requires salt.") | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl std::error::Error for BoxedAddSectionError {} | 
|  |  | 
|  | impl From<AddSectionError> for BoxedAddSectionError { | 
|  | fn from(wrapped: AddSectionError) -> Self { | 
|  | BoxedAddSectionError::Underlying(wrapped) | 
|  | } | 
|  | } | 
|  |  | 
|  | fn wrap_owning_section_builder<S: Into<BoxedSectionBuilder<AdvBuilder>>>( | 
|  | maybe_section_builder: Result<S, (AdvBuilder, AddSectionError)>, | 
|  | ) -> Result<BoxedSectionBuilder<AdvBuilder>, (BoxedAdvBuilder, BoxedAddSectionError)> { | 
|  | match maybe_section_builder { | 
|  | Ok(section_builder) => Ok(section_builder.into()), | 
|  | Err((adv_builder, err)) => Err((adv_builder.into(), err.into())), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn wrap_mut_ref_section_builder<'a, S: Into<BoxedSectionBuilder<&'a mut AdvBuilder>>>( | 
|  | maybe_section_builder: Result<S, AddSectionError>, | 
|  | ) -> Result<BoxedSectionBuilder<&'a mut AdvBuilder>, BoxedAddSectionError> { | 
|  | let section_builder = maybe_section_builder?; | 
|  | Ok(section_builder.into()) | 
|  | } | 
|  |  | 
|  | impl BoxedAdvBuilder { | 
|  | /// Gets the current count of sections which have been added | 
|  | /// to this advertisement builder. Does not count currently- | 
|  | /// outstanding section builders. | 
|  | pub fn section_count(&self) -> usize { | 
|  | self.adv_builder.section_count() | 
|  | } | 
|  |  | 
|  | /// Create a section builder using the given identity, | 
|  | /// taking ownership of this advertisement builder. | 
|  | /// | 
|  | /// Unlike `BoxedAdvBuilder#section_builder`, the returned | 
|  | /// section builder will take ownership of this advertisement | 
|  | /// builder, if the operation was successful. Otherwise, | 
|  | /// this advertisement builder will be returned back to the | 
|  | /// caller unaltered as part of the `Err` arm. | 
|  | pub fn into_section_builder( | 
|  | self, | 
|  | identity: BoxedEncoder, | 
|  | ) -> Result<BoxedSectionBuilder<AdvBuilder>, (Self, BoxedAddSectionError)> { | 
|  | match identity { | 
|  | BoxedEncoder::Unencrypted => wrap_owning_section_builder( | 
|  | self.adv_builder.into_section_builder(UnencryptedSectionEncoder), | 
|  | ), | 
|  | BoxedEncoder::MicEncrypted(ident) => { | 
|  | wrap_owning_section_builder(self.adv_builder.into_section_builder(ident)) | 
|  | } | 
|  | BoxedEncoder::SignedEncrypted(ident) => { | 
|  | wrap_owning_section_builder(self.adv_builder.into_section_builder(ident)) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Create a section builder using the given encoder. | 
|  | /// | 
|  | /// Returns `Err` if the underlying advertisement builder | 
|  | /// yields an error when attempting to add a new section | 
|  | /// (typically because there's no more available adv space), | 
|  | /// or if the requested identity requires salt, and the | 
|  | /// advertisement builder is salt-less. | 
|  | pub fn section_builder( | 
|  | &mut self, | 
|  | encoder: BoxedEncoder, | 
|  | ) -> Result<BoxedSectionBuilder<&mut AdvBuilder>, BoxedAddSectionError> { | 
|  | match encoder { | 
|  | BoxedEncoder::Unencrypted => wrap_mut_ref_section_builder( | 
|  | self.adv_builder.section_builder(UnencryptedSectionEncoder), | 
|  | ), | 
|  | BoxedEncoder::MicEncrypted(ident) => { | 
|  | wrap_mut_ref_section_builder(self.adv_builder.section_builder(ident)) | 
|  | } | 
|  | BoxedEncoder::SignedEncrypted(ident) => { | 
|  | wrap_mut_ref_section_builder(self.adv_builder.section_builder(ident)) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Convert the builder into an encoded advertisement. | 
|  | pub fn into_advertisement(self) -> EncodedAdvertisement { | 
|  | self.adv_builder.into_advertisement() | 
|  | } | 
|  | } | 
|  |  | 
|  | /// A wrapped v1 encoder whose type is given at run-time. | 
|  | pub enum BoxedEncoder { | 
|  | /// Unencrypted encoder. | 
|  | Unencrypted, | 
|  | /// An encrypted encoder leveraging MIC for verification. | 
|  | MicEncrypted(MicEncryptedSectionEncoder<MultiSalt>), | 
|  | /// An encrypted encoder leveraging signatures for verification. | 
|  | SignedEncrypted(SignedEncryptedSectionEncoder), | 
|  | } | 
|  |  | 
|  | /// A `SectionBuilder` whose corresponding Identity | 
|  | /// and salted-ness is given at run-time instead of | 
|  | /// at compile-time. | 
|  | pub enum BoxedSectionBuilder<R: AsMut<AdvBuilder>> { | 
|  | /// A builder for a public section. | 
|  | Public(Box<SectionBuilder<R, UnencryptedSectionEncoder>>), | 
|  | /// A builder for a MIC-verified section. | 
|  | MicEncrypted(Box<SectionBuilder<R, MicEncryptedSectionEncoder<MultiSalt>>>), | 
|  | /// A builder for a signature-verified section. | 
|  | SignedEncrypted(Box<SectionBuilder<R, SignedEncryptedSectionEncoder>>), | 
|  | } | 
|  |  | 
|  | impl BoxedSectionBuilder<AdvBuilder> { | 
|  | /// Gets the 0-based index of the section currently under construction | 
|  | /// in the context of the containing advertisement. | 
|  | pub fn section_index(&self) -> usize { | 
|  | match self { | 
|  | BoxedSectionBuilder::Public(x) => x.section_index(), | 
|  | BoxedSectionBuilder::MicEncrypted(x) => x.section_index(), | 
|  | BoxedSectionBuilder::SignedEncrypted(x) => x.section_index(), | 
|  | } | 
|  | } | 
|  | /// Add this builder to the advertisement that created it, | 
|  | /// returning the containing advertisement builder. | 
|  | pub fn add_to_advertisement<C: CryptoProvider>(self) -> BoxedAdvBuilder { | 
|  | let adv_builder = match self { | 
|  | BoxedSectionBuilder::Public(x) => x.add_to_advertisement::<C>(), | 
|  | BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement::<C>(), | 
|  | BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement::<C>(), | 
|  | }; | 
|  | BoxedAdvBuilder::from(adv_builder) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<'a> BoxedSectionBuilder<&'a mut AdvBuilder> { | 
|  | /// Add this builder to the advertisement that created it. | 
|  | pub fn add_to_advertisement<C: CryptoProvider>(self) { | 
|  | match self { | 
|  | BoxedSectionBuilder::Public(x) => x.add_to_advertisement::<C>(), | 
|  | BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement::<C>(), | 
|  | BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement::<C>(), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<R: AsMut<AdvBuilder>> BoxedSectionBuilder<R> { | 
|  | /// Returns true if this wraps a section builder which | 
|  | /// leverages some encrypted identity. | 
|  | pub fn is_encrypted(&self) -> bool { | 
|  | match self { | 
|  | BoxedSectionBuilder::Public(_) => false, | 
|  | BoxedSectionBuilder::MicEncrypted(_) => true, | 
|  | BoxedSectionBuilder::SignedEncrypted(_) => true, | 
|  | } | 
|  | } | 
|  | /// Gets the derived salt of the next DE to be added to the section, | 
|  | /// if this section-builder corresponds to an encrypted section that can | 
|  | /// provide per-DE salts. | 
|  | /// Otherwise, returns nothing. | 
|  | /// | 
|  | /// Suitable for scenarios (like FFI) where a closure would be inappropriate | 
|  | /// for DE construction, and interaction with the client is preferred. | 
|  | pub fn next_de_salt(&self) -> Option<DeSalt> { | 
|  | match self { | 
|  | BoxedSectionBuilder::Public(_) => None, | 
|  | BoxedSectionBuilder::MicEncrypted(x) => x.next_de_salt(), | 
|  | BoxedSectionBuilder::SignedEncrypted(x) => Some(x.next_de_salt()), | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Add a data element to the section with a closure that returns a `Result`. | 
|  | /// | 
|  | /// The provided `build_de` closure will be invoked with the derived salt for this DE, | 
|  | /// if any salt has been specified for the surrounding advertisement. | 
|  | pub fn add_de_res<E>( | 
|  | &mut self, | 
|  | build_de: impl FnOnce(Option<DeSalt>) -> Result<BoxedWriteDataElement, E>, | 
|  | ) -> Result<(), AddDataElementError<E>> { | 
|  | match self { | 
|  | BoxedSectionBuilder::Public(x) => { | 
|  | let build_de_modified = |()| build_de(None); | 
|  | x.add_de_res(build_de_modified) | 
|  | } | 
|  | BoxedSectionBuilder::MicEncrypted(x) => x.add_de_res(build_de), | 
|  | BoxedSectionBuilder::SignedEncrypted(x) => { | 
|  | let build_de_modified = |de_salt: DeSalt| build_de(Some(de_salt)); | 
|  | x.add_de_res(build_de_modified) | 
|  | } | 
|  | } | 
|  | } | 
|  | /// Like add_de_res, but for infalliable closures | 
|  | pub fn add_de( | 
|  | &mut self, | 
|  | build_de: impl FnOnce(Option<DeSalt>) -> BoxedWriteDataElement, | 
|  | ) -> Result<(), AddDataElementError<()>> { | 
|  | self.add_de_res(|derived_salt| Ok::<_, ()>(build_de(derived_salt))) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<R: AsMut<AdvBuilder>> From<SectionBuilder<R, UnencryptedSectionEncoder>> | 
|  | for BoxedSectionBuilder<R> | 
|  | { | 
|  | fn from(section_builder: SectionBuilder<R, UnencryptedSectionEncoder>) -> Self { | 
|  | BoxedSectionBuilder::Public(Box::new(section_builder)) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<R: AsMut<AdvBuilder>> From<SectionBuilder<R, MicEncryptedSectionEncoder<MultiSalt>>> | 
|  | for BoxedSectionBuilder<R> | 
|  | { | 
|  | fn from(section_builder: SectionBuilder<R, MicEncryptedSectionEncoder<MultiSalt>>) -> Self { | 
|  | BoxedSectionBuilder::MicEncrypted(Box::new(section_builder)) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<R: AsMut<AdvBuilder>> From<SectionBuilder<R, SignedEncryptedSectionEncoder>> | 
|  | for BoxedSectionBuilder<R> | 
|  | { | 
|  | fn from(section_builder: SectionBuilder<R, SignedEncryptedSectionEncoder>) -> Self { | 
|  | BoxedSectionBuilder::SignedEncrypted(Box::new(section_builder)) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Mutable trait object reference to a `Sink<u8>` | 
|  | pub struct DynSink<'a> { | 
|  | wrapped: &'a mut dyn Sink<u8>, | 
|  | } | 
|  |  | 
|  | impl<'a> Sink<u8> for DynSink<'a> { | 
|  | fn try_extend_from_slice(&mut self, items: &[u8]) -> Option<()> { | 
|  | self.wrapped.try_extend_from_slice(items) | 
|  | } | 
|  | fn try_push(&mut self, item: u8) -> Option<()> { | 
|  | self.wrapped.try_push(item) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<'a> From<&'a mut dyn Sink<u8>> for DynSink<'a> { | 
|  | fn from(wrapped: &'a mut dyn Sink<u8>) -> Self { | 
|  | DynSink { wrapped } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// A version of the WriteDataElement trait which is object-safe | 
|  | pub trait DynWriteDataElement { | 
|  | /// Gets the data-element header for the data element | 
|  | fn de_header(&self) -> DeHeader; | 
|  | /// Writes the contents of the DE payload to the given DynSink. | 
|  | /// Returns Some(()) if the write operation was successful, | 
|  | /// and None if it was unsuccessful | 
|  | fn write_de_contents(&self, sink: DynSink) -> Option<()>; | 
|  | } | 
|  |  | 
|  | impl<T: WriteDataElement> DynWriteDataElement for T { | 
|  | fn de_header(&self) -> DeHeader { | 
|  | WriteDataElement::de_header(self) | 
|  | } | 
|  | fn write_de_contents(&self, mut sink: DynSink) -> Option<()> { | 
|  | WriteDataElement::write_de_contents(self, &mut sink) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Trait object wrapper for DynWriteDataElement instances | 
|  | pub struct BoxedWriteDataElement { | 
|  | wrapped: Box<dyn DynWriteDataElement>, | 
|  | } | 
|  |  | 
|  | impl BoxedWriteDataElement { | 
|  | /// Constructs a new `BoxedWriteDataElement` from a `WriteDataElement` | 
|  | /// whose trait impl is valid for a `'static` lifetime. | 
|  | pub fn new<D: WriteDataElement + 'static>(wrapped: D) -> Self { | 
|  | let wrapped = Box::new(wrapped); | 
|  | Self { wrapped } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl WriteDataElement for BoxedWriteDataElement { | 
|  | fn de_header(&self) -> DeHeader { | 
|  | self.wrapped.de_header() | 
|  | } | 
|  | fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> { | 
|  | let sink: &mut dyn Sink<u8> = sink; | 
|  | let dyn_sink = DynSink::from(sink); | 
|  | self.wrapped.write_de_contents(dyn_sink) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl From<TxPower> for BoxedWriteDataElement { | 
|  | fn from(tx_power: TxPower) -> Self { | 
|  | BoxedWriteDataElement::new::<TxPowerDataElement>(tx_power.into()) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl From<ContextSyncSeqNum> for BoxedWriteDataElement { | 
|  | fn from(context_sync_sequence_num: ContextSyncSeqNum) -> Self { | 
|  | BoxedWriteDataElement::new::<ContextSyncSeqNumDataElement>(context_sync_sequence_num.into()) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl From<ActionsDataElement> for BoxedWriteDataElement { | 
|  | fn from(data: ActionsDataElement) -> Self { | 
|  | BoxedWriteDataElement::new(data) | 
|  | } | 
|  | } |