Skip to content

Add CvmUtil support for CPSinTEE provisioning and vTPM-backed disk-unlock workflows. - #4475

Open
Fikret Can (canfikret) wants to merge 23 commits into
microsoft:mainfrom
canfikret:main
Open

Fikret Can (canfikret) wants to merge 23 commits into
microsoft:mainfrom
canfikret:main

Conversation

@canfikret

Copy link
Copy Markdown

Changes

  • Add commands for creating and inspecting persisted vTPM state and SRK material.
  • Support offline sealing, online unsealing, and TPM import/export blob formats.
  • Export RSA public keys in DER PKCS#1 format and correct SRK serialization and RSA exponent handling.
  • Add compatibility fixes for Ubuntu sealed-key disk unlock.
  • Add a socket-based TPM simulator with data/control ports, persistent state, and graceful Ctrl+C shutdown.
  • Extend TPM library and protocol definitions required by these workflows.
  • Update workspace dependencies and align the new code with Rust 2024.

Validation

  • cargo clippy --all-targets -p cvmutil
  • cargo doc --no-deps -p cvmutil
  • cargo test -p cvmutil
  • cargo xtask fmt --fix

@canfikret
Fikret Can (canfikret) requested a review from a team as a code owner September 17, 2026 16:31
Copilot AI lite review requested due to automatic review settings September 17, 2026 16:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical sealing, key export/import, serialization, and simulator correctness issues remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds cvmutil support for vTPM provisioning, sealed-key workflows, TPM import/export, and socket-based TPM simulation.

Changes:

  • Extends TPM protocol and library helpers.
  • Adds sealing, unsealing, SRK, and import/export workflows.
  • Adds persistent vTPM socket simulator support.
  • Registers the new workspace package and dependencies.
File summaries
File Description
vm/devices/tpm/tpm_protocol/src/tpm20proto.rs TPM compatibility parsing and structure updates
vm/devices/tpm/tpm_protocol/Cargo.toml Adds tracing dependency
vm/devices/tpm/tpm_lib/src/lib.rs TPM helpers, SRK templates, and tests
vm/devices/tpm/tpm_device/src/lib.rs TPM handle and index constants
vm/cvmutil/src/vtpm_sock_server.rs Socket-based TPM simulator
vm/cvmutil/src/vtpm_helper.rs vTPM engine and state callbacks
vm/cvmutil/src/marshal.rs Sealed-key and AF-split serialization
vm/cvmutil/src/main.rs CLI workflows and TPM operations
vm/cvmutil/Cargo.toml New utility package dependencies
Cargo.toml Workspace membership
Cargo.lock Dependency lock updates
Review details

Suppressed comments (5)

vm/cvmutil/Cargo.toml:7

  • The newly added cvmutil exposes multiple user-facing provisioning, sealing, import/export, and socket-server commands, but there is no corresponding Guide developer-tool page or SUMMARY/mapping entry. This leaves the documented command contract and security-sensitive workflow out of the repository documentation.
[package]
name = "cvmutil"
edition.workspace = true
rust-version.workspace = true

vm/cvmutil/src/main.rs:35

  • The new cvmutil command-line tool introduces multiple user-facing commands, but it has no corresponding Guide documentation or entry. Please add a developer-tool/reference page covering the flags, argument order, blob formats, and socket ports, and link it from Guide/src/SUMMARY.md; this is needed to keep the user-facing interface discoverable and synchronized.
#[derive(Parser, Debug)]
#[clap(name = "cvmutil", about = "Tool to interact with vTPM blobs.")]

vm/cvmutil/src/vtpm_sock_server.rs:315

  • The Stop command only returns success and breaks the control-client loop; it never changes running, so the server and data listener continue accepting connections. A simulator client requesting Stop cannot actually stop this server.
        ControlCommand::Stop => {
            tracing::info!("TPM Stop requested");
            vec![0x00, 0x00, 0x00, 0x00] // Success
        }

vm/cvmutil/src/vtpm_sock_server.rs:46

  • The documented bind address accepts port 65535, but adding one for the control listener overflows u16 and panics. Reject the maximum data port (or choose a separately validated control port) before performing this addition.
    // Parse the bind address to extract host and port
    let (host, data_port) = parse_bind_address(bind_addr);
    let ctrl_port = data_port + 1; // Control port is typically data_port + 1

    tracing::info!("Data port: {}, Control port: {}", data_port, ctrl_port);

vm/cvmutil/src/vtpm_sock_server.rs:355

  • The TPM simulator acknowledges MS_SIM_NV_ON with success without changing any TPM state. Clients use this control command to enable NV access before issuing commands; reporting success while leaving NV disabled can make subsequent provisioning fail or behave differently from the advertised simulator protocol. Implement the state transition or return an unsupported/error response.
        ControlCommand::NvOn => {
            tracing::info!("MS_SIM_NV_ON (TPM NV Enable) requested");

            // This is the command that was failing
            // Enable NV storage in the TPM
            // The ms-tpm-20-ref might have specific methods for this

            // For now, acknowledge success
            vec![0x00, 0x00, 0x00, 0x00] // Success
  • Files reviewed: 10/11 changed files
  • Comments generated: 14
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread vm/cvmutil/src/main.rs
Comment on lines +818 to +837
/// Seal data to SRK using TPM-standard format compatible with Ubuntu secboot.
fn seal_data_to_srk(_srk_pub_path: &str, input_file: &str, output_file: &str) {
use marshal::{AfSplitData, CURRENT_METADATA_VERSION, KEY_DATA_HEADER};
use std::fs;

tracing::info!("Creating TPM-standard sealed key compatible with Ubuntu secboot");
tracing::info!("Reading input data from: {}", input_file);
let input_data = fs::read(input_file).expect("failed to read input file");
tracing::info!("Input data size: {} bytes", input_data.len());

// Create minimal TPM structures for a sealed data object
// We'll create a simple keyedobject that contains the sealed data

// 1. Create a minimal TPM2B_PRIVATE containing our data
let key_private = Tpm2bBuffer::new(&input_data).expect("input data too large for TPM2B buffer");

// 2. Create a minimal TPM2B_PUBLIC for the sealed object
// Use the SRK public key template but mark it as a data object
let srk_template = tpm_helper::srk_pub_template().expect("failed to create SRK template");
let mut sealed_template = srk_template;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this command is for testing only.

Comment thread vm/cvmutil/src/main.rs
Comment on lines +1102 to +1134
// The TPM2B_PRIVATE contains our original sealed data
// In our implementation, we stored the data directly in the TPM2B_PRIVATE buffer
let sealed_data_size = key_private.size.get() as usize;
if sealed_data_size == 0 {
panic!("No data found in sealed key");
}

let sealed_data = &key_private.buffer[0..sealed_data_size];

// Check if this looks like our sealed object by examining the unique field in the public key
let unique_marker = &key_public.public_area.unique.buffer[0..4];
if unique_marker[0] == 0xDA && unique_marker[1] == 0x7A {
// This is our sealed data format
let expected_data_size = (unique_marker[2] as usize) | ((unique_marker[3] as usize) << 8);
tracing::info!(
"Detected sealed data object, expected size: {} bytes",
expected_data_size
);

if sealed_data_size != expected_data_size {
tracing::warn!(
"Data size mismatch: stored {} bytes, expected {} bytes",
sealed_data_size,
expected_data_size
);
}
}

tracing::info!("Extracted original data: {} bytes", sealed_data.len());

// Write the unsealed data
tracing::info!("Writing unsealed data to: {}", output_file);
fs::write(output_file, sealed_data).expect("failed to write unsealed data file");
Comment thread vm/cvmutil/src/main.rs
Comment on lines +1298 to +1302
// Create a TPM2B_PRIVATE structure
// For RSA import format, use the first prime factor (p), not the private exponent (d)
let prime1_bytes = rsa.p().unwrap().to_vec();
tracing::trace!("RSA prime1 (p) size: {} bytes", prime1_bytes.len());
let sensitive_rsa = Tpm2bBuffer::new(&prime1_bytes).unwrap();
Comment thread vm/cvmutil/src/main.rs
Comment on lines +1897 to +1913
// Since we don't have access to the actual private key from create_primary,
// we still need to create dummy private key data
// TODO: Implement TPM2_Create under SRK to get real private key data
let mut dummy_private_data = vec![0u8; 64];
getrandom::fill(&mut dummy_private_data).expect("Failed to generate dummy private data");
let dummy_private = Tpm2bBuffer::new(&dummy_private_data);

// Clean up the temporary key handle
if let Err(e) = tpm_engine_helper.flush_context(key_handle) {
tracing::warn!("Failed to flush temporary key context: {:?}", e);
}

// Create the sealed key data with the new key public area and dummy private data
let sealed_key_data = create_sealed_key_blob_v2_with_real_data(
&dummy_private.unwrap(),
&key_public,
&import_seed,
Comment thread vm/cvmutil/src/marshal.rs
Comment thread vm/cvmutil/src/marshal.rs
Comment thread vm/cvmutil/src/marshal.rs
Comment thread vm/cvmutil/src/marshal.rs
Comment thread vm/cvmutil/src/vtpm_sock_server.rs
Comment thread vm/cvmutil/src/vtpm_sock_server.rs
Comment thread vm/cvmutil/Cargo.toml
Comment thread vm/devices/tpm/tpm_protocol/Cargo.toml
Comment thread vm/devices/tpm/tpm_protocol/src/tpm20proto.rs
Comment thread vm/devices/tpm/tpm_protocol/src/tpm20proto.rs
Comment thread vm/devices/tpm/tpm_lib/src/lib.rs
Comment thread vm/devices/tpm/tpm_device/src/lib.rs
Comment thread vm/cvmutil/src/main.rs
Comment thread vm/cvmutil/src/main.rs
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! The module includes the CvmUtil, which is a tool to create and manage vTPM blobs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! The module includes the CvmUtil, which is a tool to create and manage vTPM blobs.
//! CvmUtil is a tool to create and manage vTPM blobs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want more explanation here on what these blobs contain and how they're used.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also probably a better name. Is there anything about this that's really CVM specific? Could it just be tpm_util?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is historically cvmutil and currently CPS expects that. I'm open to rename it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might rename to vtpm_util

Comment thread vm/cvmutil/src/main.rs
Comment thread vm/cvmutil/src/main.rs
Comment thread vm/cvmutil/src/main.rs
Comment thread vm/cvmutil/Cargo.toml
Comment thread vm/cvmutil/src/main.rs
Comment thread vm/cvmutil/src/marshal.rs
@smalis-msft

Copy link
Copy Markdown
Contributor

Lets also make sure #4481 gets merged first, and this PR can be rebased on top of it and add a guide page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants