@@ -3,10 +3,10 @@ use km_common::crypto::PublicKey;
33use km_common:: key_types:: { KeyRecord , KeyRegistry , KeySpec } ;
44use prost:: Message ;
55use std:: slice;
6+ use std:: sync:: atomic:: AtomicBool ;
67use std:: sync:: Arc ;
78use std:: sync:: LazyLock ;
8- use std:: sync:: atomic:: AtomicBool ;
9- use std:: time:: Duration ;
9+ use std:: time:: { Duration , Instant } ;
1010use uuid:: Uuid ;
1111
1212static KEY_REGISTRY : LazyLock < KeyRegistry > = LazyLock :: new ( || {
@@ -196,6 +196,125 @@ fn decap_and_seal_internal(
196196 }
197197}
198198
199+ pub const MAX_ALGORITHM_LEN : usize = 128 ;
200+ pub const MAX_PUBLIC_KEY_LEN : usize = 2048 ;
201+
202+ #[ repr( C ) ]
203+ pub struct KpsKeyInfo {
204+ pub uuid : [ u8 ; 16 ] ,
205+ pub algorithm : [ u8 ; MAX_ALGORITHM_LEN ] ,
206+ pub algorithm_len : usize ,
207+ pub pub_key : [ u8 ; MAX_PUBLIC_KEY_LEN ] ,
208+ pub pub_key_len : usize ,
209+ pub binding_pub_key : [ u8 ; MAX_PUBLIC_KEY_LEN ] ,
210+ pub binding_pub_key_len : usize ,
211+ pub remaining_lifespan_secs : u64 ,
212+ }
213+
214+ impl Default for KpsKeyInfo {
215+ fn default ( ) -> Self {
216+ KpsKeyInfo {
217+ uuid : [ 0 ; 16 ] ,
218+ algorithm : [ 0 ; MAX_ALGORITHM_LEN ] ,
219+ algorithm_len : 0 ,
220+ pub_key : [ 0 ; MAX_PUBLIC_KEY_LEN ] ,
221+ pub_key_len : 0 ,
222+ binding_pub_key : [ 0 ; MAX_PUBLIC_KEY_LEN ] ,
223+ binding_pub_key_len : 0 ,
224+ remaining_lifespan_secs : 0 ,
225+ }
226+ }
227+ }
228+
229+ fn enumerate_kem_keys_internal (
230+ entries : & mut [ KpsKeyInfo ] ,
231+ offset : usize ,
232+ ) -> Result < ( usize , bool ) , i32 > {
233+ let ( metas, total_count) = KEY_REGISTRY . list_all_keys ( offset, entries. len ( ) ) ;
234+ let count = metas. len ( ) ;
235+ let has_more = offset + count < total_count;
236+
237+ for ( entry, meta) in entries. iter_mut ( ) . zip ( metas. into_iter ( ) ) {
238+ let KeySpec :: KemWithBindingPub {
239+ algo,
240+ kem_public_key : pub_key,
241+ binding_public_key : binding_pub_key,
242+ ..
243+ } = & meta. spec
244+ else {
245+ return Err ( -1 ) ; // Implementation error, KPS should only contain KEM keys.
246+ } ;
247+
248+ let algo_bytes = algo. encode_to_vec ( ) ;
249+
250+ if pub_key. as_bytes ( ) . len ( ) > MAX_PUBLIC_KEY_LEN || algo_bytes. len ( ) > MAX_ALGORITHM_LEN {
251+ debug_assert ! (
252+ false ,
253+ "Implementation error: Key size exceeds buffer limits! (algo={}, pub={})" ,
254+ algo_bytes. len( ) ,
255+ pub_key. as_bytes( ) . len( )
256+ ) ;
257+ return Err ( -2 ) ; // Buffer Limit Exceeded
258+ }
259+ if binding_pub_key. as_bytes ( ) . len ( ) > MAX_PUBLIC_KEY_LEN {
260+ debug_assert ! (
261+ false ,
262+ "Implementation error: Binding Key size exceeds buffer limits! (bpk={})" ,
263+ binding_pub_key. as_bytes( ) . len( )
264+ ) ;
265+ return Err ( -2 ) ;
266+ }
267+
268+ let now = Instant :: now ( ) ;
269+ let remaining = meta. delete_after . saturating_duration_since ( now) . as_secs ( ) ;
270+
271+ * entry = KpsKeyInfo :: default ( ) ;
272+
273+ entry. uuid . copy_from_slice ( meta. id . as_bytes ( ) ) ;
274+
275+ entry. algorithm [ ..algo_bytes. len ( ) ] . copy_from_slice ( & algo_bytes) ;
276+ entry. algorithm_len = algo_bytes. len ( ) ;
277+
278+ entry. pub_key [ ..pub_key. as_bytes ( ) . len ( ) ] . copy_from_slice ( pub_key. as_bytes ( ) ) ;
279+ entry. pub_key_len = pub_key. as_bytes ( ) . len ( ) ;
280+
281+ entry. binding_pub_key [ ..binding_pub_key. as_bytes ( ) . len ( ) ]
282+ . copy_from_slice ( binding_pub_key. as_bytes ( ) ) ;
283+ entry. binding_pub_key_len = binding_pub_key. as_bytes ( ) . len ( ) ;
284+
285+ entry. remaining_lifespan_secs = remaining;
286+ }
287+
288+ Ok ( ( count, has_more) )
289+ }
290+
291+ #[ unsafe( no_mangle) ]
292+ pub unsafe extern "C" fn key_manager_enumerate_kem_keys (
293+ out_entries : * mut KpsKeyInfo ,
294+ max_entries : usize ,
295+ offset : usize ,
296+ out_has_more : Option < & mut bool > ,
297+ ) -> i32 {
298+ std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
299+ if out_entries. is_null ( ) {
300+ return -1 ;
301+ }
302+
303+ let entries = unsafe { slice:: from_raw_parts_mut ( out_entries, max_entries) } ;
304+
305+ match enumerate_kem_keys_internal ( entries, offset) {
306+ Ok ( ( count, has_more) ) => {
307+ if let Some ( has_more_ref) = out_has_more {
308+ * has_more_ref = has_more;
309+ }
310+ count as i32
311+ }
312+ Err ( e) => e,
313+ }
314+ } ) )
315+ . unwrap_or ( -1 )
316+ }
317+
199318/// Decapsulates a shared secret using a stored KEM key and immediately reseals it using the associated binding public key.
200319///
201320/// ## Arguments
@@ -498,6 +617,150 @@ mod tests {
498617 assert_eq ! ( result, -1 ) ;
499618 }
500619
620+ #[ test]
621+ fn test_enumerate_kem_keys_null_pointers ( ) {
622+ let result = unsafe { key_manager_enumerate_kem_keys ( std:: ptr:: null_mut ( ) , 10 , 0 , None ) } ;
623+ assert_eq ! ( result, -1 ) ;
624+ }
625+
626+ #[ test]
627+ fn test_enumerate_kem_keys_after_generate ( ) {
628+ let binding_pubkey = [ 7u8 ; 32 ] ;
629+ let mut uuid_bytes = [ 0u8 ; 16 ] ;
630+ let mut pubkey_bytes = [ 0u8 ; 32 ] ;
631+ let pubkey_len: usize = 32 ;
632+ let algo = HpkeAlgorithm {
633+ kem : KemAlgorithm :: DhkemX25519HkdfSha256 as i32 ,
634+ kdf : KdfAlgorithm :: HkdfSha256 as i32 ,
635+ aead : AeadAlgorithm :: Aes256Gcm as i32 ,
636+ } ;
637+ // MUST encode to bytes
638+ let algo_bytes = algo. encode_to_vec ( ) ;
639+
640+ // Generate a key first.
641+ let rc = unsafe {
642+ key_manager_generate_kem_keypair (
643+ algo_bytes. as_ptr ( ) ,
644+ algo_bytes. len ( ) ,
645+ binding_pubkey. as_ptr ( ) ,
646+ binding_pubkey. len ( ) ,
647+ 3600 ,
648+ uuid_bytes. as_mut_ptr ( ) ,
649+ pubkey_bytes. as_mut_ptr ( ) ,
650+ pubkey_len,
651+ )
652+ } ;
653+ assert_eq ! ( rc, 0 ) ;
654+
655+ // Enumerate.
656+ let mut entries: Vec < KpsKeyInfo > = Vec :: with_capacity ( 256 ) ;
657+ // Initialize with default/zero values. Note: Arrays are larger now.
658+ entries. resize_with ( 100 , || KpsKeyInfo {
659+ uuid : [ 0 ; 16 ] ,
660+ algorithm : [ 0 ; MAX_ALGORITHM_LEN ] ,
661+ algorithm_len : 0 ,
662+ pub_key : [ 0 ; MAX_PUBLIC_KEY_LEN ] ,
663+ pub_key_len : 0 ,
664+ binding_pub_key : [ 0 ; MAX_PUBLIC_KEY_LEN ] ,
665+ binding_pub_key_len : 0 ,
666+ remaining_lifespan_secs : 0 ,
667+ } ) ;
668+ let mut has_more = false ;
669+
670+ let rc = unsafe {
671+ // max_entries=100, offset=0
672+ key_manager_enumerate_kem_keys (
673+ entries. as_mut_ptr ( ) ,
674+ entries. len ( ) ,
675+ 0 ,
676+ Some ( & mut has_more) ,
677+ )
678+ } ;
679+ assert ! ( rc >= 1 ) ;
680+ let count = rc as usize ;
681+
682+ // Find our key in the results.
683+ let mut found = false ;
684+ for i in 0 ..count {
685+ if entries[ i] . uuid == uuid_bytes {
686+ found = true ;
687+ let encoded_algo = & entries[ i] . algorithm [ ..entries[ i] . algorithm_len ] ;
688+ let decoded_algo = HpkeAlgorithm :: decode ( encoded_algo) . unwrap ( ) ;
689+ assert_eq ! ( decoded_algo. kem, KemAlgorithm :: DhkemX25519HkdfSha256 as i32 ) ;
690+ assert_eq ! ( entries[ i] . pub_key_len, 32 ) ;
691+ assert ! ( entries[ i] . remaining_lifespan_secs > 0 ) ;
692+ break ;
693+ }
694+ }
695+ assert ! ( found, "generated key not found in enumerate results" ) ;
696+ }
697+
698+ #[ test]
699+ fn test_enumerate_kem_keys_has_more ( ) {
700+ // Assume there is at least one key from initialization/other tests or we'll generate one
701+ let mut uuid_bytes = [ 0u8 ; 16 ] ;
702+ let mut pubkey_bytes = [ 0u8 ; 32 ] ;
703+ let algo = HpkeAlgorithm {
704+ kem : KemAlgorithm :: DhkemX25519HkdfSha256 as i32 ,
705+ kdf : KdfAlgorithm :: HkdfSha256 as i32 ,
706+ aead : AeadAlgorithm :: Aes256Gcm as i32 ,
707+ } ;
708+ let algo_bytes = algo. encode_to_vec ( ) ;
709+
710+ // Let's explicitly generate a key so we know there's at least one in the registry
711+ unsafe {
712+ key_manager_generate_kem_keypair (
713+ algo_bytes. as_ptr ( ) ,
714+ algo_bytes. len ( ) ,
715+ [ 7u8 ; 32 ] . as_ptr ( ) , // fake binding key
716+ 32 ,
717+ 3600 ,
718+ uuid_bytes. as_mut_ptr ( ) ,
719+ pubkey_bytes. as_mut_ptr ( ) ,
720+ 32 ,
721+ ) ;
722+ }
723+
724+ let mut entries: Vec < KpsKeyInfo > = Vec :: with_capacity ( 256 ) ;
725+ entries. resize_with ( 100 , || KpsKeyInfo {
726+ uuid : [ 0 ; 16 ] ,
727+ algorithm : [ 0 ; MAX_ALGORITHM_LEN ] ,
728+ algorithm_len : 0 ,
729+ pub_key : [ 0 ; MAX_PUBLIC_KEY_LEN ] ,
730+ pub_key_len : 0 ,
731+ binding_pub_key : [ 0 ; MAX_PUBLIC_KEY_LEN ] ,
732+ binding_pub_key_len : 0 ,
733+ remaining_lifespan_secs : 0 ,
734+ } ) ;
735+
736+ // 1. Ask for 0 entries. We should get has_more = true.
737+ let mut has_more = false ;
738+ let rc = unsafe {
739+ key_manager_enumerate_kem_keys ( entries. as_mut_ptr ( ) , 0 , 0 , Some ( & mut has_more) )
740+ } ;
741+ assert_eq ! ( rc, 0 ) ;
742+ assert ! (
743+ has_more,
744+ "has_more should be true when max_entries is 0 and keys exist"
745+ ) ;
746+
747+ // 2. Ask for 100 entries (which should cover all generated keys). has_more = false.
748+ has_more = true ; // reset to true to ensure it gets set to false
749+ let rc = unsafe {
750+ key_manager_enumerate_kem_keys (
751+ entries. as_mut_ptr ( ) ,
752+ entries. len ( ) ,
753+ 0 ,
754+ Some ( & mut has_more) ,
755+ )
756+ } ;
757+ assert ! ( rc >= 1 ) ;
758+ assert ! (
759+ !has_more,
760+ "has_more should be false when all keys are retrieved"
761+ ) ;
762+ }
763+
501764 #[ test]
502765 fn test_decap_and_seal_success ( ) {
503766 // 1. Setup binding key (receiver for seal)
@@ -531,9 +794,8 @@ mod tests {
531794 // 3. Generate a "client" ciphertext/encapsulation targeting KEM key.
532795 let aad = b"test_aad" ;
533796 // We use `encap` to act as the client to generate a valid encapsulation
534- let kem_pub_key_obj = PublicKey :: try_from ( kem_pubkey_bytes. to_vec ( ) ) . unwrap ( ) ;
535- let ( client_shared_secret, client_enc) =
536- km_common:: crypto:: encap ( & kem_pub_key_obj) . unwrap ( ) ;
797+ let pub_key_obj = PublicKey :: try_from ( kem_pubkey_bytes. to_vec ( ) ) . unwrap ( ) ;
798+ let ( client_shared_secret, client_enc) = km_common:: crypto:: encap ( & pub_key_obj) . unwrap ( ) ;
537799
538800 // Step 3: Call `decap_and_seal`.
539801 let mut out_enc_key = [ 0u8 ; 32 ] ;
@@ -704,10 +966,9 @@ mod tests {
704966 assert_eq ! ( res, 0 , "Setup failed: key generation returned error" ) ;
705967
706968 // 3. Generate valid client encapsulation
707- let kem_pub_key_obj = PublicKey :: try_from ( kem_pubkey_bytes. to_vec ( ) ) . unwrap ( ) ;
969+ let pub_key_obj = PublicKey :: try_from ( kem_pubkey_bytes. to_vec ( ) ) . unwrap ( ) ;
708970 let pt = km_common:: crypto:: secret_box:: SecretBox :: new ( b"secret" . to_vec ( ) ) ;
709- let ( client_enc, _) =
710- km_common:: crypto:: hpke_seal ( & kem_pub_key_obj, & pt, b"" , & algo) . unwrap ( ) ;
971+ let ( client_enc, _) = km_common:: crypto:: hpke_seal ( & pub_key_obj, & pt, b"" , & algo) . unwrap ( ) ;
711972
712973 // 4. Call with small output buffers
713974 let mut out_enc_key = [ 0u8 ; 31 ] ; // Small
0 commit comments