Skip to content

Commit 3bd2cac

Browse files
committed
docker_credentials stored in map update
1 parent 343fc67 commit 3bd2cac

File tree

2 files changed

+97
-70
lines changed

2 files changed

+97
-70
lines changed

src/contract.rs

Lines changed: 91 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::backtrace::Backtrace;
88
use thiserror::Error;
99
use crate::crypto::{KeyPair, SIVEncryptable, SECRET_KEY_SIZE};
1010
use 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};
1212
use crate::import_helpers::{from_high_half, from_low_half};
1313
use crate::memory::{build_region, Region};
1414
use 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())?;

src/state.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ pub static VM_RECORDS: Keymap<Vec<u8>, VmRecord> = Keymap::new(VM_RECORDS_KEY);
2323
pub static ENV_SECRETS_KEY: &[u8] = b"env_secrets";
2424
pub static DOCKER_CREDENTIALS_KEY: &[u8] = b"docker_credentials";
2525

26+
// NEW: primary VM-keyed map for docker credentials.
27+
pub const DOCKER_CREDENTIALS_MAP_KEY: &[u8] = b"docker_credentials_by_vm";
28+
29+
/// Primary storage: map keyed by vm_uid (Vec<u8>)
30+
pub static DOCKER_CREDENTIALS: Keymap<Vec<u8>, DockerCredential> =
31+
Keymap::new(DOCKER_CREDENTIALS_MAP_KEY);
2632

2733
// NEW: import bucket helpers
2834
use cosmwasm_storage::{bucket, bucket_read, Bucket, ReadonlyBucket};

0 commit comments

Comments
 (0)