Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions cmd/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,7 @@ import (
)

var (
key string
teeTechnology string
)

// Add constants for other devices when required
const (
// SevSnp is a constant denotes device name for teeTechnology
SevSnp = "sev-snp"
// Tdx is a constant denotes device name for teeTechnology
Tdx = "tdx"
key string
)

var attestationKeys = map[string]map[tpm2.Algorithm]func(rw io.ReadWriter) (*client.Key, error){
Expand Down Expand Up @@ -183,10 +174,6 @@ func addKeyFlag(cmd *cobra.Command) {
cmd.PersistentFlags().StringVar(&key, "key", "AK", "indicates type of attestation key to use <gceAK|AK>")
}

func addTeeTechnology(cmd *cobra.Command) {
cmd.PersistentFlags().StringVar(&teeTechnology, "tee-technology", "", "indicates the type of TEE hardware. Should be either empty or one of sev-snp or tdx")
}

func init() {
RootCmd.AddCommand(attestCmd)
addKeyFlag(attestCmd)
Expand Down
39 changes: 26 additions & 13 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,28 @@ import (
)

var (
output string
input string
nvIndex uint32
nonce []byte
teeNonce []byte
keyAlgo = tpm2.AlgRSA
pcrs []int
format string
asAddress string
audience string
eventLog string
cloudLog bool
customNonce []string
output string
input string
nvIndex uint32
nonce []byte
teeNonce []byte
teeTechnology string
keyAlgo = tpm2.AlgRSA
pcrs []int
format string
asAddress string
audience string
eventLog string
cloudLog bool
customNonce []string
)

// Add constants for other devices when required
const (
// SevSnp is a constant denotes device name for teeTechnology
SevSnp = "sev-snp"
// Tdx is a constant denotes device name for teeTechnology
Tdx = "tdx"
)

type pcrsFlag struct {
Expand Down Expand Up @@ -192,6 +201,10 @@ func addTeeNonceflag(cmd *cobra.Command) {
cmd.PersistentFlags().BytesHexVar(&teeNonce, "tee-nonce", []byte{}, "hex encoded teenonce for hardware attestation, can be empty")
}

func addTeeTechnology(cmd *cobra.Command) {
cmd.PersistentFlags().StringVar(&teeTechnology, "tee-technology", "", "indicates the type of TEE hardware. Should be either empty or one of sev-snp or tdx")
}

// alwaysError implements io.ReadWriter by always returning an error
type alwaysError struct {
error
Expand Down
62 changes: 59 additions & 3 deletions cmd/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ package cmd

import (
"context"
_ "crypto/sha512" // Ensure SHA384 is available
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"os"
"time"

"cloud.google.com/go/compute/metadata"
"cloud.google.com/go/logging"
"github.com/golang-jwt/jwt/v4"
tabi "github.com/google/go-tdx-guest/abi"
"github.com/google/go-tpm-tools/client"
"github.com/google/go-tpm-tools/internal"
"github.com/google/go-tpm-tools/verifier"
"github.com/google/go-tpm-tools/verifier/models"
"github.com/google/go-tpm-tools/verifier/util"
Expand Down Expand Up @@ -128,7 +132,33 @@ The OIDC token includes claims regarding the GCE VM, which is verified by Attest
if err != nil {
return fmt.Errorf("failed to get an AK: %w", err)
}
attestation, err := ak.Attest(client.AttestOpts{Nonce: challenge.Nonce, CertChainFetcher: http.DefaultClient})

attestOpts := client.AttestOpts{Nonce: challenge.Nonce, CertChainFetcher: http.DefaultClient}

// Add logic to open other hardware devices when required.
switch teeTechnology {
case SevSnp:
attestOpts.TEEDevice, err = client.CreateSevSnpQuoteProvider()
if err != nil {
return fmt.Errorf("failed to open %s device: %v", SevSnp, err)
}
attestOpts.TEENonce = teeNonce
case Tdx:
attestOpts.TEEDevice, err = client.CreateTdxQuoteProvider()
if err != nil {
return fmt.Errorf("failed to create %s quote provider: %v", Tdx, err)
}
attestOpts.TEENonce = teeNonce
case "":
if len(teeNonce) != 0 {
return fmt.Errorf("use of --tee-nonce requires specifying TEE hardware type with --tee-technology")
}
default:
// Change the return statement when more devices are added
return fmt.Errorf("tee-technology should be either empty or should have values %s or %s", SevSnp, Tdx)
}

attestation, err := ak.Attest(attestOpts)
Copy link
Collaborator

@yawangwang yawangwang Jan 2, 2026

Choose a reason for hiding this comment

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

Currently there's no binding b/w TPM and TDX attestation, so it's premature to enable tee-nonce and tee-technology flags for the token command.

Comment on lines +138 to +161
Copy link
Contributor

Choose a reason for hiding this comment

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

This is duplicated with https://github.com/google/go-tpm-tools/blob/main/cmd/attest.go#L84-L105. Please refactor into a helper like func addTEEOpts(opts client.AttestOpts) error

if err != nil {
return fmt.Errorf("failed to attest: %v", err)
}
Expand All @@ -141,6 +171,32 @@ The OIDC token includes claims regarding the GCE VM, which is verified by Attest
TokenOptions: &models.TokenOptions{Audience: audience, Nonces: customNonce, TokenType: "OIDC"},
}

if teeTechnology == Tdx {
// If TDX, check if we should populate TDCCELAttestation
if attestation.GetTdxAttestation() != nil {
fmt.Fprintln(debugOutput(), "Using Explicit TDCCELAttestation Path (ACPI tables)")

rawQuote, err := tabi.QuoteToAbiBytes(attestation.GetTdxAttestation())
if err != nil {
return fmt.Errorf("failed to convert TDX quote to bytes: %v", err)
}

// Try to read CCEL Table and Data
ccelTable, _ := os.ReadFile(internal.AcpiTableFile)
ccelData, _ := os.ReadFile(internal.CcelEventLogFile)

req.TDCCELAttestation = &verifier.TDCCELAttestation{
TdQuote: rawQuote,
CcelAcpiTable: ccelTable,
CcelData: ccelData,
AkCert: attestation.AkCert,
IntermediateCerts: attestation.IntermediateCerts,
}
Comment on lines +188 to +194
Copy link
Collaborator

@yawangwang yawangwang Jan 2, 2026

Choose a reason for hiding this comment

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

This enables sending TDX CVM attestation to GCA for verification. However, since TDX CVM support is not yet GA, users will likely encounter errors when using the CLI.

This PR should be held until TDX CVM and hardware binding reach GA.

// Force using TDCCELAttestation path in verifier client
req.Attestation = nil
}
}

resp, err := verifierClient.VerifyAttestation(ctx, req)
if err != nil {
return err
Expand Down Expand Up @@ -210,6 +266,6 @@ func init() {
addEventLogFlag(tokenCmd)
addCustomNonceFlag(tokenCmd)
// TODO: Add TEE hardware OIDC token generation
// addTeeNonceflag(tokenCmd)
// addTeeTechnology(tokenCmd)
addTeeNonceflag(tokenCmd)
addTeeTechnology(tokenCmd)
}
4 changes: 2 additions & 2 deletions cmd/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ func TestTokenWithGCEAK(t *testing.T) {
}

if op.fail {
RootCmd.SetArgs([]string{"token", "--algo", op.algo, "--output", secretFile1, "--verifier-endpoint", mockAttestationServer.Server.URL, "--cloud-log", "--audience", util.FakeCustomAudience, "--custom-nonce", "fail test"})
RootCmd.SetArgs([]string{"token", "--algo", op.algo, "--output", secretFile1, "--verifier-endpoint", mockAttestationServer.Server.URL, "--cloud-log", "--audience", util.FakeCustomAudience, "--custom-nonce", "fail test", "--tee-technology", "", "--tee-nonce", ""})
if err := RootCmd.Execute(); err != nil && !strings.Contains(err.Error(), "googleapi: Error 400") {
t.Error(err)
}
} else {
RootCmd.SetArgs([]string{"token", "--algo", op.algo, "--output", secretFile1, "--verifier-endpoint", mockAttestationServer.Server.URL, "--cloud-log", "--audience", util.FakeCustomAudience, "--custom-nonce", util.FakeCustomNonce[0], "--custom-nonce", util.FakeCustomNonce[1]})
RootCmd.SetArgs([]string{"token", "--algo", op.algo, "--output", secretFile1, "--verifier-endpoint", mockAttestationServer.Server.URL, "--cloud-log", "--audience", util.FakeCustomAudience, "--custom-nonce", util.FakeCustomNonce[0], "--custom-nonce", util.FakeCustomNonce[1], "--tee-technology", "", "--tee-nonce", ""})
if err := RootCmd.Execute(); err != nil {
t.Error(err)
}
Expand Down
8 changes: 8 additions & 0 deletions internal/ccel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package internal

const (
// AcpiTableFile is the path to the CCEL ACPI table.
AcpiTableFile = "/sys/firmware/acpi/tables/CCEL"
// CcelEventLogFile is the path to the CCEL event log data.
CcelEventLogFile = "/sys/firmware/acpi/tables/data/CCEL"
)
4 changes: 2 additions & 2 deletions launcher/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,11 @@ func (t *tdxAttestRoot) Attest(nonce []byte) (any, error) {
return nil, err
}

ccelData, err := os.ReadFile("/sys/firmware/acpi/tables/data/CCEL")
ccelData, err := os.ReadFile(internal.CcelEventLogFile)
if err != nil {
return nil, err
}
ccelTable, err := os.ReadFile("/sys/firmware/acpi/tables/CCEL")
ccelTable, err := os.ReadFile(internal.AcpiTableFile)
if err != nil {
return nil, err
}
Expand Down
Loading