1 // Copyright 2023 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //      http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //! NP Rust FFI structures and methods for V0 advertisement serialization.
15 
16 use crate::common::*;
17 use crate::credentials::V0BroadcastCredential;
18 use crate::serialize::AdvertisementBuilderKind;
19 use crate::utils::FfiEnum;
20 use crate::v0::V0DataElement;
21 use crypto_provider_default::CryptoProviderImpl;
22 use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
23 use np_adv_dynamic::legacy::BoxedAdvConstructionError;
24 
25 /// A handle to a builder for V0 advertisements.
26 #[derive(Clone, Copy)]
27 #[repr(C)]
28 pub struct V0AdvertisementBuilder {
29     kind: AdvertisementBuilderKind,
30     handle: V0AdvertisementBuilderHandle,
31 }
32 
33 impl V0AdvertisementBuilder {
34     /// Gets the kind of advertisement builder (public/encrypted).
kind(&self) -> AdvertisementBuilderKind35     pub fn kind(&self) -> AdvertisementBuilderKind {
36         self.kind
37     }
38 
39     /// Attempts to add the given data element to the V0 advertisement builder behind this handle.
40     /// This function does not take ownership of the handle.
add_de(&self, de: V0DataElement) -> Result<AddV0DEResult, InvalidStackDataStructure>41     pub fn add_de(&self, de: V0DataElement) -> Result<AddV0DEResult, InvalidStackDataStructure> {
42         match self.handle.get_mut() {
43             Ok(mut adv_builder_write_guard) => adv_builder_write_guard.add_de(de),
44             Err(_) => Ok(AddV0DEResult::InvalidAdvertisementBuilderHandle),
45         }
46     }
47     /// Attempts to serialize the contents of the advertisement builder behind this handle to
48     /// bytes. This function takes ownership of the handle.
into_advertisement(self) -> SerializeV0AdvertisementResult49     pub fn into_advertisement(self) -> SerializeV0AdvertisementResult {
50         match self.handle.deallocate() {
51             Ok(adv_builder) => adv_builder.into_advertisement(),
52             Err(_) => SerializeV0AdvertisementResult::InvalidAdvertisementBuilderHandle,
53         }
54     }
55     /// Attempts to deallocate the V0 advertisement builder behind this handle. This function takes
56     /// ownership of the handle.
deallocate(self) -> DeallocateResult57     pub fn deallocate(self) -> DeallocateResult {
58         self.handle.deallocate().map(|_| ()).into()
59     }
60 }
61 
62 /// Discriminant for `CreateV0AdvertisementBuilderResult`
63 #[derive(Copy, Clone)]
64 #[repr(u8)]
65 pub enum CreateV0AdvertisementBuilderResultKind {
66     /// The attempt to create a new advertisement builder
67     /// failed since there are no more available
68     /// slots for V0 advertisement builders in their handle-map.
69     NoSpaceLeft = 0,
70     /// The attempt succeeded. The wrapped advertisement builder
71     /// may be obtained via
72     /// `CreateV0AdvertisementBuilderResult#into_success`.
73     Success = 1,
74 }
75 
76 /// The result of attempting to create a new V0 advertisement builder.
77 #[repr(C)]
78 #[allow(missing_docs)]
79 pub enum CreateV0AdvertisementBuilderResult {
80     NoSpaceLeft,
81     Success(V0AdvertisementBuilder),
82 }
83 
84 impl From<Result<V0AdvertisementBuilder, HandleMapFullError>>
85     for CreateV0AdvertisementBuilderResult
86 {
from(result: Result<V0AdvertisementBuilder, HandleMapFullError>) -> Self87     fn from(result: Result<V0AdvertisementBuilder, HandleMapFullError>) -> Self {
88         match result {
89             Ok(builder) => CreateV0AdvertisementBuilderResult::Success(builder),
90             Err(_) => CreateV0AdvertisementBuilderResult::NoSpaceLeft,
91         }
92     }
93 }
94 
95 impl FfiEnum for CreateV0AdvertisementBuilderResult {
96     type Kind = CreateV0AdvertisementBuilderResultKind;
kind(&self) -> Self::Kind97     fn kind(&self) -> Self::Kind {
98         match self {
99             CreateV0AdvertisementBuilderResult::NoSpaceLeft => {
100                 CreateV0AdvertisementBuilderResultKind::NoSpaceLeft
101             }
102             CreateV0AdvertisementBuilderResult::Success(_) => {
103                 CreateV0AdvertisementBuilderResultKind::Success
104             }
105         }
106     }
107 }
108 
109 impl CreateV0AdvertisementBuilderResult {
110     declare_enum_cast! {into_success, Success, V0AdvertisementBuilder }
111 }
112 
113 /// Creates a new V0 advertisement builder for a public advertisement. The caller is given
114 /// ownership of the created handle.
create_v0_public_advertisement_builder() -> CreateV0AdvertisementBuilderResult115 pub fn create_v0_public_advertisement_builder() -> CreateV0AdvertisementBuilderResult {
116     V0AdvertisementBuilderHandle::allocate(V0AdvertisementBuilderInternals::new_public)
117         .map(|handle| V0AdvertisementBuilder { kind: AdvertisementBuilderKind::Public, handle })
118         .into()
119 }
120 
121 /// Creates a new V0 advertisement builder for an encrypted advertisement. The caller is given
122 /// ownership of the created handle.
create_v0_encrypted_advertisement_builder( broadcast_cred: V0BroadcastCredential, salt: FixedSizeArray<2>, ) -> CreateV0AdvertisementBuilderResult123 pub fn create_v0_encrypted_advertisement_builder(
124     broadcast_cred: V0BroadcastCredential,
125     salt: FixedSizeArray<2>,
126 ) -> CreateV0AdvertisementBuilderResult {
127     V0AdvertisementBuilderHandle::allocate(move || {
128         V0AdvertisementBuilderInternals::new_ldt(broadcast_cred, salt.into_array())
129     })
130     .map(|handle| V0AdvertisementBuilder { kind: AdvertisementBuilderKind::Encrypted, handle })
131     .into()
132 }
133 
134 /// Discriminant for `SerializeV0AdvertisementResult`.
135 #[repr(u8)]
136 pub enum SerializeV0AdvertisementResultKind {
137     /// Serializing the advertisement to bytes was successful.
138     Success = 0,
139     /// The advertisement builder handle was invalid.
140     InvalidAdvertisementBuilderHandle = 1,
141     /// Serializing the advertisement to bytes failed
142     /// because the data in the advertisement wasn't
143     /// of an appropriate size for LDT encryption
144     /// to succeed.
145     LdtError = 2,
146     /// Serializing an unencrypted adv failed because the adv data didn't meet the length
147     /// requirements.
148     UnencryptedError = 3,
149 }
150 
151 /// The result of attempting to serialize the contents
152 /// of a V0 advertisement builder to raw bytes.
153 #[repr(C)]
154 #[allow(missing_docs)]
155 pub enum SerializeV0AdvertisementResult {
156     Success(ByteBuffer<24>),
157     InvalidAdvertisementBuilderHandle,
158     LdtError,
159     UnencryptedError,
160 }
161 
162 impl SerializeV0AdvertisementResult {
163     declare_enum_cast! { into_success, Success, ByteBuffer<24> }
164 }
165 
166 impl FfiEnum for SerializeV0AdvertisementResult {
167     type Kind = SerializeV0AdvertisementResultKind;
kind(&self) -> SerializeV0AdvertisementResultKind168     fn kind(&self) -> SerializeV0AdvertisementResultKind {
169         match self {
170             Self::Success(_) => SerializeV0AdvertisementResultKind::Success,
171             Self::InvalidAdvertisementBuilderHandle => {
172                 SerializeV0AdvertisementResultKind::InvalidAdvertisementBuilderHandle
173             }
174             Self::LdtError => SerializeV0AdvertisementResultKind::LdtError,
175             Self::UnencryptedError => SerializeV0AdvertisementResultKind::UnencryptedError,
176         }
177     }
178 }
179 
180 /// Internal Rust-side implementation of a V0 advertisement builder.
181 pub struct V0AdvertisementBuilderInternals {
182     adv_builder: np_adv_dynamic::legacy::BoxedAdvBuilder<CryptoProviderImpl>,
183 }
184 
185 impl V0AdvertisementBuilderInternals {
new_public() -> Self186     pub(crate) fn new_public() -> Self {
187         Self::new(np_adv::legacy::serialize::UnencryptedEncoder.into())
188     }
new_ldt(broadcast_cred: V0BroadcastCredential, salt: [u8; 2]) -> Self189     pub(crate) fn new_ldt(broadcast_cred: V0BroadcastCredential, salt: [u8; 2]) -> Self {
190         // TODO: What do about salts? Need to prevent re-use fo the same salt,
191         // but have no current rich representation of used salts...
192         let salt = ldt_np_adv::V0Salt::from(salt);
193         let internal_broadcast_cred = broadcast_cred.into_internal();
194         let identity = np_adv::legacy::serialize::LdtEncoder::new(salt, &internal_broadcast_cred);
195         Self::new(identity.into())
196     }
new(encoder: np_adv_dynamic::legacy::BoxedEncoder<CryptoProviderImpl>) -> Self197     fn new(encoder: np_adv_dynamic::legacy::BoxedEncoder<CryptoProviderImpl>) -> Self {
198         let adv_builder =
199             np_adv_dynamic::legacy::BoxedAdvBuilder::<CryptoProviderImpl>::new(encoder);
200         Self { adv_builder }
201     }
add_de(&mut self, de: V0DataElement) -> Result<AddV0DEResult, InvalidStackDataStructure>202     fn add_de(&mut self, de: V0DataElement) -> Result<AddV0DEResult, InvalidStackDataStructure> {
203         let to_boxed = np_adv_dynamic::legacy::ToBoxedSerializeDataElement::try_from(de)?;
204         use np_adv::legacy::serialize::AddDataElementError;
205         use np_adv_dynamic::legacy::BoxedAddDataElementError;
206         match self.adv_builder.add_data_element(to_boxed) {
207             Ok(_) => Ok(AddV0DEResult::Success),
208             Err(BoxedAddDataElementError::FlavorMismatchError) => {
209                 Ok(AddV0DEResult::InvalidIdentityTypeForDataElement)
210             }
211             Err(BoxedAddDataElementError::UnderlyingError(
212                 AddDataElementError::InsufficientAdvSpace,
213             )) => Ok(AddV0DEResult::InsufficientAdvertisementSpace),
214         }
215     }
into_advertisement(self) -> SerializeV0AdvertisementResult216     fn into_advertisement(self) -> SerializeV0AdvertisementResult {
217         self.adv_builder
218             .into_advertisement()
219             .map(|serialized_bytes| {
220                 SerializeV0AdvertisementResult::Success(ByteBuffer::from_array_view(
221                     serialized_bytes,
222                 ))
223             })
224             .unwrap_or_else(|e| match e {
225                 BoxedAdvConstructionError::Ldt(_) => SerializeV0AdvertisementResult::LdtError,
226                 BoxedAdvConstructionError::Unencrypted(_) => {
227                     SerializeV0AdvertisementResult::UnencryptedError
228                 }
229             })
230     }
231 }
232 
233 /// A `#[repr(C)]` handle to a value of type `V0AdvertisementBuilderInternals`
234 #[repr(C)]
235 #[derive(Clone, Copy, PartialEq, Eq)]
236 struct V0AdvertisementBuilderHandle {
237     handle_id: u64,
238 }
239 
240 declare_handle_map!(
241     advertisement_builder,
242     crate::common::default_handle_map_dimensions(),
243     super::V0AdvertisementBuilderHandle,
244     super::V0AdvertisementBuilderInternals
245 );
246 
247 /// Result code for the operation of adding a DE to a V0
248 /// advertisement builder.
249 #[derive(Clone, Copy)]
250 #[repr(u8)]
251 pub enum AddV0DEResult {
252     /// The DE was successfully added to the advertisement builder
253     /// behind the given handle.
254     Success = 0,
255     /// The handle for the advertisement builder was invalid.
256     InvalidAdvertisementBuilderHandle = 1,
257     /// There was not enough available space left in the advertisement
258     /// to append the given data element.
259     InsufficientAdvertisementSpace = 2,
260     /// The passed data element is not broadcastable under the
261     /// identity type of the advertisement (public/private).
262     InvalidIdentityTypeForDataElement = 3,
263 }
264 
265 impl From<np_adv_dynamic::legacy::BoxedAddDataElementError> for AddV0DEResult {
from(err: np_adv_dynamic::legacy::BoxedAddDataElementError) -> Self266     fn from(err: np_adv_dynamic::legacy::BoxedAddDataElementError) -> Self {
267         use np_adv_dynamic::legacy::BoxedAddDataElementError;
268         match err {
269             BoxedAddDataElementError::UnderlyingError(_) => Self::InsufficientAdvertisementSpace,
270             BoxedAddDataElementError::FlavorMismatchError => {
271                 Self::InvalidIdentityTypeForDataElement
272             }
273         }
274     }
275 }
276