|
1 | 1 | //! Nostr MLS Key Packages |
2 | 2 |
|
3 | 3 | use mdk_storage_traits::MdkStorageProvider; |
| 4 | +use mdk_storage_traits::mls_codec::JsonCodec; |
4 | 5 | use nostr::{Event, Kind, PublicKey, RelayUrl, Tag, TagKind}; |
| 6 | +use openmls::ciphersuite::hash_ref::HashReference; |
5 | 7 | use openmls::key_packages::KeyPackage; |
6 | 8 | use openmls::prelude::*; |
7 | 9 | use openmls_basic_credential::SignatureKeyPair; |
@@ -508,6 +510,36 @@ where |
508 | 510 | Ok(()) |
509 | 511 | } |
510 | 512 |
|
| 513 | + /// Computes and serializes the hash_ref for a key package. |
| 514 | + /// |
| 515 | + /// Returns the hash_ref as serialized bytes that can be stored externally |
| 516 | + /// and later used with [`delete_key_package_from_storage_by_hash_ref`](Self::delete_key_package_from_storage_by_hash_ref) |
| 517 | + /// to delete the key package from storage. |
| 518 | + pub fn compute_key_package_hash_ref(&self, key_package: &KeyPackage) -> Result<Vec<u8>, Error> { |
| 519 | + let hash_ref = key_package.hash_ref(self.provider.crypto())?; |
| 520 | + JsonCodec::serialize(&hash_ref) |
| 521 | + .map_err(|e| Error::Provider(format!("Failed to serialize hash_ref: {}", e))) |
| 522 | + } |
| 523 | + |
| 524 | + /// Deletes a key package from storage using previously serialized hash_ref bytes. |
| 525 | + /// |
| 526 | + /// The `hash_ref_bytes` should be bytes previously returned by |
| 527 | + /// [`compute_key_package_hash_ref`](Self::compute_key_package_hash_ref). |
| 528 | + pub fn delete_key_package_from_storage_by_hash_ref( |
| 529 | + &self, |
| 530 | + hash_ref_bytes: &[u8], |
| 531 | + ) -> Result<(), Error> { |
| 532 | + let hash_ref: HashReference = JsonCodec::deserialize(hash_ref_bytes) |
| 533 | + .map_err(|e| Error::Provider(format!("Failed to deserialize hash_ref: {}", e)))?; |
| 534 | + |
| 535 | + self.provider |
| 536 | + .storage() |
| 537 | + .delete_key_package(&hash_ref) |
| 538 | + .map_err(|e| Error::Provider(e.to_string()))?; |
| 539 | + |
| 540 | + Ok(()) |
| 541 | + } |
| 542 | + |
511 | 543 | /// Generates a credential with a key for MLS (Messaging Layer Security) operations. |
512 | 544 | /// |
513 | 545 | /// This function creates a new credential and associated signature key pair for use in MLS. |
@@ -943,6 +975,46 @@ mod tests { |
943 | 975 | .expect("Failed to delete key package"); |
944 | 976 | } |
945 | 977 |
|
| 978 | + #[test] |
| 979 | + fn test_key_package_deletion_by_hash_ref_roundtrip() { |
| 980 | + let mdk = create_test_mdk(); |
| 981 | + let test_pubkey = |
| 982 | + PublicKey::from_hex("884704bd421671e01c13f854d2ce23ce2a5bfe9562f4f297ad2bc921ba30c3a6") |
| 983 | + .unwrap(); |
| 984 | + |
| 985 | + let relays = vec![RelayUrl::parse("wss://relay.example.com").unwrap()]; |
| 986 | + |
| 987 | + // Create and parse key package |
| 988 | + let (key_package_str, _) = mdk |
| 989 | + .create_key_package_for_event(&test_pubkey, relays.clone()) |
| 990 | + .expect("Failed to create key package"); |
| 991 | + |
| 992 | + let deletion_mdk = create_test_mdk(); |
| 993 | + let key_package = deletion_mdk |
| 994 | + .parse_serialized_key_package(&key_package_str, ContentEncoding::Base64) |
| 995 | + .expect("Failed to parse key package"); |
| 996 | + |
| 997 | + // Compute hash_ref bytes (simulates what whitenoise-rs caches in DB) |
| 998 | + let hash_ref_bytes = deletion_mdk |
| 999 | + .compute_key_package_hash_ref(&key_package) |
| 1000 | + .expect("Failed to compute hash_ref"); |
| 1001 | + |
| 1002 | + assert!( |
| 1003 | + !hash_ref_bytes.is_empty(), |
| 1004 | + "hash_ref bytes should not be empty" |
| 1005 | + ); |
| 1006 | + |
| 1007 | + // Delete using the serialized hash_ref bytes (simulates delayed cleanup) |
| 1008 | + deletion_mdk |
| 1009 | + .delete_key_package_from_storage_by_hash_ref(&hash_ref_bytes) |
| 1010 | + .expect("Failed to delete key package by hash_ref"); |
| 1011 | + |
| 1012 | + // Deleting again should also succeed (idempotent, no-op) |
| 1013 | + deletion_mdk |
| 1014 | + .delete_key_package_from_storage_by_hash_ref(&hash_ref_bytes) |
| 1015 | + .expect("Second deletion should succeed (idempotent)"); |
| 1016 | + } |
| 1017 | + |
946 | 1018 | #[test] |
947 | 1019 | fn test_invalid_key_package_parsing() { |
948 | 1020 | let mdk = create_test_mdk(); |
|
0 commit comments