• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! Common externally-acessible V0 constructs for both of the
16 //! serialization+deserialization flows.
17 
18 use crate::common::InvalidStackDataStructure;
19 use crate::serialize::AdvertisementBuilderKind;
20 use crate::utils::FfiEnum;
21 use np_adv::{
22     legacy::data_elements::actions::ActionsDataElement,
23     legacy::{data_elements as np_adv_de, Ciphertext, PacketFlavorEnum, Plaintext},
24 };
25 use strum::IntoEnumIterator;
26 
27 /// Discriminant for `V0DataElement`.
28 #[repr(u8)]
29 pub enum V0DataElementKind {
30     /// A transmission Power (Tx Power) data-element.
31     /// The associated payload may be obtained via
32     /// `V0DataElement#into_tx_power`.
33     TxPower = 1,
34     /// The Actions data-element.
35     /// The associated payload may be obtained via
36     /// `V0DataElement#into_actions`.
37     Actions = 2,
38 }
39 
40 /// Representation of a V0 data element.
41 #[repr(C)]
42 #[allow(missing_docs)]
43 #[derive(Clone)]
44 pub enum V0DataElement {
45     TxPower(TxPower),
46     Actions(V0Actions),
47 }
48 
49 impl TryFrom<V0DataElement> for np_adv_dynamic::legacy::ToBoxedSerializeDataElement {
50     type Error = InvalidStackDataStructure;
try_from(de: V0DataElement) -> Result<Self, InvalidStackDataStructure>51     fn try_from(de: V0DataElement) -> Result<Self, InvalidStackDataStructure> {
52         match de {
53             V0DataElement::TxPower(x) => x.try_into(),
54             V0DataElement::Actions(x) => x.try_into(),
55         }
56     }
57 }
58 
59 impl<F: np_adv::legacy::PacketFlavor> From<np_adv::legacy::deserialize::DeserializedDataElement<F>>
60     for V0DataElement
61 {
from(de: np_adv::legacy::deserialize::DeserializedDataElement<F>) -> Self62     fn from(de: np_adv::legacy::deserialize::DeserializedDataElement<F>) -> Self {
63         use np_adv::legacy::deserialize::DeserializedDataElement;
64         match de {
65             DeserializedDataElement::Actions(x) => V0DataElement::Actions(x.into()),
66             DeserializedDataElement::TxPower(x) => V0DataElement::TxPower(x.into()),
67         }
68     }
69 }
70 
71 impl FfiEnum for V0DataElement {
72     type Kind = V0DataElementKind;
kind(&self) -> Self::Kind73     fn kind(&self) -> Self::Kind {
74         match self {
75             V0DataElement::Actions(_) => V0DataElementKind::Actions,
76             V0DataElement::TxPower(_) => V0DataElementKind::TxPower,
77         }
78     }
79 }
80 
81 impl V0DataElement {
82     declare_enum_cast! {into_tx_power, TxPower, TxPower}
83     declare_enum_cast! {into_actions, Actions, V0Actions}
84 }
85 
86 /// Discriminant for `BuildTxPowerResult`.
87 #[repr(u8)]
88 #[derive(Clone, Copy)]
89 pub enum BuildTxPowerResultKind {
90     /// The transmission power was outside the
91     /// allowed -100dBm to 20dBm range.
92     OutOfRange = 0,
93     /// The transmission power was in range,
94     /// and so a `TxPower` struct was constructed.
95     Success = 1,
96 }
97 
98 /// Result type for attempting to construct a
99 /// Tx Power from a signed byte.
100 #[repr(C)]
101 #[allow(missing_docs)]
102 pub enum BuildTxPowerResult {
103     OutOfRange,
104     Success(TxPower),
105 }
106 
107 impl FfiEnum for BuildTxPowerResult {
108     type Kind = BuildTxPowerResultKind;
kind(&self) -> Self::Kind109     fn kind(&self) -> Self::Kind {
110         match self {
111             Self::OutOfRange => BuildTxPowerResultKind::OutOfRange,
112             Self::Success(_) => BuildTxPowerResultKind::Success,
113         }
114     }
115 }
116 
117 impl BuildTxPowerResult {
118     declare_enum_cast! {into_success, Success, TxPower}
119 }
120 
121 /// Representation of a transmission power,
122 /// as used for the Tx Power DE in V0 and V1.
123 #[derive(Clone)]
124 #[repr(C)]
125 pub struct TxPower {
126     tx_power: i8,
127 }
128 
129 impl TxPower {
130     /// Attempts to construct a new TxPower from the given signed-byte value.
build_from_signed_byte(tx_power: i8) -> BuildTxPowerResult131     pub fn build_from_signed_byte(tx_power: i8) -> BuildTxPowerResult {
132         match np_adv::shared_data::TxPower::try_from(tx_power) {
133             Ok(_) => BuildTxPowerResult::Success(Self { tx_power }),
134             Err(_) => BuildTxPowerResult::OutOfRange,
135         }
136     }
137     /// Yields this Tx Power value as an i8.
as_i8(&self) -> i8138     pub fn as_i8(&self) -> i8 {
139         self.tx_power
140     }
141 }
142 
143 impl From<np_adv_de::tx_power::TxPowerDataElement> for TxPower {
from(de: np_adv_de::tx_power::TxPowerDataElement) -> Self144     fn from(de: np_adv_de::tx_power::TxPowerDataElement) -> Self {
145         Self { tx_power: de.tx_power_value() }
146     }
147 }
148 
149 impl TryFrom<TxPower> for np_adv_dynamic::legacy::ToBoxedSerializeDataElement {
150     type Error = InvalidStackDataStructure;
try_from(value: TxPower) -> Result<Self, InvalidStackDataStructure>151     fn try_from(value: TxPower) -> Result<Self, InvalidStackDataStructure> {
152         np_adv::shared_data::TxPower::try_from(value.as_i8())
153             .map_err(|_| InvalidStackDataStructure)
154             .map(|x| x.into())
155     }
156 }
157 
158 /// Representation of the Actions DE in V0.
159 #[derive(Clone, Copy)]
160 #[repr(C)]
161 pub enum V0Actions {
162     /// A set of action bits which were present in a plaintext identity advertisement
163     Plaintext(V0ActionBits),
164     /// A set of action bits which were present in a encrypted identity advertisement
165     Encrypted(V0ActionBits),
166 }
167 
168 impl TryFrom<V0Actions> for np_adv_dynamic::legacy::ToBoxedSerializeDataElement {
169     type Error = InvalidStackDataStructure;
try_from(value: V0Actions) -> Result<Self, InvalidStackDataStructure>170     fn try_from(value: V0Actions) -> Result<Self, InvalidStackDataStructure> {
171         let boxed_action_bits = np_adv_dynamic::legacy::BoxedActionBits::try_from(value)?;
172         Ok(boxed_action_bits.into())
173     }
174 }
175 
176 impl<F: np_adv::legacy::PacketFlavor> From<ActionsDataElement<F>> for V0Actions {
from(value: ActionsDataElement<F>) -> Self177     fn from(value: ActionsDataElement<F>) -> Self {
178         match F::ENUM_VARIANT {
179             PacketFlavorEnum::Plaintext => {
180                 Self::Plaintext(V0ActionBits { bitfield: value.action.as_u32() })
181             }
182             PacketFlavorEnum::Ciphertext => {
183                 Self::Encrypted(V0ActionBits { bitfield: value.action.as_u32() })
184             }
185         }
186     }
187 }
188 
189 #[repr(C)]
190 #[derive(Clone, Copy)]
191 /// The bitfield data of a V0Actions data element
192 pub struct V0ActionBits {
193     bitfield: u32,
194 }
195 
196 impl From<u32> for V0ActionBits {
from(bitfield: u32) -> Self197     fn from(bitfield: u32) -> Self {
198         // No need to validate here. Validation of this struct is done at all places where it is
199         // taken as a parameter. See [`InvalidStackDataStructure`]. Since this struct is
200         // `#[repr(C)]` if rules were enforced here, they could be broken by a foreign language
201         // user.
202         Self { bitfield }
203     }
204 }
205 
206 #[derive(Clone, Copy, strum_macros::EnumIter)]
207 #[allow(missing_docs)]
208 #[repr(u8)]
209 /// The possible boolean action types which can be present in an Actions data element
210 pub enum ActionType {
211     CrossDevSdk = 1,
212     CallTransfer = 4,
213     ActiveUnlock = 8,
214     NearbyShare = 9,
215     InstantTethering = 10,
216     PhoneHub = 11,
217 }
218 
219 #[derive(Clone, Copy, Debug)]
220 /// The given int is out of range for conversion.
221 pub struct TryFromIntError;
222 
223 impl From<core::num::TryFromIntError> for TryFromIntError {
from(_: core::num::TryFromIntError) -> Self224     fn from(_: core::num::TryFromIntError) -> Self {
225         Self
226     }
227 }
228 
229 impl TryFrom<u8> for ActionType {
230     type Error = TryFromIntError;
try_from(n: u8) -> Result<Self, Self::Error>231     fn try_from(n: u8) -> Result<Self, Self::Error> {
232         // cast is safe since it's a repr(u8) unit enum
233         Self::iter().find(|t| *t as u8 == n).ok_or(TryFromIntError)
234     }
235 }
236 
237 impl ActionType {
as_boxed_action_element( &self, value: bool, ) -> np_adv_dynamic::legacy::ToBoxedActionElement238     pub(crate) fn as_boxed_action_element(
239         &self,
240         value: bool,
241     ) -> np_adv_dynamic::legacy::ToBoxedActionElement {
242         use np_adv_dynamic::legacy::ToBoxedActionElement;
243         match self {
244             Self::CrossDevSdk => ToBoxedActionElement::CrossDevSdk(value),
245             Self::CallTransfer => ToBoxedActionElement::CallTransfer(value),
246             Self::ActiveUnlock => ToBoxedActionElement::ActiveUnlock(value),
247             Self::NearbyShare => ToBoxedActionElement::NearbyShare(value),
248             Self::InstantTethering => ToBoxedActionElement::InstantTethering(value),
249             Self::PhoneHub => ToBoxedActionElement::PhoneHub(value),
250         }
251     }
252 }
253 
254 impl From<ActionType> for np_adv::legacy::data_elements::actions::ActionType {
from(value: ActionType) -> Self255     fn from(value: ActionType) -> Self {
256         match value {
257             ActionType::CrossDevSdk => {
258                 np_adv::legacy::data_elements::actions::ActionType::CrossDevSdk
259             }
260             ActionType::CallTransfer => {
261                 np_adv::legacy::data_elements::actions::ActionType::CallTransfer
262             }
263             ActionType::ActiveUnlock => {
264                 np_adv::legacy::data_elements::actions::ActionType::ActiveUnlock
265             }
266             ActionType::NearbyShare => {
267                 np_adv::legacy::data_elements::actions::ActionType::NearbyShare
268             }
269             ActionType::InstantTethering => {
270                 np_adv::legacy::data_elements::actions::ActionType::InstantTethering
271             }
272             ActionType::PhoneHub => np_adv::legacy::data_elements::actions::ActionType::PhoneHub,
273         }
274     }
275 }
276 
277 // ensure bidirectional mapping
278 impl From<np_adv::legacy::data_elements::actions::ActionType> for ActionType {
from(value: np_adv_de::actions::ActionType) -> Self279     fn from(value: np_adv_de::actions::ActionType) -> Self {
280         match value {
281             np_adv_de::actions::ActionType::CrossDevSdk => ActionType::CrossDevSdk,
282             np_adv_de::actions::ActionType::CallTransfer => ActionType::CallTransfer,
283             np_adv_de::actions::ActionType::ActiveUnlock => ActionType::ActiveUnlock,
284             np_adv_de::actions::ActionType::NearbyShare => ActionType::NearbyShare,
285             np_adv_de::actions::ActionType::InstantTethering => ActionType::InstantTethering,
286             np_adv_de::actions::ActionType::PhoneHub => ActionType::PhoneHub,
287         }
288     }
289 }
290 
291 impl From<np_adv_dynamic::legacy::BoxedActionBits> for V0Actions {
from(bits: np_adv_dynamic::legacy::BoxedActionBits) -> Self292     fn from(bits: np_adv_dynamic::legacy::BoxedActionBits) -> Self {
293         use np_adv_dynamic::legacy::BoxedActionBits;
294         match bits {
295             BoxedActionBits::Plaintext(x) => Self::Plaintext(V0ActionBits { bitfield: x.as_u32() }),
296             BoxedActionBits::Ciphertext(x) => {
297                 Self::Encrypted(V0ActionBits { bitfield: x.as_u32() })
298             }
299         }
300     }
301 }
302 
303 impl TryFrom<V0Actions> for np_adv_dynamic::legacy::BoxedActionBits {
304     type Error = InvalidStackDataStructure;
try_from(actions: V0Actions) -> Result<Self, InvalidStackDataStructure>305     fn try_from(actions: V0Actions) -> Result<Self, InvalidStackDataStructure> {
306         match actions {
307             V0Actions::Plaintext(action_bits) => {
308                 let bits =
309                     np_adv::legacy::data_elements::actions::ActionBits::<Plaintext>::try_from(
310                         action_bits.bitfield,
311                     )
312                     .map_err(|_| InvalidStackDataStructure)?;
313                 Ok(bits.into())
314             }
315             V0Actions::Encrypted(action_bits) => {
316                 let bits =
317                     np_adv::legacy::data_elements::actions::ActionBits::<Ciphertext>::try_from(
318                         action_bits.bitfield,
319                     )
320                     .map_err(|_| InvalidStackDataStructure)?;
321                 Ok(bits.into())
322             }
323         }
324     }
325 }
326 
327 /// Discriminant for `SetV0ActionResult`.
328 #[repr(u8)]
329 pub enum SetV0ActionResultKind {
330     /// The attempt to set the action bit failed. The
331     /// action bits were yielded back to the caller,
332     /// unmodified.
333     Error = 0,
334     /// The attempt to set the action bit succeeded.
335     /// The updated action bits were yielded back to the caller.
336     Success = 1,
337 }
338 
339 /// The result of attempting to set a particular action
340 /// bit on some `V0Actions`.
341 #[repr(C)]
342 #[allow(missing_docs)]
343 pub enum SetV0ActionResult {
344     Success(V0Actions),
345     Error(V0Actions),
346 }
347 
348 impl FfiEnum for SetV0ActionResult {
349     type Kind = SetV0ActionResultKind;
kind(&self) -> Self::Kind350     fn kind(&self) -> Self::Kind {
351         match self {
352             Self::Success(_) => SetV0ActionResultKind::Success,
353             Self::Error(_) => SetV0ActionResultKind::Error,
354         }
355     }
356 }
357 
358 impl SetV0ActionResult {
359     declare_enum_cast! { into_success, Success, V0Actions }
360     declare_enum_cast! { into_error, Error, V0Actions }
361 }
362 
363 impl V0Actions {
364     /// Constructs a new V0 actions DE with no declared boolean
365     /// actions and a zeroed context sync sequence number,
366     /// where the DE is intended for the given advertisement
367     /// kind (plaintext/encrypted).
new_zeroed(kind: AdvertisementBuilderKind) -> Self368     pub fn new_zeroed(kind: AdvertisementBuilderKind) -> Self {
369         match kind {
370             AdvertisementBuilderKind::Public => Self::Plaintext(V0ActionBits { bitfield: 0 }),
371             AdvertisementBuilderKind::Encrypted => Self::Encrypted(V0ActionBits { bitfield: 0 }),
372         }
373     }
374 
375     /// Gets the V0 Action bits as represented by a u32 where the last 8 bits are
376     /// always 0 since V0 actions can only hold up to 24 bits.
as_u32(&self) -> u32377     pub fn as_u32(&self) -> u32 {
378         match self {
379             V0Actions::Plaintext(bits) => bits.bitfield,
380             V0Actions::Encrypted(bits) => bits.bitfield,
381         }
382     }
383 
384     /// Return whether a boolean action type is set in this data element
385     #[allow(clippy::expect_used)]
has_action(&self, action_type: ActionType) -> Result<bool, InvalidStackDataStructure>386     pub fn has_action(&self, action_type: ActionType) -> Result<bool, InvalidStackDataStructure> {
387         let boxed_action_bits = np_adv_dynamic::legacy::BoxedActionBits::try_from(*self)?;
388         let action_type = action_type.into();
389         Ok(boxed_action_bits.has_action(action_type))
390     }
391 
392     /// Attempts to set the given action bit to the given boolean value.
393     /// This operation may fail if the requested action bit may not be
394     /// set for the kind of containing advertisement (public/encrypted)
395     /// that this action DE is intended to belong to. In this case,
396     /// the original action bits will be yielded back to the caller,
397     /// unaltered.
set_action( self, action_type: ActionType, value: bool, ) -> Result<SetV0ActionResult, InvalidStackDataStructure>398     pub fn set_action(
399         self,
400         action_type: ActionType,
401         value: bool,
402     ) -> Result<SetV0ActionResult, InvalidStackDataStructure> {
403         let mut boxed_action_bits = np_adv_dynamic::legacy::BoxedActionBits::try_from(self)?;
404         let boxed_action_element = action_type.as_boxed_action_element(value);
405         match boxed_action_bits.set_action(boxed_action_element) {
406             Ok(()) => Ok(SetV0ActionResult::Success(boxed_action_bits.into())),
407             Err(_) => Ok(SetV0ActionResult::Error(self)),
408         }
409     }
410 }
411