@@ -8,7 +8,7 @@ use std::backtrace::Backtrace;
88use thiserror:: Error ;
99use crate :: crypto:: { KeyPair , SIVEncryptable , SECRET_KEY_SIZE } ;
1010use crate :: msg:: { EnvSecretResponse , ExecuteMsg , ImageFilterHex , ImageFilterHexEntry , InstantiateMsg , ListImageResponse , MigrateMsg , MsgImageFilter , QueryMsg , SecretKeyResponse , ServiceResponse , DockerCredentialsResponse } ;
11- use crate :: state:: { global_state, global_state_read, services, services_read, GlobalState , Service , ImageFilter , image_secret_keys, image_secret_keys_read, env_secrets, EnvSecret , env_secrets_read, SERVICES_MAP , FilterEntry , OldService , DockerCredential , docker_credentials, docker_credentials_read, OLD_SERVICES_MAP , OldFilterEntry , VM_RECORDS , VmRecord } ;
11+ use crate :: state:: { global_state, global_state_read, services, services_read, GlobalState , Service , ImageFilter , image_secret_keys, image_secret_keys_read, env_secrets, EnvSecret , env_secrets_read, SERVICES_MAP , FilterEntry , OldService , DockerCredential , docker_credentials, docker_credentials_read, OLD_SERVICES_MAP , OldFilterEntry , VM_RECORDS , VmRecord , DOCKER_CREDENTIALS } ;
1212use crate :: import_helpers:: { from_high_half, from_low_half} ;
1313use crate :: memory:: { build_region, Region } ;
1414use crate :: msg:: QueryMsg :: ListImageFilters ;
@@ -280,19 +280,18 @@ pub fn try_add_docker_credentials_by_image(
280280 username : String ,
281281 password_plaintext : String ,
282282) -> StdResult < Response > {
283- // Ensure the sender is the global admin.
283+ // Only global admin can add docker credentials
284284 let gs = global_state_read ( deps. storage ) . load ( ) ?;
285285 if info. sender != gs. admin {
286- return Err ( StdError :: generic_err (
287- "Only the admin can add docker credentials" ,
288- ) ) ;
286+ return Err ( StdError :: generic_err ( "Only the admin can add docker credentials" ) ) ;
289287 }
290288
291- // Extract the VM uid (required)
292- let vm_uid = image_filter. vm_uid
289+ // vm_uid is REQUIRED (we store by vm_uid)
290+ let vm_uid = image_filter
291+ . vm_uid
293292 . ok_or_else ( || StdError :: generic_err ( "vm_uid required" ) ) ?;
294293
295- // Check that the required fields are provided.
294+ // Pin to exact image values
296295 if image_filter. mr_td . is_none ( )
297296 || image_filter. rtmr1 . is_none ( )
298297 || image_filter. rtmr2 . is_none ( )
@@ -303,48 +302,38 @@ pub fn try_add_docker_credentials_by_image(
303302 ) ) ;
304303 }
305304
306- // Create a new DockerCredential structure from the provided image filter.
307- let new_docker_credential = DockerCredential {
305+ let new_rec = DockerCredential {
308306 mr_td : image_filter. mr_td . clone ( ) . unwrap ( ) ,
309307 rtmr1 : image_filter. rtmr1 . clone ( ) . unwrap ( ) ,
310308 rtmr2 : image_filter. rtmr2 . clone ( ) . unwrap ( ) ,
311309 rtmr3 : image_filter. rtmr3 . clone ( ) . unwrap ( ) ,
312- vm_uid : Some ( vm_uid) ,
310+ vm_uid : Some ( vm_uid. clone ( ) ) ,
313311 docker_username : username. clone ( ) ,
314312 docker_password_plaintext : password_plaintext. clone ( ) ,
315313 } ;
316314
317- let mut docker_credentials_list: Vec < DockerCredential > =
318- docker_credentials ( deps. storage ) . load ( ) . unwrap_or_else ( |_| Vec :: new ( ) ) ;
319-
315+ // Write/update in the VM-keyed map
320316 let mut updated = false ;
321- for cred in docker_credentials_list. iter_mut ( ) {
322- if cred. mr_td == new_docker_credential. mr_td
323- && cred. rtmr1 == new_docker_credential. rtmr1
324- && cred. rtmr2 == new_docker_credential. rtmr2
325- && cred. rtmr3 == new_docker_credential. rtmr3
326- && cred. vm_uid == new_docker_credential. vm_uid
327- {
328- // Update the credentials.
329- cred. docker_username = username. clone ( ) ;
330- cred. docker_password_plaintext = password_plaintext. clone ( ) ;
331- updated = true ;
332- break ;
333- }
334- }
335-
336- if !updated {
337- docker_credentials_list. push ( new_docker_credential) ;
317+ if let Some ( mut existing) = DOCKER_CREDENTIALS . get ( deps. storage , & vm_uid) {
318+ // Overwrite to keep latest filter+creds
319+ existing. mr_td = new_rec. mr_td . clone ( ) ;
320+ existing. rtmr1 = new_rec. rtmr1 . clone ( ) ;
321+ existing. rtmr2 = new_rec. rtmr2 . clone ( ) ;
322+ existing. rtmr3 = new_rec. rtmr3 . clone ( ) ;
323+ existing. docker_username = new_rec. docker_username . clone ( ) ;
324+ existing. docker_password_plaintext = new_rec. docker_password_plaintext . clone ( ) ;
325+ DOCKER_CREDENTIALS . insert ( deps. storage , & vm_uid, & existing) ?;
326+ updated = true ;
327+ } else {
328+ DOCKER_CREDENTIALS . insert ( deps. storage , & vm_uid, & new_rec) ?;
338329 }
339330
340- docker_credentials ( deps. storage ) . save ( & docker_credentials_list) ?;
341-
331+ // We do NOT write to the legacy vector; query has a legacy reverse-scan fallback.
342332 Ok ( Response :: new ( )
343333 . add_attribute ( "action" , "add_docker_credentials_by_image" )
344334 . add_attribute ( "updated" , updated. to_string ( ) ) )
345335}
346336
347-
348337/// NEW: Add or update an environment secret by image.
349338/// This function expects that the provided image filter includes non‑None values for
350339/// mr_td, rtmr1, rtmr2, and rtmr3, and it takes an additional secrets_plaintext string.
@@ -821,44 +810,65 @@ pub fn try_get_docker_credentials_by_image(
821810 quote : Vec < u8 > ,
822811 collateral : Vec < u8 > ,
823812) -> StdResult < DockerCredentialsResponse > {
824- // Verify + parse
825813 let tdx = parse_tdx_attestation ( & quote, & collateral)
826814 . ok_or_else ( || StdError :: generic_err ( "Attestation invalid" ) ) ?;
827815
828- // Extract mr_td, rtmr1, rtmr2, rtmr3
816+ // Fields from quote
829817 let mr_td = tdx. mr_td . to_vec ( ) ;
830- let r1 = tdx. rtmr1 . to_vec ( ) ;
831- let r2 = tdx. rtmr2 . to_vec ( ) ;
832- let r3 = tdx. rtmr3 . to_vec ( ) ;
833-
834- // Extract VM UID from report_data: next 16 bytes after the 32-byte pubkey, hex-encoded
835- let vm_uid = tdx. report_data [ 32 ..48 ] . to_vec ( ) ;
836-
837- let docker_credentials_list = docker_credentials_read ( deps. storage ) . load ( ) ?;
838- let docker_credential = docker_credentials_list
839- . into_iter ( )
840- . find ( |cred| {
841- cred. mr_td == mr_td
842- && cred. rtmr1 == r1
843- && cred. rtmr2 == r2
844- && cred. rtmr3 == r3
845- && cred. vm_uid . as_ref ( ) . unwrap ( ) == & vm_uid
846- } )
847- . ok_or_else ( || StdError :: generic_err ( "No docker credentials found for this image" ) ) ?;
848-
818+ let r1 = tdx. rtmr1 . to_vec ( ) ;
819+ let r2 = tdx. rtmr2 . to_vec ( ) ;
820+ let r3 = tdx. rtmr3 . to_vec ( ) ;
821+ let vm_uid = tdx. report_data [ 32 ..48 ] . to_vec ( ) ; // 16 bytes after pubkey
822+
823+ // 1) Primary lookup: VM-keyed map
824+ if let Some ( rec) = DOCKER_CREDENTIALS . get ( deps. storage , & vm_uid) {
825+ // Enforce exact match between stored image params and attestation
826+ if rec. mr_td != mr_td || rec. rtmr1 != r1 || rec. rtmr2 != r2 || rec. rtmr3 != r3 {
827+ return Err ( StdError :: generic_err (
828+ "Filter mismatch for docker credentials VM record" ,
829+ ) ) ;
830+ }
831+ let other_pub: [ u8 ; 32 ] = tdx. report_data [ 0 ..32 ]
832+ . try_into ( )
833+ . map_err ( |_| StdError :: generic_err ( "Bad report_data" ) ) ?;
834+ let height_bytes = env. block . height . to_string ( ) . into_bytes ( ) ;
835+ return encrypt_docker_credentials (
836+ rec. docker_username ,
837+ rec. docker_password_plaintext ,
838+ other_pub,
839+ & quote,
840+ height_bytes,
841+ ) ;
842+ }
849843
850- // Encrypt that plaintext
851- let other_pub: [ u8 ; 32 ] = tdx. report_data [ 0 ..32 ] . try_into ( )
852- . map_err ( |_| StdError :: generic_err ( "Bad report_data" ) ) ?;
853- let height_bytes = env. block . height . to_string ( ) . into_bytes ( ) ;
844+ // 2) Legacy fallback: scan the old Vec from the END (last-write wins)
845+ let legacy_list = docker_credentials_read ( deps. storage ) . load ( ) . unwrap_or_default ( ) ;
846+ if let Some ( rec) = legacy_list. iter ( ) . rev ( ) . find ( |cred| {
847+ cred. mr_td == mr_td
848+ && cred. rtmr1 == r1
849+ && cred. rtmr2 == r2
850+ && cred. rtmr3 == r3
851+ && cred. vm_uid
852+ . as_ref ( )
853+ . map ( |v| v == & vm_uid)
854+ . unwrap_or ( true ) // legacy may have vm_uid=None
855+ } ) {
856+ let other_pub: [ u8 ; 32 ] = tdx. report_data [ 0 ..32 ]
857+ . try_into ( )
858+ . map_err ( |_| StdError :: generic_err ( "Bad report_data" ) ) ?;
859+ let height_bytes = env. block . height . to_string ( ) . into_bytes ( ) ;
860+ return encrypt_docker_credentials (
861+ rec. docker_username . clone ( ) ,
862+ rec. docker_password_plaintext . clone ( ) ,
863+ other_pub,
864+ & quote,
865+ height_bytes,
866+ ) ;
867+ }
854868
855- encrypt_docker_credentials (
856- docker_credential. docker_username ,
857- docker_credential. docker_password_plaintext ,
858- other_pub,
859- & quote,
860- height_bytes,
861- )
869+ Err ( StdError :: generic_err (
870+ "No docker credentials found for this image" ,
871+ ) )
862872}
863873
864874/// Query image filters, returning hex-encoded image fields
@@ -918,10 +928,21 @@ pub fn try_get_env_by_image(
918928 // Legacy fallback (scan vector)
919929 let legacy = env_secrets_read ( deps. storage ) . load ( ) . map_err ( |_| StdError :: generic_err ( "Legacy env storage missing" ) ) ?;
920930 let mr_td = tdx. mr_td . to_vec ( ) ; let r1 = tdx. rtmr1 . to_vec ( ) ; let r2 = tdx. rtmr2 . to_vec ( ) ; let r3 = tdx. rtmr3 . to_vec ( ) ;
921- let candidate = legacy. into_iter ( ) . find ( |e| {
922- e. mr_td == mr_td && e. rtmr1 == r1 && e. rtmr2 == r2 && e. rtmr3 == r3 &&
923- e. vm_uid . as_ref ( ) . map ( |v| v == & vm_uid) . unwrap_or ( true )
924- } ) . ok_or_else ( || StdError :: generic_err ( "No env secret found" ) ) ?;
931+ let candidate = legacy
932+ . iter ( )
933+ . rev ( )
934+ . find ( |e| {
935+ e. mr_td == mr_td
936+ && e. rtmr1 == r1
937+ && e. rtmr2 == r2
938+ && e. rtmr3 == r3
939+ && e. vm_uid
940+ . as_ref ( )
941+ . map ( |v| v == & vm_uid)
942+ . unwrap_or ( true ) // legacy may have vm_uid = None
943+ } )
944+ . cloned ( ) // we iter() above, so clone the found EnvSecret
945+ . ok_or_else ( || StdError :: generic_err ( "No env secret found" ) ) ?;
925946
926947 let other_pub: [ u8 ; 32 ] = tdx. report_data [ 0 ..32 ] . try_into ( ) . map_err ( |_| StdError :: generic_err ( "Bad report_data" ) ) ?;
927948 let enc = encrypt_secret ( candidate. secrets_plaintext . into_bytes ( ) , other_pub, & quote, env. block . height . to_string ( ) . into_bytes ( ) ) ?;
0 commit comments