@@ -128,8 +128,13 @@ pub fn start_gcp_vtpm(runtime_dir: &Path, config: &TeeSimulatorConfig) -> Result
128128 if let Some ( error) = startup_error {
129129 return Err ( error) . context ( "GCP vTPM did not become ready" ) ;
130130 }
131- replay_fixture_event_log ( ) ?;
132- install_fixture_event_log ( ) ?;
131+ let replay = config
132+ . gcp_tpm_replay
133+ . as_ref ( )
134+ . context ( "tee_simulator.gcp_tpm_replay is required for GCP" ) ?;
135+ validate_gcp_event_log ( config, & replay. event_log ) ?;
136+ replay_gcp_event_log ( & replay. event_log ) ?;
137+ install_gcp_event_log ( & replay. event_log ) ?;
133138
134139 let template_with_size = state_dir. join ( "ak.tpm2b-public" ) ;
135140 let generated_public = state_dir. join ( "ak.public" ) ;
@@ -216,26 +221,57 @@ pub fn start_gcp_vtpm(runtime_dir: &Path, config: &TeeSimulatorConfig) -> Result
216221 Ok ( ( ) )
217222}
218223
219- fn replay_fixture_event_log ( ) -> Result < ( ) > {
220- let bytes = include_bytes ! ( "../../cc-eventlog/samples/tpm_eventlog.bin" ) ;
221- let event_log = cc_eventlog:: tpm:: TpmEventLog :: decode ( & mut bytes. as_slice ( ) ) ?;
224+ fn validate_gcp_event_log ( config : & TeeSimulatorConfig , bytes : & [ u8 ] ) -> Result < ( ) > {
225+ let vm_config: dstack_types:: VmConfig = serde_json:: from_str (
226+ config
227+ . vm_config
228+ . as_deref ( )
229+ . context ( "tee_simulator.vm_config is required for GCP" ) ?,
230+ ) ?;
231+ let expected = vm_config
232+ . gcp_measurement
233+ . as_ref ( )
234+ . context ( "vm_config.gcp_measurement is required for GCP" ) ?
235+ . decode_measurement ( )
236+ . map_err ( anyhow:: Error :: msg) ?
237+ . uki_authenticode_sha256 ;
238+ let event_log = cc_eventlog:: tpm:: TpmEventLog :: decode ( & mut bytes. as_ref ( ) ) ?;
239+ let actual = event_log
240+ . pcr2_events ( )
241+ . get ( 2 )
242+ . context ( "GCP TPM event log is missing the UKI event" ) ?
243+ . digest
244+ . clone ( ) ;
245+ anyhow:: ensure!(
246+ actual == expected,
247+ "GCP TPM event-log UKI digest does not match measurement.gcp.cbor"
248+ ) ;
249+ Ok ( ( ) )
250+ }
251+
252+ fn replay_gcp_event_log ( bytes : & [ u8 ] ) -> Result < ( ) > {
253+ let event_log = cc_eventlog:: tpm:: TpmEventLog :: decode ( & mut bytes. as_ref ( ) ) ?;
222254 for event in event_log. events {
223255 let extension = format ! ( "{}:sha256={}" , event. pcr_index, hex:: encode( event. digest) ) ;
224256 command ( "tpm2_pcrextend" , & [ & extension] ) ?;
225257 }
226258 Ok ( ( ) )
227259}
228260
229- fn install_fixture_event_log ( ) -> Result < ( ) > {
261+ fn install_gcp_event_log ( bytes : & [ u8 ] ) -> Result < ( ) > {
230262 let security_root = Path :: new ( "/sys/kernel/security" ) ;
231263 let event_log = security_root. join ( "tpm0/binary_bios_measurements" ) ;
232264 if event_log. exists ( ) {
265+ anyhow:: ensure!(
266+ fs_err:: read( & event_log) ? == bytes,
267+ "existing simulated TPM event log does not match the image"
268+ ) ;
233269 return Ok ( ( ) ) ;
234270 }
235271 let tpm_dir = event_log. parent ( ) . context ( "TPM event log has no parent" ) ?;
236272 // securityfs does not permit userspace to create a synthetic TPM event
237273 // log hierarchy. Shadow it in this development-only guest before
238- // publishing the fixture that was replayed into the simulated PCRs.
274+ // publishing the event log that was replayed into the simulated PCRs.
239275 let flags = nix:: mount:: MsFlags :: MS_NOSUID
240276 | nix:: mount:: MsFlags :: MS_NODEV
241277 | nix:: mount:: MsFlags :: MS_NOEXEC ;
@@ -249,14 +285,8 @@ fn install_fixture_event_log() -> Result<()> {
249285 . context ( "failed to mount simulated securityfs shadow" ) ?;
250286 fs_err:: create_dir_all ( tpm_dir)
251287 . context ( "failed to create TPM event-log directory in securityfs shadow" ) ?;
252- fs_err:: write (
253- event_log,
254- include_bytes ! ( "../../cc-eventlog/samples/tpm_eventlog.bin" ) ,
255- )
256- . context ( "failed to install simulated TPM event log" ) ?;
257- Ok ( ( ) )
288+ fs_err:: write ( event_log, bytes) . context ( "failed to install simulated TPM event log" )
258289}
259-
260290fn create_tpm_device_node ( ) -> Result < ( ) > {
261291 if Path :: new ( "/dev/tpm0" ) . exists ( ) {
262292 return Ok ( ( ) ) ;
@@ -586,6 +616,38 @@ fn set_nv_public_size(response: &mut [u8], size: usize) -> Result<()> {
586616 Ok ( ( ) )
587617}
588618
619+ #[ cfg( test) ]
620+ mod tests {
621+ use super :: * ;
622+
623+ #[ test]
624+ fn gcp_event_log_is_bound_to_vm_measurement ( ) {
625+ let fixture = include_bytes ! ( "../../cc-eventlog/samples/tpm_eventlog.bin" ) ;
626+ let fixture_hash =
627+ hex:: decode ( "9ab14a46f858662a89adc102d2a57a13f52f75c1769d65a4c34edbbfc8855f0f" )
628+ . unwrap ( ) ;
629+ let image_hash = vec ! [ 0x5a ; 32 ] ;
630+ let offset = fixture
631+ . windows ( fixture_hash. len ( ) )
632+ . position ( |window| window == fixture_hash)
633+ . unwrap ( ) ;
634+ let mut event_log = fixture. to_vec ( ) ;
635+ event_log[ offset..offset + image_hash. len ( ) ] . copy_from_slice ( & image_hash) ;
636+
637+ let measurement = dstack_types:: GcpOsImageMeasurement :: new ( image_hash) . unwrap ( ) ;
638+ let document =
639+ dstack_types:: GcpOsImageMeasurementDocument :: from_measurement ( Vec :: new ( ) , measurement) ;
640+ let mut config = TeeSimulatorConfig {
641+ vm_config : Some ( serde_json:: json!( { "gcp_measurement" : document } ) . to_string ( ) ) ,
642+ ..Default :: default ( )
643+ } ;
644+ validate_gcp_event_log ( & config, & event_log) . unwrap ( ) ;
645+
646+ config. vm_config = Some ( "{}" . into ( ) ) ;
647+ assert ! ( validate_gcp_event_log( & config, & event_log) . is_err( ) ) ;
648+ }
649+ }
650+
589651fn nv_read_response ( command : & [ u8 ] , contents : & [ u8 ] ) -> Result < Vec < u8 > > {
590652 anyhow:: ensure!( command. len( ) >= 4 , "truncated NV_Read command" ) ;
591653 let size = read_be_u16 (
0 commit comments