1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crypto_provider::CryptoProvider;
16 use np_adv::{
17     legacy::{
18         data_elements::{actions::*, tx_power::TxPowerDataElement, *},
19         serialize::*,
20         *,
21     },
22     shared_data::*,
23 };
24 use std::fmt::{Display, Formatter};
25 
26 /// Wrapper around a V0 advertisement builder which
27 /// is agnostic to the kind of identity used in the advertisement.
28 /// Instead of compile-time errors for non-matching packet flavors,
29 /// this builder instead defers any generated errors to run-time.
30 /// Generic over the Aes algorithm used for any encrypted identities,
31 /// since that is generally specified at compile-time.
32 pub enum BoxedAdvBuilder<C: CryptoProvider> {
33     /// Builder for unencrypted advertisements.
34     Unencrypted(AdvBuilder<UnencryptedEncoder>),
35     /// Builder for LDT-encrypted advertisements.
36     Ldt(AdvBuilder<LdtEncoder<C>>),
37 }
38 
39 /// Wrapper around possible errors which occur only during
40 /// advertisement construction from a builder.
41 #[derive(Debug)]
42 pub enum BoxedAdvConstructionError {
43     /// An error originating from a problem with LDT
44     /// encryption of the advertisement contents.
45     Ldt(LdtEncodeError),
46     /// An error from encoding an unencrypted adv
47     Unencrypted(UnencryptedEncodeError),
48 }
49 
50 impl<C: CryptoProvider> BoxedAdvBuilder<C> {
51     /// Returns true if this wraps an adv builder which
52     /// leverages some encrypted identity.
is_encrypted(&self) -> bool53     pub fn is_encrypted(&self) -> bool {
54         match self {
55             BoxedAdvBuilder::Unencrypted(_) => false,
56             BoxedAdvBuilder::Ldt(_) => true,
57         }
58     }
59     /// Constructs a new BoxedAdvBuilder from the given BoxedIdentity
new(identity: BoxedEncoder<C>) -> Self60     pub fn new(identity: BoxedEncoder<C>) -> Self {
61         match identity {
62             BoxedEncoder::Unencrypted(encoder) => {
63                 BoxedAdvBuilder::Unencrypted(AdvBuilder::new(encoder))
64             }
65             BoxedEncoder::LdtEncrypted(encoder) => BoxedAdvBuilder::Ldt(AdvBuilder::new(encoder)),
66         }
67     }
68     /// Attempts to add a data element to the advertisement
69     /// being built by this BoxedAdvBuilder. Returns
70     /// nothing if successful, and a BoxedAddDataElementError
71     /// if something went wrong in the attempt to add the DE.
add_data_element( &mut self, data_element: ToBoxedSerializeDataElement, ) -> Result<(), BoxedAddDataElementError>72     pub fn add_data_element(
73         &mut self,
74         data_element: ToBoxedSerializeDataElement,
75     ) -> Result<(), BoxedAddDataElementError> {
76         match self {
77             BoxedAdvBuilder::Unencrypted(public_builder) => {
78                 //Verify that we can get the data element as plaintext
79                 let maybe_plaintext_data_element = data_element.to_plaintext();
80                 match maybe_plaintext_data_element {
81                     Some(plaintext_data_element) => public_builder
82                         .add_data_element(plaintext_data_element)
83                         .map_err(|e| e.into()),
84                     None => Err(BoxedAddDataElementError::FlavorMismatchError),
85                 }
86             }
87             BoxedAdvBuilder::Ldt(private_builder) => {
88                 //Verify that we can get the data element as ciphertext
89                 let maybe_ciphertext_data_element = data_element.to_ciphertext();
90                 match maybe_ciphertext_data_element {
91                     Some(ciphertext_data_element) => private_builder
92                         .add_data_element(ciphertext_data_element)
93                         .map_err(|e| e.into()),
94                     None => Err(BoxedAddDataElementError::FlavorMismatchError),
95                 }
96             }
97         }
98     }
99     /// Consume this BoxedAdvBuilder and attempt to create
100     /// a serialized advertisement including the added DEs.
into_advertisement(self) -> Result<SerializedAdv, BoxedAdvConstructionError>101     pub fn into_advertisement(self) -> Result<SerializedAdv, BoxedAdvConstructionError> {
102         match self {
103             BoxedAdvBuilder::Unencrypted(x) => {
104                 x.into_advertisement().map_err(BoxedAdvConstructionError::Unencrypted)
105             }
106             BoxedAdvBuilder::Ldt(x) => {
107                 x.into_advertisement().map_err(BoxedAdvConstructionError::Ldt)
108             }
109         }
110     }
111 }
112 
113 /// Possible errors generated when trying to add a DE to a
114 /// BoxedAdvBuilder.
115 #[derive(Debug)]
116 pub enum BoxedAddDataElementError {
117     /// Some kind of error in adding the data element which
118     /// is not an issue with trying to add a DE of the incorrect
119     /// packet flavoring.
120     UnderlyingError(AddDataElementError),
121     /// Error when attempting to add a DE which requires one
122     /// of an (encrypted/plaintext) advertisement, but the
123     /// advertisement builder doesn't match this requirement.
124     FlavorMismatchError,
125 }
126 
127 impl Display for BoxedAddDataElementError {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result128     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
129         match self {
130             BoxedAddDataElementError::UnderlyingError(u) => {
131                 write!(f, "{0:?}", u)
132             }
133             BoxedAddDataElementError::FlavorMismatchError => {
134                 write!(f, "Expected packet flavoring for added DEs does not match the actual packet flavor of the DE")
135             }
136         }
137     }
138 }
139 
140 impl std::error::Error for BoxedAddDataElementError {}
141 
142 impl From<AddDataElementError> for BoxedAddDataElementError {
from(add_data_element_error: AddDataElementError) -> Self143     fn from(add_data_element_error: AddDataElementError) -> Self {
144         BoxedAddDataElementError::UnderlyingError(add_data_element_error)
145     }
146 }
147 
148 /// Trait for types which can provide trait object
149 /// references to either plaintext or ciphertext [SerializeDataElement]
150 pub trait ToMultiFlavorSerializeDataElement {
151     /// Gets the associated trait object reference to a `SerializeDataElement<Plaintext>`
152     /// with the same lifetime as a reference to the implementor.
to_plaintext(&self) -> DynamicSerializeDataElement<Plaintext>153     fn to_plaintext(&self) -> DynamicSerializeDataElement<Plaintext>;
154 
155     /// Gets the associated trait object reference to a `SerializeDataElement<Ciphertext>`
156     /// with the same lifetime as a reference to the implementor.
to_ciphertext(&self) -> DynamicSerializeDataElement<Ciphertext>157     fn to_ciphertext(&self) -> DynamicSerializeDataElement<Ciphertext>;
158 }
159 
160 /// Blanket impl of [ToMultiFlavorSerializeDataElement] for implementors of [SerializeDataElement]
161 /// for both [Plaintext] and [Ciphertext] packet flavors.
162 impl<T: SerializeDataElement<Plaintext> + SerializeDataElement<Ciphertext>>
163     ToMultiFlavorSerializeDataElement for T
164 {
to_plaintext(&self) -> DynamicSerializeDataElement<Plaintext>165     fn to_plaintext(&self) -> DynamicSerializeDataElement<Plaintext> {
166         let reference: &dyn SerializeDataElement<Plaintext> = self;
167         reference.into()
168     }
to_ciphertext(&self) -> DynamicSerializeDataElement<Ciphertext>169     fn to_ciphertext(&self) -> DynamicSerializeDataElement<Ciphertext> {
170         let reference: &dyn SerializeDataElement<Ciphertext> = self;
171         reference.into()
172     }
173 }
174 
175 /// Boxed trait object version of [SerializeDataElement] which incorporates
176 /// all possible variants on generatable packet flavoring
177 /// (`Plaintext`, `Ciphertext`, or both, as a [ToMultiFlavorSerializeDataElement])
178 pub enum ToBoxedSerializeDataElement {
179     /// The underlying DE is plaintext-only.
180     Plaintext(Box<dyn SerializeDataElement<Plaintext>>),
181     /// The underlying DE is ciphertext-only.
182     Ciphertext(Box<dyn SerializeDataElement<Ciphertext>>),
183     /// The underlying DE may exist in plaintext or
184     /// in ciphertext advertisements.
185     Both(Box<dyn ToMultiFlavorSerializeDataElement>),
186 }
187 
188 impl ToBoxedSerializeDataElement {
189     /// If this [ToBoxedSerializeDataElement] can generate plaintext, returns
190     /// a trait object reference to a `SerializeDataElement<Plaintext>`
to_plaintext(&self) -> Option<DynamicSerializeDataElement<Plaintext>>191     pub fn to_plaintext(&self) -> Option<DynamicSerializeDataElement<Plaintext>> {
192         match &self {
193             ToBoxedSerializeDataElement::Plaintext(x) => Some(x.as_ref().into()),
194             ToBoxedSerializeDataElement::Ciphertext(_) => None,
195             ToBoxedSerializeDataElement::Both(x) => Some(x.as_ref().to_plaintext()),
196         }
197     }
198     /// If this [ToBoxedSerializeDataElement] can generate ciphertext, returns
199     /// a trait object reference to a `SerializeDataElement<Ciphertext>`
to_ciphertext(&self) -> Option<DynamicSerializeDataElement<Ciphertext>>200     pub fn to_ciphertext(&self) -> Option<DynamicSerializeDataElement<Ciphertext>> {
201         match &self {
202             ToBoxedSerializeDataElement::Plaintext(_) => None,
203             ToBoxedSerializeDataElement::Ciphertext(x) => Some(x.as_ref().into()),
204             ToBoxedSerializeDataElement::Both(x) => Some(x.as_ref().to_ciphertext()),
205         }
206     }
207 }
208 
209 /// Boxed version of implementors of the [AdvEncoder] trait.
210 pub enum BoxedEncoder<C: CryptoProvider> {
211     /// Unencrypted encoding.
212     Unencrypted(UnencryptedEncoder),
213     /// An encrypted encoding, using LDT encryption.
214     LdtEncrypted(LdtEncoder<C>),
215 }
216 
217 impl<C: CryptoProvider> From<UnencryptedEncoder> for BoxedEncoder<C> {
from(encoder: UnencryptedEncoder) -> BoxedEncoder<C>218     fn from(encoder: UnencryptedEncoder) -> BoxedEncoder<C> {
219         BoxedEncoder::Unencrypted(encoder)
220     }
221 }
222 
223 impl<C: CryptoProvider> From<LdtEncoder<C>> for BoxedEncoder<C> {
from(encoder: LdtEncoder<C>) -> BoxedEncoder<C>224     fn from(encoder: LdtEncoder<C>) -> BoxedEncoder<C> {
225         BoxedEncoder::LdtEncrypted(encoder)
226     }
227 }
228 
229 impl From<TxPower> for ToBoxedSerializeDataElement {
from(data: TxPower) -> Self230     fn from(data: TxPower) -> Self {
231         ToBoxedSerializeDataElement::Both(Box::new(TxPowerDataElement::from(data)))
232     }
233 }
234 
235 impl From<BoxedActionBits> for ToBoxedSerializeDataElement {
from(action_bits: BoxedActionBits) -> Self236     fn from(action_bits: BoxedActionBits) -> Self {
237         match action_bits {
238             BoxedActionBits::Plaintext(action_bits) => ToBoxedSerializeDataElement::Plaintext(
239                 Box::new(ActionsDataElement::from(action_bits)),
240             ),
241             BoxedActionBits::Ciphertext(action_bits) => ToBoxedSerializeDataElement::Ciphertext(
242                 Box::new(ActionsDataElement::from(action_bits)),
243             ),
244         }
245     }
246 }
247 
248 /// Boxed version of `ToActionElement` which allows abstracting over
249 /// what packet flavors are supported by a given action.
250 pub enum ToBoxedActionElement {
251     /// Action bit for cross device SDK.
252     CrossDevSdk(bool),
253     /// Action bit for call transfer.
254     CallTransfer(bool),
255     /// Action bit for active unlock.
256     ActiveUnlock(bool),
257     /// Action bit for nearby share.
258     NearbyShare(bool),
259     /// Action bit for instant tethering.
260     InstantTethering(bool),
261     /// Action bit for PhoneHub.
262     PhoneHub(bool),
263 }
264 
265 /// [`ActionBits`] with runtime-determined packet flavoring
266 pub enum BoxedActionBits {
267     /// Action-bits for a plaintext advertisement.
268     Plaintext(ActionBits<Plaintext>),
269     /// Action-bits for a ciphertext advertisement.
270     Ciphertext(ActionBits<Ciphertext>),
271 }
272 
273 impl From<ActionBits<Plaintext>> for BoxedActionBits {
from(bits: ActionBits<Plaintext>) -> Self274     fn from(bits: ActionBits<Plaintext>) -> Self {
275         Self::Plaintext(bits)
276     }
277 }
278 
279 impl From<ActionBits<Ciphertext>> for BoxedActionBits {
from(bits: ActionBits<Ciphertext>) -> Self280     fn from(bits: ActionBits<Ciphertext>) -> Self {
281         Self::Ciphertext(bits)
282     }
283 }
284 
285 /// Error which is raised when the flavor of a [`BoxedActionBits`]
286 /// does not match the supported flavors of a [`ToBoxedActionElement`]
287 /// upon attempting to add the action to the bit-field.
288 #[derive(Debug)]
289 pub struct BoxedSetActionFlavorError;
290 
291 impl BoxedActionBits {
292     /// Constructs the [`BoxedActionBits`] variant with the specified packet
293     /// flavor variant, and no bits set.
new(packet_flavor: PacketFlavorEnum) -> Self294     pub fn new(packet_flavor: PacketFlavorEnum) -> Self {
295         match packet_flavor {
296             PacketFlavorEnum::Plaintext => BoxedActionBits::Plaintext(ActionBits::default()),
297             PacketFlavorEnum::Ciphertext => BoxedActionBits::Ciphertext(ActionBits::default()),
298         }
299     }
300 
301     /// Returns whether a boolean action type is set in these action bits, or `None`
302     /// if the given action type does not represent a boolean (e.g: a context-sync
303     /// sequence number).
has_action(&self, action_type: ActionType) -> bool304     pub fn has_action(&self, action_type: ActionType) -> bool {
305         match self {
306             BoxedActionBits::Plaintext(x) => x.has_action(action_type),
307             BoxedActionBits::Ciphertext(x) => x.has_action(action_type),
308         }
309     }
310 
set<F: PacketFlavor, E: ActionElementFlavor<F>>( action_bits: &mut ActionBits<F>, to_element: E, ) -> Result<(), BoxedSetActionFlavorError>311     fn set<F: PacketFlavor, E: ActionElementFlavor<F>>(
312         action_bits: &mut ActionBits<F>,
313         to_element: E,
314     ) -> Result<(), BoxedSetActionFlavorError> {
315         action_bits.set_action(to_element);
316         Ok(())
317     }
318 
319     /// Attempts to set the specified [`ToBoxedActionElement`], yielding
320     /// a [`BoxedSetActionFlavorError`] if the flavor of this
321     /// [`BoxedActionBits`] isn't supported by the passed [`ToBoxedActionElement`].
set_action( &mut self, to_element: ToBoxedActionElement, ) -> Result<(), BoxedSetActionFlavorError>322     pub fn set_action(
323         &mut self,
324         to_element: ToBoxedActionElement,
325     ) -> Result<(), BoxedSetActionFlavorError> {
326         match self {
327             BoxedActionBits::Plaintext(action_bits) => match to_element {
328                 ToBoxedActionElement::CrossDevSdk(b) => {
329                     Self::set(action_bits, CrossDevSdk::from(b))
330                 }
331                 ToBoxedActionElement::NearbyShare(b) => {
332                     Self::set(action_bits, NearbyShare::from(b))
333                 }
334                 ToBoxedActionElement::CallTransfer(_)
335                 | ToBoxedActionElement::ActiveUnlock(_)
336                 | ToBoxedActionElement::InstantTethering(_)
337                 | ToBoxedActionElement::PhoneHub(_) => Err(BoxedSetActionFlavorError),
338             },
339             BoxedActionBits::Ciphertext(action_bits) => match to_element {
340                 ToBoxedActionElement::CrossDevSdk(b) => {
341                     Self::set(action_bits, CrossDevSdk::from(b))
342                 }
343                 ToBoxedActionElement::CallTransfer(b) => {
344                     Self::set(action_bits, CallTransfer::from(b))
345                 }
346                 ToBoxedActionElement::ActiveUnlock(b) => {
347                     Self::set(action_bits, ActiveUnlock::from(b))
348                 }
349                 ToBoxedActionElement::NearbyShare(b) => {
350                     Self::set(action_bits, NearbyShare::from(b))
351                 }
352                 ToBoxedActionElement::InstantTethering(b) => {
353                     Self::set(action_bits, InstantTethering::from(b))
354                 }
355                 ToBoxedActionElement::PhoneHub(b) => Self::set(action_bits, PhoneHub::from(b)),
356             },
357         }
358     }
359 }
360