Skip to content

Initial implementation of all core functionality of TPM 2.0#1

Open
hyperfinitism wants to merge 1 commit intomainfrom
feature/core-functionality
Open

Initial implementation of all core functionality of TPM 2.0#1
hyperfinitism wants to merge 1 commit intomainfrom
feature/core-functionality

Conversation

@hyperfinitism
Copy link
Owner

This PR implements 100 subcommands covering all core functionality of TPM 2.0, including cryptography, key management, PCR bank, NV storage, attestation, session and policy management, and utilities.

@hyperfinitism hyperfinitism self-assigned this Mar 9, 2026
@hyperfinitism hyperfinitism added the enhancement New feature or request label Mar 9, 2026
@hyperfinitism hyperfinitism force-pushed the feature/core-functionality branch from f5bf612 to 7ef75b7 Compare March 9, 2026 15:12
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers an initial implementation of a Rust-based command-line tool for TPM 2.0, providing a broad range of functionalities for interacting with and managing TPM devices. It aims to offer a secure and reliable alternative to existing tools, leveraging Rust's safety features.

Highlights

  • Core TPM 2.0 Functionality: This PR introduces a comprehensive set of subcommands, totaling 100, that expose the core functionalities of the TPM 2.0. These commands cover a wide spectrum of TPM operations.
  • Command-Line Interface: The changes establish a command-line interface (CLI) for interacting with TPM 2.0 devices, enabling users to manage cryptographic keys, PCR banks, NV storage, attestation, sessions, and policies directly from the command line.
  • Rust Implementation: The implementation is written in Rust, leveraging the tss-esapi crate for interacting with the TPM and providing memory safety guarantees.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Activity
  • Added .gitattributes to configure line endings.
  • Added Cargo.lock to manage dependencies.
  • Added Cargo.toml to define project metadata and dependencies.
  • Modified README.md to include project description, quick start guide, and comparison with tpm2-tools.
  • Added src/cli.rs to define the command-line interface structure.
  • Added src/cmd/* to implement individual TPM commands.
  • Added src/context.rs to manage TPM context creation.
  • Added src/error.rs to define custom error types.
  • Added src/handle.rs to manage TPM handles and object loading.
  • Added src/logger.rs to configure logging.
  • Added src/output.rs to handle command output formatting.
  • Added src/parse.rs to parse command-line arguments.
  • Added src/pcr.rs to manage PCR-related operations.
  • Added src/raw_esys.rs to provide raw ESAPI FFI wrappers.
  • Added src/session.rs to manage TPM sessions.
  • Added src/tcti.rs to handle TCTI configuration.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Generative AI Prohibited Use Policy, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7ef75b734d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

if let Some(ref path) = self.public_out {
std::fs::write(path, format!("{:?}", result.out_public))?;

Choose a reason for hiding this comment

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

P1 Badge Serialize created public key in TPM binary format

The create command writes out_public using Debug formatting, so the --public output is text instead of TPM marshaled bytes. That breaks the documented createload flow because load expects PublicBuffer::try_from(pub_bytes) binary data (src/cmd/load.rs:72), causing invalid public buffer when users try to load keys created by this command.

Useful? React with 👍 / 👎.

Comment on lines +81 to +83
let tss_ticket: TPMT_TK_HASHCHECK =
unsafe { std::ptr::read(ticket_data.as_ptr() as *const TPMT_TK_HASHCHECK) };
HashcheckTicket::try_from(tss_ticket)

Choose a reason for hiding this comment

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

P1 Badge Unmarshal hash tickets instead of raw struct casts

This reads ticket bytes with ptr::read into TPMT_TK_HASHCHECK, which treats file contents as native in-memory layout (endianness/alignment/padding dependent) rather than TPM marshaled format. In practice, tickets produced by other TPM tooling or different architectures can be misparsed and rejected, so restricted-key signing with --ticket becomes unreliable.

Useful? React with 👍 / 👎.

Comment on lines +75 to +76
} else if self.hmac_session || self.audit_session {
SessionType::Hmac

Choose a reason for hiding this comment

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

P2 Badge Set audit bit when --audit-session is selected

The CLI advertises --audit-session as “HMAC with audit flag”, but this branch only changes the session type to Hmac and never sets the session audit attribute before saving the context. As a result, sessions created with --audit-session behave like regular HMAC sessions and do not produce audit behavior the option name promises.

Useful? React with 👍 / 👎.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive set of command-line tools for interacting with TPM 2.0, covering a wide range of core functionalities. The overall structure is well-organized, with clear separation of concerns into modules for CLI parsing, command implementation, and various utilities. The use of helpers for session management, handle loading, and raw ESYS FFI calls is commendable. However, there are several areas for improvement regarding configuration, consistency, and correctness that should be addressed.

Note: Security Review is unavailable for this PR.

Comment on lines +72 to +80
fn credentialed_context_source(&self) -> anyhow::Result<ContextSource> {
match (&self.credentialed_context, self.credentialed_context_handle) {
(Some(path), None) => Ok(ContextSource::File(path.clone())),
(None, Some(handle)) => Ok(ContextSource::Handle(handle)),
_ => anyhow::bail!(
"exactly one of --credentialedkey-context or --credentialedkey-context-handle must be provided"
),
}
}

Choose a reason for hiding this comment

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

high

The error message "exactly one of ... must be provided" is incorrect for the case where neither --credentialedkey-context nor --credentialedkey-context-handle is provided. clap's conflicts_with attribute prevents both from being used, but doesn't enforce that at least one is present. The logic should be updated to provide a more accurate error message when neither argument is given. This pattern is repeated for credential_key_context_source in this file and for similar context/handle source functions in many other command files.

Suggested change
fn credentialed_context_source(&self) -> anyhow::Result<ContextSource> {
match (&self.credentialed_context, self.credentialed_context_handle) {
(Some(path), None) => Ok(ContextSource::File(path.clone())),
(None, Some(handle)) => Ok(ContextSource::Handle(handle)),
_ => anyhow::bail!(
"exactly one of --credentialedkey-context or --credentialedkey-context-handle must be provided"
),
}
}
fn credentialed_context_source(&self) -> anyhow::Result<ContextSource> {
match (&self.credentialed_context, self.credentialed_context_handle) {
(Some(path), None) => Ok(ContextSource::File(path.clone())),
(None, Some(handle)) => Ok(ContextSource::Handle(handle)),
(None, None) => anyhow::bail!(
"one of --credentialedkey-context or --credentialedkey-context-handle must be provided"
),
(Some(_), Some(_)) => unreachable!("clap should prevent both arguments from being used"),
}
}

README.md Outdated

- **Extended context file support**: Some arguments in `tpm2-tools` accept only a TPM handle in hex string form without an apparent reason. `rust-tpm2-cli` removes this restriction and allows a context file to be specified wherever it is semantically appropriate.

- **Subcommand splitting**: Subcommands that conflate distinct operations have been separated. For example, the `encryptdecrypt` subcommand of `tpm2-tools` is split into two dedicated subcommands.

Choose a reason for hiding this comment

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

medium

The documentation states that the encryptdecrypt subcommand is split into two dedicated subcommands (encrypt and decrypt). However, the encryptdecrypt subcommand is still present in the CLI implementation alongside the new ones. This is confusing for users. Please either remove the encryptdecrypt subcommand to align with the documentation or update the documentation to clarify its purpose (e.g., if it's kept for compatibility with tpm2-tools).

/// Encrypt data with a symmetric TPM key
Encrypt(cmd::encrypt::EncryptCmd),
/// Encrypt or decrypt data with a symmetric key
Encryptdecrypt(cmd::encryptdecrypt::EncryptDecryptCmd),

Choose a reason for hiding this comment

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

medium

The encryptdecrypt subcommand appears to be redundant, as separate encrypt and decrypt subcommands are also implemented. As mentioned in the README.md, splitting this command improves clarity. To maintain a clean and unambiguous API, consider removing the Encryptdecrypt variant.

"ecc" => {
let params = PublicEccParametersBuilder::new()
.with_ecc_scheme(EccScheme::EcDsa(HashScheme::new(hash_alg)))
.with_curve(EccCurve::NistP256)

Choose a reason for hiding this comment

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

medium

The ECC curve is hardcoded to NistP256. This limits the command's utility. It would be better to allow users to specify the curve via a command-line argument, for example --curve.

Comment on lines +182 to +236
fn build_ak_public(alg: &str, hash_alg: HashingAlgorithm) -> anyhow::Result<Public> {
let attributes = ObjectAttributesBuilder::new()
.with_fixed_tpm(true)
.with_fixed_parent(true)
.with_sensitive_data_origin(true)
.with_user_with_auth(true)
.with_sign_encrypt(true)
.with_restricted(true)
.build()
.context("failed to build object attributes")?;

let builder = PublicBuilder::new()
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
.with_object_attributes(attributes);

match alg.to_lowercase().as_str() {
"rsa" => {
let params = PublicRsaParametersBuilder::new()
.with_scheme(RsaScheme::RsaSsa(HashScheme::new(hash_alg)))
.with_key_bits(RsaKeyBits::Rsa2048)
.with_exponent(RsaExponent::default())
.with_is_signing_key(true)
.with_restricted(true)
.with_symmetric(SymmetricDefinitionObject::Null)
.build()
.context("failed to build RSA parameters")?;

builder
.with_public_algorithm(PublicAlgorithm::Rsa)
.with_rsa_parameters(params)
.with_rsa_unique_identifier(Default::default())
.build()
.context("failed to build RSA AK public")
}
"ecc" => {
let params = PublicEccParametersBuilder::new()
.with_ecc_scheme(EccScheme::EcDsa(HashScheme::new(hash_alg)))
.with_curve(EccCurve::NistP256)
.with_is_signing_key(true)
.with_restricted(true)
.with_symmetric(SymmetricDefinitionObject::Null)
.with_key_derivation_function_scheme(KeyDerivationFunctionScheme::Null)
.build()
.context("failed to build ECC parameters")?;

builder
.with_public_algorithm(PublicAlgorithm::Ecc)
.with_ecc_parameters(params)
.with_ecc_unique_identifier(Default::default())
.build()
.context("failed to build ECC AK public")
}
_ => bail!("unsupported AK algorithm: {alg}; supported: rsa, ecc"),
}
}

Choose a reason for hiding this comment

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

medium

The build_ak_public function hardcodes several parameters, such as the name hashing algorithm (Sha256), RSA key size (Rsa2048), and ECC curve (NistP256). This reduces the flexibility of the createak command. Consider adding command-line arguments to allow users to configure these parameters.

Comment on lines +159 to +217
fn build_ek_public(alg: &str) -> anyhow::Result<Public> {
let auth_policy = Digest::try_from(EK_AUTH_POLICY_SHA256.as_slice())
.map_err(|e| anyhow::anyhow!("invalid auth policy: {e}"))?;

let attributes = ObjectAttributesBuilder::new()
.with_fixed_tpm(true)
.with_fixed_parent(true)
.with_sensitive_data_origin(true)
.with_admin_with_policy(true)
.with_restricted(true)
.with_decrypt(true)
.build()
.context("failed to build object attributes")?;

let builder = PublicBuilder::new()
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
.with_object_attributes(attributes)
.with_auth_policy(auth_policy);

match alg.to_lowercase().as_str() {
"rsa" => {
let params = PublicRsaParametersBuilder::new()
.with_scheme(RsaScheme::Null)
.with_key_bits(RsaKeyBits::Rsa2048)
.with_exponent(RsaExponent::default())
.with_is_decryption_key(true)
.with_restricted(true)
.with_symmetric(SymmetricDefinitionObject::AES_128_CFB)
.build()
.context("failed to build RSA parameters")?;

builder
.with_public_algorithm(PublicAlgorithm::Rsa)
.with_rsa_parameters(params)
.with_rsa_unique_identifier(Default::default())
.build()
.context("failed to build RSA EK public template")
}
"ecc" => {
let params = PublicEccParametersBuilder::new()
.with_ecc_scheme(EccScheme::Null)
.with_curve(EccCurve::NistP256)
.with_is_decryption_key(true)
.with_restricted(true)
.with_symmetric(SymmetricDefinitionObject::AES_128_CFB)
.with_key_derivation_function_scheme(KeyDerivationFunctionScheme::Null)
.build()
.context("failed to build ECC parameters")?;

builder
.with_public_algorithm(PublicAlgorithm::Ecc)
.with_ecc_parameters(params)
.with_ecc_unique_identifier(Default::default())
.build()
.context("failed to build ECC EK public template")
}
_ => bail!("unsupported EK algorithm: {alg}; supported: rsa, ecc"),
}
}

Choose a reason for hiding this comment

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

medium

The build_ek_public function hardcodes the RSA key size to Rsa2048 and the ECC curve to NistP256. To increase the command's flexibility, these should be configurable via command-line arguments.

Comment on lines +35 to +36
#[arg(short = 's', long = "setup-parameters")]
pub setup_parameters: bool,

Choose a reason for hiding this comment

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

medium

The help text for --setup-parameters implies that --max-tries, --recovery-time, and --lockout-recovery-time are required. However, the implementation provides default values if they are not specified. This should be made consistent. You can either update the help text, or enforce that the arguments are provided when --setup-parameters is used.

For example, you could make the arguments required using clap attributes:

#[arg(long = "max-tries", required_if_eq("setup_parameters", "true"))]
pub max_tries: Option<u32>,

And then unwrap() them in execute.

Implements 100 subcommands covering all core functionality of TPM 2.0,
including cryptography, key management, PCR bank, NV storage,
attestation,
session and policy management, and utilities.

Signed-off-by: Takuma IMAMURA
<209989118+hyperfinitism@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@hyperfinitism hyperfinitism force-pushed the feature/core-functionality branch from 7ef75b7 to 5078aa5 Compare March 10, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant