@@ -22,6 +22,8 @@ mod tpm_helper;
22
22
use self :: io_port_interface:: PpiOperation ;
23
23
use self :: io_port_interface:: TpmIoCommand ;
24
24
use crate :: ak_cert:: TpmAkCertType ;
25
+ use crate :: tpm20proto:: TpmaObject ;
26
+ use crate :: tpm20proto:: TpmaObjectBits ;
25
27
use chipset_device:: ChipsetDevice ;
26
28
use chipset_device:: io:: IoError ;
27
29
use chipset_device:: io:: IoResult ;
@@ -55,6 +57,7 @@ use tpm20proto::NV_INDEX_RANGE_BASE_TCG_ASSIGNED;
55
57
use tpm20proto:: ReservedHandle ;
56
58
use tpm20proto:: TPM20_HT_PERSISTENT ;
57
59
use tpm20proto:: TPM20_RH_PLATFORM ;
60
+ use underhill_confidentiality:: is_confidential_vm;
58
61
use vmcore:: device_state:: ChangeDeviceState ;
59
62
use vmcore:: non_volatile_store:: NonVolatileStore ;
60
63
use vmcore:: non_volatile_store:: NonVolatileStoreError ;
@@ -224,6 +227,7 @@ pub struct Tpm {
224
227
io_region : Option < ( & ' static str , RangeInclusive < u16 > ) > , // Valid only on HypervX64
225
228
#[ inspect( skip) ]
226
229
mmio_region : Vec < ( & ' static str , RangeInclusive < u64 > ) > ,
230
+ allow_ak_cert_renewal : bool ,
227
231
228
232
// Runtime glue
229
233
rt : TpmRuntime ,
@@ -396,6 +400,7 @@ impl Tpm {
396
400
refresh_tpm_seeds,
397
401
io_region,
398
402
mmio_region,
403
+ allow_ak_cert_renewal : false ,
399
404
400
405
rt : TpmRuntime {
401
406
mem,
@@ -446,6 +451,7 @@ impl Tpm {
446
451
447
452
async fn on_first_boot ( & mut self , guest_secret_key : Option < Vec < u8 > > ) -> Result < ( ) , TpmError > {
448
453
use ms_tpm_20_ref:: NvError ;
454
+ let mut force_ak_regen = false ;
449
455
let fixup_16k_ak_cert;
450
456
451
457
// Check whether or not we need to pave-over the blank TPM with our
@@ -473,6 +479,13 @@ impl Tpm {
473
479
return Err ( TpmErrorKind :: ResetTpmWithState ( e) . into ( ) ) ;
474
480
}
475
481
482
+ // If this is a confidential VM or has a vTPM blob size that indicates that it was
483
+ // HCL-provisioned, regenerate the AK from TPM seeds. This prevents an attack where
484
+ // the VTL0 admin can replace the AK and get an AKCert for it.
485
+ force_ak_regen = self . refresh_tpm_seeds
486
+ || blob. len ( ) != LEGACY_VTPM_SIZE
487
+ || is_confidential_vm ( ) ;
488
+
476
489
// If this is a small vTPM blob, potentially fixup the AK cert.
477
490
fixup_16k_ak_cert = blob. len ( ) == LEGACY_VTPM_SIZE ;
478
491
} else {
@@ -543,15 +556,21 @@ impl Tpm {
543
556
// Initialize `TpmKeys`.
544
557
// The procedure also generates randomized AK based on the TPM seed
545
558
// and writes the AK into `TPM_AZURE_AIK_HANDLE` NV store.
546
- let ak_pub = self
559
+ let ( ak_pub, can_renew_ak ) = self
547
560
. tpm_engine_helper
548
- . create_ak_pub ( self . refresh_tpm_seeds )
561
+ . create_ak_pub ( force_ak_regen )
549
562
. map_err ( TpmErrorKind :: CreateAkPublic ) ?;
550
563
let ek_pub = self
551
564
. tpm_engine_helper
552
565
. create_ek_pub ( )
553
566
. map_err ( TpmErrorKind :: CreateEkPublic ) ?;
554
567
self . keys = Some ( TpmKeys { ak_pub, ek_pub } ) ;
568
+ tracing:: info!(
569
+ CVM_ALLOWED ,
570
+ can_renew_ak = can_renew_ak,
571
+ "loaded existing AK from VMGS vTPM state"
572
+ ) ;
573
+ self . allow_ak_cert_renewal = can_renew_ak;
555
574
556
575
// Conditionally define nv indexes for ak cert and attestation report.
557
576
// The Nvram size can only be defined with platform hierarchy. Otherwise
@@ -869,6 +888,12 @@ impl Tpm {
869
888
/// This routine calls (via GET) external server to issue AK cert.
870
889
/// This function can only be called when `ak_cert_type` is `Trusted` or `HwAttested`.
871
890
fn renew_ak_cert ( & mut self ) -> Result < ( ) , TpmError > {
891
+ // Silently do nothing if renewal is not allowed.
892
+ if !self . allow_ak_cert_renewal {
893
+ tracing:: info!( CVM_ALLOWED , "AK cert renewal is not allowed" ) ;
894
+ return Ok ( ( ) ) ;
895
+ }
896
+
872
897
// Return if the request is pending
873
898
if self . async_ak_cert_request . is_some ( ) {
874
899
return Ok ( ( ) ) ;
@@ -983,6 +1008,12 @@ impl Tpm {
983
1008
984
1009
/// Renew device attestation data (i.e., attestation report and AK cert) on NV_Read if needed
985
1010
fn refresh_device_attestation_data_on_nv_read ( & mut self ) {
1011
+ // Silently do nothing if renewal is not allowed.
1012
+ if !self . allow_ak_cert_renewal {
1013
+ tracing:: info!( CVM_ALLOWED , "AK cert renewal is not allowed" ) ;
1014
+ return ;
1015
+ }
1016
+
986
1017
let Some ( nv_read) = tpm20proto:: protocol:: NvReadCmd :: deserialize ( & self . command_buffer )
987
1018
else {
988
1019
return ;
@@ -1334,6 +1365,19 @@ impl MmioIntercept for Tpm {
1334
1365
}
1335
1366
}
1336
1367
1368
+ /// Expected attributes for a correctly-provisioned AK.
1369
+ pub fn expected_ak_attributes ( ) -> TpmaObject {
1370
+ TpmaObjectBits :: new ( )
1371
+ . with_fixed_tpm ( true )
1372
+ . with_fixed_parent ( true )
1373
+ . with_sensitive_data_origin ( true )
1374
+ . with_user_with_auth ( true )
1375
+ . with_no_da ( true )
1376
+ . with_restricted ( true )
1377
+ . with_sign_encrypt ( true )
1378
+ . into ( )
1379
+ }
1380
+
1337
1381
/// The IO port interface bespoke to the Hyper-V implementation of the vTPM.
1338
1382
mod io_port_interface {
1339
1383
use inspect:: Inspect ;
@@ -1566,6 +1610,8 @@ mod save_restore {
1566
1610
pub auth_value : Option < u64 > ,
1567
1611
#[ mesh( 61 ) ]
1568
1612
pub keys : Option < SavedTpmKeys > ,
1613
+ #[ mesh( 62 ) ]
1614
+ pub allow_ak_cert_renewal : Option < bool > ,
1569
1615
}
1570
1616
}
1571
1617
@@ -1663,6 +1709,7 @@ mod save_restore {
1663
1709
tpm_state_blob : self . tpm_engine_helper . tpm_engine . save_state ( ) ,
1664
1710
auth_value : self . auth_value ,
1665
1711
keys,
1712
+ allow_ak_cert_renewal : Some ( self . allow_ak_cert_renewal ) ,
1666
1713
} ;
1667
1714
1668
1715
Ok ( saved_state)
@@ -1677,6 +1724,7 @@ mod save_restore {
1677
1724
tpm_state_blob,
1678
1725
auth_value,
1679
1726
keys,
1727
+ allow_ak_cert_renewal,
1680
1728
} = state;
1681
1729
1682
1730
self . control_area = {
@@ -1743,6 +1791,18 @@ mod save_restore {
1743
1791
} ,
1744
1792
} ) ;
1745
1793
1794
+ if allow_ak_cert_renewal. is_none ( ) {
1795
+ // Whether AKCert renewal is allowed depends on the attributes of the AK
1796
+ // saved in the vTPM. It may not be safe to read it here (which requires
1797
+ // executing a readpublic command) because the vTPM may be in the middle
1798
+ // of executing another command.
1799
+ tracing:: info!(
1800
+ CVM_ALLOWED ,
1801
+ "vTPM servicing state does not include allow_ak_cert_renewal; denying renewal until reboot"
1802
+ ) ;
1803
+ }
1804
+ self . allow_ak_cert_renewal = allow_ak_cert_renewal. unwrap_or ( false ) ;
1805
+
1746
1806
Ok ( ( ) )
1747
1807
}
1748
1808
}
0 commit comments