diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index a1c73c10..b1e846d0 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ./scripts/attestation_policy + working-directory: ./scripts/attestation_policy/sev-snp steps: - name: Checkout Code diff --git a/Makefile b/Makefile index 8edd779e..ccbb35ed 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ install: $(SERVICES) $(ATTESTATION_POLICY) install $(BUILD_DIR)/cocos-cli $(INSTALL_DIR)/cocos-cli install $(BUILD_DIR)/cocos-manager $(INSTALL_DIR)/cocos-manager install $(BUILD_DIR)/attestation_policy $(INSTALL_DIR)/attestation_policy + install $(BUILD_DIR)/attestation_policy_tdx $(INSTALL_DIR)/attestation_policy_tdx install -d $(CONFIG_DIR) install cocos-manager.env $(CONFIG_DIR)/cocos-manager.env diff --git a/cli/attestation_policy.go b/cli/attestation_policy.go index 016e7b74..47143242 100644 --- a/cli/attestation_policy.go +++ b/cli/attestation_policy.go @@ -301,6 +301,52 @@ func (cli *CLI) NewAzureAttestationPolicy() *cobra.Command { return cmd } +func (cli *CLI) NewTDXAttestationPolicy() *cobra.Command { + cmd := &cobra.Command{ + Use: "tdx", + Short: "Get attestation policy for TDX CVM", + Example: `tdx `, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + attestationFile := args[0] + + // Parse TDX configuration from flags or config file + if err := validateTDXFlags(); err != nil { + printError(cmd, "Error validating TDX flags: %v ❌ ", err) + return + } + + // Read and verify the attestation report to extract policy values + attestationBytes, err := os.ReadFile(attestationFile) + if err != nil { + printError(cmd, "Error reading attestation report file: %v ❌ ", err) + return + } + + // If the config is not provided via flags, we can extract it from the attestation report + // For now, we'll use the cfgTDX that was populated from flags + if len(attestationBytes) > 0 { + cmd.Printf("Read %d bytes from attestation report\n", len(attestationBytes)) + } + + attestationPolicyJson, err := json.MarshalIndent(cfgTDX, "", " ") + if err != nil { + printError(cmd, "Error marshaling attestation policy: %v ❌ ", err) + return + } + + if err := os.WriteFile("attestation_policy.json", attestationPolicyJson, filePermission); err != nil { + printError(cmd, "Error writing attestation policy file: %v ❌ ", err) + return + } + + cmd.Println("Attestation policy file generated successfully ✅") + }, + } + + return addTDXVerificationOptions(cmd) +} + func (cli *CLI) NewExtendWithManifestCmd() *cobra.Command { return &cobra.Command{ Use: "extend", diff --git a/cli/attestation_policy_test.go b/cli/attestation_policy_test.go index 6ceef637..a7181f52 100644 --- a/cli/attestation_policy_test.go +++ b/cli/attestation_policy_test.go @@ -397,7 +397,7 @@ func TestExtendWithManifestHandling(t *testing.T) { t.Run("Invalid manifest file", func(t *testing.T) { cmd := cli.NewExtendWithManifestCmd() - cmd.SetArgs([]string{"../scripts/attestation_policy/attestation_policy.json", "nonexistent.manifest.json"}) + cmd.SetArgs([]string{"../scripts/attestation_policy/sev-snp/attestation_policy.json", "nonexistent.manifest.json"}) var buf bytes.Buffer cmd.SetOut(&buf) @@ -458,7 +458,7 @@ func TestExtendWithManifestHandling(t *testing.T) { } cmd := cli.NewExtendWithManifestCmd() - cmd.SetArgs([]string{"../scripts/attestation_policy/attestation_policy.json", manifestFile.Name()}) + cmd.SetArgs([]string{"../scripts/attestation_policy/sev-snp/attestation_policy.json", manifestFile.Name()}) var buf bytes.Buffer cmd.SetOut(&buf) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index a337f434..003a7f13 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -164,6 +164,7 @@ func main() { attestationPolicyCmd.AddCommand(cliSVC.NewGCPAttestationPolicy()) attestationPolicyCmd.AddCommand(cliSVC.NewDownloadGCPOvmfFile()) attestationPolicyCmd.AddCommand(cliSVC.NewAzureAttestationPolicy()) + attestationPolicyCmd.AddCommand(cliSVC.NewTDXAttestationPolicy()) attestationPolicyCmd.AddCommand(cliSVC.NewExtendWithManifestCmd()) if err := rootCmd.Execute(); err != nil { diff --git a/pkg/atls/atls_test.go b/pkg/atls/atls_test.go index 208e7ce3..1eb639a6 100644 --- a/pkg/atls/atls_test.go +++ b/pkg/atls/atls_test.go @@ -596,7 +596,7 @@ func prepVerifyAttReport(t *testing.T) *sevsnp.Attestation { } func setAttestationPolicy(rr *sevsnp.Attestation, policyDirectory string) error { - attestationPolicyFile, err := os.ReadFile("../../scripts/attestation_policy/attestation_policy.json") + attestationPolicyFile, err := os.ReadFile("../../scripts/attestation_policy/sev-snp/attestation_policy.json") if err != nil { return err } diff --git a/pkg/attestation/vtpm/vtpm_test.go b/pkg/attestation/vtpm/vtpm_test.go index 6d8c3321..955664e9 100644 --- a/pkg/attestation/vtpm/vtpm_test.go +++ b/pkg/attestation/vtpm/vtpm_test.go @@ -807,7 +807,7 @@ func prepVerifyAttReport(t *testing.T) (*sevsnp.Attestation, []byte) { } func setAttestationPolicy(rr *sevsnp.Attestation, policyDirectory string) error { - attestationPolicyFile, err := os.ReadFile("../../../scripts/attestation_policy/attestation_policy.json") + attestationPolicyFile, err := os.ReadFile("../../../scripts/attestation_policy/sev-snp/attestation_policy.json") if err != nil { return err } diff --git a/pkg/clients/grpc/connect_test.go b/pkg/clients/grpc/connect_test.go index 9dca66c8..2a3d38a4 100644 --- a/pkg/clients/grpc/connect_test.go +++ b/pkg/clients/grpc/connect_test.go @@ -92,7 +92,7 @@ func TestNewClient(t *testing.T) { ClientKey: clientKeyFile, }, AttestedTLS: true, - AttestationPolicy: "../../../scripts/attestation_policy/attestation_policy.json", + AttestationPolicy: "../../../scripts/attestation_policy/sev-snp/attestation_policy.json", }, wantErr: false, err: nil, diff --git a/scripts/attestation_policy/Makefile b/scripts/attestation_policy/Makefile index a2012425..8be0257f 100644 --- a/scripts/attestation_policy/Makefile +++ b/scripts/attestation_policy/Makefile @@ -1,24 +1,16 @@ -CARGO = cargo -TARGET = target -BUILD_DIR = $(TARGET)/release -BIN_NAME = attestation_policy -OUTPUT_DIR ?= $(BUILD_DIR) +OUTPUT_DIR ?= ../../build +PLATFORMS = sev-snp tdx +# Convert OUTPUT_DIR to absolute path +ABS_OUTPUT_DIR := $(shell cd $(OUTPUT_DIR) 2>/dev/null && pwd || mkdir -p $(OUTPUT_DIR) && cd $(OUTPUT_DIR) && pwd) -all: build +.PHONY: all build clean $(PLATFORMS) -build: - $(CARGO) build --release - @if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ]; then \ - mkdir -p $(OUTPUT_DIR) && \ - cp $(BUILD_DIR)/$(BIN_NAME) $(OUTPUT_DIR)/$(BIN_NAME) && \ - echo "Copied $(BIN_NAME) to $(OUTPUT_DIR)/"; \ - fi +all: $(PLATFORMS) + +$(PLATFORMS): + $(MAKE) -C $@ OUTPUT_DIR=$(ABS_OUTPUT_DIR) clean: - $(CARGO) clean - @if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ] && [ -f "$(OUTPUT_DIR)/$(BIN_NAME)" ]; then \ - rm -f $(OUTPUT_DIR)/$(BIN_NAME) && \ - echo "Removed $(BIN_NAME) from $(OUTPUT_DIR)/"; \ - fi - -.PHONY: all build clean + @for platform in $(PLATFORMS); do \ + $(MAKE) -C $$platform OUTPUT_DIR=$(ABS_OUTPUT_DIR) clean; \ + done diff --git a/scripts/attestation_policy/Cargo.toml b/scripts/attestation_policy/sev-snp/Cargo.toml similarity index 100% rename from scripts/attestation_policy/Cargo.toml rename to scripts/attestation_policy/sev-snp/Cargo.toml diff --git a/scripts/attestation_policy/sev-snp/Makefile b/scripts/attestation_policy/sev-snp/Makefile new file mode 100644 index 00000000..a2012425 --- /dev/null +++ b/scripts/attestation_policy/sev-snp/Makefile @@ -0,0 +1,24 @@ +CARGO = cargo +TARGET = target +BUILD_DIR = $(TARGET)/release +BIN_NAME = attestation_policy +OUTPUT_DIR ?= $(BUILD_DIR) + +all: build + +build: + $(CARGO) build --release + @if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ]; then \ + mkdir -p $(OUTPUT_DIR) && \ + cp $(BUILD_DIR)/$(BIN_NAME) $(OUTPUT_DIR)/$(BIN_NAME) && \ + echo "Copied $(BIN_NAME) to $(OUTPUT_DIR)/"; \ + fi + +clean: + $(CARGO) clean + @if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ] && [ -f "$(OUTPUT_DIR)/$(BIN_NAME)" ]; then \ + rm -f $(OUTPUT_DIR)/$(BIN_NAME) && \ + echo "Removed $(BIN_NAME) from $(OUTPUT_DIR)/"; \ + fi + +.PHONY: all build clean diff --git a/scripts/attestation_policy/README.md b/scripts/attestation_policy/sev-snp/README.md similarity index 100% rename from scripts/attestation_policy/README.md rename to scripts/attestation_policy/sev-snp/README.md diff --git a/scripts/attestation_policy/attestation_policy.go b/scripts/attestation_policy/sev-snp/attestation_policy.go similarity index 100% rename from scripts/attestation_policy/attestation_policy.go rename to scripts/attestation_policy/sev-snp/attestation_policy.go diff --git a/scripts/attestation_policy/attestation_policy.json b/scripts/attestation_policy/sev-snp/attestation_policy.json similarity index 100% rename from scripts/attestation_policy/attestation_policy.json rename to scripts/attestation_policy/sev-snp/attestation_policy.json diff --git a/scripts/attestation_policy/attestation_policy_tdx.json b/scripts/attestation_policy/sev-snp/attestation_policy_tdx.json similarity index 100% rename from scripts/attestation_policy/attestation_policy_tdx.json rename to scripts/attestation_policy/sev-snp/attestation_policy_tdx.json diff --git a/scripts/attestation_policy/pcr_values.json b/scripts/attestation_policy/sev-snp/pcr_values.json similarity index 100% rename from scripts/attestation_policy/pcr_values.json rename to scripts/attestation_policy/sev-snp/pcr_values.json diff --git a/scripts/attestation_policy/src/main.rs b/scripts/attestation_policy/sev-snp/src/main.rs similarity index 100% rename from scripts/attestation_policy/src/main.rs rename to scripts/attestation_policy/sev-snp/src/main.rs diff --git a/scripts/attestation_policy/tdx/Makefile b/scripts/attestation_policy/tdx/Makefile new file mode 100644 index 00000000..affd672e --- /dev/null +++ b/scripts/attestation_policy/tdx/Makefile @@ -0,0 +1,27 @@ +GO = go +TARGET = target +BUILD_DIR = . +BIN_NAME = attestation_policy_tdx +OUTPUT_DIR ?= $(BUILD_DIR) + +all: build + +build: + $(GO) build -o $(BUILD_DIR)/$(BIN_NAME) . + @if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ]; then \ + mkdir -p $(OUTPUT_DIR) && \ + cp $(BUILD_DIR)/$(BIN_NAME) $(OUTPUT_DIR)/$(BIN_NAME) && \ + echo "Copied $(BIN_NAME) to $(OUTPUT_DIR)/"; \ + fi + +clean: + @if [ -f "$(BUILD_DIR)/$(BIN_NAME)" ]; then \ + rm -f $(BUILD_DIR)/$(BIN_NAME) && \ + echo "Removed $(BIN_NAME) from $(BUILD_DIR)/"; \ + fi + @if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ] && [ -f "$(OUTPUT_DIR)/$(BIN_NAME)" ]; then \ + rm -f $(OUTPUT_DIR)/$(BIN_NAME) && \ + echo "Removed $(BIN_NAME) from $(OUTPUT_DIR)/"; \ + fi + +.PHONY: all build clean diff --git a/scripts/attestation_policy/tdx/go.mod b/scripts/attestation_policy/tdx/go.mod new file mode 100644 index 00000000..bf0d67d9 --- /dev/null +++ b/scripts/attestation_policy/tdx/go.mod @@ -0,0 +1,7 @@ +module tdxpolicy + +go 1.24.2 + +require github.com/google/go-tdx-guest v0.3.1 + +require google.golang.org/protobuf v1.36.10 diff --git a/scripts/attestation_policy/tdx/go.sum b/scripts/attestation_policy/tdx/go.sum new file mode 100644 index 00000000..985e1427 --- /dev/null +++ b/scripts/attestation_policy/tdx/go.sum @@ -0,0 +1,6 @@ +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw= +github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= diff --git a/scripts/attestation_policy/tdx/main.go b/scripts/attestation_policy/tdx/main.go new file mode 100644 index 00000000..40436c83 --- /dev/null +++ b/scripts/attestation_policy/tdx/main.go @@ -0,0 +1,112 @@ +package main + +import ( + "encoding/hex" + "os" + + ccpb "github.com/google/go-tdx-guest/proto/checkconfig" + "google.golang.org/protobuf/encoding/protojson" +) + +var ( + SGXVendorID = []byte{0x93, 0x9A, 0x72, 0x33, 0xF7, 0x9C, 0x4C, 0xA9, 0x94, 0x0A, 0x0D, 0xB3, 0x95, 0x7F, 0x06, 0x07} + MinTdxSvn = []byte{0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + MrSeam = "5b38e33a6487958b72c3c12a938eaa5e3fd4510c51aeeab58c7d5ecee41d7c436489d6c8e4f92f160b7cad34207b00c1" + TdAttributes = []byte{ + 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, + } + Xfam = []byte{ + 0xe7, 0x02, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, + } + MrTd = []byte{ + 0x91, 0xeb, 0x2b, 0x44, 0xd1, 0x41, 0xd4, 0xec, + 0xe0, 0x9f, 0x0c, 0x75, 0xc2, 0xc5, 0x3d, 0x24, + 0x7a, 0x3c, 0x68, 0xed, 0xd7, 0xfa, 0xfe, 0x8a, + 0x35, 0x20, 0xc9, 0x42, 0xa6, 0x04, 0xa4, 0x07, + 0xde, 0x03, 0xae, 0x6d, 0xc5, 0xf8, 0x7f, 0x27, + 0x42, 0x8b, 0x25, 0x38, 0x87, 0x31, 0x18, 0xb7, + } + rtmr = []string{ + "ce0891f46a18db93e7691f1cf73ed76593f7dec1b58f0927ccb56a99242bf63bc9551561f9ee7833d40395fae59547ab", + "062ac322e26b10874a84977a09735408a856aec77ff62b4975b1e90e33c18f05220ea522cdbffc3b2cf4451cc209e418", + "5fd86e8c3d5e45386f1ed0852de7e83ae1b774ee4366bd5213c9890e8e3ac8fad3f7e690891d37f7c81ac20a445cc0ff", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + } +) + +func main() { + cfgTdx := &ccpb.Config{ + RootOfTrust: &ccpb.RootOfTrust{}, + Policy: &ccpb.Policy{HeaderPolicy: &ccpb.HeaderPolicy{}, TdQuoteBodyPolicy: &ccpb.TDQuoteBodyPolicy{}}, + } + + cfgTdx.RootOfTrust.CheckCrl = true + cfgTdx.RootOfTrust.GetCollateral = true + + cfgTdx.Policy.HeaderPolicy.QeVendorId = SGXVendorID + cfgTdx.Policy.TdQuoteBodyPolicy.MinimumTeeTcbSvn = MinTdxSvn + + seam, err := hex.DecodeString(MrSeam) + if err != nil { + panic(err) + } + + cfgTdx.Policy.TdQuoteBodyPolicy.MrSeam = seam + cfgTdx.Policy.TdQuoteBodyPolicy.TdAttributes = TdAttributes + cfgTdx.Policy.TdQuoteBodyPolicy.Xfam = Xfam + cfgTdx.Policy.TdQuoteBodyPolicy.MrTd = MrTd + + for _, reg := range rtmr { + r, err := hex.DecodeString(reg) + if err != nil { + panic(err) + } + cfgTdx.Policy.TdQuoteBodyPolicy.Rtmrs = append(cfgTdx.Policy.TdQuoteBodyPolicy.Rtmrs, r) + } + marshaler := protojson.MarshalOptions{ + Multiline: true, + Indent: " ", + } + tdxPolicy, err := marshaler.Marshal(cfgTdx) + if err != nil { + panic(err) + } + + err = os.WriteFile("tdx_policy.json", tdxPolicy, 0644) + if err != nil { + panic(err) + } + + _, err = os.Stdout.Write(tdxPolicy) + if err != nil { + panic(err) + } + + Policy := &ccpb.Config{ + RootOfTrust: &ccpb.RootOfTrust{}, + Policy: &ccpb.Policy{HeaderPolicy: &ccpb.HeaderPolicy{}, TdQuoteBodyPolicy: &ccpb.TDQuoteBodyPolicy{}}, + } + + if err := ReadTDXAttestationPolicy("/home/cocosai/work/test/tdxpolicy/tdx_policy.json", Policy); err != nil { + panic(err) + } + +} + +func ReadTDXAttestationPolicy(policyPath string, policy *ccpb.Config) error { + policyByte, err := os.ReadFile(policyPath) + if err != nil { + return err + } + + if err := protojson.Unmarshal(policyByte, policy); err != nil { + return err + } + + // fmt.Print("Read TDX Attestation Policy:\n") + // fmt.Printf(policy.String()) + + return nil +} diff --git a/test/manual/README.md b/test/manual/README.md index cad20c3b..3bd62dcf 100644 --- a/test/manual/README.md +++ b/test/manual/README.md @@ -64,7 +64,7 @@ cd ../.. ./build/cocos-cli policy hostdata '' '' # For attested TLS, also define the path to the attestation_policy.json that contains reference values for the fields of the attestation report -export AGENT_GRPC_ATTESTATION_POLICY=./scripts/attestation_policy/attestation_policy.json +export AGENT_GRPC_ATTESTATION_POLICY=./scripts/attestation_policy/sev-snp/attestation_policy.json export AGENT_GRPC_ATTESTED_TLS=true # Retrieve Attestation