• 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 //! Core NP Rust FFI structures and methods for v0 advertisement deserialization.
15 
16 use crate::common::*;
17 use crate::credentials::CredentialBook;
18 use crate::credentials::MatchedCredential;
19 use crate::deserialize::{
20     allocate_decrypted_metadata_handle, DecryptMetadataError, DecryptMetadataResult,
21 };
22 use crate::utils::{FfiEnum, LocksLongerThan};
23 use crate::v0::V0DataElement;
24 use crypto_provider_default::CryptoProviderImpl;
25 use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
26 use np_adv::credential::matched::HasIdentityMatch;
27 use np_adv::legacy;
28 
29 /// Discriminant for possible results of V0 advertisement deserialization
30 #[derive(Clone, Copy)]
31 #[repr(u8)]
32 pub enum DeserializedV0AdvertisementKind {
33     /// The deserialized V0 advertisement was legible.
34     /// The associated payload may be obtained via
35     /// `DeserializedV0Advertisement#into_legible`.
36     Legible = 1,
37     /// The deserialized V0 advertisement is illegible,
38     /// likely meaning that the receiver does not hold
39     /// the proper credentials to be able to read
40     /// the received advertisement.
41     NoMatchingCredentials = 2,
42 }
43 
44 /// Represents a deserialized V0 advertisement
45 #[repr(C)]
46 #[allow(missing_docs)]
47 pub enum DeserializedV0Advertisement {
48     Legible(LegibleDeserializedV0Advertisement),
49     NoMatchingCredentials,
50 }
51 
52 impl FfiEnum for DeserializedV0Advertisement {
53     type Kind = DeserializedV0AdvertisementKind;
kind(&self) -> Self::Kind54     fn kind(&self) -> Self::Kind {
55         match self {
56             DeserializedV0Advertisement::Legible(_) => DeserializedV0AdvertisementKind::Legible,
57             DeserializedV0Advertisement::NoMatchingCredentials => {
58                 DeserializedV0AdvertisementKind::NoMatchingCredentials
59             }
60         }
61     }
62 }
63 
64 impl DeserializedV0Advertisement {
65     /// Attempts to deallocate memory utilized internally by this V0 advertisement (which contains
66     /// a handle to actual advertisement contents behind-the-scenes). This function takes ownership
67     /// of that internal handle.
deallocate(self) -> DeallocateResult68     pub fn deallocate(self) -> DeallocateResult {
69         match self {
70             DeserializedV0Advertisement::Legible(adv) => adv.deallocate(),
71             DeserializedV0Advertisement::NoMatchingCredentials => DeallocateResult::Success,
72         }
73     }
74 
allocate_with_contents( contents: legacy::V0AdvertisementContents< np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>, >, ) -> Result<Self, DeserializeAdvertisementError>75     pub(crate) fn allocate_with_contents(
76         contents: legacy::V0AdvertisementContents<
77             np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>,
78         >,
79     ) -> Result<Self, DeserializeAdvertisementError> {
80         match contents {
81             legacy::V0AdvertisementContents::Plaintext(plaintext_contents) => {
82                 let adv = LegibleDeserializedV0Advertisement::allocate_with_plaintext_contents(
83                     plaintext_contents,
84                 )?;
85                 Ok(Self::Legible(adv))
86             }
87             legacy::V0AdvertisementContents::Decrypted(decrypted_contents) => {
88                 let decrypted_contents = decrypted_contents.clone_match_data();
89                 let adv = LegibleDeserializedV0Advertisement::allocate_with_decrypted_contents(
90                     decrypted_contents,
91                 )?;
92                 Ok(Self::Legible(adv))
93             }
94             legacy::V0AdvertisementContents::NoMatchingCredentials => {
95                 Ok(Self::NoMatchingCredentials)
96             }
97         }
98     }
99 
100     declare_enum_cast! {into_legible, Legible, LegibleDeserializedV0Advertisement}
101 }
102 
103 /// Represents a deserialized V0 advertisement whose DE contents may be read
104 #[repr(C)]
105 pub struct LegibleDeserializedV0Advertisement {
106     num_des: u8,
107     payload: V0Payload,
108     identity_kind: DeserializedV0IdentityKind,
109 }
110 
111 impl LegibleDeserializedV0Advertisement {
allocate_with_plaintext_contents( contents: legacy::deserialize::UnencryptedAdvContents, ) -> Result<Self, DeserializeAdvertisementError>112     pub(crate) fn allocate_with_plaintext_contents(
113         contents: legacy::deserialize::UnencryptedAdvContents,
114     ) -> Result<Self, DeserializeAdvertisementError> {
115         let data_elements = contents
116             .data_elements()
117             .collect::<Result<Vec<_>, _>>()
118             .map_err(|_| DeserializeAdvertisementError)?;
119         let num_des = data_elements.len() as u8;
120         let payload = V0Payload::allocate_with_plaintext_data_elements(data_elements)?;
121         Ok(Self { num_des, payload, identity_kind: DeserializedV0IdentityKind::Plaintext })
122     }
allocate_with_decrypted_contents( contents: np_adv::credential::matched::WithMatchedCredential< MatchedCredential, legacy::deserialize::DecryptedAdvContents, >, ) -> Result<Self, DeserializeAdvertisementError>123     pub(crate) fn allocate_with_decrypted_contents(
124         contents: np_adv::credential::matched::WithMatchedCredential<
125             MatchedCredential,
126             legacy::deserialize::DecryptedAdvContents,
127         >,
128     ) -> Result<Self, DeserializeAdvertisementError> {
129         let data_elements = contents
130             .contents()
131             .data_elements()
132             .collect::<Result<Vec<_>, _>>()
133             .map_err(|_| DeserializeAdvertisementError)?;
134         let num_des = data_elements.len() as u8;
135 
136         let salt = contents.contents().salt();
137 
138         // Reduce the information contained in the contents to just
139         // the metadata key, since we're done copying over the DEs
140         // and other data into an FFI-friendly form.
141         let match_data = contents.map(|x| x.identity_token());
142 
143         let payload = V0Payload::allocate_with_decrypted_contents(salt, match_data, data_elements)?;
144 
145         Ok(Self { num_des, payload, identity_kind: DeserializedV0IdentityKind::Decrypted })
146     }
147     /// Gets the number of data-elements in this adv's payload. This number is suitable as an
148     /// iteration bound for `Self.into_payload().get_de(...)`.
num_des(&self) -> u8149     pub fn num_des(&self) -> u8 {
150         self.num_des
151     }
152     /// Get a copy of the payload handle. This does not copy the underlying payload. The copied
153     /// handle shares a lifetime with the payload handle in this struct; it's the same handle.
payload(&self) -> V0Payload154     pub fn payload(&self) -> V0Payload {
155         self.payload
156     }
157     /// Destructures this legible advertisement into just the discriminant
158     /// for the kind of identity (plaintext/encrypted) used for its contents.
identity_kind(&self) -> DeserializedV0IdentityKind159     pub fn identity_kind(&self) -> DeserializedV0IdentityKind {
160         self.identity_kind
161     }
162     /// Deallocates the underlying handle of the payload. This function takes ownership of the
163     /// payload handle.
deallocate(self) -> DeallocateResult164     pub fn deallocate(self) -> DeallocateResult {
165         self.payload.deallocate().map(|_| ()).into()
166     }
167 }
168 
169 /// Discriminant for deserialized information about the V0
170 /// identity utilized by a deserialized V0 advertisement.
171 #[derive(Clone, Copy)]
172 #[repr(u8)]
173 pub enum DeserializedV0IdentityKind {
174     /// The deserialized identity was a plaintext identity.
175     Plaintext = 1,
176     /// The deserialized identity was some decrypted identity.
177     Decrypted = 2,
178 }
179 
180 /// Information about the identity which matched a
181 /// decrypted V0 advertisement.
182 #[derive(Clone, Copy)]
183 #[repr(C)]
184 pub struct DeserializedV0IdentityDetails {
185     /// The ID of the credential which
186     /// matched the deserialized adv
187     cred_id: u32,
188     /// The 14-byte legacy identity token
189     identity_token: [u8; 14],
190     /// The 2-byte advertisement salt
191     salt: [u8; 2],
192 }
193 
194 impl DeserializedV0IdentityDetails {
new( cred_id: u32, salt: ldt_np_adv::V0Salt, identity_token: ldt_np_adv::V0IdentityToken, ) -> Self195     pub(crate) fn new(
196         cred_id: u32,
197         salt: ldt_np_adv::V0Salt,
198         identity_token: ldt_np_adv::V0IdentityToken,
199     ) -> Self {
200         let salt = salt.bytes();
201         Self { cred_id, salt, identity_token: identity_token.bytes() }
202     }
203     /// Returns the ID of the credential which matched the deserialized adv
cred_id(&self) -> u32204     pub fn cred_id(&self) -> u32 {
205         self.cred_id
206     }
207     /// Returns the 14-byte legacy metadata key
identity_token(&self) -> [u8; 14]208     pub fn identity_token(&self) -> [u8; 14] {
209         self.identity_token
210     }
211     /// Returns the 2-byte advertisement salt
salt(&self) -> [u8; 2]212     pub fn salt(&self) -> [u8; 2] {
213         self.salt
214     }
215 }
216 
217 /// Discriminant for `GetV0IdentityDetailsResult`
218 #[derive(Clone, Copy)]
219 #[repr(u8)]
220 pub enum GetV0IdentityDetailsResultKind {
221     /// The attempt to get the identity details
222     /// for the advertisement failed, possibly
223     /// due to the advertisement being a public
224     /// advertisement, or the underlying
225     /// advertisement has already been deallocated.
226     Error = 0,
227     /// The attempt to get the identity details succeeded.
228     /// The wrapped identity details may be obtained via
229     /// `GetV0IdentityDetailsResult#into_success`.
230     Success = 1,
231 }
232 
233 /// The result of attempting to get the identity details
234 /// for a V0 advertisement via
235 /// `DeserializedV0Advertisement#get_identity_details`.
236 #[repr(C)]
237 #[allow(missing_docs)]
238 pub enum GetV0IdentityDetailsResult {
239     Error,
240     Success(DeserializedV0IdentityDetails),
241 }
242 
243 impl FfiEnum for GetV0IdentityDetailsResult {
244     type Kind = GetV0IdentityDetailsResultKind;
kind(&self) -> Self::Kind245     fn kind(&self) -> Self::Kind {
246         match self {
247             GetV0IdentityDetailsResult::Error => GetV0IdentityDetailsResultKind::Error,
248             GetV0IdentityDetailsResult::Success(_) => GetV0IdentityDetailsResultKind::Success,
249         }
250     }
251 }
252 
253 impl GetV0IdentityDetailsResult {
254     declare_enum_cast! {into_success, Success, DeserializedV0IdentityDetails}
255 }
256 
257 /// Internal implementation of a deserialized V0 identity.
258 pub(crate) struct DeserializedV0IdentityInternals {
259     /// The details about the identity, suitable
260     /// for direct communication over FFI
261     details: DeserializedV0IdentityDetails,
262     /// The metadata key, together with the matched
263     /// credential and enough information to decrypt
264     /// the credential metadata, if desired.
265     match_data: np_adv::credential::matched::WithMatchedCredential<
266         MatchedCredential,
267         ldt_np_adv::V0IdentityToken,
268     >,
269 }
270 
271 impl DeserializedV0IdentityInternals {
new( salt: ldt_np_adv::V0Salt, match_data: np_adv::credential::matched::WithMatchedCredential< MatchedCredential, ldt_np_adv::V0IdentityToken, >, ) -> Self272     pub(crate) fn new(
273         salt: ldt_np_adv::V0Salt,
274         match_data: np_adv::credential::matched::WithMatchedCredential<
275             MatchedCredential,
276             ldt_np_adv::V0IdentityToken,
277         >,
278     ) -> Self {
279         let cred_id = match_data.matched_credential().id();
280         let identity_token = match_data.contents();
281         let details = DeserializedV0IdentityDetails::new(cred_id, salt, *identity_token);
282         Self { details, match_data }
283     }
284     /// Gets the directly-transmissible details about
285     /// this deserialized V0 identity. Does not include
286     /// decrypted metadata bytes.
details(&self) -> DeserializedV0IdentityDetails287     pub(crate) fn details(&self) -> DeserializedV0IdentityDetails {
288         self.details
289     }
290     /// Attempts to decrypt the metadata associated
291     /// with this identity.
decrypt_metadata(&self) -> Option<Vec<u8>>292     pub(crate) fn decrypt_metadata(&self) -> Option<Vec<u8>> {
293         self.match_data.decrypt_metadata::<CryptoProviderImpl>().ok()
294     }
295 }
296 
297 /// The internal data-structure used for storing
298 /// the payload of a deserialized V0 advertisement.
299 pub struct V0PayloadInternals {
300     identity: Option<DeserializedV0IdentityInternals>,
301     des: Vec<V0DataElement>,
302 }
303 
304 impl V0PayloadInternals {
305     /// Attempts to get the DE with the given index
306     /// in this v0 payload.
get_de(&self, index: u8) -> GetV0DEResult307     fn get_de(&self, index: u8) -> GetV0DEResult {
308         match self.des.get(index as usize) {
309             Some(de) => GetV0DEResult::Success(de.clone()),
310             None => GetV0DEResult::Error,
311         }
312     }
313     /// Gets the identity details for this V0 payload,
314     /// if this payload was associated with an identity.
get_identity_details(&self) -> GetV0IdentityDetailsResult315     fn get_identity_details(&self) -> GetV0IdentityDetailsResult {
316         match &self.identity {
317             Some(x) => GetV0IdentityDetailsResult::Success(x.details()),
318             None => GetV0IdentityDetailsResult::Error,
319         }
320     }
321     /// Attempts to decrypt the metadata for the matched
322     /// credential for this V0 payload (if any)
decrypt_metadata(&self) -> Result<Vec<u8>, DecryptMetadataError>323     fn decrypt_metadata(&self) -> Result<Vec<u8>, DecryptMetadataError> {
324         match &self.identity {
325             None => Err(DecryptMetadataError::EncryptedMetadataNotAvailable),
326             Some(identity) => {
327                 identity.decrypt_metadata().ok_or(DecryptMetadataError::DecryptionFailed)
328             }
329         }
330     }
331 }
332 
333 /// A `#[repr(C)]` handle to a value of type `V0PayloadInternals`
334 #[repr(C)]
335 #[derive(Clone, Copy, PartialEq, Eq)]
336 pub struct V0Payload {
337     handle_id: u64,
338 }
339 
340 declare_handle_map!(
341     v0_payload,
342     crate::common::default_handle_map_dimensions(),
343     super::V0Payload,
344     super::V0PayloadInternals
345 );
346 
347 use super::DeserializeAdvertisementError;
348 
349 impl LocksLongerThan<V0Payload> for CredentialBook {}
350 
351 impl V0Payload {
allocate_with_plaintext_data_elements( data_elements: Vec<legacy::deserialize::DeserializedDataElement<legacy::Plaintext>>, ) -> Result<Self, HandleMapFullError>352     pub(crate) fn allocate_with_plaintext_data_elements(
353         data_elements: Vec<legacy::deserialize::DeserializedDataElement<legacy::Plaintext>>,
354     ) -> Result<Self, HandleMapFullError> {
355         Self::allocate(move || {
356             let des = data_elements.into_iter().map(V0DataElement::from).collect();
357             let identity = None;
358             V0PayloadInternals { des, identity }
359         })
360     }
allocate_with_decrypted_contents( salt: ldt_np_adv::V0Salt, match_data: np_adv::credential::matched::WithMatchedCredential< MatchedCredential, ldt_np_adv::V0IdentityToken, >, data_elements: Vec<legacy::deserialize::DeserializedDataElement<legacy::Ciphertext>>, ) -> Result<Self, HandleMapFullError>361     pub(crate) fn allocate_with_decrypted_contents(
362         salt: ldt_np_adv::V0Salt,
363         match_data: np_adv::credential::matched::WithMatchedCredential<
364             MatchedCredential,
365             ldt_np_adv::V0IdentityToken,
366         >,
367         data_elements: Vec<legacy::deserialize::DeserializedDataElement<legacy::Ciphertext>>,
368     ) -> Result<Self, HandleMapFullError> {
369         Self::allocate(move || {
370             let des = data_elements.into_iter().map(V0DataElement::from).collect();
371             let identity = Some(DeserializedV0IdentityInternals::new(salt, match_data));
372             V0PayloadInternals { des, identity }
373         })
374     }
375     /// Gets the data-element with the given index in this v0 adv payload. This uses the handle but
376     /// does not take ownership of it.
get_de(&self, index: u8) -> GetV0DEResult377     pub fn get_de(&self, index: u8) -> GetV0DEResult {
378         match self.get() {
379             Ok(read_guard) => read_guard.get_de(index),
380             Err(_) => GetV0DEResult::Error,
381         }
382     }
383 
384     /// Gets the identity details for this V0 payload, if this payload was associated with an
385     /// identity (i.e: encrypted advertisements). This uses the handle but does not take ownership
386     /// of it.
get_identity_details(&self) -> GetV0IdentityDetailsResult387     pub fn get_identity_details(&self) -> GetV0IdentityDetailsResult {
388         match self.get() {
389             Ok(read_guard) => read_guard.get_identity_details(),
390             Err(_) => GetV0IdentityDetailsResult::Error,
391         }
392     }
393 
394     /// Attempts to decrypt the metadata for the matched credential for this V0 payload (if any).
395     /// This uses the handle but does not take ownership of it. The caller is given ownership of
396     /// the returned `DecryptedMetadata` handle if present.
decrypt_metadata(&self) -> DecryptMetadataResult397     pub fn decrypt_metadata(&self) -> DecryptMetadataResult {
398         match self.get() {
399             Ok(read_guard) => match read_guard.decrypt_metadata() {
400                 Ok(decrypted_metadata) => allocate_decrypted_metadata_handle(decrypted_metadata),
401                 Err(_) => DecryptMetadataResult::Error,
402             },
403             Err(_) => DecryptMetadataResult::Error,
404         }
405     }
406 
407     /// Deallocates any underlying data held by a `V0Payload`. This takes ownership of the handle.
deallocate_payload(&self) -> DeallocateResult408     pub fn deallocate_payload(&self) -> DeallocateResult {
409         self.deallocate().map(|_| ()).into()
410     }
411 }
412 
413 /// Discriminant of `GetV0DEResult`.
414 #[derive(Clone, Copy)]
415 #[repr(u8)]
416 pub enum GetV0DEResultKind {
417     /// The attempt to get the DE succeeded.
418     /// The associated payload may be obtained via
419     /// `GetV0DEResult#into_success`.
420     Success = 0,
421     /// The attempt to get the DE failed,
422     /// possibly due to the requested index being
423     /// out-of-bounds or due to the advertisement
424     /// having been previously deallocated.
425     Error = 1,
426 }
427 
428 /// The result of `V0Payload#get_de`.
429 #[repr(C)]
430 #[allow(missing_docs)]
431 pub enum GetV0DEResult {
432     Success(V0DataElement),
433     Error,
434 }
435 
436 impl FfiEnum for GetV0DEResult {
437     type Kind = GetV0DEResultKind;
kind(&self) -> Self::Kind438     fn kind(&self) -> Self::Kind {
439         match self {
440             GetV0DEResult::Success(_) => GetV0DEResultKind::Success,
441             GetV0DEResult::Error => GetV0DEResultKind::Error,
442         }
443     }
444 }
445 
446 impl GetV0DEResult {
447     declare_enum_cast! {into_success, Success, V0DataElement}
448 }
449