@@ -105,6 +105,20 @@ pub trait VtpmInterface: TcgTpmSimulatorInterface {
105105 fn get_ekpub ( & mut self ) -> Result < Vec < u8 > , SvsmReqError > ;
106106}
107107
108+ /// vTPM boot mode driving the persistence cycle.
109+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
110+ pub enum VtpmBootMode {
111+ /// First boot: manufacture vTPM, extract internal state, seal to the
112+ /// external TPM, return the sealed blob for host-side storage.
113+ Provision ,
114+ /// Subsequent boot: unseal the sealed blob, inject state, light power-on.
115+ Recover ,
116+ }
117+
118+ /// VSOCK addressing for the host-side TPM endpoint.
119+ pub const VSOCK_HOST_CID : u32 = 2 ;
120+ pub const VSOCK_TPM_PORT : u32 = 9999 ;
121+
108122static VTPM : SpinLock < Vtpm > = SpinLock :: new ( Vtpm :: new ( ) ) ;
109123
110124/// Initialize the TPM by calling the init() implementation of the
@@ -118,6 +132,180 @@ pub fn vtpm_init() -> Result<(), SvsmReqError> {
118132 Ok ( ( ) )
119133}
120134
135+ /// vTPM initialization with seal/unseal integration.
136+ ///
137+ /// Provision mode: manufacture vTPM, extract internal state, AES-256-GCM
138+ /// encrypt, TPM2_Seal the key bundle, return the packed SealedBlob bytes.
139+ /// Recover mode: unpack the SealedBlob, TPM2_Unseal the key bundle, AES
140+ /// decrypt, inject internal state, perform a light power-on.
141+ pub fn vtpm_init_sealed < T : TpmTransport > (
142+ transport : T ,
143+ mode : VtpmBootMode ,
144+ vm_id : [ u8 ; 16 ] ,
145+ sealed_blob_data : Option < & [ u8 ] > ,
146+ ) -> Result < Option < Vec < u8 > > , SvsmReqError > {
147+ let mut vtpm = VTPM . lock ( ) ;
148+ if vtpm. is_powered_on ( ) {
149+ return Ok ( None ) ;
150+ }
151+
152+ let mut proxy = TpmProxy :: new ( transport) ;
153+
154+ match mode {
155+ VtpmBootMode :: Provision => {
156+ vtpm. init ( ) ?;
157+ log:: info!( "VTPM: manufactured (Provision mode)" ) ;
158+
159+ // Bulk-serialize the TPM internal state (seeds, PCR save area,
160+ // auth values, counters) into an opaque byte buffer. This is
161+ // round-tripped via inject_serialized_state() on Recover.
162+ let serialized = state:: extract_serialized_state ( ) ?;
163+ log:: info!(
164+ "VTPM: internal state extracted ({} bytes)" ,
165+ serialized. len( )
166+ ) ;
167+
168+ let vtpm_state = VtpmState {
169+ ek_priv : Vec :: new ( ) ,
170+ ek_pub : vtpm. get_ekpub ( ) ?,
171+ srk_priv : Vec :: new ( ) ,
172+ srk_pub : Vec :: new ( ) ,
173+ owner_auth : [ 0u8 ; 32 ] ,
174+ endorsement_auth : [ 0u8 ; 32 ] ,
175+ lockout_auth : [ 0u8 ; 32 ] ,
176+ nv_data : Vec :: new ( ) ,
177+ nv_counter : 0 ,
178+ platform_auth : [ 0u8 ; 32 ] ,
179+ extra : serialized,
180+ } ;
181+
182+ let ( aes_key, nonce) = platform_entropy ( ) ?;
183+ let blob = sealed:: seal_state ( & mut proxy, & vtpm_state, vm_id, & aes_key, & nonce)
184+ . map_err ( |_| SvsmReqError :: invalid_request ( ) ) ?;
185+ zeroize_key_material ( & aes_key, & nonce) ;
186+
187+ log:: info!(
188+ "VTPM: state sealed to TPM (counter={}, enc_size={})" ,
189+ blob. counter,
190+ blob. encrypted_data. len( )
191+ ) ;
192+
193+ proxy. flush_primary ( ) ;
194+ Ok ( Some ( blob. pack ( ) ) )
195+ }
196+
197+ VtpmBootMode :: Recover => {
198+ let blob_data = sealed_blob_data. ok_or_else ( || {
199+ log:: error!( "VTPM: Recover mode requires sealed_blob_data" ) ;
200+ SvsmReqError :: invalid_request ( )
201+ } ) ?;
202+
203+ let blob = SealedBlob :: unpack ( blob_data) . map_err ( |_| {
204+ log:: error!( "VTPM: failed to unpack SealedBlob" ) ;
205+ SvsmReqError :: invalid_request ( )
206+ } ) ?;
207+
208+ log:: info!( "VTPM: SealedBlob loaded (counter={})" , blob. counter) ;
209+
210+ let vtpm_state = sealed:: unseal_state ( & mut proxy, & blob) . map_err ( |_| {
211+ log:: error!( "VTPM: unseal_state failed" ) ;
212+ SvsmReqError :: invalid_request ( )
213+ } ) ?;
214+
215+ proxy. flush_primary ( ) ;
216+
217+ // Inject the serialized internal state back into the TPM via the
218+ // C-side bulk deserializer.
219+ state:: inject_serialized_state ( & vtpm_state. extra ) . map_err ( |_| {
220+ log:: error!( "VTPM: inject_serialized_state failed" ) ;
221+ SvsmReqError :: invalid_request ( )
222+ } ) ?;
223+
224+ log:: info!( "VTPM: internal state injected" ) ;
225+
226+ vtpm. signal_poweron ( false ) ?;
227+ vtpm. signal_nvon ( ) ?;
228+
229+ log:: info!(
230+ "VTPM: state recovered from TPM seal (counter={})" ,
231+ blob. counter
232+ ) ;
233+
234+ Ok ( None )
235+ }
236+ }
237+ }
238+
239+ /// Generate an AES-256 key and a GCM nonce from the platform entropy source
240+ /// (RDSEED).
241+ fn platform_entropy ( ) -> Result < ( [ u8 ; 32 ] , [ u8 ; 12 ] ) , SvsmReqError > {
242+ let mut key = [ 0u8 ; 32 ] ;
243+ let mut nonce = [ 0u8 ; 12 ] ;
244+
245+ // SAFETY: `_rdseed64_step` is a CPU intrinsic with no memory side-effects
246+ // beyond the pointed-to u64; pointers come from properly aligned slices.
247+ unsafe {
248+ for i in 0 ..4 {
249+ let mut retries = 0 ;
250+ loop {
251+ if core:: arch:: x86_64:: _rdseed64_step (
252+ & mut * ( key[ i * 8 ..] [ ..8 ] . as_mut_ptr ( ) as * mut u64 ) ,
253+ ) == 1
254+ {
255+ break ;
256+ }
257+ retries += 1 ;
258+ if retries > 100 {
259+ return Err ( SvsmReqError :: invalid_request ( ) ) ;
260+ }
261+ core:: arch:: x86_64:: _mm_pause ( ) ;
262+ }
263+ }
264+ let mut retries = 0 ;
265+ loop {
266+ if core:: arch:: x86_64:: _rdseed64_step ( & mut * ( nonce[ ..8 ] . as_mut_ptr ( ) as * mut u64 ) ) == 1
267+ {
268+ break ;
269+ }
270+ retries += 1 ;
271+ if retries > 100 {
272+ return Err ( SvsmReqError :: invalid_request ( ) ) ;
273+ }
274+ core:: arch:: x86_64:: _mm_pause ( ) ;
275+ }
276+ let mut retries = 0 ;
277+ loop {
278+ if core:: arch:: x86_64:: _rdseed64_step ( & mut * ( nonce[ 8 ..] . as_mut_ptr ( ) as * mut u64 ) ) == 1
279+ {
280+ break ;
281+ }
282+ retries += 1 ;
283+ if retries > 100 {
284+ return Err ( SvsmReqError :: invalid_request ( ) ) ;
285+ }
286+ core:: arch:: x86_64:: _mm_pause ( ) ;
287+ }
288+ }
289+
290+ Ok ( ( key, nonce) )
291+ }
292+
293+ /// Zeroize key material in-place using volatile writes.
294+ fn zeroize_key_material ( aes_key : & [ u8 ; 32 ] , nonce : & [ u8 ; 12 ] ) {
295+ // SAFETY: volatile writes into stack-owned arrays whose lifetimes are
296+ // still live; pointers are valid for `write_volatile` and within bounds.
297+ unsafe {
298+ let key_ptr = aes_key. as_ptr ( ) as * mut u8 ;
299+ for i in 0 ..32 {
300+ key_ptr. add ( i) . write_volatile ( 0 ) ;
301+ }
302+ let nonce_ptr = nonce. as_ptr ( ) as * mut u8 ;
303+ for i in 0 ..12 {
304+ nonce_ptr. add ( i) . write_volatile ( 0 ) ;
305+ }
306+ }
307+ }
308+
121309pub fn vtpm_get_locked < ' a > ( ) -> LockGuard < ' a , Vtpm > {
122310 VTPM . lock ( )
123311}
@@ -128,3 +316,13 @@ pub fn vtpm_get_manifest() -> Result<Vec<u8>, SvsmReqError> {
128316 let mut vtpm = VTPM . lock ( ) ;
129317 vtpm. get_ekpub ( )
130318}
319+
320+ // Re-export sealed/proxy/state types for use by kernel init (svsm.rs)
321+ #[ cfg( feature = "vsock" ) ]
322+ pub use proxy:: VsockTransport ;
323+ pub use proxy:: { MockTransport , TpmProxy , TpmTransport } ;
324+ pub use sealed:: { SealedBlob , VtpmState } ;
325+ #[ cfg( feature = "vsock" ) ]
326+ pub use sealed_store:: VsockHostStore ;
327+ pub use sealed_store:: { IgvmVarStore , SealedBlobStore , StaticBufStore } ;
328+ pub use state:: VtpmInternalState ;
0 commit comments