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