lib.rs (48327B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use mls_platform_api::{ 6 ClientIdentifiers, ExporterOutput, GroupDetails, GroupIdEpoch, MlsCommitOutput, Received, 7 }; 8 use nserror::{nsresult, NS_ERROR_FAILURE, NS_ERROR_INVALID_ARG, NS_OK}; 9 use nsstring::nsACString; 10 use thin_vec::ThinVec; 11 12 mod storage; 13 use storage::get_key_path; 14 use storage::get_storage_key; 15 use storage::get_storage_path; 16 17 // Access the platform state for the given storage prefix 18 pub fn state_access( 19 storage_prefix: &nsACString, 20 ) -> Result<mls_platform_api::PlatformState, nsresult> { 21 if storage_prefix.is_empty() { 22 log::error!("Input storage prefix cannot be empty"); 23 return Err(NS_ERROR_INVALID_ARG); 24 }; 25 26 let db_path = get_storage_path(storage_prefix); 27 let Ok(db_key) = get_storage_key(storage_prefix) else { 28 log::error!("Failed to get storage key"); 29 return Err(NS_ERROR_FAILURE); 30 }; 31 32 mls_platform_api::state_access(&db_path, &db_key).map_err(|e| { 33 log::error!("Failed to access state: {:?}", e); 34 NS_ERROR_FAILURE 35 }) 36 } 37 38 #[no_mangle] 39 pub extern "C" fn mls_state_delete(storage_prefix: &nsACString) -> nsresult { 40 // Log the function call 41 log::debug!("Entering mls_state_delete"); 42 43 // Retrieve the database path from the caller and retrieve the key 44 let db_path = get_storage_path(storage_prefix); 45 let key_path = get_key_path(storage_prefix); 46 47 // Delete the key file which is owned by the storage module 48 let key_removal_result = match std::fs::remove_file(&key_path) { 49 Ok(_) => Ok(()), 50 Err(e) => { 51 log::error!("{:?}", e); 52 Err(NS_ERROR_FAILURE) 53 } 54 }; 55 56 // Delete the database file which is owned by the mls-platform-api library 57 // Note: we do not check if the key is valid before destructing the database 58 let db_removal_result = match mls_platform_api::state_delete(&db_path) { 59 Ok(_) => Ok(()), 60 Err(e) => { 61 log::error!("{:?}", e); 62 Err(NS_ERROR_FAILURE) 63 } 64 }; 65 66 // Return an error if either operation failed 67 if key_removal_result.is_err() || db_removal_result.is_err() { 68 return NS_ERROR_FAILURE; 69 } 70 71 // Log the name of the database and keys that were deleted 72 log::debug!(" (input) storage_prefix: {:?}", storage_prefix); 73 log::info!("State deleted successfully"); 74 NS_OK 75 } 76 77 #[repr(C)] 78 pub struct GkGroupIdEpoch { 79 pub group_id: ThinVec<u8>, 80 pub group_epoch: ThinVec<u8>, 81 } 82 83 impl From<GroupIdEpoch> for GkGroupIdEpoch { 84 fn from(v: GroupIdEpoch) -> Self { 85 let GroupIdEpoch { 86 group_id, 87 group_epoch, 88 } = v; 89 Self { 90 group_id: group_id.into(), 91 group_epoch: ThinVec::from(group_epoch.to_le_bytes()), 92 } 93 } 94 } 95 96 #[no_mangle] 97 pub unsafe extern "C" fn mls_state_delete_group( 98 storage_prefix: &nsACString, 99 group_id_bytes_ptr: *const u8, 100 group_id_bytes_len: usize, 101 identifier_bytes_ptr: *const u8, 102 identifier_bytes_len: usize, 103 ret_group_id_epoch: &mut GkGroupIdEpoch, 104 ) -> nsresult { 105 // Log the function call 106 log::debug!("Entering mls_state_delete_group"); 107 108 // Validate the inputs 109 if group_id_bytes_len == 0 { 110 log::error!("Group Identifier argument cannot be empty"); 111 return NS_ERROR_INVALID_ARG; 112 } 113 if identifier_bytes_len == 0 { 114 log::error!("Identifier argument cannot be empty"); 115 return NS_ERROR_INVALID_ARG; 116 } 117 118 // Convert the raw pointers to slices 119 let group_id_bytes: &[u8] = 120 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 121 let identifier_bytes: &[u8] = 122 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 123 124 // Retrieve the platform state based on the storage prefix 125 let Ok(mut pstate) = state_access(storage_prefix) else { 126 return NS_ERROR_FAILURE; 127 }; 128 129 // Call the platform API to delete the group for the selected client 130 let groupid_and_epoch = 131 match mls_platform_api::state_delete_group(&mut pstate, group_id_bytes, identifier_bytes) { 132 Ok(gid) => gid, 133 Err(e) => { 134 log::error!("{:?}", e); 135 return NS_ERROR_FAILURE; 136 } 137 }; 138 139 log::debug!( 140 " (returns) Group Identifier: {:?}", 141 hex::encode(&groupid_and_epoch.group_id) 142 ); 143 144 log::debug!( 145 " (returns) Group Epoch: {:?}", 146 hex::encode(&groupid_and_epoch.group_epoch.to_le_bytes()) 147 ); 148 149 // Write the results 150 *ret_group_id_epoch = groupid_and_epoch.into(); 151 log::info!("Successfully deleted group"); 152 153 NS_OK 154 } 155 156 #[no_mangle] 157 pub extern "C" fn mls_generate_identity( 158 storage_prefix: &nsACString, 159 ret_identifier: &mut ThinVec<u8>, 160 ) -> nsresult { 161 // Log the function call 162 log::debug!("Entering mls_generate_identity"); 163 164 // Retrieve the platform state based on the storage prefix 165 let Ok(mut pstate) = state_access(storage_prefix) else { 166 return NS_ERROR_FAILURE; 167 }; 168 169 // Note: We set the GroupConfig to default for now 170 let default_gc = mls_platform_api::GroupConfig::default(); 171 172 // Generate the signature keypair 173 let identifier = 174 match mls_platform_api::mls_generate_identity(&mut pstate, default_gc.ciphersuite) { 175 Ok(id) => id, 176 Err(e) => { 177 log::error!("{:?}", e); 178 return NS_ERROR_FAILURE; 179 } 180 }; 181 182 // Log the identifier 183 log::debug!( 184 " (returns) Client Identifier: {:?}", 185 hex::encode(&identifier) 186 ); 187 188 // Write the result to ret_val 189 ret_identifier.extend_from_slice(&identifier); 190 191 log::info!("Successfully generated signature keypair"); 192 193 NS_OK 194 } 195 196 #[no_mangle] 197 pub unsafe extern "C" fn mls_generate_credential_basic( 198 cred_content_bytes_ptr: *const u8, 199 cred_content_bytes_len: usize, 200 ret_credential: &mut ThinVec<u8>, 201 ) -> nsresult { 202 // Log the function call 203 log::debug!("Entering mls_generate_credential_basic"); 204 205 // Validate the inputs 206 if cred_content_bytes_len == 0 { 207 log::error!("Credential content argument cannot be empty"); 208 return NS_ERROR_INVALID_ARG; 209 } 210 211 // Convert the raw pointers to slices 212 let cred_content_bytes: &[u8] = 213 unsafe { std::slice::from_raw_parts(cred_content_bytes_ptr, cred_content_bytes_len) }; 214 215 // Generate the basic credential 216 let credential_bytes = match mls_platform_api::mls_generate_credential_basic(cred_content_bytes) 217 { 218 Ok(cred) => cred, 219 Err(e) => { 220 log::error!("{:?}", e); 221 return NS_ERROR_FAILURE; 222 } 223 }; 224 225 // Log the credential 226 log::debug!( 227 " (returns) Credential: {:?}", 228 hex::encode(&credential_bytes) 229 ); 230 231 // Write the result to ret_val 232 ret_credential.extend_from_slice(&credential_bytes); 233 234 log::info!("Successfully generated basic credential"); 235 NS_OK 236 } 237 238 #[no_mangle] 239 pub unsafe extern "C" fn mls_generate_keypackage( 240 storage_prefix: &nsACString, 241 identifier_bytes_ptr: *const u8, 242 identifier_bytes_len: usize, 243 credential_bytes_ptr: *const u8, 244 credential_bytes_len: usize, 245 ret_keypackage: &mut ThinVec<u8>, 246 ) -> nsresult { 247 // Log the function call 248 log::debug!("Entering mls_generate_keypackage"); 249 250 // Validate the inputs 251 if identifier_bytes_len == 0 { 252 log::error!("Identifier argument cannot be empty"); 253 return NS_ERROR_INVALID_ARG; 254 } 255 if credential_bytes_len == 0 { 256 log::error!("Credential argument cannot be empty"); 257 return NS_ERROR_INVALID_ARG; 258 } 259 260 // Convert the raw pointers to slices 261 let identifier_bytes: &[u8] = 262 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 263 let credential_bytes: &[u8] = 264 unsafe { std::slice::from_raw_parts(credential_bytes_ptr, credential_bytes_len) }; 265 266 // Retrieve the platform state based on the storage prefix 267 let Ok(pstate) = state_access(storage_prefix) else { 268 return NS_ERROR_FAILURE; 269 }; 270 271 // Generate the key package 272 let key_package = match mls_platform_api::mls_generate_key_package( 273 &pstate, 274 identifier_bytes, 275 credential_bytes, 276 &Default::default(), 277 ) { 278 Ok(kp) => kp, 279 Err(e) => { 280 log::error!("{:?}", e); 281 return NS_ERROR_FAILURE; 282 } 283 }; 284 285 let key_package_bytes = match key_package.to_bytes() { 286 Ok(kp) => kp, 287 Err(e) => { 288 log::error!("{:?}", e); 289 return NS_ERROR_FAILURE; 290 } 291 }; 292 293 // Write the result 294 ret_keypackage.extend_from_slice(&key_package_bytes); 295 296 log::debug!( 297 " (returns) Key Package: {:?}", 298 hex::encode(&key_package_bytes) 299 ); 300 301 log::info!("Successfully generated key package"); 302 303 NS_OK 304 } 305 306 #[repr(C)] 307 pub struct GkClientIdentifiers { 308 pub identity: ThinVec<u8>, 309 pub credential: ThinVec<u8>, 310 } 311 312 impl From<ClientIdentifiers> for GkClientIdentifiers { 313 fn from(v: ClientIdentifiers) -> Self { 314 let ClientIdentifiers { 315 identity, 316 credential, 317 } = v; 318 Self { 319 identity: identity.into(), 320 credential: credential.into(), 321 } 322 } 323 } 324 325 #[repr(C)] 326 pub struct GkGroupDetails { 327 pub group_id: ThinVec<u8>, 328 pub group_epoch: ThinVec<u8>, 329 pub group_members: ThinVec<GkClientIdentifiers>, 330 } 331 332 impl From<GroupDetails> for GkGroupDetails { 333 fn from(v: GroupDetails) -> Self { 334 let GroupDetails { 335 group_id, 336 group_epoch, 337 group_members, 338 } = v; 339 Self { 340 group_id: group_id.into(), 341 group_epoch: ThinVec::from(group_epoch.to_le_bytes()), 342 group_members: group_members.into_iter().map(Into::into).collect(), 343 } 344 } 345 } 346 347 #[no_mangle] 348 pub unsafe extern "C" fn mls_group_details( 349 storage_prefix: &nsACString, 350 group_id_bytes_ptr: *const u8, 351 group_id_bytes_len: usize, 352 identifier_bytes_ptr: *const u8, 353 identifier_bytes_len: usize, 354 ret_group_details: &mut GkGroupDetails, 355 ) -> nsresult { 356 // Log the function call 357 log::debug!("Entering mls_group_details"); 358 359 // Validate the inputs 360 if group_id_bytes_len == 0 { 361 log::error!("Group Identifier argument cannot be empty"); 362 return NS_ERROR_INVALID_ARG; 363 } 364 if identifier_bytes_len == 0 { 365 log::error!("Identifier argument cannot be empty"); 366 return NS_ERROR_INVALID_ARG; 367 } 368 369 // Convert the raw pointers to slices 370 let group_id_bytes: &[u8] = 371 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 372 let identifier_bytes: &[u8] = 373 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 374 375 // Retrieve the platform state based on the storage prefix 376 let Ok(mut pstate) = state_access(storage_prefix) else { 377 return NS_ERROR_FAILURE; 378 }; 379 380 // Retrieve the group details (includes members) 381 let Ok(group_details) = 382 mls_platform_api::mls_group_details(&mut pstate, group_id_bytes, identifier_bytes) 383 else { 384 log::error!("Failed to retrieve group details"); 385 return NS_ERROR_FAILURE; 386 }; 387 388 // Log the result 389 log::debug!(" (returns) Group Details: {:?}", group_details); 390 391 // Write the result 392 *ret_group_details = group_details.into(); 393 394 NS_OK 395 } 396 397 #[no_mangle] 398 pub unsafe extern "C" fn mls_group_create( 399 storage_prefix: &nsACString, 400 identifier_bytes_ptr: *const u8, 401 identifier_bytes_len: usize, 402 credential_bytes_ptr: *const u8, 403 credential_bytes_len: usize, 404 opt_group_id_bytes_ptr: *const u8, 405 opt_group_id_bytes_len: usize, 406 ret_group_id_epoch: &mut GkGroupIdEpoch, 407 ) -> nsresult { 408 // Log the function call 409 log::debug!("Entering mls_group_create"); 410 411 // Validate the inputs 412 if identifier_bytes_len == 0 { 413 log::error!("Identifier argument cannot be empty"); 414 return NS_ERROR_INVALID_ARG; 415 } 416 if credential_bytes_len == 0 { 417 log::error!("Credential argument cannot be empty"); 418 return NS_ERROR_INVALID_ARG; 419 } 420 421 // Convert the raw pointers to slices 422 let identifier_bytes: &[u8] = 423 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 424 let credential_bytes: &[u8] = 425 unsafe { std::slice::from_raw_parts(credential_bytes_ptr, credential_bytes_len) }; 426 let opt_group_id_bytes: &[u8] = 427 unsafe { std::slice::from_raw_parts(opt_group_id_bytes_ptr, opt_group_id_bytes_len) }; 428 429 // Retrieve the platform state based on the storage prefix 430 let Ok(mut pstate) = state_access(storage_prefix) else { 431 return NS_ERROR_FAILURE; 432 }; 433 434 // Note: we implicitely require a 32 bytes identifier 435 let gid_opt_vec: Vec<u8> = opt_group_id_bytes.to_vec(); 436 let gid_opt_res = if opt_group_id_bytes_len != 32 { 437 log::debug!( 438 "Optional group identifier provided has incorrect length, generating a random GID..." 439 ); 440 None 441 } else { 442 Some(gid_opt_vec.as_ref()) 443 }; 444 445 // Retrieve the group membership 446 let gide = match mls_platform_api::mls_group_create( 447 &mut pstate, 448 identifier_bytes, 449 credential_bytes, 450 gid_opt_res, 451 None, 452 &Default::default(), 453 ) { 454 Ok(v) => v, 455 Err(e) => { 456 log::error!("{:?}", e); 457 return NS_ERROR_FAILURE; 458 } 459 }; 460 461 // Log the result 462 log::debug!( 463 " (returns) Group Identifier created: {:?}", 464 hex::encode(&gide.group_id) 465 ); 466 467 // Write the result to ret_val 468 *ret_group_id_epoch = gide.into(); 469 470 log::info!("Successfully created group"); 471 NS_OK 472 } 473 474 #[no_mangle] 475 pub unsafe extern "C" fn mls_group_add( 476 storage_prefix: &nsACString, 477 group_id_bytes_ptr: *const u8, 478 group_id_bytes_len: usize, 479 identifier_bytes_ptr: *const u8, 480 identifier_bytes_len: usize, 481 keypackage_bytes_ptr: *const u8, 482 keypackage_bytes_len: usize, 483 ret_commit_output: &mut GkMlsCommitOutput, 484 ) -> nsresult { 485 // Log the function call 486 log::debug!("Entering mls_group_add"); 487 488 // Validate the inputs 489 if group_id_bytes_len == 0 { 490 log::error!("Group Identifier argument cannot be empty"); 491 return NS_ERROR_INVALID_ARG; 492 } 493 if identifier_bytes_len == 0 { 494 log::error!("Identifier argument cannot be empty"); 495 return NS_ERROR_INVALID_ARG; 496 } 497 if keypackage_bytes_len == 0 { 498 log::error!("Key Package argument cannot be empty"); 499 return NS_ERROR_INVALID_ARG; 500 } 501 502 // Convert the raw pointers to slices 503 let group_id_bytes: &[u8] = 504 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 505 let identifier_bytes: &[u8] = 506 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 507 let keypackage_bytes: &[u8] = 508 unsafe { std::slice::from_raw_parts(keypackage_bytes_ptr, keypackage_bytes_len) }; 509 510 // Retrieve the platform state based on the storage prefix 511 let Ok(mut pstate) = state_access(storage_prefix) else { 512 return NS_ERROR_FAILURE; 513 }; 514 515 // Retrieve the key_package from the caller 516 let key_package = match mls_platform_api::MlsMessage::from_bytes(&keypackage_bytes) { 517 Ok(kp) => kp, 518 Err(e) => { 519 log::error!("{:?}", e); 520 return NS_ERROR_INVALID_ARG; 521 } 522 }; 523 524 let Ok(commit_output) = mls_platform_api::mls_group_add( 525 &mut pstate, 526 group_id_bytes, 527 identifier_bytes, 528 vec![key_package], 529 ) else { 530 log::error!("Failed to add client to the group"); 531 return NS_ERROR_FAILURE; 532 }; 533 534 // Log the result 535 log::debug!(" (returns) Commit: {:?}", &commit_output.commit); 536 log::debug!(" (returns) Welcome: {:?}", &commit_output.welcome); 537 log::debug!(" (returns) Identity: {:?}", &commit_output.identity); 538 539 // Write the result 540 *ret_commit_output = commit_output.into(); 541 542 log::info!("Successfully added client to the group"); 543 544 NS_OK 545 } 546 547 #[no_mangle] 548 pub unsafe extern "C" fn mls_group_propose_add( 549 storage_prefix: &nsACString, 550 group_id_bytes_ptr: *const u8, 551 group_id_bytes_len: usize, 552 identifier_bytes_ptr: *const u8, 553 identifier_bytes_len: usize, 554 keypackage_bytes_ptr: *const u8, 555 keypackage_bytes_len: usize, 556 ret_proposal: &mut ThinVec<u8>, 557 ) -> nsresult { 558 // Log the function call 559 log::debug!("Entering mls_group_propose_add"); 560 561 // Validate the inputs 562 if group_id_bytes_len == 0 { 563 log::error!("Group Identifier argument cannot be empty"); 564 return NS_ERROR_INVALID_ARG; 565 } 566 if identifier_bytes_len == 0 { 567 log::error!("Identifier argument cannot be empty"); 568 return NS_ERROR_INVALID_ARG; 569 } 570 if keypackage_bytes_len == 0 { 571 log::error!("Key Package argument cannot be empty"); 572 return NS_ERROR_INVALID_ARG; 573 } 574 575 // Convert the raw pointers to slices 576 let group_id_bytes: &[u8] = 577 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 578 let identifier_bytes: &[u8] = 579 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 580 let keypackage_bytes: &[u8] = 581 unsafe { std::slice::from_raw_parts(keypackage_bytes_ptr, keypackage_bytes_len) }; 582 583 // Retrieve the platform state based on the storage prefix 584 let Ok(mut pstate) = state_access(storage_prefix) else { 585 return NS_ERROR_FAILURE; 586 }; 587 588 // Retrieve the key_package from the caller 589 let key_package = match mls_platform_api::MlsMessage::from_bytes(&keypackage_bytes) { 590 Ok(kp) => kp, 591 Err(e) => { 592 log::error!("{:?}", e); 593 return NS_ERROR_INVALID_ARG; 594 } 595 }; 596 597 // Retrieve the group membership 598 let proposal = match mls_platform_api::mls_group_propose_add( 599 &mut pstate, 600 group_id_bytes, 601 identifier_bytes, 602 key_package, 603 ) { 604 Ok(m) => m, 605 Err(e) => { 606 log::error!("{:?}", e); 607 return NS_ERROR_FAILURE; 608 } 609 }; 610 611 // Log the result 612 log::debug!( 613 " (returns) Add Proposal: {:?}", 614 hex::encode(&proposal.to_bytes().unwrap()) 615 ); 616 617 // Write the result to ret_val 618 ret_proposal.extend_from_slice(&proposal.to_bytes().unwrap()); 619 620 log::info!("Successfully proposed adding client to the group"); 621 NS_OK 622 } 623 624 #[no_mangle] 625 pub unsafe extern "C" fn mls_group_remove( 626 storage_prefix: &nsACString, 627 group_id_bytes_ptr: *const u8, 628 group_id_bytes_len: usize, 629 identifier_bytes_ptr: *const u8, 630 identifier_bytes_len: usize, 631 rem_identifier_bytes_ptr: *const u8, 632 rem_identifier_bytes_len: usize, 633 ret_commit_output: &mut GkMlsCommitOutput, 634 ) -> nsresult { 635 // Log the function call 636 log::debug!("Entering mls_group_remove"); 637 638 // Validate the inputs 639 if group_id_bytes_len == 0 { 640 log::error!("Group Identifier argument cannot be empty"); 641 return NS_ERROR_INVALID_ARG; 642 } 643 if identifier_bytes_len == 0 { 644 log::error!("Identifier argument cannot be empty"); 645 return NS_ERROR_INVALID_ARG; 646 } 647 if rem_identifier_bytes_len == 0 { 648 log::error!("Identifier to remove argument cannot be empty"); 649 return NS_ERROR_INVALID_ARG; 650 } 651 652 // Convert the raw pointers to slices 653 let group_id_bytes: &[u8] = 654 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 655 let identifier_bytes: &[u8] = 656 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 657 let rem_identifier_bytes: &[u8] = 658 unsafe { std::slice::from_raw_parts(rem_identifier_bytes_ptr, rem_identifier_bytes_len) }; 659 660 // Retrieve the platform state based on the storage prefix 661 let Ok(mut pstate) = state_access(storage_prefix) else { 662 return NS_ERROR_FAILURE; 663 }; 664 665 // Retrieve the group membership 666 let commit_output = match mls_platform_api::mls_group_remove( 667 &mut pstate, 668 group_id_bytes, 669 identifier_bytes, 670 rem_identifier_bytes, 671 ) { 672 Ok(gid) => gid, 673 Err(e) => { 674 log::error!("{:?}", e); 675 return NS_ERROR_FAILURE; 676 } 677 }; 678 679 // Log the result 680 log::debug!(" (returns) Commit: {:?}", &commit_output.commit); 681 log::debug!(" (returns) Welcome: {:?}", &commit_output.welcome); 682 log::debug!(" (returns) Identity: {:?}", &commit_output.identity); 683 684 // Write the result 685 *ret_commit_output = commit_output.into(); 686 687 log::info!("Successfully removed client from the group"); 688 689 NS_OK 690 } 691 692 #[no_mangle] 693 pub unsafe extern "C" fn mls_group_propose_remove( 694 storage_prefix: &nsACString, 695 group_id_bytes_ptr: *const u8, 696 group_id_bytes_len: usize, 697 identifier_bytes_ptr: *const u8, 698 identifier_bytes_len: usize, 699 rem_identifier_bytes_ptr: *const u8, 700 rem_identifier_bytes_len: usize, 701 ret_proposal: &mut ThinVec<u8>, 702 ) -> nsresult { 703 // Log the function call 704 log::info!("Entering mls_group_propose_remove"); 705 706 // Validate the inputs 707 if group_id_bytes_len == 0 { 708 log::error!("Group Identifier argument cannot be empty"); 709 return NS_ERROR_INVALID_ARG; 710 } 711 if identifier_bytes_len == 0 { 712 log::error!("Identifier argument cannot be empty"); 713 return NS_ERROR_INVALID_ARG; 714 } 715 if rem_identifier_bytes_len == 0 { 716 log::error!("Identifier to remove argument cannot be empty"); 717 return NS_ERROR_INVALID_ARG; 718 } 719 720 // Convert the raw pointers to slices 721 let group_id_bytes: &[u8] = 722 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 723 let identifier_bytes: &[u8] = 724 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 725 let rem_identifier_bytes: &[u8] = 726 unsafe { std::slice::from_raw_parts(rem_identifier_bytes_ptr, rem_identifier_bytes_len) }; 727 728 // Retrieve the platform state based on the storage prefix 729 let Ok(mut pstate) = state_access(storage_prefix) else { 730 return NS_ERROR_FAILURE; 731 }; 732 733 // Retrieve the group membership 734 let proposal = match mls_platform_api::mls_group_propose_remove( 735 &mut pstate, 736 group_id_bytes, 737 identifier_bytes, 738 rem_identifier_bytes, 739 ) { 740 Ok(gid) => gid, 741 Err(e) => { 742 log::error!("{:?}", e); 743 return NS_ERROR_FAILURE; 744 } 745 }; 746 747 let proposal_bytes = match proposal.to_bytes() { 748 Ok(gid) => gid, 749 Err(e) => { 750 log::error!("{:?}", e); 751 return NS_ERROR_FAILURE; 752 } 753 }; 754 755 // Log the result 756 log::info!( 757 " (returns) Remove Proposal: {:?}", 758 hex::encode(&proposal_bytes) 759 ); 760 761 // Write the result 762 ret_proposal.extend_from_slice(&proposal_bytes); 763 764 log::info!("Successfully proposed removing client from the group"); 765 766 NS_OK 767 } 768 769 #[no_mangle] 770 pub unsafe extern "C" fn mls_group_join( 771 storage_prefix: &nsACString, 772 identifier_bytes_ptr: *const u8, 773 identifier_bytes_len: usize, 774 welcome_bytes_ptr: *const u8, 775 welcome_bytes_len: usize, 776 ret_group_id_epoch: &mut GkGroupIdEpoch, 777 ) -> nsresult { 778 // Log the function call 779 log::debug!("Entering mls_group_join"); 780 781 // Validate the inputs 782 if identifier_bytes_len == 0 { 783 log::error!("Identifier argument cannot be empty"); 784 return NS_ERROR_INVALID_ARG; 785 } 786 if welcome_bytes_len == 0 { 787 log::error!("Welcome message argument cannot be empty"); 788 return NS_ERROR_INVALID_ARG; 789 } 790 791 // Convert the raw pointers to slices 792 let identifier_bytes: &[u8] = 793 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 794 let welcome_bytes: &[u8] = 795 unsafe { std::slice::from_raw_parts(welcome_bytes_ptr, welcome_bytes_len) }; 796 797 // Retrieve the platform state based on the storage prefix 798 let Ok(pstate) = state_access(storage_prefix) else { 799 return NS_ERROR_FAILURE; 800 }; 801 802 // Retrieve the welcome message from the caller 803 let welcome = match mls_platform_api::MlsMessage::from_bytes(&welcome_bytes) { 804 Ok(kp) => kp, 805 Err(e) => { 806 log::error!("{:?}", e); 807 return NS_ERROR_INVALID_ARG; 808 } 809 }; 810 811 // Retrieve the group membership 812 let gide = match mls_platform_api::mls_group_join(&pstate, identifier_bytes, &welcome, None) { 813 Ok(gid) => gid, 814 Err(e) => { 815 log::error!("{:?}", e); 816 return NS_ERROR_FAILURE; 817 } 818 }; 819 820 // Log the result 821 log::debug!( 822 " (returns) Group Identifier joined: {:?}", 823 hex::encode(&gide.group_id) 824 ); 825 826 // Write the result to ret_val 827 *ret_group_id_epoch = gide.into(); 828 829 log::info!("Successfully joined group"); 830 NS_OK 831 } 832 833 #[no_mangle] 834 pub unsafe extern "C" fn mls_group_close( 835 storage_prefix: &nsACString, 836 group_id_bytes_ptr: *const u8, 837 group_id_bytes_len: usize, 838 identifier_bytes_ptr: *const u8, 839 identifier_bytes_len: usize, 840 ret_commit_output: &mut GkMlsCommitOutput, 841 ) -> nsresult { 842 // Log the function call 843 log::debug!("Entering mls_group_close"); 844 845 // Validate the inputs 846 if group_id_bytes_len == 0 { 847 log::error!("Group Identifier argument cannot be empty"); 848 return NS_ERROR_INVALID_ARG; 849 } 850 if identifier_bytes_len == 0 { 851 log::error!("Identifier argument cannot be empty"); 852 return NS_ERROR_INVALID_ARG; 853 } 854 855 // Convert the raw pointers to slices 856 let group_id_bytes: &[u8] = 857 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 858 let identifier_bytes: &[u8] = 859 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 860 861 // Retrieve the platform state based on the storage prefix 862 let Ok(mut pstate) = state_access(storage_prefix) else { 863 return NS_ERROR_FAILURE; 864 }; 865 866 // Retrieve the commit output 867 let commit_output = 868 match mls_platform_api::mls_group_close(&mut pstate, group_id_bytes, identifier_bytes) { 869 Ok(gid) => gid, 870 Err(e) => { 871 log::error!("{:?}", e); 872 return NS_ERROR_FAILURE; 873 } 874 }; 875 876 // Log the result 877 log::debug!(" (returns) Commit: {:?}", &commit_output.commit); 878 log::debug!(" (returns) Welcome: {:?}", &commit_output.welcome); 879 log::debug!(" (returns) Identity: {:?}", &commit_output.identity); 880 881 // Write the result 882 *ret_commit_output = commit_output.into(); 883 884 log::info!("Successfully closed group"); 885 886 NS_OK 887 } 888 889 #[repr(C)] 890 pub struct GkMlsCommitOutput { 891 pub commit: ThinVec<u8>, 892 pub welcome: ThinVec<u8>, 893 pub group_info: ThinVec<u8>, 894 pub ratchet_tree: ThinVec<u8>, 895 pub identity: ThinVec<u8>, 896 } 897 898 impl From<MlsCommitOutput> for GkMlsCommitOutput { 899 fn from(v: MlsCommitOutput) -> Self { 900 let MlsCommitOutput { 901 commit, 902 welcome, 903 group_info, 904 ratchet_tree, 905 identity, 906 } = v; 907 Self { 908 commit: commit.to_bytes().unwrap_or_default().into(), 909 welcome: welcome 910 .first() 911 .and_then(|f| f.to_bytes().ok()) 912 .map_or(ThinVec::new(), |b| b.into()), 913 group_info: group_info 914 .and_then(|gi| gi.to_bytes().ok()) 915 .map_or(ThinVec::new(), |b| b.into()), 916 ratchet_tree: ratchet_tree.unwrap_or_default().into(), 917 identity: identity.unwrap_or_default().into(), 918 } 919 } 920 } 921 922 #[repr(C)] 923 /// cbindgen:derive-constructor=false 924 /// cbindgen:derive-tagged-enum-copy-constructor=false 925 /// cbindgen:derive-tagged-enum-copy-assignment=false 926 pub enum GkReceived { 927 None, 928 ApplicationMessage(ThinVec<u8>), 929 GroupIdEpoch(GkGroupIdEpoch), 930 CommitOutput(GkMlsCommitOutput), 931 } 932 933 #[no_mangle] 934 pub unsafe extern "C" fn mls_receive( 935 storage_prefix: &nsACString, 936 identifier_bytes_ptr: *const u8, 937 identifier_bytes_len: usize, 938 message_bytes_ptr: *const u8, 939 message_bytes_len: usize, 940 ret_group_id: &mut ThinVec<u8>, 941 ret_received: &mut GkReceived, 942 ) -> nsresult { 943 // Log the function call 944 log::debug!("Entering mls_receive"); 945 946 // Validate the inputs 947 if identifier_bytes_len == 0 { 948 log::error!("Identifier argument cannot be empty"); 949 return NS_ERROR_INVALID_ARG; 950 } 951 if message_bytes_len == 0 { 952 log::error!("Message argument cannot be empty"); 953 return NS_ERROR_INVALID_ARG; 954 } 955 956 // Convert the raw pointers to slices 957 let identifier_bytes: &[u8] = 958 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 959 let message_bytes: &[u8] = 960 unsafe { std::slice::from_raw_parts(message_bytes_ptr, message_bytes_len) }; 961 962 // Retrieve the platform state based on the storage prefix 963 let Ok(pstate) = state_access(storage_prefix) else { 964 return NS_ERROR_FAILURE; 965 }; 966 967 // Retrieve the message from the caller 968 let message = match mls_platform_api::MlsMessage::from_bytes(&message_bytes) { 969 Ok(kp) => kp, 970 Err(e) => { 971 log::error!("{:?}", e); 972 return NS_ERROR_INVALID_ARG; 973 } 974 }; 975 976 // Retrieve the received output and the group identifier 977 let (gid, received) = match mls_platform_api::mls_receive( 978 &pstate, 979 identifier_bytes, 980 &mls_platform_api::MessageOrAck::MlsMessage(message), 981 ) { 982 Ok(recv) => recv, 983 Err(e) => { 984 log::error!("Failed to receive message: {:?}", e); 985 return NS_ERROR_FAILURE; 986 } 987 }; 988 989 // Log the result 990 log::debug!(" (returns) Group Identifier: {:?}", hex::encode(&gid)); 991 992 // Write the group id to ret_group_id 993 ret_group_id.extend_from_slice(&gid); 994 *ret_received = match received { 995 Received::ApplicationMessage(message) => { 996 log::info!("Received an ApplicationMessage"); 997 log::debug!( 998 " (returns) Received Application Message: {:?}", 999 hex::encode(&message) 1000 ); 1001 GkReceived::ApplicationMessage(message.into()) 1002 } 1003 Received::GroupIdEpoch(epoch) => { 1004 log::info!("Received a GroupIdEpoch"); 1005 log::debug!(" (returns) Received GroupIdEpoch: {:?}", epoch); 1006 GkReceived::GroupIdEpoch(epoch.into()) 1007 } 1008 Received::CommitOutput(commit_output) => { 1009 log::info!("Received a CommitOutput"); 1010 log::debug!(" (returns) Received CommitOutput: {:?}", commit_output); 1011 GkReceived::CommitOutput(commit_output.into()) 1012 } 1013 Received::None => { 1014 log::info!("Received None"); 1015 GkReceived::None 1016 } 1017 }; 1018 1019 log::info!("Successfully received message"); 1020 1021 NS_OK 1022 } 1023 1024 #[no_mangle] 1025 pub unsafe extern "C" fn mls_has_pending_proposals( 1026 storage_prefix: &nsACString, 1027 group_id_bytes_ptr: *const u8, 1028 group_id_bytes_len: usize, 1029 identifier_bytes_ptr: *const u8, 1030 identifier_bytes_len: usize, 1031 ret_has_pending_proposals: &mut bool, 1032 ) -> nsresult { 1033 // Log the function call 1034 log::debug!("Entering mls_has_pending_proposals"); 1035 1036 // Validate the inputs 1037 if group_id_bytes_len == 0 { 1038 log::error!("Group Identifier argument cannot be empty"); 1039 return NS_ERROR_INVALID_ARG; 1040 } 1041 if identifier_bytes_len == 0 { 1042 log::error!("Identifier argument cannot be empty"); 1043 return NS_ERROR_INVALID_ARG; 1044 } 1045 1046 // Convert the raw pointers to slices 1047 let group_id_bytes: &[u8] = 1048 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 1049 let identifier_bytes: &[u8] = 1050 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 1051 1052 // Retrieve the platform state based on the storage prefix 1053 let Ok(pstate) = state_access(storage_prefix) else { 1054 return NS_ERROR_FAILURE; 1055 }; 1056 1057 // Retrieve whether there are pending proposals 1058 let has_pending_proposals = match mls_platform_api::mls_has_pending_proposals( 1059 &pstate, 1060 group_id_bytes, 1061 identifier_bytes, 1062 ) { 1063 Ok(result) => result, 1064 Err(e) => { 1065 log::error!("{:?}", e); 1066 return NS_ERROR_FAILURE; 1067 } 1068 }; 1069 1070 // Write whether there are pending proposals to ret_has_pending_proposals 1071 *ret_has_pending_proposals = has_pending_proposals; 1072 1073 log::info!("Successfully retrieved has pending proposals"); 1074 NS_OK 1075 } 1076 1077 #[no_mangle] 1078 pub unsafe extern "C" fn mls_clear_pending_proposals( 1079 storage_prefix: &nsACString, 1080 group_id_bytes_ptr: *const u8, 1081 group_id_bytes_len: usize, 1082 identifier_bytes_ptr: *const u8, 1083 identifier_bytes_len: usize, 1084 ret_cleared_pending_proposals: &mut bool, 1085 ) -> nsresult { 1086 // Log the function call 1087 log::debug!("Entering mls_clear_pending_proposals"); 1088 1089 // Validate the inputs 1090 if group_id_bytes_len == 0 { 1091 log::error!("Group Identifier argument cannot be empty"); 1092 return NS_ERROR_INVALID_ARG; 1093 } 1094 if identifier_bytes_len == 0 { 1095 log::error!("Identifier argument cannot be empty"); 1096 return NS_ERROR_INVALID_ARG; 1097 } 1098 1099 // Convert the raw pointers to slices 1100 let group_id_bytes: &[u8] = 1101 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 1102 let identifier_bytes: &[u8] = 1103 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 1104 1105 // Retrieve the platform state based on the storage prefix 1106 let Ok(pstate) = state_access(storage_prefix) else { 1107 return NS_ERROR_FAILURE; 1108 }; 1109 1110 // Retrieve the result of clearing pending proposals 1111 let has_cleared_pending_proposals = match mls_platform_api::mls_clear_pending_proposals( 1112 &pstate, 1113 group_id_bytes, 1114 identifier_bytes, 1115 ) { 1116 Ok(result) => result, 1117 Err(e) => { 1118 log::error!("{:?}", e); 1119 return NS_ERROR_FAILURE; 1120 } 1121 }; 1122 1123 // Write the result of clearing pending proposals to ret_cleared_pending_proposals 1124 *ret_cleared_pending_proposals = has_cleared_pending_proposals; 1125 1126 log::info!("Successfully cleared pending proposals"); 1127 NS_OK 1128 } 1129 1130 #[no_mangle] 1131 pub unsafe extern "C" fn mls_has_pending_commit( 1132 storage_prefix: &nsACString, 1133 group_id_bytes_ptr: *const u8, 1134 group_id_bytes_len: usize, 1135 identifier_bytes_ptr: *const u8, 1136 identifier_bytes_len: usize, 1137 ret_has_pending_commit: &mut bool, 1138 ) -> nsresult { 1139 // Log the function call 1140 log::debug!("Entering mls_has_pending_commit"); 1141 1142 // Validate the inputs 1143 if group_id_bytes_len == 0 { 1144 log::error!("Group Identifier argument cannot be empty"); 1145 return NS_ERROR_INVALID_ARG; 1146 } 1147 if identifier_bytes_len == 0 { 1148 log::error!("Identifier argument cannot be empty"); 1149 return NS_ERROR_INVALID_ARG; 1150 } 1151 1152 // Convert the raw pointers to slices 1153 let group_id_bytes: &[u8] = 1154 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 1155 let identifier_bytes: &[u8] = 1156 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 1157 1158 // Retrieve the platform state based on the storage prefix 1159 let Ok(pstate) = state_access(storage_prefix) else { 1160 return NS_ERROR_FAILURE; 1161 }; 1162 1163 // Retrieve whether there is pending commit 1164 let has_pending_commit = 1165 match mls_platform_api::mls_has_pending_commit(&pstate, group_id_bytes, identifier_bytes) { 1166 Ok(result) => result, 1167 Err(e) => { 1168 log::error!("{:?}", e); 1169 return NS_ERROR_FAILURE; 1170 } 1171 }; 1172 1173 // Write whether there is pending commit to ret_has_pending_commit 1174 *ret_has_pending_commit = has_pending_commit; 1175 1176 log::info!("Successfully retrieved has pending commit"); 1177 NS_OK 1178 } 1179 1180 #[no_mangle] 1181 pub unsafe extern "C" fn mls_clear_pending_commit( 1182 storage_prefix: &nsACString, 1183 group_id_bytes_ptr: *const u8, 1184 group_id_bytes_len: usize, 1185 identifier_bytes_ptr: *const u8, 1186 identifier_bytes_len: usize, 1187 ret_cleared_pending_commit: &mut bool, 1188 ) -> nsresult { 1189 // Log the function call 1190 log::debug!("Entering mls_clear_pending_commit"); 1191 1192 // Validate the inputs 1193 if group_id_bytes_len == 0 { 1194 log::error!("Group Identifier argument cannot be empty"); 1195 return NS_ERROR_INVALID_ARG; 1196 } 1197 if identifier_bytes_len == 0 { 1198 log::error!("Identifier argument cannot be empty"); 1199 return NS_ERROR_INVALID_ARG; 1200 } 1201 1202 // Convert the raw pointers to slices 1203 let group_id_bytes: &[u8] = 1204 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 1205 let identifier_bytes: &[u8] = 1206 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 1207 1208 // Retrieve the platform state based on the storage prefix 1209 let Ok(pstate) = state_access(storage_prefix) else { 1210 return NS_ERROR_FAILURE; 1211 }; 1212 1213 // Retrieve the result of clearing the pending commit 1214 let has_cleared_pending_commit = 1215 match mls_platform_api::mls_clear_pending_commit(&pstate, group_id_bytes, identifier_bytes) 1216 { 1217 Ok(result) => result, 1218 Err(e) => { 1219 log::error!("{:?}", e); 1220 return NS_ERROR_FAILURE; 1221 } 1222 }; 1223 1224 // Write the result of clearing the pending commit to ret_has_pending_proposals 1225 *ret_cleared_pending_commit = has_cleared_pending_commit; 1226 1227 log::info!("Successfully cleared pending commit"); 1228 NS_OK 1229 } 1230 1231 #[no_mangle] 1232 pub unsafe extern "C" fn mls_apply_pending_commit( 1233 storage_prefix: &nsACString, 1234 group_id_bytes_ptr: *const u8, 1235 group_id_bytes_len: usize, 1236 identifier_bytes_ptr: *const u8, 1237 identifier_bytes_len: usize, 1238 ret_received: &mut GkReceived, 1239 ) -> nsresult { 1240 // Log the function call 1241 log::debug!("Entering mls_apply_pending_commit"); 1242 1243 // Validate the inputs 1244 if group_id_bytes_len == 0 { 1245 log::error!("Group Identifier argument cannot be empty"); 1246 return NS_ERROR_INVALID_ARG; 1247 } 1248 if identifier_bytes_len == 0 { 1249 log::error!("Identifier argument cannot be empty"); 1250 return NS_ERROR_INVALID_ARG; 1251 } 1252 1253 // Convert the raw pointers to slices 1254 let group_id_bytes: &[u8] = 1255 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 1256 let identifier_bytes: &[u8] = 1257 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 1258 1259 // Retrieve the platform state based on the storage prefix 1260 let Ok(pstate) = state_access(storage_prefix) else { 1261 return NS_ERROR_FAILURE; 1262 }; 1263 1264 // Retrieve the received output and the group identifier 1265 let (gid, received) = match mls_platform_api::mls_receive( 1266 &pstate, 1267 identifier_bytes, 1268 &mls_platform_api::MessageOrAck::Ack(group_id_bytes.to_vec()), 1269 ) { 1270 Ok(recv) => recv, 1271 Err(e) => { 1272 log::error!("{:?}", e); 1273 return NS_ERROR_FAILURE; 1274 } 1275 }; 1276 1277 // Log the result 1278 log::debug!(" (returns) Group Identifier: {:?}", hex::encode(&gid)); 1279 1280 // Write the group id to ret_group_id 1281 *ret_received = match received { 1282 Received::GroupIdEpoch(epoch) => { 1283 log::info!("Received a GroupIdEpoch"); 1284 log::debug!(" (returns) Received GroupIdEpoch: {:?}", epoch); 1285 GkReceived::GroupIdEpoch(epoch.into()) 1286 } 1287 _ => { 1288 log::info!("Unexpected received type for mls_receive_ack"); 1289 GkReceived::None 1290 } 1291 }; 1292 1293 log::info!("Successfully received ack message"); 1294 1295 NS_OK 1296 } 1297 1298 #[no_mangle] 1299 pub unsafe extern "C" fn mls_send( 1300 storage_prefix: &nsACString, 1301 group_id_bytes_ptr: *const u8, 1302 group_id_bytes_len: usize, 1303 identifier_bytes_ptr: *const u8, 1304 identifier_bytes_len: usize, 1305 message_bytes_ptr: *const u8, 1306 message_bytes_len: usize, 1307 ret_encrypted: &mut ThinVec<u8>, 1308 ) -> nsresult { 1309 // Log the function call 1310 log::debug!("Entering mls_send"); 1311 1312 // Validate the inputs 1313 if group_id_bytes_len == 0 { 1314 log::error!("Group Identifier argument cannot be empty"); 1315 return NS_ERROR_INVALID_ARG; 1316 } 1317 if identifier_bytes_len == 0 { 1318 log::error!("Identifier argument cannot be empty"); 1319 return NS_ERROR_INVALID_ARG; 1320 } 1321 // Note: We allow empty messages as they could be used as control 1322 1323 // Convert the raw pointers to slices 1324 let group_id_bytes: &[u8] = 1325 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 1326 let identifier_bytes: &[u8] = 1327 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 1328 let message_bytes: &[u8] = 1329 unsafe { std::slice::from_raw_parts(message_bytes_ptr, message_bytes_len) }; 1330 1331 // Retrieve the platform state based on the storage prefix 1332 let Ok(pstate) = state_access(storage_prefix) else { 1333 return NS_ERROR_FAILURE; 1334 }; 1335 1336 // Retrieve the ciphertext 1337 let ciphertext = match mls_platform_api::mls_send( 1338 &pstate, 1339 group_id_bytes, 1340 identifier_bytes, 1341 message_bytes, 1342 ) { 1343 Ok(ctx) => ctx, 1344 Err(e) => { 1345 log::error!("{:?}", e); 1346 return NS_ERROR_FAILURE; 1347 } 1348 }; 1349 1350 // Retrieve the message from the caller 1351 let ciphertext_bytes = match mls_platform_api::MlsMessage::to_bytes(&ciphertext) { 1352 Ok(ctx) => ctx, 1353 Err(e) => { 1354 log::error!("{:?}", e); 1355 return NS_ERROR_FAILURE; 1356 } 1357 }; 1358 1359 // Log the result 1360 log::debug!(" (input) Message: {:?}", hex::encode(&message_bytes)); 1361 log::debug!( 1362 " (returns) Ciphertext: {:?}", 1363 hex::encode(&ciphertext_bytes) 1364 ); 1365 1366 // Write the result to ret_val 1367 ret_encrypted.extend_from_slice(&ciphertext_bytes); 1368 1369 log::info!("Successfully encrypted message"); 1370 NS_OK 1371 } 1372 1373 #[repr(C)] 1374 pub struct GkExporterOutput { 1375 pub group_id: ThinVec<u8>, 1376 pub group_epoch: ThinVec<u8>, 1377 pub label: ThinVec<u8>, 1378 pub context: ThinVec<u8>, 1379 pub exporter: ThinVec<u8>, 1380 } 1381 1382 impl From<ExporterOutput> for GkExporterOutput { 1383 fn from(v: ExporterOutput) -> Self { 1384 let ExporterOutput { 1385 group_id, 1386 group_epoch, 1387 label, 1388 context, 1389 exporter, 1390 } = v; 1391 Self { 1392 group_id: group_id.into(), 1393 group_epoch: ThinVec::from(group_epoch.to_le_bytes()), 1394 label: label.into(), 1395 context: context.into(), 1396 exporter: exporter.into(), 1397 } 1398 } 1399 } 1400 1401 #[no_mangle] 1402 pub unsafe extern "C" fn mls_derive_exporter( 1403 storage_prefix: &nsACString, 1404 group_id_bytes_ptr: *const u8, 1405 group_id_bytes_len: usize, 1406 identifier_bytes_ptr: *const u8, 1407 identifier_bytes_len: usize, 1408 label_bytes_ptr: *const u8, 1409 label_bytes_len: usize, 1410 context_bytes_ptr: *const u8, 1411 context_bytes_len: usize, 1412 len: u64, 1413 ret_exporter_output: &mut GkExporterOutput, 1414 ) -> nsresult { 1415 // Log the function call 1416 log::debug!("Entering mls_derive_exporter"); 1417 1418 // Validate the inputs 1419 if group_id_bytes_len == 0 { 1420 log::error!("Group Identifier argument cannot be empty"); 1421 return NS_ERROR_INVALID_ARG; 1422 } 1423 if identifier_bytes_len == 0 { 1424 log::error!("Identifier argument cannot be empty"); 1425 return NS_ERROR_INVALID_ARG; 1426 } 1427 if label_bytes_len == 0 { 1428 log::error!("Label argument cannot be empty"); 1429 return NS_ERROR_INVALID_ARG; 1430 } 1431 if len == 0 { 1432 log::error!("Length argument cannot be zero"); 1433 return NS_ERROR_INVALID_ARG; 1434 } 1435 1436 // Convert the raw pointers to slices 1437 let group_id_bytes: &[u8] = 1438 unsafe { std::slice::from_raw_parts(group_id_bytes_ptr, group_id_bytes_len) }; 1439 let identifier_bytes: &[u8] = 1440 unsafe { std::slice::from_raw_parts(identifier_bytes_ptr, identifier_bytes_len) }; 1441 let label_bytes: &[u8] = 1442 unsafe { std::slice::from_raw_parts(label_bytes_ptr, label_bytes_len) }; 1443 let context_bytes: &[u8] = 1444 unsafe { std::slice::from_raw_parts(context_bytes_ptr, context_bytes_len) }; 1445 1446 // Retrieve the platform state based on the storage prefix 1447 let Ok(pstate) = state_access(storage_prefix) else { 1448 return NS_ERROR_FAILURE; 1449 }; 1450 1451 // Retrieve the exporter output 1452 let exporter_output = match mls_platform_api::mls_derive_exporter( 1453 &pstate, 1454 group_id_bytes, 1455 identifier_bytes, 1456 label_bytes, 1457 context_bytes, 1458 len, 1459 ) { 1460 Ok(exp) => exp, 1461 Err(e) => { 1462 log::error!("{:?}", e); 1463 return NS_ERROR_FAILURE; 1464 } 1465 }; 1466 1467 log::debug!( 1468 " (returns) Exporter: {:?}", 1469 hex::encode(&exporter_output.exporter) 1470 ); 1471 1472 // Handle group identifier 1473 *ret_exporter_output = exporter_output.into(); 1474 1475 log::info!("Successfully derived exporter"); 1476 NS_OK 1477 } 1478 1479 #[no_mangle] 1480 pub unsafe extern "C" fn mls_get_group_id( 1481 message_bytes_ptr: *const u8, 1482 message_bytes_len: usize, 1483 ret_group_id: &mut ThinVec<u8>, 1484 ) -> nsresult { 1485 // Log the function call 1486 log::debug!("Entering mls_get_group_id"); 1487 1488 // Validate the inputs 1489 if message_bytes_len == 0 { 1490 log::error!("Message argument cannot be empty"); 1491 return NS_ERROR_INVALID_ARG; 1492 } 1493 1494 let message_bytes: &[u8] = 1495 unsafe { std::slice::from_raw_parts(message_bytes_ptr, message_bytes_len) }; 1496 1497 // Retrieve the message from the caller 1498 let message = match mls_platform_api::MlsMessage::from_bytes(&message_bytes) { 1499 Ok(kp) => kp, 1500 Err(e) => { 1501 log::error!("{:?}", e); 1502 return NS_ERROR_INVALID_ARG; 1503 } 1504 }; 1505 1506 // Retrieve the group identifier 1507 let gid = match mls_platform_api::mls_get_group_id(&mls_platform_api::MessageOrAck::MlsMessage( 1508 message, 1509 )) { 1510 Ok(recv) => recv, 1511 Err(e) => { 1512 log::error!("{:?}", e); 1513 return NS_ERROR_FAILURE; 1514 } 1515 }; 1516 1517 // Write the group id to ret_group_id 1518 ret_group_id.extend_from_slice(&gid); 1519 1520 // Log the result 1521 log::debug!(" (returns) Group Identifier: {:?}", hex::encode(&gid)); 1522 log::info!("Successfully retrieved group id"); 1523 1524 NS_OK 1525 } 1526 1527 #[no_mangle] 1528 pub unsafe extern "C" fn mls_get_group_epoch( 1529 message_bytes_ptr: *const u8, 1530 message_bytes_len: usize, 1531 ret_group_epoch: &mut ThinVec<u8>, 1532 ) -> nsresult { 1533 // Log the function call 1534 log::debug!("Entering mls_get_group_epoch"); 1535 1536 // Validate the inputs 1537 if message_bytes_len == 0 { 1538 log::error!("Message argument cannot be empty"); 1539 return NS_ERROR_INVALID_ARG; 1540 } 1541 1542 let message_bytes: &[u8] = 1543 unsafe { std::slice::from_raw_parts(message_bytes_ptr, message_bytes_len) }; 1544 1545 // Retrieve the message from the caller 1546 let message = match mls_platform_api::MlsMessage::from_bytes(&message_bytes) { 1547 Ok(kp) => kp, 1548 Err(e) => { 1549 log::error!("{:?}", e); 1550 return NS_ERROR_INVALID_ARG; 1551 } 1552 }; 1553 1554 // Retrieve the group epoch 1555 let group_epoch = match mls_platform_api::mls_get_group_epoch( 1556 &mls_platform_api::MessageOrAck::MlsMessage(message), 1557 ) { 1558 Ok(epoch) => epoch, 1559 Err(e) => { 1560 log::error!("{:?}", e); 1561 return NS_ERROR_FAILURE; 1562 } 1563 }; 1564 1565 // Write the group epoch to ret_group_epoch 1566 // Convert the u64 group_epoch to little-endian bytes 1567 let epoch_bytes = group_epoch.to_le_bytes(); 1568 ret_group_epoch.extend_from_slice(&epoch_bytes); 1569 1570 log::info!(" (returns) Group Epoch: {:?}", group_epoch); 1571 log::info!("Successfully retrieved group epoch"); 1572 NS_OK 1573 }