From 492c8d344a783e54435b542885ed676502cee0f0 Mon Sep 17 00:00:00 2001 From: thinksyncs Date: Sun, 30 Aug 2026 13:05:24 +0900 Subject: [PATCH 1/8] refactor(module): migrate repository imports to v2 --- .beads/config.yaml | 2 +- .github/workflows/checkproto.yaml | 2 +- .../hardware-attestation-red-team.yaml | 2 +- .github/workflows/main.yaml | 6 +-- .../workflows/redis-sentinel-failover.yaml | 2 +- .github/workflows/security-red-team.yaml | 2 +- .zenodo.json | 4 +- ATTRIBUTION.md | 2 +- CITATION.cff | 8 ++-- PUBLICATION_TODO.md | 14 +++++-- README.md | 12 ++++-- SECURITY.md | 8 ++-- agent/algorithm/binary/binary.go | 6 +-- agent/algorithm/binary/binary_test.go | 4 +- agent/algorithm/docker/docker.go | 4 +- agent/algorithm/docker/docker_test.go | 2 +- agent/algorithm/logging/logging.go | 2 +- agent/algorithm/logging/logging_test.go | 6 +-- agent/algorithm/python/python.go | 6 +-- agent/algorithm/python/python_test.go | 4 +- agent/algorithm/results_test.go | 4 +- agent/algorithm/wasm/wasm.go | 6 +-- agent/algorithm/wasm/wasm_test.go | 4 +- agent/api/grpc/endpoint.go | 4 +- agent/api/grpc/endpoint_test.go | 6 +-- agent/api/grpc/interceptors.go | 4 +- agent/api/grpc/interceptors_test.go | 6 +-- agent/api/grpc/requests.go | 4 +- agent/api/grpc/server.go | 6 +-- agent/api/grpc/server_test.go | 8 ++-- agent/api/logging.go | 6 +-- agent/api/metrics.go | 6 +-- agent/auth/auth.go | 4 +- agent/auth/auth_test.go | 4 +- agent/auth/mocks/authenticator.go | 2 +- agent/cvms/api/grpc/client.go | 18 ++++----- agent/cvms/api/grpc/client_test.go | 18 ++++----- agent/cvms/api/grpc/server.go | 2 +- agent/cvms/api/grpc/server_test.go | 4 +- agent/cvms/api/grpc/storage/mocks/storage.go | 4 +- agent/cvms/api/grpc/storage/storage.go | 2 +- agent/cvms/api/grpc/storage/storage_test.go | 2 +- agent/cvms/server/cvm.go | 8 ++-- agent/cvms/server/cvm_test.go | 4 +- agent/cvms/server/mocks/agentserver.go | 2 +- agent/events/events.go | 2 +- agent/events/events_test.go | 2 +- agent/log/service/service.go | 4 +- agent/log/service/service_test.go | 4 +- agent/mock_attestation_client_test.go | 2 +- agent/mocks/agentservice_algoclient.go | 2 +- agent/mocks/agentservice_dataclient.go | 2 +- .../agentservice_imameasurementsclient.go | 2 +- agent/mocks/service.go | 4 +- agent/runner/events/adapter.go | 6 +-- agent/runner/events/adapter_test.go | 2 +- agent/runner/service/service.go | 14 +++---- agent/runner/service/service_test.go | 4 +- agent/service.go | 22 +++++----- agent/service_test.go | 28 ++++++------- agent/state_test.go | 2 +- agent/statemachine/mocks/statemachine.go | 2 +- cli/algorithm_test.go | 2 +- cli/algorithms.go | 4 +- cli/attestation.go | 6 +-- cli/attestation_policy.go | 2 +- cli/attestation_policy_corim.go | 8 ++-- cli/attestation_policy_corim_test.go | 4 +- cli/attestation_policy_test.go | 2 +- cli/attestation_test.go | 6 +-- cli/checksum.go | 4 +- cli/datasets.go | 6 +-- cli/datasets_test.go | 2 +- cli/errors.go | 6 +-- cli/errors_test.go | 6 +-- cli/ima_measurements.go | 2 +- cli/ima_measurements_test.go | 2 +- cli/manager.go | 2 +- cli/manager_test.go | 4 +- cli/result_test.go | 2 +- cli/sdk.go | 14 +++---- cmd/agent/main.go | 40 +++++++++---------- cmd/attestation-service/fetch_raw_evidence.go | 2 +- cmd/attestation-service/main.go | 24 +++++------ cmd/cli/main.go | 6 +-- cmd/computation-runner/main.go | 16 ++++---- cmd/egress-proxy/main.go | 10 ++--- cmd/hardware-attestation-redteam/main.go | 8 ++-- cmd/ingress-proxy/main.go | 18 ++++----- cmd/log-forwarder/main.go | 12 +++--- cmd/manager/main.go | 24 +++++------ cmd/redis-failover-redteam/main.go | 2 +- examples/a2a-multiprocess/agent_a.go | 2 +- examples/a2a-multiprocess/agent_a_v2.go | 2 +- examples/a2a-multiprocess/agent_b.go | 6 +-- examples/a2a-multiprocess/agent_b_v2.go | 8 ++-- examples/a2a-multiprocess/attester.go | 4 +- examples/a2a-multiprocess/conversation.go | 2 +- .../a2a-multiprocess/conversation_test.go | 2 +- .../a2a-multiprocess/conversation_v2_test.go | 8 ++-- examples/a2a-multiprocess/main_test.go | 2 +- examples/a2a-multiprocess/manager.go | 2 +- examples/a2a-multiprocess/multi_host.go | 2 +- examples/a2a-multiprocess/multi_host_test.go | 2 +- .../operation_acceptance_v2.go | 4 +- .../operation_acceptance_v2_test.go | 4 +- examples/a2a-multiprocess/protocol.go | 8 ++-- examples/a2a-multiprocess/protocol_v2.go | 6 +-- examples/a2a-multiprocess/replay.go | 4 +- examples/a2a-multiprocess/report.go | 4 +- .../a2a-multiprocess/result_recovery_v2.go | 2 +- .../result_recovery_v2_test.go | 2 +- .../a2a-multiprocess/section21_v2_test.go | 2 +- .../a2a-multiprocess/wire_fixture_v2_test.go | 6 +-- examples/a2a/main.go | 8 ++-- examples/agtp-discover-consumer/app.go | 6 +-- examples/agtp-discover-consumer/e2e_test.go | 8 ++-- examples/authority-quorum-demo/main.go | 2 +- examples/protected-change-consumer/app.go | 4 +- .../protected-change-consumer/e2e_test.go | 6 +-- go.mod | 4 +- internal/logger/protohandler.go | 4 +- internal/logger/protohandler_test.go | 4 +- .../attestation-agent/attestation-agent.pb.go | 2 +- .../attestation-agent/attestation-agent.proto | 2 +- .../mocks/attestationagentserviceclient.go | 2 +- .../proto/attestation/v1/attestation.pb.go | 2 +- .../proto/attestation/v1/attestation.proto | 2 +- internal/runtime/server/grpc/grpc.go | 4 +- internal/runtime/server/http/http.go | 2 +- internal/runtime/tracing/tracing.go | 2 +- manager/api/grpc/server.go | 2 +- manager/api/grpc/server_test.go | 4 +- manager/api/logging.go | 2 +- manager/api/metrics.go | 2 +- manager/attestation_policy.go | 6 +-- manager/attestation_policy_embed.go | 2 +- manager/attestation_policy_test.go | 6 +-- manager/mocks/managerserviceclient.go | 2 +- manager/mocks/service.go | 2 +- manager/qemu/mocks/persistence.go | 2 +- manager/qemu/vm.go | 6 +-- manager/qemu/vm_test.go | 4 +- manager/service.go | 10 ++--- manager/service_test.go | 12 +++--- manager/tracing/tracing.go | 2 +- manager/vm/mocks/provider.go | 2 +- manager/vm/mocks/statemachine.go | 2 +- manager/vm/mocks/vm.go | 2 +- manager/vm/state.go | 2 +- manager/vm/state_test.go | 2 +- manager/vm/vm.go | 2 +- pkg/agtp/cwt.go | 2 +- pkg/agtp/cwt_test.go | 2 +- pkg/agtp/discovery/peer/auth.go | 6 +-- pkg/agtp/discovery/peer/client.go | 8 ++-- pkg/agtp/discovery/peer/node.go | 2 +- .../discovery/peer/node_integration_test.go | 6 +-- pkg/agtp/discovery/peer/protocol.go | 2 +- pkg/agtp/discovery/peer/replay.go | 2 +- pkg/agtp/discovery/peer/store.go | 2 +- pkg/agtp/discovery/peer/store_test.go | 4 +- pkg/agtp/gateway_route.go | 2 +- pkg/agtp/gateway_route_cwt.go | 2 +- pkg/agtp/gateway_route_jwt.go | 2 +- pkg/agtp/gateway_route_test.go | 2 +- pkg/agtp/gatewayroute/route.go | 2 +- pkg/agtp/gatewayroute/route_test.go | 2 +- pkg/agtp/jwt.go | 2 +- pkg/agtp/jwt_test.go | 4 +- pkg/atls/ea/authenticator.go | 2 +- pkg/atls/ea/authenticator_test.go | 2 +- pkg/atls/ea/session.go | 2 +- pkg/atls/evidence_verifier.go | 12 +++--- pkg/atls/evidence_verifier_test.go | 4 +- pkg/atls/identity_binding.go | 8 ++-- pkg/atls/identity_binding_test.go | 2 +- .../identitypolicy/session_binding_test.go | 2 +- pkg/atls/internal_transport/conn.go | 6 +-- pkg/atls/internal_transport/conn_test.go | 6 +-- pkg/atls/mocks/certificateprovider.go | 2 +- pkg/atls/provider.go | 8 ++-- pkg/atls/transport.go | 8 ++-- pkg/attestation/azure/snp.go | 6 +-- pkg/attestation/azure/snp_test.go | 4 +- pkg/attestation/ccaa/provider.go | 6 +-- pkg/attestation/ccaa/provider_test.go | 4 +- pkg/attestation/corimgen/README.md | 4 +- pkg/attestation/eat/eat.go | 2 +- pkg/attestation/eat/eat_test.go | 2 +- pkg/attestation/eat/extractor_test.go | 2 +- pkg/attestation/eat/intuse_test.go | 2 +- pkg/attestation/emptyprovider.go | 2 +- pkg/attestation/emptyprovider_test.go | 2 +- pkg/attestation/generator/README.md | 2 +- pkg/attestation/generator/generator.go | 2 +- pkg/attestation/igvmmeasure/README.md | 2 +- pkg/attestation/tdx/tdx.go | 6 +-- pkg/attestation/tdx/tdx_coverage_test.go | 2 +- pkg/attestation/tdx/tdx_test.go | 2 +- pkg/attestation/vtpm/vtpm.go | 4 +- pkg/authorityquorum/asbbinding/credential.go | 2 +- pkg/authorityquorum/asbbinding/profile.go | 6 +-- .../asbbinding/profile_test.go | 6 +-- pkg/authorityquorum/asbbinding/tls.go | 6 +-- pkg/authorityquorum/asbbinding/tls_test.go | 4 +- pkg/clients/agtp_tls_exporter_test.go | 4 +- pkg/clients/clients.go | 6 +-- pkg/clients/clients_test.go | 8 ++-- pkg/clients/grpc/agent/agent.go | 10 ++--- pkg/clients/grpc/agent/agent_test.go | 10 ++--- pkg/clients/grpc/attestation-agent/client.go | 6 +-- .../grpc/attestation-agent/client_test.go | 2 +- pkg/clients/grpc/attestation/client.go | 4 +- pkg/clients/grpc/attestation/client_test.go | 4 +- pkg/clients/grpc/connect_test.go | 8 ++-- pkg/clients/grpc/cvm/cvm.go | 6 +-- pkg/clients/grpc/cvm/cvm_test.go | 10 ++--- pkg/clients/grpc/grpc.go | 10 ++--- pkg/clients/grpc/log/client.go | 2 +- pkg/clients/grpc/log/client_test.go | 2 +- pkg/clients/grpc/manager/manager.go | 6 +-- pkg/clients/grpc/manager/manager_test.go | 4 +- pkg/clients/grpc/runner/client.go | 2 +- pkg/clients/grpc/runner/client_test.go | 2 +- pkg/clients/grpc/runner/mocks/client.go | 2 +- pkg/clients/http/client.go | 6 +-- pkg/clients/http/client_test.go | 6 +-- pkg/clients/identity_jwt.go | 2 +- pkg/clients/identity_jwt_v2_test.go | 2 +- pkg/crypto/decrypt.go | 2 +- pkg/ingress/adapter.go | 2 +- pkg/ingress/adapter_test.go | 2 +- pkg/ingress/proxy.go | 2 +- pkg/ingress/proxy_test.go | 2 +- pkg/production/binding.go | 4 +- pkg/production/profile.go | 4 +- pkg/production/profile_test.go | 4 +- pkg/production/redis_acceptance.go | 2 +- pkg/production/redis_acceptance_test.go | 2 +- pkg/production/software_only.go | 2 +- pkg/production/software_only_test.go | 4 +- pkg/progressbar/progress_test.go | 6 +-- pkg/progressbar/progressbar.go | 4 +- pkg/sdk/agent.go | 10 ++--- pkg/sdk/agent_test.go | 8 ++-- pkg/sdk/setup_test.go | 6 +-- pkg/taskcoord/asbbinding/canonical.go | 2 +- pkg/taskcoord/asbbinding/canonical_test.go | 2 +- pkg/taskcoord/asbbinding/ingress.go | 10 ++--- pkg/taskcoord/asbbinding/ingress_test.go | 6 +-- pkg/taskcoord/asbbinding/profile.go | 6 +-- pkg/taskcoord/asbbinding/profile_test.go | 6 +-- pkg/tls/tls.go | 4 +- ...a2a_security_test_report_validator_test.go | 2 +- schemas/task-participant-v1.schema.json | 2 +- schemas/task_participant_schema_test.go | 2 +- test/cvms/main.go | 12 +++--- 258 files changed, 633 insertions(+), 619 deletions(-) diff --git a/.beads/config.yaml b/.beads/config.yaml index e95f8fa3..95573f66 100644 --- a/.beads/config.yaml +++ b/.beads/config.yaml @@ -65,4 +65,4 @@ # - linear.api_key → use LINEAR_API_KEY env var instead # - github.token → use GITHUB_TOKEN env var instead -sync.remote: "git+https://github.com/thinksyncs/agents-secure-binding.git" \ No newline at end of file +sync.remote: "git+https://github.com/ToppyMicroServices/agents-secure-binding.git" diff --git a/.github/workflows/checkproto.yaml b/.github/workflows/checkproto.yaml index 2ce30818..ea0d116d 100644 --- a/.github/workflows/checkproto.yaml +++ b/.github/workflows/checkproto.yaml @@ -31,7 +31,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 1.26.x + go-version: 1.26.6 - name: Set up protoc run: | diff --git a/.github/workflows/hardware-attestation-red-team.yaml b/.github/workflows/hardware-attestation-red-team.yaml index b6e2d605..91e329e9 100644 --- a/.github/workflows/hardware-attestation-red-team.yaml +++ b/.github/workflows/hardware-attestation-red-team.yaml @@ -46,7 +46,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 1.26.x + go-version: 1.26.6 cache-dependency-path: go.sum - name: Run hardware attestation replay gate diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 4371c46f..fc981341 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -21,7 +21,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 1.26.x + go-version: 1.26.6 - name: Verify module downloads run: go mod verify @@ -51,7 +51,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 1.26.x + go-version: 1.26.6 - name: Create coverage directory run: mkdir -p coverage @@ -103,7 +103,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 1.26.x + go-version: 1.26.6 cache-dependency-path: go.sum - name: Verify modules diff --git a/.github/workflows/redis-sentinel-failover.yaml b/.github/workflows/redis-sentinel-failover.yaml index 1b35abf6..dc9a5996 100644 --- a/.github/workflows/redis-sentinel-failover.yaml +++ b/.github/workflows/redis-sentinel-failover.yaml @@ -45,7 +45,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 1.26.x + go-version: 1.26.6 cache-dependency-path: go.sum - name: Run real Redis Sentinel failover gate diff --git a/.github/workflows/security-red-team.yaml b/.github/workflows/security-red-team.yaml index 13f4e413..0f3790b5 100644 --- a/.github/workflows/security-red-team.yaml +++ b/.github/workflows/security-red-team.yaml @@ -63,7 +63,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: 1.26.x + go-version: 1.26.6 cache-dependency-path: go.sum - name: Run Direct-Agent security red-team tests diff --git a/.zenodo.json b/.zenodo.json index 440ef747..27a9e12e 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,6 +1,6 @@ { "title": "Agents Secure Binding", - "version": "0.4", + "version": "1.1.1", "upload_type": "software", "description": "A security-hardening profile and Go implementation helpers for binding upper-layer agent identity and authorization material to an accepted TLS 1.3 session, post-handshake platform-attestation facts, and local policy.", "creators": [ @@ -20,7 +20,7 @@ ], "related_identifiers": [ { - "identifier": "https://github.com/thinksyncs/agents-secure-binding", + "identifier": "https://github.com/ToppyMicroServices/agents-secure-binding", "relation": "isSupplementTo", "scheme": "url" } diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index 8620eeb9..252446c6 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -14,6 +14,6 @@ replaced with local scratch implementations. Retained notices remain where the file still contains derived Cocos code. Agents Secure Binding profile documentation, tests, vectors, and profile helper -code are maintained by ToppyMicroServices OU unless a file states otherwise. +code are maintained by ToppyMicroServices OÜ unless a file states otherwise. Cocos is implementation provenance and experience; it is not the normative scope of the Session-Bound Agent Identity Profile. diff --git a/CITATION.cff b/CITATION.cff index c9e4d23d..a0e58525 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -5,10 +5,10 @@ type: software authors: - family-names: "Okutomi" given-names: "Akira" -version: "0.4" -date-released: "2026-06-21" -repository-code: "https://github.com/thinksyncs/agents-secure-binding" -url: "https://github.com/thinksyncs/agents-secure-binding/releases/tag/v0.4" +version: "1.1.1" +date-released: "2026-08-04" +repository-code: "https://github.com/ToppyMicroServices/agents-secure-binding" +url: "https://github.com/ToppyMicroServices/agents-secure-binding/releases/tag/v1.1.1" license: "Apache-2.0" abstract: > A security-hardening profile and Go implementation helpers for binding diff --git a/PUBLICATION_TODO.md b/PUBLICATION_TODO.md index a231b870..5a975be4 100644 --- a/PUBLICATION_TODO.md +++ b/PUBLICATION_TODO.md @@ -4,9 +4,17 @@ Release blockers and evidence limits for the public draft. ## Repository Identity -Resolved for public v0.5: the repository name, Go module path, imports, -protobuf `go_package` options, examples, and local documentation use -`github.com/thinksyncs/agents-secure-binding`. +The canonical repository is moving to +`github.com/ToppyMicroServices/agents-secure-binding`. Because the published +release line has reached `v1.1.1`, the breaking namespace migration uses the +Go module path `github.com/ToppyMicroServices/agents-secure-binding/v2` and +requires a v2 release. A v0.6.0 release would incorrectly move the published +version backwards. + +The module declaration, internal imports, protobuf `go_package` options, +examples, and local documentation must use the v2 module path before that +release. Repository links, schema identifiers, and Git remotes use the +canonical repository URL without `/v2`. ## Recorded CI and Red-Team Status diff --git a/README.md b/README.md index b6286e72..358ecf33 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,9 @@ The primary failure class is context diversion: accepting cryptographically valid material for a different service, tenant, Agent, task, delegation, or authority boundary than the verifier intended. -Release `v1.1.0` defines the supported verifier product surface for the -Direct-Agent v1 profile. It provides distinct attested and software-only +Release `v1.1.1` is the latest release in the supported v1 verifier product +line for the Direct-Agent v1 profile. It provides distinct attested and +software-only production compositions covering role-separated trust keys, revocation, exact TLS/action binding, TLS-protected distributed replay, and a concrete non-Split-Knowledge protected-change consumer. See @@ -313,8 +314,11 @@ for Agents Secure Binding. The repository keeps the Apache-2.0 license and retained upstream notices. See `ATTRIBUTION.md`. -Repository identity note: the public repository name and Go module path are -both `github.com/thinksyncs/agents-secure-binding`. +Repository identity note: the canonical public repository is +`github.com/ToppyMicroServices/agents-secure-binding`, and the next major Go +module path is `github.com/ToppyMicroServices/agents-secure-binding/v2`. +The Go module major version is independent of the Direct-Agent wire-profile +version. ## Verification Commands diff --git a/SECURITY.md b/SECURITY.md index 49ccefa3..2d3eb8e8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,11 @@ ## Supported Scope -This repository is pre-1.0 and currently publishes security-profile drafts, -tests, vectors, and implementation helpers. Security reports should target the -current `main` branch and any tagged release that remains publicly referenced. +This repository publishes a supported v1 verifier product surface together +with experimental security-profile drafts, tests, vectors, and implementation +helpers. Security reports should target the current `main` branch and any +tagged release that remains within the support window documented in +`docs/API_COMPATIBILITY.md`. ## Reporting a Vulnerability diff --git a/agent/algorithm/binary/binary.go b/agent/algorithm/binary/binary.go index c941a9a8..c5ef2eea 100644 --- a/agent/algorithm/binary/binary.go +++ b/agent/algorithm/binary/binary.go @@ -11,9 +11,9 @@ import ( "os/exec" "sync" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/logging" - "github.com/thinksyncs/agents-secure-binding/agent/events" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" ) var execCommand = exec.Command diff --git a/agent/algorithm/binary/binary_test.go b/agent/algorithm/binary/binary_test.go index ed9219d8..383e9be9 100644 --- a/agent/algorithm/binary/binary_test.go +++ b/agent/algorithm/binary/binary_test.go @@ -10,10 +10,10 @@ import ( "os/exec" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/logging" - "github.com/thinksyncs/agents-secure-binding/agent/events/mocks" ) func TestNewAlgorithm(t *testing.T) { diff --git a/agent/algorithm/docker/docker.go b/agent/algorithm/docker/docker.go index 40bbba08..55c90199 100644 --- a/agent/algorithm/docker/docker.go +++ b/agent/algorithm/docker/docker.go @@ -6,8 +6,8 @@ import ( "errors" "log/slog" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/agent/events" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" ) var ( diff --git a/agent/algorithm/docker/docker_test.go b/agent/algorithm/docker/docker_test.go index 8707dfe8..f9caa179 100644 --- a/agent/algorithm/docker/docker_test.go +++ b/agent/algorithm/docker/docker_test.go @@ -7,8 +7,8 @@ import ( "os" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events/mocks" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/agent/events/mocks" ) // TestNewAlgorithm tests the NewAlgorithm function. diff --git a/agent/algorithm/logging/logging.go b/agent/algorithm/logging/logging.go index 7c2c006d..51148c1b 100644 --- a/agent/algorithm/logging/logging.go +++ b/agent/algorithm/logging/logging.go @@ -8,7 +8,7 @@ import ( "io" "log/slog" - "github.com/thinksyncs/agents-secure-binding/agent/events" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" ) var ( diff --git a/agent/algorithm/logging/logging_test.go b/agent/algorithm/logging/logging_test.go index e2d07abe..856492d9 100644 --- a/agent/algorithm/logging/logging_test.go +++ b/agent/algorithm/logging/logging_test.go @@ -6,11 +6,11 @@ import ( "strings" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events/mocks" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent/events/mocks" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - "github.com/thinksyncs/agents-secure-binding/pkg/manager" ) func TestStdoutWrite(t *testing.T) { diff --git a/agent/algorithm/python/python.go b/agent/algorithm/python/python.go index 036a017b..7cc14c62 100644 --- a/agent/algorithm/python/python.go +++ b/agent/algorithm/python/python.go @@ -13,9 +13,9 @@ import ( "path/filepath" "sync" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/logging" - "github.com/thinksyncs/agents-secure-binding/agent/events" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" "google.golang.org/grpc/metadata" ) diff --git a/agent/algorithm/python/python_test.go b/agent/algorithm/python/python_test.go index 1c902ea4..8c50e63b 100644 --- a/agent/algorithm/python/python_test.go +++ b/agent/algorithm/python/python_test.go @@ -13,11 +13,11 @@ import ( "strings" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/logging" - "github.com/thinksyncs/agents-secure-binding/agent/events/mocks" "google.golang.org/grpc/metadata" ) diff --git a/agent/algorithm/results_test.go b/agent/algorithm/results_test.go index 3d9a46b4..dc9fb587 100644 --- a/agent/algorithm/results_test.go +++ b/agent/algorithm/results_test.go @@ -6,8 +6,8 @@ import ( "os" "testing" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/internal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal" ) func TestZipDirectory(t *testing.T) { diff --git a/agent/algorithm/wasm/wasm.go b/agent/algorithm/wasm/wasm.go index 7f13743c..36d7670b 100644 --- a/agent/algorithm/wasm/wasm.go +++ b/agent/algorithm/wasm/wasm.go @@ -11,9 +11,9 @@ import ( "os/exec" "sync" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/logging" - "github.com/thinksyncs/agents-secure-binding/agent/events" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" ) var execCommand = exec.Command diff --git a/agent/algorithm/wasm/wasm_test.go b/agent/algorithm/wasm/wasm_test.go index e8dbaa18..30acf0f8 100644 --- a/agent/algorithm/wasm/wasm_test.go +++ b/agent/algorithm/wasm/wasm_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/logging" - "github.com/thinksyncs/agents-secure-binding/agent/events/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events/mocks" ) const testWasm = "test.wasm" diff --git a/agent/api/grpc/endpoint.go b/agent/api/grpc/endpoint.go index 0b9c76d6..0fa29c3b 100644 --- a/agent/api/grpc/endpoint.go +++ b/agent/api/grpc/endpoint.go @@ -5,9 +5,9 @@ package grpc import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/go-kit/kit/endpoint" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" ) func algoEndpoint(svc agent.Service) endpoint.Endpoint { diff --git a/agent/api/grpc/endpoint_test.go b/agent/api/grpc/endpoint_test.go index 04e85193..b7070005 100644 --- a/agent/api/grpc/endpoint_test.go +++ b/agent/api/grpc/endpoint_test.go @@ -7,10 +7,10 @@ import ( "errors" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/mocks" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" "golang.org/x/crypto/sha3" ) diff --git a/agent/api/grpc/interceptors.go b/agent/api/grpc/interceptors.go index 45ddf2e2..35b8d7f0 100644 --- a/agent/api/grpc/interceptors.go +++ b/agent/api/grpc/interceptors.go @@ -5,8 +5,8 @@ package grpc import ( "context" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/auth" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/auth" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" diff --git a/agent/api/grpc/interceptors_test.go b/agent/api/grpc/interceptors_test.go index 6458301e..c2d48c2f 100644 --- a/agent/api/grpc/interceptors_test.go +++ b/agent/api/grpc/interceptors_test.go @@ -6,10 +6,10 @@ import ( "context" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/auth" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/auth/mocks" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/auth" - "github.com/thinksyncs/agents-secure-binding/agent/auth/mocks" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) diff --git a/agent/api/grpc/requests.go b/agent/api/grpc/requests.go index 57f293d5..4de0df40 100644 --- a/agent/api/grpc/requests.go +++ b/agent/api/grpc/requests.go @@ -5,8 +5,8 @@ package grpc import ( "errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" ) type algoReq struct { diff --git a/agent/api/grpc/server.go b/agent/api/grpc/server.go index 187b3a65..26ca1bcb 100644 --- a/agent/api/grpc/server.go +++ b/agent/api/grpc/server.go @@ -10,11 +10,11 @@ import ( "io" "strconv" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" "github.com/go-kit/kit/endpoint" "github.com/go-kit/kit/transport/grpc" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" diff --git a/agent/api/grpc/server_test.go b/agent/api/grpc/server_test.go index 50891bf1..30596877 100644 --- a/agent/api/grpc/server_test.go +++ b/agent/api/grpc/server_test.go @@ -7,12 +7,12 @@ import ( "io" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/mocks" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" diff --git a/agent/api/logging.go b/agent/api/logging.go index c0bba407..e568bf71 100644 --- a/agent/api/logging.go +++ b/agent/api/logging.go @@ -11,9 +11,9 @@ import ( "log/slog" "time" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" ) var _ agent.Service = (*loggingMiddleware)(nil) diff --git a/agent/api/metrics.go b/agent/api/metrics.go index e3105b21..54fc508a 100644 --- a/agent/api/metrics.go +++ b/agent/api/metrics.go @@ -9,10 +9,10 @@ import ( "context" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" "github.com/go-kit/kit/metrics" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" ) var _ agent.Service = (*metricsMiddleware)(nil) diff --git a/agent/auth/auth.go b/agent/auth/auth.go index 97842397..54c435c0 100644 --- a/agent/auth/auth.go +++ b/agent/auth/auth.go @@ -16,8 +16,8 @@ import ( "sync" "time" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" diff --git a/agent/auth/auth_test.go b/agent/auth/auth_test.go index 6d8be28b..e6f0491b 100644 --- a/agent/auth/auth_test.go +++ b/agent/auth/auth_test.go @@ -16,10 +16,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/internal/errors" "google.golang.org/grpc/metadata" ) diff --git a/agent/auth/mocks/authenticator.go b/agent/auth/mocks/authenticator.go index 7268f65d..ba4bbfbf 100644 --- a/agent/auth/mocks/authenticator.go +++ b/agent/auth/mocks/authenticator.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/auth" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent/auth" ) // NewAuthenticator creates a new instance of Authenticator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/agent/cvms/api/grpc/client.go b/agent/cvms/api/grpc/client.go index f28e9794..5c5ca983 100644 --- a/agent/cvms/api/grpc/client.go +++ b/agent/cvms/api/grpc/client.go @@ -10,15 +10,15 @@ import ( "sync" "time" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/agent/cvms/api/grpc/storage" - "github.com/thinksyncs/agents-secure-binding/agent/cvms/server" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" - "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc" - "github.com/thinksyncs/agents-secure-binding/pkg/ingress" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/api/grpc/storage" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/server" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/ingress" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/proto" ) diff --git a/agent/cvms/api/grpc/client_test.go b/agent/cvms/api/grpc/client_test.go index cf77e31a..e6188bea 100644 --- a/agent/cvms/api/grpc/client_test.go +++ b/agent/cvms/api/grpc/client_test.go @@ -7,17 +7,17 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/api/grpc/storage" + servermocks "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/server/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/mocks" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + pkggrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" + clientmocks "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/ingress" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/agent/cvms/api/grpc/storage" - servermocks "github.com/thinksyncs/agents-secure-binding/agent/cvms/server/mocks" - "github.com/thinksyncs/agents-secure-binding/agent/mocks" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - pkggrpc "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc" - clientmocks "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/mocks" - "github.com/thinksyncs/agents-secure-binding/pkg/ingress" "golang.org/x/crypto/sha3" "google.golang.org/grpc" "google.golang.org/protobuf/proto" diff --git a/agent/cvms/api/grpc/server.go b/agent/cvms/api/grpc/server.go index db4e0943..a9e97774 100644 --- a/agent/cvms/api/grpc/server.go +++ b/agent/cvms/api/grpc/server.go @@ -10,7 +10,7 @@ import ( "log/slog" "time" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" "golang.org/x/sync/errgroup" "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" diff --git a/agent/cvms/api/grpc/server_test.go b/agent/cvms/api/grpc/server_test.go index 8e0f314a..1d9bd3c8 100644 --- a/agent/cvms/api/grpc/server_test.go +++ b/agent/cvms/api/grpc/server_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/internal/errors" "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" ) diff --git a/agent/cvms/api/grpc/storage/mocks/storage.go b/agent/cvms/api/grpc/storage/mocks/storage.go index cbf7d602..90d1e4a8 100644 --- a/agent/cvms/api/grpc/storage/mocks/storage.go +++ b/agent/cvms/api/grpc/storage/mocks/storage.go @@ -8,9 +8,9 @@ package mocks import ( + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/api/grpc/storage" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/agent/cvms/api/grpc/storage" ) // NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/agent/cvms/api/grpc/storage/storage.go b/agent/cvms/api/grpc/storage/storage.go index ea6a46b4..07af3b87 100644 --- a/agent/cvms/api/grpc/storage/storage.go +++ b/agent/cvms/api/grpc/storage/storage.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" ) // Message represents a pending message with its timestamp. diff --git a/agent/cvms/api/grpc/storage/storage_test.go b/agent/cvms/api/grpc/storage/storage_test.go index 23164773..d07d84d1 100644 --- a/agent/cvms/api/grpc/storage/storage_test.go +++ b/agent/cvms/api/grpc/storage/storage_test.go @@ -8,9 +8,9 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" ) func createTempDir(t *testing.T) string { diff --git a/agent/cvms/server/cvm.go b/agent/cvms/server/cvm.go index e9054b4d..7bcee067 100644 --- a/agent/cvms/server/cvm.go +++ b/agent/cvms/server/cvm.go @@ -10,10 +10,10 @@ import ( "os" "sync" - "github.com/thinksyncs/agents-secure-binding/agent" - agentgrpc "github.com/thinksyncs/agents-secure-binding/agent/api/grpc" - "github.com/thinksyncs/agents-secure-binding/agent/auth" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/netguard" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + agentgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/auth" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/netguard" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" diff --git a/agent/cvms/server/cvm_test.go b/agent/cvms/server/cvm_test.go index 7ef3ad00..31d8f03d 100644 --- a/agent/cvms/server/cvm_test.go +++ b/agent/cvms/server/cvm_test.go @@ -13,9 +13,9 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/mocks" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/mocks" ) func setupTest(t *testing.T) (*slog.Logger, *mocks.Service, string, []byte) { diff --git a/agent/cvms/server/mocks/agentserver.go b/agent/cvms/server/mocks/agentserver.go index 529a3f79..dbd6df6b 100644 --- a/agent/cvms/server/mocks/agentserver.go +++ b/agent/cvms/server/mocks/agentserver.go @@ -8,8 +8,8 @@ package mocks import ( + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" ) // NewAgentServer creates a new instance of AgentServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/agent/events/events.go b/agent/events/events.go index 80c54d0d..94fe12d9 100644 --- a/agent/events/events.go +++ b/agent/events/events.go @@ -5,7 +5,7 @@ package events import ( "encoding/json" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" "google.golang.org/protobuf/types/known/timestamppb" ) diff --git a/agent/events/events_test.go b/agent/events/events_test.go index 527cac69..b1d3af04 100644 --- a/agent/events/events_test.go +++ b/agent/events/events_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" ) func TestSendEventSuccess(t *testing.T) { diff --git a/agent/log/service/service.go b/agent/log/service/service.go index b48e2dbe..2208faf1 100644 --- a/agent/log/service/service.go +++ b/agent/log/service/service.go @@ -6,8 +6,8 @@ import ( "context" "log/slog" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/agent/log" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" "google.golang.org/protobuf/types/known/emptypb" ) diff --git a/agent/log/service/service_test.go b/agent/log/service/service_test.go index 0cb93dda..7d9444e2 100644 --- a/agent/log/service/service_test.go +++ b/agent/log/service/service_test.go @@ -10,10 +10,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/agent/log" "google.golang.org/protobuf/types/known/timestamppb" ) diff --git a/agent/mock_attestation_client_test.go b/agent/mock_attestation_client_test.go index 41d64c7d..2d2c6389 100644 --- a/agent/mock_attestation_client_test.go +++ b/agent/mock_attestation_client_test.go @@ -5,8 +5,8 @@ package agent import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" ) type MockAttestationClient struct { diff --git a/agent/mocks/agentservice_algoclient.go b/agent/mocks/agentservice_algoclient.go index 9c85a022..32969b08 100644 --- a/agent/mocks/agentservice_algoclient.go +++ b/agent/mocks/agentservice_algoclient.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" "google.golang.org/grpc/metadata" ) diff --git a/agent/mocks/agentservice_dataclient.go b/agent/mocks/agentservice_dataclient.go index 7f258580..9c6191be 100644 --- a/agent/mocks/agentservice_dataclient.go +++ b/agent/mocks/agentservice_dataclient.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" "google.golang.org/grpc/metadata" ) diff --git a/agent/mocks/agentservice_imameasurementsclient.go b/agent/mocks/agentservice_imameasurementsclient.go index ebc6e4f7..268e392d 100644 --- a/agent/mocks/agentservice_imameasurementsclient.go +++ b/agent/mocks/agentservice_imameasurementsclient.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" "google.golang.org/grpc/metadata" ) diff --git a/agent/mocks/service.go b/agent/mocks/service.go index 0f7d5639..238445b9 100644 --- a/agent/mocks/service.go +++ b/agent/mocks/service.go @@ -10,9 +10,9 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" ) // NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/agent/runner/events/adapter.go b/agent/runner/events/adapter.go index a815c038..51012c29 100644 --- a/agent/runner/events/adapter.go +++ b/agent/runner/events/adapter.go @@ -7,9 +7,9 @@ import ( "encoding/json" "log/slog" - "github.com/thinksyncs/agents-secure-binding/agent/events" - logpb "github.com/thinksyncs/agents-secure-binding/agent/log" - logclient "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/log" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" + logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" + logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" ) type adapter struct { diff --git a/agent/runner/events/adapter_test.go b/agent/runner/events/adapter_test.go index 534974d6..02feece1 100644 --- a/agent/runner/events/adapter_test.go +++ b/agent/runner/events/adapter_test.go @@ -7,9 +7,9 @@ import ( "encoding/json" "testing" + logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - logpb "github.com/thinksyncs/agents-secure-binding/agent/log" ) const testServiceName = "test-service" diff --git a/agent/runner/service/service.go b/agent/runner/service/service.go index 79ba10bc..f515b2c0 100644 --- a/agent/runner/service/service.go +++ b/agent/runner/service/service.go @@ -10,13 +10,13 @@ import ( "path/filepath" "sync" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/binary" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/docker" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/python" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/wasm" - "github.com/thinksyncs/agents-secure-binding/agent/events" - pb "github.com/thinksyncs/agents-secure-binding/agent/runner" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/binary" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/docker" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/python" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/wasm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" + pb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner" "google.golang.org/protobuf/types/known/emptypb" ) diff --git a/agent/runner/service/service_test.go b/agent/runner/service/service_test.go index 89cd91de..5924c218 100644 --- a/agent/runner/service/service_test.go +++ b/agent/runner/service/service_test.go @@ -12,10 +12,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/docker" + pb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/docker" - pb "github.com/thinksyncs/agents-secure-binding/agent/runner" ) // MockEventService is a mock implementation of events.Service. diff --git a/agent/service.go b/agent/service.go index d3e4f51b..b484bf13 100644 --- a/agent/service.go +++ b/agent/service.go @@ -16,17 +16,17 @@ import ( sync "sync" "time" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/agent/events" - runnerpb "github.com/thinksyncs/agents-secure-binding/agent/runner" - "github.com/thinksyncs/agents-secure-binding/agent/statemachine" - "github.com/thinksyncs/agents-secure-binding/internal" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" - attestation_client "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/attestation" - runner_client "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/runner" - "github.com/thinksyncs/agents-secure-binding/pkg/oci" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" + runnerpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/statemachine" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" + attestation_client "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/attestation" + runner_client "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/runner" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/oci" "golang.org/x/crypto/sha3" ) diff --git a/agent/service_test.go b/agent/service_test.go index 06607cab..6b9c68ec 100644 --- a/agent/service_test.go +++ b/agent/service_test.go @@ -18,22 +18,22 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/python" + agentevents "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events/mocks" + runnerpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/statemachine" + smmocks "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/statemachine/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" + runnermocks "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/runner/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/oci" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/python" - agentevents "github.com/thinksyncs/agents-secure-binding/agent/events" - "github.com/thinksyncs/agents-secure-binding/agent/events/mocks" - runnerpb "github.com/thinksyncs/agents-secure-binding/agent/runner" - "github.com/thinksyncs/agents-secure-binding/agent/statemachine" - smmocks "github.com/thinksyncs/agents-secure-binding/agent/statemachine/mocks" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" - runnermocks "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/runner/mocks" - "github.com/thinksyncs/agents-secure-binding/pkg/oci" "golang.org/x/crypto/sha3" "google.golang.org/grpc/metadata" "google.golang.org/protobuf/types/known/emptypb" @@ -598,7 +598,7 @@ func TestStopComputation(t *testing.T) { // Use real dirs for test // algorithm.DatasetsDir refers to global var? - // "github.com/thinksyncs/agents-secure-binding/agent/algorithm" + // "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" // It uses hardcoded path "datasets" and "results" in current dir. // Tests create them in current dir. diff --git a/agent/state_test.go b/agent/state_test.go index 04da0a43..e4aba881 100644 --- a/agent/state_test.go +++ b/agent/state_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/agent/statemachine" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/statemachine" ) type MockState int diff --git a/agent/statemachine/mocks/statemachine.go b/agent/statemachine/mocks/statemachine.go index cf743d84..d84257dd 100644 --- a/agent/statemachine/mocks/statemachine.go +++ b/agent/statemachine/mocks/statemachine.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/statemachine" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent/statemachine" ) // NewStateMachine creates a new instance of StateMachine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/cli/algorithm_test.go b/cli/algorithm_test.go index 62797303..4ba63005 100644 --- a/cli/algorithm_test.go +++ b/cli/algorithm_test.go @@ -12,9 +12,9 @@ import ( "os" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/sdk/mocks" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/pkg/sdk/mocks" ) const algorithmFile = "test_algo_file.py" diff --git a/cli/algorithms.go b/cli/algorithms.go index aa887366..a5a74bbf 100644 --- a/cli/algorithms.go +++ b/cli/algorithms.go @@ -7,10 +7,10 @@ import ( "encoding/pem" "os" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/algorithm/python" "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm" - "github.com/thinksyncs/agents-secure-binding/agent/algorithm/python" "google.golang.org/grpc/metadata" ) diff --git a/cli/attestation.go b/cli/attestation.go index 75f0fe18..ca37299f 100644 --- a/cli/attestation.go +++ b/cli/attestation.go @@ -9,14 +9,14 @@ import ( "os" "strings" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" "github.com/fatih/color" "github.com/google/go-sev-guest/abi" tpmAttest "github.com/google/go-tpm-tools/proto/attest" "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" ) diff --git a/cli/attestation_policy.go b/cli/attestation_policy.go index d8d7e250..d96141d0 100644 --- a/cli/attestation_policy.go +++ b/cli/attestation_policy.go @@ -8,9 +8,9 @@ import ( "fmt" "os" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/gcp" "github.com/google/go-tpm-tools/proto/attest" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/gcp" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" ) diff --git a/cli/attestation_policy_corim.go b/cli/attestation_policy_corim.go index ebd15c1d..57b03779 100644 --- a/cli/attestation_policy_corim.go +++ b/cli/attestation_policy_corim.go @@ -7,11 +7,11 @@ import ( "fmt" "os" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/gcp" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/generator" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/azure" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/corimgen" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/gcp" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/generator" ) func (cli *CLI) NewCreateCoRIMCmd() *cobra.Command { diff --git a/cli/attestation_policy_corim_test.go b/cli/attestation_policy_corim_test.go index ceada8d3..534c767d 100644 --- a/cli/attestation_policy_corim_test.go +++ b/cli/attestation_policy_corim_test.go @@ -10,11 +10,11 @@ import ( "path/filepath" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/gcp" "github.com/google/gce-tcb-verifier/proto/endorsement" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/azure" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/gcp" "google.golang.org/protobuf/proto" ) diff --git a/cli/attestation_policy_test.go b/cli/attestation_policy_test.go index 936ced5f..03262e0d 100644 --- a/cli/attestation_policy_test.go +++ b/cli/attestation_policy_test.go @@ -10,12 +10,12 @@ import ( "path/filepath" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/gcp" "github.com/google/gce-tcb-verifier/proto/endorsement" "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-tpm-tools/proto/attest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/gcp" "google.golang.org/protobuf/proto" ) diff --git a/cli/attestation_test.go b/cli/attestation_test.go index ffbf6fea..41b1292e 100644 --- a/cli/attestation_test.go +++ b/cli/attestation_test.go @@ -8,12 +8,12 @@ import ( "os" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/sdk/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" - "github.com/thinksyncs/agents-secure-binding/pkg/sdk/mocks" ) func TestNewAttestationCmd(t *testing.T) { diff --git a/cli/checksum.go b/cli/checksum.go index da9797ef..e9792b5a 100644 --- a/cli/checksum.go +++ b/cli/checksum.go @@ -8,9 +8,9 @@ import ( "encoding/json" "os" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/internal" "golang.org/x/crypto/sha3" ) diff --git a/cli/datasets.go b/cli/datasets.go index ae125f9f..3f95d01a 100644 --- a/cli/datasets.go +++ b/cli/datasets.go @@ -9,11 +9,11 @@ import ( "os" "path" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/internal" - "github.com/thinksyncs/agents-secure-binding/internal/errors" "google.golang.org/grpc/metadata" ) diff --git a/cli/datasets_test.go b/cli/datasets_test.go index 9ef3d0bd..c66e0df9 100644 --- a/cli/datasets_test.go +++ b/cli/datasets_test.go @@ -8,9 +8,9 @@ import ( "os" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/sdk/mocks" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/pkg/sdk/mocks" ) func createTempDatasetFile(content string) (string, error) { diff --git a/cli/errors.go b/cli/errors.go index 1d48c1f0..f7ef98c4 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -3,11 +3,11 @@ package cli import ( + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/auth" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/agent" "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/agent/auth" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/agent" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/cli/errors_test.go b/cli/errors_test.go index a8b5d4a0..c27a90c7 100644 --- a/cli/errors_test.go +++ b/cli/errors_test.go @@ -7,11 +7,11 @@ import ( "errors" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/auth" + mgerrors "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/agent" "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/agent/auth" - mgerrors "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/agent" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/cli/ima_measurements.go b/cli/ima_measurements.go index cfa8e691..aa86c988 100644 --- a/cli/ima_measurements.go +++ b/cli/ima_measurements.go @@ -9,9 +9,9 @@ import ( "os" "strings" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" ) const ( diff --git a/cli/ima_measurements_test.go b/cli/ima_measurements_test.go index 7d3b2aa3..3af731a9 100644 --- a/cli/ima_measurements_test.go +++ b/cli/ima_measurements_test.go @@ -9,9 +9,9 @@ import ( "os" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/sdk/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/pkg/sdk/mocks" ) func TestCLI_NewIMAMeasurementsCmd(t *testing.T) { diff --git a/cli/manager.go b/cli/manager.go index b2cf227d..af042a5a 100644 --- a/cli/manager.go +++ b/cli/manager.go @@ -6,9 +6,9 @@ import ( "os" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/manager" ) const ( diff --git a/cli/manager_test.go b/cli/manager_test.go index 2fdb0659..7f04d564 100644 --- a/cli/manager_test.go +++ b/cli/manager_test.go @@ -10,11 +10,11 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/manager" - "github.com/thinksyncs/agents-secure-binding/manager/mocks" "google.golang.org/protobuf/types/known/emptypb" ) diff --git a/cli/result_test.go b/cli/result_test.go index 1115b962..651c9934 100644 --- a/cli/result_test.go +++ b/cli/result_test.go @@ -10,9 +10,9 @@ import ( "path/filepath" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/sdk/mocks" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/pkg/sdk/mocks" ) const compResult = "Test computation result" diff --git a/cli/sdk.go b/cli/sdk.go index 8b67bd75..d99064a2 100644 --- a/cli/sdk.go +++ b/cli/sdk.go @@ -5,14 +5,14 @@ package cli import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/cmdconfig" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/agent" + managergrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/sdk" "github.com/spf13/cobra" - "github.com/thinksyncs/agents-secure-binding/manager" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/cmdconfig" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc" - "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/agent" - managergrpc "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/manager" - "github.com/thinksyncs/agents-secure-binding/pkg/sdk" ) var Verbose bool diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 1da9aa5c..154d6267 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -16,27 +16,27 @@ import ( "os/signal" "syscall" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/api" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + cvmsapi "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/server" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" + logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" + agentlogger "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/logger" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/metrics" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + pkggrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" + attestation_client "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/attestation" + cvmsgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/cvm" + logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" + runnerclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/runner" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/ingress" "github.com/caarlos0/env/v11" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/api" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - cvmsapi "github.com/thinksyncs/agents-secure-binding/agent/cvms/api/grpc" - "github.com/thinksyncs/agents-secure-binding/agent/cvms/server" - "github.com/thinksyncs/agents-secure-binding/agent/events" - logpb "github.com/thinksyncs/agents-secure-binding/agent/log" - agentlogger "github.com/thinksyncs/agents-secure-binding/internal/logger" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/metrics" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/azure" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - pkggrpc "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc" - attestation_client "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/attestation" - cvmsgrpc "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/cvm" - logclient "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/log" - runnerclient "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/runner" - "github.com/thinksyncs/agents-secure-binding/pkg/ingress" "golang.org/x/sync/errgroup" ) diff --git a/cmd/attestation-service/fetch_raw_evidence.go b/cmd/attestation-service/fetch_raw_evidence.go index aca5d819..afc700bc 100644 --- a/cmd/attestation-service/fetch_raw_evidence.go +++ b/cmd/attestation-service/fetch_raw_evidence.go @@ -7,7 +7,7 @@ import ( "encoding/hex" "fmt" - attestationpb "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation/v1" + attestationpb "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation/v1" ) func (s *service) FetchRawEvidence(ctx context.Context, req *attestationpb.AttestationRequest) (*attestationpb.RawEvidenceResponse, error) { diff --git a/cmd/attestation-service/main.go b/cmd/attestation-service/main.go index 49fd64fc..e97148b6 100644 --- a/cmd/attestation-service/main.go +++ b/cmd/attestation-service/main.go @@ -12,19 +12,19 @@ import ( "os/signal" "syscall" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" + agentlogger "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/logger" + attestationpb "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation/v1" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/ccaa" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/tdx" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" + logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" "github.com/caarlos0/env/v11" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - logpb "github.com/thinksyncs/agents-secure-binding/agent/log" - agentlogger "github.com/thinksyncs/agents-secure-binding/internal/logger" - attestationpb "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation/v1" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/azure" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/ccaa" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/eat" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/tdx" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" - logclient "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/log" "golang.org/x/sync/errgroup" "google.golang.org/grpc" ) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 74a21044..95bf0346 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -9,13 +9,13 @@ import ( "path" "syscall" + "github.com/ToppyMicroServices/agents-secure-binding/v2/cli" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/cmdconfig" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/caarlos0/env/v11" "github.com/fatih/color" "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/thinksyncs/agents-secure-binding/cli" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/cmdconfig" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" cmd "github.com/virtee/sev-snp-measure-go/sevsnpmeasure/cmd" ) diff --git a/cmd/computation-runner/main.go b/cmd/computation-runner/main.go index 283a4b54..4b151b76 100644 --- a/cmd/computation-runner/main.go +++ b/cmd/computation-runner/main.go @@ -11,15 +11,15 @@ import ( "os/signal" "syscall" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" + pb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner" + runnerevents "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner/events" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner/service" + agentlogger "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/logger" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" "github.com/caarlos0/env/v11" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - logpb "github.com/thinksyncs/agents-secure-binding/agent/log" - pb "github.com/thinksyncs/agents-secure-binding/agent/runner" - runnerevents "github.com/thinksyncs/agents-secure-binding/agent/runner/events" - "github.com/thinksyncs/agents-secure-binding/agent/runner/service" - agentlogger "github.com/thinksyncs/agents-secure-binding/internal/logger" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - logclient "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/log" "golang.org/x/sync/errgroup" "google.golang.org/grpc" ) diff --git a/cmd/egress-proxy/main.go b/cmd/egress-proxy/main.go index d60f34ab..7e020e8c 100644 --- a/cmd/egress-proxy/main.go +++ b/cmd/egress-proxy/main.go @@ -11,14 +11,14 @@ import ( "strings" "syscall" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" + agentlogger "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/logger" + logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/egress" "github.com/caarlos0/env/v11" "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - logpb "github.com/thinksyncs/agents-secure-binding/agent/log" - agentlogger "github.com/thinksyncs/agents-secure-binding/internal/logger" - logclient "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/log" - "github.com/thinksyncs/agents-secure-binding/pkg/egress" "golang.org/x/sync/errgroup" ) diff --git a/cmd/hardware-attestation-redteam/main.go b/cmd/hardware-attestation-redteam/main.go index f2ae8471..ef5455fe 100644 --- a/cmd/hardware-attestation-redteam/main.go +++ b/cmd/hardware-attestation-redteam/main.go @@ -17,13 +17,13 @@ import ( "strings" "time" + qemu "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/tdx" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" sevsnppb "github.com/google/go-sev-guest/proto/sevsnp" tdxabi "github.com/google/go-tdx-guest/abi" tdxpb "github.com/google/go-tdx-guest/proto/tdx" - qemu "github.com/thinksyncs/agents-secure-binding/manager/qemu" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/tdx" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" "google.golang.org/protobuf/proto" ) diff --git a/cmd/ingress-proxy/main.go b/cmd/ingress-proxy/main.go index 228f1d32..e6189d01 100644 --- a/cmd/ingress-proxy/main.go +++ b/cmd/ingress-proxy/main.go @@ -11,18 +11,18 @@ import ( "os/signal" "syscall" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" + agentlogger "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/logger" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" + attestation_client "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/attestation" + logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/ingress" "github.com/caarlos0/env/v11" "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - logpb "github.com/thinksyncs/agents-secure-binding/agent/log" - agentlogger "github.com/thinksyncs/agents-secure-binding/internal/logger" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/azure" - attestation_client "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/attestation" - logclient "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/log" - "github.com/thinksyncs/agents-secure-binding/pkg/ingress" "golang.org/x/sync/errgroup" ) diff --git a/cmd/log-forwarder/main.go b/cmd/log-forwarder/main.go index 07471bb9..987ae7b5 100644 --- a/cmd/log-forwarder/main.go +++ b/cmd/log-forwarder/main.go @@ -11,13 +11,13 @@ import ( "os/signal" "syscall" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + pb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log/service" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + cvmsgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/cvm" "github.com/caarlos0/env/v11" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - pb "github.com/thinksyncs/agents-secure-binding/agent/log" - "github.com/thinksyncs/agents-secure-binding/agent/log/service" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - cvmsgrpc "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/cvm" "golang.org/x/sync/errgroup" "google.golang.org/grpc" ) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 3eb10f2f..fdc3b96a 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -13,21 +13,21 @@ import ( "os" "strings" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/metrics" + smqserver "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/server" + grpcserver "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/server/grpc" + httpserver "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/server/http" + runtimetracing "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/tracing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/api" + managergrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/api/http" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" + managertracing "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/tracing" "github.com/caarlos0/env/v11" "github.com/go-chi/chi/v5" "github.com/google/uuid" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/metrics" - smqserver "github.com/thinksyncs/agents-secure-binding/internal/runtime/server" - grpcserver "github.com/thinksyncs/agents-secure-binding/internal/runtime/server/grpc" - httpserver "github.com/thinksyncs/agents-secure-binding/internal/runtime/server/http" - runtimetracing "github.com/thinksyncs/agents-secure-binding/internal/runtime/tracing" - "github.com/thinksyncs/agents-secure-binding/manager" - "github.com/thinksyncs/agents-secure-binding/manager/api" - managergrpc "github.com/thinksyncs/agents-secure-binding/manager/api/grpc" - "github.com/thinksyncs/agents-secure-binding/manager/api/http" - "github.com/thinksyncs/agents-secure-binding/manager/qemu" - managertracing "github.com/thinksyncs/agents-secure-binding/manager/tracing" "go.opentelemetry.io/otel/trace" "golang.org/x/sync/errgroup" "google.golang.org/grpc" diff --git a/cmd/redis-failover-redteam/main.go b/cmd/redis-failover-redteam/main.go index d38e7034..e8070599 100644 --- a/cmd/redis-failover-redteam/main.go +++ b/cmd/redis-failover-redteam/main.go @@ -20,7 +20,7 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/production" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" ) const ( diff --git a/examples/a2a-multiprocess/agent_a.go b/examples/a2a-multiprocess/agent_a.go index fe5fac51..c522e189 100644 --- a/examples/a2a-multiprocess/agent_a.go +++ b/examples/a2a-multiprocess/agent_a.go @@ -24,8 +24,8 @@ import ( "sync/atomic" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" ) type a2aConnection struct { diff --git a/examples/a2a-multiprocess/agent_a_v2.go b/examples/a2a-multiprocess/agent_a_v2.go index e8360349..d9af7f9c 100644 --- a/examples/a2a-multiprocess/agent_a_v2.go +++ b/examples/a2a-multiprocess/agent_a_v2.go @@ -16,8 +16,8 @@ import ( "path/filepath" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" ) type issueMutationV2 func(claims jwt.MapClaims, sbo *securityBindingObjectV2, request *a2aSendMessageRequest) diff --git a/examples/a2a-multiprocess/agent_b.go b/examples/a2a-multiprocess/agent_b.go index ee944094..44376636 100644 --- a/examples/a2a-multiprocess/agent_b.go +++ b/examples/a2a-multiprocess/agent_b.go @@ -15,10 +15,10 @@ import ( "path/filepath" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/llmruntime" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/llmruntime" ) type agentBServer struct { diff --git a/examples/a2a-multiprocess/agent_b_v2.go b/examples/a2a-multiprocess/agent_b_v2.go index c0e6ac61..f680022e 100644 --- a/examples/a2a-multiprocess/agent_b_v2.go +++ b/examples/a2a-multiprocess/agent_b_v2.go @@ -15,11 +15,11 @@ import ( "path/filepath" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/llmruntime" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/operationjournal" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/llmruntime" - "github.com/thinksyncs/agents-secure-binding/pkg/operationjournal" ) type agentBServerV2 struct { diff --git a/examples/a2a-multiprocess/attester.go b/examples/a2a-multiprocess/attester.go index f5720857..2fc121ce 100644 --- a/examples/a2a-multiprocess/attester.go +++ b/examples/a2a-multiprocess/attester.go @@ -14,10 +14,10 @@ import ( "strings" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/tdx" "github.com/golang-jwt/jwt/v5" sevclient "github.com/google/go-sev-guest/client" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/tdx" "google.golang.org/protobuf/proto" ) diff --git a/examples/a2a-multiprocess/conversation.go b/examples/a2a-multiprocess/conversation.go index 5625ba99..9de6bda3 100644 --- a/examples/a2a-multiprocess/conversation.go +++ b/examples/a2a-multiprocess/conversation.go @@ -14,7 +14,7 @@ import ( "unicode" "unicode/utf8" - "github.com/thinksyncs/agents-secure-binding/pkg/llmruntime" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/llmruntime" ) const ( diff --git a/examples/a2a-multiprocess/conversation_test.go b/examples/a2a-multiprocess/conversation_test.go index f555242a..e31e3590 100644 --- a/examples/a2a-multiprocess/conversation_test.go +++ b/examples/a2a-multiprocess/conversation_test.go @@ -18,7 +18,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/a2asecuritytest" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/a2asecuritytest" ) func TestConversationServerWriteTimeoutCoversModelCall(t *testing.T) { diff --git a/examples/a2a-multiprocess/conversation_v2_test.go b/examples/a2a-multiprocess/conversation_v2_test.go index 2a5c0284..bc9b728a 100644 --- a/examples/a2a-multiprocess/conversation_v2_test.go +++ b/examples/a2a-multiprocess/conversation_v2_test.go @@ -13,10 +13,10 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/a2asecuritytest" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/llmruntime" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/a2asecuritytest" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/llmruntime" ) type recordingConversationGeneratorV2 struct { diff --git a/examples/a2a-multiprocess/main_test.go b/examples/a2a-multiprocess/main_test.go index 099a02b1..e11074ad 100644 --- a/examples/a2a-multiprocess/main_test.go +++ b/examples/a2a-multiprocess/main_test.go @@ -18,7 +18,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/a2asecuritytest" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/a2asecuritytest" "gopkg.in/yaml.v3" ) diff --git a/examples/a2a-multiprocess/manager.go b/examples/a2a-multiprocess/manager.go index 7c3c4ec1..5731d10b 100644 --- a/examples/a2a-multiprocess/manager.go +++ b/examples/a2a-multiprocess/manager.go @@ -11,8 +11,8 @@ import ( "path/filepath" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" ) func runManager(ctx context.Context, opts options, out outputWriter) error { diff --git a/examples/a2a-multiprocess/multi_host.go b/examples/a2a-multiprocess/multi_host.go index 45417e2a..2b9b8f6a 100644 --- a/examples/a2a-multiprocess/multi_host.go +++ b/examples/a2a-multiprocess/multi_host.go @@ -23,7 +23,7 @@ import ( "time" "unicode/utf8" - "github.com/thinksyncs/agents-secure-binding/pkg/a2asecuritytest" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/a2asecuritytest" ) const ( diff --git a/examples/a2a-multiprocess/multi_host_test.go b/examples/a2a-multiprocess/multi_host_test.go index 52cbdd7f..a377eebb 100644 --- a/examples/a2a-multiprocess/multi_host_test.go +++ b/examples/a2a-multiprocess/multi_host_test.go @@ -12,7 +12,7 @@ import ( "strings" "testing" - "github.com/thinksyncs/agents-secure-binding/pkg/a2asecuritytest" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/a2asecuritytest" ) const multiHostExamplePath = "testdata/multihost-deployment.example.json" diff --git a/examples/a2a-multiprocess/operation_acceptance_v2.go b/examples/a2a-multiprocess/operation_acceptance_v2.go index d766fae2..da00b517 100644 --- a/examples/a2a-multiprocess/operation_acceptance_v2.go +++ b/examples/a2a-multiprocess/operation_acceptance_v2.go @@ -17,8 +17,8 @@ import ( "sync" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/operationjournal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/operationjournal" ) const ( diff --git a/examples/a2a-multiprocess/operation_acceptance_v2_test.go b/examples/a2a-multiprocess/operation_acceptance_v2_test.go index 2d695c80..6e254322 100644 --- a/examples/a2a-multiprocess/operation_acceptance_v2_test.go +++ b/examples/a2a-multiprocess/operation_acceptance_v2_test.go @@ -21,8 +21,8 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/operationjournal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/operationjournal" ) func TestApplicationOperationV2ExcludesAuthenticationAttemptData(t *testing.T) { diff --git a/examples/a2a-multiprocess/protocol.go b/examples/a2a-multiprocess/protocol.go index 26dd30ec..81a769ae 100644 --- a/examples/a2a-multiprocess/protocol.go +++ b/examples/a2a-multiprocess/protocol.go @@ -11,11 +11,11 @@ import ( "fmt" "strings" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" ) func canonicalRequestContext(request a2aSendMessageRequest) ([]byte, error) { diff --git a/examples/a2a-multiprocess/protocol_v2.go b/examples/a2a-multiprocess/protocol_v2.go index b565d3d6..c3c5df5e 100644 --- a/examples/a2a-multiprocess/protocol_v2.go +++ b/examples/a2a-multiprocess/protocol_v2.go @@ -21,10 +21,10 @@ import ( "unicode" "unicode/utf8" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/sbaipv2" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/sbaipv2" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" ) type challengeRequestV2 struct{} diff --git a/examples/a2a-multiprocess/replay.go b/examples/a2a-multiprocess/replay.go index 9865a9ca..023bf18d 100644 --- a/examples/a2a-multiprocess/replay.go +++ b/examples/a2a-multiprocess/replay.go @@ -16,8 +16,8 @@ import ( "sync" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/operationjournal" - "github.com/thinksyncs/agents-secure-binding/pkg/production" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/operationjournal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" ) const ( diff --git a/examples/a2a-multiprocess/report.go b/examples/a2a-multiprocess/report.go index 1218368c..655c97c5 100644 --- a/examples/a2a-multiprocess/report.go +++ b/examples/a2a-multiprocess/report.go @@ -11,8 +11,8 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/a2asecuritytest" - "github.com/thinksyncs/agents-secure-binding/schemas" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/a2asecuritytest" + "github.com/ToppyMicroServices/agents-secure-binding/v2/schemas" ) const acceptedReason = "accepted" diff --git a/examples/a2a-multiprocess/result_recovery_v2.go b/examples/a2a-multiprocess/result_recovery_v2.go index 60ab9c39..eefddd45 100644 --- a/examples/a2a-multiprocess/result_recovery_v2.go +++ b/examples/a2a-multiprocess/result_recovery_v2.go @@ -16,7 +16,7 @@ import ( "os" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/operationjournal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/operationjournal" ) const resultSealingKeyBytesV2 = 32 diff --git a/examples/a2a-multiprocess/result_recovery_v2_test.go b/examples/a2a-multiprocess/result_recovery_v2_test.go index 248eb369..0f0d1e10 100644 --- a/examples/a2a-multiprocess/result_recovery_v2_test.go +++ b/examples/a2a-multiprocess/result_recovery_v2_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/operationjournal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/operationjournal" ) func TestResultSealerV2BindsExactOperationAndResponse(t *testing.T) { diff --git a/examples/a2a-multiprocess/section21_v2_test.go b/examples/a2a-multiprocess/section21_v2_test.go index 5e2bded1..80e8c1f7 100644 --- a/examples/a2a-multiprocess/section21_v2_test.go +++ b/examples/a2a-multiprocess/section21_v2_test.go @@ -17,8 +17,8 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" ) func TestDraft06Section21RejectsMissingOrWrongExporter(t *testing.T) { diff --git a/examples/a2a-multiprocess/wire_fixture_v2_test.go b/examples/a2a-multiprocess/wire_fixture_v2_test.go index 88509a77..578143ce 100644 --- a/examples/a2a-multiprocess/wire_fixture_v2_test.go +++ b/examples/a2a-multiprocess/wire_fixture_v2_test.go @@ -22,9 +22,9 @@ import ( "time" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/sbaipv2" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/sbaipv2" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" ) type draft06V2WireFixture struct { diff --git a/examples/a2a/main.go b/examples/a2a/main.go index 2bce33b2..9dcaa0e9 100644 --- a/examples/a2a/main.go +++ b/examples/a2a/main.go @@ -22,11 +22,11 @@ import ( "os" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" ) const ( diff --git a/examples/agtp-discover-consumer/app.go b/examples/agtp-discover-consumer/app.go index b590a3a4..c44c12c4 100644 --- a/examples/agtp-discover-consumer/app.go +++ b/examples/agtp-discover-consumer/app.go @@ -16,9 +16,9 @@ import ( "net/http" "strings" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/production" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" ) const ( diff --git a/examples/agtp-discover-consumer/e2e_test.go b/examples/agtp-discover-consumer/e2e_test.go index a2c009ab..3a8565a6 100644 --- a/examples/agtp-discover-consumer/e2e_test.go +++ b/examples/agtp-discover-consumer/e2e_test.go @@ -23,11 +23,11 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/production" ) const ( diff --git a/examples/authority-quorum-demo/main.go b/examples/authority-quorum-demo/main.go index 2a477620..47e4154e 100644 --- a/examples/authority-quorum-demo/main.go +++ b/examples/authority-quorum-demo/main.go @@ -15,7 +15,7 @@ import ( "log" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/authorityquorum" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/authorityquorum" ) func main() { diff --git a/examples/protected-change-consumer/app.go b/examples/protected-change-consumer/app.go index 556468a9..c9afac7b 100644 --- a/examples/protected-change-consumer/app.go +++ b/examples/protected-change-consumer/app.go @@ -20,8 +20,8 @@ import ( "strings" "sync" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/production" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" ) const ( diff --git a/examples/protected-change-consumer/e2e_test.go b/examples/protected-change-consumer/e2e_test.go index 05209d46..5d442e93 100644 --- a/examples/protected-change-consumer/e2e_test.go +++ b/examples/protected-change-consumer/e2e_test.go @@ -25,10 +25,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/production" ) const ( diff --git a/go.mod b/go.mod index 1dbac83f..45beed4b 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/thinksyncs/agents-secure-binding +module github.com/ToppyMicroServices/agents-secure-binding/v2 go 1.26.0 @@ -118,7 +118,7 @@ require ( golang.org/x/text v0.39.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + gopkg.in/yaml.v3 v3.0.1 ) replace github.com/virtee/sev-snp-measure-go => github.com/sammyoina/sev-snp-measure-go v0.0.0-20241202151803-ef189f0ff825 diff --git a/internal/logger/protohandler.go b/internal/logger/protohandler.go index aa69b32c..92779b88 100644 --- a/internal/logger/protohandler.go +++ b/internal/logger/protohandler.go @@ -7,8 +7,8 @@ import ( "io" "log/slog" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/agent/events" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/timestamppb" ) diff --git a/internal/logger/protohandler_test.go b/internal/logger/protohandler_test.go index f0823993..f4d65ae9 100644 --- a/internal/logger/protohandler_test.go +++ b/internal/logger/protohandler_test.go @@ -9,9 +9,9 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/internal/errors" ) type failedWriter struct{} diff --git a/internal/proto/attestation-agent/attestation-agent.pb.go b/internal/proto/attestation-agent/attestation-agent.pb.go index 6edad5cb..6fd389d3 100644 --- a/internal/proto/attestation-agent/attestation-agent.pb.go +++ b/internal/proto/attestation-agent/attestation-agent.pb.go @@ -609,7 +609,7 @@ const file_internal_proto_attestation_agent_attestation_agent_proto_rawDesc = "" "\x18ExtendRuntimeMeasurement\x122.attestation_agent.ExtendRuntimeMeasurementRequest\x1a3.attestation_agent.ExtendRuntimeMeasurementResponse\"\x00\x12a\n" + "\fBindInitData\x12&.attestation_agent.BindInitDataRequest\x1a'.attestation_agent.BindInitDataResponse\"\x00\x12[\n" + "\n" + - "GetTeeType\x12$.attestation_agent.GetTeeTypeRequest\x1a%.attestation_agent.GetTeeTypeResponse\"\x00B`Z^github.com/thinksyncs/agents-secure-binding/internal/proto/attestation-agent;attestation_agentb\x06proto3" + "GetTeeType\x12$.attestation_agent.GetTeeTypeRequest\x1a%.attestation_agent.GetTeeTypeResponse\"\x00BkZigithub.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation-agent;attestation_agentb\x06proto3" var ( file_internal_proto_attestation_agent_attestation_agent_proto_rawDescOnce sync.Once diff --git a/internal/proto/attestation-agent/attestation-agent.proto b/internal/proto/attestation-agent/attestation-agent.proto index 7e0588f2..4bda4b23 100644 --- a/internal/proto/attestation-agent/attestation-agent.proto +++ b/internal/proto/attestation-agent/attestation-agent.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package attestation_agent; -option go_package = "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation-agent;attestation_agent"; +option go_package = "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation-agent;attestation_agent"; // Get the primary evidence from the guest. message GetEvidenceRequest { diff --git a/internal/proto/attestation-agent/mocks/attestationagentserviceclient.go b/internal/proto/attestation-agent/mocks/attestationagentserviceclient.go index c5cce533..c01422a3 100644 --- a/internal/proto/attestation-agent/mocks/attestationagentserviceclient.go +++ b/internal/proto/attestation-agent/mocks/attestationagentserviceclient.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation-agent" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation-agent" "google.golang.org/grpc" ) diff --git a/internal/proto/attestation/v1/attestation.pb.go b/internal/proto/attestation/v1/attestation.pb.go index 284d7067..5c84ae48 100644 --- a/internal/proto/attestation/v1/attestation.pb.go +++ b/internal/proto/attestation/v1/attestation.pb.go @@ -347,7 +347,7 @@ const file_internal_proto_attestation_v1_attestation_proto_rawDesc = "" + "\x12AttestationService\x12[\n" + "\x10FetchAttestation\x12\".attestation.v1.AttestationRequest\x1a#.attestation.v1.AttestationResponse\x12[\n" + "\x10FetchRawEvidence\x12\".attestation.v1.AttestationRequest\x1a#.attestation.v1.RawEvidenceResponse\x12X\n" + - "\x0fFetchAzureToken\x12!.attestation.v1.AzureTokenRequest\x1a\".attestation.v1.AzureTokenResponseBWZUgithub.com/thinksyncs/agents-secure-binding/internal/proto/attestation/v1;attestationb\x06proto3" + "\x0fFetchAzureToken\x12!.attestation.v1.AzureTokenRequest\x1a\".attestation.v1.AzureTokenResponseBbZ`github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation/v1;attestationb\x06proto3" var ( file_internal_proto_attestation_v1_attestation_proto_rawDescOnce sync.Once diff --git a/internal/proto/attestation/v1/attestation.proto b/internal/proto/attestation/v1/attestation.proto index 6c13771f..8a69ab62 100644 --- a/internal/proto/attestation/v1/attestation.proto +++ b/internal/proto/attestation/v1/attestation.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package attestation.v1; -option go_package = "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation/v1;attestation"; +option go_package = "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation/v1;attestation"; service AttestationService { rpc FetchAttestation (AttestationRequest) returns (AttestationResponse); diff --git a/internal/runtime/server/grpc/grpc.go b/internal/runtime/server/grpc/grpc.go index ba5b19bf..b61aec44 100644 --- a/internal/runtime/server/grpc/grpc.go +++ b/internal/runtime/server/grpc/grpc.go @@ -12,8 +12,8 @@ import ( "net" "time" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/netguard" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/server" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/netguard" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/server" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" diff --git a/internal/runtime/server/http/http.go b/internal/runtime/server/http/http.go index ed622ddc..530b0c2d 100644 --- a/internal/runtime/server/http/http.go +++ b/internal/runtime/server/http/http.go @@ -10,7 +10,7 @@ import ( "log/slog" "net/http" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/server" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/server" ) type runtimeServer struct { diff --git a/internal/runtime/tracing/tracing.go b/internal/runtime/tracing/tracing.go index 7a179070..a0c07256 100644 --- a/internal/runtime/tracing/tracing.go +++ b/internal/runtime/tracing/tracing.go @@ -8,7 +8,7 @@ import ( "errors" "net/url" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/netguard" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/netguard" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" diff --git a/manager/api/grpc/server.go b/manager/api/grpc/server.go index a64662ad..fadf0ddf 100644 --- a/manager/api/grpc/server.go +++ b/manager/api/grpc/server.go @@ -6,7 +6,7 @@ import ( "context" "errors" - "github.com/thinksyncs/agents-secure-binding/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" "google.golang.org/protobuf/types/known/emptypb" ) diff --git a/manager/api/grpc/server_test.go b/manager/api/grpc/server_test.go index 26431d1d..14f66b17 100644 --- a/manager/api/grpc/server_test.go +++ b/manager/api/grpc/server_test.go @@ -10,10 +10,10 @@ import ( "errors" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/manager" - "github.com/thinksyncs/agents-secure-binding/manager/mocks" "google.golang.org/protobuf/types/known/emptypb" ) diff --git a/manager/api/logging.go b/manager/api/logging.go index 6efb211d..ea958e68 100644 --- a/manager/api/logging.go +++ b/manager/api/logging.go @@ -11,7 +11,7 @@ import ( "log/slog" "time" - "github.com/thinksyncs/agents-secure-binding/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" ) var _ manager.Service = (*loggingMiddleware)(nil) diff --git a/manager/api/metrics.go b/manager/api/metrics.go index 30617cbb..4ea07e82 100644 --- a/manager/api/metrics.go +++ b/manager/api/metrics.go @@ -9,8 +9,8 @@ import ( "context" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" "github.com/go-kit/kit/metrics" - "github.com/thinksyncs/agents-secure-binding/manager" ) var _ manager.Service = (*metricsMiddleware)(nil) diff --git a/manager/attestation_policy.go b/manager/attestation_policy.go index 86e5badd..f19783e9 100644 --- a/manager/attestation_policy.go +++ b/manager/attestation_policy.go @@ -11,9 +11,9 @@ import ( "context" "fmt" - "github.com/thinksyncs/agents-secure-binding/manager/qemu" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/generator" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/igvmmeasure" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/generator" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/igvmmeasure" ) func (ms *managerService) FetchAttestationPolicy(_ context.Context, computationId string) ([]byte, error) { diff --git a/manager/attestation_policy_embed.go b/manager/attestation_policy_embed.go index 835d4145..5893ff43 100644 --- a/manager/attestation_policy_embed.go +++ b/manager/attestation_policy_embed.go @@ -8,7 +8,7 @@ package manager import ( "context" /* - attestationPolicy "github.com/thinksyncs/agents-secure-binding/scripts/attestation_policy/sev-snp" + attestationPolicy "github.com/ToppyMicroServices/agents-secure-binding/v2/scripts/attestation_policy/sev-snp" */) func (ms *managerService) FetchAttestationPolicy(_ context.Context, _ string) ([]byte, error) { diff --git a/manager/attestation_policy_test.go b/manager/attestation_policy_test.go index 1049dbda..1abf20a9 100644 --- a/manager/attestation_policy_test.go +++ b/manager/attestation_policy_test.go @@ -8,10 +8,10 @@ import ( "path/filepath" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/vm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/vm/mocks" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/manager/qemu" - "github.com/thinksyncs/agents-secure-binding/manager/vm" - "github.com/thinksyncs/agents-secure-binding/manager/vm/mocks" "github.com/veraison/corim/corim" ) diff --git a/manager/mocks/managerserviceclient.go b/manager/mocks/managerserviceclient.go index b4252411..98f2553e 100644 --- a/manager/mocks/managerserviceclient.go +++ b/manager/mocks/managerserviceclient.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/manager" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" ) diff --git a/manager/mocks/service.go b/manager/mocks/service.go index f6a138a7..ecdd6303 100644 --- a/manager/mocks/service.go +++ b/manager/mocks/service.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/manager" ) // NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/manager/qemu/mocks/persistence.go b/manager/qemu/mocks/persistence.go index f08ad68d..a9167519 100644 --- a/manager/qemu/mocks/persistence.go +++ b/manager/qemu/mocks/persistence.go @@ -8,8 +8,8 @@ package mocks import ( + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/manager/qemu" ) // NewPersistence creates a new instance of Persistence. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/manager/qemu/vm.go b/manager/qemu/vm.go index 78b1813f..268a9b33 100644 --- a/manager/qemu/vm.go +++ b/manager/qemu/vm.go @@ -11,10 +11,10 @@ import ( "syscall" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/vm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" "github.com/gofrs/uuid" - "github.com/thinksyncs/agents-secure-binding/internal" - "github.com/thinksyncs/agents-secure-binding/manager/vm" - "github.com/thinksyncs/agents-secure-binding/pkg/manager" ) const ( diff --git a/manager/qemu/vm_test.go b/manager/qemu/vm_test.go index cde98ce7..277a91fa 100644 --- a/manager/qemu/vm_test.go +++ b/manager/qemu/vm_test.go @@ -8,9 +8,9 @@ import ( "os/exec" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/vm/mocks" + pkgmanager "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/manager/vm/mocks" - pkgmanager "github.com/thinksyncs/agents-secure-binding/pkg/manager" ) const testComputationID = "test-computation" diff --git a/manager/service.go b/manager/service.go index 6a221108..a4a8a1dd 100644 --- a/manager/service.go +++ b/manager/service.go @@ -17,12 +17,12 @@ import ( "time" "unicode" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/vm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" "github.com/google/uuid" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/manager/qemu" - "github.com/thinksyncs/agents-secure-binding/manager/vm" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/corimgen" - "github.com/thinksyncs/agents-secure-binding/pkg/manager" ) const ( diff --git a/manager/service_test.go b/manager/service_test.go index cb40b2f4..30fb6a5c 100644 --- a/manager/service_test.go +++ b/manager/service_test.go @@ -13,15 +13,15 @@ import ( "path/filepath" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" + persistenceMocks "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/vm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/vm/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - "github.com/thinksyncs/agents-secure-binding/manager/qemu" - persistenceMocks "github.com/thinksyncs/agents-secure-binding/manager/qemu/mocks" - "github.com/thinksyncs/agents-secure-binding/manager/vm" - "github.com/thinksyncs/agents-secure-binding/manager/vm/mocks" "github.com/veraison/corim/corim" ) diff --git a/manager/tracing/tracing.go b/manager/tracing/tracing.go index a6624159..eb3d5570 100644 --- a/manager/tracing/tracing.go +++ b/manager/tracing/tracing.go @@ -5,7 +5,7 @@ package tracing import ( "context" - "github.com/thinksyncs/agents-secure-binding/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" "go.opentelemetry.io/otel/trace" ) diff --git a/manager/vm/mocks/provider.go b/manager/vm/mocks/provider.go index d8946609..5c68a931 100644 --- a/manager/vm/mocks/provider.go +++ b/manager/vm/mocks/provider.go @@ -10,7 +10,7 @@ import ( mock "github.com/stretchr/testify/mock" - vm "github.com/thinksyncs/agents-secure-binding/manager/vm" + vm "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/vm" ) // Provider is an autogenerated mock type for the Provider type diff --git a/manager/vm/mocks/statemachine.go b/manager/vm/mocks/statemachine.go index b9064350..7128c670 100644 --- a/manager/vm/mocks/statemachine.go +++ b/manager/vm/mocks/statemachine.go @@ -8,8 +8,8 @@ package mocks import ( + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/pkg/manager" ) // NewStateMachine creates a new instance of StateMachine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/manager/vm/mocks/vm.go b/manager/vm/mocks/vm.go index 2a4e2986..9b91b2dd 100644 --- a/manager/vm/mocks/vm.go +++ b/manager/vm/mocks/vm.go @@ -8,8 +8,8 @@ package mocks import ( + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/pkg/manager" ) // NewVM creates a new instance of VM. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. diff --git a/manager/vm/state.go b/manager/vm/state.go index 8085466f..5779bbf3 100644 --- a/manager/vm/state.go +++ b/manager/vm/state.go @@ -6,7 +6,7 @@ import ( "errors" "sync" - "github.com/thinksyncs/agents-secure-binding/pkg/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" ) type sm struct { diff --git a/manager/vm/state_test.go b/manager/vm/state_test.go index db15a1fd..9d05a8b2 100644 --- a/manager/vm/state_test.go +++ b/manager/vm/state_test.go @@ -6,8 +6,8 @@ import ( "sync" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/pkg/manager" ) const numGoroutines = 10 diff --git a/manager/vm/vm.go b/manager/vm/vm.go index fc97ea29..f9fca5e2 100644 --- a/manager/vm/vm.go +++ b/manager/vm/vm.go @@ -5,7 +5,7 @@ package vm import ( "log/slog" - pkgmanager "github.com/thinksyncs/agents-secure-binding/pkg/manager" + pkgmanager "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/manager" "google.golang.org/protobuf/types/known/timestamppb" ) diff --git a/pkg/agtp/cwt.go b/pkg/agtp/cwt.go index 8a9adee0..f4ff78ab 100644 --- a/pkg/agtp/cwt.go +++ b/pkg/agtp/cwt.go @@ -11,8 +11,8 @@ import ( "strings" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/fxamacker/cbor/v2" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" "github.com/veraison/go-cose" ) diff --git a/pkg/agtp/cwt_test.go b/pkg/agtp/cwt_test.go index d0fa0318..03ee003d 100644 --- a/pkg/agtp/cwt_test.go +++ b/pkg/agtp/cwt_test.go @@ -13,8 +13,8 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/fxamacker/cbor/v2" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" "github.com/veraison/go-cose" ) diff --git a/pkg/agtp/discovery/peer/auth.go b/pkg/agtp/discovery/peer/auth.go index da6e724e..0d72df49 100644 --- a/pkg/agtp/discovery/peer/auth.go +++ b/pkg/agtp/discovery/peer/auth.go @@ -14,9 +14,9 @@ import ( "sync" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/production" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" ) const nonceLifetime = 30 * time.Second diff --git a/pkg/agtp/discovery/peer/client.go b/pkg/agtp/discovery/peer/client.go index 0ce362bc..1bd91ea1 100644 --- a/pkg/agtp/discovery/peer/client.go +++ b/pkg/agtp/discovery/peer/client.go @@ -19,11 +19,11 @@ import ( "net/http" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/production" ) // RemoteAuthorization contains verifier-specific values for one destination. diff --git a/pkg/agtp/discovery/peer/node.go b/pkg/agtp/discovery/peer/node.go index 84c7bcdf..e7000a01 100644 --- a/pkg/agtp/discovery/peer/node.go +++ b/pkg/agtp/discovery/peer/node.go @@ -19,7 +19,7 @@ import ( "sync/atomic" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" ) const ( diff --git a/pkg/agtp/discovery/peer/node_integration_test.go b/pkg/agtp/discovery/peer/node_integration_test.go index 5e15ca37..ac5815ca 100644 --- a/pkg/agtp/discovery/peer/node_integration_test.go +++ b/pkg/agtp/discovery/peer/node_integration_test.go @@ -26,10 +26,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/production" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/production" ) const ( diff --git a/pkg/agtp/discovery/peer/protocol.go b/pkg/agtp/discovery/peer/protocol.go index 00892108..0e326eb3 100644 --- a/pkg/agtp/discovery/peer/protocol.go +++ b/pkg/agtp/discovery/peer/protocol.go @@ -9,7 +9,7 @@ import ( "encoding/json" "errors" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" ) const ( diff --git a/pkg/agtp/discovery/peer/replay.go b/pkg/agtp/discovery/peer/replay.go index 6371c944..34bf1f2d 100644 --- a/pkg/agtp/discovery/peer/replay.go +++ b/pkg/agtp/discovery/peer/replay.go @@ -10,7 +10,7 @@ import ( "sync" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) type replaySnapshot struct { diff --git a/pkg/agtp/discovery/peer/store.go b/pkg/agtp/discovery/peer/store.go index 0a6cb6fe..325fe8fb 100644 --- a/pkg/agtp/discovery/peer/store.go +++ b/pkg/agtp/discovery/peer/store.go @@ -14,7 +14,7 @@ import ( "sync" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" ) const stateVersion = 1 diff --git a/pkg/agtp/discovery/peer/store_test.go b/pkg/agtp/discovery/peer/store_test.go index 3405f467..95e6e140 100644 --- a/pkg/agtp/discovery/peer/store_test.go +++ b/pkg/agtp/discovery/peer/store_test.go @@ -10,8 +10,8 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/discovery" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/discovery" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) func TestStateStoreRoundTripAndCorruption(t *testing.T) { diff --git a/pkg/agtp/gateway_route.go b/pkg/agtp/gateway_route.go index ebf0e657..100a45af 100644 --- a/pkg/agtp/gateway_route.go +++ b/pkg/agtp/gateway_route.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/gatewayroute" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/gatewayroute" ) func validateGatewayRouteCore(a gatewayroute.Assertion) error { diff --git a/pkg/agtp/gateway_route_cwt.go b/pkg/agtp/gateway_route_cwt.go index 89e37112..d7cd551a 100644 --- a/pkg/agtp/gateway_route_cwt.go +++ b/pkg/agtp/gateway_route_cwt.go @@ -6,7 +6,7 @@ package agtp import ( "time" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/gatewayroute" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/gatewayroute" "github.com/veraison/go-cose" ) diff --git a/pkg/agtp/gateway_route_jwt.go b/pkg/agtp/gateway_route_jwt.go index 6723e3f6..c353cf98 100644 --- a/pkg/agtp/gateway_route_jwt.go +++ b/pkg/agtp/gateway_route_jwt.go @@ -6,8 +6,8 @@ package agtp import ( "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/gatewayroute" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/gatewayroute" ) // GatewayRouteJWTOptions contains local policy for accepting a signed Gateway diff --git a/pkg/agtp/gateway_route_test.go b/pkg/agtp/gateway_route_test.go index 24214204..1a2f1a55 100644 --- a/pkg/agtp/gateway_route_test.go +++ b/pkg/agtp/gateway_route_test.go @@ -13,8 +13,8 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/agtp/gatewayroute" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/agtp/gatewayroute" "github.com/veraison/go-cose" ) diff --git a/pkg/agtp/gatewayroute/route.go b/pkg/agtp/gatewayroute/route.go index 87da6b84..cde3f154 100644 --- a/pkg/agtp/gatewayroute/route.go +++ b/pkg/agtp/gatewayroute/route.go @@ -16,7 +16,7 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) var ( diff --git a/pkg/agtp/gatewayroute/route_test.go b/pkg/agtp/gatewayroute/route_test.go index 6cb408df..b9460972 100644 --- a/pkg/agtp/gatewayroute/route_test.go +++ b/pkg/agtp/gatewayroute/route_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) func TestValidateAcceptsRouteAssertionWithAgentHolderProof(t *testing.T) { diff --git a/pkg/agtp/jwt.go b/pkg/agtp/jwt.go index def67b5e..e6ba811d 100644 --- a/pkg/agtp/jwt.go +++ b/pkg/agtp/jwt.go @@ -21,8 +21,8 @@ import ( "strings" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" ) var ( diff --git a/pkg/agtp/jwt_test.go b/pkg/agtp/jwt_test.go index 96385cf0..f125c31c 100644 --- a/pkg/agtp/jwt_test.go +++ b/pkg/agtp/jwt_test.go @@ -28,9 +28,9 @@ import ( "testing" "time" + attestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/golang-jwt/jwt/v5" - attestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" diff --git a/pkg/atls/ea/authenticator.go b/pkg/atls/ea/authenticator.go index f9ce705e..8823d255 100644 --- a/pkg/atls/ea/authenticator.go +++ b/pkg/atls/ea/authenticator.go @@ -10,7 +10,7 @@ import ( "errors" "fmt" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" ) var ( diff --git a/pkg/atls/ea/authenticator_test.go b/pkg/atls/ea/authenticator_test.go index 7bf353d1..028f672b 100644 --- a/pkg/atls/ea/authenticator_test.go +++ b/pkg/atls/ea/authenticator_test.go @@ -17,7 +17,7 @@ import ( "testing" "time" - attestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" + attestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" ) func selfSignedCert(t *testing.T) tls.Certificate { diff --git a/pkg/atls/ea/session.go b/pkg/atls/ea/session.go index f380a4e4..1aeafe59 100644 --- a/pkg/atls/ea/session.go +++ b/pkg/atls/ea/session.go @@ -8,7 +8,7 @@ import ( "crypto/x509" "sync" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" ) type Session struct { diff --git a/pkg/atls/evidence_verifier.go b/pkg/atls/evidence_verifier.go index f07c65fc..37e9eea5 100644 --- a/pkg/atls/evidence_verifier.go +++ b/pkg/atls/evidence_verifier.go @@ -8,15 +8,15 @@ import ( "fmt" "os" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + asbattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/tdx" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-tpm-tools/proto/attest" "github.com/google/go-tpm/legacy/tpm2" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - asbattestation "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/azure" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/eat" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/tdx" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" "github.com/veraison/corim/corim" "golang.org/x/crypto/sha3" "google.golang.org/protobuf/proto" diff --git a/pkg/atls/evidence_verifier_test.go b/pkg/atls/evidence_verifier_test.go index e1024843..f1ac5f75 100644 --- a/pkg/atls/evidence_verifier_test.go +++ b/pkg/atls/evidence_verifier_test.go @@ -7,13 +7,13 @@ import ( "crypto/sha3" "testing" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + asbattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-tpm-tools/proto/attest" tpmpb "github.com/google/go-tpm-tools/proto/tpm" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - asbattestation "github.com/thinksyncs/agents-secure-binding/pkg/attestation" "google.golang.org/protobuf/proto" ) diff --git a/pkg/atls/identity_binding.go b/pkg/atls/identity_binding.go index c5616ccf..f1746e06 100644 --- a/pkg/atls/identity_binding.go +++ b/pkg/atls/identity_binding.go @@ -9,10 +9,10 @@ import ( "encoding/hex" "fmt" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - internaltransport "github.com/thinksyncs/agents-secure-binding/pkg/atls/internal_transport" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + internaltransport "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/internal_transport" ) // IdentityBindingFromConnectionState derives identity-policy binding values diff --git a/pkg/atls/identity_binding_test.go b/pkg/atls/identity_binding_test.go index a06a1066..6802f27d 100644 --- a/pkg/atls/identity_binding_test.go +++ b/pkg/atls/identity_binding_test.go @@ -15,7 +15,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" ) func TestIdentityBindingFromConnectionStateRejectsInvalidTLSState(t *testing.T) { diff --git a/pkg/atls/identitypolicy/session_binding_test.go b/pkg/atls/identitypolicy/session_binding_test.go index 6e5b0712..57069caa 100644 --- a/pkg/atls/identitypolicy/session_binding_test.go +++ b/pkg/atls/identitypolicy/session_binding_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" ) const testManagerKeyID = "manager-key-1" diff --git a/pkg/atls/internal_transport/conn.go b/pkg/atls/internal_transport/conn.go index 8d000dfb..48c7f15a 100644 --- a/pkg/atls/internal_transport/conn.go +++ b/pkg/atls/internal_transport/conn.go @@ -15,9 +15,9 @@ import ( "net" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) // ErrMissingObservedIdentity reports an enabled identity policy without a diff --git a/pkg/atls/internal_transport/conn_test.go b/pkg/atls/internal_transport/conn_test.go index ee4e1d35..8127d3e2 100644 --- a/pkg/atls/internal_transport/conn_test.go +++ b/pkg/atls/internal_transport/conn_test.go @@ -19,9 +19,9 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) const testIdentityBindingNonce = "identity-binding-nonce" diff --git a/pkg/atls/mocks/certificateprovider.go b/pkg/atls/mocks/certificateprovider.go index c94fc250..36fb3bb6 100644 --- a/pkg/atls/mocks/certificateprovider.go +++ b/pkg/atls/mocks/certificateprovider.go @@ -8,8 +8,8 @@ import ( "crypto/tls" "crypto/x509" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" ) type CertificateProvider struct { diff --git a/pkg/atls/provider.go b/pkg/atls/provider.go index 04a08738..13d9347c 100644 --- a/pkg/atls/provider.go +++ b/pkg/atls/provider.go @@ -11,10 +11,10 @@ import ( "crypto/x509" "fmt" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - asbattestation "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - attestationclient "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + asbattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + attestationclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/attestation" ) // CertificateProvider builds leaf extensions for accepted aTLS call sites. diff --git a/pkg/atls/transport.go b/pkg/atls/transport.go index dba225a5..7bfe5fc7 100644 --- a/pkg/atls/transport.go +++ b/pkg/atls/transport.go @@ -15,10 +15,10 @@ import ( "crypto/x509" "net" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - internaltransport "github.com/thinksyncs/agents-secure-binding/pkg/atls/internal_transport" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + internaltransport "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/internal_transport" ) type Conn = internaltransport.Conn diff --git a/pkg/attestation/azure/snp.go b/pkg/attestation/azure/snp.go index 2f4d08e5..6749c826 100644 --- a/pkg/attestation/azure/snp.go +++ b/pkg/attestation/azure/snp.go @@ -13,12 +13,12 @@ import ( "net/url" "strings" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" jose "github.com/go-jose/go-jose/v4" "github.com/golang-jwt/jwt/v5" "github.com/google/go-tpm-tools/proto/attest" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" "google.golang.org/protobuf/proto" diff --git a/pkg/attestation/azure/snp_test.go b/pkg/attestation/azure/snp_test.go index 2c2b34f2..49908387 100644 --- a/pkg/attestation/azure/snp_test.go +++ b/pkg/attestation/azure/snp_test.go @@ -13,12 +13,12 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" "github.com/golang-jwt/jwt/v5" "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-tpm-tools/proto/attest" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" "google.golang.org/protobuf/proto" diff --git a/pkg/attestation/ccaa/provider.go b/pkg/attestation/ccaa/provider.go index 1363ae38..98ad429c 100644 --- a/pkg/attestation/ccaa/provider.go +++ b/pkg/attestation/ccaa/provider.go @@ -7,9 +7,9 @@ import ( "fmt" "time" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - attestation_agent "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation-agent" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/netguard" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + attestation_agent "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation-agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/netguard" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) diff --git a/pkg/attestation/ccaa/provider_test.go b/pkg/attestation/ccaa/provider_test.go index 6e459894..7b7ecea3 100644 --- a/pkg/attestation/ccaa/provider_test.go +++ b/pkg/attestation/ccaa/provider_test.go @@ -7,9 +7,9 @@ import ( "errors" "testing" + attestation_agent "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation-agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation-agent/mocks" "github.com/stretchr/testify/mock" - attestation_agent "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation-agent" - "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation-agent/mocks" ) func TestNewProviderRejectsRemotePlaintextTCP(t *testing.T) { diff --git a/pkg/attestation/corimgen/README.md b/pkg/attestation/corimgen/README.md index a4f81b0a..dfb6bfef 100644 --- a/pkg/attestation/corimgen/README.md +++ b/pkg/attestation/corimgen/README.md @@ -21,7 +21,7 @@ The `corimgen` package generates CoRIM attestation policies for confidential com ### Basic Usage (Unsigned) ```go -import "github.com/thinksyncs/agents-secure-binding/pkg/attestation/corimgen" +import "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" opts := corimgen.Options{ Platform: "snp", @@ -41,7 +41,7 @@ import ( "crypto/elliptic" "crypto/rand" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/corimgen" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" ) // Generate signing key diff --git a/pkg/attestation/eat/eat.go b/pkg/attestation/eat/eat.go index 61ad2132..c22e1de3 100644 --- a/pkg/attestation/eat/eat.go +++ b/pkg/attestation/eat/eat.go @@ -6,7 +6,7 @@ package eat import ( "errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" ) // EATClaims represents the Entity Attestation Token claims following RFC 9711. diff --git a/pkg/attestation/eat/eat_test.go b/pkg/attestation/eat/eat_test.go index f663ef11..e595dc17 100644 --- a/pkg/attestation/eat/eat_test.go +++ b/pkg/attestation/eat/eat_test.go @@ -6,8 +6,8 @@ package eat import ( "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" ) func TestNewEATClaims(t *testing.T) { diff --git a/pkg/attestation/eat/extractor_test.go b/pkg/attestation/eat/extractor_test.go index 52dd44f4..60b278f6 100644 --- a/pkg/attestation/eat/extractor_test.go +++ b/pkg/attestation/eat/extractor_test.go @@ -8,9 +8,9 @@ import ( "fmt" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/google/go-sev-guest/abi" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" ) func TestExtractSNPClaims(t *testing.T) { diff --git a/pkg/attestation/eat/intuse_test.go b/pkg/attestation/eat/intuse_test.go index 43c8cc20..6d98f65b 100644 --- a/pkg/attestation/eat/intuse_test.go +++ b/pkg/attestation/eat/intuse_test.go @@ -6,8 +6,8 @@ package eat import ( "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" ) func TestIntUse(t *testing.T) { diff --git a/pkg/attestation/emptyprovider.go b/pkg/attestation/emptyprovider.go index e911f261..7735a985 100644 --- a/pkg/attestation/emptyprovider.go +++ b/pkg/attestation/emptyprovider.go @@ -6,7 +6,7 @@ package attestation import ( "fmt" - asb "github.com/thinksyncs/agents-secure-binding" + asb "github.com/ToppyMicroServices/agents-secure-binding/v2" ) var _ Provider = (*EmptyProvider)(nil) diff --git a/pkg/attestation/emptyprovider_test.go b/pkg/attestation/emptyprovider_test.go index dc94c020..7bd871bc 100644 --- a/pkg/attestation/emptyprovider_test.go +++ b/pkg/attestation/emptyprovider_test.go @@ -6,9 +6,9 @@ package attestation import ( "testing" + asb "github.com/ToppyMicroServices/agents-secure-binding/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - asb "github.com/thinksyncs/agents-secure-binding" ) func TestEmptyProvider_Attestation(t *testing.T) { diff --git a/pkg/attestation/generator/README.md b/pkg/attestation/generator/README.md index df5c981c..6612b701 100644 --- a/pkg/attestation/generator/README.md +++ b/pkg/attestation/generator/README.md @@ -18,7 +18,7 @@ This package consolidates CoRIM generation logic for SNP and TDX platforms, prov ### Basic Example ```go -import "github.com/thinksyncs/agents-secure-binding/pkg/attestation/generator" +import "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/generator" // Generate SNP CoRIM with defaults opts := generator.Options{ diff --git a/pkg/attestation/generator/generator.go b/pkg/attestation/generator/generator.go index 1e01f2de..96b98a71 100644 --- a/pkg/attestation/generator/generator.go +++ b/pkg/attestation/generator/generator.go @@ -5,7 +5,7 @@ package generator import ( "crypto" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/corimgen" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" ) // Legacy SNP Defaults (re-exported from corimgen). diff --git a/pkg/attestation/igvmmeasure/README.md b/pkg/attestation/igvmmeasure/README.md index 4d3e351e..9e6f7480 100644 --- a/pkg/attestation/igvmmeasure/README.md +++ b/pkg/attestation/igvmmeasure/README.md @@ -20,7 +20,7 @@ This package executes the `igvmmeasure` binary to compute cryptographic measurem ```go import ( "bytes" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/igvmmeasure" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/igvmmeasure" ) var stdout, stderr bytes.Buffer diff --git a/pkg/attestation/tdx/tdx.go b/pkg/attestation/tdx/tdx.go index 5357afb2..5d17965e 100644 --- a/pkg/attestation/tdx/tdx.go +++ b/pkg/attestation/tdx/tdx.go @@ -11,15 +11,15 @@ import ( "os" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" "github.com/google/go-tdx-guest/abi" "github.com/google/go-tdx-guest/client" "github.com/google/go-tdx-guest/proto/checkconfig" valdatetdx "github.com/google/go-tdx-guest/validate" verifytdx "github.com/google/go-tdx-guest/verify" trusttdx "github.com/google/go-tdx-guest/verify/trust" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/eat" "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" "google.golang.org/protobuf/encoding/protojson" diff --git a/pkg/attestation/tdx/tdx_coverage_test.go b/pkg/attestation/tdx/tdx_coverage_test.go index 047605ad..4e387346 100644 --- a/pkg/attestation/tdx/tdx_coverage_test.go +++ b/pkg/attestation/tdx/tdx_coverage_test.go @@ -11,9 +11,9 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/eat" ) func TestVerifyEAT_TDX(t *testing.T) { diff --git a/pkg/attestation/tdx/tdx_test.go b/pkg/attestation/tdx/tdx_test.go index 1cfb552d..60312642 100644 --- a/pkg/attestation/tdx/tdx_test.go +++ b/pkg/attestation/tdx/tdx_test.go @@ -9,10 +9,10 @@ import ( "runtime" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/google/go-tdx-guest/proto/checkconfig" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" "github.com/veraison/swid" diff --git a/pkg/attestation/vtpm/vtpm.go b/pkg/attestation/vtpm/vtpm.go index deb6d038..20938a2d 100644 --- a/pkg/attestation/vtpm/vtpm.go +++ b/pkg/attestation/vtpm/vtpm.go @@ -9,13 +9,13 @@ import ( "io" "os" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/proto/attest" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" "golang.org/x/crypto/sha3" diff --git a/pkg/authorityquorum/asbbinding/credential.go b/pkg/authorityquorum/asbbinding/credential.go index 50a43d4b..f588e052 100644 --- a/pkg/authorityquorum/asbbinding/credential.go +++ b/pkg/authorityquorum/asbbinding/credential.go @@ -14,7 +14,7 @@ import ( "fmt" "strings" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" ) const principalDigestDomain = "ASB-AUTHORITY-QUORUM-PRINCIPAL-v1" diff --git a/pkg/authorityquorum/asbbinding/profile.go b/pkg/authorityquorum/asbbinding/profile.go index 7ad2e7a6..943f5911 100644 --- a/pkg/authorityquorum/asbbinding/profile.go +++ b/pkg/authorityquorum/asbbinding/profile.go @@ -10,9 +10,9 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/authorityquorum" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/authorityquorum" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" ) var ( diff --git a/pkg/authorityquorum/asbbinding/profile_test.go b/pkg/authorityquorum/asbbinding/profile_test.go index 85d26e10..2a8a7144 100644 --- a/pkg/authorityquorum/asbbinding/profile_test.go +++ b/pkg/authorityquorum/asbbinding/profile_test.go @@ -11,9 +11,9 @@ import ( "time" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/authorityquorum" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/authorityquorum" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" ) const testAudience = "reveal.example" diff --git a/pkg/authorityquorum/asbbinding/tls.go b/pkg/authorityquorum/asbbinding/tls.go index 06609a2e..2f1ce433 100644 --- a/pkg/authorityquorum/asbbinding/tls.go +++ b/pkg/authorityquorum/asbbinding/tls.go @@ -13,9 +13,9 @@ import ( "fmt" "strings" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/authorityquorum" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/authorityquorum" ) var ErrTLSRequired = errors.New("authorityquorum asbbinding: verified TLS 1.3 client connection required") diff --git a/pkg/authorityquorum/asbbinding/tls_test.go b/pkg/authorityquorum/asbbinding/tls_test.go index cf22c6b4..68bed0d9 100644 --- a/pkg/authorityquorum/asbbinding/tls_test.go +++ b/pkg/authorityquorum/asbbinding/tls_test.go @@ -18,8 +18,8 @@ import ( "testing" "time" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/authorityquorum" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/authorityquorum" ) func TestBindingFromTLSDerivesVerifiedPeerBinding(t *testing.T) { diff --git a/pkg/clients/agtp_tls_exporter_test.go b/pkg/clients/agtp_tls_exporter_test.go index 964e7c7e..c97d2252 100644 --- a/pkg/clients/agtp_tls_exporter_test.go +++ b/pkg/clients/agtp_tls_exporter_test.go @@ -7,8 +7,8 @@ import ( "errors" "testing" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) func TestAGTPObservedIdentityRejectsMockedTLSExporterWithRealState(t *testing.T) { diff --git a/pkg/clients/clients.go b/pkg/clients/clients.go index d7b935b3..5470c773 100644 --- a/pkg/clients/clients.go +++ b/pkg/clients/clients.go @@ -11,9 +11,9 @@ import ( "log/slog" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) var ( diff --git a/pkg/clients/clients_test.go b/pkg/clients/clients_test.go index 96aa535b..8c2c32bc 100644 --- a/pkg/clients/clients_test.go +++ b/pkg/clients/clients_test.go @@ -28,11 +28,11 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + attestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/ea" - attestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" ) func TestAGTPObservedIdentityAcceptsSessionBoundJWT(t *testing.T) { diff --git a/pkg/clients/grpc/agent/agent.go b/pkg/clients/grpc/agent/agent.go index 98f0ae84..ed8ce230 100644 --- a/pkg/clients/grpc/agent/agent.go +++ b/pkg/clients/grpc/agent/agent.go @@ -5,11 +5,11 @@ package agent import ( "context" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc" - "github.com/thinksyncs/agents-secure-binding/pkg/tls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/tls" grpchealth "google.golang.org/grpc/health/grpc_health_v1" ) diff --git a/pkg/clients/grpc/agent/agent_test.go b/pkg/clients/grpc/agent/agent_test.go index 762f8ad2..ca747dfc 100644 --- a/pkg/clients/grpc/agent/agent_test.go +++ b/pkg/clients/grpc/agent/agent_test.go @@ -9,13 +9,13 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + agentgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent" - agentgrpc "github.com/thinksyncs/agents-secure-binding/agent/api/grpc" - "github.com/thinksyncs/agents-secure-binding/agent/mocks" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" "google.golang.org/grpc" "google.golang.org/grpc/health" grpchealth "google.golang.org/grpc/health/grpc_health_v1" diff --git a/pkg/clients/grpc/attestation-agent/client.go b/pkg/clients/grpc/attestation-agent/client.go index a0183d84..9feca9bf 100644 --- a/pkg/clients/grpc/attestation-agent/client.go +++ b/pkg/clients/grpc/attestation-agent/client.go @@ -8,9 +8,9 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - aa "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation-agent" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/netguard" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + aa "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation-agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/netguard" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) diff --git a/pkg/clients/grpc/attestation-agent/client_test.go b/pkg/clients/grpc/attestation-agent/client_test.go index 3e77ca0f..e7cf8b4e 100644 --- a/pkg/clients/grpc/attestation-agent/client_test.go +++ b/pkg/clients/grpc/attestation-agent/client_test.go @@ -12,9 +12,9 @@ import ( "testing" "time" + aa "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation-agent" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - aa "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation-agent" "google.golang.org/grpc" ) diff --git a/pkg/clients/grpc/attestation/client.go b/pkg/clients/grpc/attestation/client.go index d7a18f3d..30fdc57f 100644 --- a/pkg/clients/grpc/attestation/client.go +++ b/pkg/clients/grpc/attestation/client.go @@ -7,8 +7,8 @@ import ( "fmt" "time" - attestation_v1 "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation/v1" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" + attestation_v1 "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation/v1" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) diff --git a/pkg/clients/grpc/attestation/client_test.go b/pkg/clients/grpc/attestation/client_test.go index b66c45b6..4277c487 100644 --- a/pkg/clients/grpc/attestation/client_test.go +++ b/pkg/clients/grpc/attestation/client_test.go @@ -10,10 +10,10 @@ import ( "testing" "time" + attestation_v1 "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/proto/attestation/v1" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - attestation_v1 "github.com/thinksyncs/agents-secure-binding/internal/proto/attestation/v1" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" "google.golang.org/grpc" ) diff --git a/pkg/clients/grpc/connect_test.go b/pkg/clients/grpc/connect_test.go index 5aa9711f..9db1e5de 100644 --- a/pkg/clients/grpc/connect_test.go +++ b/pkg/clients/grpc/connect_test.go @@ -18,12 +18,12 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/tls" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/tls" ) func TestNewClient(t *testing.T) { diff --git a/pkg/clients/grpc/cvm/cvm.go b/pkg/clients/grpc/cvm/cvm.go index cd3a1662..fb16e457 100644 --- a/pkg/clients/grpc/cvm/cvm.go +++ b/pkg/clients/grpc/cvm/cvm.go @@ -3,9 +3,9 @@ package cvm import ( - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" ) // NewManagerClient creates new manager gRPC client instance. diff --git a/pkg/clients/grpc/cvm/cvm_test.go b/pkg/clients/grpc/cvm/cvm_test.go index 461427b9..cd1d3dae 100644 --- a/pkg/clients/grpc/cvm/cvm_test.go +++ b/pkg/clients/grpc/cvm/cvm_test.go @@ -8,13 +8,13 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + agentgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent" - agentgrpc "github.com/thinksyncs/agents-secure-binding/agent/api/grpc" - "github.com/thinksyncs/agents-secure-binding/agent/mocks" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" "google.golang.org/grpc" "google.golang.org/grpc/health" grpchealth "google.golang.org/grpc/health/grpc_health_v1" diff --git a/pkg/clients/grpc/grpc.go b/pkg/clients/grpc/grpc.go index 4d08688a..1306aa70 100644 --- a/pkg/clients/grpc/grpc.go +++ b/pkg/clients/grpc/grpc.go @@ -9,11 +9,11 @@ import ( "net" "strings" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/internal/runtime/netguard" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/tls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/netguard" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/tls" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" diff --git a/pkg/clients/grpc/log/client.go b/pkg/clients/grpc/log/client.go index 2ad18174..4d24ecbf 100644 --- a/pkg/clients/grpc/log/client.go +++ b/pkg/clients/grpc/log/client.go @@ -6,7 +6,7 @@ import ( "context" "time" - "github.com/thinksyncs/agents-secure-binding/agent/log" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/types/known/timestamppb" diff --git a/pkg/clients/grpc/log/client_test.go b/pkg/clients/grpc/log/client_test.go index b857b5a7..e3df9e19 100644 --- a/pkg/clients/grpc/log/client_test.go +++ b/pkg/clients/grpc/log/client_test.go @@ -11,9 +11,9 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent/log" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" diff --git a/pkg/clients/grpc/manager/manager.go b/pkg/clients/grpc/manager/manager.go index 8fc2f15f..f7bd07d6 100644 --- a/pkg/clients/grpc/manager/manager.go +++ b/pkg/clients/grpc/manager/manager.go @@ -3,9 +3,9 @@ package manager import ( - "github.com/thinksyncs/agents-secure-binding/manager" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/clients/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/manager" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" ) // NewManagerClient creates new manager gRPC client instance. diff --git a/pkg/clients/grpc/manager/manager_test.go b/pkg/clients/grpc/manager/manager_test.go index 3c584a28..fc32da40 100644 --- a/pkg/clients/grpc/manager/manager_test.go +++ b/pkg/clients/grpc/manager/manager_test.go @@ -5,9 +5,9 @@ package manager import ( "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" ) func TestNewManagerClient(t *testing.T) { diff --git a/pkg/clients/grpc/runner/client.go b/pkg/clients/grpc/runner/client.go index f77310cf..bb5a320f 100644 --- a/pkg/clients/grpc/runner/client.go +++ b/pkg/clients/grpc/runner/client.go @@ -6,7 +6,7 @@ import ( "context" "time" - pb "github.com/thinksyncs/agents-secure-binding/agent/runner" + pb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/types/known/emptypb" diff --git a/pkg/clients/grpc/runner/client_test.go b/pkg/clients/grpc/runner/client_test.go index f5a86cc4..9ef6718b 100644 --- a/pkg/clients/grpc/runner/client_test.go +++ b/pkg/clients/grpc/runner/client_test.go @@ -10,9 +10,9 @@ import ( "testing" "time" + pb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - pb "github.com/thinksyncs/agents-secure-binding/agent/runner" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" ) diff --git a/pkg/clients/grpc/runner/mocks/client.go b/pkg/clients/grpc/runner/mocks/client.go index b6f38544..0ea2cb4e 100644 --- a/pkg/clients/grpc/runner/mocks/client.go +++ b/pkg/clients/grpc/runner/mocks/client.go @@ -10,8 +10,8 @@ package mocks import ( "context" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/runner" mock "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent/runner" "google.golang.org/protobuf/types/known/emptypb" ) diff --git a/pkg/clients/http/client.go b/pkg/clients/http/client.go index 1d433a7e..67cc8beb 100644 --- a/pkg/clients/http/client.go +++ b/pkg/clients/http/client.go @@ -11,9 +11,9 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/tls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/tls" ) type Client interface { diff --git a/pkg/clients/http/client_test.go b/pkg/clients/http/client_test.go index 5d914d6f..88506aeb 100644 --- a/pkg/clients/http/client_test.go +++ b/pkg/clients/http/client_test.go @@ -18,10 +18,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/tls" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/tls" ) func TestConfig_Configuration(t *testing.T) { diff --git a/pkg/clients/identity_jwt.go b/pkg/clients/identity_jwt.go index 0d86876d..e8a6b094 100644 --- a/pkg/clients/identity_jwt.go +++ b/pkg/clients/identity_jwt.go @@ -16,8 +16,8 @@ import ( "unicode" "unicode/utf8" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" ) var ( diff --git a/pkg/clients/identity_jwt_v2_test.go b/pkg/clients/identity_jwt_v2_test.go index 6bf1b5ee..02535f3a 100644 --- a/pkg/clients/identity_jwt_v2_test.go +++ b/pkg/clients/identity_jwt_v2_test.go @@ -12,8 +12,8 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" ) const ( diff --git a/pkg/crypto/decrypt.go b/pkg/crypto/decrypt.go index 3e794c1d..8025b439 100644 --- a/pkg/crypto/decrypt.go +++ b/pkg/crypto/decrypt.go @@ -11,7 +11,7 @@ import ( "encoding/base64" "encoding/json" - "github.com/thinksyncs/agents-secure-binding/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "golang.org/x/crypto/hkdf" ) diff --git a/pkg/ingress/adapter.go b/pkg/ingress/adapter.go index 29972abc..466918b2 100644 --- a/pkg/ingress/adapter.go +++ b/pkg/ingress/adapter.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 package ingress -import "github.com/thinksyncs/agents-secure-binding/agent" +import "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" // AgentConfigToProxyConfig converts agent.AgentConfig to ProxyConfig. func AgentConfigToProxyConfig(cfg agent.AgentConfig) ProxyConfig { diff --git a/pkg/ingress/adapter_test.go b/pkg/ingress/adapter_test.go index db0645d5..6aadaa65 100644 --- a/pkg/ingress/adapter_test.go +++ b/pkg/ingress/adapter_test.go @@ -5,8 +5,8 @@ package ingress import ( "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" "github.com/stretchr/testify/assert" - "github.com/thinksyncs/agents-secure-binding/agent" ) // TestAgentConfigToProxyConfig tests conversion from AgentConfig to ProxyConfig. diff --git a/pkg/ingress/proxy.go b/pkg/ingress/proxy.go index 241d52f5..4c8fa4b1 100644 --- a/pkg/ingress/proxy.go +++ b/pkg/ingress/proxy.go @@ -15,7 +15,7 @@ import ( "os" "sync" - "github.com/thinksyncs/agents-secure-binding/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" ) diff --git a/pkg/ingress/proxy_test.go b/pkg/ingress/proxy_test.go index 35b15263..42c1d680 100644 --- a/pkg/ingress/proxy_test.go +++ b/pkg/ingress/proxy_test.go @@ -23,9 +23,9 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/mocks" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" ) diff --git a/pkg/production/binding.go b/pkg/production/binding.go index 9b78c115..4548bee6 100644 --- a/pkg/production/binding.go +++ b/pkg/production/binding.go @@ -12,8 +12,8 @@ import ( "errors" "strings" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) var ErrInvalidAcceptedBinding = errors.New("production: invalid accepted binding input") diff --git a/pkg/production/profile.go b/pkg/production/profile.go index 6ad4507f..6588aac6 100644 --- a/pkg/production/profile.go +++ b/pkg/production/profile.go @@ -15,8 +15,8 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" ) var ( diff --git a/pkg/production/profile_test.go b/pkg/production/profile_test.go index 97aa7d1b..bbb7a2a3 100644 --- a/pkg/production/profile_test.go +++ b/pkg/production/profile_test.go @@ -14,9 +14,9 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" ) const ( diff --git a/pkg/production/redis_acceptance.go b/pkg/production/redis_acceptance.go index b867130c..60552d04 100644 --- a/pkg/production/redis_acceptance.go +++ b/pkg/production/redis_acceptance.go @@ -16,7 +16,7 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/operationjournal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/operationjournal" ) const ( diff --git a/pkg/production/redis_acceptance_test.go b/pkg/production/redis_acceptance_test.go index eb0be9ad..7fcd446f 100644 --- a/pkg/production/redis_acceptance_test.go +++ b/pkg/production/redis_acceptance_test.go @@ -25,7 +25,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/operationjournal" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/operationjournal" ) func TestRedisAcceptanceStoreCoordinatesReplicasAndResultsOverTLS(t *testing.T) { diff --git a/pkg/production/software_only.go b/pkg/production/software_only.go index ea2bb5ed..c3ff4f72 100644 --- a/pkg/production/software_only.go +++ b/pkg/production/software_only.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) var ErrUnexpectedAttestationBinding = errors.New("production: software-only profile forbids attestation binding") diff --git a/pkg/production/software_only_test.go b/pkg/production/software_only_test.go index 93428dfd..365f1ec1 100644 --- a/pkg/production/software_only_test.go +++ b/pkg/production/software_only_test.go @@ -9,9 +9,9 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" ) type softwareOnlyFixture struct { diff --git a/pkg/progressbar/progress_test.go b/pkg/progressbar/progress_test.go index 07936ba1..16f46b76 100644 --- a/pkg/progressbar/progress_test.go +++ b/pkg/progressbar/progress_test.go @@ -10,11 +10,11 @@ import ( "strings" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/mocks" - "github.com/thinksyncs/agents-secure-binding/internal/errors" ) func TestNew(t *testing.T) { diff --git a/pkg/progressbar/progressbar.go b/pkg/progressbar/progressbar.go index bddf3851..53766405 100644 --- a/pkg/progressbar/progressbar.go +++ b/pkg/progressbar/progressbar.go @@ -8,9 +8,9 @@ import ( "os" "strings" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" "github.com/fatih/color" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" "golang.org/x/term" ) diff --git a/pkg/sdk/agent.go b/pkg/sdk/agent.go index fc4f7d76..6afd7ca8 100644 --- a/pkg/sdk/agent.go +++ b/pkg/sdk/agent.go @@ -16,11 +16,11 @@ import ( "strconv" "time" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/agent/api/grpc" - "github.com/thinksyncs/agents-secure-binding/agent/auth" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/progressbar" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/auth" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/progressbar" "google.golang.org/grpc/metadata" ) diff --git a/pkg/sdk/agent_test.go b/pkg/sdk/agent_test.go index 01a91c85..a11f8c50 100644 --- a/pkg/sdk/agent_test.go +++ b/pkg/sdk/agent_test.go @@ -14,13 +14,13 @@ import ( "os" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/sdk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/thinksyncs/agents-secure-binding/agent" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation/vtpm" - "github.com/thinksyncs/agents-secure-binding/pkg/sdk" "golang.org/x/crypto/sha3" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" diff --git a/pkg/sdk/setup_test.go b/pkg/sdk/setup_test.go index bd5087f1..098c85ea 100644 --- a/pkg/sdk/setup_test.go +++ b/pkg/sdk/setup_test.go @@ -10,9 +10,9 @@ import ( "os" "testing" - "github.com/thinksyncs/agents-secure-binding/agent" - agentgrpc "github.com/thinksyncs/agents-secure-binding/agent/api/grpc" - "github.com/thinksyncs/agents-secure-binding/agent/mocks" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + agentgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/mocks" "google.golang.org/grpc" "google.golang.org/grpc/test/bufconn" ) diff --git a/pkg/taskcoord/asbbinding/canonical.go b/pkg/taskcoord/asbbinding/canonical.go index befe4545..77528df8 100644 --- a/pkg/taskcoord/asbbinding/canonical.go +++ b/pkg/taskcoord/asbbinding/canonical.go @@ -16,7 +16,7 @@ import ( "unicode" "unicode/utf8" - "github.com/thinksyncs/agents-secure-binding/pkg/taskcoord" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/taskcoord" ) const ( diff --git a/pkg/taskcoord/asbbinding/canonical_test.go b/pkg/taskcoord/asbbinding/canonical_test.go index f844c258..66bf173c 100644 --- a/pkg/taskcoord/asbbinding/canonical_test.go +++ b/pkg/taskcoord/asbbinding/canonical_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/taskcoord" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/taskcoord" ) const ( diff --git a/pkg/taskcoord/asbbinding/ingress.go b/pkg/taskcoord/asbbinding/ingress.go index d6a9618b..e980fd74 100644 --- a/pkg/taskcoord/asbbinding/ingress.go +++ b/pkg/taskcoord/asbbinding/ingress.go @@ -21,11 +21,11 @@ import ( "time" "unicode/utf8" - eaattestation "github.com/thinksyncs/agents-secure-binding/pkg/atls/eaattestation" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/taskcoord" - "github.com/thinksyncs/agents-secure-binding/schemas" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/taskcoord" + "github.com/ToppyMicroServices/agents-secure-binding/v2/schemas" ) const ( diff --git a/pkg/taskcoord/asbbinding/ingress_test.go b/pkg/taskcoord/asbbinding/ingress_test.go index 5a4bbf3f..4ee4f8d7 100644 --- a/pkg/taskcoord/asbbinding/ingress_test.go +++ b/pkg/taskcoord/asbbinding/ingress_test.go @@ -23,10 +23,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/taskcoord" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/taskcoord" ) const ( diff --git a/pkg/taskcoord/asbbinding/profile.go b/pkg/taskcoord/asbbinding/profile.go index b14e2a52..4640da11 100644 --- a/pkg/taskcoord/asbbinding/profile.go +++ b/pkg/taskcoord/asbbinding/profile.go @@ -10,9 +10,9 @@ import ( "strings" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/taskcoord" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/taskcoord" ) var ( diff --git a/pkg/taskcoord/asbbinding/profile_test.go b/pkg/taskcoord/asbbinding/profile_test.go index 04e515e4..49c4fb53 100644 --- a/pkg/taskcoord/asbbinding/profile_test.go +++ b/pkg/taskcoord/asbbinding/profile_test.go @@ -11,10 +11,10 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/taskcoord" "github.com/golang-jwt/jwt/v5" - "github.com/thinksyncs/agents-secure-binding/pkg/atls/identitypolicy" - "github.com/thinksyncs/agents-secure-binding/pkg/clients" - "github.com/thinksyncs/agents-secure-binding/pkg/taskcoord" ) const ( diff --git a/pkg/tls/tls.go b/pkg/tls/tls.go index 9a1a3c4b..4a5471e6 100644 --- a/pkg/tls/tls.go +++ b/pkg/tls/tls.go @@ -8,8 +8,8 @@ import ( "crypto/x509" "os" - "github.com/thinksyncs/agents-secure-binding/internal/errors" - "github.com/thinksyncs/agents-secure-binding/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" ) // Security represents the type of TLS security configuration. diff --git a/schemas/a2a_security_test_report_validator_test.go b/schemas/a2a_security_test_report_validator_test.go index 8407b3a0..c7fd9db0 100644 --- a/schemas/a2a_security_test_report_validator_test.go +++ b/schemas/a2a_security_test_report_validator_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/thinksyncs/agents-secure-binding/pkg/a2asecuritytest" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/a2asecuritytest" ) func TestA2ASecurityTestReportSchemaAcceptsReport(t *testing.T) { diff --git a/schemas/task-participant-v1.schema.json b/schemas/task-participant-v1.schema.json index 725231b2..e9ecaf37 100644 --- a/schemas/task-participant-v1.schema.json +++ b/schemas/task-participant-v1.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/thinksyncs/agents-secure-binding/schemas/task-participant-v1.schema.json", + "$id": "https://github.com/ToppyMicroServices/agents-secure-binding/schemas/task-participant-v1.schema.json", "title": "ASB Task Participant v1 documents", "oneOf": [ { "$ref": "#/$defs/participant" }, diff --git a/schemas/task_participant_schema_test.go b/schemas/task_participant_schema_test.go index f0e18e00..4c23c793 100644 --- a/schemas/task_participant_schema_test.go +++ b/schemas/task_participant_schema_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/taskcoord" "github.com/santhosh-tekuri/jsonschema/v6" - "github.com/thinksyncs/agents-secure-binding/pkg/taskcoord" ) const taskParticipantSchemaFile = "task-participant-v1.schema.json" diff --git a/test/cvms/main.go b/test/cvms/main.go index de8899d0..a7e72472 100644 --- a/test/cvms/main.go +++ b/test/cvms/main.go @@ -14,13 +14,13 @@ import ( "strconv" "strings" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + cvmsgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/internal" + mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" + smqserver "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/server" + grpcserver "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/server/grpc" "github.com/caarlos0/env/v11" - "github.com/thinksyncs/agents-secure-binding/agent/cvms" - cvmsgrpc "github.com/thinksyncs/agents-secure-binding/agent/cvms/api/grpc" - "github.com/thinksyncs/agents-secure-binding/internal" - mglog "github.com/thinksyncs/agents-secure-binding/internal/runtime/logging" - smqserver "github.com/thinksyncs/agents-secure-binding/internal/runtime/server" - grpcserver "github.com/thinksyncs/agents-secure-binding/internal/runtime/server/grpc" "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/credentials" From 286acbb80c60ed5a634676707f44adaae0a4b6c0 Mon Sep 17 00:00:00 2001 From: thinksyncs Date: Sun, 30 Aug 2026 13:09:31 +0900 Subject: [PATCH 2/8] feat(attestation): add isolated SNP and TDX modules --- modules/attestation/snp/README.md | 24 ++ modules/attestation/snp/go.mod | 16 + modules/attestation/snp/go.sum | 27 ++ modules/attestation/snp/verifier.go | 386 +++++++++++++++++++++++ modules/attestation/snp/verifier_test.go | 301 ++++++++++++++++++ modules/attestation/tdx/README.md | 31 ++ modules/attestation/tdx/go.mod | 15 + modules/attestation/tdx/go.sum | 23 ++ modules/attestation/tdx/verifier.go | 235 ++++++++++++++ modules/attestation/tdx/verifier_test.go | 160 ++++++++++ 10 files changed, 1218 insertions(+) create mode 100644 modules/attestation/snp/README.md create mode 100644 modules/attestation/snp/go.mod create mode 100644 modules/attestation/snp/go.sum create mode 100644 modules/attestation/snp/verifier.go create mode 100644 modules/attestation/snp/verifier_test.go create mode 100644 modules/attestation/tdx/README.md create mode 100644 modules/attestation/tdx/go.mod create mode 100644 modules/attestation/tdx/go.sum create mode 100644 modules/attestation/tdx/verifier.go create mode 100644 modules/attestation/tdx/verifier_test.go diff --git a/modules/attestation/snp/README.md b/modules/attestation/snp/README.md new file mode 100644 index 00000000..5a7eb28a --- /dev/null +++ b/modules/attestation/snp/README.md @@ -0,0 +1,24 @@ +# Direct SEV-SNP attestation module + +Status: experimental `v0.x`. The deterministic cryptographic tests pass, but +this module has not been qualified on live SNP hardware and is not +production-ready. + +This module accepts either a `sevsnp.Attestation` protobuf or an AMD ABI report +with its certificate table. `Verify` checks the certificate chain, report +signature, CRL, local SNP policy, debug state, VMPL, and the caller-supplied +`REPORT_DATA`. Its default network getter is bounded and restricted to AMD KDS. + +It does not implement ASB, TLS, EAT, or CoRIM. Those concerns stay in the root +composition layer. + +Run the hardware-independent checks with: + +```sh +GOWORK=off go mod verify +GOWORK=off go test -race ./... +GOWORK=off go vet ./... +``` + +Live qualification must use the exact target image, launch policy, AMD product, +endorsement path, and collateral environment. diff --git a/modules/attestation/snp/go.mod b/modules/attestation/snp/go.mod new file mode 100644 index 00000000..8382e9f9 --- /dev/null +++ b/modules/attestation/snp/go.mod @@ -0,0 +1,16 @@ +module github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/snp + +go 1.26.0 + +require ( + github.com/google/go-sev-guest v0.14.1 + google.golang.org/protobuf v1.36.11 +) + +require ( + github.com/google/logger v1.1.1 // indirect + github.com/google/uuid v1.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sys v0.15.0 // indirect +) diff --git a/modules/attestation/snp/go.sum b/modules/attestation/snp/go.sum new file mode 100644 index 00000000..12615fcf --- /dev/null +++ b/modules/attestation/snp/go.sum @@ -0,0 +1,27 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98= +github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-sev-guest v0.14.1 h1:j/DXy9jk1qSW/dEV9vDiQnhAVFD1zqnWNVu6p1J0Jgo= +github.com/google/go-sev-guest v0.14.1/go.mod h1:SK9vW+uyfuzYdVN0m8BShL3OQCtXZe/JPF7ZkpD3760= +github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= +github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/modules/attestation/snp/verifier.go b/modules/attestation/snp/verifier.go new file mode 100644 index 00000000..999ec751 --- /dev/null +++ b/modules/attestation/snp/verifier.go @@ -0,0 +1,386 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +// Package snp parses and verifies direct AMD SEV-SNP evidence independently +// from the ASB protocol implementation. ASB supplies the expected session +// REPORT_DATA and treats the returned error as the module appraisal result. +package snp + +import ( + "context" + "crypto/subtle" + "crypto/x509" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/google/go-sev-guest/abi" + "github.com/google/go-sev-guest/proto/sevsnp" + "github.com/google/go-sev-guest/validate" + "github.com/google/go-sev-guest/verify" + "github.com/google/go-sev-guest/verify/trust" + "google.golang.org/protobuf/proto" +) + +const ( + amdKDSHost = "kdsintf.amd.com" + defaultCollateralTimeout = 2 * time.Minute + defaultMaxRetryDelay = 30 * time.Second + defaultHTTPTimeout = 20 * time.Second + maxCollateralBytes = 16 << 20 +) + +var ( + // ErrInvalidEvidence means the input was neither a structurally valid + // sevsnp.Attestation protobuf nor an AMD ABI report/certificate table. + ErrInvalidEvidence = errors.New("snp module: invalid direct SNP evidence") + // ErrInvalidConfig means the verifier or validator does not satisfy the + // strict verification baseline enforced by this module. + ErrInvalidConfig = errors.New("snp module: strict verification configuration is required") + // ErrBinding means REPORT_DATA is absent, malformed, or does not match the + // ASB session binding supplied by the caller. + ErrBinding = errors.New("snp module: REPORT_DATA does not match ASB session binding") + // ErrVerification means certificate, signature, or revocation verification + // failed. + ErrVerification = errors.New("snp module: certificate, signature, or revocation verification failed") + // ErrValidation means the authenticated report did not satisfy local SNP + // policy. + ErrValidation = errors.New("snp module: report policy validation failed") +) + +// Parse accepts the two direct SNP representations used at the module +// boundary: a sevsnp.Attestation protobuf, or an AMD ABI attestation report +// optionally followed by its certificate table. Parsing is structural only; +// callers must use Verify before trusting any field. +func Parse(evidence []byte) (*sevsnp.Attestation, error) { + if len(evidence) == 0 { + return nil, fmt.Errorf("%w: evidence is empty", ErrInvalidEvidence) + } + + if len(evidence) >= abi.ReportSize { + if err := abi.ValidateReportFormat(evidence[:abi.ReportSize]); err == nil { + parsed, err := abi.ReportCertsToProto(evidence) + if err != nil { + return nil, fmt.Errorf("%w: malformed ABI certificate table: %v", ErrInvalidEvidence, err) + } + return parsed, nil + } + } + + var attestation sevsnp.Attestation + if err := proto.Unmarshal(evidence, &attestation); err != nil { + return nil, fmt.Errorf("%w: failed to parse ABI report or protobuf: %v", ErrInvalidEvidence, err) + } + if attestation.GetReport() == nil { + return nil, fmt.Errorf("%w: protobuf is missing its report", ErrInvalidEvidence) + } + if err := validateReportShape(attestation.GetReport()); err != nil { + return nil, fmt.Errorf("%w: malformed protobuf report: %v", ErrInvalidEvidence, err) + } + return &attestation, nil +} + +// NewKDSGetter returns a context-aware, bounded AMD KDS getter. It permits +// only HTTPS requests to AMD's KDS host. Deployments using a reviewed local +// collateral service can provide their own getter instead. +func NewKDSGetter() trust.HTTPSGetter { + client := &http.Client{ + Timeout: defaultHTTPTimeout, + CheckRedirect: func(request *http.Request, _ []*http.Request) error { + if err := validateKDSURL(request.URL); err != nil { + return fmt.Errorf("SNP collateral redirect rejected: %w", err) + } + return nil + }, + } + return &trust.RetryHTTPSGetter{ + Timeout: defaultCollateralTimeout, + MaxRetryDelay: defaultMaxRetryDelay, + Getter: &boundedKDSGetter{client: client}, + } +} + +type boundedKDSGetter struct { + client *http.Client +} + +func (g *boundedKDSGetter) Get(rawURL string) ([]byte, error) { + return g.GetContext(context.Background(), rawURL) +} + +func (g *boundedKDSGetter) GetContext(ctx context.Context, rawURL string) ([]byte, error) { + parsed, err := url.Parse(rawURL) + if err != nil { + return nil, fmt.Errorf("invalid SNP collateral URL: %w", err) + } + if err := validateKDSURL(parsed); err != nil { + return nil, err + } + request, err := http.NewRequestWithContext(ctx, http.MethodGet, parsed.String(), nil) + if err != nil { + return nil, err + } + response, err := g.client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + if response.StatusCode >= http.StatusMultipleChoices { + return nil, fmt.Errorf("failed to retrieve %s, status code received %d", parsed.Redacted(), response.StatusCode) + } + body, err := io.ReadAll(io.LimitReader(response.Body, maxCollateralBytes+1)) + if err != nil { + return nil, err + } + if len(body) > maxCollateralBytes { + return nil, fmt.Errorf("SNP collateral response exceeds %d bytes", maxCollateralBytes) + } + return body, nil +} + +func validateKDSURL(parsed *url.URL) error { + if parsed == nil || parsed.Scheme != "https" || parsed.Host == "" { + return fmt.Errorf("SNP collateral URL must use HTTPS") + } + if !strings.EqualFold(parsed.Hostname(), amdKDSHost) { + return fmt.Errorf("SNP collateral URL host must be %s", amdKDSHost) + } + if port := parsed.Port(); port != "" && port != "443" { + return fmt.Errorf("SNP collateral URL port must be 443") + } + return nil +} + +// ValidateConfig checks the module's non-negotiable verification baseline. Revocation +// checking, an explicit collateral getter and product, disabled guest debug, +// and an exact VMPL are required. +func ValidateConfig(verification *verify.Options, validation *validate.Options) error { + if verification == nil { + return fmt.Errorf("%w: verification options are nil", ErrInvalidConfig) + } + if !verification.CheckRevocations { + return fmt.Errorf("%w: CheckRevocations must be true", ErrInvalidConfig) + } + if verification.Getter == nil { + return fmt.Errorf("%w: an explicit collateral getter is required", ErrInvalidConfig) + } + if verification.Product == nil || !knownProduct(verification.Product.GetName()) { + return fmt.Errorf("%w: an explicit AMD product is required", ErrInvalidConfig) + } + for product, roots := range verification.TrustedRoots { + for i, root := range roots { + if root == nil || root.ProductCerts == nil || root.ProductCerts.Ark == nil || root.ProductCerts.Ask == nil { + return fmt.Errorf("%w: trusted root %q[%d] must contain ARK and ASK certificates", ErrInvalidConfig, product, i) + } + } + } + if validation == nil { + return fmt.Errorf("%w: validation options are nil", ErrInvalidConfig) + } + if validation.GuestPolicy.Debug { + return fmt.Errorf("%w: guest debug mode must be disabled", ErrInvalidConfig) + } + if validation.VMPL == nil { + return fmt.Errorf("%w: an exact VMPL is required", ErrInvalidConfig) + } + if *validation.VMPL < 0 || *validation.VMPL > 3 { + return fmt.Errorf("%w: VMPL must be in the range 0-3", ErrInvalidConfig) + } + if err := validateOptionLengths(validation); err != nil { + return fmt.Errorf("%w: %v", ErrInvalidConfig, err) + } + for guid, option := range validation.CertTableOptions { + if option == nil || option.Validate == nil { + return fmt.Errorf("%w: certificate-table option %q requires a validator", ErrInvalidConfig, guid) + } + } + return nil +} + +// Verify parses evidence, authenticates the report and certificate chain, +// checks revocation, forces the caller's expected REPORT_DATA into a cloned +// validation policy, and validates all report fields. Caller-owned options are +// not mutated. +func Verify( + ctx context.Context, + evidence []byte, + expectedReportData []byte, + verification *verify.Options, + validation *validate.Options, +) (*sevsnp.Attestation, error) { + if ctx == nil { + return nil, fmt.Errorf("%w: context is required", ErrInvalidConfig) + } + if err := ValidateConfig(verification, validation); err != nil { + return nil, err + } + if len(expectedReportData) != abi.ReportDataSize { + return nil, fmt.Errorf("%w: expected %d bytes, got %d", ErrBinding, abi.ReportDataSize, len(expectedReportData)) + } + if err := ctx.Err(); err != nil { + return nil, fmt.Errorf("%w: %v", ErrVerification, err) + } + + attestation, err := Parse(evidence) + if err != nil { + return nil, err + } + verificationCopy := cloneVerificationOptions(verification) + validationCopy := cloneValidationOptions(validation) + validationCopy.ReportData = append([]byte(nil), expectedReportData...) + + if err := verify.SnpAttestationContext(ctx, attestation, verificationCopy); err != nil { + return nil, fmt.Errorf("%w: %v", ErrVerification, err) + } + if err := ctx.Err(); err != nil { + return nil, fmt.Errorf("%w: %v", ErrVerification, err) + } + if subtle.ConstantTimeCompare(attestation.GetReport().GetReportData(), expectedReportData) != 1 { + return nil, ErrBinding + } + if err := validate.SnpAttestation(attestation, validationCopy); err != nil { + return nil, fmt.Errorf("%w: %v", ErrValidation, err) + } + return attestation, nil +} + +func validateReportShape(report *sevsnp.Report) error { + raw, err := abi.ReportToAbiBytes(report) + if err != nil { + return err + } + return abi.ValidateReportFormat(raw) +} + +func knownProduct(product sevsnp.SevProduct_SevProductName) bool { + switch product { + case sevsnp.SevProduct_SEV_PRODUCT_MILAN, + sevsnp.SevProduct_SEV_PRODUCT_GENOA, + sevsnp.SevProduct_SEV_PRODUCT_TURIN: + return true + default: + return false + } +} + +func validateOptionLengths(options *validate.Options) error { + checks := []struct { + name string + want int + got []byte + }{ + {name: "family ID", want: abi.FamilyIDSize, got: options.FamilyID}, + {name: "image ID", want: abi.ImageIDSize, got: options.ImageID}, + {name: "REPORT_DATA", want: abi.ReportDataSize, got: options.ReportData}, + {name: "measurement", want: abi.MeasurementSize, got: options.Measurement}, + {name: "HOST_DATA", want: abi.HostDataSize, got: options.HostData}, + {name: "report ID", want: abi.ReportIDSize, got: options.ReportID}, + {name: "report ID MA", want: abi.ReportIDMASize, got: options.ReportIDMA}, + {name: "chip ID", want: abi.ChipIDSize, got: options.ChipID}, + } + for _, check := range checks { + if check.got != nil && len(check.got) != check.want { + return fmt.Errorf("%s must be %d bytes, got %d", check.name, check.want, len(check.got)) + } + } + return nil +} + +func cloneVerificationOptions(source *verify.Options) *verify.Options { + clone := *source + if source.Product != nil { + clone.Product = proto.Clone(source.Product).(*sevsnp.SevProduct) + } + if source.TrustedRoots != nil { + clone.TrustedRoots = make(map[string][]*trust.AMDRootCerts, len(source.TrustedRoots)) + for product, roots := range source.TrustedRoots { + clonedRoots := make([]*trust.AMDRootCerts, len(roots)) + for i, root := range roots { + clonedRoots[i] = cloneRoot(root) + } + clone.TrustedRoots[product] = clonedRoots + } + } + return &clone +} + +func cloneRoot(source *trust.AMDRootCerts) *trust.AMDRootCerts { + if source == nil { + return nil + } + source.Mu.Lock() + defer source.Mu.Unlock() + clone := &trust.AMDRootCerts{ + Product: source.Product, + ProductLine: source.ProductLine, + AskSev: source.AskSev, + ArkSev: source.ArkSev, + CRL: source.CRL, + } + if source.ProductCerts != nil { + clone.ProductCerts = &trust.ProductCerts{ + Ask: source.ProductCerts.Ask, + Asvk: source.ProductCerts.Asvk, + Ark: source.ProductCerts.Ark, + } + } + return clone +} + +func cloneValidationOptions(source *validate.Options) *validate.Options { + clone := *source + clone.ReportData = cloneBytes(source.ReportData) + clone.HostData = cloneBytes(source.HostData) + clone.ImageID = cloneBytes(source.ImageID) + clone.FamilyID = cloneBytes(source.FamilyID) + clone.ReportID = cloneBytes(source.ReportID) + clone.ReportIDMA = cloneBytes(source.ReportIDMA) + clone.Measurement = cloneBytes(source.Measurement) + clone.ChipID = cloneBytes(source.ChipID) + clone.TrustedAuthorKeyHashes = cloneByteSlices(source.TrustedAuthorKeyHashes) + clone.TrustedIDKeyHashes = cloneByteSlices(source.TrustedIDKeyHashes) + clone.TrustedAuthorKeys = append([]*x509.Certificate(nil), source.TrustedAuthorKeys...) + clone.TrustedIDKeys = append([]*x509.Certificate(nil), source.TrustedIDKeys...) + if source.PlatformInfo != nil { + platformInfo := *source.PlatformInfo + clone.PlatformInfo = &platformInfo + } + if source.VMPL != nil { + vmpl := *source.VMPL + clone.VMPL = &vmpl + } + if source.CertTableOptions != nil { + clone.CertTableOptions = make(map[string]*validate.CertEntryOption, len(source.CertTableOptions)) + for guid, option := range source.CertTableOptions { + if option == nil { + clone.CertTableOptions[guid] = nil + continue + } + optionCopy := *option + clone.CertTableOptions[guid] = &optionCopy + } + } + return &clone +} + +func cloneBytes(source []byte) []byte { + if source == nil { + return nil + } + return append([]byte(nil), source...) +} + +func cloneByteSlices(source [][]byte) [][]byte { + if source == nil { + return nil + } + clone := make([][]byte, len(source)) + for i := range source { + clone[i] = cloneBytes(source[i]) + } + return clone +} diff --git a/modules/attestation/snp/verifier_test.go b/modules/attestation/snp/verifier_test.go new file mode 100644 index 00000000..6d6ef958 --- /dev/null +++ b/modules/attestation/snp/verifier_test.go @@ -0,0 +1,301 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package snp + +import ( + "context" + "crypto/x509" + "encoding/binary" + "errors" + "math/big" + mathrand "math/rand" + "net/http" + "net/url" + "testing" + "time" + + "github.com/google/go-sev-guest/abi" + "github.com/google/go-sev-guest/kds" + "github.com/google/go-sev-guest/proto/sevsnp" + sevtest "github.com/google/go-sev-guest/testing" + "github.com/google/go-sev-guest/validate" + "github.com/google/go-sev-guest/verify" + "github.com/google/go-sev-guest/verify/trust" + "google.golang.org/protobuf/proto" +) + +type signedFixture struct { + raw []byte + protobuf []byte + reportData []byte + verification *verify.Options + validation *validate.Options + callerTrustedRoot *trust.AMDRootCerts + signer *sevtest.AmdSigner +} + +func newSignedFixture(t *testing.T) *signedFixture { + t.Helper() + + createdAt := time.Date(2024, time.January, 2, 3, 4, 5, 0, time.UTC) + verificationTime := createdAt.Add(time.Hour) + product := abi.DefaultSevProduct() + productLine := kds.ProductLine(product) + signer, err := sevtest.DefaultTestOnlyCertChain(kds.ProductName(product), createdAt) + if err != nil { + t.Fatalf("DefaultTestOnlyCertChain() error = %v", err) + } + + reportData := make([]byte, abi.ReportDataSize) + for i := range reportData { + reportData[i] = byte(i + 1) + } + rawBuffer := sevtest.CreateRawReport(&sevtest.TestReportOptions{ReportData: reportData}) + rawReport := append([]byte(nil), rawBuffer[:abi.ReportSize]...) + binary.LittleEndian.PutUint64(rawReport[0x08:0x10], abi.SnpPolicyToBytes(abi.SnpPolicy{})) + r, s, err := signer.Sign(abi.SignedComponent(rawReport)) + if err != nil { + t.Fatalf("sign report: %v", err) + } + if err := abi.SetSignature(r, s, rawReport); err != nil { + t.Fatalf("SetSignature() error = %v", err) + } + certTable, err := signer.CertTableBytes() + if err != nil { + t.Fatalf("CertTableBytes() error = %v", err) + } + rawEvidence := append(append([]byte(nil), rawReport...), certTable...) + attestation, err := abi.ReportCertsToProto(rawEvidence) + if err != nil { + t.Fatalf("ReportCertsToProto() error = %v", err) + } + attestation.Product = proto.Clone(product).(*sevsnp.SevProduct) + protobufEvidence, err := proto.Marshal(attestation) + if err != nil { + t.Fatalf("proto.Marshal() error = %v", err) + } + + crlTemplate := &x509.RevocationList{ + SignatureAlgorithm: x509.SHA384WithRSAPSS, + Number: big.NewInt(1), + ThisUpdate: createdAt, + NextUpdate: createdAt.Add(24 * time.Hour), + } + crl, err := x509.CreateRevocationList( + mathrand.New(mathrand.NewSource(0x5e5)), + crlTemplate, + signer.Ark, + signer.Keys.Ark, + ) + if err != nil { + t.Fatalf("CreateRevocationList() error = %v", err) + } + getter := sevtest.SimpleGetter(map[string][]byte{ + kds.CrlLinkByKey(productLine, abi.VcekReportSigner): crl, + }) + root := trust.AMDRootCertsProduct(productLine) + root.ProductCerts = &trust.ProductCerts{Ark: signer.Ark, Ask: signer.Ask} + verification := &verify.Options{ + CheckRevocations: true, + Getter: getter, + Now: verificationTime, + TrustedRoots: map[string][]*trust.AMDRootCerts{ + productLine: {root}, + }, + Product: proto.Clone(product).(*sevsnp.SevProduct), + } + vmpl := 0 + validation := &validate.Options{ + GuestPolicy: abi.SnpPolicy{}, + ReportData: make([]byte, abi.ReportDataSize), + VMPL: &vmpl, + } + + return &signedFixture{ + raw: rawEvidence, + protobuf: protobufEvidence, + reportData: reportData, + verification: verification, + validation: validation, + callerTrustedRoot: root, + signer: signer, + } +} + +func TestVerifySignedProtobufAndABIWithCertificateTable(t *testing.T) { + fixture := newSignedFixture(t) + originalConfiguredReportData := append([]byte(nil), fixture.validation.ReportData...) + + for name, evidence := range map[string][]byte{ + "protobuf": fixture.protobuf, + "ABI with certificate table": fixture.raw, + } { + t.Run(name, func(t *testing.T) { + attestation, err := Verify( + context.Background(), + evidence, + fixture.reportData, + fixture.verification, + fixture.validation, + ) + if err != nil { + t.Fatalf("Verify() error = %v", err) + } + if got := attestation.GetReport().GetReportData(); string(got) != string(fixture.reportData) { + t.Fatalf("verified REPORT_DATA = %x, want %x", got, fixture.reportData) + } + }) + } + + if fixture.callerTrustedRoot.CRL != nil { + t.Fatal("Verify() mutated the caller's trusted-root CRL cache") + } + if string(fixture.validation.ReportData) != string(originalConfiguredReportData) { + t.Fatal("Verify() mutated the caller's validation REPORT_DATA") + } +} + +func TestVerifyRejectsTamperedSignature(t *testing.T) { + fixture := newSignedFixture(t) + attestation, err := Parse(fixture.protobuf) + if err != nil { + t.Fatal(err) + } + attestation.Report.Signature[0] ^= 0x80 + tampered, err := proto.Marshal(attestation) + if err != nil { + t.Fatal(err) + } + + _, err = Verify(context.Background(), tampered, fixture.reportData, fixture.verification, fixture.validation) + if !errors.Is(err, ErrVerification) { + t.Fatalf("Verify() error = %v, want ErrVerification", err) + } +} + +func TestVerifyRejectsReportDataMismatch(t *testing.T) { + fixture := newSignedFixture(t) + wrong := append([]byte(nil), fixture.reportData...) + wrong[0] ^= 0xff + + _, err := Verify(context.Background(), fixture.protobuf, wrong, fixture.verification, fixture.validation) + if !errors.Is(err, ErrBinding) { + t.Fatalf("Verify() error = %v, want ErrBinding", err) + } +} + +func TestVerifyRejectsDebugEnabledReport(t *testing.T) { + fixture := newSignedFixture(t) + attestation, err := Parse(fixture.protobuf) + if err != nil { + t.Fatal(err) + } + attestation.Report.Policy = abi.SnpPolicyToBytes(abi.SnpPolicy{Debug: true}) + rawReport, err := abi.ReportToAbiBytes(attestation.Report) + if err != nil { + t.Fatal(err) + } + r, s, err := fixture.signer.Sign(abi.SignedComponent(rawReport)) + if err != nil { + t.Fatal(err) + } + if err := abi.SetSignature(r, s, rawReport); err != nil { + t.Fatal(err) + } + attestation.Report, err = abi.ReportToProto(rawReport) + if err != nil { + t.Fatal(err) + } + debugEvidence, err := proto.Marshal(attestation) + if err != nil { + t.Fatal(err) + } + + _, err = Verify(context.Background(), debugEvidence, fixture.reportData, fixture.verification, fixture.validation) + if !errors.Is(err, ErrValidation) { + t.Fatalf("Verify() error = %v, want ErrValidation", err) + } +} + +func TestVerifyFailsClosedWhenCRLIsUnavailable(t *testing.T) { + fixture := newSignedFixture(t) + verification := *fixture.verification + verification.Getter = sevtest.SimpleGetter(nil) + + _, err := Verify(context.Background(), fixture.protobuf, fixture.reportData, &verification, fixture.validation) + if !errors.Is(err, ErrVerification) { + t.Fatalf("Verify() error = %v, want ErrVerification", err) + } +} + +func TestValidateConfigFailsClosed(t *testing.T) { + tests := []struct { + name string + mutate func(*verify.Options, *validate.Options) + }{ + {name: "no revocation", mutate: func(v *verify.Options, _ *validate.Options) { v.CheckRevocations = false }}, + {name: "no getter", mutate: func(v *verify.Options, _ *validate.Options) { v.Getter = nil }}, + {name: "no product", mutate: func(v *verify.Options, _ *validate.Options) { v.Product = nil }}, + {name: "unknown product", mutate: func(v *verify.Options, _ *validate.Options) { v.Product = &sevsnp.SevProduct{} }}, + {name: "invalid product enum", mutate: func(v *verify.Options, _ *validate.Options) { + v.Product = &sevsnp.SevProduct{Name: sevsnp.SevProduct_SevProductName(99)} + }}, + {name: "nil trusted root", mutate: func(v *verify.Options, _ *validate.Options) { + v.TrustedRoots[kds.ProductLine(v.Product)] = []*trust.AMDRootCerts{nil} + }}, + {name: "debug allowed", mutate: func(_ *verify.Options, v *validate.Options) { v.GuestPolicy.Debug = true }}, + {name: "no VMPL", mutate: func(_ *verify.Options, v *validate.Options) { v.VMPL = nil }}, + {name: "invalid VMPL", mutate: func(_ *verify.Options, v *validate.Options) { value := 4; v.VMPL = &value }}, + {name: "invalid measurement", mutate: func(_ *verify.Options, v *validate.Options) { v.Measurement = []byte{1} }}, + {name: "nil certificate-table option", mutate: func(_ *verify.Options, v *validate.Options) { + v.CertTableOptions = map[string]*validate.CertEntryOption{"00000000-0000-0000-0000-000000000000": nil} + }}, + } + + if !errors.Is(ValidateConfig(nil, &validate.Options{}), ErrInvalidConfig) { + t.Fatal("nil verification options were accepted") + } + if !errors.Is(ValidateConfig(&verify.Options{}, nil), ErrInvalidConfig) { + t.Fatal("nil validation options were accepted") + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fixture := newSignedFixture(t) + test.mutate(fixture.verification, fixture.validation) + if err := ValidateConfig(fixture.verification, fixture.validation); !errors.Is(err, ErrInvalidConfig) { + t.Fatalf("ValidateConfig() error = %v, want ErrInvalidConfig", err) + } + }) + } +} + +func TestParseRejectsMalformedEvidence(t *testing.T) { + tests := [][]byte{ + nil, + {0x0a, 0x00}, + make([]byte, abi.ReportSize), + } + for _, evidence := range tests { + if _, err := Parse(evidence); !errors.Is(err, ErrInvalidEvidence) { + t.Fatalf("Parse(%x) error = %v, want ErrInvalidEvidence", evidence, err) + } + } +} + +func TestKDSGetterRejectsUntrustedURLs(t *testing.T) { + getter := &boundedKDSGetter{client: http.DefaultClient} + if _, err := getter.Get("http://kdsintf.amd.com/vcek/v1/Milan/crl"); err == nil { + t.Fatal("non-HTTPS KDS URL was accepted") + } + if _, err := getter.Get("https://example.test/vcek/v1/Milan/crl"); err == nil { + t.Fatal("non-AMD KDS host was accepted") + } + parsed, err := url.Parse("https://kdsintf.amd.com/vcek/v1/Milan/crl") + if err != nil { + t.Fatal(err) + } + if err := validateKDSURL(parsed); err != nil { + t.Fatalf("AMD KDS URL rejected: %v", err) + } +} diff --git a/modules/attestation/tdx/README.md b/modules/attestation/tdx/README.md new file mode 100644 index 00000000..0cbe3a63 --- /dev/null +++ b/modules/attestation/tdx/README.md @@ -0,0 +1,31 @@ +# Intel TDX attestation module + +Status: experimental `v0.x`. A pinned quote passes real quote authentication +and local-policy validation in deterministic tests. The pinned upstream PCS +fixture is deliberately rejected because its TCB levels do not match that +quote, so there is still no successful strict collateral/CRL fixture or live +TDX run for this change. The module is not production-ready. + +This module accepts a raw QuoteV4. `Verify` checks quote authentication, +certificate and PCS collateral, CRL status, local TDX policy, debug state, and +the caller-supplied `REPORT_DATA`. Its default network getter is bounded and +restricted to Intel PCS. A deployment may inject a reviewed collateral getter. + +It does not implement ASB, TLS, EAT, or CoRIM. Those concerns stay in the root +composition layer. + +Run the hardware-independent checks with: + +```sh +GOWORK=off go mod verify +GOWORK=off go test -race ./... +GOWORK=off go vet ./... +``` + +Live qualification must use the exact target image, launch policy, Intel +platform, endorsement path, and collateral environment. + +A future strict offline success fixture must capture the quote and all four PCS +responses, including issuer-chain headers, as one time-consistent set. Mixing +the pinned quote with unrelated or differently dated collateral would create a +misleading green test. diff --git a/modules/attestation/tdx/go.mod b/modules/attestation/tdx/go.mod new file mode 100644 index 00000000..03fa56f4 --- /dev/null +++ b/modules/attestation/tdx/go.mod @@ -0,0 +1,15 @@ +module github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/tdx + +go 1.26.0 + +require ( + github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843 + google.golang.org/protobuf v1.36.11 +) + +require ( + github.com/google/logger v1.1.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sys v0.19.0 // indirect +) diff --git a/modules/attestation/tdx/go.sum b/modules/attestation/tdx/go.sum new file mode 100644 index 00000000..4b39c5eb --- /dev/null +++ b/modules/attestation/tdx/go.sum @@ -0,0 +1,23 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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.2-0.20241009005452-097ee70d0843 h1:+MoPobRN9HrDhGyn6HnF5NYo4uMBKaiFqAtf/D/OB4A= +github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843/go.mod h1:g/n8sKITIT9xRivBUbizo34DTsUm2nN2uU3A662h09g= +github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= +github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/modules/attestation/tdx/verifier.go b/modules/attestation/tdx/verifier.go new file mode 100644 index 00000000..e6bfadea --- /dev/null +++ b/modules/attestation/tdx/verifier.go @@ -0,0 +1,235 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +// Package tdx verifies Intel TDX QuoteV4 evidence independently from the ASB +// protocol implementation. ASB supplies the expected session REPORT_DATA and +// treats the returned error as the module appraisal result. +package tdx + +import ( + "context" + "crypto/subtle" + "encoding/binary" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/google/go-tdx-guest/abi" + "github.com/google/go-tdx-guest/proto/checkconfig" + tdxpb "github.com/google/go-tdx-guest/proto/tdx" + tdxvalidate "github.com/google/go-tdx-guest/validate" + tdxverify "github.com/google/go-tdx-guest/verify" + tdxtrust "github.com/google/go-tdx-guest/verify/trust" + "google.golang.org/protobuf/proto" +) + +var ( + ErrPolicyRequired = errors.New("tdx module: strict verification policy is required") + ErrInvalidQuote = errors.New("tdx module: invalid QuoteV4 evidence") + ErrBinding = errors.New("tdx module: REPORT_DATA does not match ASB session binding") + ErrVerification = errors.New("tdx module: quote, certificate, collateral, or revocation verification failed") + ErrValidation = errors.New("tdx module: quote policy validation failed") +) + +const ( + defaultCollateralTimeout = 2 * time.Minute + defaultMaxRetryDelay = 30 * time.Second + defaultHTTPRequestTimeout = 20 * time.Second + maxCollateralBytes = 16 << 20 + tdxDebugAttribute = uint64(1) + intelPCSHost = "api.trustedservices.intel.com" +) + +// RuntimeOptions permits deterministic collateral injection in tests and +// deployments with a reviewed local collateral service. Nil uses a bounded +// HTTPS-only getter. +type RuntimeOptions struct { + Getter tdxtrust.HTTPSGetter + Now time.Time +} + +// ValidateConfig enforces the strict verification baseline before evidence is accepted. +func ValidateConfig(policy *checkconfig.Config) error { + if policy == nil || policy.RootOfTrust == nil || policy.Policy == nil || + policy.Policy.HeaderPolicy == nil || policy.Policy.TdQuoteBodyPolicy == nil { + return ErrPolicyRequired + } + if !policy.RootOfTrust.CheckCrl { + return fmt.Errorf("%w: check_crl must be true", ErrPolicyRequired) + } + if !policy.RootOfTrust.GetCollateral { + return fmt.Errorf("%w: get_collateral must be true", ErrPolicyRequired) + } + attributes := policy.Policy.TdQuoteBodyPolicy.TdAttributes + if len(attributes) != abi.TdAttributesSize { + return fmt.Errorf("%w: exact 8-byte td_attributes is required", ErrPolicyRequired) + } + if binary.LittleEndian.Uint64(attributes)&tdxDebugAttribute != 0 { + return fmt.Errorf("%w: TD debug mode must be disabled", ErrPolicyRequired) + } + return nil +} + +// ParseQuote parses raw evidence and rejects every format other than QuoteV4. +func ParseQuote(evidence []byte) (*tdxpb.QuoteV4, error) { + quote, err := abi.QuoteToProto(evidence) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrInvalidQuote, err) + } + quoteV4, ok := quote.(*tdxpb.QuoteV4) + if !ok || quoteV4.GetTdQuoteBody() == nil { + return nil, fmt.Errorf("%w: unsupported quote type %T", ErrInvalidQuote, quote) + } + return quoteV4, nil +} + +// Verify authenticates the quote and collateral, forces ASB's dynamic +// REPORT_DATA into the local validation policy, and rejects debug-enabled TDs. +func Verify(ctx context.Context, evidence, expectedReportData []byte, policy *checkconfig.Config, runtime *RuntimeOptions) error { + if ctx == nil { + return fmt.Errorf("%w: context is required", ErrPolicyRequired) + } + if err := ValidateConfig(policy); err != nil { + return err + } + if len(expectedReportData) != abi.ReportDataSize { + return fmt.Errorf("%w: expected %d bytes, got %d", ErrBinding, abi.ReportDataSize, len(expectedReportData)) + } + quote, err := ParseQuote(evidence) + if err != nil { + return err + } + if subtle.ConstantTimeCompare(quote.GetTdQuoteBody().GetReportData(), expectedReportData) != 1 { + return ErrBinding + } + if err := ctx.Err(); err != nil { + return fmt.Errorf("%w: %v", ErrVerification, err) + } + + policyCopy := proto.Clone(policy).(*checkconfig.Config) + verification, err := tdxverify.RootOfTrustToOptions(policyCopy.RootOfTrust) + if err != nil { + return fmt.Errorf("%w: root of trust: %v", ErrPolicyRequired, err) + } + if runtime != nil && runtime.Getter != nil { + verification.Getter = runtime.Getter + } else { + verification.Getter = newRetryHTTPSGetter(ctx) + } + if runtime != nil && !runtime.Now.IsZero() { + verification.Now = runtime.Now + } else { + verification.Now = time.Now() + } + if err := tdxverify.TdxQuote(quote, verification); err != nil { + return fmt.Errorf("%w: %v", ErrVerification, err) + } + if err := ctx.Err(); err != nil { + return fmt.Errorf("%w: %v", ErrVerification, err) + } + + validation, err := tdxvalidate.PolicyToOptions(policyCopy.Policy) + if err != nil { + return fmt.Errorf("%w: %v", ErrPolicyRequired, err) + } + validation.TdQuoteBodyOptions.ReportData = append([]byte(nil), expectedReportData...) + if err := tdxvalidate.TdxQuote(quote, validation); err != nil { + return fmt.Errorf("%w: %v", ErrValidation, err) + } + return nil +} + +type retryHTTPSGetter struct { + ctx context.Context + client *http.Client + timeout time.Duration + maxRetryDelay time.Duration +} + +func newRetryHTTPSGetter(ctx context.Context) tdxtrust.HTTPSGetter { + client := &http.Client{ + Timeout: defaultHTTPRequestTimeout, + CheckRedirect: func(request *http.Request, _ []*http.Request) error { + if err := validatePCSURL(request.URL); err != nil { + return fmt.Errorf("TDX collateral redirect rejected: %w", err) + } + return nil + }, + } + return &retryHTTPSGetter{ + ctx: ctx, + client: client, + timeout: defaultCollateralTimeout, + maxRetryDelay: defaultMaxRetryDelay, + } +} + +func (g *retryHTTPSGetter) Get(rawURL string) (map[string][]string, []byte, error) { + parsed, err := url.Parse(rawURL) + if err != nil { + return nil, nil, fmt.Errorf("invalid TDX collateral URL: %w", err) + } + if err := validatePCSURL(parsed); err != nil { + return nil, nil, err + } + ctx, cancel := context.WithTimeout(g.ctx, g.timeout) + defer cancel() + delay := 2 * time.Second + var lastErr error + for { + headers, body, err := g.getOnce(ctx, parsed) + if err == nil { + return headers, body, nil + } + lastErr = err + select { + case <-ctx.Done(): + return nil, nil, fmt.Errorf("TDX collateral fetch failed: %w", errors.Join(lastErr, ctx.Err())) + case <-time.After(delay): + } + delay *= 2 + if delay > g.maxRetryDelay { + delay = g.maxRetryDelay + } + } +} + +func validatePCSURL(parsed *url.URL) error { + if parsed == nil || parsed.Scheme != "https" || parsed.Host == "" { + return fmt.Errorf("TDX collateral URL must use HTTPS") + } + if !strings.EqualFold(parsed.Hostname(), intelPCSHost) { + return fmt.Errorf("TDX collateral URL host must be %s", intelPCSHost) + } + if port := parsed.Port(); port != "" && port != "443" { + return fmt.Errorf("TDX collateral URL port must be 443") + } + return nil +} + +func (g *retryHTTPSGetter) getOnce(ctx context.Context, parsed *url.URL) (map[string][]string, []byte, error) { + request, err := http.NewRequestWithContext(ctx, http.MethodGet, parsed.String(), nil) + if err != nil { + return nil, nil, err + } + response, err := g.client.Do(request) + if err != nil { + return nil, nil, err + } + defer response.Body.Close() + if response.StatusCode >= http.StatusMultipleChoices { + return nil, nil, fmt.Errorf("failed to retrieve %s, status code received %d", parsed.Redacted(), response.StatusCode) + } + body, err := io.ReadAll(io.LimitReader(response.Body, maxCollateralBytes+1)) + if err != nil { + return nil, nil, err + } + if len(body) > maxCollateralBytes { + return nil, nil, fmt.Errorf("TDX collateral response exceeds %d bytes", maxCollateralBytes) + } + return response.Header, body, nil +} diff --git a/modules/attestation/tdx/verifier_test.go b/modules/attestation/tdx/verifier_test.go new file mode 100644 index 00000000..8cbb5a3b --- /dev/null +++ b/modules/attestation/tdx/verifier_test.go @@ -0,0 +1,160 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package tdx + +import ( + "context" + "errors" + "fmt" + "net/url" + "strings" + "testing" + "time" + + "github.com/google/go-tdx-guest/abi" + "github.com/google/go-tdx-guest/proto/checkconfig" + tdxtesting "github.com/google/go-tdx-guest/testing" + tdxtestdata "github.com/google/go-tdx-guest/testing/testdata" + tdxvalidate "github.com/google/go-tdx-guest/validate" + tdxverify "github.com/google/go-tdx-guest/verify" +) + +type rejectingGetter struct{} + +func (rejectingGetter) Get(rawURL string) (map[string][]string, []byte, error) { + return nil, nil, fmt.Errorf("offline: %s", rawURL) +} + +func productionTestPolicy() *checkconfig.Config { + return &checkconfig.Config{ + RootOfTrust: &checkconfig.RootOfTrust{CheckCrl: true, GetCollateral: true}, + Policy: &checkconfig.Policy{ + HeaderPolicy: &checkconfig.HeaderPolicy{}, + TdQuoteBodyPolicy: &checkconfig.TDQuoteBodyPolicy{ + TdAttributes: make([]byte, abi.TdAttributesSize), + }, + }, + } +} + +func TestParseQuoteAndRejectBindingMismatch(t *testing.T) { + quote, err := ParseQuote(tdxtestdata.RawQuote) + if err != nil { + t.Fatal(err) + } + expected := append([]byte(nil), quote.GetTdQuoteBody().GetReportData()...) + expected[0] ^= 0xff + err = Verify(context.Background(), tdxtestdata.RawQuote, expected, productionTestPolicy(), &RuntimeOptions{Getter: rejectingGetter{}}) + if !errors.Is(err, ErrBinding) { + t.Fatalf("Verify() error = %v, want ErrBinding", err) + } +} + +func TestValidateConfigFailsClosed(t *testing.T) { + tests := []struct { + name string + mutate func(*checkconfig.Config) + }{ + {name: "nil", mutate: func(*checkconfig.Config) {}}, + {name: "no CRL", mutate: func(p *checkconfig.Config) { p.RootOfTrust.CheckCrl = false }}, + {name: "no collateral", mutate: func(p *checkconfig.Config) { p.RootOfTrust.GetCollateral = false }}, + {name: "no attributes", mutate: func(p *checkconfig.Config) { p.Policy.TdQuoteBodyPolicy.TdAttributes = nil }}, + {name: "debug", mutate: func(p *checkconfig.Config) { p.Policy.TdQuoteBodyPolicy.TdAttributes[0] = 1 }}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.name == "nil" { + if !errors.Is(ValidateConfig(nil), ErrPolicyRequired) { + t.Fatal("nil policy was accepted") + } + return + } + policy := productionTestPolicy() + tc.mutate(policy) + if !errors.Is(ValidateConfig(policy), ErrPolicyRequired) { + t.Fatalf("ValidateConfig() accepted %s", tc.name) + } + }) + } +} + +func TestVerifyReachesCryptographicCollateralStage(t *testing.T) { + quote, err := ParseQuote(tdxtestdata.RawQuote) + if err != nil { + t.Fatal(err) + } + expected := quote.GetTdQuoteBody().GetReportData() + err = Verify(context.Background(), tdxtestdata.RawQuote, expected, productionTestPolicy(), &RuntimeOptions{Getter: rejectingGetter{}}) + if !errors.Is(err, ErrVerification) { + t.Fatalf("Verify() error = %v, want ErrVerification", err) + } +} + +func TestPinnedQuotePassesCryptographicAndLocalPolicyStages(t *testing.T) { + quote, err := ParseQuote(tdxtestdata.RawQuote) + if err != nil { + t.Fatal(err) + } + fixtureTime := time.Date(2023, time.July, 1, 1, 0, 0, 0, time.UTC) + if err := tdxverify.TdxQuote(quote, &tdxverify.Options{ + CheckRevocations: false, + GetCollateral: false, + Now: fixtureTime, + }); err != nil { + t.Fatalf("fixture quote cryptographic verification failed: %v", err) + } + + policy := productionTestPolicy() + policy.Policy.TdQuoteBodyPolicy.TdAttributes = append([]byte(nil), quote.GetTdQuoteBody().GetTdAttributes()...) + validation, err := tdxvalidate.PolicyToOptions(policy.Policy) + if err != nil { + t.Fatal(err) + } + validation.TdQuoteBodyOptions.ReportData = append([]byte(nil), quote.GetTdQuoteBody().GetReportData()...) + if err := tdxvalidate.TdxQuote(quote, validation); err != nil { + t.Fatalf("fixture quote local-policy validation failed: %v", err) + } +} + +func TestPinnedCollateralFailsAtTCBMatchRatherThanTransport(t *testing.T) { + quote, err := ParseQuote(tdxtestdata.RawQuote) + if err != nil { + t.Fatal(err) + } + fixtureTime := time.Date(2023, time.July, 1, 1, 0, 0, 0, time.UTC) + policy := productionTestPolicy() + policy.Policy.TdQuoteBodyPolicy.TdAttributes = append([]byte(nil), quote.GetTdQuoteBody().GetTdAttributes()...) + err = Verify( + context.Background(), + tdxtestdata.RawQuote, + quote.GetTdQuoteBody().GetReportData(), + policy, + &RuntimeOptions{Getter: tdxtesting.TestGetter, Now: fixtureTime}, + ) + if !errors.Is(err, ErrVerification) || !strings.Contains(err.Error(), "no matching TCB") { + t.Fatalf("Verify() error = %v, want deterministic TCB mismatch", err) + } +} + +func TestRetryGetterRejectsNonHTTPS(t *testing.T) { + getter := newRetryHTTPSGetter(context.Background()) + if _, _, err := getter.Get("http://example.test/collateral"); err == nil { + t.Fatal("non-HTTPS collateral URL was accepted") + } + if _, _, err := getter.Get("https://example.test/collateral"); err == nil { + t.Fatal("non-Intel collateral host was accepted") + } + if err := validatePCSURL(mustURL(t, "https://api.trustedservices.intel.com/tdx/certification/v4/tcb")); err != nil { + t.Fatalf("Intel PCS URL rejected: %v", err) + } +} + +func mustURL(t *testing.T, raw string) *url.URL { + t.Helper() + parsed, err := url.Parse(raw) + if err != nil { + t.Fatal(err) + } + return parsed +} From b0c1861f1876aa0174ff25e55fd7d0df01f8ee08 Mon Sep 17 00:00:00 2001 From: thinksyncs Date: Sun, 30 Aug 2026 18:00:04 +0900 Subject: [PATCH 3/8] refactor(atls): inject platform-neutral attestation --- Makefile | 38 +- cmd/agent/main.go | 362 ------------------ cmd/cli/main.go | 2 - cmd/hardware-attestation-redteam/main.go | 323 ---------------- cmd/hardware-attestation-redteam/main_test.go | 80 ---- cmd/ingress-proxy/main.go | 178 --------- go.mod | 3 - go.sum | 2 - pkg/atls/eaattestation/source.go | 42 ++ pkg/atls/eaattestation/source_test.go | 17 + pkg/atls/evidence_verifier.go | 201 ---------- pkg/atls/evidence_verifier_test.go | 161 -------- pkg/atls/internal_transport/conn.go | 37 +- pkg/atls/internal_transport/conn_test.go | 70 ++++ pkg/atls/provider.go | 28 +- pkg/atls/provider_test.go | 148 +++++++ pkg/attestation/attestation.go | 2 - pkg/clients/clients.go | 24 ++ pkg/clients/grpc/agent/agent_test.go | 4 +- pkg/clients/grpc/connect_test.go | 65 +++- pkg/clients/grpc/grpc.go | 23 +- pkg/clients/http/client.go | 20 +- pkg/clients/http/client_test.go | 89 +++-- pkg/clients/identity_jwt.go | 2 +- pkg/tls/tls.go | 29 +- pkg/tls/tls_test.go | 120 +----- 26 files changed, 543 insertions(+), 1527 deletions(-) delete mode 100644 cmd/agent/main.go delete mode 100644 cmd/hardware-attestation-redteam/main.go delete mode 100644 cmd/hardware-attestation-redteam/main_test.go delete mode 100644 cmd/ingress-proxy/main.go create mode 100644 pkg/atls/eaattestation/source.go create mode 100644 pkg/atls/eaattestation/source_test.go delete mode 100644 pkg/atls/evidence_verifier.go delete mode 100644 pkg/atls/evidence_verifier_test.go create mode 100644 pkg/atls/provider_test.go diff --git a/Makefile b/Makefile index 48f26fac..61bdd30f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ BUILD_DIR = build -SERVICES = manager agent cli attestation-service log-forwarder computation-runner egress-proxy ingress-proxy +SERVICES = manager cli attestation-service log-forwarder computation-runner egress-proxy DIRECT_AGENT_CORE_PKGS = ./pkg/atls/... ./pkg/clients/... ./pkg/agtp/... ./pkg/production +ASB_CORE_PKGS = ./pkg/atls/... ./pkg/clients ./pkg/clients/http ./pkg/clients/grpc ./pkg/agtp/... ./pkg/tls PRODUCTION_CONSUMER_PKGS = ./examples/protected-change-consumer CGO_ENABLED ?= 0 GOARCH ?= amd64 @@ -26,7 +27,10 @@ define compile_service -o ${BUILD_DIR}/agents-secure-binding-$(1) ./cmd/$(1) endef -.PHONY: all $(SERVICES) a2a-test install install-a2a-test clean product-security-gate fuzz-smoke +.PHONY: all $(SERVICES) a2a-test install install-a2a-test clean product-security-gate fuzz-smoke \ + test-asb-core test-attestation-modules check-asb-core-boundary \ + check-attestation-v2-boundary check-attestation-v2-release \ + check-attestation-release check-cocos-release all: $(SERVICES) @@ -83,10 +87,34 @@ build-igvm: product-security-gate: go mod verify - GOTOOLCHAIN=go1.26.0+auto go test $(DIRECT_AGENT_CORE_PKGS) - GOTOOLCHAIN=go1.26.0+auto go test -v -race -count=1 ./pkg/atls/identitypolicy ./pkg/clients ./pkg/production ./cmd/redis-failover-redteam $(PRODUCTION_CONSUMER_PKGS) + GOTOOLCHAIN=go1.26.6+auto go test $(DIRECT_AGENT_CORE_PKGS) + GOTOOLCHAIN=go1.26.6+auto go test -v -race -count=1 ./pkg/atls/identitypolicy ./pkg/clients ./pkg/production ./cmd/redis-failover-redteam $(PRODUCTION_CONSUMER_PKGS) $(MAKE) fuzz-smoke $(GOVULNCHECK) ./... fuzz-smoke: - GOTOOLCHAIN=go1.26.0+auto go test -run '^$$' -fuzz=FuzzVerifySessionIdentityJWTRejectsMalformedCompactTokens -fuzztime=10s ./pkg/agtp + GOTOOLCHAIN=go1.26.6+auto go test -run '^$$' -fuzz=FuzzVerifySessionIdentityJWTRejectsMalformedCompactTokens -fuzztime=10s ./pkg/agtp + +check-asb-core-boundary: + sh ./scripts/check-asb-core-boundary.sh + +check-attestation-v2-boundary: + sh ./scripts/check-attestation-v2-boundary.sh + +check-attestation-v2-release: + sh ./scripts/check-attestation-v2-release.sh + +check-attestation-release: + sh ./scripts/check-attestation-release.sh + +check-cocos-release: + sh ./scripts/check-cocos-release.sh + +test-asb-core: check-asb-core-boundary + GOWORK=off go test $(ASB_CORE_PKGS) + +test-attestation-modules: + GOWORK=off go test ./pkg/attestation/... + cd modules/attestation/snp && GOWORK=off go test ./... + cd modules/attestation/tdx && GOWORK=off go test ./... + cd integrations/cocos && GOWORK=off go test ./... diff --git a/cmd/agent/main.go b/cmd/agent/main.go deleted file mode 100644 index 154d6267..00000000 --- a/cmd/agent/main.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (c) Ultraviolet -// SPDX-License-Identifier: Apache-2.0 -package main - -import ( - "context" - "crypto/sha256" - "crypto/sha512" - "crypto/x509" - "encoding/pem" - "fmt" - "log" - "log/slog" - "net/url" - "os" - "os/signal" - "syscall" - - "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" - "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/api" - "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" - cvmsapi "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/api/grpc" - "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/server" - "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" - logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" - agentlogger "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/logger" - mglog "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/logging" - "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/runtime/metrics" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" - pkggrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" - attestation_client "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/attestation" - cvmsgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/cvm" - logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" - runnerclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/runner" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/ingress" - "github.com/caarlos0/env/v11" - "golang.org/x/sync/errgroup" -) - -const ( - svcName = "agent" - envPrefixCVMGRPC = "AGENT_CVM_GRPC_" - storageDir = "/var/lib/agents-secure-binding/agent" -) - -type config struct { - LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"debug"` - Vmpl int `env:"AGENT_VMPL" envDefault:"2"` - AgentGrpcHost string `env:"AGENT_GRPC_HOST" envDefault:"0.0.0.0"` - CAUrl string `env:"AGENT_CVM_CA_URL" envDefault:""` - CVMId string `env:"AGENT_CVM_ID" envDefault:""` - CertsToken string `env:"AGENT_CERTS_TOKEN" envDefault:""` - AgentMaaURL string `env:"AGENT_MAA_URL" envDefault:"https://sharedeus2.eus2.attest.azure.net"` - AgentOSBuild string `env:"AGENT_OS_BUILD" envDefault:"UVC"` - AgentOSDistro string `env:"AGENT_OS_DISTRO" envDefault:"UVC"` - AgentOSType string `env:"AGENT_OS_TYPE" envDefault:"UVC"` - AttestationServiceSocket string `env:"ATTESTATION_SERVICE_SOCKET" envDefault:"/run/agents-secure-binding/attestation.sock"` -} - -func main() { - ctx, cancel := context.WithCancel(context.Background()) - g, ctx := errgroup.WithContext(ctx) - - var cfg config - if err := env.Parse(&cfg); err != nil { - log.Fatalf("failed to load %s configuration : %s", svcName, err) - } - - var exitCode int - defer mglog.ExitWithError(&exitCode) - - var level slog.Level - if err := level.UnmarshalText([]byte(cfg.LogLevel)); err != nil { - log.Println(err) - exitCode = 1 - return - } - - logQueue := make(chan *cvms.ClientStreamMessage, 1000) - cvmsQueue := make(chan *cvms.ClientStreamMessage, 1000) - - handler := agentlogger.NewProtoHandler(os.Stdout, &slog.HandlerOptions{Level: level}, logQueue) - logger := slog.New(handler) - - eventSvc, err := events.New(svcName, logQueue) - if err != nil { - logger.Error(fmt.Sprintf("failed to create events service %s", err.Error())) - exitCode = 1 - return - } - - logClient, err := logclient.NewClient("/run/agents-secure-binding/log.sock") - if err != nil { - logger.Warn(fmt.Sprintf("failed to create log client: %s. Logging will be local only until service is available.", err)) - } else { - defer logClient.Close() - } - - g.Go(func() error { - for { - select { - case <-ctx.Done(): - return nil - case msg := <-logQueue: - if logClient == nil { - continue - } - switch m := msg.Message.(type) { - case *cvms.ClientStreamMessage_AgentLog: - err := logClient.SendLog(ctx, &logpb.LogEntry{ - Message: m.AgentLog.Message, - ComputationId: m.AgentLog.ComputationId, - Level: m.AgentLog.Level, - Timestamp: m.AgentLog.Timestamp, - }) - if err != nil { - logger.Error("failed to send log", "error", err) - } - case *cvms.ClientStreamMessage_AgentEvent: - err := logClient.SendEvent(ctx, &logpb.EventEntry{ - EventType: m.AgentEvent.EventType, - Timestamp: m.AgentEvent.Timestamp, - ComputationId: m.AgentEvent.ComputationId, - Details: m.AgentEvent.Details, - Originator: m.AgentEvent.Originator, - Status: m.AgentEvent.Status, - }) - if err != nil { - logger.Error("failed to send event", "error", err) - } - } - } - } - }) - - ccPlatform := attestation.CCPlatform() - logger.Info(fmt.Sprintf("Detected confidential computing platform: %v", ccPlatform)) - - azureConfig := azure.NewEnvConfigFromAgent( - cfg.AgentOSBuild, - cfg.AgentOSType, - cfg.AgentOSDistro, - cfg.AgentMaaURL, - ) - azure.InitializeDefaultMAAVars(azureConfig) - - cvmGrpcConfig := clients.StandardClientConfig{} - if err := env.ParseWithOptions(&cvmGrpcConfig, env.Options{Prefix: envPrefixCVMGRPC}); err != nil { - logger.Error(fmt.Sprintf("failed to load %s gRPC client configuration : %s", svcName, err)) - exitCode = 1 - return - } - - cvmGRPCClient, cvmsClient, err := cvmsgrpc.NewCVMClient(cvmGrpcConfig) - if err != nil { - logger.Error(err.Error()) - exitCode = 1 - return - } - defer cvmGRPCClient.Close() - - reconnectFn := func(ctx context.Context) (pkggrpc.Client, cvms.Service_ProcessClient, error) { - grpcClient, newClient, err := cvmsgrpc.NewCVMClient(cvmGrpcConfig) - if err != nil { - return nil, nil, err - } - // Don't defer close here as we want to keep the connection open - - pc, err := newClient.Process(ctx) - if err != nil { - grpcClient.Close() - return nil, nil, err - } - return grpcClient, pc, nil - } - - if cfg.Vmpl < 0 || cfg.Vmpl > 3 { - logger.Error("vmpl level must be in a range [0, 3]") - exitCode = 1 - return - } - - attClient, err := attestation_client.NewClient(cfg.AttestationServiceSocket) - if err != nil { - logger.Error(fmt.Sprintf("failed to create attestation client: %s", err)) - exitCode = 1 - return - } - defer attClient.Close() - - runnerClient, err := runnerclient.NewClient("/run/agents-secure-binding/runner.sock") - if err != nil { - logger.Error(fmt.Sprintf("failed to create runner client: %s", err)) - exitCode = 1 - return - } - defer runnerClient.Close() - - svc := newService(ctx, logger, eventSvc, attClient, runnerClient, cfg.Vmpl) - - if err := os.MkdirAll(storageDir, 0o755); err != nil { - logger.Error(fmt.Sprintf("failed to create storage directory: %s", err)) - exitCode = 1 - return - } - - var certProvider atls.CertificateProvider - if ccPlatform != attestation.NoCC { - logger.Info(fmt.Sprintf("Initializing aTLS for platform %v with attestation service at %s", ccPlatform, cfg.AttestationServiceSocket)) - certProvider, err = atls.NewProvider(attClient, ccPlatform, cfg.CertsToken, cfg.CVMId, nil) - if err != nil { - logger.Error(fmt.Sprintf("failed to create certificate provider for aTLS: %s. Continuing without attested TLS.", err)) - } else { - logger.Info("Successfully created aTLS certificate provider") - } - } else { - logger.Warn("No Confidential Computing platform detected (NoCC). Certificate provider remains nil; aTLS will not be available for computations.") - } - - // Create ingress proxy server - backendURL, err := url.Parse("unix:///run/agents-secure-binding/agent.sock") - if err != nil { - logger.Error(fmt.Sprintf("failed to parse backend URL: %s", err)) - exitCode = 1 - return - } - ingressProxy := ingress.NewProxyServer(logger, backendURL, certProvider) - - pc, err := cvmsClient.Process(ctx) - if err != nil { - logger.Error(fmt.Sprintf("failed to connect to cvm server: %s", err)) - exitCode = 1 - return - } - - mc, err := cvmsapi.NewClient(pc, svc, cvmsQueue, logger, server.NewServer(logger, svc, cfg.AgentGrpcHost), ingressProxy, storageDir, reconnectFn, cvmGRPCClient) - if err != nil { - logger.Error(err.Error()) - exitCode = 1 - return - } - - g.Go(func() error { - ch := make(chan os.Signal, 1) - signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) - defer signal.Stop(ch) - - select { - case <-ch: - logger.Info("Received signal, shutting down...") - cancel() - return nil - case <-ctx.Done(): - return ctx.Err() - } - }) - - g.Go(func() error { - return mc.Process(ctx, cancel) - }) - - attest, certSerialNumber, err := attestationFromCert(ctx, cvmGrpcConfig.ClientCert, svc) - if err != nil { - logger.Error(fmt.Sprintf("failed to get attestation: %s", err)) - exitCode = 1 - return - } - - if ccPlatform == attestation.Azure { - azureAttestationToken, azureCertSerialNumber, err := azureAttestationFromCert(ctx, cvmGrpcConfig.ClientCert, svc) - if err != nil { - logger.Error(fmt.Sprintf("failed to get attestation: %s", err)) - exitCode = 1 - return - } - cvmsQueue <- &cvms.ClientStreamMessage{ - Message: &cvms.ClientStreamMessage_AzureAttestationToken{ - AzureAttestationToken: &cvms.AzureAttestationToken{ - File: azureAttestationToken, - CertSerialNumber: azureCertSerialNumber, - }, - }, - } - } - - cvmsQueue <- &cvms.ClientStreamMessage{ - Message: &cvms.ClientStreamMessage_VTPMattestationReport{ - VTPMattestationReport: &cvms.AttestationResponse{ - File: attest, - CertSerialNumber: certSerialNumber, - }, - }, - } - - if err := g.Wait(); err != nil { - logger.Error(fmt.Sprintf("%s service terminated: %s", svcName, err)) - } -} - -func newService(ctx context.Context, logger *slog.Logger, eventSvc events.Service, attClient attestation_client.Client, runnerClient runnerclient.Client, vmpl int) agent.Service { - svc := agent.New(ctx, logger, eventSvc, attClient, runnerClient, vmpl) - - svc = api.LoggingMiddleware(svc, logger) - counter, latency := metrics.MakeMetrics(svcName, "api") - svc = api.MetricsMiddleware(svc, counter, latency) - - return svc -} - -func attestationFromCert(ctx context.Context, certFilePath string, svc agent.Service) ([]byte, string, error) { - if certFilePath == "" { - return nil, "", nil - } - - certFile, err := os.ReadFile(certFilePath) - if err != nil { - return nil, "", err - } - - certPem, _ := pem.Decode(certFile) - certx509, err := x509.ParseCertificate(certPem.Bytes) - if err != nil { - return nil, "", err - } - - nonceSNP := sha512.Sum512(certFile) - nonceVTPM := sha256.Sum256(certFile) - attest, err := svc.Attestation(ctx, nonceSNP, nonceVTPM, attestation.SNPvTPM) - if err != nil { - return nil, "", err - } - - return attest, certx509.SerialNumber.String(), nil -} - -func azureAttestationFromCert(ctx context.Context, certFilePath string, svc agent.Service) ([]byte, string, error) { - if certFilePath == "" { - return nil, "", nil - } - - certFile, err := os.ReadFile(certFilePath) - if err != nil { - return nil, "", err - } - - certPem, _ := pem.Decode(certFile) - certx509, err := x509.ParseCertificate(certPem.Bytes) - if err != nil { - return nil, "", err - } - - nonceAzure := sha256.Sum256(certFile) - attestation, err := svc.AzureAttestationToken(ctx, nonceAzure) - if err != nil { - return nil, "", err - } - - return attestation, certx509.SerialNumber.String(), nil -} diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 95bf0346..de85f840 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -16,7 +16,6 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" "github.com/spf13/pflag" - cmd "github.com/virtee/sev-snp-measure-go/sevsnpmeasure/cmd" ) const ( @@ -146,7 +145,6 @@ func main() { attestationCmd.AddCommand(cliSVC.NewValidateAttestationValidationCmd()) // measure. - rootCmd.AddCommand(cmd.NewRootCmd()) rootCmd.AddCommand(cliSVC.NewMeasureCmd(cfg.IgvmBinaryPath)) // Flags diff --git a/cmd/hardware-attestation-redteam/main.go b/cmd/hardware-attestation-redteam/main.go deleted file mode 100644 index ef5455fe..00000000 --- a/cmd/hardware-attestation-redteam/main.go +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright (c) Ultraviolet -// SPDX-License-Identifier: Apache-2.0 - -package main - -import ( - "bytes" - "crypto/rand" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "flag" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - qemu "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/tdx" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" - sevsnppb "github.com/google/go-sev-guest/proto/sevsnp" - tdxabi "github.com/google/go-tdx-guest/abi" - tdxpb "github.com/google/go-tdx-guest/proto/tdx" - "google.golang.org/protobuf/proto" -) - -const ( - platformAuto = "auto" - platformSNP = "snp" - platformSNPvTPM = "snp-vtpm" - platformTDX = "tdx" - reportDataSize = 64 -) - -var errChallengeMismatch = errors.New("attestation evidence is not bound to verifier challenge") - -type runOptions struct { - Platform string - VMPL uint - ExpectedHostDataHex string - RequireKernelHashes bool - KernelHashesEvidence bool - EvidenceDir string -} - -type extractedEvidence struct { - ReportData []byte - HostData []byte -} - -type runSummary struct { - TimestampUTC string `json:"timestamp_utc"` - Platform string `json:"platform"` - EvidenceASHA256 string `json:"evidence_a_sha256"` - EvidenceBSHA256 string `json:"evidence_b_sha256"` - ChallengeASHA256 string `json:"challenge_a_sha256"` - ChallengeBSHA256 string `json:"challenge_b_sha256"` - HostDataSHA256 string `json:"host_data_sha256,omitempty"` - AppraisalContractCheck bool `json:"appraisal_contract_check"` -} - -func main() { - opts := runOptions{} - flag.StringVar(&opts.Platform, "platform", platformAuto, "attestation platform: auto, snp, snp-vtpm, or tdx") - flag.UintVar(&opts.VMPL, "vmpl", 0, "SEV-SNP VM privilege level") - flag.StringVar(&opts.ExpectedHostDataHex, "expected-host-data-hex", "", "expected SEV-SNP HostData as hex") - flag.BoolVar(&opts.RequireKernelHashes, "require-kernel-hashes", false, "require external evidence that kernel-hashes=on was used") - flag.BoolVar(&opts.KernelHashesEvidence, "kernel-hashes-evidence", false, "runner-provided evidence that kernel-hashes=on was used") - flag.StringVar(&opts.EvidenceDir, "evidence-dir", "", "directory for non-sensitive evidence fingerprints") - flag.Parse() - - summary, err := run(opts) - if err != nil { - fmt.Fprintf(os.Stderr, "hardware attestation red-team failed: %v\n", err) - os.Exit(1) - } - - fmt.Printf( - "hardware attestation red-team passed: platform=%s evidence_a_sha256=%s evidence_b_sha256=%s\n", - summary.Platform, - summary.EvidenceASHA256, - summary.EvidenceBSHA256, - ) -} - -func run(opts runOptions) (*runSummary, error) { - platform, err := resolvePlatform(opts.Platform) - if err != nil { - return nil, err - } - - switch platform { - case platformSNP, platformSNPvTPM: - return runSEVSNP(platform, opts) - case platformTDX: - if opts.ExpectedHostDataHex != "" || opts.RequireKernelHashes { - return nil, fmt.Errorf("HostData and kernel-hashes appraisal is only defined for SEV-SNP") - } - return exerciseTEE(platform, tdx.NewProvider().TeeAttestation, extractTDXEvidence, opts) - default: - return nil, fmt.Errorf("unsupported attestation platform %q", platform) - } -} - -func resolvePlatform(requested string) (string, error) { - normalized := strings.ToLower(strings.TrimSpace(requested)) - switch normalized { - case "", platformAuto: - switch attestation.CCPlatform() { - case attestation.SNP: - return platformSNP, nil - case attestation.SNPvTPM: - return platformSNPvTPM, nil - case attestation.TDX: - return platformTDX, nil - case attestation.Azure: - return "", fmt.Errorf("Azure MAA runtime fetch is disabled in this repository; use a direct SEV-SNP or TDX runner") - case attestation.NoCC: - return "", fmt.Errorf("no confidential-computing attestation device detected") - default: - return "", fmt.Errorf("detected confidential-computing platform is not supported by this gate") - } - case platformSNP, platformSNPvTPM, platformTDX: - return normalized, nil - default: - return "", fmt.Errorf("unsupported attestation platform %q", requested) - } -} - -func runSEVSNP(platform string, opts runOptions) (*runSummary, error) { - provider := vtpm.NewProvider(false, opts.VMPL) - return exerciseTEE(platform, provider.TeeAttestation, extractSEVSNPEvidence, opts) -} - -func exerciseTEE( - platform string, - collect func([]byte) ([]byte, error), - extract func([]byte) (*extractedEvidence, error), - opts runOptions, -) (*runSummary, error) { - challengeA, err := newReportData("agents-secure-binding/hardware-red-team/session-A") - if err != nil { - return nil, err - } - challengeB, err := newReportData("agents-secure-binding/hardware-red-team/session-B") - if err != nil { - return nil, err - } - - evidenceA, err := collect(challengeA) - if err != nil { - return nil, fmt.Errorf("collect %s evidence for session A: %w", platform, err) - } - evidenceB, err := collect(challengeB) - if err != nil { - return nil, fmt.Errorf("collect %s evidence for session B: %w", platform, err) - } - if len(evidenceA) == 0 || len(evidenceB) == 0 { - return nil, fmt.Errorf("%s provider returned empty evidence", platform) - } - if bytes.Equal(evidenceA, evidenceB) { - return nil, fmt.Errorf("%s provider returned identical evidence for distinct challenges", platform) - } - - parsedA, err := extract(evidenceA) - if err != nil { - return nil, fmt.Errorf("extract %s session A report data: %w", platform, err) - } - parsedB, err := extract(evidenceB) - if err != nil { - return nil, fmt.Errorf("extract %s session B report data: %w", platform, err) - } - - if err := validateChallengeBinding(parsedA.ReportData, challengeA); err != nil { - return nil, fmt.Errorf("session A evidence rejected for its own challenge: %w", err) - } - if err := validateChallengeBinding(parsedB.ReportData, challengeB); err != nil { - return nil, fmt.Errorf("session B evidence rejected for its own challenge: %w", err) - } - if err := validateChallengeBinding(parsedA.ReportData, challengeB); !errors.Is(err, errChallengeMismatch) { - return nil, fmt.Errorf("stale session A evidence was not rejected for session B challenge") - } - if err := validateChallengeBinding(parsedB.ReportData, challengeA); !errors.Is(err, errChallengeMismatch) { - return nil, fmt.Errorf("stale session B evidence was not rejected for session A challenge") - } - - appraisalChecked := false - hostDataHash := "" - if opts.ExpectedHostDataHex != "" || opts.RequireKernelHashes { - if platform != platformSNP && platform != platformSNPvTPM { - return nil, fmt.Errorf("SEV-SNP appraisal contract requested for non-SNP platform %q", platform) - } - if opts.ExpectedHostDataHex != "" && len(parsedA.HostData) == 0 { - return nil, fmt.Errorf("SEV-SNP evidence does not contain HostData") - } - expectedHostData := strings.TrimSpace(opts.ExpectedHostDataHex) - if expectedHostData != "" { - var err error - expectedHostData, err = qemu.NormalizeSEVSNPHostData(expectedHostData) - if err != nil { - return nil, fmt.Errorf("decode expected HostData: %w", err) - } - } - contract := qemu.SEVSNPAppraisalContract{ - RequireHostData: expectedHostData != "", - ExpectedHostData: expectedHostData, - RequireKernelHashes: opts.RequireKernelHashes, - } - evidence := qemu.SEVSNPAppraisalEvidence{ - HostData: hex.EncodeToString(parsedA.HostData), - KernelHashesEnabled: opts.KernelHashesEvidence, - } - if err := contract.Validate(evidence); err != nil { - return nil, fmt.Errorf("SEV-SNP appraisal contract rejected evidence: %w", err) - } - appraisalChecked = true - if len(parsedA.HostData) > 0 { - hostDataHash = sha256Hex(parsedA.HostData) - } - } - - summary := &runSummary{ - TimestampUTC: time.Now().UTC().Format(time.RFC3339), - Platform: platform, - EvidenceASHA256: sha256Hex(evidenceA), - EvidenceBSHA256: sha256Hex(evidenceB), - ChallengeASHA256: sha256Hex(challengeA), - ChallengeBSHA256: sha256Hex(challengeB), - HostDataSHA256: hostDataHash, - AppraisalContractCheck: appraisalChecked, - } - if err := writeSummary(opts.EvidenceDir, summary); err != nil { - return nil, err - } - return summary, nil -} - -func newReportData(context string) ([]byte, error) { - reportData := make([]byte, reportDataSize) - if _, err := rand.Read(reportData[:32]); err != nil { - return nil, fmt.Errorf("generate verifier challenge entropy: %w", err) - } - contextHash := sha256.Sum256([]byte(context)) - copy(reportData[32:], contextHash[:]) - return reportData, nil -} - -func validateChallengeBinding(reportData []byte, challenge []byte) error { - if len(reportData) != reportDataSize { - return fmt.Errorf("attestation report_data length is %d, expected %d", len(reportData), reportDataSize) - } - if len(challenge) != reportDataSize { - return fmt.Errorf("verifier challenge length is %d, expected %d", len(challenge), reportDataSize) - } - if !bytes.Equal(reportData, challenge) { - return fmt.Errorf( - "%w: report_data_sha256=%s verifier_challenge_sha256=%s", - errChallengeMismatch, - sha256Hex(reportData), - sha256Hex(challenge), - ) - } - return nil -} - -func extractSEVSNPEvidence(evidence []byte) (*extractedEvidence, error) { - attestation := &sevsnppb.Attestation{} - if err := proto.Unmarshal(evidence, attestation); err != nil { - return nil, err - } - report := attestation.GetReport() - if report == nil { - return nil, fmt.Errorf("missing SEV-SNP report") - } - return &extractedEvidence{ - ReportData: append([]byte(nil), report.GetReportData()...), - HostData: append([]byte(nil), report.GetHostData()...), - }, nil -} - -func extractTDXEvidence(evidence []byte) (*extractedEvidence, error) { - quoteAny, err := tdxabi.QuoteToProto(evidence) - if err != nil { - return nil, err - } - quote, ok := quoteAny.(*tdxpb.QuoteV4) - if !ok { - return nil, fmt.Errorf("unexpected TDX quote type %T", quoteAny) - } - body := quote.GetTdQuoteBody() - if body == nil { - return nil, fmt.Errorf("missing TDX quote body") - } - return &extractedEvidence{ - ReportData: append([]byte(nil), body.GetReportData()...), - }, nil -} - -func writeSummary(dir string, summary *runSummary) error { - if strings.TrimSpace(dir) == "" { - return nil - } - if err := os.MkdirAll(dir, 0o755); err != nil { - return fmt.Errorf("create evidence directory: %w", err) - } - payload, err := json.MarshalIndent(summary, "", " ") - if err != nil { - return fmt.Errorf("marshal evidence summary: %w", err) - } - path := filepath.Join(dir, "summary.json") - if err := os.WriteFile(path, append(payload, '\n'), 0o644); err != nil { - return fmt.Errorf("write evidence summary: %w", err) - } - return nil -} - -func sha256Hex(data []byte) string { - sum := sha256.Sum256(data) - return hex.EncodeToString(sum[:]) -} diff --git a/cmd/hardware-attestation-redteam/main_test.go b/cmd/hardware-attestation-redteam/main_test.go deleted file mode 100644 index 5f9cdfaf..00000000 --- a/cmd/hardware-attestation-redteam/main_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Ultraviolet -// SPDX-License-Identifier: Apache-2.0 - -package main - -import ( - "errors" - "os" - "path/filepath" - "testing" - - sevsnppb "github.com/google/go-sev-guest/proto/sevsnp" - "google.golang.org/protobuf/proto" -) - -func TestValidateChallengeBindingRejectsStaleEvidence(t *testing.T) { - challengeA := make([]byte, reportDataSize) - challengeB := make([]byte, reportDataSize) - challengeA[0] = 1 - challengeB[0] = 2 - - if err := validateChallengeBinding(challengeA, challengeA); err != nil { - t.Fatalf("validateChallengeBinding() rejected matching challenge: %v", err) - } - err := validateChallengeBinding(challengeA, challengeB) - if !errors.Is(err, errChallengeMismatch) { - t.Fatalf("validateChallengeBinding() error = %v, want errChallengeMismatch", err) - } -} - -func TestExtractSEVSNPEvidence(t *testing.T) { - reportData := make([]byte, reportDataSize) - reportData[0] = 0x7a - hostData := make([]byte, 32) - hostData[0] = 0x42 - encoded, err := proto.Marshal(&sevsnppb.Attestation{ - Report: &sevsnppb.Report{ - ReportData: reportData, - HostData: hostData, - }, - }) - if err != nil { - t.Fatalf("proto.Marshal() error = %v", err) - } - - extracted, err := extractSEVSNPEvidence(encoded) - if err != nil { - t.Fatalf("extractSEVSNPEvidence() error = %v", err) - } - if got := extracted.ReportData[0]; got != 0x7a { - t.Fatalf("ReportData[0] = %#x, want 0x7a", got) - } - if got := extracted.HostData[0]; got != 0x42 { - t.Fatalf("HostData[0] = %#x, want 0x42", got) - } -} - -func TestResolvePlatformRejectsUnsupportedInput(t *testing.T) { - if _, err := resolvePlatform("azure"); err == nil { - t.Fatal("resolvePlatform() accepted unsupported explicit platform") - } -} - -func TestWriteSummary(t *testing.T) { - dir := t.TempDir() - summary := &runSummary{ - TimestampUTC: "2026-06-30T00:00:00Z", - Platform: platformSNP, - EvidenceASHA256: "evidence-a", - EvidenceBSHA256: "evidence-b", - ChallengeASHA256: "challenge-a", - ChallengeBSHA256: "challenge-b", - } - if err := writeSummary(dir, summary); err != nil { - t.Fatalf("writeSummary() error = %v", err) - } - if _, err := os.Stat(filepath.Join(dir, "summary.json")); err != nil { - t.Fatalf("summary.json was not written: %v", err) - } -} diff --git a/cmd/ingress-proxy/main.go b/cmd/ingress-proxy/main.go deleted file mode 100644 index e6189d01..00000000 --- a/cmd/ingress-proxy/main.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Ultraviolet -// SPDX-License-Identifier: Apache-2.0 -package main - -import ( - "context" - "fmt" - "log/slog" - "net/url" - "os" - "os/signal" - "syscall" - - "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" - logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" - agentlogger "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/logger" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" - attestation_client "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/attestation" - logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/ingress" - "github.com/caarlos0/env/v11" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "golang.org/x/sync/errgroup" -) - -const ( - svcName = "ingress-proxy" -) - -type config struct { - LogLevel string `env:"ASB_LOG_LEVEL" envAlternate:"AGENT_LOG_LEVEL" envDefault:"info"` - Backend string `env:"ASB_INGRESS_BACKEND" envDefault:"http://localhost:7001"` - - // ATLS Config - CAUrl string `env:"AGENT_CVM_CA_URL" envDefault:""` - CVMId string `env:"AGENT_CVM_ID" envDefault:""` - CertsToken string `env:"AGENT_CERTS_TOKEN" envDefault:""` - AgentMaaURL string `env:"AGENT_MAA_URL" envDefault:"https://sharedeus2.eus2.attest.azure.net"` - AgentOSBuild string `env:"AGENT_OS_BUILD" envDefault:"UVC"` - AgentOSDistro string `env:"AGENT_OS_DISTRO" envDefault:"UVC"` - AgentOSType string `env:"AGENT_OS_TYPE" envDefault:"UVC"` - LogForwarder string `env:"LOG_FORWARDER_SOCKET" envDefault:"/run/agents-secure-binding/log.sock"` -} - -func main() { - var cfg config - if err := env.Parse(&cfg); err != nil { - fmt.Fprintf(os.Stderr, "failed to load configuration: %s\n", err) - os.Exit(1) - } - - cmd := &cobra.Command{ - Use: svcName, - Short: "Ingress Proxy Service", - RunE: func(cmd *cobra.Command, args []string) error { - return run(cfg) - }, - } - - pflag.StringVar(&cfg.LogLevel, "log-level", cfg.LogLevel, "Log level") - pflag.StringVar(&cfg.Backend, "backend", cfg.Backend, "Backend URL") - - if err := cmd.Execute(); err != nil { - fmt.Fprintf(os.Stderr, "Error: %s\n", err) - os.Exit(1) - } -} - -func run(cfg config) error { - var level slog.Level - if err := level.UnmarshalText([]byte(cfg.LogLevel)); err != nil { - return fmt.Errorf("invalid log level: %w", err) - } - - logQueue := make(chan *cvms.ClientStreamMessage, 1000) - handler := agentlogger.NewProtoHandler(os.Stdout, &slog.HandlerOptions{Level: level}, logQueue) - logger := slog.New(handler) - - logClient, err := logclient.NewClient(cfg.LogForwarder) - if err != nil { - logger.Warn(fmt.Sprintf("failed to connect to log-forwarder: %s. Logs will not be forwarded.", err)) - } else { - defer logClient.Close() - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - g, ctx := errgroup.WithContext(ctx) - - g.Go(func() error { - for { - select { - case <-ctx.Done(): - return nil - case msg := <-logQueue: - if logClient == nil { - continue - } - switch m := msg.Message.(type) { - case *cvms.ClientStreamMessage_AgentLog: - err := logClient.SendLog(ctx, &logpb.LogEntry{ - Message: m.AgentLog.Message, - ComputationId: m.AgentLog.ComputationId, - Level: m.AgentLog.Level, - Timestamp: m.AgentLog.Timestamp, - }) - if err != nil { - logger.Error("failed to send log", "error", err) - } - } - } - } - }) - - backendURL, err := url.Parse(cfg.Backend) - if err != nil { - return fmt.Errorf("failed to parse backend URL: %w", err) - } - - // Initialize Certificate Provider - ccPlatform := attestation.CCPlatform() - - azureConfig := azure.NewEnvConfigFromAgent( - cfg.AgentOSBuild, - cfg.AgentOSType, - cfg.AgentOSDistro, - cfg.AgentMaaURL, - ) - azure.InitializeDefaultMAAVars(azureConfig) - - var certProvider atls.CertificateProvider - - if ccPlatform != attestation.NoCC { - // Create attestation client - attClient, err := attestation_client.NewClient("/run/agents-secure-binding/attestation.sock") - if err != nil { - return fmt.Errorf("failed to create attestation client: %w", err) - } - defer attClient.Close() - - certProvider, err = atls.NewProvider(attClient, ccPlatform, cfg.CertsToken, cfg.CVMId, nil) - if err != nil { - return fmt.Errorf("failed to create certificate provider: %w", err) - } - } else { - logger.Warn("No Confidential Computing platform detected. ATLS will not be available.") - } - - // Create proxy server (but don't start it yet - it will be started per-computation) - _ = ingress.NewProxyServer(logger, backendURL, certProvider) - - // Note: The proxy server will be started dynamically when a computation is initiated - // via the Manager's ComputationRunReq message. For now, we just keep the service alive. - logger.Info("ingress-proxy service initialized, waiting for computation requests...") - - g.Go(func() error { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - select { - case s := <-c: - logger.Info(fmt.Sprintf("received signal %s, stopping", s)) - cancel() - return nil - case <-ctx.Done(): - return nil - } - }) - - if err := g.Wait(); err != nil { - return fmt.Errorf("server exit with error: %w", err) - } - - return nil -} diff --git a/go.mod b/go.mod index 45beed4b..8b2a12f7 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 - github.com/virtee/sev-snp-measure-go v0.0.0-20240530153610-e6e8dc9b6877 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 go.opentelemetry.io/otel/trace v1.43.0 golang.org/x/crypto v0.52.0 @@ -120,5 +119,3 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect gopkg.in/yaml.v3 v3.0.1 ) - -replace github.com/virtee/sev-snp-measure-go => github.com/sammyoina/sev-snp-measure-go v0.0.0-20241202151803-ef189f0ff825 diff --git a/go.sum b/go.sum index 47bd98e5..34ab5528 100644 --- a/go.sum +++ b/go.sum @@ -167,8 +167,6 @@ github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05Zp github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sammyoina/sev-snp-measure-go v0.0.0-20241202151803-ef189f0ff825 h1:SqNaL9udBIc026SGNEuEuiVL0/hw9fXxM5qrFhWGkdE= -github.com/sammyoina/sev-snp-measure-go v0.0.0-20241202151803-ef189f0ff825/go.mod h1:dEkBe8JnxU5itNjZDEQINFd7f7l4DtjfqRuzPQcit4w= github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= diff --git a/pkg/atls/eaattestation/source.go b/pkg/atls/eaattestation/source.go new file mode 100644 index 00000000..faebdc4d --- /dev/null +++ b/pkg/atls/eaattestation/source.go @@ -0,0 +1,42 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package attestation + +import ( + "context" + "errors" +) + +var ErrNilEvidenceSourceFunc = errors.New("attestation: nil evidence source function") + +// EvidenceRequest contains the session-bound values that an evidence source +// must place in, or otherwise bind to, newly collected evidence. +type EvidenceRequest struct { + ReportData [64]byte + Nonce [32]byte +} + +// EvidenceResult is the opaque statement returned by an evidence source. +// Platform-specific parsing and collection remain outside the ASB core. +type EvidenceResult struct { + MediaType string + Evidence []byte +} + +// EvidenceSource collects evidence for one ASB authentication attempt. +type EvidenceSource interface { + GetEvidence(context.Context, EvidenceRequest) (EvidenceResult, error) +} + +// EvidenceSourceFunc adapts a function to EvidenceSource. It is useful at an +// application composition boundary without adding a platform dependency to +// the ASB core. +type EvidenceSourceFunc func(context.Context, EvidenceRequest) (EvidenceResult, error) + +func (f EvidenceSourceFunc) GetEvidence(ctx context.Context, request EvidenceRequest) (EvidenceResult, error) { + if f == nil { + return EvidenceResult{}, ErrNilEvidenceSourceFunc + } + return f(ctx, request) +} diff --git a/pkg/atls/eaattestation/source_test.go b/pkg/atls/eaattestation/source_test.go new file mode 100644 index 00000000..1db12e38 --- /dev/null +++ b/pkg/atls/eaattestation/source_test.go @@ -0,0 +1,17 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package attestation + +import ( + "context" + "errors" + "testing" +) + +func TestNilEvidenceSourceFuncFailsClosed(t *testing.T) { + var source EvidenceSourceFunc + if _, err := source.GetEvidence(context.Background(), EvidenceRequest{}); !errors.Is(err, ErrNilEvidenceSourceFunc) { + t.Fatalf("GetEvidence() error = %v, want %v", err, ErrNilEvidenceSourceFunc) + } +} diff --git a/pkg/atls/evidence_verifier.go b/pkg/atls/evidence_verifier.go deleted file mode 100644 index 37e9eea5..00000000 --- a/pkg/atls/evidence_verifier.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Ultraviolet -// SPDX-License-Identifier: Apache-2.0 - -package atls - -import ( - "crypto/subtle" - "fmt" - "os" - - eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" - asbattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/azure" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/tdx" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" - "github.com/google/go-sev-guest/proto/sevsnp" - "github.com/google/go-tpm-tools/proto/attest" - "github.com/google/go-tpm/legacy/tpm2" - "github.com/veraison/corim/corim" - "golang.org/x/crypto/sha3" - "google.golang.org/protobuf/proto" -) - -type policyEvidenceVerifier struct { - policyPath string -} - -func NewEvidenceVerifier(policyPath string) eaattestation.EvidenceVerifier { - return &policyEvidenceVerifier{policyPath: policyPath} -} - -func (v *policyEvidenceVerifier) VerifyEvidence(evidence []byte, expected eaattestation.EvidenceBinding) error { - if v.policyPath == "" { - return fmt.Errorf("atls: attestation policy path is not set") - } - claims, err := eat.DecodeCBOR(evidence, nil) - if err != nil { - return fmt.Errorf("atls: failed to decode EAT evidence: %w", err) - } - if !constantTimeEqual(claims.Nonce, expected.Nonce[:]) { - return fmt.Errorf("atls: evidence nonce does not match TLS exporter binding") - } - platformType := platformTypeFromClaims(claims.PlatformType) - if err := verifyEvidenceBinding(platformType, claims.RawReport, expected); err != nil { - return err - } - manifest, err := loadCoRIM(v.policyPath) - if err != nil { - return err - } - verifier, err := platformVerifier(platformType) - if err != nil { - return err - } - return verifier.VerifyWithCoRIM(claims.RawReport, manifest) -} - -func verifyEvidenceBinding(platformType asbattestation.PlatformType, report []byte, expected eaattestation.EvidenceBinding) error { - switch platformType { - case asbattestation.TDX: - return verifyTDXReportData(report, expected.ReportData[:]) - case asbattestation.SNP, asbattestation.Azure: - return verifySNPReportData(report, expected.ReportData[:]) - case asbattestation.SNPvTPM: - if err := verifyVTPMNonce(report, expected.Nonce[:]); err != nil { - return err - } - return verifySNPvTPMReportData(report, expected.ReportData[:]) - case asbattestation.VTPM: - return verifyVTPMNonce(report, expected.Nonce[:]) - default: - return fmt.Errorf("atls: unsupported platform type for binding verification: %d", platformType) - } -} - -func verifyTDXReportData(report []byte, expectedReportData []byte) error { - const ( - tdxReportDataStart = 0x208 - tdxReportDataEnd = 0x248 - ) - if len(report) < tdxReportDataEnd { - return fmt.Errorf("atls: TDX report too small to extract report data") - } - if !constantTimeEqual(report[tdxReportDataStart:tdxReportDataEnd], expectedReportData) { - return fmt.Errorf("atls: TDX report data does not match TLS exporter binding") - } - return nil -} - -func verifySNPReportData(report []byte, expectedReportData []byte) error { - if snpReport, ok := snpReportFromAttestation(report); ok { - return compareSNPReportData(snpReport, expectedReportData) - } - var snpAtt sevsnp.Attestation - if err := proto.Unmarshal(report, &snpAtt); err != nil { - return fmt.Errorf("atls: failed to parse SNP attestation report: %w", err) - } - return compareSNPReportData(&snpAtt, expectedReportData) -} - -func verifySNPvTPMReportData(report []byte, expectedReportData []byte) error { - var att attest.Attestation - if err := proto.Unmarshal(report, &att); err != nil { - return fmt.Errorf("atls: failed to parse SNP-vTPM attestation report: %w", err) - } - snpReport := att.GetSevSnpAttestation() - if snpReport == nil { - return fmt.Errorf("atls: SNP-vTPM attestation is missing embedded SNP report") - } - expectedTEEReportData := sha3.Sum512(append(append([]byte(nil), expectedReportData...), att.GetAkPub()...)) - return compareSNPReportData(snpReport, expectedTEEReportData[:]) -} - -func snpReportFromAttestation(report []byte) (*sevsnp.Attestation, bool) { - var att attest.Attestation - if err := proto.Unmarshal(report, &att); err != nil { - return nil, false - } - snpReport := att.GetSevSnpAttestation() - return snpReport, snpReport != nil -} - -func compareSNPReportData(snpReport *sevsnp.Attestation, expectedReportData []byte) error { - if snpReport == nil || snpReport.GetReport() == nil { - return fmt.Errorf("atls: SNP attestation is missing report data") - } - if !constantTimeEqual(snpReport.GetReport().GetReportData(), expectedReportData) { - return fmt.Errorf("atls: SNP report data does not match TLS exporter binding") - } - return nil -} - -func verifyVTPMNonce(report []byte, expectedNonce []byte) error { - var att attest.Attestation - if err := proto.Unmarshal(report, &att); err != nil { - return fmt.Errorf("atls: failed to parse vTPM attestation report: %w", err) - } - for _, quote := range att.GetQuotes() { - attested, err := tpm2.DecodeAttestationData(quote.GetQuote()) - if err != nil { - continue - } - if constantTimeEqual(attested.ExtraData, expectedNonce) { - return nil - } - } - return fmt.Errorf("atls: vTPM quote nonce does not match TLS exporter binding") -} - -func constantTimeEqual(a, b []byte) bool { - return subtle.ConstantTimeCompare(a, b) == 1 -} - -func loadCoRIM(path string) (*corim.UnsignedCorim, error) { - corimBytes, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("atls: failed to read CoRIM file: %w", err) - } - - var sc corim.SignedCorim - if err := sc.FromCOSE(corimBytes); err == nil { - return &sc.UnsignedCorim, nil - } - - var uc corim.UnsignedCorim - if err := uc.FromCBOR(corimBytes); err != nil { - return nil, fmt.Errorf("atls: failed to parse CoRIM: %w", err) - } - return &uc, nil -} - -func platformTypeFromClaims(name string) asbattestation.PlatformType { - switch name { - case "SNP": - return asbattestation.SNP - case "TDX": - return asbattestation.TDX - case "vTPM": - return asbattestation.VTPM - case "SNP-vTPM": - return asbattestation.SNPvTPM - case "Azure": - return asbattestation.Azure - default: - return asbattestation.NoCC - } -} - -func platformVerifier(platformType asbattestation.PlatformType) (asbattestation.Verifier, error) { - switch platformType { - case asbattestation.SNP, asbattestation.SNPvTPM, asbattestation.VTPM: - return vtpm.NewVerifier(nil), nil - case asbattestation.Azure: - return azure.NewVerifier(nil), nil - case asbattestation.TDX: - return tdx.NewVerifier(), nil - default: - return nil, fmt.Errorf("atls: unsupported platform type: %d", platformType) - } -} diff --git a/pkg/atls/evidence_verifier_test.go b/pkg/atls/evidence_verifier_test.go deleted file mode 100644 index f1ac5f75..00000000 --- a/pkg/atls/evidence_verifier_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) Ultraviolet -// SPDX-License-Identifier: Apache-2.0 - -package atls - -import ( - "crypto/sha3" - "testing" - - eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" - asbattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" - "github.com/google/go-sev-guest/proto/sevsnp" - "github.com/google/go-tpm-tools/proto/attest" - tpmpb "github.com/google/go-tpm-tools/proto/tpm" - "github.com/google/go-tpm/legacy/tpm2" - "github.com/google/go-tpm/tpmutil" - "google.golang.org/protobuf/proto" -) - -func testEvidenceBinding() eaattestation.EvidenceBinding { - var binding eaattestation.EvidenceBinding - for i := range binding.ReportData { - binding.ReportData[i] = byte(i + 1) - } - for i := range binding.Nonce { - binding.Nonce[i] = byte(0xa0 + i) - } - return binding -} - -func TestVerifyEvidenceBindingRejectsTDXReportDataMismatch(t *testing.T) { - expected := testEvidenceBinding() - report := make([]byte, 0x248) - copy(report[0x208:0x248], expected.ReportData[:]) - - if err := verifyEvidenceBinding(asbattestation.TDX, report, expected); err != nil { - t.Fatalf("unexpected error: %v", err) - } - - report[0x208] ^= 0xff - if err := verifyEvidenceBinding(asbattestation.TDX, report, expected); err == nil { - t.Fatal("expected mismatched TDX report data to fail") - } -} - -func TestVerifyEvidenceBindingRejectsSNPReportDataMismatch(t *testing.T) { - expected := testEvidenceBinding() - report, err := proto.Marshal(&sevsnp.Attestation{ - Report: &sevsnp.Report{ - ReportData: append([]byte(nil), expected.ReportData[:]...), - }, - }) - if err != nil { - t.Fatal(err) - } - - if err := verifyEvidenceBinding(asbattestation.SNP, report, expected); err != nil { - t.Fatalf("unexpected error: %v", err) - } - - var wrong eaattestation.EvidenceBinding - copy(wrong.ReportData[:], expected.ReportData[:]) - wrong.ReportData[0] ^= 0xff - if err := verifyEvidenceBinding(asbattestation.SNP, report, wrong); err == nil { - t.Fatal("expected mismatched SNP report data to fail") - } -} - -func TestVerifyEvidenceBindingRejectsVTPMNonceMismatch(t *testing.T) { - expected := testEvidenceBinding() - report := testVTPMReport(t, expected.Nonce[:]) - - if err := verifyEvidenceBinding(asbattestation.VTPM, report, expected); err != nil { - t.Fatalf("unexpected error: %v", err) - } - - wrong := expected - wrong.Nonce[0] ^= 0xff - if err := verifyEvidenceBinding(asbattestation.VTPM, report, wrong); err == nil { - t.Fatal("expected mismatched vTPM nonce to fail") - } -} - -func TestVerifyEvidenceBindingRejectsSNPvTPMQuoteNonceMismatch(t *testing.T) { - expected := testEvidenceBinding() - report := testSNPvTPMReport(t, expected) - - if err := verifyEvidenceBinding(asbattestation.SNPvTPM, report, expected); err != nil { - t.Fatalf("unexpected error: %v", err) - } - - wrong := expected - wrong.Nonce[0] ^= 0xff - if err := verifyEvidenceBinding(asbattestation.SNPvTPM, report, wrong); err == nil { - t.Fatal("expected mismatched SNP-vTPM quote nonce to fail") - } -} - -func TestVerifyEvidenceBindingRejectsSNPvTPMReportDataMismatch(t *testing.T) { - expected := testEvidenceBinding() - report := testSNPvTPMReport(t, expected) - - wrong := expected - wrong.ReportData[0] ^= 0xff - if err := verifyEvidenceBinding(asbattestation.SNPvTPM, report, wrong); err == nil { - t.Fatal("expected mismatched SNP-vTPM report data to fail") - } -} - -func testVTPMReport(t *testing.T, nonce []byte) []byte { - t.Helper() - quote := testTPMQuote(t, nonce) - report, err := proto.Marshal(&attest.Attestation{ - Quotes: []*tpmpb.Quote{{Quote: quote}}, - }) - if err != nil { - t.Fatal(err) - } - return report -} - -func testSNPvTPMReport(t *testing.T, expected eaattestation.EvidenceBinding) []byte { - t.Helper() - akPub := []byte("test-ak-public-key") - teeReportData := sha3.Sum512(append(append([]byte(nil), expected.ReportData[:]...), akPub...)) - quote := testTPMQuote(t, expected.Nonce[:]) - report, err := proto.Marshal(&attest.Attestation{ - AkPub: akPub, - Quotes: []*tpmpb.Quote{{Quote: quote}}, - TeeAttestation: &attest.Attestation_SevSnpAttestation{ - SevSnpAttestation: &sevsnp.Attestation{ - Report: &sevsnp.Report{ - ReportData: teeReportData[:], - }, - }, - }, - }) - if err != nil { - t.Fatal(err) - } - return report -} - -func testTPMQuote(t *testing.T, nonce []byte) []byte { - t.Helper() - quote, err := tpm2.AttestationData{ - Magic: 0xff544347, - Type: tpm2.TagAttestQuote, - QualifiedSigner: tpm2.Name{}, - ExtraData: tpmutil.U16Bytes(nonce), - ClockInfo: tpm2.ClockInfo{}, - AttestedQuoteInfo: &tpm2.QuoteInfo{ - PCRSelection: tpm2.PCRSelection{Hash: tpm2.AlgSHA256}, - PCRDigest: tpmutil.U16Bytes{}, - }, - }.Encode() - if err != nil { - t.Fatal(err) - } - return quote -} diff --git a/pkg/atls/internal_transport/conn.go b/pkg/atls/internal_transport/conn.go index 48c7f15a..3fe1d7ba 100644 --- a/pkg/atls/internal_transport/conn.go +++ b/pkg/atls/internal_transport/conn.go @@ -13,6 +13,7 @@ import ( "fmt" "log/slog" "net" + "strings" "time" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" @@ -22,7 +23,10 @@ import ( // ErrMissingObservedIdentity reports an enabled identity policy without a // trusted observed-identity source. -var ErrMissingObservedIdentity = errors.New("atls: missing observed identity source") +var ( + ErrMissingObservedIdentity = errors.New("atls: missing observed identity source") + ErrMissingTLSServerName = errors.New("atls: TLS server name is required for a non-TCP target") +) type Conn struct { *tls.Conn @@ -67,13 +71,17 @@ func DialWithDialer(d *net.Dialer, network, address string, cfg *ClientConfig) ( if cfg == nil || cfg.TLSConfig == nil { return nil, fmt.Errorf("atls: missing client TLS config") } + tlsConfig, err := clientTLSConfigForTarget(network, address, cfg.TLSConfig) + if err != nil { + return nil, err + } rawConn, err := d.Dial(network, address) if err != nil { return nil, err } - tlsConn := tls.Client(rawConn, cfg.TLSConfig.Clone()) + tlsConn := tls.Client(rawConn, tlsConfig) conn, err := Client(tlsConn, cfg) if err != nil { _ = tlsConn.Close() @@ -89,13 +97,17 @@ func DialContextWithDialer(ctx context.Context, d *net.Dialer, network, address if ctx == nil { return nil, fmt.Errorf("atls: missing client context") } + tlsConfig, err := clientTLSConfigForTarget(network, address, cfg.TLSConfig) + if err != nil { + return nil, err + } rawConn, err := d.DialContext(ctx, network, address) if err != nil { return nil, err } - tlsConn := tls.Client(rawConn, cfg.TLSConfig.Clone()) + tlsConn := tls.Client(rawConn, tlsConfig) conn, err := ClientContext(ctx, tlsConn, cfg) if err != nil { _ = tlsConn.Close() @@ -104,6 +116,25 @@ func DialContextWithDialer(ctx context.Context, d *net.Dialer, network, address return conn, nil } +func clientTLSConfigForTarget(network, address string, source *tls.Config) (*tls.Config, error) { + config := source.Clone() + if config.ServerName != "" || config.InsecureSkipVerify { + return config, nil + } + if !strings.HasPrefix(network, "tcp") { + return nil, ErrMissingTLSServerName + } + host, _, err := net.SplitHostPort(address) + if err != nil { + return nil, fmt.Errorf("atls: derive TLS server name from %q: %w", address, err) + } + if host == "" { + return nil, fmt.Errorf("atls: derive TLS server name from %q: host is empty", address) + } + config.ServerName = host + return config, nil +} + func Client(tlsConn *tls.Conn, cfg *ClientConfig) (*Conn, error) { return ClientContext(context.Background(), tlsConn, cfg) } diff --git a/pkg/atls/internal_transport/conn_test.go b/pkg/atls/internal_transport/conn_test.go index 8127d3e2..f3eaee50 100644 --- a/pkg/atls/internal_transport/conn_test.go +++ b/pkg/atls/internal_transport/conn_test.go @@ -5,6 +5,7 @@ package internaltransport import ( "bytes" + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -42,6 +43,7 @@ func selfSignedCert(t *testing.T) tls.Certificate { KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, DNSNames: []string{"localhost"}, + IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, } der, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv) @@ -55,6 +57,74 @@ func selfSignedCert(t *testing.T) tls.Certificate { } } +func TestDialContextDerivesServerNameAndValidatesCA(t *testing.T) { + cert := selfSignedCert(t) + roots := x509.NewCertPool() + root, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + t.Fatal(err) + } + roots.AddCert(root) + + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer listener.Close() + + serverResult := make(chan error, 1) + go func() { + raw, err := listener.Accept() + if err != nil { + serverResult <- err + return + } + serverTLS := tls.Server(raw, &tls.Config{ + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS13, + MaxVersion: tls.VersionTLS13, + }) + conn, err := Server(serverTLS, &ServerConfig{Identity: cert}) + if conn != nil { + _ = conn.Close() + } + serverResult <- err + }() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + conn, err := DialContext(ctx, "tcp", listener.Addr().String(), &ClientConfig{ + TLSConfig: &tls.Config{ + RootCAs: roots, + MinVersion: tls.VersionTLS13, + MaxVersion: tls.VersionTLS13, + }, + VerifyOptions: &x509.VerifyOptions{Roots: roots}, + }) + if err != nil { + t.Fatalf("DialContext() failed CA-validated handshake: %v", err) + } + _ = conn.Close() + if err := <-serverResult; err != nil { + t.Fatalf("server failed: %v", err) + } +} + +func TestClientTLSConfigForTargetRequiresNameForUnix(t *testing.T) { + _, err := clientTLSConfigForTarget("unix", "/tmp/asb.sock", &tls.Config{MinVersion: tls.VersionTLS13}) + if !errors.Is(err, ErrMissingTLSServerName) { + t.Fatalf("clientTLSConfigForTarget() error = %v, want ErrMissingTLSServerName", err) + } + + config, err := clientTLSConfigForTarget("unix", "/tmp/asb.sock", &tls.Config{ + MinVersion: tls.VersionTLS13, + ServerName: "agent.internal", + }) + if err != nil || config.ServerName != "agent.internal" { + t.Fatalf("explicit unix server name = (%q, %v)", config.ServerName, err) + } +} + func TestServerAllowsIdentityWithoutTLSConfig(t *testing.T) { cert := selfSignedCert(t) a, b := net.Pipe() diff --git a/pkg/atls/provider.go b/pkg/atls/provider.go index 13d9347c..d7d91b4d 100644 --- a/pkg/atls/provider.go +++ b/pkg/atls/provider.go @@ -13,8 +13,6 @@ import ( "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" - asbattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" - attestationclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/attestation" ) // CertificateProvider builds leaf extensions for accepted aTLS call sites. @@ -25,21 +23,14 @@ type CertificateProvider interface { } type provider struct { - attClient attestationclient.Client - platformType asbattestation.PlatformType + evidenceSource eaattestation.EvidenceSource } -func NewProvider(attClient attestationclient.Client, platformType asbattestation.PlatformType, _ string, _ string, _ any) (CertificateProvider, error) { - if attClient == nil { - return nil, fmt.Errorf("atls: missing attestation client") +func NewProvider(evidenceSource eaattestation.EvidenceSource) (CertificateProvider, error) { + if evidenceSource == nil { + return nil, fmt.Errorf("atls: missing evidence source") } - if platformType == asbattestation.NoCC { - return nil, fmt.Errorf("atls: confidential computing platform not available") - } - return &provider{ - attClient: attClient, - platformType: platformType, - }, nil + return &provider{evidenceSource: evidenceSource}, nil } func (p *provider) BuildLeafExtensions(st *tls.ConnectionState, req *ea.AuthenticatorRequest, leaf *x509.Certificate) ([]ea.Extension, error) { @@ -56,15 +47,18 @@ func (p *provider) BuildLeafExtensions(st *tls.ConnectionState, req *ea.Authenti var nonce [32]byte copy(nonce[:], nonceBytes[:]) - evidence, err := p.attClient.GetAttestation(context.Background(), reportData, nonce, p.platformType) + result, err := p.evidenceSource.GetEvidence(context.Background(), eaattestation.EvidenceRequest{ + ReportData: reportData, + Nonce: nonce, + }) if err != nil { return nil, fmt.Errorf("atls: failed to fetch attestation evidence: %w", err) } payloadBytes, err := eaattestation.MarshalPayload(eaattestation.Payload{ Version: 1, - MediaType: "application/eat+cwt", - Evidence: evidence, + MediaType: result.MediaType, + Evidence: result.Evidence, Binder: eaattestation.AttestationBinder{ ExporterLabel: eaattestation.ExporterLabelAttestation, AIKPubHash: aikPubHash, diff --git a/pkg/atls/provider_test.go b/pkg/atls/provider_test.go new file mode 100644 index 00000000..a69fd056 --- /dev/null +++ b/pkg/atls/provider_test.go @@ -0,0 +1,148 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package atls + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "errors" + "math/big" + "net" + "testing" + "time" + + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/stretchr/testify/require" +) + +type recordingEvidenceSource struct { + called bool + request eaattestation.EvidenceRequest + result eaattestation.EvidenceResult + err error +} + +func (s *recordingEvidenceSource) GetEvidence(_ context.Context, request eaattestation.EvidenceRequest) (eaattestation.EvidenceResult, error) { + s.called = true + s.request = request + return s.result, s.err +} + +func TestProviderBuildLeafExtensionsUsesSessionBinding(t *testing.T) { + certificate, leaf := providerTestCertificate(t) + server, client := providerTLSPair(t, certificate) + defer server.Close() + defer client.Close() + + source := &recordingEvidenceSource{result: eaattestation.EvidenceResult{ + MediaType: "application/eat+cwt", + Evidence: []byte("opaque-evidence"), + }} + provider, err := NewProvider(source) + require.NoError(t, err) + + request := &ea.AuthenticatorRequest{Context: []byte("request-context")} + state := client.ConnectionState() + extensions, err := provider.BuildLeafExtensions(&state, request, leaf) + require.NoError(t, err) + require.True(t, source.called) + require.Len(t, extensions, 1) + + exportedValue, aikPubHash, binding, err := eaattestation.ComputeBinding( + &state, + eaattestation.ExporterLabelAttestation, + request.Context, + leaf, + ) + require.NoError(t, err) + require.Equal(t, sha512.Sum512(binding), source.request.ReportData) + require.Equal(t, sha256.Sum256(exportedValue), source.request.Nonce) + + rawPayload, present, err := ea.ExtractCMWAttestationFromExtensions(extensions) + require.NoError(t, err) + require.True(t, present) + payload, err := eaattestation.ParsePayload(rawPayload) + require.NoError(t, err) + require.Equal(t, "application/eat+cwt", payload.MediaType) + require.Equal(t, []byte("opaque-evidence"), payload.Evidence) + require.Equal(t, eaattestation.ExporterLabelAttestation, payload.Binder.ExporterLabel) + require.True(t, bytes.Equal(aikPubHash, payload.Binder.AIKPubHash)) + require.True(t, bytes.Equal(binding, payload.Binder.Binding)) +} + +func TestNewProviderRejectsMissingEvidenceSource(t *testing.T) { + provider, err := NewProvider(nil) + require.EqualError(t, err, "atls: missing evidence source") + require.Nil(t, provider) +} + +func TestProviderBuildLeafExtensionsWrapsEvidenceSourceError(t *testing.T) { + certificate, leaf := providerTestCertificate(t) + server, client := providerTLSPair(t, certificate) + defer server.Close() + defer client.Close() + + sourceErr := errors.New("source unavailable") + provider, err := NewProvider(&recordingEvidenceSource{err: sourceErr}) + require.NoError(t, err) + state := client.ConnectionState() + + _, err = provider.BuildLeafExtensions(&state, &ea.AuthenticatorRequest{Context: []byte("context")}, leaf) + require.ErrorIs(t, err, sourceErr) + require.ErrorContains(t, err, "failed to fetch attestation evidence") +} + +func providerTestCertificate(t *testing.T) (tls.Certificate, *x509.Certificate) { + t.Helper() + + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "provider-test"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + der, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey) + require.NoError(t, err) + leaf, err := x509.ParseCertificate(der) + require.NoError(t, err) + return tls.Certificate{Certificate: [][]byte{der}, PrivateKey: privateKey}, leaf +} + +func providerTLSPair(t *testing.T, certificate tls.Certificate) (*tls.Conn, *tls.Conn) { + t.Helper() + + serverConfig := &tls.Config{ + Certificates: []tls.Certificate{certificate}, + MinVersion: tls.VersionTLS13, + MaxVersion: tls.VersionTLS13, + } + clientConfig := &tls.Config{ + InsecureSkipVerify: true, // The test verifies the ASB binding, not PKI. + MinVersion: tls.VersionTLS13, + MaxVersion: tls.VersionTLS13, + } + serverConn, clientConn := net.Pipe() + server := tls.Server(serverConn, serverConfig) + client := tls.Client(clientConn, clientConfig) + errCh := make(chan error, 2) + go func() { errCh <- server.Handshake() }() + go func() { errCh <- client.Handshake() }() + for range 2 { + require.NoError(t, <-errCh) + } + return server, client +} diff --git a/pkg/attestation/attestation.go b/pkg/attestation/attestation.go index e5d5957c..a4fc03ab 100644 --- a/pkg/attestation/attestation.go +++ b/pkg/attestation/attestation.go @@ -30,8 +30,6 @@ const ( azureApiVersion = "2021-02-01" ) -var AttestationPolicyPath string - type ccCheck struct { checkFunc func() bool platform PlatformType diff --git a/pkg/clients/clients.go b/pkg/clients/clients.go index 5470c773..e01ae9f2 100644 --- a/pkg/clients/clients.go +++ b/pkg/clients/clients.go @@ -13,6 +13,7 @@ import ( "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/ea" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" ) @@ -21,6 +22,8 @@ var ( _ ClientConfiguration = (*StandardClientConfig)(nil) ErrInvalidAttestationRequestContext = errors.New("invalid attestation request context") + ErrNilAttestedClientConfig = errors.New("attested client configuration is nil") + ErrAttestedTLSRequiresHTTPS = errors.New("attested HTTP client requires HTTPS") ErrInvalidIdentityJWTConfig = errors.New("invalid identity JWT config") ) @@ -35,14 +38,20 @@ type StandardClientConfig struct { ClientCert string `env:"CLIENT_CERT" envDefault:""` ClientKey string `env:"CLIENT_KEY" envDefault:""` ServerCAFile string `env:"SERVER_CA_CERTS" envDefault:""` + ServerName string `env:"SERVER_NAME" envDefault:""` } // AttestedClientConfig represents a client configuration with attested TLS capabilities. type AttestedClientConfig struct { StandardClientConfig + // AttestationPolicy is retained for compatibility with legacy callers. + // Generic clients do not load or interpret this platform policy path. AttestationPolicy string `env:"ATTESTATION_POLICY" envDefault:""` AttestedTLS bool `env:"ATTESTED_TLS" envDefault:"false"` ProductName string `env:"PRODUCT_NAME" envDefault:"Milan"` + // AttestationVerificationPolicy is supplied by the selected deployment + // module. The generic client does not select or construct a platform verifier. + AttestationVerificationPolicy eaattestation.VerificationPolicy `env:"-"` // AttestationRequestContextHex, when set, is decoded from hex and used as // the exported authenticator certificate_request_context. This lets the // caller provide the background-check freshness value directly. @@ -69,6 +78,21 @@ func (c StandardClientConfig) Config() StandardClientConfig { return c } +// AsAttestedClientConfig normalizes both supported representations of an +// attested client configuration. Callers must treat a nil config with ok=true +// as ErrNilAttestedClientConfig rather than falling back to basic TLS. +func AsAttestedClientConfig(cfg ClientConfiguration) (attested *AttestedClientConfig, ok bool) { + switch typed := cfg.(type) { + case AttestedClientConfig: + copy := typed + return ©, true + case *AttestedClientConfig: + return typed, true + default: + return nil, false + } +} + func (c AttestedClientConfig) RequestContext() ([]byte, error) { if len(c.AttestationRequestContext) > 0 { return append([]byte(nil), c.AttestationRequestContext...), nil diff --git a/pkg/clients/grpc/agent/agent_test.go b/pkg/clients/grpc/agent/agent_test.go index ca747dfc..dc2b2266 100644 --- a/pkg/clients/grpc/agent/agent_test.go +++ b/pkg/clients/grpc/agent/agent_test.go @@ -104,7 +104,7 @@ func TestAgentClientIntegration(t *testing.T) { err: errors.New("agent service is unavailable"), }, { - name: "invalid config, missing AttestationPolicy with aTLS", + name: "invalid config, missing attestation verifier with aTLS", config: clients.AttestedClientConfig{ StandardClientConfig: clients.StandardClientConfig{ URL: testServer.listenAddr, @@ -112,7 +112,7 @@ func TestAgentClientIntegration(t *testing.T) { }, AttestedTLS: true, }, - err: errors.New("failed to stat attestation policy file"), + err: clients.ErrMissingAttestationVerifier, }, } diff --git a/pkg/clients/grpc/connect_test.go b/pkg/clients/grpc/connect_test.go index 9db1e5de..eb070194 100644 --- a/pkg/clients/grpc/connect_test.go +++ b/pkg/clients/grpc/connect_test.go @@ -19,6 +19,7 @@ import ( "time" "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/tls" @@ -30,18 +31,10 @@ func TestNewClient(t *testing.T) { caCertFile, clientCertFile, clientKeyFile, err := createCertificatesFiles() require.NoError(t, err) - policyFile, err := os.CreateTemp("", "attestation_policy.json") - require.NoError(t, err) - _, err = policyFile.WriteString("{}") - require.NoError(t, err) - err = policyFile.Close() - require.NoError(t, err) - t.Cleanup(func() { os.Remove(caCertFile) os.Remove(clientCertFile) os.Remove(clientKeyFile) - os.Remove(policyFile.Name()) }) tests := []struct { @@ -109,8 +102,8 @@ func TestNewClient(t *testing.T) { ClientCert: clientCertFile, ClientKey: clientKeyFile, }, - AttestedTLS: true, - AttestationPolicy: policyFile.Name(), + AttestedTLS: true, + AttestationVerificationPolicy: grpcAttestationVerificationPolicy(), }, wantErr: false, err: nil, @@ -124,15 +117,15 @@ func TestNewClient(t *testing.T) { ClientCert: clientCertFile, ClientKey: clientKeyFile, }, - AttestedTLS: true, - AttestationPolicy: policyFile.Name(), - AttestationRequestContextHex: "01020304", + AttestedTLS: true, + AttestationVerificationPolicy: grpcAttestationVerificationPolicy(), + AttestationRequestContextHex: "01020304", }, wantErr: false, err: nil, }, { - name: "Failed agent client with aTLS", + name: "Failed agent client with aTLS and no verifier", agentCfg: clients.AttestedClientConfig{ StandardClientConfig: clients.StandardClientConfig{ URL: "localhost:7001", @@ -140,11 +133,10 @@ func TestNewClient(t *testing.T) { ClientCert: clientCertFile, ClientKey: clientKeyFile, }, - AttestedTLS: true, - AttestationPolicy: "no such file", + AttestedTLS: true, }, wantErr: true, - err: fmt.Errorf("failed to stat attestation policy file"), + err: clients.ErrMissingAttestationVerifier, }, { name: "Failed agent client with invalid attestation request context", @@ -155,9 +147,9 @@ func TestNewClient(t *testing.T) { ClientCert: clientCertFile, ClientKey: clientKeyFile, }, - AttestedTLS: true, - AttestationPolicy: policyFile.Name(), - AttestationRequestContextHex: "xyz", + AttestedTLS: true, + AttestationVerificationPolicy: grpcAttestationVerificationPolicy(), + AttestationRequestContextHex: "xyz", }, wantErr: true, err: clients.ErrInvalidAttestationRequestContext, @@ -216,6 +208,26 @@ func TestNewClient(t *testing.T) { } } +func TestConnect_ATLSPointerCannotBypassVerifier(t *testing.T) { + config := &clients.AttestedClientConfig{ + StandardClientConfig: clients.StandardClientConfig{URL: "localhost:7001"}, + AttestedTLS: true, + } + + conn, security, err := connect(config) + assert.ErrorIs(t, err, clients.ErrMissingAttestationVerifier) + assert.Nil(t, conn) + assert.Equal(t, tls.WithoutTLS, security) +} + +func TestConnect_RejectsNilAttestedConfig(t *testing.T) { + var config *clients.AttestedClientConfig + conn, security, err := connect(config) + assert.ErrorIs(t, err, clients.ErrNilAttestedClientConfig) + assert.Nil(t, conn) + assert.Equal(t, tls.WithoutTLS, security) +} + func TestBuildATLSClientConfigCopiesIdentityBindingInputs(t *testing.T) { grant := &identitypolicy.VerifiedGrant{ Issuer: "manager-key-1", @@ -240,6 +252,7 @@ func TestBuildATLSClientConfigCopiesIdentityBindingInputs(t *testing.T) { replay := newGRPCReplayCache() logger := slog.New(slog.NewTextHandler(io.Discard, nil)) agcfg := clients.AttestedClientConfig{ + AttestationVerificationPolicy: grpcAttestationVerificationPolicy(), IdentityPolicy: identitypolicy.Policy{ Require: identitypolicy.Requirements{L3: true}, Expected: identitypolicy.Values{Service: "payments"}, @@ -253,6 +266,7 @@ func TestBuildATLSClientConfigCopiesIdentityBindingInputs(t *testing.T) { atlsConfig, err := buildATLSClientConfig(agcfg, &stdtls.Config{}) require.NoError(t, err) + assert.Equal(t, agcfg.AttestationVerificationPolicy, atlsConfig.AttestationPolicy) assert.Equal(t, agcfg.IdentityPolicy, atlsConfig.IdentityPolicy) assert.Same(t, grant, atlsConfig.IdentityGrant) assert.Same(t, binding, atlsConfig.IdentityBinding) @@ -262,6 +276,7 @@ func TestBuildATLSClientConfigCopiesIdentityBindingInputs(t *testing.T) { func TestBuildATLSClientConfigWiresAGTPObservedIdentity(t *testing.T) { agcfg := clients.AttestedClientConfig{ + AttestationVerificationPolicy: grpcAttestationVerificationPolicy(), IdentityPolicy: identitypolicy.Policy{ Require: identitypolicy.Requirements{L3: true}, Expected: identitypolicy.Values{Service: "payments"}, @@ -332,6 +347,16 @@ func TestClientSecure(t *testing.T) { type grpcReplayCache struct{} +type grpcAcceptEvidenceVerifier struct{} + +func (grpcAcceptEvidenceVerifier) VerifyEvidence([]byte, eaattestation.EvidenceBinding) error { + return nil +} + +func grpcAttestationVerificationPolicy() eaattestation.VerificationPolicy { + return eaattestation.VerificationPolicy{EvidenceVerifier: grpcAcceptEvidenceVerifier{}} +} + func newGRPCReplayCache() *grpcReplayCache { return &grpcReplayCache{} } diff --git a/pkg/clients/grpc/grpc.go b/pkg/clients/grpc/grpc.go index 1306aa70..f393b6f5 100644 --- a/pkg/clients/grpc/grpc.go +++ b/pkg/clients/grpc/grpc.go @@ -74,9 +74,13 @@ func connect(cfg clients.ClientConfiguration) (*grpc.ClientConn, tls.Security, e } security := tls.WithoutTLS - if agcfg, ok := cfg.(clients.AttestedClientConfig); ok && agcfg.AttestedTLS { - result, err := tls.LoadATLSConfig( - agcfg.AttestationPolicy, + if agcfg, ok := clients.AsAttestedClientConfig(cfg); ok && agcfg == nil { + return nil, security, clients.ErrNilAttestedClientConfig + } else if ok && agcfg.AttestedTLS { + if !agcfg.AttestationVerificationPolicy.RequiresAttestation() { + return nil, security, clients.ErrMissingAttestationVerifier + } + result, err := tls.LoadASBConfig( agcfg.ServerCAFile, agcfg.ClientCert, agcfg.ClientKey, @@ -84,8 +88,9 @@ func connect(cfg clients.ClientConfiguration) (*grpc.ClientConn, tls.Security, e if err != nil { return nil, security, err } + result.Config.ServerName = agcfg.ServerName - atlsConfig, err := buildATLSClientConfig(agcfg, result.Config) + atlsConfig, err := buildATLSClientConfig(*agcfg, result.Config) if err != nil { return nil, security, err } @@ -105,7 +110,7 @@ func connect(cfg clients.ClientConfiguration) (*grpc.ClientConn, tls.Security, e security = result.Security } else { conf := cfg.Config() - transportCreds, sec, err := loadTLSConfig(conf.ServerCAFile, conf.ClientCert, conf.ClientKey) + transportCreds, sec, err := loadTLSConfig(conf.ServerCAFile, conf.ClientCert, conf.ClientKey, conf.ServerName) if err != nil { return nil, security, err } @@ -124,6 +129,9 @@ func connect(cfg clients.ClientConfiguration) (*grpc.ClientConn, tls.Security, e } func buildATLSClientConfig(agcfg clients.AttestedClientConfig, baseTLSConfig *stdtls.Config) (*atls.ClientConfig, error) { + if !agcfg.AttestationVerificationPolicy.RequiresAttestation() { + return nil, clients.ErrMissingAttestationVerifier + } tlsConfig := baseTLSConfig.Clone() tlsConfig.MinVersion = stdtls.VersionTLS13 tlsConfig.NextProtos = []string{"h2"} @@ -131,7 +139,7 @@ func buildATLSClientConfig(agcfg clients.AttestedClientConfig, baseTLSConfig *st atlsConfig := &atls.ClientConfig{ TLSConfig: tlsConfig, VerifyOptions: atls.VerifyOptionsFromTLSConfig(tlsConfig), - AttestationPolicy: atls.VerificationPolicyFromEvidenceVerifier(atls.NewEvidenceVerifier(agcfg.AttestationPolicy)), + AttestationPolicy: agcfg.AttestationVerificationPolicy, IdentityPolicy: agcfg.IdentityPolicy, IdentityGrant: agcfg.IdentityGrant, IdentityBinding: agcfg.IdentityBinding, @@ -173,7 +181,7 @@ func dialTarget(addr string) (string, string) { return "tcp", addr } -func loadTLSConfig(serverCAFile, clientCert, clientKey string) (credentials.TransportCredentials, tls.Security, error) { +func loadTLSConfig(serverCAFile, clientCert, clientKey, serverName string) (credentials.TransportCredentials, tls.Security, error) { result, err := tls.LoadBasicConfig(serverCAFile, clientCert, clientKey) if err != nil { return nil, tls.WithoutTLS, err @@ -182,6 +190,7 @@ func loadTLSConfig(serverCAFile, clientCert, clientKey string) (credentials.Tran if result.Security == tls.WithoutTLS || result.Config == nil { return insecure.NewCredentials(), result.Security, nil } + result.Config.ServerName = serverName return credentials.NewTLS(result.Config), result.Security, nil } diff --git a/pkg/clients/http/client.go b/pkg/clients/http/client.go index 67cc8beb..5a728b9c 100644 --- a/pkg/clients/http/client.go +++ b/pkg/clients/http/client.go @@ -64,9 +64,13 @@ func createTransport(cfg clients.ClientConfiguration) (*http.Transport, tls.Secu security := tls.WithoutTLS - if agcfg, ok := cfg.(*clients.AttestedClientConfig); ok && agcfg.AttestedTLS { - result, err := tls.LoadATLSConfig( - agcfg.AttestationPolicy, + if agcfg, ok := clients.AsAttestedClientConfig(cfg); ok && agcfg == nil { + return nil, security, clients.ErrNilAttestedClientConfig + } else if ok && agcfg.AttestedTLS { + if !agcfg.AttestationVerificationPolicy.RequiresAttestation() { + return nil, security, clients.ErrMissingAttestationVerifier + } + result, err := tls.LoadASBConfig( agcfg.ServerCAFile, agcfg.ClientCert, agcfg.ClientKey, @@ -74,6 +78,7 @@ func createTransport(cfg clients.ClientConfiguration) (*http.Transport, tls.Secu if err != nil { return nil, security, err } + result.Config.ServerName = agcfg.ServerName atlsConfig, err := buildATLSClientConfig(agcfg, result.Config) if err != nil { @@ -84,6 +89,9 @@ func createTransport(cfg clients.ClientConfiguration) (*http.Transport, tls.Secu dialNetwork, target := httpDialTarget(network, addr) return atls.DialContext(ctx, dialNetwork, target, atlsConfig) } + transport.DialContext = func(context.Context, string, string) (net.Conn, error) { + return nil, clients.ErrAttestedTLSRequiresHTTPS + } security = result.Security } else { conf := cfg.Config() @@ -94,6 +102,7 @@ func createTransport(cfg clients.ClientConfiguration) (*http.Transport, tls.Secu } if result.Security != tls.WithoutTLS { + result.Config.ServerName = conf.ServerName transport.TLSClientConfig = result.Config } @@ -104,12 +113,15 @@ func createTransport(cfg clients.ClientConfiguration) (*http.Transport, tls.Secu } func buildATLSClientConfig(agcfg *clients.AttestedClientConfig, baseTLSConfig *stdtls.Config) (*atls.ClientConfig, error) { + if !agcfg.AttestationVerificationPolicy.RequiresAttestation() { + return nil, clients.ErrMissingAttestationVerifier + } tlsConfig := baseTLSConfig.Clone() tlsConfig.MinVersion = stdtls.VersionTLS13 atlsConfig := &atls.ClientConfig{ TLSConfig: tlsConfig, VerifyOptions: atls.VerifyOptionsFromTLSConfig(tlsConfig), - AttestationPolicy: atls.VerificationPolicyFromEvidenceVerifier(atls.NewEvidenceVerifier(agcfg.AttestationPolicy)), + AttestationPolicy: agcfg.AttestationVerificationPolicy, IdentityPolicy: agcfg.IdentityPolicy, IdentityGrant: agcfg.IdentityGrant, IdentityBinding: agcfg.IdentityBinding, diff --git a/pkg/clients/http/client_test.go b/pkg/clients/http/client_test.go index 88506aeb..cb360ee2 100644 --- a/pkg/clients/http/client_test.go +++ b/pkg/clients/http/client_test.go @@ -18,6 +18,7 @@ import ( "testing" "time" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/tls" @@ -202,15 +203,14 @@ func TestCreateTransport_DefaultSettings(t *testing.T) { assert.Nil(t, transport.TLSClientConfig) } -func TestCreateTransport_ATLSError(t *testing.T) { +func TestCreateTransport_ATLSRejectsMissingVerifier(t *testing.T) { config := &clients.AttestedClientConfig{ StandardClientConfig: clients.StandardClientConfig{ URL: "https://agent.example.com", Timeout: 60 * time.Second, }, - AttestationPolicy: "invalid", - AttestedTLS: true, - ProductName: "Milan", + AttestedTLS: true, + ProductName: "Milan", } transport, security, err := createTransport(config) @@ -218,18 +218,50 @@ func TestCreateTransport_ATLSError(t *testing.T) { assert.Error(t, err) assert.Nil(t, transport) assert.Equal(t, tls.WithoutTLS, security) - assert.Contains(t, err.Error(), "failed to stat attestation policy") + assert.ErrorIs(t, err, clients.ErrMissingAttestationVerifier) } -func TestCreateTransport_ATLSCustomRequestContext(t *testing.T) { - policyFile, err := os.CreateTemp("", "attestation_policy.json") +func TestCreateTransport_ATLSValueCannotBypassVerifier(t *testing.T) { + config := clients.AttestedClientConfig{ + StandardClientConfig: clients.StandardClientConfig{URL: "https://agent.example.com"}, + AttestedTLS: true, + } + + transport, security, err := createTransport(config) + assert.ErrorIs(t, err, clients.ErrMissingAttestationVerifier) + assert.Nil(t, transport) + assert.Equal(t, tls.WithoutTLS, security) +} + +func TestCreateTransport_ATLSRejectsPlainHTTP(t *testing.T) { + config := &clients.AttestedClientConfig{ + StandardClientConfig: clients.StandardClientConfig{ + URL: "http://example.invalid", + ServerCAFile: createHTTPTestCAFile(t), + }, + AttestedTLS: true, + AttestationVerificationPolicy: httpAttestationVerificationPolicy(), + } + transport, security, err := createTransport(config) assert.NoError(t, err) - _, err = policyFile.WriteString("{}") + assert.Equal(t, tls.WithMATLS, security) + + request, err := http.NewRequest(http.MethodGet, config.URL, nil) assert.NoError(t, err) - assert.NoError(t, policyFile.Close()) - t.Cleanup(func() { - _ = os.Remove(policyFile.Name()) - }) + response, err := transport.RoundTrip(request) + assert.Nil(t, response) + assert.ErrorIs(t, err, clients.ErrAttestedTLSRequiresHTTPS) +} + +func TestCreateTransport_RejectsNilAttestedConfig(t *testing.T) { + var config *clients.AttestedClientConfig + transport, security, err := createTransport(config) + assert.ErrorIs(t, err, clients.ErrNilAttestedClientConfig) + assert.Nil(t, transport) + assert.Equal(t, tls.WithoutTLS, security) +} + +func TestCreateTransport_ATLSCustomRequestContext(t *testing.T) { caFile := createHTTPTestCAFile(t) config := &clients.AttestedClientConfig{ @@ -238,9 +270,9 @@ func TestCreateTransport_ATLSCustomRequestContext(t *testing.T) { Timeout: 60 * time.Second, ServerCAFile: caFile, }, - AttestationPolicy: policyFile.Name(), - AttestedTLS: true, - AttestationRequestContextHex: "01020304", + AttestedTLS: true, + AttestationVerificationPolicy: httpAttestationVerificationPolicy(), + AttestationRequestContextHex: "01020304", } transport, security, err := createTransport(config) @@ -275,6 +307,7 @@ func TestBuildATLSClientConfigCopiesIdentityBindingInputs(t *testing.T) { replay := newHTTPReplayCache() logger := slog.New(slog.NewTextHandler(io.Discard, nil)) agcfg := &clients.AttestedClientConfig{ + AttestationVerificationPolicy: httpAttestationVerificationPolicy(), IdentityPolicy: identitypolicy.Policy{ Require: identitypolicy.Requirements{L3: true}, Expected: identitypolicy.Values{Service: "payments"}, @@ -288,6 +321,7 @@ func TestBuildATLSClientConfigCopiesIdentityBindingInputs(t *testing.T) { atlsConfig, err := buildATLSClientConfig(agcfg, &stdtls.Config{}) assert.NoError(t, err) + assert.Equal(t, agcfg.AttestationVerificationPolicy, atlsConfig.AttestationPolicy) assert.Equal(t, agcfg.IdentityPolicy, atlsConfig.IdentityPolicy) assert.Same(t, grant, atlsConfig.IdentityGrant) assert.Same(t, binding, atlsConfig.IdentityBinding) @@ -297,6 +331,7 @@ func TestBuildATLSClientConfigCopiesIdentityBindingInputs(t *testing.T) { func TestBuildATLSClientConfigWiresAGTPObservedIdentity(t *testing.T) { agcfg := &clients.AttestedClientConfig{ + AttestationVerificationPolicy: httpAttestationVerificationPolicy(), IdentityPolicy: identitypolicy.Policy{ Require: identitypolicy.Requirements{L3: true}, Expected: identitypolicy.Values{Service: "payments"}, @@ -325,14 +360,6 @@ func TestBuildATLSClientConfigWiresAGTPObservedIdentity(t *testing.T) { } func TestCreateTransport_ATLSInvalidRequestContext(t *testing.T) { - policyFile, err := os.CreateTemp("", "attestation_policy.json") - assert.NoError(t, err) - _, err = policyFile.WriteString("{}") - assert.NoError(t, err) - assert.NoError(t, policyFile.Close()) - t.Cleanup(func() { - _ = os.Remove(policyFile.Name()) - }) caFile := createHTTPTestCAFile(t) config := &clients.AttestedClientConfig{ @@ -341,9 +368,9 @@ func TestCreateTransport_ATLSInvalidRequestContext(t *testing.T) { Timeout: 60 * time.Second, ServerCAFile: caFile, }, - AttestationPolicy: policyFile.Name(), - AttestedTLS: true, - AttestationRequestContextHex: "xyz", + AttestedTLS: true, + AttestationVerificationPolicy: httpAttestationVerificationPolicy(), + AttestationRequestContextHex: "xyz", } transport, security, err := createTransport(config) @@ -356,6 +383,16 @@ func TestCreateTransport_ATLSInvalidRequestContext(t *testing.T) { type httpReplayCache struct{} +type httpAcceptEvidenceVerifier struct{} + +func (httpAcceptEvidenceVerifier) VerifyEvidence([]byte, eaattestation.EvidenceBinding) error { + return nil +} + +func httpAttestationVerificationPolicy() eaattestation.VerificationPolicy { + return eaattestation.VerificationPolicy{EvidenceVerifier: httpAcceptEvidenceVerifier{}} +} + func newHTTPReplayCache() *httpReplayCache { return &httpReplayCache{} } diff --git a/pkg/clients/identity_jwt.go b/pkg/clients/identity_jwt.go index e8a6b094..3c45bd6d 100644 --- a/pkg/clients/identity_jwt.go +++ b/pkg/clients/identity_jwt.go @@ -55,7 +55,7 @@ var ( ErrMissingIssuedAt = errors.New("binding jwt v2: missing issued-at time") ErrMissingTargetField = errors.New("binding jwt v2: missing target field") ErrMissingAttestationBinder = errors.New("binding jwt v2: missing locally expected attestation binder") - ErrMissingAttestationVerifier = errors.New("binding jwt v2: missing attestation verifier") + ErrMissingAttestationVerifier = errors.New("attestation verifier is required") ) const ( diff --git a/pkg/tls/tls.go b/pkg/tls/tls.go index 4a5471e6..83c5cd17 100644 --- a/pkg/tls/tls.go +++ b/pkg/tls/tls.go @@ -9,7 +9,6 @@ import ( "os" "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" - "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" ) // Security represents the type of TLS security configuration. @@ -44,10 +43,9 @@ func (s Security) String() string { const AttestationReportSize = 0x4A0 var ( - ErrFailedToLoadClientCertKey = errors.New("failed to load client certificate and key") - ErrFailedToLoadRootCA = errors.New("failed to load root ca file") - ErrMissingRootCA = errors.New("server CA file is required") - errAttestationPolicyIrregular = errors.New("attestation policy file is not a regular file") + ErrFailedToLoadClientCertKey = errors.New("failed to load client certificate and key") + ErrFailedToLoadRootCA = errors.New("failed to load root ca file") + ErrMissingRootCA = errors.New("server CA file is required") ) // Result contains the result of TLS configuration. @@ -86,11 +84,10 @@ func LoadBasicConfig(serverCAFile, clientCert, clientKey string) (*Result, error return &Result{Config: tlsConfig, Security: security}, nil } -// LoadATLSConfig configures Attested TLS. -func LoadATLSConfig(attestationPolicy, serverCAFile, clientCert, clientKey string) (*Result, error) { - if err := validateRegularFile(attestationPolicy); err != nil { - return nil, err - } +// LoadASBConfig configures the TLS 1.3 transport consumed by Agents Secure +// Binding. Platform attestation policy is supplied separately by the injected +// attestation module. +func LoadASBConfig(serverCAFile, clientCert, clientKey string) (*Result, error) { if serverCAFile == "" { return nil, ErrMissingRootCA } @@ -100,7 +97,6 @@ func LoadATLSConfig(attestationPolicy, serverCAFile, clientCert, clientKey strin return nil, err } - attestation.AttestationPolicyPath = attestationPolicy tlsConfig := &tls.Config{ MinVersion: tls.VersionTLS13, RootCAs: rootCAs, @@ -118,17 +114,6 @@ func LoadATLSConfig(attestationPolicy, serverCAFile, clientCert, clientKey strin return &Result{Config: tlsConfig, Security: security}, nil } -func validateRegularFile(path string) error { - info, err := os.Stat(path) - if err != nil { - return errors.Wrap(errors.New("failed to stat attestation policy file"), err) - } - if !info.Mode().IsRegular() { - return errAttestationPolicyIrregular - } - return nil -} - // loadRootCAs loads root CA certificates from a file. func loadRootCAs(serverCAFile string) (*x509.CertPool, error) { certPEM, err := os.ReadFile(serverCAFile) diff --git a/pkg/tls/tls_test.go b/pkg/tls/tls_test.go index 23026b4c..323c56c6 100644 --- a/pkg/tls/tls_test.go +++ b/pkg/tls/tls_test.go @@ -185,123 +185,31 @@ func TestLoadBasicConfig(t *testing.T) { } } -func TestLoadATLSConfig(t *testing.T) { +func TestLoadASBConfig(t *testing.T) { tmpDir := t.TempDir() - - // Create test files cert, key, caPEM := generateTestCertificates(t) certFile := filepath.Join(tmpDir, "client.crt") keyFile := filepath.Join(tmpDir, "client.key") caFile := filepath.Join(tmpDir, "ca.crt") - policyFile := filepath.Join(tmpDir, "policy.json") - require.NoError(t, os.WriteFile(certFile, cert, 0o644)) require.NoError(t, os.WriteFile(keyFile, key, 0o644)) require.NoError(t, os.WriteFile(caFile, caPEM, 0o644)) - require.NoError(t, os.WriteFile(policyFile, []byte(`{"policy": "test"}`), 0o644)) - - tests := []struct { - name string - attestationPolicy string - serverCAFile string - clientCert string - clientKey string - expectedSec Security - expectError bool - errorMsg string - }{ - { - name: "ATLSWithoutCARejected", - attestationPolicy: policyFile, - serverCAFile: "", - clientCert: "", - clientKey: "", - expectedSec: WithoutTLS, - expectError: true, - errorMsg: "server CA file is required", - }, - { - name: "ValidMATLSConfig", - attestationPolicy: policyFile, - serverCAFile: caFile, - clientCert: "", - clientKey: "", - expectedSec: WithMATLS, - expectError: false, - }, - { - name: "MATLSWithClientCert", - attestationPolicy: policyFile, - serverCAFile: caFile, - clientCert: certFile, - clientKey: keyFile, - expectedSec: WithMATLS, - expectError: false, - }, - { - name: "NonexistentPolicyFile", - attestationPolicy: filepath.Join(tmpDir, "nonexistent.json"), - serverCAFile: "", - clientCert: "", - clientKey: "", - expectedSec: WithoutTLS, - expectError: true, - errorMsg: "failed to stat attestation policy file", - }, - { - name: "PolicyFileIsDirectory", - attestationPolicy: tmpDir, // Directory instead of file - serverCAFile: "", - clientCert: "", - clientKey: "", - expectedSec: WithoutTLS, - expectError: true, - errorMsg: "attestation policy file is not a regular file", - }, - { - name: "InvalidCAFile", - attestationPolicy: policyFile, - serverCAFile: filepath.Join(tmpDir, "nonexistent.crt"), - clientCert: "", - clientKey: "", - expectedSec: WithoutTLS, - expectError: true, - errorMsg: "failed to read certificate file", - }, - { - name: "InvalidClientCert", - attestationPolicy: policyFile, - serverCAFile: "", - clientCert: filepath.Join(tmpDir, "nonexistent.crt"), - clientKey: keyFile, - expectedSec: WithoutTLS, - expectError: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := LoadATLSConfig(tt.attestationPolicy, tt.serverCAFile, tt.clientCert, tt.clientKey) - - if tt.expectError { - assert.Error(t, err) - if tt.errorMsg != "" { - assert.Contains(t, err.Error(), tt.errorMsg) - } - return - } - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, tt.expectedSec, result.Security) - assert.NotNil(t, result.Config) + result, err := LoadASBConfig(caFile, certFile, keyFile) + require.NoError(t, err) + require.NotNil(t, result) + require.NotNil(t, result.Config) + assert.Equal(t, WithMATLS, result.Security) + assert.Equal(t, uint16(stdtls.VersionTLS13), result.Config.MinVersion) + assert.NotNil(t, result.Config.RootCAs) + assert.False(t, result.Config.InsecureSkipVerify) +} - assert.False(t, result.Config.InsecureSkipVerify) - assert.Equal(t, uint16(stdtls.VersionTLS13), result.Config.MinVersion) - assert.NotNil(t, result.Config.RootCAs) - }) - } +func TestLoadASBConfigRejectsMissingCA(t *testing.T) { + result, err := LoadASBConfig("", "", "") + assert.ErrorIs(t, err, ErrMissingRootCA) + assert.Nil(t, result) } func TestLoadRootCAs(t *testing.T) { From 09b49f124ded49fa80bc04a1307c736b33167877 Mon Sep 17 00:00:00 2001 From: thinksyncs Date: Sun, 30 Aug 2026 18:01:39 +0900 Subject: [PATCH 4/8] fix(attestation): harden compatibility appraisal --- cli/CORIM_GENERATION.md | 18 +- cli/attestation_policy_corim.go | 5 +- cli/attestation_policy_corim_test.go | 17 +- pkg/attestation/corimgen/README.md | 30 +-- pkg/attestation/corimgen/generator.go | 134 ++++++++-- pkg/attestation/corimgen/generator_test.go | 125 +++++++++- pkg/attestation/eat/decoder.go | 18 ++ pkg/attestation/eat/decoder_test.go | 35 +++ pkg/attestation/eat/key.go | 116 +++++++++ pkg/attestation/eat/key_test.go | 106 ++++++++ pkg/attestation/generator/README.md | 17 +- pkg/attestation/generator/generator.go | 2 +- pkg/attestation/tdx/tdx.go | 198 +++++++++++++-- pkg/attestation/tdx/tdx_test.go | 272 ++++++++++++++++++--- pkg/attestation/vtpm/sev.go | 101 +++++--- pkg/attestation/vtpm/vtpm.go | 170 ++++++++++++- pkg/attestation/vtpm/vtpm_test.go | 131 +++++++++- 17 files changed, 1316 insertions(+), 179 deletions(-) create mode 100644 pkg/attestation/eat/key.go create mode 100644 pkg/attestation/eat/key_test.go diff --git a/cli/CORIM_GENERATION.md b/cli/CORIM_GENERATION.md index 5b14a001..2b6fba23 100644 --- a/cli/CORIM_GENERATION.md +++ b/cli/CORIM_GENERATION.md @@ -63,10 +63,10 @@ cocos-cli policy create-corim snp [flags] **Flags:** - `--measurement` (optional): Measurement/Launch Digest (hex string, defaults to zero if not provided) - `--policy` (optional): SNP policy flags (default: 0) -- `--svn` (optional): Security Version Number/TCB (default: 0) +- `--svn` (optional): Exact SNP guest SVN; omitted when zero - `--product` (optional): Processor product name (default: "Milan") -- `--host-data` (optional): Host data (hex string) -- `--launch-tcb` (optional): Minimum launch TCB (default: 0) +- `--host-data` (optional): Exact 32-byte host data (64 hex characters) +- `--launch-tcb` (optional): Component-wise minimum launch TCB; omitted when zero - `--output` (optional): Output file path (default: stdout) **Examples:** @@ -92,7 +92,7 @@ Generate with host data and launch TCB: ```bash cocos-cli policy create-corim snp \ --measurement abc123... \ - --host-data deadbeef \ + --host-data 0000000000000000000000000000000000000000000000000000000000000000 \ --launch-tcb 1 \ --output snp-policy.corim ``` @@ -107,7 +107,6 @@ cocos-cli policy create-corim tdx [flags] **Flags:** - `--measurement` (optional): MRTD measurement (hex string, uses default if not provided) -- `--svn` (optional): Security Version Number (default: 0) - `--rtmrs` (optional): Comma-separated RTMRs (hex) - `--mr-seam` (optional): MRSEAM (hex) - `--output` (optional): Output file path (default: stdout) @@ -126,10 +125,12 @@ cocos-cli policy create-corim tdx \ --measurement abc123def456... \ --rtmrs rtmr0,rtmr1,rtmr2,rtmr3 \ --mr-seam 789abc... \ - --svn 2 \ --output tdx-policy.corim ``` +TDX does not expose a scalar `--svn` flag. Configure the 16-byte +`minimum_tee_tcb_svn` in the TDX platform JSON policy. + ## Signing CoRIMs CoRIMs can be signed using a private key (COSE_Sign1). The generated output will be a COSE-wrapped CoRIM in CBOR format. @@ -167,7 +168,10 @@ The output file is a standard COSE_Sign1 message containing the CoRIM. It can be ## Output Format -All commands output CoRIM in CBOR (Concise Binary Object Representation) format. By default, output is written to stdout, allowing for piping: +All commands output CoRIM in CBOR (Concise Binary Object Representation) +format. SNP and TDX fields use repository-local unsigned-integer CoMID keys; +the keys are not IETF-assigned values or a general interoperability profile. +By default, output is written to stdout, allowing for piping: ```bash # Pipe to file diff --git a/cli/attestation_policy_corim.go b/cli/attestation_policy_corim.go index 57b03779..0d3a71c2 100644 --- a/cli/attestation_policy_corim.go +++ b/cli/attestation_policy_corim.go @@ -217,7 +217,7 @@ func (cli *CLI) NewCreateCoRIMSNPCmd() *cobra.Command { cmd.Flags().StringVar(&measurement, "measurement", "", "Measurement/Launch Digest (hex string, defaults to zero if not provided)") cmd.Flags().Uint64Var(&policy, "policy", 0, "SNP policy flags") - cmd.Flags().Uint64Var(&svn, "svn", 0, "Security Version Number (TCB)") + cmd.Flags().Uint64Var(&svn, "svn", 0, "Exact SNP guest SVN") cmd.Flags().StringVar(&product, "product", "Milan", "Processor product name (Milan, Genoa, etc.)") cmd.Flags().StringVar(&hostData, "host-data", "", "Host data (hex string)") cmd.Flags().Uint64Var(&launchTCB, "launch-tcb", 0, "Minimum launch TCB") @@ -230,7 +230,6 @@ func (cli *CLI) NewCreateCoRIMSNPCmd() *cobra.Command { func (cli *CLI) NewCreateCoRIMTDXCmd() *cobra.Command { var ( measurement string - svn uint64 rtmrs string mrSeam string output string @@ -245,7 +244,6 @@ func (cli *CLI) NewCreateCoRIMTDXCmd() *cobra.Command { opts := generator.Options{ Platform: "tdx", Measurement: measurement, - SVN: svn, RTMRs: rtmrs, MrSeam: mrSeam, } @@ -279,7 +277,6 @@ func (cli *CLI) NewCreateCoRIMTDXCmd() *cobra.Command { } cmd.Flags().StringVar(&measurement, "measurement", "", "MRTD measurement (hex string, uses default if not provided)") - cmd.Flags().Uint64Var(&svn, "svn", 0, "Security Version Number") cmd.Flags().StringVar(&rtmrs, "rtmrs", "", "Comma-separated RTMRs (hex)") cmd.Flags().StringVar(&mrSeam, "mr-seam", "", "MRSEAM (hex)") cmd.Flags().StringVar(&output, "output", "", "Output file path (default: stdout)") diff --git a/cli/attestation_policy_corim_test.go b/cli/attestation_policy_corim_test.go index 534c767d..bb56373e 100644 --- a/cli/attestation_policy_corim_test.go +++ b/cli/attestation_policy_corim_test.go @@ -18,6 +18,8 @@ import ( "google.golang.org/protobuf/proto" ) +const validSHA384Measurement = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" + func TestCLI_NewCreateCoRIMCmd(t *testing.T) { cli := &CLI{} cmd := cli.NewCreateCoRIMCmd() @@ -119,7 +121,7 @@ func TestCLI_NewCreateCoRIMAzureCmd_Success(t *testing.T) { validateFunc: func(token string) (map[string]any, error) { return map[string]any{ "x-ms-isolation-tee": map[string]any{ - "x-ms-sevsnpvm-launchmeasurement": "00112233", + "x-ms-sevsnpvm-launchmeasurement": validSHA384Measurement, "x-ms-sevsnpvm-guestsvn": 1.0, }, }, nil @@ -173,7 +175,7 @@ func TestCLI_NewCreateCoRIMGCPCmd_More(t *testing.T) { goldenUEFI := &endorsement.VMGoldenMeasurement{ SevSnp: &endorsement.VMSevSnp{ Policy: 123, - Measurements: map[uint32][]byte{1: {0x1, 0x2}}, + Measurements: map[uint32][]byte{1: bytes.Repeat([]byte{0x01}, 48)}, }, } goldenBytes, _ := proto.Marshal(goldenUEFI) @@ -213,7 +215,7 @@ func TestCLI_NewCreateCoRIMSNPCmd_More(t *testing.T) { "--policy", "1", "--svn", "1", "--product", "Genoa", - "--host-data", "00112233", + "--host-data", "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", "--launch-tcb", "1", "--output", outputFile, }) @@ -232,10 +234,9 @@ func TestCLI_NewCreateCoRIMTDXCmd_More(t *testing.T) { outputFile := filepath.Join(tmpDir, "tdx-corim.cbor") cmd.SetArgs([]string{ - "--measurement", "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", - "--svn", "1", - "--rtmrs", "0011,2233", - "--mr-seam", "aabbcc", + "--measurement", validSHA384Measurement, + "--rtmrs", validSHA384Measurement + "," + validSHA384Measurement, + "--mr-seam", validSHA384Measurement, "--output", outputFile, }) @@ -351,7 +352,7 @@ func TestCLI_NewCreateCoRIMGCPCmd_Success(t *testing.T) { goldenUEFI := &endorsement.VMGoldenMeasurement{ SevSnp: &endorsement.VMSevSnp{ Policy: 123, - Measurements: map[uint32][]byte{1: {0x1, 0x2}}, + Measurements: map[uint32][]byte{1: bytes.Repeat([]byte{0x01}, 48)}, }, } goldenBytes, _ := proto.Marshal(goldenUEFI) diff --git a/pkg/attestation/corimgen/README.md b/pkg/attestation/corimgen/README.md index dfb6bfef..5e4af3ee 100644 --- a/pkg/attestation/corimgen/README.md +++ b/pkg/attestation/corimgen/README.md @@ -4,14 +4,14 @@ This package provides CoRIM (Concise Reference Integrity Manifest) generation us ## Overview -The `corimgen` package generates CoRIM attestation policies for confidential computing platforms (SNP and TDX) using the veraison/corim library, which provides: -- Standard-compliant CoRIM/CoMID structures per RFC 9393 -- Built-in COSE signing and verification -- Ecosystem compatibility with Veraison attestation services +The `corimgen` package generates CoRIM containers for local SNP and TDX +attestation policy. The container and CoMID encoding use the `veraison/corim` +implementation of RFC 9393. The SNP/TDX measurement-key mapping is an ASB-local +appraisal profile, not an IETF-assigned interoperability profile. ## Features -- **SNP Support**: Generate CoRIM for AMD SEV-SNP with measurements, SVN, and product information +- **SNP Support**: Generate keyed constraints for measurement, guest SVN, host data, policy, and minimum launch TCB - **TDX Support**: Generate CoRIM for Intel TDX with MRTD, MRSEAM, and RTMRs - **COSE Signing**: Optional COSE_Sign1 signing with crypto.Signer keys - **Defaults**: Sensible defaults for testing and development @@ -65,7 +65,6 @@ opts := corimgen.Options{ Measurement: "91eb2b44...", // MRTD MrSeam: "5b38e33a...", // MRSEAM RTMRs: "ce0891f4...,062ac322...,5fd86e8c...,00000000...", // comma-separated - SVN: 2, } corimBytes, err := corimgen.GenerateCoRIM(opts) @@ -78,12 +77,12 @@ corimBytes, err := corimgen.GenerateCoRIM(opts) | `Platform` | string | Platform type: "snp" or "tdx" | | `Measurement` | string | Hex-encoded measurement (MRTD for TDX, measurement for SNP) | | `Product` | string | SNP processor product name (e.g., "Milan", "Genoa") | -| `SVN` | uint64 | Security Version Number | -| `Policy` | uint64 | SNP policy flags | +| `SVN` | uint64 | Exact SNP guest SVN; unsupported for TDX | +| `Policy` | uint64 | Exact nonzero SNP policy flags | | `RTMRs` | string | TDX Runtime Measurement Registers (comma-separated hex) | | `MrSeam` | string | TDX SEAM module measurement (hex) | -| `HostData` | string | SNP host data (hex) | -| `LaunchTCB` | uint64 | SNP minimum launch TCB | +| `HostData` | string | Exact 32-byte SNP host data (64 hex characters) | +| `LaunchTCB` | uint64 | Component-wise minimum SNP launch TCB when nonzero | | `SigningKey` | crypto.Signer | Optional COSE signing key (ES256) | ## Defaults @@ -108,10 +107,13 @@ Generated CoRIM contains: - **CoMID Tags**: One or more CoMID tags with: - **Tag Identity**: Unique tag ID and version - **Environment**: Platform class (UUID) and optional instance (product) - - **Reference Values**: Measurements with: - - **Key**: UUID identifier for each measurement - - **Digests**: SHA-256 hash of measurement - - **SVN**: Security version number (if specified) + - **Reference Values**: Measurements with repository-local unsigned-integer + keys. SNP uses `0x1000`-`0x1003`; TDX uses `0x2000`, `0x2001`, and + `0x2010`-`0x2013`. These values are not IETF-assigned code points. + +The matching appraisers reject unkeyed, unknown, duplicate, and unsupported +constraints. TDX TCB policy remains in the platform JSON policy as +`minimum_tee_tcb_svn`; it is not represented by the scalar `SVN` option. ### Signing diff --git a/pkg/attestation/corimgen/generator.go b/pkg/attestation/corimgen/generator.go index 85add130..7d60f2dc 100644 --- a/pkg/attestation/corimgen/generator.go +++ b/pkg/attestation/corimgen/generator.go @@ -4,20 +4,34 @@ package corimgen import ( "crypto" + "encoding/binary" "encoding/hex" "fmt" "strings" + sevabi "github.com/google/go-sev-guest/abi" "github.com/google/uuid" "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" "github.com/veraison/go-cose" + "github.com/veraison/swid" ) // Legacy SNP Defaults. const ( SNPDefaultVmpl = 2 SNPDefaultMeasurement = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // 48 bytes + + // These unsigned-integer measurement keys define the repository's local + // CoMID appraisal profile. They are not IETF-assigned code points. + SNPMeasurementMKey uint64 = 0x1000 + SNPHostDataMKey uint64 = 0x1001 + SNPPolicyMKey uint64 = 0x1002 + SNPMinimumLaunchTCBMKey uint64 = 0x1003 + TDXMRTDMKey uint64 = 0x2000 + TDXMRSEAMMKey uint64 = 0x2001 + TDXRTMR0MKey uint64 = 0x2010 + maximumTDXRuntimeRegisters = 4 ) // Legacy TDX Defaults. @@ -37,7 +51,7 @@ type Options struct { Platform string // "snp" or "tdx" Measurement string // Hex-encoded measurement Product string // SNP processor product name - SVN uint64 // Security Version Number + SVN uint64 // Exact SNP guest SVN; unsupported for TDX Policy uint64 // SNP policy flags RTMRs string // TDX RTMRs (comma-separated hex) MrSeam string // TDX MRSEAM (hex) @@ -71,6 +85,7 @@ func GenerateCoRIM(opts Options) ([]byte, error) { // Sign the CoRIM signedCorim := &corim.SignedCorim{} signedCorim.UnsignedCorim = *unsignedCorim + signedCorim.Meta = *corim.NewMeta().SetSigner("agents-secure-binding corimgen", nil) // Create COSE signer (use ES256 for ECDSA keys) signer, err := cose.NewSigner(cose.AlgorithmES256, opts.SigningKey) @@ -155,55 +170,76 @@ func createReferenceValue(opts Options) (*comid.ReferenceValue, error) { if err != nil { return nil, fmt.Errorf("failed to decode measurement: %w", err) } + measurementAlg, err := platformMeasurementAlgorithm(opts.Platform) + if err != nil { + return nil, err + } - // Create main measurement with UUID key - measUUID := uuid.New() - mval, err := comid.NewUUIDMeasurement(comid.UUID(measUUID)) + measurementKey, err := mainMeasurementKey(opts.Platform) + if err != nil { + return nil, err + } + mval, err := comid.NewUintMeasurement(measurementKey) if err != nil { return nil, fmt.Errorf("failed to create measurement: %w", err) } - // Add digest with SHA-256 algorithm (algID = 1) - mval.AddDigest(1, measBytes) + if mval.AddDigest(measurementAlg, measBytes) == nil { + return nil, fmt.Errorf("measurement must be a valid SHA-384 value (48 bytes)") + } // Add SVN if specified if opts.SVN > 0 { mval.SetSVN(opts.SVN) } - // Initialize measurements slice + // Initialize measurements slice with the platform's primary measurement. refVal.Measurements = comid.Measurements{*mval} + if opts.Platform == "snp" { + if err := addSNPReferenceValues(refVal, opts); err != nil { + return nil, err + } + } // Platform-specific additions if opts.Platform == "tdx" { + if opts.SVN != 0 { + return nil, fmt.Errorf("TDX scalar SVN is unsupported; use the platform policy's 16-byte minimum_tee_tcb_svn") + } // Add MRSEAM if opts.MrSeam != "" { mrSeamBytes, err := hex.DecodeString(opts.MrSeam) if err != nil { return nil, fmt.Errorf("failed to decode MRSEAM: %w", err) } - seamUUID := uuid.New() - seamMval, err := comid.NewUUIDMeasurement(comid.UUID(seamUUID)) + seamMval, err := comid.NewUintMeasurement(TDXMRSEAMMKey) if err != nil { return nil, fmt.Errorf("failed to create MRSEAM measurement: %w", err) } - seamMval.AddDigest(1, mrSeamBytes) + if seamMval.AddDigest(swid.Sha384, mrSeamBytes) == nil { + return nil, fmt.Errorf("MRSEAM must be a valid SHA-384 value (48 bytes)") + } refVal.Measurements = append(refVal.Measurements, *seamMval) } // Add RTMRs if opts.RTMRs != "" { - for _, rtmr := range strings.Split(opts.RTMRs, ",") { + rtmrs := strings.Split(opts.RTMRs, ",") + if len(rtmrs) > maximumTDXRuntimeRegisters { + return nil, fmt.Errorf("TDX CoRIM supports at most %d RTMR values", maximumTDXRuntimeRegisters) + } + for index, rtmr := range rtmrs { rtmrBytes, err := hex.DecodeString(strings.TrimSpace(rtmr)) if err != nil { return nil, fmt.Errorf("failed to decode RTMR: %w", err) } - rtmrUUID := uuid.New() - rtmrMval, err := comid.NewUUIDMeasurement(comid.UUID(rtmrUUID)) + rtmrMval, err := comid.NewUintMeasurement(TDXRTMR0MKey + uint64(index)) if err != nil { return nil, fmt.Errorf("failed to create RTMR measurement: %w", err) } - rtmrMval.AddDigest(1, rtmrBytes) + if rtmrMval.AddDigest(swid.Sha384, rtmrBytes) == nil { + return nil, fmt.Errorf("RTMR must be a valid SHA-384 value (48 bytes)") + } refVal.Measurements = append(refVal.Measurements, *rtmrMval) } } @@ -211,3 +247,73 @@ func createReferenceValue(opts Options) (*comid.ReferenceValue, error) { return refVal, nil } + +func mainMeasurementKey(platform string) (uint64, error) { + switch platform { + case "snp": + return SNPMeasurementMKey, nil + case "tdx": + return TDXMRTDMKey, nil + default: + return 0, fmt.Errorf("unsupported CoRIM platform %q", platform) + } +} + +func addSNPReferenceValues(refVal *comid.ReferenceValue, opts Options) error { + if opts.HostData != "" { + hostData, err := hex.DecodeString(opts.HostData) + if err != nil { + return fmt.Errorf("failed to decode SNP HOST_DATA: %w", err) + } + if len(hostData) != sevabi.HostDataSize { + return fmt.Errorf("SNP HOST_DATA must be %d bytes, got %d", sevabi.HostDataSize, len(hostData)) + } + measurement, err := newRawValueMeasurement(SNPHostDataMKey, hostData) + if err != nil { + return err + } + refVal.Measurements = append(refVal.Measurements, *measurement) + } + if opts.Policy != 0 { + measurement, err := newUint64RawValueMeasurement(SNPPolicyMKey, opts.Policy) + if err != nil { + return err + } + refVal.Measurements = append(refVal.Measurements, *measurement) + } + if opts.LaunchTCB != 0 { + measurement, err := newUint64RawValueMeasurement(SNPMinimumLaunchTCBMKey, opts.LaunchTCB) + if err != nil { + return err + } + refVal.Measurements = append(refVal.Measurements, *measurement) + } + return nil +} + +func newUint64RawValueMeasurement(key, value uint64) (*comid.Measurement, error) { + raw := make([]byte, 8) + binary.LittleEndian.PutUint64(raw, value) + return newRawValueMeasurement(key, raw) +} + +func newRawValueMeasurement(key uint64, raw []byte) (*comid.Measurement, error) { + measurement, err := comid.NewUintMeasurement(key) + if err != nil { + return nil, fmt.Errorf("failed to create keyed CoRIM measurement: %w", err) + } + if measurement.SetRawValueBytes(raw, nil) == nil { + return nil, fmt.Errorf("failed to set keyed CoRIM raw value") + } + return measurement, nil +} + +func platformMeasurementAlgorithm(platform string) (uint64, error) { + switch platform { + case "snp", "tdx": + // SNP MEASUREMENT and the TDX MRTD/RTMR fields are SHA-384 values. + return swid.Sha384, nil + default: + return 0, fmt.Errorf("unsupported CoRIM platform %q", platform) + } +} diff --git a/pkg/attestation/corimgen/generator_test.go b/pkg/attestation/corimgen/generator_test.go index 91de18d6..237f394c 100644 --- a/pkg/attestation/corimgen/generator_test.go +++ b/pkg/attestation/corimgen/generator_test.go @@ -3,17 +3,25 @@ package corimgen import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/binary" + "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" + "github.com/veraison/swid" ) func TestGenerateCoRIM_SNP_Unsigned(t *testing.T) { opts := Options{ Platform: "snp", - Measurement: "abc123", + Measurement: strings.Repeat("ab", 48), Product: "Milan", SVN: 1, } @@ -74,6 +82,43 @@ func TestGenerateCoRIM_InvalidMeasurement(t *testing.T) { assert.Contains(t, err.Error(), "failed to decode measurement") } +func TestGenerateCoRIM_RejectsWrongMeasurementLength(t *testing.T) { + _, err := GenerateCoRIM(Options{ + Platform: "snp", + Measurement: "abc123", + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "48 bytes") +} + +func TestGenerateCoRIM_UsesSHA384ForSNPMeasurement(t *testing.T) { + want := bytes.Repeat([]byte{0xab}, 48) + corimBytes, err := GenerateCoRIM(Options{ + Platform: "snp", + Measurement: strings.Repeat("ab", 48), + }) + require.NoError(t, err) + + var manifest corim.UnsignedCorim + require.NoError(t, manifest.FromCBOR(corimBytes)) + require.Len(t, manifest.Tags, 1) + require.True(t, bytes.HasPrefix(manifest.Tags[0], corim.ComidTag)) + + var tag comid.Comid + require.NoError(t, tag.FromCBOR(manifest.Tags[0][len(corim.ComidTag):])) + require.NotNil(t, tag.Triples.ReferenceValues) + require.Len(t, *tag.Triples.ReferenceValues, 1) + measurements := (*tag.Triples.ReferenceValues)[0].Measurements + require.Len(t, measurements, 1) + key, err := measurements[0].Key.GetKeyUint() + require.NoError(t, err) + assert.Equal(t, SNPMeasurementMKey, key) + require.NotNil(t, measurements[0].Val.Digests) + require.Len(t, *measurements[0].Val.Digests, 1) + assert.Equal(t, swid.Sha384, (*measurements[0].Val.Digests)[0].HashAlgID) + assert.Equal(t, want, (*measurements[0].Val.Digests)[0].HashValue) +} + func TestApplyDefaults_SNP(t *testing.T) { opts := Options{ Platform: "snp", @@ -105,7 +150,6 @@ func TestGenerateCoRIM_TDX_WithRTMRs(t *testing.T) { Measurement: TDXDefaultMrTd, MrSeam: TDXDefaultMrSeam, RTMRs: rtmr1 + "," + rtmr2, - SVN: 2, } corimBytes, err := GenerateCoRIM(opts) @@ -116,13 +160,16 @@ func TestGenerateCoRIM_TDX_WithRTMRs(t *testing.T) { var unsignedCorim corim.UnsignedCorim err = unsignedCorim.FromCBOR(corimBytes) require.NoError(t, err) + measurements := referenceMeasurements(t, &unsignedCorim) + assert.Equal(t, []uint64{TDXMRTDMKey, TDXMRSEAMMKey, TDXRTMR0MKey, TDXRTMR0MKey + 1}, measurementKeys(t, measurements)) } func TestGenerateCoRIM_SNP_WithHostData(t *testing.T) { opts := Options{ Platform: "snp", - Measurement: "abc123", - HostData: "deadbeef", + Measurement: strings.Repeat("ab", 48), + Policy: 0x30000, + HostData: strings.Repeat("cd", 32), LaunchTCB: 1, SVN: 1, } @@ -130,6 +177,36 @@ func TestGenerateCoRIM_SNP_WithHostData(t *testing.T) { corimBytes, err := GenerateCoRIM(opts) require.NoError(t, err) require.NotEmpty(t, corimBytes) + var manifest corim.UnsignedCorim + require.NoError(t, manifest.FromCBOR(corimBytes)) + measurements := referenceMeasurements(t, &manifest) + assert.Equal(t, []uint64{SNPMeasurementMKey, SNPHostDataMKey, SNPPolicyMKey, SNPMinimumLaunchTCBMKey}, measurementKeys(t, measurements)) + hostData, err := measurements[1].Val.RawValue.GetBytes() + require.NoError(t, err) + assert.Equal(t, bytes.Repeat([]byte{0xcd}, 32), hostData) + policy, err := measurements[2].Val.RawValue.GetBytes() + require.NoError(t, err) + assert.Equal(t, uint64(0x30000), binary.LittleEndian.Uint64(policy)) +} + +func TestGenerateCoRIMRejectsUnenforceableInputs(t *testing.T) { + _, err := GenerateCoRIM(Options{Platform: "snp", HostData: "deadbeef"}) + require.Error(t, err) + assert.Contains(t, err.Error(), "HOST_DATA must be 32 bytes") + + _, err = GenerateCoRIM(Options{Platform: "tdx", SVN: 1}) + require.Error(t, err) + assert.Contains(t, err.Error(), "scalar SVN is unsupported") + + _, err = GenerateCoRIM(Options{ + Platform: "tdx", + RTMRs: strings.Join([]string{ + strings.Repeat("00", 48), strings.Repeat("01", 48), strings.Repeat("02", 48), + strings.Repeat("03", 48), strings.Repeat("04", 48), + }, ","), + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "at most 4 RTMR") } func TestGenerateCoRIM_TDX_InvalidMrSeam(t *testing.T) { @@ -155,8 +232,40 @@ func TestGenerateCoRIM_TDX_InvalidRTMR(t *testing.T) { } func TestGenerateCoRIM_WithSigning(t *testing.T) { - // This would require a mock signer, but for now we can test that it - // fails if we provide something that looks like a key but is invalid or not fully supported - // However, we've already tested the unsigned paths which are the main focus. - t.Skip("Signing test requires mock signer") + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + payload, err := GenerateCoRIM(Options{ + Platform: "snp", + SigningKey: key, + }) + require.NoError(t, err) + + var signed corim.SignedCorim + require.NoError(t, signed.FromCOSE(payload)) + require.NoError(t, signed.Meta.Valid()) + require.NoError(t, signed.Verify(&key.PublicKey)) +} + +func referenceMeasurements(t *testing.T, manifest *corim.UnsignedCorim) comid.Measurements { + t.Helper() + require.NotNil(t, manifest) + require.Len(t, manifest.Tags, 1) + require.True(t, bytes.HasPrefix(manifest.Tags[0], corim.ComidTag)) + var tag comid.Comid + require.NoError(t, tag.FromCBOR(manifest.Tags[0][len(corim.ComidTag):])) + require.NotNil(t, tag.Triples.ReferenceValues) + require.Len(t, *tag.Triples.ReferenceValues, 1) + return (*tag.Triples.ReferenceValues)[0].Measurements +} + +func measurementKeys(t *testing.T, measurements comid.Measurements) []uint64 { + t.Helper() + keys := make([]uint64, len(measurements)) + for i := range measurements { + key, err := measurements[i].Key.GetKeyUint() + require.NoError(t, err) + keys[i] = key + } + return keys } diff --git a/pkg/attestation/eat/decoder.go b/pkg/attestation/eat/decoder.go index 517fb425..7654c300 100644 --- a/pkg/attestation/eat/decoder.go +++ b/pkg/attestation/eat/decoder.go @@ -7,6 +7,7 @@ import ( "bytes" "crypto/ecdsa" "encoding/json" + "errors" "fmt" "github.com/fxamacker/cbor/v2" @@ -14,6 +15,11 @@ import ( "github.com/veraison/go-cose" ) +var ( + ErrVerificationKeyRequired = errors.New("EAT verification key is required") + ErrSignedCBORRequired = errors.New("signed COSE EAT is required") +) + // Decoder decodes EAT tokens (auto-detects JWT vs CBOR). type Decoder struct { verifyKey *ecdsa.PublicKey @@ -79,6 +85,9 @@ func (d *Decoder) decodeCBOR(token []byte) (*EATClaims, error) { // Try to unmarshal as COSE_Sign1 message var msg cose.Sign1Message if err := msg.UnmarshalCBOR(token); err != nil { + if d.verifyKey != nil { + return nil, fmt.Errorf("%w: %v", ErrSignedCBORRequired, err) + } // If it's not a COSE message, try to decode as plain CBOR (backward compatibility) claims := &EATClaims{} if err := cbor.Unmarshal(token, claims); err != nil { @@ -108,6 +117,15 @@ func (d *Decoder) decodeCBOR(token []byte) (*EATClaims, error) { return claims, nil } +// DecodeVerifiedCBOR verifies a signed COSE EAT and decodes its claims. Unlike +// DecodeCBOR, it never accepts plain CBOR or an omitted verification key. +func DecodeVerifiedCBOR(token []byte, verifyKey *ecdsa.PublicKey) (*EATClaims, error) { + if verifyKey == nil { + return nil, ErrVerificationKeyRequired + } + return NewDecoder(verifyKey).decodeCBOR(token) +} + // DecodeJWT is a convenience function to decode JWT EAT token. func DecodeJWT(tokenString string, verifyKey *ecdsa.PublicKey) (*EATClaims, error) { decoder := NewDecoder(verifyKey) diff --git a/pkg/attestation/eat/decoder_test.go b/pkg/attestation/eat/decoder_test.go index 0c3dd653..0e9966f6 100644 --- a/pkg/attestation/eat/decoder_test.go +++ b/pkg/attestation/eat/decoder_test.go @@ -140,6 +140,15 @@ func TestDecodeCBOR(t *testing.T) { }, wantErr: false, }, + { + name: "Plain CBOR rejected when verification is requested", + args: args{ + token: payload, + verifyKey: &privateKey.PublicKey, + }, + wantErr: true, + expectedErr: ErrSignedCBORRequired.Error(), + }, { name: "Invalid COSE signature", args: args{ @@ -180,6 +189,32 @@ func TestDecodeCBOR(t *testing.T) { } } +func TestDecodeVerifiedCBORRequiresSignatureAndKey(t *testing.T) { + claims := &EATClaims{Nonce: []byte("test-nonce")} + payload, err := cbor.Marshal(claims) + require.NoError(t, err) + + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + signer, err := cose.NewSigner(cose.AlgorithmES256, privateKey) + require.NoError(t, err) + msg := cose.NewSign1Message() + msg.Payload = payload + require.NoError(t, msg.Sign(rand.Reader, nil, signer)) + token, err := msg.MarshalCBOR() + require.NoError(t, err) + + got, err := DecodeVerifiedCBOR(token, &privateKey.PublicKey) + require.NoError(t, err) + assert.Equal(t, claims.Nonce, got.Nonce) + + _, err = DecodeVerifiedCBOR(token, nil) + assert.ErrorIs(t, err, ErrVerificationKeyRequired) + + _, err = DecodeVerifiedCBOR(payload, &privateKey.PublicKey) + assert.ErrorIs(t, err, ErrSignedCBORRequired) +} + func TestDecodeAutoDetect(t *testing.T) { key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) claims := &EATClaims{Nonce: []byte("jwt")} diff --git a/pkg/attestation/eat/key.go b/pkg/attestation/eat/key.go new file mode 100644 index 00000000..feddf5e9 --- /dev/null +++ b/pkg/attestation/eat/key.go @@ -0,0 +1,116 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package eat + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "errors" + "fmt" + "os" +) + +var ( + ErrInvalidSigningKey = errors.New("invalid EAT signing key") + ErrInvalidVerificationKey = errors.New("invalid EAT verification key") +) + +// LoadSigningKey loads an ES256 private key from a PEM-encoded SEC 1 or PKCS#8 +// file. Private key files with group or other permission bits are rejected. +func LoadSigningKey(path string) (*ecdsa.PrivateKey, error) { + info, err := os.Stat(path) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrInvalidSigningKey, err) + } + if !info.Mode().IsRegular() { + return nil, fmt.Errorf("%w: not a regular file", ErrInvalidSigningKey) + } + if info.Mode().Perm()&0o077 != 0 { + return nil, fmt.Errorf("%w: private key file permissions must not grant group or other access", ErrInvalidSigningKey) + } + encoded, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrInvalidSigningKey, err) + } + block, _ := pem.Decode(encoded) + if block == nil { + return nil, fmt.Errorf("%w: PEM block not found", ErrInvalidSigningKey) + } + + if key, parseErr := x509.ParseECPrivateKey(block.Bytes); parseErr == nil { + return validateSigningKey(key) + } + parsed, parseErr := x509.ParsePKCS8PrivateKey(block.Bytes) + if parseErr != nil { + return nil, fmt.Errorf("%w: unsupported private key encoding", ErrInvalidSigningKey) + } + key, ok := parsed.(*ecdsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("%w: expected ECDSA private key", ErrInvalidSigningKey) + } + return validateSigningKey(key) +} + +// LoadVerificationKey loads an ES256 public key from a PEM-encoded PKIX public +// key or X.509 certificate. +func LoadVerificationKey(path string) (*ecdsa.PublicKey, error) { + encoded, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrInvalidVerificationKey, err) + } + block, _ := pem.Decode(encoded) + if block == nil { + return nil, fmt.Errorf("%w: PEM block not found", ErrInvalidVerificationKey) + } + + if parsed, parseErr := x509.ParsePKIXPublicKey(block.Bytes); parseErr == nil { + key, ok := parsed.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("%w: expected ECDSA public key", ErrInvalidVerificationKey) + } + return validateVerificationKey(key) + } + certificate, parseErr := x509.ParseCertificate(block.Bytes) + if parseErr != nil { + return nil, fmt.Errorf("%w: unsupported public key encoding", ErrInvalidVerificationKey) + } + key, ok := certificate.PublicKey.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("%w: certificate does not contain an ECDSA public key", ErrInvalidVerificationKey) + } + return validateVerificationKey(key) +} + +// VerificationKeyFingerprint returns the SHA-256 fingerprint of a public key's +// PKIX encoding. +func VerificationKeyFingerprint(key *ecdsa.PublicKey) (string, error) { + key, err := validateVerificationKey(key) + if err != nil { + return "", err + } + der, err := x509.MarshalPKIXPublicKey(key) + if err != nil { + return "", fmt.Errorf("marshal EAT verification key: %w", err) + } + digest := sha256.Sum256(der) + return hex.EncodeToString(digest[:]), nil +} + +func validateSigningKey(key *ecdsa.PrivateKey) (*ecdsa.PrivateKey, error) { + if key == nil || key.Curve != elliptic.P256() || key.D == nil { + return nil, fmt.Errorf("%w: ES256 requires a P-256 private key", ErrInvalidSigningKey) + } + return key, nil +} + +func validateVerificationKey(key *ecdsa.PublicKey) (*ecdsa.PublicKey, error) { + if key == nil || key.Curve != elliptic.P256() || key.X == nil || key.Y == nil || !key.Curve.IsOnCurve(key.X, key.Y) { + return nil, fmt.Errorf("%w: ES256 requires a valid P-256 public key", ErrInvalidVerificationKey) + } + return key, nil +} diff --git a/pkg/attestation/eat/key_test.go b/pkg/attestation/eat/key_test.go new file mode 100644 index 00000000..10a280b3 --- /dev/null +++ b/pkg/attestation/eat/key_test.go @@ -0,0 +1,106 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package eat + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "errors" + "os" + "path/filepath" + "testing" +) + +func TestLoadEATKeyPair(t *testing.T) { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + privateDER, err := x509.MarshalECPrivateKey(privateKey) + if err != nil { + t.Fatal(err) + } + publicDER, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) + if err != nil { + t.Fatal(err) + } + + privatePath := filepath.Join(t.TempDir(), "eat-signing-key.pem") + publicPath := filepath.Join(t.TempDir(), "eat-verification-key.pem") + if err := os.WriteFile(privatePath, pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privateDER}), 0o600); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(publicPath, pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: publicDER}), 0o644); err != nil { + t.Fatal(err) + } + + loadedPrivate, err := LoadSigningKey(privatePath) + if err != nil { + t.Fatal(err) + } + loadedPublic, err := LoadVerificationKey(publicPath) + if err != nil { + t.Fatal(err) + } + if loadedPrivate.D.Cmp(privateKey.D) != 0 || !loadedPublic.Equal(&privateKey.PublicKey) { + t.Fatal("loaded EAT keys do not match the encoded key pair") + } + fingerprint, err := VerificationKeyFingerprint(loadedPublic) + if err != nil { + t.Fatal(err) + } + if len(fingerprint) != 64 { + t.Fatalf("fingerprint length = %d, want 64", len(fingerprint)) + } +} + +func TestLoadSigningKeyRejectsBroadPermissions(t *testing.T) { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + der, err := x509.MarshalECPrivateKey(privateKey) + if err != nil { + t.Fatal(err) + } + path := filepath.Join(t.TempDir(), "insecure.pem") + if err := os.WriteFile(path, pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: der}), 0o644); err != nil { + t.Fatal(err) + } + if _, err := LoadSigningKey(path); !errors.Is(err, ErrInvalidSigningKey) { + t.Fatalf("LoadSigningKey() error = %v, want ErrInvalidSigningKey", err) + } +} + +func TestLoadEATKeysRejectP384(t *testing.T) { + privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + if err != nil { + t.Fatal(err) + } + privateDER, err := x509.MarshalECPrivateKey(privateKey) + if err != nil { + t.Fatal(err) + } + publicDER, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) + if err != nil { + t.Fatal(err) + } + privatePath := filepath.Join(t.TempDir(), "p384-private.pem") + publicPath := filepath.Join(t.TempDir(), "p384-public.pem") + if err := os.WriteFile(privatePath, pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privateDER}), 0o600); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(publicPath, pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: publicDER}), 0o644); err != nil { + t.Fatal(err) + } + if _, err := LoadSigningKey(privatePath); !errors.Is(err, ErrInvalidSigningKey) { + t.Fatalf("LoadSigningKey() error = %v, want ErrInvalidSigningKey", err) + } + if _, err := LoadVerificationKey(publicPath); !errors.Is(err, ErrInvalidVerificationKey) { + t.Fatalf("LoadVerificationKey() error = %v, want ErrInvalidVerificationKey", err) + } +} diff --git a/pkg/attestation/generator/README.md b/pkg/attestation/generator/README.md index 6612b701..4d3bd276 100644 --- a/pkg/attestation/generator/README.md +++ b/pkg/attestation/generator/README.md @@ -40,7 +40,7 @@ opts := generator.Options{ Product: "Genoa", SVN: 1, Policy: 0x30000, - HostData: "deadbeef", // hex string + HostData: "0000000000000000000000000000000000000000000000000000000000000000", LaunchTCB: 1, } corimBytes, err := generator.GenerateCoRIM(opts) @@ -52,7 +52,6 @@ corimBytes, err := generator.GenerateCoRIM(opts) opts := generator.Options{ Platform: "tdx", Measurement: "def456...", // MRTD hex string - SVN: 2, RTMRs: "rtmr0,rtmr1,rtmr2,rtmr3", // comma-separated hex MrSeam: "789abc...", // hex string } @@ -64,12 +63,12 @@ corimBytes, err := generator.GenerateCoRIM(opts) ### Common Fields - `Platform` (string): Platform type - "snp" or "tdx" - `Measurement` (string): Hex-encoded measurement (defaults provided if empty) -- `SVN` (uint64): Security Version Number ### SNP-Specific Fields +- `SVN` (uint64): Exact guest SVN when nonzero - `Product` (string): Processor product name (e.g., "Milan", "Genoa") - `Policy` (uint64): SNP policy flags -- `HostData` (string): Hex-encoded host data +- `HostData` (string): Exact 32-byte host data (64 hex characters) - `LaunchTCB` (uint64): Minimum launch TCB version ### TDX-Specific Fields @@ -81,14 +80,20 @@ corimBytes, err := generator.GenerateCoRIM(opts) ### SNP Defaults - Measurement: 48 bytes of zeros (if not provided) - Product: "Milan" -- SVN: 0 +- SVN: omitted when 0 - Policy: 0 ### TDX Defaults - Measurement (MRTD): `000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000` - MRSEAM: `2fd279c16164a93dd5bf373d834328d46008c2b693af9ebb865b08b2ced320c9a89b4869a9fab60fbe9d0c5a5363c656` - RTMRs: Four 48-byte zero values -- SVN: 0 + +TDX does not accept the scalar `SVN` option. Configure the 16-byte +`minimum_tee_tcb_svn` in the TDX platform policy instead. + +The generated CoMID uses repository-local unsigned-integer measurement keys so +the appraiser can distinguish SNP and TDX fields. These keys are not +IETF-assigned values or a general interoperability profile. ## Integration diff --git a/pkg/attestation/generator/generator.go b/pkg/attestation/generator/generator.go index 96b98a71..c22ee0a6 100644 --- a/pkg/attestation/generator/generator.go +++ b/pkg/attestation/generator/generator.go @@ -27,7 +27,7 @@ type Options struct { Platform string // "snp" or "tdx" Measurement string // Hex-encoded measurement Product string // SNP processor product name - SVN uint64 // Security Version Number + SVN uint64 // Exact SNP guest SVN; unsupported for TDX Policy uint64 // SNP policy flags RTMRs string // TDX RTMRs (comma-separated hex) MrSeam string // TDX MRSEAM (hex) diff --git a/pkg/attestation/tdx/tdx.go b/pkg/attestation/tdx/tdx.go index 5d17965e..0ac6bd81 100644 --- a/pkg/attestation/tdx/tdx.go +++ b/pkg/attestation/tdx/tdx.go @@ -8,20 +8,26 @@ package tdx import ( "bytes" "fmt" + "io" + "net/http" + "net/url" "os" "time" "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" "github.com/google/go-tdx-guest/abi" "github.com/google/go-tdx-guest/client" "github.com/google/go-tdx-guest/proto/checkconfig" + tdxpb "github.com/google/go-tdx-guest/proto/tdx" valdatetdx "github.com/google/go-tdx-guest/validate" verifytdx "github.com/google/go-tdx-guest/verify" trusttdx "github.com/google/go-tdx-guest/verify/trust" "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" + "github.com/veraison/swid" "google.golang.org/protobuf/encoding/protojson" ) @@ -33,10 +39,49 @@ var ( ) var ( - timeout = time.Minute * 2 - maxTryDelay = time.Second * 30 + timeout = time.Minute * 2 + maxTryDelay = time.Second * 30 + httpRequestTimeout = time.Second * 20 ) +const maxCollateralResponseBytes = 16 << 20 + +type boundedHTTPSGetter struct { + client *http.Client +} + +func (g *boundedHTTPSGetter) Get(rawURL string) (map[string][]string, []byte, error) { + parsed, err := url.Parse(rawURL) + if err != nil || parsed.Scheme != "https" || parsed.Host == "" { + return nil, nil, fmt.Errorf("TDX collateral URL must use HTTPS") + } + client := g.client + if client == nil { + client = &http.Client{Timeout: httpRequestTimeout} + } + request, err := http.NewRequest(http.MethodGet, parsed.String(), nil) + if err != nil { + return nil, nil, err + } + response, err := client.Do(request) + if err != nil { + return nil, nil, err + } + defer response.Body.Close() + if response.StatusCode >= http.StatusMultipleChoices { + return nil, nil, fmt.Errorf("failed to retrieve %s, status code received %d", parsed.Redacted(), response.StatusCode) + } + limited := io.LimitReader(response.Body, maxCollateralResponseBytes+1) + body, err := io.ReadAll(limited) + if err != nil { + return nil, nil, err + } + if len(body) > maxCollateralResponseBytes { + return nil, nil, fmt.Errorf("TDX collateral response exceeds %d bytes", maxCollateralResponseBytes) + } + return response.Header, body, nil +} + type provider struct{} func NewProvider() attestation.Provider { @@ -97,37 +142,49 @@ func NewVerifierWithPolicy(policy *checkconfig.Config) attestation.Verifier { } func (v verifier) VerifTeeAttestation(report []byte, teeNonce []byte) error { - if v.Policy == nil { + return VerifyAttestationWithPolicy(report, teeNonce, v.Policy) +} + +// VerifyAttestationWithPolicy authenticates a raw TDX quote and validates its +// fields against an explicit local policy. expectedReportData is always forced +// into the validation options so a policy file cannot omit the ASB session +// binding check. +func VerifyAttestationWithPolicy(report []byte, expectedReportData []byte, policy *checkconfig.Config) error { + if policy == nil || policy.RootOfTrust == nil || policy.Policy == nil { return fmt.Errorf("tdx policy is not provided") } + if len(expectedReportData) != abi.ReportDataSize { + return fmt.Errorf("invalid TDX REPORT_DATA length: expected %d bytes, got %d", abi.ReportDataSize, len(expectedReportData)) + } quote, err := abi.QuoteToProto(report) if err != nil { - return err + return fmt.Errorf("failed to parse TDX quote: %w", err) } - sopts, err := verifytdx.RootOfTrustToOptions(v.Policy.RootOfTrust) + sopts, err := verifytdx.RootOfTrustToOptions(policy.RootOfTrust) if err != nil { - return err + return fmt.Errorf("failed to configure TDX root of trust: %w", err) } sopts.Getter = &trusttdx.RetryHTTPSGetter{ Timeout: timeout, MaxRetryDelay: maxTryDelay, - Getter: &trusttdx.SimpleHTTPSGetter{}, + Getter: &boundedHTTPSGetter{}, } if err := verifytdx.TdxQuote(quote, sopts); err != nil { - return err + return fmt.Errorf("TDX cryptographic verification failed: %w", err) } - opts, err := valdatetdx.PolicyToOptions(v.Policy.Policy) + opts, err := valdatetdx.PolicyToOptions(policy.Policy) if err != nil { - return err + return fmt.Errorf("failed to configure TDX validation policy: %w", err) } + opts.TdQuoteBodyOptions.ReportData = append([]byte(nil), expectedReportData...) if err := valdatetdx.TdxQuote(quote, opts); err != nil { - return err + return fmt.Errorf("TDX policy validation failed: %w", err) } return nil @@ -162,13 +219,16 @@ func (v verifier) VerifyWithCoRIM(report []byte, manifest *corim.UnsignedCorim) return fmt.Errorf("CoRIM manifest is nil") } - // 1. Extract MRTD manually - if len(report) < 160 { - return fmt.Errorf("TDX report too small to extract MRTD") + quote, err := abi.QuoteToProto(report) + if err != nil { + return fmt.Errorf("failed to parse TDX quote for CoRIM appraisal: %w", err) + } + quoteV4, ok := quote.(*tdxpb.QuoteV4) + if !ok || quoteV4.GetTdQuoteBody() == nil { + return fmt.Errorf("unsupported TDX quote format for CoRIM appraisal") } - // MRTD is at offset 112, 48 bytes - mrtd := make([]byte, 48) - copy(mrtd, report[112:160]) + body := quoteV4.GetTdQuoteBody() + referenceProfiles := 0 // Iterate over CoMIDs tags looking for measurements for _, tag := range manifest.Tags { @@ -185,27 +245,113 @@ func (v verifier) VerifyWithCoRIM(report []byte, manifest *corim.UnsignedCorim) return fmt.Errorf("failed to parse CoMID from tag: %w", err) } - // Match measurements in CoMID + // Match measurements in CoMID. A successful MRTD match is not enough: + // every supplied keyed TDX constraint must be understood and match its + // corresponding QuoteV4 field before the manifest is accepted. if c.Triples.ReferenceValues != nil { for _, rv := range *c.Triples.ReferenceValues { + referenceProfiles++ + if referenceProfiles > 1 { + return fmt.Errorf("TDX CoRIM must contain exactly one reference-value profile") + } if err := rv.Measurements.Valid(); err != nil { return fmt.Errorf("invalid CoRIM measurements for TDX: %w", err) } + seen := make(map[uint64]struct{}, len(rv.Measurements)) for _, m := range rv.Measurements { - if m.Val.Digests == nil { - continue - } - for _, digest := range *m.Val.Digests { - if bytes.Equal(digest.HashValue, mrtd) { - return nil - } + if err := appraiseTDXMeasurement(m, body, seen); err != nil { + return err } } + if _, ok := seen[corimgen.TDXMRTDMKey]; !ok { + return fmt.Errorf("TDX CoRIM is missing the required MRTD measurement key %d", corimgen.TDXMRTDMKey) + } } } } - return fmt.Errorf("no matching reference value found in CoRIM for TDX") + if referenceProfiles == 0 { + return fmt.Errorf("TDX CoRIM is missing the required MRTD measurement key %d", corimgen.TDXMRTDMKey) + } + + return nil +} + +func appraiseTDXMeasurement(m comid.Measurement, body *tdxpb.TDQuoteBody, seen map[uint64]struct{}) error { + if m.Key == nil { + return fmt.Errorf("TDX CoRIM measurement key is required") + } + key, err := m.Key.GetKeyUint() + if err != nil { + return fmt.Errorf("TDX CoRIM measurement key must be an unsigned integer: %w", err) + } + if _, duplicate := seen[key]; duplicate { + return fmt.Errorf("duplicate TDX CoRIM measurement key %d", key) + } + + expected, field, ok := tdxQuoteFieldForMeasurementKey(body, key) + if !ok { + return fmt.Errorf("unknown TDX CoRIM measurement key %d", key) + } + if m.AuthorizedBy != nil { + return fmt.Errorf("TDX CoRIM measurement key %d contains unsupported authorized-by metadata", key) + } + if hasUnsupportedTDXMeasurementValue(m.Val) { + return fmt.Errorf("TDX CoRIM measurement key %d contains unsupported measurement values", key) + } + if m.Val.Digests == nil || len(*m.Val.Digests) != 1 { + return fmt.Errorf("TDX CoRIM measurement key %d must contain exactly one digest", key) + } + + digest := (*m.Val.Digests)[0] + if digest.HashAlgID != swid.Sha384 { + return fmt.Errorf("TDX CoRIM measurement key %d must use SHA-384", key) + } + if len(digest.HashValue) != abi.MrTdSize { + return fmt.Errorf("TDX CoRIM measurement key %d must contain a 48-byte digest", key) + } + if len(expected) != abi.MrTdSize { + return fmt.Errorf("TDX quote field %s has invalid length %d", field, len(expected)) + } + if !bytes.Equal(digest.HashValue, expected) { + return fmt.Errorf("TDX CoRIM %s measurement does not match the quote", field) + } + + seen[key] = struct{}{} + return nil +} + +func tdxQuoteFieldForMeasurementKey(body *tdxpb.TDQuoteBody, key uint64) ([]byte, string, bool) { + switch key { + case corimgen.TDXMRTDMKey: + return body.GetMrTd(), "MRTD", true + case corimgen.TDXMRSEAMMKey: + return body.GetMrSeam(), "MRSEAM", true + } + if key >= corimgen.TDXRTMR0MKey && key < corimgen.TDXRTMR0MKey+4 { + index := int(key - corimgen.TDXRTMR0MKey) + rtmrs := body.GetRtmrs() + if index >= len(rtmrs) { + return nil, fmt.Sprintf("RTMR%d", index), true + } + return rtmrs[index], fmt.Sprintf("RTMR%d", index), true + } + return nil, "", false +} + +func hasUnsupportedTDXMeasurementValue(value comid.Mval) bool { + return value.Ver != nil || + value.SVN != nil || + value.Flags != nil || + value.RawValue != nil || + value.RawValueMask != nil || + value.MACAddr != nil || + value.IPAddr != nil || + value.SerialNumber != nil || + value.UEID != nil || + value.UUID != nil || + value.IntegrityRegisters != nil || + value.GetExtensions() != nil } func ReadTDXAttestationPolicy(policyPath string, policy *checkconfig.Config) error { diff --git a/pkg/attestation/tdx/tdx_test.go b/pkg/attestation/tdx/tdx_test.go index 60312642..693e003a 100644 --- a/pkg/attestation/tdx/tdx_test.go +++ b/pkg/attestation/tdx/tdx_test.go @@ -4,13 +4,19 @@ package tdx import ( + "encoding/hex" "os" "path/filepath" "runtime" + "strings" "testing" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" + "github.com/google/go-tdx-guest/abi" "github.com/google/go-tdx-guest/proto/checkconfig" + "github.com/google/go-tdx-guest/proto/tdx" + tdxtestdata "github.com/google/go-tdx-guest/testing/testdata" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/veraison/corim/comid" @@ -366,6 +372,27 @@ func TestVerifier_VerifTeeAttestation(t *testing.T) { } } +func TestVerifyAttestationWithPolicyBindsReportData(t *testing.T) { + quoteAny, err := abi.QuoteToProto(tdxtestdata.RawQuote) + require.NoError(t, err) + quote, ok := quoteAny.(*tdx.QuoteV4) + require.True(t, ok) + expected := append([]byte(nil), quote.GetTdQuoteBody().GetReportData()...) + policy := &checkconfig.Config{ + RootOfTrust: &checkconfig.RootOfTrust{}, + Policy: &checkconfig.Policy{ + HeaderPolicy: &checkconfig.HeaderPolicy{}, + TdQuoteBodyPolicy: &checkconfig.TDQuoteBodyPolicy{}, + }, + } + + require.NoError(t, VerifyAttestationWithPolicy(tdxtestdata.RawQuote, expected, policy)) + expected[0] ^= 0xff + err = VerifyAttestationWithPolicy(tdxtestdata.RawQuote, expected, policy) + require.Error(t, err) + assert.Contains(t, err.Error(), "REPORT_DATA") +} + func TestVerifier_VerifVTpmAttestation(t *testing.T) { tests := []struct { name string @@ -632,59 +659,232 @@ func TestReadTDXAttestationPolicy(t *testing.T) { } } +func tdxCoRIMTestQuote(t *testing.T) ([]byte, *tdx.TDQuoteBody) { + t.Helper() + report := append([]byte(nil), tdxtestdata.RawQuote...) + parsed, err := abi.QuoteToProto(report) + require.NoError(t, err) + quoteV4, ok := parsed.(*tdx.QuoteV4) + require.True(t, ok) + require.NotNil(t, quoteV4.GetTdQuoteBody()) + return report, quoteV4.GetTdQuoteBody() +} + +func tdxCoRIMMeasurement(t *testing.T, key uint64, digest []byte) comid.Measurement { + t.Helper() + measurement, err := comid.NewUintMeasurement(key) + require.NoError(t, err) + require.NotNil(t, measurement.AddDigest(swid.Sha384, append([]byte(nil), digest...))) + return *measurement +} + +func allTDXCoRIMMeasurements(t *testing.T, body *tdx.TDQuoteBody) []comid.Measurement { + t.Helper() + measurements := []comid.Measurement{ + tdxCoRIMMeasurement(t, corimgen.TDXMRTDMKey, body.GetMrTd()), + tdxCoRIMMeasurement(t, corimgen.TDXMRSEAMMKey, body.GetMrSeam()), + } + require.Len(t, body.GetRtmrs(), 4) + for index, rtmr := range body.GetRtmrs() { + measurements = append(measurements, tdxCoRIMMeasurement(t, corimgen.TDXRTMR0MKey+uint64(index), rtmr)) + } + return measurements +} + +func tdxCoRIMManifest(measurements ...comid.Measurement) *corim.UnsignedCorim { + tag := comid.NewComid(). + SetTagIdentity("tdx-test-tag", 0). + AddReferenceValue(comid.ReferenceValue{ + Environment: comid.Environment{ + Class: comid.NewClassOID(comid.TestOID), + Instance: comid.MustNewUEIDInstance(comid.TestUEID), + }, + Measurements: measurements, + }) + manifest := corim.NewUnsignedCorim() + manifest.AddComid(*tag) + return manifest +} + func TestVerifier_VerifyWithCoRIM(t *testing.T) { v := verifier{} - // 1. Report too small err := v.VerifyWithCoRIM([]byte("small"), &corim.UnsignedCorim{}) - assert.Error(t, err) - assert.Contains(t, err.Error(), "TDX report too small") + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to parse TDX quote") - // 2. No tags in CoRIM - report := make([]byte, 160) + report, body := tdxCoRIMTestQuote(t) err = v.VerifyWithCoRIM(report, &corim.UnsignedCorim{}) - assert.Error(t, err) - assert.Contains(t, err.Error(), "no matching reference value found in CoRIM for TDX") + require.Error(t, err) + assert.Contains(t, err.Error(), "missing the required MRTD") - // 3. With non-comid tag - manifest := &corim.UnsignedCorim{ + err = v.VerifyWithCoRIM(report, &corim.UnsignedCorim{ Tags: []corim.Tag{corim.Tag("not-a-comid")}, - } - err = v.VerifyWithCoRIM(report, manifest) - assert.Error(t, err) - assert.Contains(t, err.Error(), "no matching reference value found in CoRIM for TDX") + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "missing the required MRTD") - // 4. With invalid comid tag - manifest = &corim.UnsignedCorim{ + err = v.VerifyWithCoRIM(report, &corim.UnsignedCorim{ Tags: []corim.Tag{append(corim.ComidTag, []byte("invalid")...)}, - } - err = v.VerifyWithCoRIM(report, manifest) - assert.Error(t, err) + }) + require.Error(t, err) assert.Contains(t, err.Error(), "failed to parse CoMID from tag") - // 5. Successful MRTD match - mrtd := make([]byte, 48) - for i := range mrtd { - mrtd[i] = byte(i + 1) + require.NoError(t, v.VerifyWithCoRIM(report, tdxCoRIMManifest( + tdxCoRIMMeasurement(t, corimgen.TDXMRTDMKey, body.GetMrTd()), + ))) + require.NoError(t, v.VerifyWithCoRIM(report, tdxCoRIMManifest(allTDXCoRIMMeasurements(t, body)...))) +} + +func TestVerifier_VerifyWithGeneratedTDXCoRIM(t *testing.T) { + report, body := tdxCoRIMTestQuote(t) + rtmrs := make([]string, 0, len(body.GetRtmrs())) + for _, rtmr := range body.GetRtmrs() { + rtmrs = append(rtmrs, hex.EncodeToString(rtmr)) } - copy(report[112:160], mrtd) + payload, err := corimgen.GenerateCoRIM(corimgen.Options{ + Platform: "tdx", + Measurement: hex.EncodeToString(body.GetMrTd()), + MrSeam: hex.EncodeToString(body.GetMrSeam()), + RTMRs: strings.Join(rtmrs, ","), + }) + require.NoError(t, err) - c := comid.NewComid(). - SetTagIdentity("tdx-test-tag", 0). - AddReferenceValue(comid.ReferenceValue{ - Environment: comid.Environment{ - Class: comid.NewClassOID(comid.TestOID), - Instance: comid.MustNewUEIDInstance(comid.TestUEID), - }, - Measurements: *comid.NewMeasurements(). - AddMeasurement(comid.MustNewUUIDMeasurement(comid.TestUUID).AddDigest(swid.Sha3_384, mrtd)), + var manifest corim.UnsignedCorim + require.NoError(t, manifest.FromCBOR(payload)) + require.NoError(t, (verifier{}).VerifyWithCoRIM(report, &manifest)) +} + +func TestVerifier_VerifyWithCoRIMRejectsFieldMismatch(t *testing.T) { + v := verifier{} + report, body := tdxCoRIMTestQuote(t) + tests := []struct { + name string + measurementIndex int + field string + }{ + {name: "mrtd", measurementIndex: 0, field: "MRTD"}, + {name: "mrseam", measurementIndex: 1, field: "MRSEAM"}, + {name: "rtmr0", measurementIndex: 2, field: "RTMR0"}, + {name: "rtmr1", measurementIndex: 3, field: "RTMR1"}, + {name: "rtmr2", measurementIndex: 4, field: "RTMR2"}, + {name: "rtmr3", measurementIndex: 5, field: "RTMR3"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + measurements := allTDXCoRIMMeasurements(t, body) + digest := (*measurements[test.measurementIndex].Val.Digests)[0].HashValue + digest[0] ^= 0xff + err := v.VerifyWithCoRIM(report, tdxCoRIMManifest(measurements...)) + require.Error(t, err) + assert.Contains(t, err.Error(), test.field+" measurement does not match") + }) + } +} + +func TestVerifier_VerifyWithCoRIMDoesNotTreatOtherFieldsAsMRTD(t *testing.T) { + v := verifier{} + report, body := tdxCoRIMTestQuote(t) + for _, test := range []struct { + name string + key uint64 + field string + }{ + {name: "mrseam", key: corimgen.TDXMRSEAMMKey, field: "MRSEAM"}, + {name: "rtmr0", key: corimgen.TDXRTMR0MKey, field: "RTMR0"}, + } { + t.Run(test.name, func(t *testing.T) { + manifest := tdxCoRIMManifest(tdxCoRIMMeasurement(t, test.key, body.GetMrTd())) + err := v.VerifyWithCoRIM(report, manifest) + require.Error(t, err) + assert.Contains(t, err.Error(), test.field+" measurement does not match") }) + } +} + +func TestVerifier_VerifyWithCoRIMRejectsInvalidKeys(t *testing.T) { + v := verifier{} + report, body := tdxCoRIMTestQuote(t) + mrtd := tdxCoRIMMeasurement(t, corimgen.TDXMRTDMKey, body.GetMrTd()) + + t.Run("unknown", func(t *testing.T) { + unknown := tdxCoRIMMeasurement(t, corimgen.TDXRTMR0MKey+4, body.GetMrTd()) + err := v.VerifyWithCoRIM(report, tdxCoRIMManifest(mrtd, unknown)) + require.Error(t, err) + assert.Contains(t, err.Error(), "unknown TDX CoRIM measurement key") + }) + + t.Run("duplicate", func(t *testing.T) { + duplicate := tdxCoRIMMeasurement(t, corimgen.TDXMRTDMKey, body.GetMrTd()) + err := v.VerifyWithCoRIM(report, tdxCoRIMManifest(mrtd, duplicate)) + require.Error(t, err) + assert.Contains(t, err.Error(), "duplicate TDX CoRIM measurement key") + }) + + t.Run("unkeyed", func(t *testing.T) { + unkeyed := tdxCoRIMMeasurement(t, corimgen.TDXMRTDMKey, body.GetMrTd()) + unkeyed.Key = nil + err := v.VerifyWithCoRIM(report, tdxCoRIMManifest(unkeyed)) + require.Error(t, err) + assert.Contains(t, err.Error(), "measurement key is required") + }) + + t.Run("non-integer", func(t *testing.T) { + nonInteger := *comid.MustNewUUIDMeasurement(comid.TestUUID).AddDigest(swid.Sha384, body.GetMrTd()) + err := v.VerifyWithCoRIM(report, tdxCoRIMManifest(mrtd, nonInteger)) + require.Error(t, err) + assert.Contains(t, err.Error(), "must be an unsigned integer") + }) +} - unsignedCorim := corim.NewUnsignedCorim() - unsignedCorim.AddComid(*c) +func TestVerifier_VerifyWithCoRIMRejectsMultipleReferenceProfiles(t *testing.T) { + report, body := tdxCoRIMTestQuote(t) + measurement := tdxCoRIMMeasurement(t, corimgen.TDXMRTDMKey, body.GetMrTd()) + reference := comid.ReferenceValue{ + Environment: comid.Environment{Class: comid.NewClassOID(comid.TestOID)}, + Measurements: comid.Measurements{measurement}, + } + tag := comid.NewComid(). + SetTagIdentity("tdx-multiple-reference-values", 0). + AddReferenceValue(reference). + AddReferenceValue(reference) + manifest := corim.NewUnsignedCorim() + manifest.AddComid(*tag) + + err := (verifier{}).VerifyWithCoRIM(report, manifest) + require.Error(t, err) + assert.Contains(t, err.Error(), "exactly one reference-value profile") +} - err = v.VerifyWithCoRIM(report, unsignedCorim) - assert.NoError(t, err) +func TestVerifier_VerifyWithCoRIMRejectsUnsupportedValuesAndDigests(t *testing.T) { + v := verifier{} + report, body := tdxCoRIMTestQuote(t) + + t.Run("svn", func(t *testing.T) { + measurement := tdxCoRIMMeasurement(t, corimgen.TDXMRTDMKey, body.GetMrTd()) + measurement.SetSVN(1) + err := v.VerifyWithCoRIM(report, tdxCoRIMManifest(measurement)) + require.Error(t, err) + assert.Contains(t, err.Error(), "unsupported measurement values") + }) + + t.Run("multiple digests", func(t *testing.T) { + measurement := tdxCoRIMMeasurement(t, corimgen.TDXMRTDMKey, body.GetMrTd()) + second := append([]byte(nil), body.GetMrTd()...) + second[0] ^= 0xff + require.NotNil(t, measurement.AddDigest(swid.Sha384, second)) + err := v.VerifyWithCoRIM(report, tdxCoRIMManifest(measurement)) + require.Error(t, err) + assert.Contains(t, err.Error(), "exactly one digest") + }) + + t.Run("wrong algorithm", func(t *testing.T) { + measurement := comid.MustNewUintMeasurement(corimgen.TDXMRTDMKey) + require.NotNil(t, measurement.AddDigest(swid.Sha3_384, body.GetMrTd())) + err := v.VerifyWithCoRIM(report, tdxCoRIMManifest(*measurement)) + require.Error(t, err) + assert.Contains(t, err.Error(), "must use SHA-384") + }) } func TestVerifier_VerifyEAT(t *testing.T) { diff --git a/pkg/attestation/vtpm/sev.go b/pkg/attestation/vtpm/sev.go index 25fb51f8..10000a3c 100644 --- a/pkg/attestation/vtpm/sev.go +++ b/pkg/attestation/vtpm/sev.go @@ -39,23 +39,28 @@ func getLeveledQuoteProvider() (client.LeveledQuoteProvider, error) { // fetchSEVAttestation fetches a SEV-SNP attestation report. func fetchSEVAttestation(reportDataSlice []byte, vmpl uint) ([]byte, error) { var reportData [SEVNonce]byte + if len(reportDataSlice) != len(reportData) { + return nil, fmt.Errorf("invalid SEV-SNP REPORT_DATA length: expected %d bytes, got %d", len(reportData), len(reportDataSlice)) + } + copy(reportData[:], reportDataSlice) qp, err := getLeveledQuoteProvider() if err != nil { - return []byte{}, fmt.Errorf("could not get quote provider") - } - - if len(reportData) > SEVNonce { - return []byte{}, fmt.Errorf("attestation report size mismatch") + return nil, fmt.Errorf("could not get SEV-SNP quote provider: %w", err) } - copy(reportData[:], reportDataSlice) quoteProto, err := client.GetQuoteProtoAtLevel(qp, reportData, vmpl) if err != nil { - return []byte{}, fmt.Errorf("failed to get quote proto") + return nil, fmt.Errorf("failed to get SEV-SNP quote: %w", err) + } + if quoteProto.GetProduct() == nil { + return nil, fmt.Errorf("SEV-SNP quote is missing product information") } - homePath, _ := os.UserHomeDir() + homePath, err := os.UserHomeDir() + if err != nil { + return nil, fmt.Errorf("resolve certificate cache home directory: %w", err) + } vcekPath := path.Join(homePath, certCacheDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), vcekName) arkAskBundlePath := path.Join(homePath, certCacheDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), arkAskBundleName) @@ -69,13 +74,11 @@ func fetchSEVAttestation(reportDataSlice []byte, vmpl uint) ([]byte, error) { return []byte{}, fmt.Errorf("could not read ask/ark bundle file: %v", err) } - vcekPem, _ := pem.Decode(vcekBytes) - arkPem, rest := pem.Decode(arkAskBundleBytes) - askPem, _ := pem.Decode(rest) - - quoteProto.CertificateChain.VcekCert = vcekPem.Bytes - quoteProto.CertificateChain.AskCert = askPem.Bytes - quoteProto.CertificateChain.ArkCert = arkPem.Bytes + certificateChain, err := decodeCachedSEVCertificateChain(vcekBytes, arkAskBundleBytes) + if err != nil { + return nil, err + } + quoteProto.CertificateChain = certificateChain result, err := proto.Marshal(quoteProto) if err != nil { @@ -85,6 +88,28 @@ func fetchSEVAttestation(reportDataSlice []byte, vmpl uint) ([]byte, error) { return result, nil } +// decodeCachedSEVCertificateChain decodes the cache format written by +// FetchSEVCertificates: one VCEK PEM file and an ASK-then-ARK PEM bundle. +func decodeCachedSEVCertificateChain(vcekBytes, askArkBundleBytes []byte) (*sevsnp.CertificateChain, error) { + vcekPEM, _ := pem.Decode(vcekBytes) + if vcekPEM == nil { + return nil, fmt.Errorf("cached VCEK is not valid PEM") + } + askPEM, rest := pem.Decode(askArkBundleBytes) + if askPEM == nil { + return nil, fmt.Errorf("cached ASK/ARK bundle is missing ASK PEM") + } + arkPEM, _ := pem.Decode(rest) + if arkPEM == nil { + return nil, fmt.Errorf("cached ASK/ARK bundle is missing ARK PEM") + } + return &sevsnp.CertificateChain{ + VcekCert: vcekPEM.Bytes, + AskCert: askPEM.Bytes, + ArkCert: arkPEM.Bytes, + }, nil +} + // GetSEVProductName maps a product string to a SEV product name. func GetSEVProductName(product string) sevsnp.SevProduct_SevProductName { switch product { @@ -113,21 +138,19 @@ func FetchSEVCertificates(vmpl uint) error { qp, err := getLeveledQuoteProvider() if err != nil { - return fmt.Errorf("could not get quote provider") - } - - if len(reportData) > SEVNonce { - return fmt.Errorf("attestation report size mismatch") + return fmt.Errorf("could not get SEV-SNP quote provider: %w", err) } - _, err = rand.Read(reportData[:]) - if err != nil { - return fmt.Errorf("failed to read random data: %v", err) + if _, err := rand.Read(reportData[:]); err != nil { + return fmt.Errorf("failed to create SEV-SNP certificate-fetch challenge: %w", err) } quoteProto, err := client.GetQuoteProtoAtLevel(qp, reportData, vmpl) // for coverage if err != nil { - return fmt.Errorf("failed to get quote proto") + return fmt.Errorf("failed to get SEV-SNP quote for certificate fetch: %w", err) + } + if quoteProto.GetProduct() == nil || quoteProto.GetReport() == nil { + return fmt.Errorf("SEV-SNP quote is missing product or report information") } options := &verify.Options{ @@ -141,10 +164,16 @@ func FetchSEVCertificates(vmpl uint) error { result, err := verify.GetAttestationFromReport(quoteProto.Report, options) if err != nil { - return fmt.Errorf("could not get fetch certificates: %v", err) + return fmt.Errorf("fetch SEV-SNP certificates: %w", err) + } + if result.GetCertificateChain() == nil { + return fmt.Errorf("fetched SEV-SNP attestation is missing its certificate chain") } - homePath, _ := os.UserHomeDir() + homePath, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("resolve certificate cache home directory: %w", err) + } vcekPath := path.Join(homePath, certCacheDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), vcekName) arkAskBundlePath := path.Join(homePath, certCacheDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), arkAskBundleName) @@ -156,24 +185,20 @@ func FetchSEVCertificates(vmpl uint) error { arkAskBundlePem := append(askPem, arkPem...) vcekDir := filepath.Dir(vcekPath) - err = os.MkdirAll(vcekDir, 0o755) - if err != nil { - return fmt.Errorf("could not create VCEK directory: %v", err) + if err := os.MkdirAll(vcekDir, 0o755); err != nil { + return fmt.Errorf("could not create VCEK directory: %w", err) } askArkBundleDir := filepath.Dir(arkAskBundlePath) - err = os.MkdirAll(askArkBundleDir, 0o755) - if err != nil { - return fmt.Errorf("could not create ask/ark bundle directory: %v", err) + if err := os.MkdirAll(askArkBundleDir, 0o755); err != nil { + return fmt.Errorf("could not create ASK/ARK bundle directory: %w", err) } - err = os.WriteFile(vcekPath, vcekPem, 0o644) - if err != nil { - return fmt.Errorf("could not write VCEK file: %v", err) + if err := os.WriteFile(vcekPath, vcekPem, 0o644); err != nil { + return fmt.Errorf("could not write VCEK file: %w", err) } - err = os.WriteFile(arkAskBundlePath, arkAskBundlePem, 0o644) - if err != nil { - return fmt.Errorf("could not write ark/ask bundle file: %v", err) + if err := os.WriteFile(arkAskBundlePath, arkAskBundlePem, 0o644); err != nil { + return fmt.Errorf("could not write ASK/ARK bundle file: %w", err) } return nil diff --git a/pkg/attestation/vtpm/vtpm.go b/pkg/attestation/vtpm/vtpm.go index 20938a2d..ff9fa8d2 100644 --- a/pkg/attestation/vtpm/vtpm.go +++ b/pkg/attestation/vtpm/vtpm.go @@ -5,12 +5,15 @@ package vtpm import ( "bytes" + "encoding/binary" "fmt" "io" "os" "github.com/ToppyMicroServices/agents-secure-binding/v2/internal/errors" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" + "github.com/google/go-sev-guest/kds" "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/proto/attest" @@ -18,6 +21,7 @@ import ( "github.com/google/go-tpm/tpmutil" "github.com/veraison/corim/comid" "github.com/veraison/corim/corim" + "github.com/veraison/swid" "golang.org/x/crypto/sha3" "google.golang.org/protobuf/proto" ) @@ -146,7 +150,11 @@ func (v *verifier) VerifyWithCoRIM(report []byte, manifest *corim.UnsignedCorim) return fmt.Errorf("no SEV-SNP attestation found in report") } - measurement := snp.GetReport().GetMeasurement() + snpReport := snp.GetReport() + if snpReport == nil { + return fmt.Errorf("SEV-SNP attestation is missing its report") + } + measurement := snpReport.GetMeasurement() if len(measurement) == 0 { return fmt.Errorf("no measurement in SEV-SNP report") } @@ -165,27 +173,163 @@ func (v *verifier) VerifyWithCoRIM(report []byte, manifest *corim.UnsignedCorim) return fmt.Errorf("failed to parse CoMID from tag: %w", err) } - // Match measurements in CoMID + // Match one complete, field-aware reference-value profile in the CoMID. if c.Triples.ReferenceValues != nil { for _, rv := range *c.Triples.ReferenceValues { if err := rv.Measurements.Valid(); err != nil { - return fmt.Errorf("invalid CoRIM measurements for vTPM: %w", err) + return fmt.Errorf("invalid CoRIM measurements for SNP: %w", err) + } + matched, err := matchSNPReferenceValue(snpReport, rv.Measurements) + if err != nil { + return err } - for _, m := range rv.Measurements { - if m.Val.Digests == nil { - continue - } - for _, digest := range *m.Val.Digests { - if bytes.Equal(digest.HashValue, measurement) { - return nil // Match found - } - } + if matched { + return nil } } } } - return fmt.Errorf("no matching reference value found in CoRIM for vTPM") + return fmt.Errorf("no matching reference value found in CoRIM for SNP") +} + +func matchSNPReferenceValue(report *sevsnp.Report, measurements comid.Measurements) (bool, error) { + seen := make(map[uint64]struct{}, len(measurements)) + matched := true + foundMeasurement := false + for _, measurement := range measurements { + if measurement.AuthorizedBy != nil { + return false, fmt.Errorf("SNP CoRIM measurement contains unsupported authorized-by metadata") + } + if measurement.Key == nil { + return false, fmt.Errorf("SNP CoRIM measurement key is required") + } + key, err := measurement.Key.GetKeyUint() + if err != nil { + return false, fmt.Errorf("SNP CoRIM measurement key must use the ASB uint profile: %w", err) + } + if _, duplicate := seen[key]; duplicate { + return false, fmt.Errorf("duplicate SNP CoRIM measurement key %#x", key) + } + seen[key] = struct{}{} + + switch key { + case corimgen.SNPMeasurementMKey: + foundMeasurement = true + valueMatched, err := matchSNPDigestAndSVN(report, measurement.Val) + if err != nil { + return false, err + } + matched = matched && valueMatched + case corimgen.SNPHostDataMKey: + value, err := exactRawValue(measurement.Val, "SNP HOST_DATA") + if err != nil { + return false, err + } + if len(value) != Hash256 { + return false, fmt.Errorf("SNP HOST_DATA must be %d bytes", Hash256) + } + matched = matched && bytes.Equal(report.GetHostData(), value) + case corimgen.SNPPolicyMKey: + value, err := exactRawUint64Value(measurement.Val, "SNP policy") + if err != nil { + return false, err + } + matched = matched && report.GetPolicy() == value + case corimgen.SNPMinimumLaunchTCBMKey: + value, err := exactRawUint64Value(measurement.Val, "SNP minimum launch TCB") + if err != nil { + return false, err + } + minimum := kds.DecomposeTCBVersion(kds.TCBVersion(value)) + actual := kds.DecomposeTCBVersion(kds.TCBVersion(report.GetLaunchTcb())) + matched = matched && kds.TCBPartsLE(minimum, actual) + default: + return false, fmt.Errorf("unsupported SNP CoRIM measurement key %#x", key) + } + } + if !foundMeasurement { + return false, fmt.Errorf("SNP CoRIM reference value is missing measurement key %#x", corimgen.SNPMeasurementMKey) + } + return matched, nil +} + +func matchSNPDigestAndSVN(report *sevsnp.Report, value comid.Mval) (bool, error) { + if value.Digests == nil || len(*value.Digests) != 1 { + return false, fmt.Errorf("SNP measurement key requires exactly one digest") + } + digest := (*value.Digests)[0] + if digest.HashAlgID != swid.Sha384 || len(digest.HashValue) != Hash384 { + return false, fmt.Errorf("SNP measurement key requires one 48-byte SHA-384 digest") + } + if err := rejectUnexpectedMeasurementValues(value, true, value.SVN != nil, false, "SNP measurement"); err != nil { + return false, err + } + matched := bytes.Equal(report.GetMeasurement(), digest.HashValue) + if value.SVN != nil { + svnMatched, err := matchSVN(value.SVN, uint64(report.GetGuestSvn())) + if err != nil { + return false, fmt.Errorf("invalid SNP guest SVN constraint: %w", err) + } + matched = matched && svnMatched + } + return matched, nil +} + +func matchSVN(svn *comid.SVN, actual uint64) (bool, error) { + if svn == nil || svn.Value == nil { + return false, fmt.Errorf("SVN value is missing") + } + switch value := svn.Value.(type) { + case comid.TaggedSVN: + return actual == uint64(value), nil + case *comid.TaggedSVN: + return value != nil && actual == uint64(*value), nil + case comid.TaggedMinSVN: + return actual >= uint64(value), nil + case *comid.TaggedMinSVN: + return value != nil && actual >= uint64(*value), nil + default: + return false, fmt.Errorf("unsupported SVN type %T", svn.Value) + } +} + +func exactRawUint64Value(value comid.Mval, label string) (uint64, error) { + raw, err := exactRawValue(value, label) + if err != nil { + return 0, err + } + if len(raw) != 8 { + return 0, fmt.Errorf("%s must be an 8-byte little-endian value", label) + } + return binary.LittleEndian.Uint64(raw), nil +} + +func exactRawValue(value comid.Mval, label string) ([]byte, error) { + if value.RawValue == nil { + return nil, fmt.Errorf("%s CoRIM raw value is missing", label) + } + if err := rejectUnexpectedMeasurementValues(value, false, false, true, label); err != nil { + return nil, err + } + raw, err := value.RawValue.GetBytes() + if err != nil { + return nil, fmt.Errorf("invalid %s CoRIM raw value: %w", label, err) + } + return raw, nil +} + +func rejectUnexpectedMeasurementValues(value comid.Mval, allowDigests, allowSVN, allowRaw bool, label string) error { + if (!allowDigests && value.Digests != nil) || + (!allowSVN && value.SVN != nil) || + (!allowRaw && value.RawValue != nil) || + value.Ver != nil || value.Flags != nil || value.RawValueMask != nil || + value.MACAddr != nil || value.IPAddr != nil || value.SerialNumber != nil || + value.UEID != nil || value.UUID != nil || value.IntegrityRegisters != nil || + value.GetExtensions() != nil { + return fmt.Errorf("%s CoRIM value contains unsupported constraints", label) + } + return nil } func Attest(teeNonce []byte, vTPMNonce []byte, teeAttestaion bool, vmpl uint) ([]byte, error) { diff --git a/pkg/attestation/vtpm/vtpm_test.go b/pkg/attestation/vtpm/vtpm_test.go index f4e16c35..6e63be81 100644 --- a/pkg/attestation/vtpm/vtpm_test.go +++ b/pkg/attestation/vtpm/vtpm_test.go @@ -5,11 +5,14 @@ package vtpm import ( "bytes" + "encoding/binary" + "encoding/pem" "fmt" "io" "os" "testing" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-tpm-tools/proto/attest" "github.com/stretchr/testify/assert" @@ -20,6 +23,29 @@ import ( "google.golang.org/protobuf/proto" ) +func TestDecodeCachedSEVCertificateChainPreservesASKARKOrder(t *testing.T) { + vcek := []byte("vcek-der") + ask := []byte("ask-der") + ark := []byte("ark-der") + encode := func(value []byte) []byte { + return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: value}) + } + + chain, err := decodeCachedSEVCertificateChain( + encode(vcek), + append(encode(ask), encode(ark)...), + ) + require.NoError(t, err) + assert.Equal(t, vcek, chain.GetVcekCert()) + assert.Equal(t, ask, chain.GetAskCert()) + assert.Equal(t, ark, chain.GetArkCert()) + + _, err = decodeCachedSEVCertificateChain([]byte("not PEM"), append(encode(ask), encode(ark)...)) + assert.Error(t, err) + _, err = decodeCachedSEVCertificateChain(encode(vcek), encode(ask)) + assert.Error(t, err) +} + type mockTPM struct { *bytes.Buffer closeErr error @@ -253,7 +279,7 @@ func TestVerifier_VerifyWithCoRIM(t *testing.T) { assert.Contains(t, err.Error(), "no measurement in SEV-SNP report") // 4. Successful match - measurement := make([]byte, 32) + measurement := make([]byte, 48) att = &attest.Attestation{ TeeAttestation: &attest.Attestation_SevSnpAttestation{ SevSnpAttestation: &sevsnp.Attestation{ @@ -274,7 +300,7 @@ func TestVerifier_VerifyWithCoRIM(t *testing.T) { Instance: comid.MustNewUEIDInstance(comid.TestUEID), }, Measurements: *comid.NewMeasurements(). - AddMeasurement(comid.MustNewUUIDMeasurement(comid.TestUUID).AddDigest(swid.Sha3_256, measurement)), + AddMeasurement(comid.MustNewUintMeasurement(corimgen.SNPMeasurementMKey).AddDigest(swid.Sha384, measurement)), }) unsignedCorim := corim.NewUnsignedCorim() @@ -287,13 +313,13 @@ func TestVerifier_VerifyWithCoRIM(t *testing.T) { unsignedCorim.Tags = nil err = v.VerifyWithCoRIM(reportBytes, unsignedCorim) assert.Error(t, err) - assert.Contains(t, err.Error(), "no matching reference value found in CoRIM for vTPM") + assert.Contains(t, err.Error(), "no matching reference value found in CoRIM for SNP") // 6. Non-CoMID tag unsignedCorim.Tags = []corim.Tag{corim.Tag([]byte("non-comid-tag"))} err = v.VerifyWithCoRIM(reportBytes, unsignedCorim) assert.Error(t, err) - assert.Contains(t, err.Error(), "no matching reference value found in CoRIM for vTPM") + assert.Contains(t, err.Error(), "no matching reference value found in CoRIM for SNP") // 7. Invalid CoMID tag unsignedCorim.Tags = []corim.Tag{corim.Tag(append(corim.ComidTag, []byte("invalid")...))} @@ -301,3 +327,100 @@ func TestVerifier_VerifyWithCoRIM(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "failed to parse CoMID from tag") } + +func TestVerifierVerifyWithCoRIMEnforcesKeyedSNPConstraints(t *testing.T) { + report := &sevsnp.Report{ + Measurement: bytes.Repeat([]byte{0x11}, Hash384), + HostData: bytes.Repeat([]byte{0x22}, Hash256), + Policy: 0x30000, + GuestSvn: 3, + LaunchTcb: 0x0202, + } + primary := comid.MustNewUintMeasurement(corimgen.SNPMeasurementMKey). + AddDigest(swid.Sha384, report.GetMeasurement()). + SetSVN(uint64(report.GetGuestSvn())) + manifest := testSNPManifest(t, + primary, + testRawMeasurement(t, corimgen.SNPHostDataMKey, report.GetHostData()), + testRawUint64Measurement(t, corimgen.SNPPolicyMKey, report.GetPolicy()), + testRawUint64Measurement(t, corimgen.SNPMinimumLaunchTCBMKey, 0x0101), + ) + + verifier := NewVerifier(nil) + if err := verifier.VerifyWithCoRIM(testWrappedSNPReport(t, report), manifest); err != nil { + t.Fatalf("VerifyWithCoRIM() error = %v", err) + } + + mutations := map[string]func(*sevsnp.Report){ + "measurement": func(r *sevsnp.Report) { r.Measurement[0] ^= 0xff }, + "host data": func(r *sevsnp.Report) { r.HostData[0] ^= 0xff }, + "policy": func(r *sevsnp.Report) { r.Policy ^= 1 }, + "guest SVN": func(r *sevsnp.Report) { r.GuestSvn-- }, + "launch TCB": func(r *sevsnp.Report) { r.LaunchTcb = 0 }, + } + for name, mutate := range mutations { + t.Run(name, func(t *testing.T) { + changed := proto.Clone(report).(*sevsnp.Report) + mutate(changed) + if err := verifier.VerifyWithCoRIM(testWrappedSNPReport(t, changed), manifest); err == nil { + t.Fatal("VerifyWithCoRIM() accepted a mismatched keyed constraint") + } + }) + } + + unkeyed := testSNPManifest(t, + comid.MustNewUUIDMeasurement(comid.TestUUID).AddDigest(swid.Sha384, report.GetMeasurement()), + ) + if err := verifier.VerifyWithCoRIM(testWrappedSNPReport(t, report), unkeyed); err == nil { + t.Fatal("VerifyWithCoRIM() accepted an unkeyed digest") + } + + authorized := *primary + authorized.AuthorizedBy = &comid.CryptoKey{} + if _, err := matchSNPReferenceValue(report, comid.Measurements{authorized}); err == nil { + t.Fatal("matchSNPReferenceValue() accepted unsupported authorized-by metadata") + } +} + +func testSNPManifest(t *testing.T, measurements ...*comid.Measurement) *corim.UnsignedCorim { + t.Helper() + values := comid.Measurements{} + for _, measurement := range measurements { + values = append(values, *measurement) + } + tag := comid.NewComid(). + SetTagIdentity("snp-keyed-test", 0). + AddReferenceValue(comid.ReferenceValue{ + Environment: comid.Environment{Class: comid.NewClassOID(comid.TestOID)}, + Measurements: values, + }) + manifest := corim.NewUnsignedCorim() + manifest.AddComid(*tag) + return manifest +} + +func testRawMeasurement(t *testing.T, key uint64, value []byte) *comid.Measurement { + t.Helper() + measurement, err := comid.NewUintMeasurement(key) + require.NoError(t, err) + require.NotNil(t, measurement.SetRawValueBytes(value, nil)) + return measurement +} + +func testRawUint64Measurement(t *testing.T, key, value uint64) *comid.Measurement { + t.Helper() + raw := make([]byte, 8) + binary.LittleEndian.PutUint64(raw, value) + return testRawMeasurement(t, key, raw) +} + +func testWrappedSNPReport(t *testing.T, report *sevsnp.Report) []byte { + t.Helper() + payload, err := proto.Marshal(&attest.Attestation{ + TeeAttestation: &attest.Attestation_SevSnpAttestation{ + SevSnpAttestation: &sevsnp.Attestation{Report: report}, + }, + }) + require.NoError(t, err) + return payload +} From 3675fa3e4378df141396fae7a742d601e01a7efc Mon Sep 17 00:00:00 2001 From: thinksyncs Date: Sun, 30 Aug 2026 18:02:07 +0900 Subject: [PATCH 5/8] refactor(cocos): externalize attestation composition --- go.work | 8 + go.work.sum | 225 ++++++ integrations/cocos/Makefile | 29 + integrations/cocos/README.md | 77 +++ integrations/cocos/cmd/agent/main.go | 273 ++++++++ .../cocos/cmd/cli/attestation_module.go | 71 ++ integrations/cocos/cmd/cli/main.go | 221 ++++++ integrations/cocos/cmd/cli/main_test.go | 167 +++++ .../cmd/hardware-attestation-redteam/main.go | 447 ++++++++++++ .../hardware-attestation-redteam/main_test.go | 126 ++++ integrations/cocos/evidencesource/source.go | 64 ++ .../cocos/evidencesource/source_test.go | 85 +++ integrations/cocos/go.mod | 116 ++++ integrations/cocos/go.sum | 251 +++++++ .../cocos/internal/logger/protohandler.go | 116 ++++ .../cocos/internal/platformselect/select.go | 53 ++ .../internal/platformselect/select_test.go | 55 ++ .../cocos/internal/runtime/logging/logging.go | 31 + .../cocos/internal/runtime/metrics/metrics.go | 31 + integrations/cocos/platformmodule/policy.go | 76 +++ integrations/cocos/platformmodule/verifier.go | 425 ++++++++++++ .../cocos/platformmodule/verifier_test.go | 639 ++++++++++++++++++ 22 files changed, 3586 insertions(+) create mode 100644 go.work create mode 100644 go.work.sum create mode 100644 integrations/cocos/Makefile create mode 100644 integrations/cocos/README.md create mode 100644 integrations/cocos/cmd/agent/main.go create mode 100644 integrations/cocos/cmd/cli/attestation_module.go create mode 100644 integrations/cocos/cmd/cli/main.go create mode 100644 integrations/cocos/cmd/cli/main_test.go create mode 100644 integrations/cocos/cmd/hardware-attestation-redteam/main.go create mode 100644 integrations/cocos/cmd/hardware-attestation-redteam/main_test.go create mode 100644 integrations/cocos/evidencesource/source.go create mode 100644 integrations/cocos/evidencesource/source_test.go create mode 100644 integrations/cocos/go.mod create mode 100644 integrations/cocos/go.sum create mode 100644 integrations/cocos/internal/logger/protohandler.go create mode 100644 integrations/cocos/internal/platformselect/select.go create mode 100644 integrations/cocos/internal/platformselect/select_test.go create mode 100644 integrations/cocos/internal/runtime/logging/logging.go create mode 100644 integrations/cocos/internal/runtime/metrics/metrics.go create mode 100644 integrations/cocos/platformmodule/policy.go create mode 100644 integrations/cocos/platformmodule/verifier.go create mode 100644 integrations/cocos/platformmodule/verifier_test.go diff --git a/go.work b/go.work new file mode 100644 index 00000000..de1cf117 --- /dev/null +++ b/go.work @@ -0,0 +1,8 @@ +go 1.26.0 + +use ( + . + ./integrations/cocos + ./modules/attestation/snp + ./modules/attestation/tdx +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 00000000..bcc424db --- /dev/null +++ b/go.work.sum @@ -0,0 +1,225 @@ +cloud.google.com/go/accessapproval v1.8.6/go.mod h1:FfmTs7Emex5UvfnnpMkhuNkRCP85URnBFt5ClLxhZaQ= +cloud.google.com/go/accesscontextmanager v1.9.6/go.mod h1:884XHwy1AQpCX5Cj2VqYse77gfLaq9f8emE2bYriilk= +cloud.google.com/go/aiplatform v1.89.0/go.mod h1:TzZtegPkinfXTtXVvZZpxx7noINFMVDrLkE7cEWhYEk= +cloud.google.com/go/analytics v0.28.1/go.mod h1:iPaIVr5iXPB3JzkKPW1JddswksACRFl3NSHgVHsuYC4= +cloud.google.com/go/apigateway v1.7.6/go.mod h1:SiBx36VPjShaOCk8Emf63M2t2c1yF+I7mYZaId7OHiA= +cloud.google.com/go/apigeeconnect v1.7.6/go.mod h1:zqDhHY99YSn2li6OeEjFpAlhXYnXKl6DFb/fGu0ye2w= +cloud.google.com/go/apigeeregistry v0.9.6/go.mod h1:AFEepJBKPtGDfgabG2HWaLH453VVWWFFs3P4W00jbPs= +cloud.google.com/go/appengine v1.9.6/go.mod h1:jPp9T7Opvzl97qytaRGPwoH7pFI3GAcLDaui1K8PNjY= +cloud.google.com/go/area120 v0.9.6/go.mod h1:qKSokqe0iTmwBDA3tbLWonMEnh0pMAH4YxiceiHUed4= +cloud.google.com/go/artifactregistry v1.17.1/go.mod h1:06gLv5QwQPWtaudI2fWO37gfwwRUHwxm3gA8Fe568Hc= +cloud.google.com/go/asset v1.21.1/go.mod h1:7AzY1GCC+s1O73yzLM1IpHFLHz3ws2OigmCpOQHwebk= +cloud.google.com/go/assuredworkloads v1.12.6/go.mod h1:QyZHd7nH08fmZ+G4ElihV1zoZ7H0FQCpgS0YWtwjCKo= +cloud.google.com/go/automl v1.14.7/go.mod h1:8a4XbIH5pdvrReOU72oB+H3pOw2JBxo9XTk39oljObE= +cloud.google.com/go/baremetalsolution v1.3.6/go.mod h1:7/CS0LzpLccRGO0HL3q2Rofxas2JwjREKut414sE9iM= +cloud.google.com/go/batch v1.12.2/go.mod h1:tbnuTN/Iw59/n1yjAYKV2aZUjvMM2VJqAgvUgft6UEU= +cloud.google.com/go/beyondcorp v1.1.6/go.mod h1:V1PigSWPGh5L/vRRmyutfnjAbkxLI2aWqJDdxKbwvsQ= +cloud.google.com/go/bigquery v1.69.0/go.mod h1:TdGLquA3h/mGg+McX+GsqG9afAzTAcldMjqhdjHTLew= +cloud.google.com/go/bigtable v1.37.0/go.mod h1:HXqddP6hduwzrtiTCqZPpj9ij4hGZb4Zy1WF/dT+yaU= +cloud.google.com/go/billing v1.20.4/go.mod h1:hBm7iUmGKGCnBm6Wp439YgEdt+OnefEq/Ib9SlJYxIU= +cloud.google.com/go/binaryauthorization v1.9.5/go.mod h1:CV5GkS2eiY461Bzv+OH3r5/AsuB6zny+MruRju3ccB8= +cloud.google.com/go/certificatemanager v1.9.5/go.mod h1:kn7gxT/80oVGhjL8rurMUYD36AOimgtzSBPadtAeffs= +cloud.google.com/go/channel v1.19.5/go.mod h1:vevu+LK8Oy1Yuf7lcpDbkQQQm5I7oiY5fFTn3uwfQLY= +cloud.google.com/go/cloudbuild v1.22.2/go.mod h1:rPyXfINSgMqMZvuTk1DbZcbKYtvbYF/i9IXQ7eeEMIM= +cloud.google.com/go/clouddms v1.8.7/go.mod h1:DhWLd3nzHP8GoHkA6hOhso0R9Iou+IGggNqlVaq/KZ4= +cloud.google.com/go/cloudtasks v1.13.6/go.mod h1:/IDaQqGKMixD+ayM43CfsvWF2k36GeomEuy9gL4gLmU= +cloud.google.com/go/compute v1.38.0 h1:MilCLYQW2m7Dku8hRIIKo4r0oKastlD74sSu16riYKs= +cloud.google.com/go/compute v1.38.0/go.mod h1:oAFNIuXOmXbK/ssXm3z4nZB8ckPdjltJ7xhHCdbWFZM= +cloud.google.com/go/contactcenterinsights v1.17.3/go.mod h1:7Uu2CpxS3f6XxhRdlEzYAkrChpR5P5QfcdGAFEdHOG8= +cloud.google.com/go/container v1.43.0/go.mod h1:ETU9WZ1KM9ikEKLzrhRVao7KHtalDQu6aPqM34zDr/U= +cloud.google.com/go/containeranalysis v0.14.1/go.mod h1:28e+tlZgauWGHmEbnI5UfIsjMmrkoR1tFN0K2i71jBI= +cloud.google.com/go/datacatalog v1.26.0/go.mod h1:bLN2HLBAwB3kLTFT5ZKLHVPj/weNz6bR0c7nYp0LE14= +cloud.google.com/go/dataflow v0.11.0/go.mod h1:gNHC9fUjlV9miu0hd4oQaXibIuVYTQvZhMdPievKsPk= +cloud.google.com/go/dataform v0.12.0/go.mod h1:PuDIEY0lSVuPrZqcFji1fmr5RRvz3DGz4YP/cONc8g4= +cloud.google.com/go/datafusion v1.8.6/go.mod h1:fCyKJF2zUKC+O3hc2F9ja5EUCAbT4zcH692z8HiFZFw= +cloud.google.com/go/datalabeling v0.9.6/go.mod h1:n7o4x0vtPensZOoFwFa4UfZgkSZm8Qs0Pg/T3kQjXSM= +cloud.google.com/go/dataplex v1.25.3/go.mod h1:wOJXnOg6bem0tyslu4hZBTncfqcPNDpYGKzed3+bd+E= +cloud.google.com/go/dataproc/v2 v2.11.2/go.mod h1:xwukBjtfiO4vMEa1VdqyFLqJmcv7t3lo+PbLDcTEw+g= +cloud.google.com/go/dataqna v0.9.7/go.mod h1:4ac3r7zm7Wqm8NAc8sDIDM0v7Dz7d1e/1Ka1yMFanUM= +cloud.google.com/go/datastore v1.20.0/go.mod h1:uFo3e+aEpRfHgtp5pp0+6M0o147KoPaYNaPAKpfh8Ew= +cloud.google.com/go/datastream v1.14.1/go.mod h1:JqMKXq/e0OMkEgfYe0nP+lDye5G2IhIlmencWxmesMo= +cloud.google.com/go/deploy v1.27.2/go.mod h1:4NHWE7ENry2A4O1i/4iAPfXHnJCZ01xckAKpZQwhg1M= +cloud.google.com/go/dialogflow v1.68.2/go.mod h1:E0Ocrhf5/nANZzBju8RX8rONf0PuIvz2fVj3XkbAhiY= +cloud.google.com/go/dlp v1.23.0/go.mod h1:vVT4RlyPMEMcVHexdPT6iMVac3seq3l6b8UPdYpgFrg= +cloud.google.com/go/documentai v1.37.0/go.mod h1:qAf3ewuIUJgvSHQmmUWvM3Ogsr5A16U2WPHmiJldvLA= +cloud.google.com/go/domains v0.10.6/go.mod h1:3xzG+hASKsVBA8dOPc4cIaoV3OdBHl1qgUpAvXK7pGY= +cloud.google.com/go/edgecontainer v1.4.3/go.mod h1:q9Ojw2ox0uhAvFisnfPRAXFTB1nfRIOIXVWzdXMZLcE= +cloud.google.com/go/errorreporting v0.3.2/go.mod h1:s5kjs5r3l6A8UUyIsgvAhGq6tkqyBCUss0FRpsoVTww= +cloud.google.com/go/essentialcontacts v1.7.6/go.mod h1:/Ycn2egr4+XfmAfxpLYsJeJlVf9MVnq9V7OMQr9R4lA= +cloud.google.com/go/eventarc v1.15.5/go.mod h1:vDCqGqyY7SRiickhEGt1Zhuj81Ya4F/NtwwL3OZNskg= +cloud.google.com/go/filestore v1.10.2/go.mod h1:w0Pr8uQeSRQfCPRsL0sYKW6NKyooRgixCkV9yyLykR4= +cloud.google.com/go/firestore v1.18.0/go.mod h1:5ye0v48PhseZBdcl0qbl3uttu7FIEwEYVaWm0UIEOEU= +cloud.google.com/go/functions v1.19.6/go.mod h1:0G0RnIlbM4MJEycfbPZlCzSf2lPOjL7toLDwl+r0ZBw= +cloud.google.com/go/gkebackup v1.8.0/go.mod h1:FjsjNldDilC9MWKEHExnK3kKJyTDaSdO1vF0QeWSOPU= +cloud.google.com/go/gkeconnect v0.12.4/go.mod h1:bvpU9EbBpZnXGo3nqJ1pzbHWIfA9fYqgBMJ1VjxaZdk= +cloud.google.com/go/gkehub v0.15.6/go.mod h1:sRT0cOPAgI1jUJrS3gzwdYCJ1NEzVVwmnMKEwrS2QaM= +cloud.google.com/go/gkemulticloud v1.5.3/go.mod h1:KPFf+/RcfvmuScqwS9/2MF5exZAmXSuoSLPuaQ98Xlk= +cloud.google.com/go/gsuiteaddons v1.7.7/go.mod h1:zTGmmKG/GEBCONsvMOY2ckDiEsq3FN+lzWGUiXccF9o= +cloud.google.com/go/iap v1.11.2/go.mod h1:Bh99DMUpP5CitL9lK0BC8MYgjjYO4b3FbyhgW1VHJvg= +cloud.google.com/go/ids v1.5.6/go.mod h1:y3SGLmEf9KiwKsH7OHvYYVNIJAtXybqsD2z8gppsziQ= +cloud.google.com/go/iot v1.8.6/go.mod h1:MThnkiihNkMysWNeNje2Hp0GSOpEq2Wkb/DkBCVYa0U= +cloud.google.com/go/kms v1.22.0/go.mod h1:U7mf8Sva5jpOb4bxYZdtw/9zsbIjrklYwPcvMk34AL8= +cloud.google.com/go/language v1.14.5/go.mod h1:nl2cyAVjcBct1Hk73tzxuKebk0t2eULFCaruhetdZIA= +cloud.google.com/go/lifesciences v0.10.6/go.mod h1:1nnZwaZcBThDujs9wXzECnd1S5d+UiDkPuJWAmhRi7Q= +cloud.google.com/go/managedidentities v1.7.6/go.mod h1:pYCWPaI1AvR8Q027Vtp+SFSM/VOVgbjBF4rxp1/z5p4= +cloud.google.com/go/maps v1.21.0/go.mod h1:cqzZ7+DWUKKbPTgqE+KuNQtiCRyg/o7WZF9zDQk+HQs= +cloud.google.com/go/mediatranslation v0.9.6/go.mod h1:WS3QmObhRtr2Xu5laJBQSsjnWFPPthsyetlOyT9fJvE= +cloud.google.com/go/memcache v1.11.6/go.mod h1:ZM6xr1mw3F8TWO+In7eq9rKlJc3jlX2MDt4+4H+/+cc= +cloud.google.com/go/metastore v1.14.7/go.mod h1:0dka99KQofeUgdfu+K/Jk1KeT9veWZlxuZdJpZPtuYU= +cloud.google.com/go/networkconnectivity v1.17.1/go.mod h1:DTZCq8POTkHgAlOAAEDQF3cMEr/B9k1ZbpklqvHEBtg= +cloud.google.com/go/networkmanagement v1.19.1/go.mod h1:icgk265dNnilxQzpr6rO9WuAuuCmUOqq9H6WBeM2Af4= +cloud.google.com/go/networksecurity v0.10.6/go.mod h1:FTZvabFPvK2kR/MRIH3l/OoQ/i53eSix2KA1vhBMJec= +cloud.google.com/go/notebooks v1.12.6/go.mod h1:3Z4TMEqAKP3pu6DI/U+aEXrNJw9hGZIVbp+l3zw8EuA= +cloud.google.com/go/optimization v1.7.6/go.mod h1:4MeQslrSJGv+FY4rg0hnZBR/tBX2awJ1gXYp6jZpsYY= +cloud.google.com/go/orchestration v1.11.9/go.mod h1:KKXK67ROQaPt7AxUS1V/iK0Gs8yabn3bzJ1cLHw4XBg= +cloud.google.com/go/orgpolicy v1.15.0/go.mod h1:NTQLwgS8N5cJtdfK55tAnMGtvPSsy95JJhESwYHaJVs= +cloud.google.com/go/osconfig v1.14.6/go.mod h1:LS39HDBH0IJDFgOUkhSZUHFQzmcWaCpYXLrc3A4CVzI= +cloud.google.com/go/oslogin v1.14.6/go.mod h1:xEvcRZTkMXHfNSKdZ8adxD6wvRzeyAq3cQX3F3kbMRw= +cloud.google.com/go/phishingprotection v0.9.6/go.mod h1:VmuGg03DCI0wRp/FLSvNyjFj+J8V7+uITgHjCD/x4RQ= +cloud.google.com/go/policytroubleshooter v1.11.6/go.mod h1:jdjYGIveoYolk38Dm2JjS5mPkn8IjVqPsDHccTMu3mY= +cloud.google.com/go/privatecatalog v0.10.7/go.mod h1:Fo/PF/B6m4A9vUYt0nEF1xd0U6Kk19/Je3eZGrQ6l60= +cloud.google.com/go/pubsub v1.49.0/go.mod h1:K1FswTWP+C1tI/nfi3HQecoVeFvL4HUOB1tdaNXKhUY= +cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI= +cloud.google.com/go/recaptchaenterprise/v2 v2.20.4/go.mod h1:3H8nb8j8N7Ss2eJ+zr+/H7gyorfzcxiDEtVBDvDjwDQ= +cloud.google.com/go/recommendationengine v0.9.6/go.mod h1:nZnjKJu1vvoxbmuRvLB5NwGuh6cDMMQdOLXTnkukUOE= +cloud.google.com/go/recommender v1.13.5/go.mod h1:v7x/fzk38oC62TsN5Qkdpn0eoMBh610UgArJtDIgH/E= +cloud.google.com/go/redis v1.18.2/go.mod h1:q6mPRhLiR2uLf584Lcl4tsiRn0xiFlu6fnJLwCORMtY= +cloud.google.com/go/resourcemanager v1.10.6/go.mod h1:VqMoDQ03W4yZmxzLPrB+RuAoVkHDS5tFUUQUhOtnRTg= +cloud.google.com/go/resourcesettings v1.8.3/go.mod h1:BzgfXFHIWOOmHe6ZV9+r3OWfpHJgnqXy8jqwx4zTMLw= +cloud.google.com/go/retail v1.21.0/go.mod h1:LuG+QvBdLfKfO+7nnF3eA3l1j4TQw3Sg+UqlUorquRc= +cloud.google.com/go/run v1.10.0/go.mod h1:z7/ZidaHOCjdn5dV0eojRbD+p8RczMk3A7Qi2L+koHg= +cloud.google.com/go/scheduler v1.11.7/go.mod h1:gqYs8ndLx2M5D0oMJh48aGS630YYvC432tHCnVWN13s= +cloud.google.com/go/secretmanager v1.14.7/go.mod h1:uRuB4F6NTFbg0vLQ6HsT7PSsfbY7FqHbtJP1J94qxGc= +cloud.google.com/go/security v1.18.5/go.mod h1:D1wuUkDwGqTKD0Nv7d4Fn2Dc53POJSmO4tlg1K1iS7s= +cloud.google.com/go/securitycenter v1.36.2/go.mod h1:80ocoXS4SNWxmpqeEPhttYrmlQzCPVGaPzL3wVcoJvE= +cloud.google.com/go/servicedirectory v1.12.6/go.mod h1:OojC1KhOMDYC45oyTn3Mup08FY/S0Kj7I58dxUMMTpg= +cloud.google.com/go/shell v1.8.6/go.mod h1:GNbTWf1QA/eEtYa+kWSr+ef/XTCDkUzRpV3JPw0LqSk= +cloud.google.com/go/spanner v1.82.0/go.mod h1:BzybQHFQ/NqGxvE/M+/iU29xgutJf7Q85/4U9RWMto0= +cloud.google.com/go/speech v1.27.1/go.mod h1:efCfklHFL4Flxcdt9gpEMEJh9MupaBzw3QiSOVeJ6ck= +cloud.google.com/go/storagetransfer v1.13.0/go.mod h1:+aov7guRxXBYgR3WCqedkyibbTICdQOiXOdpPcJCKl8= +cloud.google.com/go/talent v1.8.3/go.mod h1:oD3/BilJpJX8/ad8ZUAxlXHCslTg2YBbafFH3ciZSLQ= +cloud.google.com/go/texttospeech v1.13.0/go.mod h1:g/tW/m0VJnulGncDrAoad6WdELMTes8eb77Idz+4HCo= +cloud.google.com/go/tpu v1.8.3/go.mod h1:Do6Gq+/Jx6Xs3LcY2WhHyGwKDKVw++9jIJp+X+0rxRE= +cloud.google.com/go/translate v1.12.5/go.mod h1:o/v+QG/bdtBV1d1edmtau0PwTfActvxPk/gtqdSDBi4= +cloud.google.com/go/video v1.24.0/go.mod h1:h6Bw4yUbGNEa9dH4qMtUMnj6cEf+OyOv/f2tb70G6Fk= +cloud.google.com/go/videointelligence v1.12.6/go.mod h1:/l34WMndN5/bt04lHodxiYchLVuWPQjCU6SaiTswrIw= +cloud.google.com/go/vision/v2 v2.9.5/go.mod h1:1SiNZPpypqZDbOzU052ZYRiyKjwOcyqgGgqQCI/nlx8= +cloud.google.com/go/vmmigration v1.8.6/go.mod h1:uZ6/KXmekwK3JmC8PzBM/cKQmq404TTfWtThF6bbf0U= +cloud.google.com/go/vmwareengine v1.3.5/go.mod h1:QuVu2/b/eo8zcIkxBYY5QSwiyEcAy6dInI7N+keI+Jg= +cloud.google.com/go/vpcaccess v1.8.6/go.mod h1:61yymNplV1hAbo8+kBOFO7Vs+4ZHYI244rSFgmsHC6E= +cloud.google.com/go/webrisk v1.11.1/go.mod h1:+9SaepGg2lcp1p0pXuHyz3R2Yi2fHKKb4c1Q9y0qbtA= +cloud.google.com/go/websecurityscanner v1.7.6/go.mod h1:ucaaTO5JESFn5f2pjdX01wGbQ8D6h79KHrmO2uGZeiY= +cloud.google.com/go/workflows v1.14.2/go.mod h1:5nqKjMD+MsJs41sJhdVrETgvD5cOK3hUcAs8ygqYvXQ= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/certificate-transparency-go v1.1.2/go.mod h1:3OL+HKDqHPUfdKrHVQxO6T8nDLO0HF7LRTlkIWXaWvQ= +github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= +github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= +github.com/hashicorp/consul/api v1.14.0/go.mod h1:bcaw5CSZ7NE9qfOfKCI1xb7ZKjzu/MyvQkCLTfqLqxQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/serf v0.10.0/go.mod h1:bXN03oZc5xlH46k/K1qTrpXb9ERKyY1/i/N5mxvgrZw= +github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/lyft/protoc-gen-star/v2 v2.0.4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/moogar0880/problems v0.1.1/go.mod h1:5Dxrk2sD7BfBAgnOzQ1yaTiuCYdGPUh49L8Vhfky62c= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4= +github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rabbitmq/amqp091-go v1.2.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= +github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/veraison/apiclient v0.2.0/go.mod h1:LCXFZ3D/tJ3HLAOHUg8bnAKGvgTl53e1ntwdwjVbQ5A= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= +google.golang.org/grpc/examples v0.0.0-20250407062114-b368379ef8f6/go.mod h1:6ytKWczdvnpnO+m+JiG9NjEDzR1FJfsnmJdG7B8QVZ8= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/integrations/cocos/Makefile b/integrations/cocos/Makefile new file mode 100644 index 00000000..fdf49542 --- /dev/null +++ b/integrations/cocos/Makefile @@ -0,0 +1,29 @@ +BUILD_DIR ?= build +CGO_ENABLED ?= 0 +GO ?= go + +.PHONY: all agent cli hardware-attestation-redteam test + +all: agent cli hardware-attestation-redteam + +agent: + mkdir -p $(BUILD_DIR) + GOWORK=off CGO_ENABLED=$(CGO_ENABLED) $(GO) build \ + -o $(BUILD_DIR)/agents-secure-binding-agent ./cmd/agent + +cli: + mkdir -p $(BUILD_DIR) + GOWORK=off CGO_ENABLED=$(CGO_ENABLED) $(GO) build \ + -o $(BUILD_DIR)/agents-secure-binding-cli ./cmd/cli + +hardware-attestation-redteam: + mkdir -p $(BUILD_DIR) + GOWORK=off CGO_ENABLED=$(CGO_ENABLED) $(GO) build \ + -o $(BUILD_DIR)/hardware-attestation-redteam \ + ./cmd/hardware-attestation-redteam + +test: + GOWORK=off $(GO) mod tidy -diff + GOWORK=off $(GO) mod verify + GOWORK=off $(GO) test -race -count=1 ./... + GOWORK=off $(GO) vet ./... diff --git a/integrations/cocos/README.md b/integrations/cocos/README.md new file mode 100644 index 00000000..aa54e9ac --- /dev/null +++ b/integrations/cocos/README.md @@ -0,0 +1,77 @@ +# Cocos attestation integration for ASB + +This nested Go module keeps the legacy Cocos evidence envelope and platform +appraisal composition outside the ASB v2 core. It adapts Cocos evidence +collection and verification to the platform-neutral interfaces in +`pkg/atls/eaattestation`. + +The integration is experimental. It is not production-qualified, and no live +AMD SEV-SNP or Intel TDX hardware qualification has been completed for this +version. Passing its offline tests does not establish current vendor +collateral availability, deployment key custody, launch-policy correctness, or +production readiness. + +## Boundary + +ASB owns TLS-exporter, session, nonce, identity, and replay binding. This module +owns the Cocos-specific composition around signed EAT envelopes, local CoRIM +reference values, and the selected platform verifier. + +- `evidencesource` implements `eaattestation.EvidenceSource` over the minimal + legacy Cocos attestation-client method. +- `platformmodule` implements `eaattestation.EvidenceVerifier` and delegates + direct quote verification to the independent SNP or TDX module. + +The public `evidencesource` and `platformmodule` adapter surface supports direct +AMD SEV-SNP and Intel TDX evidence only. SNP-vTPM, standalone vTPM, and Azure +evidence cannot be selected through those adapters. The moved legacy Cocos +commands still contain unrelated runtime compatibility code; that code is not +part of the attestation-module support claim. + +The deployment selects one platform from trusted local configuration. A +platform name supplied by peer evidence cannot select a verifier. + +The moved runtime command reads `ASB_ATTESTATION_PLATFORM` (`snp`, `tdx`, +`auto`, or `none`). `auto` probes direct SNP and TDX devices only and ignores +vTPM and cloud-metadata signals; if both direct devices appear, startup fails +until one is selected explicitly. + +## Development wiring + +The local `replace` directives in this module's `go.mod` are prepublication +monorepo wiring only. They must not be present in a tagged integration release. +They allow the integration to be tested before the root ASB v2 release exists. + +Run the hardware-independent tests with the workspace disabled: + +```sh +GOWORK=off go test ./... +``` + +Build the moved Cocos commands independently from the root release build: + +```sh +make -C integrations/cocos +``` + +The agent binary is written to +`integrations/cocos/build/agents-secure-binding-agent`. Legacy files under +root `init/systemd` still refer to that binary name, but they are inherited +packaging inputs and are not built by the ASB root `make` target. + +These tests use deterministic fixtures and do not access `/dev/sev-guest`, +`/dev/tdx_guest`, a TPM, AMD KDS, or Intel PCS. + +## Release order + +1. Test and tag `modules/attestation/snp` and `modules/attestation/tdx` as + experimental `v0.x` modules. +2. Release the platform-neutral ASB root module as `v2` without a dependency on + this integration or either hardware module. +3. Replace the three local directives here with the released ASB, SNP, and TDX + versions, then run `GOWORK=off go mod verify`, `go test -race ./...`, and + `go vet ./...`. +4. Tag this module with the directory prefix, for example + `integrations/cocos/v0.1.0`. + +Hardware qualification remains a separate, platform-specific release gate. diff --git a/integrations/cocos/cmd/agent/main.go b/integrations/cocos/cmd/agent/main.go new file mode 100644 index 00000000..48678f9b --- /dev/null +++ b/integrations/cocos/cmd/agent/main.go @@ -0,0 +1,273 @@ +// Copyright (c) Ultraviolet +// SPDX-License-Identifier: Apache-2.0 +package main + +import ( + "context" + "fmt" + "log" + "log/slog" + "net/url" + "os" + "os/signal" + "syscall" + + "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/evidencesource" + agentlogger "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/internal/logger" + "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/internal/platformselect" + mglog "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/internal/runtime/logging" + "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/internal/runtime/metrics" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/api" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + cvmsapi "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/api/grpc" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms/server" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" + logpb "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/log" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + pkggrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc" + attestation_client "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/attestation" + cvmsgrpc "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/cvm" + logclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/log" + runnerclient "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients/grpc/runner" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/ingress" + "github.com/caarlos0/env/v11" + "golang.org/x/sync/errgroup" +) + +const ( + svcName = "agent" + envPrefixCVMGRPC = "AGENT_CVM_GRPC_" + storageDir = "/var/lib/agents-secure-binding/agent" +) + +type config struct { + LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"debug"` + Vmpl int `env:"AGENT_VMPL" envDefault:"2"` + AgentGrpcHost string `env:"AGENT_GRPC_HOST" envDefault:"0.0.0.0"` + AttestationPlatform string `env:"ASB_ATTESTATION_PLATFORM" envDefault:"auto"` + AttestationServiceSocket string `env:"ATTESTATION_SERVICE_SOCKET" envDefault:"/run/agents-secure-binding/attestation.sock"` +} + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + g, ctx := errgroup.WithContext(ctx) + + var cfg config + if err := env.Parse(&cfg); err != nil { + log.Fatalf("failed to load %s configuration : %s", svcName, err) + } + + var exitCode int + defer mglog.ExitWithError(&exitCode) + + var level slog.Level + if err := level.UnmarshalText([]byte(cfg.LogLevel)); err != nil { + log.Println(err) + exitCode = 1 + return + } + + logQueue := make(chan *cvms.ClientStreamMessage, 1000) + cvmsQueue := make(chan *cvms.ClientStreamMessage, 1000) + + handler := agentlogger.NewProtoHandler(os.Stdout, &slog.HandlerOptions{Level: level}, logQueue) + logger := slog.New(handler) + + eventSvc, err := events.New(svcName, logQueue) + if err != nil { + logger.Error(fmt.Sprintf("failed to create events service %s", err.Error())) + exitCode = 1 + return + } + + logClient, err := logclient.NewClient("/run/agents-secure-binding/log.sock") + if err != nil { + logger.Warn(fmt.Sprintf("failed to create log client: %s. Logging will be local only until service is available.", err)) + } else { + defer logClient.Close() + } + + g.Go(func() error { + for { + select { + case <-ctx.Done(): + return nil + case msg := <-logQueue: + if logClient == nil { + continue + } + switch m := msg.Message.(type) { + case *cvms.ClientStreamMessage_AgentLog: + err := logClient.SendLog(ctx, &logpb.LogEntry{ + Message: m.AgentLog.Message, + ComputationId: m.AgentLog.ComputationId, + Level: m.AgentLog.Level, + Timestamp: m.AgentLog.Timestamp, + }) + if err != nil { + logger.Error("failed to send log", "error", err) + } + case *cvms.ClientStreamMessage_AgentEvent: + err := logClient.SendEvent(ctx, &logpb.EventEntry{ + EventType: m.AgentEvent.EventType, + Timestamp: m.AgentEvent.Timestamp, + ComputationId: m.AgentEvent.ComputationId, + Details: m.AgentEvent.Details, + Originator: m.AgentEvent.Originator, + Status: m.AgentEvent.Status, + }) + if err != nil { + logger.Error("failed to send event", "error", err) + } + } + } + } + }) + + ccPlatform, err := platformselect.Resolve(cfg.AttestationPlatform) + if err != nil { + logger.Error(fmt.Sprintf("failed to select direct attestation platform: %s", err)) + exitCode = 1 + return + } + logger.Info(fmt.Sprintf("Detected confidential computing platform: %v", ccPlatform)) + + cvmGrpcConfig := clients.StandardClientConfig{} + if err := env.ParseWithOptions(&cvmGrpcConfig, env.Options{Prefix: envPrefixCVMGRPC}); err != nil { + logger.Error(fmt.Sprintf("failed to load %s gRPC client configuration : %s", svcName, err)) + exitCode = 1 + return + } + + cvmGRPCClient, cvmsClient, err := cvmsgrpc.NewCVMClient(cvmGrpcConfig) + if err != nil { + logger.Error(err.Error()) + exitCode = 1 + return + } + defer cvmGRPCClient.Close() + + reconnectFn := func(ctx context.Context) (pkggrpc.Client, cvms.Service_ProcessClient, error) { + grpcClient, newClient, err := cvmsgrpc.NewCVMClient(cvmGrpcConfig) + if err != nil { + return nil, nil, err + } + // Don't defer close here as we want to keep the connection open + + pc, err := newClient.Process(ctx) + if err != nil { + grpcClient.Close() + return nil, nil, err + } + return grpcClient, pc, nil + } + + if cfg.Vmpl < 0 || cfg.Vmpl > 3 { + logger.Error("vmpl level must be in a range [0, 3]") + exitCode = 1 + return + } + + attClient, err := attestation_client.NewClient(cfg.AttestationServiceSocket) + if err != nil { + logger.Error(fmt.Sprintf("failed to create attestation client: %s", err)) + exitCode = 1 + return + } + defer attClient.Close() + + runnerClient, err := runnerclient.NewClient("/run/agents-secure-binding/runner.sock") + if err != nil { + logger.Error(fmt.Sprintf("failed to create runner client: %s", err)) + exitCode = 1 + return + } + defer runnerClient.Close() + + svc := newService(ctx, logger, eventSvc, attClient, runnerClient, cfg.Vmpl) + + if err := os.MkdirAll(storageDir, 0o755); err != nil { + logger.Error(fmt.Sprintf("failed to create storage directory: %s", err)) + exitCode = 1 + return + } + + var certProvider atls.CertificateProvider + if ccPlatform != attestation.NoCC { + logger.Info(fmt.Sprintf("Initializing aTLS for platform %v with attestation service at %s", ccPlatform, cfg.AttestationServiceSocket)) + evidenceSource, sourceErr := evidencesource.NewEvidenceSource(attClient, ccPlatform) + if sourceErr != nil { + logger.Error(fmt.Sprintf("failed to configure platform evidence source: %s", sourceErr)) + exitCode = 1 + return + } + certProvider, err = atls.NewProvider(evidenceSource) + if err != nil { + logger.Error(fmt.Sprintf("failed to create certificate provider for aTLS: %s", err)) + exitCode = 1 + return + } + logger.Info("Successfully created aTLS certificate provider") + } else { + logger.Warn("No Confidential Computing platform detected (NoCC). Certificate provider remains nil; aTLS will not be available for computations.") + } + + // Create ingress proxy server + backendURL, err := url.Parse("unix:///run/agents-secure-binding/agent.sock") + if err != nil { + logger.Error(fmt.Sprintf("failed to parse backend URL: %s", err)) + exitCode = 1 + return + } + ingressProxy := ingress.NewProxyServer(logger, backendURL, certProvider) + + pc, err := cvmsClient.Process(ctx) + if err != nil { + logger.Error(fmt.Sprintf("failed to connect to cvm server: %s", err)) + exitCode = 1 + return + } + + mc, err := cvmsapi.NewClient(pc, svc, cvmsQueue, logger, server.NewServer(logger, svc, cfg.AgentGrpcHost), ingressProxy, storageDir, reconnectFn, cvmGRPCClient) + if err != nil { + logger.Error(err.Error()) + exitCode = 1 + return + } + + g.Go(func() error { + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) + defer signal.Stop(ch) + + select { + case <-ch: + logger.Info("Received signal, shutting down...") + cancel() + return nil + case <-ctx.Done(): + return ctx.Err() + } + }) + + g.Go(func() error { + return mc.Process(ctx, cancel) + }) + + if err := g.Wait(); err != nil { + logger.Error(fmt.Sprintf("%s service terminated: %s", svcName, err)) + } +} + +func newService(ctx context.Context, logger *slog.Logger, eventSvc events.Service, attClient attestation_client.Client, runnerClient runnerclient.Client, vmpl int) agent.Service { + svc := agent.New(ctx, logger, eventSvc, attClient, runnerClient, vmpl) + + svc = api.LoggingMiddleware(svc, logger) + counter, latency := metrics.MakeMetrics(svcName, "api") + svc = api.MetricsMiddleware(svc, counter, latency) + + return svc +} diff --git a/integrations/cocos/cmd/cli/attestation_module.go b/integrations/cocos/cmd/cli/attestation_module.go new file mode 100644 index 00000000..0844927b --- /dev/null +++ b/integrations/cocos/cmd/cli/attestation_module.go @@ -0,0 +1,71 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + "strings" + + "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/platformmodule" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" +) + +type attestationModuleConfig struct { + Name string + PlatformPolicyPath string + EATVerificationKeyPath string + CoRIMVerificationKeyPath string + ExpectedEATIssuer string +} + +func loadAttestationVerifierConfig(module attestationModuleConfig, corimPolicyPath string) (platformmodule.VerifierConfig, error) { + platform := platformmodule.Platform(strings.TrimSpace(module.Name)) + if !supportedLocalPlatform(platform) { + return platformmodule.VerifierConfig{}, fmt.Errorf("%w: %q", platformmodule.ErrUnsupportedPlatform, platform) + } + verificationKey, err := eat.LoadVerificationKey(strings.TrimSpace(module.EATVerificationKeyPath)) + if err != nil { + return platformmodule.VerifierConfig{}, fmt.Errorf("load EAT verification key: %w", err) + } + config := platformmodule.VerifierConfig{ + Platform: platform, + PolicyPath: strings.TrimSpace(corimPolicyPath), + EATVerificationKey: verificationKey, + ExpectedIssuer: strings.TrimSpace(module.ExpectedEATIssuer), + } + if path := strings.TrimSpace(module.CoRIMVerificationKeyPath); path != "" { + corimKey, err := eat.LoadVerificationKey(path) + if err != nil { + return platformmodule.VerifierConfig{}, fmt.Errorf("load CoRIM verification key: %w", err) + } + config.CoRIMVerificationKey = corimKey + } + + switch platform { + case platformmodule.PlatformSNP: + verification, validation, err := platformmodule.LoadSNPPlatformPolicy(module.PlatformPolicyPath) + if err != nil { + return platformmodule.VerifierConfig{}, err + } + config.SNPVerificationOptions = verification + config.SNPValidationOptions = validation + case platformmodule.PlatformTDX: + policy, err := platformmodule.LoadTDXPlatformPolicy(module.PlatformPolicyPath) + if err != nil { + return platformmodule.VerifierConfig{}, err + } + config.TDXPolicy = policy + } + + return config, nil +} + +func supportedLocalPlatform(platform platformmodule.Platform) bool { + switch platform { + case platformmodule.PlatformSNP, platformmodule.PlatformTDX: + return true + default: + return false + } +} diff --git a/integrations/cocos/cmd/cli/main.go b/integrations/cocos/cmd/cli/main.go new file mode 100644 index 00000000..02a2e2b9 --- /dev/null +++ b/integrations/cocos/cmd/cli/main.go @@ -0,0 +1,221 @@ +// Copyright (c) Ultraviolet +// SPDX-License-Identifier: Apache-2.0 +package main + +import ( + "fmt" + "os" + "os/signal" + "path" + "syscall" + + "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/platformmodule" + "github.com/ToppyMicroServices/agents-secure-binding/v2/cli" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/cmdconfig" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/caarlos0/env/v11" + "github.com/fatih/color" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +const ( + svcName = "cli" + envPrefixAgentGRPC = "AGENT_GRPC_" + envPrefixManagerGRPC = "MANAGER_GRPC_" + completion = "completion" + filePermision = 0o755 + cacheDirectory = ".agents-secure-binding" +) + +type config struct { + LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"info"` + IgvmBinaryPath string `env:"IGVM_BINARY_PATH" envDefault:"./build/igvmmeasure"` + AgentAttestationModule string `env:"AGENT_GRPC_ATTESTATION_MODULE" envDefault:""` + AgentAttestationPlatformPolicy string `env:"AGENT_GRPC_ATTESTATION_PLATFORM_POLICY" envDefault:""` + AgentAttestationEATPublicKey string `env:"AGENT_GRPC_ATTESTATION_EAT_PUBLIC_KEY" envDefault:""` + AgentAttestationCoRIMPublicKey string `env:"AGENT_GRPC_ATTESTATION_CORIM_PUBLIC_KEY" envDefault:""` + AgentAttestationExpectedEATIssuer string `env:"AGENT_GRPC_ATTESTATION_EAT_ISSUER" envDefault:""` +} + +func main() { + os.Exit(run()) +} + +func run() int { + rootCmd := &cobra.Command{ + Use: "agents-secure-binding-cli [command]", + Short: "CLI application for the Agents Secure Binding runtime API", + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("CLI application for the Agents Secure Binding runtime API\n\n") + fmt.Printf("Usage:\n %s [command]\n\n", cmd.CommandPath()) + fmt.Printf("Available Commands:\n") + + // Filter out "completion" command + availableCommands := make([]*cobra.Command, 0) + for _, subCmd := range cmd.Commands() { + if subCmd.Name() != completion { + availableCommands = append(availableCommands, subCmd) + } + } + + for _, subCmd := range availableCommands { + fmt.Printf(" %-15s%s\n", subCmd.Name(), subCmd.Short) + } + + fmt.Printf("\nFlags:\n") + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + fmt.Printf(" -%s, --%s %s\n", flag.Shorthand, flag.Name, flag.Usage) + }) + fmt.Printf("\nUse \"%s [command] --help\" for more information about a command.\n", cmd.CommandPath()) + }, + } + + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) + + go func() { + <-signalChan + fmt.Println() + rootCmd.Println(color.New(color.FgRed).Sprint("Operation aborted by user!")) + os.Exit(2) + }() + + var cfg config + if err := env.Parse(&cfg); err != nil { + message := color.New(color.FgRed).Sprintf("failed to load %s configuration : %s", svcName, err) + rootCmd.Println(message) + return 1 + } + + homePath, err := os.UserHomeDir() + if err != nil { + message := color.New(color.FgRed).Sprintf("failed to fetch user home directory: %s", err) + rootCmd.Println(message) + return 1 + } + + directoryCachePath := path.Join(homePath, cacheDirectory) + + if err := os.MkdirAll(directoryCachePath, filePermision); err != nil { + message := color.New(color.FgRed).Sprintf("failed to create directory %s : %s", directoryCachePath, err) + rootCmd.Println(message) + return 1 + } + + agentGRPCConfig := clients.AttestedClientConfig{} + if err := env.ParseWithOptions(&agentGRPCConfig, env.Options{Prefix: envPrefixAgentGRPC}); err != nil { + message := color.New(color.FgRed).Sprintf("failed to load %s gRPC client configuration : %s", svcName, err) + rootCmd.Println(message) + return 1 + } + if err := configureAttestationModule(&agentGRPCConfig, attestationModuleConfig{ + Name: cfg.AgentAttestationModule, + PlatformPolicyPath: cfg.AgentAttestationPlatformPolicy, + EATVerificationKeyPath: cfg.AgentAttestationEATPublicKey, + CoRIMVerificationKeyPath: cfg.AgentAttestationCoRIMPublicKey, + ExpectedEATIssuer: cfg.AgentAttestationExpectedEATIssuer, + }); err != nil { + message := color.New(color.FgRed).Sprintf("failed to configure the Agent attestation module: %s", err) + rootCmd.Println(message) + return 1 + } + + managerGRPCConfig := clients.StandardClientConfig{} + if err := env.ParseWithOptions(&managerGRPCConfig, env.Options{Prefix: envPrefixManagerGRPC}); err != nil { + message := color.New(color.FgRed).Sprintf("failed to load %s gRPC client configuration : %s", svcName, err) + rootCmd.Println(message) + return 1 + } + + options := cmdconfig.IgvmMeasureOptions + measurement, err := cmdconfig.NewCmdConfig(cfg.IgvmBinaryPath, options, os.Stderr) + if err != nil { + message := color.New(color.FgRed).Sprintf("failed to initialize measurement: %s", err) // Use %s instead of %w + rootCmd.Println(message) + return 1 + } + + cliSVC := cli.New(agentGRPCConfig, managerGRPCConfig, measurement) + + if err := cliSVC.InitializeAgentSDK(rootCmd); err == nil { + defer cliSVC.Close() + } + + rootCmd.PersistentFlags().BoolVarP(&cli.Verbose, "verbose", "v", false, "Enable verbose output") + + keysCmd := cliSVC.NewKeysCmd() + attestationCmd := cliSVC.NewAttestationCmd() + attestationPolicyCmd := cliSVC.NewAttestationPolicyCmd() + + // Agent Commands + rootCmd.AddCommand(cliSVC.NewAlgorithmCmd()) + rootCmd.AddCommand(cliSVC.NewDatasetsCmd()) + rootCmd.AddCommand(cliSVC.NewResultsCmd()) + rootCmd.AddCommand(attestationCmd) + rootCmd.AddCommand(cliSVC.NewFileHashCmd()) + rootCmd.AddCommand(attestationPolicyCmd) + rootCmd.AddCommand(keysCmd) + rootCmd.AddCommand(cliSVC.NewCABundleCmd(directoryCachePath, nil)) + rootCmd.AddCommand(cliSVC.NewCreateVMCmd()) + rootCmd.AddCommand(cliSVC.NewRemoveVMCmd()) + rootCmd.AddCommand(cliSVC.NewIMAMeasurementsCmd()) + + // Attestation commands + attestationCmd.AddCommand(cliSVC.NewGetAttestationCmd()) + attestationCmd.AddCommand(cliSVC.NewValidateAttestationValidationCmd()) + + // measure. + rootCmd.AddCommand(cliSVC.NewMeasureCmd(cfg.IgvmBinaryPath)) + + // Flags + keysCmd.PersistentFlags().StringVarP( + &cli.KeyType, + "key-type", + "k", + "rsa", + "User Key type", + ) + + // Attestation Policy commands + // Legacy JSON policy commands removed in favor of CoRIM. + // attestationPolicyCmd.AddCommand(cliSVC.NewAddMeasurementCmd()) + // attestationPolicyCmd.AddCommand(cliSVC.NewAddHostDataCmd()) + // attestationPolicyCmd.AddCommand(cliSVC.NewGCPAttestationPolicy()) + attestationPolicyCmd.AddCommand(cliSVC.NewDownloadGCPOvmfFile()) + // attestationPolicyCmd.AddCommand(cliSVC.NewAzureAttestationPolicy()) + // attestationPolicyCmd.AddCommand(cliSVC.NewExtendWithManifestCmd()) + + if err := rootCmd.Execute(); err != nil { + logErrorCmd(*rootCmd, err) + return 1 + } + return 0 +} + +func configureAttestationModule(cfg *clients.AttestedClientConfig, module attestationModuleConfig) error { + if cfg == nil { + return fmt.Errorf("attested client configuration is nil") + } + if !cfg.AttestedTLS { + return nil + } + verifierConfig, err := loadAttestationVerifierConfig(module, cfg.AttestationPolicy) + if err != nil { + return err + } + verifier, err := platformmodule.NewEvidenceVerifier(verifierConfig) + if err != nil { + return err + } + cfg.AttestationVerificationPolicy = eaattestation.VerificationPolicy{EvidenceVerifier: verifier} + return nil +} + +func logErrorCmd(cmd cobra.Command, err error) { + boldRed := color.New(color.FgRed, color.Bold) + boldRed.Fprintf(cmd.ErrOrStderr(), "\nerror: ") + + fmt.Fprintf(cmd.ErrOrStderr(), "%s\n\n", color.RedString(err.Error())) +} diff --git a/integrations/cocos/cmd/cli/main_test.go b/integrations/cocos/cmd/cli/main_test.go new file mode 100644 index 00000000..392770a2 --- /dev/null +++ b/integrations/cocos/cmd/cli/main_test.go @@ -0,0 +1,167 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "errors" + "os" + "path/filepath" + "testing" + + "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/platformmodule" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + sevabi "github.com/google/go-sev-guest/abi" + sevcheck "github.com/google/go-sev-guest/proto/check" + tdxabi "github.com/google/go-tdx-guest/abi" + tdxcheck "github.com/google/go-tdx-guest/proto/checkconfig" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +const testExpectedEATIssuer = "test-attestation-service" + +func TestConfigureAttestationModuleDisabled(t *testing.T) { + cfg := &clients.AttestedClientConfig{} + if err := configureAttestationModule(cfg, attestationModuleConfig{}); err != nil { + t.Fatal(err) + } + if cfg.AttestationVerificationPolicy.RequiresAttestation() { + t.Fatal("disabled attested TLS must not configure a platform verifier") + } +} + +func TestConfigureAttestationModuleRequiresExplicitPlatform(t *testing.T) { + cfg := &clients.AttestedClientConfig{AttestedTLS: true, AttestationPolicy: testAttestationPolicy(t)} + if err := configureAttestationModule(cfg, attestationModuleConfig{}); !errors.Is(err, platformmodule.ErrUnsupportedPlatform) { + t.Fatalf("error = %v, want ErrUnsupportedPlatform", err) + } +} + +func TestConfigureAttestationModuleRejectsLegacyAppraisers(t *testing.T) { + for _, platform := range []platformmodule.Platform{ + "snp-vtpm", + "vtpm", + "azure", + } { + t.Run(string(platform), func(t *testing.T) { + cfg := &clients.AttestedClientConfig{AttestedTLS: true, AttestationPolicy: testAttestationPolicy(t)} + err := configureAttestationModule(cfg, attestationModuleConfig{Name: string(platform)}) + if !errors.Is(err, platformmodule.ErrUnsupportedPlatform) { + t.Fatalf("error = %v, want ErrUnsupportedPlatform", err) + } + }) + } +} + +func TestConfigureAttestationModulePinsSNP(t *testing.T) { + cfg := &clients.AttestedClientConfig{AttestedTLS: true, AttestationPolicy: testAttestationPolicy(t)} + if err := configureAttestationModule(cfg, attestationModuleConfig{ + Name: string(platformmodule.PlatformSNP), + PlatformPolicyPath: testSNPPlatformPolicy(t), + EATVerificationKeyPath: testEATVerificationKey(t), + ExpectedEATIssuer: testExpectedEATIssuer, + }); err != nil { + t.Fatal(err) + } + if !cfg.AttestationVerificationPolicy.RequiresAttestation() { + t.Fatal("expected an injected attestation verifier") + } +} + +func TestConfigureAttestationModulePinsTDX(t *testing.T) { + cfg := &clients.AttestedClientConfig{AttestedTLS: true, AttestationPolicy: testAttestationPolicy(t)} + if err := configureAttestationModule(cfg, attestationModuleConfig{ + Name: string(platformmodule.PlatformTDX), + PlatformPolicyPath: testTDXPlatformPolicy(t), + EATVerificationKeyPath: testEATVerificationKey(t), + ExpectedEATIssuer: testExpectedEATIssuer, + }); err != nil { + t.Fatal(err) + } + if !cfg.AttestationVerificationPolicy.RequiresAttestation() { + t.Fatal("expected an injected TDX attestation verifier") + } +} + +func TestConfigureAttestationModuleRequiresEATTrust(t *testing.T) { + cfg := &clients.AttestedClientConfig{AttestedTLS: true, AttestationPolicy: testAttestationPolicy(t)} + err := configureAttestationModule(cfg, attestationModuleConfig{ + Name: string(platformmodule.PlatformSNP), + PlatformPolicyPath: testSNPPlatformPolicy(t), + ExpectedEATIssuer: testExpectedEATIssuer, + }) + if err == nil { + t.Fatal("expected a missing EAT verification key to fail") + } +} + +func testAttestationPolicy(t *testing.T) string { + t.Helper() + path := filepath.Join(t.TempDir(), "policy.corim") + if err := os.WriteFile(path, []byte("test policy placeholder"), 0o600); err != nil { + t.Fatal(err) + } + return path +} + +func testEATVerificationKey(t *testing.T) string { + t.Helper() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + if err != nil { + t.Fatal(err) + } + path := filepath.Join(t.TempDir(), "eat-public.pem") + if err := os.WriteFile(path, pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}), 0o600); err != nil { + t.Fatal(err) + } + return path +} + +func testSNPPlatformPolicy(t *testing.T) string { + t.Helper() + return writePlatformPolicy(t, "snp-policy.json", &sevcheck.Config{ + RootOfTrust: &sevcheck.RootOfTrust{CheckCrl: true}, + Policy: &sevcheck.Policy{ + Policy: sevabi.SnpPolicyToBytes(sevabi.SnpPolicy{}), + Product: sevabi.DefaultSevProduct(), + Vmpl: wrapperspb.UInt32(0), + }, + }) +} + +func testTDXPlatformPolicy(t *testing.T) string { + t.Helper() + return writePlatformPolicy(t, "tdx-policy.json", &tdxcheck.Config{ + RootOfTrust: &tdxcheck.RootOfTrust{CheckCrl: true, GetCollateral: true}, + Policy: &tdxcheck.Policy{ + HeaderPolicy: &tdxcheck.HeaderPolicy{}, + TdQuoteBodyPolicy: &tdxcheck.TDQuoteBodyPolicy{ + TdAttributes: make([]byte, tdxabi.TdAttributesSize), + }, + }, + }) +} + +func writePlatformPolicy(t *testing.T, name string, policy proto.Message) string { + t.Helper() + payload, err := protojson.Marshal(policy) + if err != nil { + t.Fatal(err) + } + path := filepath.Join(t.TempDir(), name) + if err := os.WriteFile(path, payload, 0o600); err != nil { + t.Fatal(err) + } + return path +} diff --git a/integrations/cocos/cmd/hardware-attestation-redteam/main.go b/integrations/cocos/cmd/hardware-attestation-redteam/main.go new file mode 100644 index 00000000..b0033744 --- /dev/null +++ b/integrations/cocos/cmd/hardware-attestation-redteam/main.go @@ -0,0 +1,447 @@ +// Copyright (c) Ultraviolet +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "flag" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos/platformmodule" + qemu "github.com/ToppyMicroServices/agents-secure-binding/v2/manager/qemu" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/tdx" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" + sevsnppb "github.com/google/go-sev-guest/proto/sevsnp" + tdxabi "github.com/google/go-tdx-guest/abi" + tdxpb "github.com/google/go-tdx-guest/proto/tdx" + "google.golang.org/protobuf/proto" +) + +const ( + platformSNP = "snp" + platformTDX = "tdx" + reportDataSize = 64 +) + +var errChallengeMismatch = errors.New("attestation evidence is not bound to verifier challenge") + +type runOptions struct { + Platform string + VMPL uint + ExpectedHostDataHex string + RequireKernelHashes bool + KernelHashesEvidence bool + EvidenceDir string + CoRIMPolicyPath string + PlatformPolicyPath string + CoRIMPublicKeyPath string + EATIssuer string + RequireFullVerification bool +} + +type extractedEvidence struct { + ReportData []byte + HostData []byte +} + +type runSummary struct { + TimestampUTC string `json:"timestamp_utc"` + Platform string `json:"platform"` + EvidenceASHA256 string `json:"evidence_a_sha256"` + EvidenceBSHA256 string `json:"evidence_b_sha256"` + ChallengeASHA256 string `json:"challenge_a_sha256"` + ChallengeBSHA256 string `json:"challenge_b_sha256"` + HostDataSHA256 string `json:"host_data_sha256,omitempty"` + AppraisalContractCheck bool `json:"appraisal_contract_check"` + FullModuleVerification bool `json:"full_module_verification"` + EATPublicKeySHA256 string `json:"eat_public_key_sha256,omitempty"` +} + +func main() { + opts := runOptions{} + flag.StringVar(&opts.Platform, "platform", platformSNP, "attestation platform: snp or tdx") + flag.UintVar(&opts.VMPL, "vmpl", 0, "SEV-SNP VM privilege level") + flag.StringVar(&opts.ExpectedHostDataHex, "expected-host-data-hex", "", "expected SEV-SNP HostData as hex") + flag.BoolVar(&opts.RequireKernelHashes, "require-kernel-hashes", false, "require external evidence that kernel-hashes=on was used") + flag.BoolVar(&opts.KernelHashesEvidence, "kernel-hashes-evidence", false, "runner-provided evidence that kernel-hashes=on was used") + flag.StringVar(&opts.EvidenceDir, "evidence-dir", "", "directory for non-sensitive evidence fingerprints") + flag.StringVar(&opts.CoRIMPolicyPath, "corim-policy", "", "path to the local CoRIM reference-value policy") + flag.StringVar(&opts.PlatformPolicyPath, "platform-policy", "", "path to the go-sev/go-tdx verification policy JSON") + flag.StringVar(&opts.CoRIMPublicKeyPath, "corim-public-key", "", "optional P-256 public key for a signed CoRIM") + flag.StringVar(&opts.EATIssuer, "eat-issuer", "hardware-attestation-qualification", "expected issuer for the in-process signed EAT fixture") + flag.BoolVar(&opts.RequireFullVerification, "require-full-verification", false, "fail unless the complete signed-EAT and platform verifier path succeeds") + flag.Parse() + + summary, err := run(opts) + if err != nil { + fmt.Fprintf(os.Stderr, "hardware attestation red-team failed: %v\n", err) + os.Exit(1) + } + + fmt.Printf( + "hardware attestation red-team passed: platform=%s evidence_a_sha256=%s evidence_b_sha256=%s\n", + summary.Platform, + summary.EvidenceASHA256, + summary.EvidenceBSHA256, + ) +} + +func run(opts runOptions) (*runSummary, error) { + platform, err := resolvePlatform(opts.Platform) + if err != nil { + return nil, err + } + + switch platform { + case platformSNP: + return runSEVSNP(platform, opts) + case platformTDX: + if opts.ExpectedHostDataHex != "" || opts.RequireKernelHashes { + return nil, fmt.Errorf("HostData and kernel-hashes appraisal is only defined for SEV-SNP") + } + return exerciseTEE(platform, tdx.NewProvider().TeeAttestation, extractTDXEvidence, opts) + default: + return nil, fmt.Errorf("unsupported attestation platform %q", platform) + } +} + +func resolvePlatform(requested string) (string, error) { + normalized := strings.ToLower(strings.TrimSpace(requested)) + switch normalized { + case platformSNP, platformTDX: + return normalized, nil + default: + return "", fmt.Errorf("unsupported attestation platform %q", requested) + } +} + +func runSEVSNP(platform string, opts runOptions) (*runSummary, error) { + if err := prepareSEVSNPCertificateCache(opts.VMPL, vtpm.FetchSEVCertificates); err != nil { + return nil, err + } + provider := vtpm.NewProvider(false, opts.VMPL) + return exerciseTEE(platform, provider.TeeAttestation, extractSEVSNPEvidence, opts) +} + +func prepareSEVSNPCertificateCache(vmpl uint, fetch func(uint) error) error { + if fetch == nil { + return fmt.Errorf("bootstrap SEV-SNP certificate cache: fetch function is nil") + } + if err := fetch(vmpl); err != nil { + return fmt.Errorf("bootstrap SEV-SNP certificate cache from AMD KDS: %w", err) + } + return nil +} + +func exerciseTEE( + platform string, + collect func([]byte) ([]byte, error), + extract func([]byte) (*extractedEvidence, error), + opts runOptions, +) (*runSummary, error) { + challengeA, err := newReportData("agents-secure-binding/hardware-red-team/session-A") + if err != nil { + return nil, err + } + challengeB, err := newReportData("agents-secure-binding/hardware-red-team/session-B") + if err != nil { + return nil, err + } + + evidenceA, err := collect(challengeA) + if err != nil { + return nil, fmt.Errorf("collect %s evidence for session A: %w", platform, err) + } + evidenceB, err := collect(challengeB) + if err != nil { + return nil, fmt.Errorf("collect %s evidence for session B: %w", platform, err) + } + if len(evidenceA) == 0 || len(evidenceB) == 0 { + return nil, fmt.Errorf("%s provider returned empty evidence", platform) + } + if bytes.Equal(evidenceA, evidenceB) { + return nil, fmt.Errorf("%s provider returned identical evidence for distinct challenges", platform) + } + + parsedA, err := extract(evidenceA) + if err != nil { + return nil, fmt.Errorf("extract %s session A report data: %w", platform, err) + } + parsedB, err := extract(evidenceB) + if err != nil { + return nil, fmt.Errorf("extract %s session B report data: %w", platform, err) + } + + if err := validateChallengeBinding(parsedA.ReportData, challengeA); err != nil { + return nil, fmt.Errorf("session A evidence rejected for its own challenge: %w", err) + } + if err := validateChallengeBinding(parsedB.ReportData, challengeB); err != nil { + return nil, fmt.Errorf("session B evidence rejected for its own challenge: %w", err) + } + if err := validateChallengeBinding(parsedA.ReportData, challengeB); !errors.Is(err, errChallengeMismatch) { + return nil, fmt.Errorf("stale session A evidence was not rejected for session B challenge") + } + if err := validateChallengeBinding(parsedB.ReportData, challengeA); !errors.Is(err, errChallengeMismatch) { + return nil, fmt.Errorf("stale session B evidence was not rejected for session A challenge") + } + + appraisalChecked := false + fullModuleVerified := false + eatPublicKeySHA256 := "" + hostDataHash := "" + if opts.ExpectedHostDataHex != "" || opts.RequireKernelHashes { + if platform != platformSNP { + return nil, fmt.Errorf("SEV-SNP appraisal contract requested for non-SNP platform %q", platform) + } + if opts.ExpectedHostDataHex != "" && len(parsedA.HostData) == 0 { + return nil, fmt.Errorf("SEV-SNP evidence does not contain HostData") + } + expectedHostData := strings.TrimSpace(opts.ExpectedHostDataHex) + if expectedHostData != "" { + var err error + expectedHostData, err = qemu.NormalizeSEVSNPHostData(expectedHostData) + if err != nil { + return nil, fmt.Errorf("decode expected HostData: %w", err) + } + } + contract := qemu.SEVSNPAppraisalContract{ + RequireHostData: expectedHostData != "", + ExpectedHostData: expectedHostData, + RequireKernelHashes: opts.RequireKernelHashes, + } + evidence := qemu.SEVSNPAppraisalEvidence{ + HostData: hex.EncodeToString(parsedA.HostData), + KernelHashesEnabled: opts.KernelHashesEvidence, + } + if err := contract.Validate(evidence); err != nil { + return nil, fmt.Errorf("SEV-SNP appraisal contract rejected evidence: %w", err) + } + appraisalChecked = true + if len(parsedA.HostData) > 0 { + hostDataHash = sha256Hex(parsedA.HostData) + } + } + if opts.RequireFullVerification || opts.CoRIMPolicyPath != "" || opts.PlatformPolicyPath != "" { + fingerprint, err := verifyFullModulePath(platform, evidenceA, evidenceB, challengeA, challengeB, opts) + if err != nil { + return nil, err + } + fullModuleVerified = true + eatPublicKeySHA256 = fingerprint + } + + summary := &runSummary{ + TimestampUTC: time.Now().UTC().Format(time.RFC3339), + Platform: platform, + EvidenceASHA256: sha256Hex(evidenceA), + EvidenceBSHA256: sha256Hex(evidenceB), + ChallengeASHA256: sha256Hex(challengeA), + ChallengeBSHA256: sha256Hex(challengeB), + HostDataSHA256: hostDataHash, + AppraisalContractCheck: appraisalChecked, + FullModuleVerification: fullModuleVerified, + EATPublicKeySHA256: eatPublicKeySHA256, + } + if err := writeSummary(opts.EvidenceDir, summary); err != nil { + return nil, err + } + return summary, nil +} + +func verifyFullModulePath(platform string, evidenceA, evidenceB, challengeA, challengeB []byte, opts runOptions) (string, error) { + if strings.TrimSpace(opts.CoRIMPolicyPath) == "" || strings.TrimSpace(opts.PlatformPolicyPath) == "" { + return "", fmt.Errorf("full module verification requires --corim-policy and --platform-policy") + } + issuer := strings.TrimSpace(opts.EATIssuer) + if issuer == "" { + return "", fmt.Errorf("full module verification requires a non-empty --eat-issuer") + } + signingKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return "", fmt.Errorf("generate qualification EAT key: %w", err) + } + config := platformmodule.VerifierConfig{ + Platform: platformmodule.Platform(platform), + PolicyPath: opts.CoRIMPolicyPath, + EATVerificationKey: &signingKey.PublicKey, + ExpectedIssuer: issuer, + } + if path := strings.TrimSpace(opts.CoRIMPublicKeyPath); path != "" { + config.CoRIMVerificationKey, err = eat.LoadVerificationKey(path) + if err != nil { + return "", fmt.Errorf("load CoRIM verification key: %w", err) + } + } + platformType := attestation.NoCC + switch platform { + case platformSNP: + platformType = attestation.SNP + config.SNPVerificationOptions, config.SNPValidationOptions, err = platformmodule.LoadSNPPlatformPolicy(opts.PlatformPolicyPath) + case platformTDX: + platformType = attestation.TDX + config.TDXPolicy, err = platformmodule.LoadTDXPlatformPolicy(opts.PlatformPolicyPath) + default: + return "", fmt.Errorf("full module verification is available only for direct SNP or TDX") + } + if err != nil { + return "", err + } + verifier, err := platformmodule.NewEvidenceVerifier(config) + if err != nil { + return "", fmt.Errorf("construct %s verifier: %w", platform, err) + } + + bindingA, nonceA, err := qualificationBinding(challengeA) + if err != nil { + return "", err + } + bindingB, nonceB, err := qualificationBinding(challengeB) + if err != nil { + return "", err + } + tokenA, err := qualificationEAT(evidenceA, nonceA, platformType, signingKey, issuer) + if err != nil { + return "", err + } + tokenB, err := qualificationEAT(evidenceB, nonceB, platformType, signingKey, issuer) + if err != nil { + return "", err + } + if err := verifier.VerifyEvidence(tokenA, bindingA); err != nil { + return "", fmt.Errorf("verify %s module evidence A: %w", platform, err) + } + if err := verifier.VerifyEvidence(tokenB, bindingB); err != nil { + return "", fmt.Errorf("verify %s module evidence B: %w", platform, err) + } + if err := verifier.VerifyEvidence(tokenA, bindingB); err == nil { + return "", fmt.Errorf("%s module accepted session A EAT for session B binding", platform) + } + fingerprint, err := eat.VerificationKeyFingerprint(&signingKey.PublicKey) + if err != nil { + return "", err + } + return fingerprint, nil +} + +func qualificationBinding(reportData []byte) (eaattestation.EvidenceBinding, []byte, error) { + var binding eaattestation.EvidenceBinding + if len(reportData) != len(binding.ReportData) { + return binding, nil, fmt.Errorf("qualification REPORT_DATA length is %d, expected %d", len(reportData), len(binding.ReportData)) + } + copy(binding.ReportData[:], reportData) + if _, err := rand.Read(binding.Nonce[:]); err != nil { + return binding, nil, fmt.Errorf("generate qualification EAT nonce: %w", err) + } + return binding, append([]byte(nil), binding.Nonce[:]...), nil +} + +func qualificationEAT(evidence, nonce []byte, platformType attestation.PlatformType, signingKey *ecdsa.PrivateKey, issuer string) ([]byte, error) { + claims, err := eat.NewEATClaims(evidence, nonce, platformType) + if err != nil { + return nil, fmt.Errorf("create qualification EAT claims: %w", err) + } + token, err := eat.EncodeToCBOR(claims, signingKey, issuer) + if err != nil { + return nil, fmt.Errorf("sign qualification EAT: %w", err) + } + return token, nil +} + +func newReportData(context string) ([]byte, error) { + reportData := make([]byte, reportDataSize) + if _, err := rand.Read(reportData[:32]); err != nil { + return nil, fmt.Errorf("generate verifier challenge entropy: %w", err) + } + contextHash := sha256.Sum256([]byte(context)) + copy(reportData[32:], contextHash[:]) + return reportData, nil +} + +func validateChallengeBinding(reportData []byte, challenge []byte) error { + if len(reportData) != reportDataSize { + return fmt.Errorf("attestation report_data length is %d, expected %d", len(reportData), reportDataSize) + } + if len(challenge) != reportDataSize { + return fmt.Errorf("verifier challenge length is %d, expected %d", len(challenge), reportDataSize) + } + if !bytes.Equal(reportData, challenge) { + return fmt.Errorf( + "%w: report_data_sha256=%s verifier_challenge_sha256=%s", + errChallengeMismatch, + sha256Hex(reportData), + sha256Hex(challenge), + ) + } + return nil +} + +func extractSEVSNPEvidence(evidence []byte) (*extractedEvidence, error) { + attestation := &sevsnppb.Attestation{} + if err := proto.Unmarshal(evidence, attestation); err != nil { + return nil, err + } + report := attestation.GetReport() + if report == nil { + return nil, fmt.Errorf("missing SEV-SNP report") + } + return &extractedEvidence{ + ReportData: append([]byte(nil), report.GetReportData()...), + HostData: append([]byte(nil), report.GetHostData()...), + }, nil +} + +func extractTDXEvidence(evidence []byte) (*extractedEvidence, error) { + quoteAny, err := tdxabi.QuoteToProto(evidence) + if err != nil { + return nil, err + } + quote, ok := quoteAny.(*tdxpb.QuoteV4) + if !ok { + return nil, fmt.Errorf("unexpected TDX quote type %T", quoteAny) + } + body := quote.GetTdQuoteBody() + if body == nil { + return nil, fmt.Errorf("missing TDX quote body") + } + return &extractedEvidence{ + ReportData: append([]byte(nil), body.GetReportData()...), + }, nil +} + +func writeSummary(dir string, summary *runSummary) error { + if strings.TrimSpace(dir) == "" { + return nil + } + if err := os.MkdirAll(dir, 0o755); err != nil { + return fmt.Errorf("create evidence directory: %w", err) + } + payload, err := json.MarshalIndent(summary, "", " ") + if err != nil { + return fmt.Errorf("marshal evidence summary: %w", err) + } + path := filepath.Join(dir, "summary.json") + if err := os.WriteFile(path, append(payload, '\n'), 0o644); err != nil { + return fmt.Errorf("write evidence summary: %w", err) + } + return nil +} + +func sha256Hex(data []byte) string { + sum := sha256.Sum256(data) + return hex.EncodeToString(sum[:]) +} diff --git a/integrations/cocos/cmd/hardware-attestation-redteam/main_test.go b/integrations/cocos/cmd/hardware-attestation-redteam/main_test.go new file mode 100644 index 00000000..c64294f4 --- /dev/null +++ b/integrations/cocos/cmd/hardware-attestation-redteam/main_test.go @@ -0,0 +1,126 @@ +// Copyright (c) Ultraviolet +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "errors" + "os" + "path/filepath" + "strings" + "testing" + + sevsnppb "github.com/google/go-sev-guest/proto/sevsnp" + "google.golang.org/protobuf/proto" +) + +func TestValidateChallengeBindingRejectsStaleEvidence(t *testing.T) { + challengeA := make([]byte, reportDataSize) + challengeB := make([]byte, reportDataSize) + challengeA[0] = 1 + challengeB[0] = 2 + + if err := validateChallengeBinding(challengeA, challengeA); err != nil { + t.Fatalf("validateChallengeBinding() rejected matching challenge: %v", err) + } + err := validateChallengeBinding(challengeA, challengeB) + if !errors.Is(err, errChallengeMismatch) { + t.Fatalf("validateChallengeBinding() error = %v, want errChallengeMismatch", err) + } +} + +func TestExtractSEVSNPEvidence(t *testing.T) { + reportData := make([]byte, reportDataSize) + reportData[0] = 0x7a + hostData := make([]byte, 32) + hostData[0] = 0x42 + encoded, err := proto.Marshal(&sevsnppb.Attestation{ + Report: &sevsnppb.Report{ + ReportData: reportData, + HostData: hostData, + }, + }) + if err != nil { + t.Fatalf("proto.Marshal() error = %v", err) + } + + extracted, err := extractSEVSNPEvidence(encoded) + if err != nil { + t.Fatalf("extractSEVSNPEvidence() error = %v", err) + } + if got := extracted.ReportData[0]; got != 0x7a { + t.Fatalf("ReportData[0] = %#x, want 0x7a", got) + } + if got := extracted.HostData[0]; got != 0x42 { + t.Fatalf("HostData[0] = %#x, want 0x42", got) + } +} + +func TestResolvePlatformRejectsUnsupportedInput(t *testing.T) { + for _, platform := range []string{"", "auto", "snp-vtpm", "vtpm", "azure"} { + if _, err := resolvePlatform(platform); err == nil { + t.Fatalf("resolvePlatform(%q) accepted unsupported platform", platform) + } + } +} + +func TestPrepareSEVSNPCertificateCache(t *testing.T) { + called := false + if err := prepareSEVSNPCertificateCache(2, func(vmpl uint) error { + called = true + if vmpl != 2 { + t.Fatalf("VMPL = %d, want 2", vmpl) + } + return nil + }); err != nil { + t.Fatalf("prepareSEVSNPCertificateCache() error = %v", err) + } + if !called { + t.Fatal("prepareSEVSNPCertificateCache() did not fetch certificates") + } + + err := prepareSEVSNPCertificateCache(0, func(uint) error { return errors.New("offline") }) + if err == nil || !strings.Contains(err.Error(), "AMD KDS") { + t.Fatalf("prepareSEVSNPCertificateCache() error = %v, want AMD KDS context", err) + } +} + +func TestFullModuleVerificationRequiresBothPolicies(t *testing.T) { + _, err := verifyFullModulePath(platformSNP, nil, nil, nil, nil, runOptions{}) + if err == nil { + t.Fatal("verifyFullModulePath() accepted missing policy inputs") + } +} + +func TestQualificationBindingCopiesReportDataAndCreatesNonce(t *testing.T) { + reportData := make([]byte, reportDataSize) + reportData[0] = 0x42 + binding, nonce, err := qualificationBinding(reportData) + if err != nil { + t.Fatal(err) + } + if binding.ReportData[0] != 0x42 || len(nonce) != len(binding.Nonce) { + t.Fatalf("unexpected qualification binding: report=%x nonce_len=%d", binding.ReportData[0], len(nonce)) + } + if string(nonce) != string(binding.Nonce[:]) { + t.Fatal("returned nonce does not match binding nonce") + } +} + +func TestWriteSummary(t *testing.T) { + dir := t.TempDir() + summary := &runSummary{ + TimestampUTC: "2026-06-30T00:00:00Z", + Platform: platformSNP, + EvidenceASHA256: "evidence-a", + EvidenceBSHA256: "evidence-b", + ChallengeASHA256: "challenge-a", + ChallengeBSHA256: "challenge-b", + } + if err := writeSummary(dir, summary); err != nil { + t.Fatalf("writeSummary() error = %v", err) + } + if _, err := os.Stat(filepath.Join(dir, "summary.json")); err != nil { + t.Fatalf("summary.json was not written: %v", err) + } +} diff --git a/integrations/cocos/evidencesource/source.go b/integrations/cocos/evidencesource/source.go new file mode 100644 index 00000000..4749c668 --- /dev/null +++ b/integrations/cocos/evidencesource/source.go @@ -0,0 +1,64 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +// Package evidencesource adapts the legacy Cocos attestation-service client to +// the platform-neutral ASB evidence-source boundary. +package evidencesource + +import ( + "context" + "errors" + "fmt" + + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + platformattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" +) + +var ( + ErrMissingEvidenceClient = errors.New("attestation evidence source requires a client") + ErrInvalidPlatform = errors.New("attestation evidence source requires a confidential-computing platform") +) + +// Client is the smallest legacy Cocos client surface needed by the adapter. +// Keeping this interface local avoids coupling the integration module to the +// concrete gRPC client implementation. +type Client interface { + GetAttestation(context.Context, [64]byte, [32]byte, platformattestation.PlatformType) ([]byte, error) +} + +// EvidenceSource adapts the legacy attestation-service client to ASB's +// platform-neutral evidence source. The platform is fixed by local composition +// and is never selected from peer-provided evidence. +type EvidenceSource struct { + client Client + platform platformattestation.PlatformType +} + +func NewEvidenceSource(client Client, platform platformattestation.PlatformType) (*EvidenceSource, error) { + if client == nil { + return nil, ErrMissingEvidenceClient + } + switch platform { + case platformattestation.SNP, platformattestation.TDX: + // Supported by Client.GetAttestation. + default: + return nil, ErrInvalidPlatform + } + return &EvidenceSource{client: client, platform: platform}, nil +} + +func (s *EvidenceSource) GetEvidence(ctx context.Context, request eaattestation.EvidenceRequest) (eaattestation.EvidenceResult, error) { + if s == nil || s.client == nil { + return eaattestation.EvidenceResult{}, ErrMissingEvidenceClient + } + evidence, err := s.client.GetAttestation(ctx, request.ReportData, request.Nonce, s.platform) + if err != nil { + return eaattestation.EvidenceResult{}, fmt.Errorf("fetch platform evidence: %w", err) + } + return eaattestation.EvidenceResult{ + MediaType: "application/eat+cwt", + Evidence: evidence, + }, nil +} + +var _ eaattestation.EvidenceSource = (*EvidenceSource)(nil) diff --git a/integrations/cocos/evidencesource/source_test.go b/integrations/cocos/evidencesource/source_test.go new file mode 100644 index 00000000..491cd07c --- /dev/null +++ b/integrations/cocos/evidencesource/source_test.go @@ -0,0 +1,85 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package evidencesource + +import ( + "context" + "errors" + "testing" + + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + platformattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" +) + +type evidenceSourceClient struct { + reportData [64]byte + nonce [32]byte + platform platformattestation.PlatformType + evidence []byte + err error +} + +func (c *evidenceSourceClient) GetAttestation(_ context.Context, reportData [64]byte, nonce [32]byte, platform platformattestation.PlatformType) ([]byte, error) { + c.reportData = reportData + c.nonce = nonce + c.platform = platform + return append([]byte(nil), c.evidence...), c.err +} + +func TestEvidenceSourceUsesLocallyConfiguredPlatform(t *testing.T) { + client := &evidenceSourceClient{evidence: []byte("evidence")} + source, err := NewEvidenceSource(client, platformattestation.SNP) + if err != nil { + t.Fatal(err) + } + request := eaattestation.EvidenceRequest{} + request.ReportData[0] = 0x42 + request.Nonce[0] = 0x24 + + result, err := source.GetEvidence(context.Background(), request) + if err != nil { + t.Fatal(err) + } + if client.platform != platformattestation.SNP { + t.Fatalf("platform = %v, want SNP", client.platform) + } + if client.reportData != request.ReportData || client.nonce != request.Nonce { + t.Fatal("evidence source did not preserve the ASB binding request") + } + if result.MediaType != "application/eat+cwt" || string(result.Evidence) != "evidence" { + t.Fatalf("unexpected evidence result: %#v", result) + } +} + +func TestNewEvidenceSourceRejectsMissingInputs(t *testing.T) { + if _, err := NewEvidenceSource(nil, platformattestation.SNP); !errors.Is(err, ErrMissingEvidenceClient) { + t.Fatalf("error = %v, want ErrMissingEvidenceClient", err) + } + if _, err := NewEvidenceSource(&evidenceSourceClient{}, platformattestation.NoCC); !errors.Is(err, ErrInvalidPlatform) { + t.Fatalf("error = %v, want ErrInvalidPlatform", err) + } + for _, platform := range []platformattestation.PlatformType{ + platformattestation.SNPvTPM, + platformattestation.VTPM, + platformattestation.Azure, + } { + if _, err := NewEvidenceSource(&evidenceSourceClient{}, platform); !errors.Is(err, ErrInvalidPlatform) { + t.Fatalf("platform %v error = %v, want ErrInvalidPlatform", platform, err) + } + } + if _, err := NewEvidenceSource(&evidenceSourceClient{}, platformattestation.PlatformType(999)); !errors.Is(err, ErrInvalidPlatform) { + t.Fatalf("unknown platform error = %v, want ErrInvalidPlatform", err) + } +} + +func TestEvidenceSourceFailsClosedOnClientError(t *testing.T) { + wantErr := errors.New("collector unavailable") + source, err := NewEvidenceSource(&evidenceSourceClient{err: wantErr}, platformattestation.TDX) + if err != nil { + t.Fatal(err) + } + if _, err := source.GetEvidence(context.Background(), eaattestation.EvidenceRequest{}); !errors.Is(err, wantErr) { + t.Fatalf("error = %v, want wrapped collector error", err) + } +} diff --git a/integrations/cocos/go.mod b/integrations/cocos/go.mod new file mode 100644 index 00000000..317eb9ee --- /dev/null +++ b/integrations/cocos/go.mod @@ -0,0 +1,116 @@ +module github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos + +go 1.26.0 + +require ( + github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/snp v0.0.0-00010101000000-000000000000 + github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/tdx v0.0.0-00010101000000-000000000000 + github.com/ToppyMicroServices/agents-secure-binding/v2 v2.0.0-00010101000000-000000000000 + github.com/caarlos0/env/v11 v11.4.0 + github.com/fatih/color v1.18.0 + github.com/go-kit/kit v0.13.0 + github.com/google/go-sev-guest v0.14.1 + github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843 + github.com/google/go-tpm-tools v0.4.7 + github.com/prometheus/client_golang v1.23.2 + github.com/spf13/cobra v1.10.2 + github.com/spf13/pflag v1.0.10 + github.com/veraison/corim v1.1.2 + golang.org/x/sync v0.21.0 + google.golang.org/protobuf v1.36.11 +) + +require ( + cel.dev/expr v0.25.1 // indirect + cloud.google.com/go v0.121.6 // indirect + cloud.google.com/go/auth v0.16.5 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.5.2 // indirect + cloud.google.com/go/monitoring v1.24.2 // indirect + cloud.google.com/go/storage v1.57.2 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/caarlos0/env/v10 v10.0.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.1 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/google/gce-tcb-verifier v0.3.1 // indirect + github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc // indirect + github.com/google/go-tpm v0.9.8 // indirect + github.com/google/logger v1.1.1 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lestrrat-go/blackmagic v1.0.4 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.6 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/jwx/v2 v2.1.6 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + github.com/segmentio/asm v1.2.1 // indirect + github.com/spf13/cast v1.4.1 // indirect + github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect + github.com/veraison/eat v0.0.0-20210331113810-3da8a4dd42ff // indirect + github.com/veraison/go-cose v1.3.0 // indirect + github.com/veraison/swid v1.1.1-0.20230911094910-8ffdd07a22ca // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/sdk v1.43.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.39.0 // indirect + golang.org/x/time v0.12.0 // indirect + google.golang.org/api v0.247.0 // indirect + google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect + google.golang.org/grpc v1.82.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +// Prepublication monorepo wiring only. Remove these local replacements and +// require released versions before tagging this integration module. +replace github.com/ToppyMicroServices/agents-secure-binding/v2 => ../.. + +replace github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/snp => ../../modules/attestation/snp + +replace github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/tdx => ../../modules/attestation/tdx diff --git a/integrations/cocos/go.sum b/integrations/cocos/go.sum new file mode 100644 index 00000000..8245b610 --- /dev/null +++ b/integrations/cocos/go.sum @@ -0,0 +1,251 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= +cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= +cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= +cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= +cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E= +cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY= +cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= +cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= +cloud.google.com/go/storage v1.57.2 h1:sVlym3cHGYhrp6XZKkKb+92I1V42ks2qKKpB0CF5Mb4= +cloud.google.com/go/storage v1.57.2/go.mod h1:n5ijg4yiRXXpCu0sJTD6k+eMf7GRrJmPyr9YxLXGHOk= +cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= +cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA= +github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18= +github.com/caarlos0/env/v11 v11.4.0 h1:Kcb6t5kIIr4XkoQC9AF2j+8E1Jsrl3Wz/hhm1LtoGAc= +github.com/caarlos0/env/v11 v11.4.0/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= +github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= +github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= +github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= +github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE= +github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gce-tcb-verifier v0.3.1 h1:4L9YgkOtqC2U7cj4FofCUufHFCCpdD4Y0yPKI8UhOhI= +github.com/google/gce-tcb-verifier v0.3.1/go.mod h1:GZCDLQxmEOCqUTL2BMB/zjo+hgXdUrR0Wgwz1OrwRYg= +github.com/google/go-attestation v0.6.1 h1:HcdQn+2L3yyGiKWHREJNSjSVAftyF6qB1bkqksbB0FM= +github.com/google/go-attestation v0.6.1/go.mod h1:Kin36coq5+yhHymNoDm4W/iL7QwMhDOCR/5ksu3SxcA= +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-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc h1:SG12DWUUM5igxm+//YX5Yq4vhdoRnOG9HkCodkOn+YU= +github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba h1:05m5+kgZjxYUZrx3bZfkKHl6wkch+Khao6N21rFHInk= +github.com/google/go-eventlog v0.0.2-0.20241003021507-01bb555f7cba/go.mod h1:7huE5P8w2NTObSwSJjboHmB7ioBNblkijdzoVa2skfQ= +github.com/google/go-sev-guest v0.14.1 h1:j/DXy9jk1qSW/dEV9vDiQnhAVFD1zqnWNVu6p1J0Jgo= +github.com/google/go-sev-guest v0.14.1/go.mod h1:SK9vW+uyfuzYdVN0m8BShL3OQCtXZe/JPF7ZkpD3760= +github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843 h1:+MoPobRN9HrDhGyn6HnF5NYo4uMBKaiFqAtf/D/OB4A= +github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843/go.mod h1:g/n8sKITIT9xRivBUbizo34DTsUm2nN2uU3A662h09g= +github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= +github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= +github.com/google/go-tpm-tools v0.4.7 h1:J3ycC8umYxM9A4eF73EofRZu4BxY0jjQnUnkhIBbvws= +github.com/google/go-tpm-tools v0.4.7/go.mod h1:gSyXTZHe3fgbzb6WEGd90QucmsnT1SRdlye82gH8QjQ= +github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= +github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= +github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= +github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA= +github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 h1:xzZOeCMQLA/W198ZkdVdt4EKFKJtS26B773zNU377ZY= +github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/veraison/corim v1.1.2 h1:JIk6ZK/OzKEb0FJUFHSnmkn67yyGy+5NChYax0bwttA= +github.com/veraison/corim v1.1.2/go.mod h1:yoN6+vVQJgzS926nheCbJi68SvOlN0CpiPuTxYSe5FU= +github.com/veraison/eat v0.0.0-20210331113810-3da8a4dd42ff h1:r6I2eJL/z8dp5flsQIKHMeDjyV6UO8If3MaVBLvTjF4= +github.com/veraison/eat v0.0.0-20210331113810-3da8a4dd42ff/go.mod h1:+kxt8iuFiVvKRs2VQ1Ho7bbAScXAB/kHFFuP5Biw19I= +github.com/veraison/go-cose v1.3.0 h1:2/H5w8kdSpQJyVtIhx8gmwPJ2uSz1PkyWFx0idbd7rk= +github.com/veraison/go-cose v1.3.0/go.mod h1:df09OV91aHoQWLmy1KsDdYiagtXgyAwAl8vFeFn1gMc= +github.com/veraison/swid v1.1.1-0.20230911094910-8ffdd07a22ca h1:osmCKwWO/xM68Kz+rIXio1DNzEY2NdJOpGpoy5r8NlE= +github.com/veraison/swid v1.1.1-0.20230911094910-8ffdd07a22ca/go.mod h1:d5jt76uMNbTfQ+f2qU4Lt8RvWOTsv6PFgstIM1QdMH0= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU= +go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 h1:XmiuHzgJt067+a6kwyAzkhXooYVv3/TOw9cM2VfJgUM= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0/go.mod h1:KDgtbWKTQs4bM+VPUr6WlL9m/WXcmkCcBlIzqxPGzmI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 h1:PnV4kVnw0zOmwwFkAzCN5O07fw1YOIQor120zrh0AVo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0/go.mod h1:ofAwF4uinaf8SXdVzzbL4OsxJ3VfeEg3f/F6CeF49/Y= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.247.0 h1:tSd/e0QrUlLsrwMKmkbQhYVa109qIintOls2Wh6bngc= +google.golang.org/api v0.247.0/go.mod h1:r1qZOPmxXffXg6xS5uhx16Fa/UFY8QU/K4bfKrnvovM= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec= +google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE= +google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/integrations/cocos/internal/logger/protohandler.go b/integrations/cocos/internal/logger/protohandler.go new file mode 100644 index 00000000..92779b88 --- /dev/null +++ b/integrations/cocos/internal/logger/protohandler.go @@ -0,0 +1,116 @@ +// Copyright (c) Ultraviolet +// SPDX-License-Identifier: Apache-2.0 +package logger + +import ( + "context" + "io" + "log/slog" + + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/cvms" + "github.com/ToppyMicroServices/agents-secure-binding/v2/agent/events" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/timestamppb" +) + +var _ slog.Handler = (*handler)(nil) + +type handler struct { + opts slog.HandlerOptions + w io.Writer + cmpID string + queue chan *cvms.ClientStreamMessage +} + +func NewProtoHandler(conn io.Writer, opts *slog.HandlerOptions, queue chan *cvms.ClientStreamMessage) slog.Handler { + if opts == nil { + opts = &slog.HandlerOptions{} + } + h := &handler{ + opts: *opts, + w: conn, + queue: queue, + } + + return h +} + +func (h *handler) Enabled(_ context.Context, l slog.Level) bool { + minLevel := slog.LevelInfo + if h.opts.Level != nil { + minLevel = h.opts.Level.Level() + } + return l >= minLevel +} + +func (h *handler) Handle(_ context.Context, r slog.Record) error { + slog.Info("logging message", "message", r.Message) + message := r.Message + timestamp := timestamppb.New(r.Time) + level := r.Level.String() + + chunkSize := 500 + numChunks := (len(message) + chunkSize - 1) / chunkSize + + for i := 0; i < numChunks; i++ { + start := i * chunkSize + end := start + chunkSize + if end > len(message) { + end = len(message) + } + + chunk := message[start:end] + + agentLog := events.EventsLogs{ + Message: &events.EventsLogs_AgentLog{ + AgentLog: &events.AgentLog{ + Timestamp: timestamp, + Message: chunk, + Level: level, + ComputationId: h.cmpID, + }, + }, + } + + h.queue <- &cvms.ClientStreamMessage{ + Message: &cvms.ClientStreamMessage_AgentLog{ + AgentLog: &cvms.AgentLog{ + Timestamp: timestamp, + Message: chunk, + Level: level, + ComputationId: h.cmpID, + }, + }, + } + + b, err := protojson.Marshal(&agentLog) + if err != nil { + return err + } + + _, err = h.w.Write(b) + if err != nil { + return err + } + + _, err = h.w.Write([]byte("\n")) + if err != nil { + return err + } + } + + return nil +} + +func (h *handler) WithAttrs(attrs []slog.Attr) slog.Handler { + panic("unimplemented") +} + +func (h *handler) WithGroup(name string) slog.Handler { + h.cmpID = name + return h +} + +func (h *handler) Close() error { + return nil +} diff --git a/integrations/cocos/internal/platformselect/select.go b/integrations/cocos/internal/platformselect/select.go new file mode 100644 index 00000000..bc2866fc --- /dev/null +++ b/integrations/cocos/internal/platformselect/select.go @@ -0,0 +1,53 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +// Package platformselect resolves the direct hardware platform selected by +// trusted local Cocos configuration. It deliberately ignores vTPM and cloud +// metadata so those legacy signals cannot redirect the direct SNP/TDX adapter. +package platformselect + +import ( + "errors" + "fmt" + "strings" + + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" +) + +var ( + ErrUnsupportedPlatform = errors.New("Cocos direct attestation platform must be snp, tdx, auto, or none") + ErrAmbiguousPlatform = errors.New("both direct SNP and TDX devices were detected; select one explicitly") +) + +// Resolve returns a direct platform from trusted local configuration. An +// explicit selection is not a hardware qualification check; evidence +// collection still fails closed if the selected device is unavailable. +func Resolve(configured string) (attestation.PlatformType, error) { + return resolve(configured, attestation.SevSnpGuestDeviceExists, attestation.TDXGuestDeviceExists) +} + +func resolve(configured string, snpExists, tdxExists func() bool) (attestation.PlatformType, error) { + switch strings.ToLower(strings.TrimSpace(configured)) { + case "snp": + return attestation.SNP, nil + case "tdx": + return attestation.TDX, nil + case "none", "no-cc": + return attestation.NoCC, nil + case "", "auto": + snp := snpExists != nil && snpExists() + tdx := tdxExists != nil && tdxExists() + switch { + case snp && tdx: + return attestation.NoCC, ErrAmbiguousPlatform + case snp: + return attestation.SNP, nil + case tdx: + return attestation.TDX, nil + default: + return attestation.NoCC, nil + } + default: + return attestation.NoCC, fmt.Errorf("%w: %q", ErrUnsupportedPlatform, configured) + } +} diff --git a/integrations/cocos/internal/platformselect/select_test.go b/integrations/cocos/internal/platformselect/select_test.go new file mode 100644 index 00000000..d2bb33ec --- /dev/null +++ b/integrations/cocos/internal/platformselect/select_test.go @@ -0,0 +1,55 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package platformselect + +import ( + "errors" + "testing" + + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" +) + +func TestResolveExplicitDirectPlatformDoesNotUseLegacyDetection(t *testing.T) { + panicProbe := func() bool { panic("explicit selection must not probe hardware") } + for name, want := range map[string]attestation.PlatformType{ + "snp": attestation.SNP, + "tdx": attestation.TDX, + } { + got, err := resolve(name, panicProbe, panicProbe) + if err != nil || got != want { + t.Fatalf("resolve(%q) = (%v, %v), want (%v, nil)", name, got, err, want) + } + } +} + +func TestResolveAutoUsesDirectDevicesOnly(t *testing.T) { + tests := []struct { + name string + snp bool + tdx bool + want attestation.PlatformType + wantError error + }{ + {name: "no hardware", want: attestation.NoCC}, + {name: "direct SNP even when a vTPM may also exist", snp: true, want: attestation.SNP}, + {name: "direct TDX", tdx: true, want: attestation.TDX}, + {name: "ambiguous direct devices", snp: true, tdx: true, want: attestation.NoCC, wantError: ErrAmbiguousPlatform}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := resolve("auto", func() bool { return test.snp }, func() bool { return test.tdx }) + if got != test.want || !errors.Is(err, test.wantError) { + t.Fatalf("resolve(auto) = (%v, %v), want (%v, %v)", got, err, test.want, test.wantError) + } + }) + } +} + +func TestResolveRejectsLegacyPlatforms(t *testing.T) { + for _, name := range []string{"snp-vtpm", "vtpm", "azure", "unknown"} { + if _, err := resolve(name, nil, nil); !errors.Is(err, ErrUnsupportedPlatform) { + t.Fatalf("resolve(%q) error = %v, want ErrUnsupportedPlatform", name, err) + } + } +} diff --git a/integrations/cocos/internal/runtime/logging/logging.go b/integrations/cocos/internal/runtime/logging/logging.go new file mode 100644 index 00000000..56c55919 --- /dev/null +++ b/integrations/cocos/internal/runtime/logging/logging.go @@ -0,0 +1,31 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package logging + +import ( + "bytes" + "fmt" + "io" + "log/slog" + "os" +) + +// New returns a JSON slog logger configured from a slog level string. +func New(w io.Writer, levelText string) (*slog.Logger, error) { + var level slog.Level + if err := level.UnmarshalText([]byte(levelText)); err != nil { + return nil, fmt.Errorf("invalid log level %q: %w", levelText, err) + } + return slog.New(slog.NewJSONHandler(w, &slog.HandlerOptions{Level: level})), nil +} + +// NewMock returns a quiet in-memory logger for tests. +func NewMock() *slog.Logger { + return slog.New(slog.NewJSONHandler(&bytes.Buffer{}, nil)) +} + +// ExitWithError exits with the value stored in code. +func ExitWithError(code *int) { + os.Exit(*code) +} diff --git a/integrations/cocos/internal/runtime/metrics/metrics.go b/integrations/cocos/internal/runtime/metrics/metrics.go new file mode 100644 index 00000000..b2af98b9 --- /dev/null +++ b/integrations/cocos/internal/runtime/metrics/metrics.go @@ -0,0 +1,31 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package metrics + +import ( + kitprometheus "github.com/go-kit/kit/metrics/prometheus" + stdprometheus "github.com/prometheus/client_golang/prometheus" +) + +// MakeMetrics returns Prometheus counters and latency summaries for service methods. +func MakeMetrics(namespace, subsystem string) (*kitprometheus.Counter, *kitprometheus.Summary) { + counter := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: "request_count", + Help: "Number of requests received.", + }, []string{"method"}) + latency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: "request_latency_microseconds", + Help: "Total duration of requests in microseconds.", + Objectives: map[float64]float64{ + 0.5: 0.05, + 0.9: 0.01, + 0.99: 0.001, + }, + }, []string{"method"}) + return counter, latency +} diff --git a/integrations/cocos/platformmodule/policy.go b/integrations/cocos/platformmodule/policy.go new file mode 100644 index 00000000..9684ba68 --- /dev/null +++ b/integrations/cocos/platformmodule/policy.go @@ -0,0 +1,76 @@ +// Copyright (c) 2026 ToppyMicroServices OÜ +// SPDX-License-Identifier: Apache-2.0 + +package platformmodule + +import ( + "fmt" + "os" + "strings" + + snpmodule "github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/snp" + sevcheck "github.com/google/go-sev-guest/proto/check" + sevvalidate "github.com/google/go-sev-guest/validate" + sevverify "github.com/google/go-sev-guest/verify" + tdxcheck "github.com/google/go-tdx-guest/proto/checkconfig" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" +) + +// LoadSNPPlatformPolicy loads the go-sev-guest check.Config JSON used for +// signature, revocation, TCB, debug, VMPL, and other report validation. +func LoadSNPPlatformPolicy(path string) (*sevverify.Options, *sevvalidate.Options, error) { + var policy sevcheck.Config + if err := readPlatformPolicy(path, &policy); err != nil { + return nil, nil, fmt.Errorf("load SNP platform policy: %w", err) + } + if policy.RootOfTrust == nil || policy.Policy == nil { + return nil, nil, fmt.Errorf("load SNP platform policy: root_of_trust and policy are required") + } + if policy.RootOfTrust.CheckCrl && policy.RootOfTrust.DisallowNetwork { + return nil, nil, fmt.Errorf("load SNP platform policy: check_crl and disallow_network cannot both be true") + } + verification, err := sevverify.RootOfTrustToOptions(policy.RootOfTrust) + if err != nil { + return nil, nil, fmt.Errorf("load SNP root of trust: %w", err) + } + verification.Product = policy.Policy.Product + verification.Getter = snpmodule.NewKDSGetter() + validation, err := sevvalidate.PolicyToOptions(policy.Policy) + if err != nil { + return nil, nil, fmt.Errorf("load SNP validation policy: %w", err) + } + return verification, validation, nil +} + +// LoadTDXPlatformPolicy loads the go-tdx-guest checkconfig.Config JSON used +// for quote, PCS collateral, TCB, debug, and report-field validation. +func LoadTDXPlatformPolicy(path string) (*tdxcheck.Config, error) { + var policy tdxcheck.Config + if err := readPlatformPolicy(path, &policy); err != nil { + return nil, fmt.Errorf("load TDX platform policy: %w", err) + } + return &policy, nil +} + +func readPlatformPolicy(path string, destination proto.Message) error { + path = strings.TrimSpace(path) + if path == "" { + return fmt.Errorf("platform policy path is required") + } + info, err := os.Stat(path) + if err != nil { + return err + } + if !info.Mode().IsRegular() { + return fmt.Errorf("platform policy is not a regular file") + } + payload, err := os.ReadFile(path) + if err != nil { + return err + } + if err := protojson.Unmarshal(payload, destination); err != nil { + return err + } + return nil +} diff --git a/integrations/cocos/platformmodule/verifier.go b/integrations/cocos/platformmodule/verifier.go new file mode 100644 index 00000000..47cbac80 --- /dev/null +++ b/integrations/cocos/platformmodule/verifier.go @@ -0,0 +1,425 @@ +// Copyright (c) Ultraviolet +// SPDX-License-Identifier: Apache-2.0 + +// Package platformmodule contains the experimental compatibility adapter that +// connects platform evidence appraisal to ASB's platform-neutral evidence +// verifier interface. A verifier is pinned to one locally configured platform; +// peer-provided EAT claims are checked for consistency and never select an +// appraiser. +// +// This package is not production qualification. It preserves the repository's +// legacy platform appraisers. Deployments remain responsible for qualifying +// each selected appraiser's quote-signature, endorsement-chain, collateral, +// revocation, TCB, debug-policy, measurement-policy, and freshness behavior. +package platformmodule + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/subtle" + "errors" + "fmt" + "math/big" + "os" + "time" + + snpmodule "github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/snp" + tdxmodule "github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/tdx" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + asbattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/tdx" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/vtpm" + "github.com/google/go-sev-guest/proto/sevsnp" + sevvalidate "github.com/google/go-sev-guest/validate" + sevverify "github.com/google/go-sev-guest/verify" + tdxcheckconfig "github.com/google/go-tdx-guest/proto/checkconfig" + "github.com/google/go-tpm-tools/proto/attest" + "github.com/veraison/corim/corim" + "google.golang.org/protobuf/proto" +) + +var ( + ErrUnsupportedPlatform = errors.New("attestation platform module: unsupported locally configured platform") + ErrPlatformMismatch = errors.New("attestation platform module: evidence platform does not match locally configured platform") + ErrPolicyUnavailable = errors.New("attestation platform module: policy file is unavailable") + ErrInvalidSNPEvidence = errors.New("attestation platform module: invalid direct SNP evidence") + ErrEATTrustUnavailable = errors.New("attestation platform module: EAT trust configuration is unavailable") + ErrSNPPolicyRequired = errors.New("attestation platform module: direct SNP verification policy is required") + ErrSNPVerification = errors.New("attestation platform module: direct SNP cryptographic verification failed") + ErrSNPValidation = errors.New("attestation platform module: direct SNP policy validation failed") + ErrTDXPolicyRequired = errors.New("attestation platform module: TDX verification policy is required") + ErrTDXVerification = errors.New("attestation platform module: TDX quote verification failed") + ErrCoRIMTrustRequired = errors.New("attestation platform module: signed CoRIM verification key is required") + ErrCoRIMVerification = errors.New("attestation platform module: signed CoRIM verification failed") +) + +const ( + maxEATLifetime = 5 * time.Minute + maxEATClockSkew = 30 * time.Second + platformVerificationTimeout = 2 * time.Minute +) + +// Platform identifies the appraiser selected by trusted local configuration. +// Its zero value is invalid. It is not a wire value and must not be populated +// from peer-provided evidence. +type Platform string + +const ( + PlatformSNP Platform = "snp" + PlatformTDX Platform = "tdx" +) + +type policyEvidenceVerifier struct { + platform Platform + policyPath string + eatVerificationKey *ecdsa.PublicKey + corimVerificationKey *ecdsa.PublicKey + expectedIssuer string + snpVerificationOptions *sevverify.Options + snpValidationOptions *sevvalidate.Options + tdxPolicy *tdxcheckconfig.Config + tdxRuntime *tdxmodule.RuntimeOptions +} + +var _ eaattestation.EvidenceVerifier = (*policyEvidenceVerifier)(nil) + +// VerifierConfig contains trust configuration selected by the local ASB +// deployment. None of these values may be derived from peer evidence. +type VerifierConfig struct { + Platform Platform + PolicyPath string + EATVerificationKey *ecdsa.PublicKey + CoRIMVerificationKey *ecdsa.PublicKey + ExpectedIssuer string + SNPVerificationOptions *sevverify.Options + SNPValidationOptions *sevvalidate.Options + TDXPolicy *tdxcheckconfig.Config + // TDXRuntimeOptions permits a reviewed collateral getter and fixed clock + // for deterministic qualification. Nil uses the module's bounded HTTPS + // getter and current time. + TDXRuntimeOptions *tdxmodule.RuntimeOptions +} + +// NewEvidenceVerifier constructs a compatibility verifier for exactly one +// locally selected platform. Evidence claiming a different platform is +// rejected; the claim never causes dispatch to another appraiser. +func NewEvidenceVerifier(config VerifierConfig) (eaattestation.EvidenceVerifier, error) { + if !config.Platform.valid() { + return nil, fmt.Errorf("%w: %q", ErrUnsupportedPlatform, config.Platform) + } + if err := validateEATTrustConfig(config.EATVerificationKey, config.ExpectedIssuer); err != nil { + return nil, err + } + if config.Platform == PlatformSNP { + if err := validateDirectSNPConfig(config.SNPVerificationOptions, config.SNPValidationOptions); err != nil { + return nil, err + } + } + if config.Platform == PlatformTDX { + if err := validateTDXConfig(config.TDXPolicy); err != nil { + return nil, err + } + } + info, err := os.Stat(config.PolicyPath) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrPolicyUnavailable, err) + } + if !info.Mode().IsRegular() { + return nil, fmt.Errorf("%w: not a regular file", ErrPolicyUnavailable) + } + return &policyEvidenceVerifier{ + platform: config.Platform, + policyPath: config.PolicyPath, + eatVerificationKey: cloneECDSAPublicKey(config.EATVerificationKey), + corimVerificationKey: cloneOptionalECDSAPublicKey(config.CoRIMVerificationKey), + expectedIssuer: config.ExpectedIssuer, + snpVerificationOptions: cloneSNPVerificationOptions(config.SNPVerificationOptions), + snpValidationOptions: cloneSNPValidationOptions(config.SNPValidationOptions), + tdxPolicy: cloneTDXPolicy(config.TDXPolicy), + tdxRuntime: cloneTDXRuntimeOptions(config.TDXRuntimeOptions), + }, nil +} + +func (v *policyEvidenceVerifier) VerifyEvidence(evidence []byte, expected eaattestation.EvidenceBinding) error { + if v == nil || !v.platform.valid() { + return ErrUnsupportedPlatform + } + if v.policyPath == "" { + return fmt.Errorf("attestation platform module: attestation policy path is not set") + } + claims, err := eat.DecodeVerifiedCBOR(evidence, v.eatVerificationKey) + if err != nil { + return fmt.Errorf("attestation platform module: failed to verify EAT evidence: %w", err) + } + if err := verifyEATProfile(claims, v.expectedIssuer, time.Now()); err != nil { + return err + } + if !constantTimeEqual(claims.Nonce, expected.Nonce[:]) { + return fmt.Errorf("attestation platform module: evidence nonce does not match TLS exporter binding") + } + claimedPlatform, err := platformFromEATClaim(claims.PlatformType) + if err != nil { + return err + } + if claimedPlatform != v.platform { + return fmt.Errorf("%w: configured=%q evidence=%q", ErrPlatformMismatch, v.platform, claimedPlatform) + } + appraisalEvidence, err := v.prepareEvidenceForAppraisal(claims.RawReport, expected) + if err != nil { + return err + } + manifest, err := loadCoRIM(v.policyPath, v.corimVerificationKey, time.Now()) + if err != nil { + return err + } + verifier, err := platformVerifier(v.platform) + if err != nil { + return err + } + return verifier.VerifyWithCoRIM(appraisalEvidence, manifest) +} + +func validateEATTrustConfig(key *ecdsa.PublicKey, expectedIssuer string) error { + if key == nil || key.Curve != elliptic.P256() || key.X == nil || key.Y == nil || !key.Curve.IsOnCurve(key.X, key.Y) { + return fmt.Errorf("%w: a valid P-256 verification key is required", ErrEATTrustUnavailable) + } + if expectedIssuer == "" { + return fmt.Errorf("%w: expected issuer is required", ErrEATTrustUnavailable) + } + return nil +} + +func validateDirectSNPConfig(verification *sevverify.Options, validation *sevvalidate.Options) error { + if err := snpmodule.ValidateConfig(verification, validation); err != nil { + return fmt.Errorf("%w: %v", ErrSNPPolicyRequired, err) + } + return nil +} + +func validateTDXConfig(policy *tdxcheckconfig.Config) error { + if err := tdxmodule.ValidateConfig(policy); err != nil { + return fmt.Errorf("%w: %v", ErrTDXPolicyRequired, err) + } + return nil +} + +func cloneECDSAPublicKey(key *ecdsa.PublicKey) *ecdsa.PublicKey { + return &ecdsa.PublicKey{ + Curve: key.Curve, + X: new(big.Int).Set(key.X), + Y: new(big.Int).Set(key.Y), + } +} + +func cloneOptionalECDSAPublicKey(key *ecdsa.PublicKey) *ecdsa.PublicKey { + if key == nil { + return nil + } + return cloneECDSAPublicKey(key) +} + +func cloneSNPVerificationOptions(options *sevverify.Options) *sevverify.Options { + if options == nil { + return nil + } + clone := *options + if options.Product != nil { + clone.Product = proto.Clone(options.Product).(*sevsnp.SevProduct) + } + return &clone +} + +func cloneSNPValidationOptions(options *sevvalidate.Options) *sevvalidate.Options { + if options == nil { + return nil + } + clone := *options + return &clone +} + +func cloneTDXPolicy(policy *tdxcheckconfig.Config) *tdxcheckconfig.Config { + if policy == nil { + return nil + } + return proto.Clone(policy).(*tdxcheckconfig.Config) +} + +func cloneTDXRuntimeOptions(runtime *tdxmodule.RuntimeOptions) *tdxmodule.RuntimeOptions { + if runtime == nil { + return nil + } + clone := *runtime + return &clone +} + +func verifyEATProfile(claims *eat.EATClaims, expectedIssuer string, now time.Time) error { + if claims == nil { + return fmt.Errorf("attestation platform module: EAT claims are missing") + } + if claims.Issuer != expectedIssuer { + return fmt.Errorf("attestation platform module: EAT issuer mismatch") + } + if claims.IssuedAt <= 0 || claims.ExpiresAt <= claims.IssuedAt { + return fmt.Errorf("attestation platform module: invalid EAT validity interval") + } + if claims.ExpiresAt-claims.IssuedAt > int64(maxEATLifetime/time.Second) { + return fmt.Errorf("attestation platform module: EAT validity interval exceeds %s", maxEATLifetime) + } + nowUnix := now.Unix() + if claims.IssuedAt > nowUnix+int64(maxEATClockSkew/time.Second) { + return fmt.Errorf("attestation platform module: EAT is not yet valid") + } + if claims.ExpiresAt < nowUnix-int64(maxEATClockSkew/time.Second) { + return fmt.Errorf("attestation platform module: EAT has expired") + } + return nil +} + +// prepareEvidenceForAppraisal validates the ASB binding and converts only the +// direct SNP wire representations into the legacy wrapper expected by the +// existing CoRIM appraiser. The peer-controlled platform claim never selects +// this conversion; the caller supplies the locally configured platform. +func (v *policyEvidenceVerifier) prepareEvidenceForAppraisal(report []byte, expected eaattestation.EvidenceBinding) ([]byte, error) { + if v.platform == PlatformTDX { + ctx, cancel := context.WithTimeout(context.Background(), platformVerificationTimeout) + defer cancel() + if err := tdxmodule.Verify(ctx, report, expected.ReportData[:], cloneTDXPolicy(v.tdxPolicy), cloneTDXRuntimeOptions(v.tdxRuntime)); err != nil { + return nil, translateTDXModuleError(err) + } + return report, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), platformVerificationTimeout) + defer cancel() + snpAttestation, err := snpmodule.Verify( + ctx, + report, + expected.ReportData[:], + cloneSNPVerificationOptions(v.snpVerificationOptions), + cloneSNPValidationOptions(v.snpValidationOptions), + ) + if err != nil { + return nil, translateSNPModuleError(err) + } + + wrapped, err := proto.Marshal(&attest.Attestation{ + TeeAttestation: &attest.Attestation_SevSnpAttestation{ + SevSnpAttestation: snpAttestation, + }, + }) + if err != nil { + return nil, fmt.Errorf("%w: failed to wrap evidence for appraisal: %v", ErrInvalidSNPEvidence, err) + } + return wrapped, nil +} + +func translateSNPModuleError(err error) error { + switch { + case errors.Is(err, snpmodule.ErrInvalidConfig): + return fmt.Errorf("%w: %v", ErrSNPPolicyRequired, err) + case errors.Is(err, snpmodule.ErrInvalidEvidence): + return fmt.Errorf("%w: %v", ErrInvalidSNPEvidence, err) + case errors.Is(err, snpmodule.ErrVerification): + return fmt.Errorf("%w: %v", ErrSNPVerification, err) + case errors.Is(err, snpmodule.ErrValidation): + return fmt.Errorf("%w: %v", ErrSNPValidation, err) + default: + return err + } +} + +func translateTDXModuleError(err error) error { + if errors.Is(err, tdxmodule.ErrPolicyRequired) { + return fmt.Errorf("%w: %v", ErrTDXPolicyRequired, err) + } + return fmt.Errorf("%w: %v", ErrTDXVerification, err) +} + +func constantTimeEqual(a, b []byte) bool { + return subtle.ConstantTimeCompare(a, b) == 1 +} + +func loadCoRIM(path string, verificationKey *ecdsa.PublicKey, now time.Time) (*corim.UnsignedCorim, error) { + corimBytes, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("attestation platform module: failed to read CoRIM file: %w", err) + } + + var uc corim.UnsignedCorim + if err := uc.FromCBOR(corimBytes); err == nil { + if verificationKey != nil { + return nil, fmt.Errorf("%w: unsigned CoRIM supplied while a verification key is configured", ErrCoRIMVerification) + } + if err := validateCoRIMValidity(uc.RimValidity, now); err != nil { + return nil, err + } + return &uc, nil + } + + var sc corim.SignedCorim + if err := sc.FromCOSE(corimBytes); err != nil { + return nil, fmt.Errorf("attestation platform module: failed to parse CoRIM: %w", err) + } + if verificationKey == nil { + return nil, ErrCoRIMTrustRequired + } + if err := sc.Meta.Valid(); err != nil { + return nil, fmt.Errorf("%w: invalid signed CoRIM metadata: %v", ErrCoRIMVerification, err) + } + if err := sc.Verify(verificationKey); err != nil { + return nil, fmt.Errorf("%w: %v", ErrCoRIMVerification, err) + } + if err := validateCoRIMValidity(sc.Meta.Validity, now); err != nil { + return nil, err + } + if err := validateCoRIMValidity(sc.UnsignedCorim.RimValidity, now); err != nil { + return nil, err + } + return &sc.UnsignedCorim, nil +} + +func validateCoRIMValidity(validity *corim.Validity, now time.Time) error { + if validity == nil { + return nil + } + if validity.NotBefore != nil && now.Before(*validity.NotBefore) { + return fmt.Errorf("%w: CoRIM is not yet valid", ErrCoRIMVerification) + } + if now.After(validity.NotAfter) { + return fmt.Errorf("%w: CoRIM has expired", ErrCoRIMVerification) + } + return nil +} + +func platformFromEATClaim(name string) (Platform, error) { + switch name { + case "SNP": + return PlatformSNP, nil + case "TDX": + return PlatformTDX, nil + default: + return "", fmt.Errorf("%w: EAT claim %q", ErrUnsupportedPlatform, name) + } +} + +func platformVerifier(platform Platform) (asbattestation.Verifier, error) { + switch platform { + case PlatformSNP: + return vtpm.NewVerifier(nil), nil + case PlatformTDX: + return tdx.NewVerifier(), nil + default: + return nil, fmt.Errorf("%w: %q", ErrUnsupportedPlatform, platform) + } +} + +func (p Platform) valid() bool { + switch p { + case PlatformSNP, PlatformTDX: + return true + default: + return false + } +} diff --git a/integrations/cocos/platformmodule/verifier_test.go b/integrations/cocos/platformmodule/verifier_test.go new file mode 100644 index 00000000..3a82c98a --- /dev/null +++ b/integrations/cocos/platformmodule/verifier_test.go @@ -0,0 +1,639 @@ +// Copyright (c) Ultraviolet +// SPDX-License-Identifier: Apache-2.0 + +package platformmodule + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "math/big" + "os" + "path/filepath" + "testing" + "time" + + tdxmodule "github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/tdx" + eaattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/eaattestation" + asbattestation "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/corimgen" + "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/attestation/eat" + sevabi "github.com/google/go-sev-guest/abi" + "github.com/google/go-sev-guest/proto/sevsnp" + sevtesting "github.com/google/go-sev-guest/testing" + sevvalidate "github.com/google/go-sev-guest/validate" + sevverify "github.com/google/go-sev-guest/verify" + sevtrust "github.com/google/go-sev-guest/verify/trust" + tdxabi "github.com/google/go-tdx-guest/abi" + tdxcheckconfig "github.com/google/go-tdx-guest/proto/checkconfig" + tdxpb "github.com/google/go-tdx-guest/proto/tdx" + tdxtestdata "github.com/google/go-tdx-guest/testing/testdata" + "github.com/google/go-tpm-tools/proto/attest" + "github.com/veraison/corim/corim" + "google.golang.org/protobuf/proto" +) + +const testEATIssuer = "test-issuer" + +type rejectingHTTPSGetter struct{} + +func (rejectingHTTPSGetter) Get(url string) ([]byte, error) { + return nil, fmt.Errorf("unexpected collateral request: %s", url) +} + +type rejectingTDXGetter struct{} + +func (rejectingTDXGetter) Get(url string) (map[string][]string, []byte, error) { + return nil, nil, fmt.Errorf("offline TDX collateral: %s", url) +} + +func testEATSigningKey(t *testing.T) *ecdsa.PrivateKey { + t.Helper() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + return key +} + +func testVerifierConfig(platform Platform, policyPath string, key *ecdsa.PublicKey) VerifierConfig { + config := VerifierConfig{ + Platform: platform, + PolicyPath: policyPath, + EATVerificationKey: key, + ExpectedIssuer: testEATIssuer, + } + if platform == PlatformSNP { + vmpl := 0 + config.SNPVerificationOptions = &sevverify.Options{ + CheckRevocations: true, + Getter: rejectingHTTPSGetter{}, + Product: sevabi.DefaultSevProduct(), + } + config.SNPValidationOptions = &sevvalidate.Options{VMPL: &vmpl} + } + if platform == PlatformTDX { + config.TDXPolicy = &tdxcheckconfig.Config{ + RootOfTrust: &tdxcheckconfig.RootOfTrust{CheckCrl: true, GetCollateral: true}, + Policy: &tdxcheckconfig.Policy{ + HeaderPolicy: &tdxcheckconfig.HeaderPolicy{}, + TdQuoteBodyPolicy: &tdxcheckconfig.TDQuoteBodyPolicy{ + TdAttributes: make([]byte, tdxabi.TdAttributesSize), + }, + }, + } + } + return config +} + +func testEvidenceBinding() eaattestation.EvidenceBinding { + var binding eaattestation.EvidenceBinding + for i := range binding.ReportData { + binding.ReportData[i] = byte(i + 1) + } + for i := range binding.Nonce { + binding.Nonce[i] = byte(0xa0 + i) + } + return binding +} + +func TestNewEvidenceVerifierRequiresLocalPlatform(t *testing.T) { + policyPath := testPolicyPath(t) + key := testEATSigningKey(t) + for _, platform := range []Platform{ + PlatformSNP, + PlatformTDX, + } { + t.Run(string(platform), func(t *testing.T) { + verifier, err := NewEvidenceVerifier(testVerifierConfig(platform, policyPath, &key.PublicKey)) + if err != nil { + t.Fatalf("NewEvidenceVerifier() error = %v", err) + } + if verifier == nil { + t.Fatal("NewEvidenceVerifier() returned nil") + } + }) + } + + for _, platform := range []Platform{"", "SNP", "snp-vtpm", "vtpm", "azure", "no-cc", "unknown"} { + t.Run("reject-"+string(platform), func(t *testing.T) { + config := testVerifierConfig(platform, policyPath, &key.PublicKey) + if _, err := NewEvidenceVerifier(config); !errors.Is(err, ErrUnsupportedPlatform) { + t.Fatalf("NewEvidenceVerifier() error = %v, want ErrUnsupportedPlatform", err) + } + }) + } +} + +func TestNewEvidenceVerifierRequiresPolicyFile(t *testing.T) { + key := testEATSigningKey(t) + if _, err := NewEvidenceVerifier(testVerifierConfig(PlatformSNP, "", &key.PublicKey)); !errors.Is(err, ErrPolicyUnavailable) { + t.Fatalf("NewEvidenceVerifier() error = %v, want ErrPolicyUnavailable", err) + } + if _, err := NewEvidenceVerifier(testVerifierConfig(PlatformSNP, t.TempDir(), &key.PublicKey)); !errors.Is(err, ErrPolicyUnavailable) { + t.Fatalf("NewEvidenceVerifier() error = %v, want ErrPolicyUnavailable", err) + } +} + +func TestNewEvidenceVerifierRequiresTrustConfiguration(t *testing.T) { + key := testEATSigningKey(t) + policyPath := testPolicyPath(t) + + tests := []struct { + name string + mutate func(*VerifierConfig) + want error + }{ + {name: "missing EAT key", mutate: func(c *VerifierConfig) { c.EATVerificationKey = nil }, want: ErrEATTrustUnavailable}, + {name: "missing issuer", mutate: func(c *VerifierConfig) { c.ExpectedIssuer = "" }, want: ErrEATTrustUnavailable}, + {name: "missing SNP verification options", mutate: func(c *VerifierConfig) { c.SNPVerificationOptions = nil }, want: ErrSNPPolicyRequired}, + {name: "missing SNP validation options", mutate: func(c *VerifierConfig) { c.SNPValidationOptions = nil }, want: ErrSNPPolicyRequired}, + {name: "revocation disabled", mutate: func(c *VerifierConfig) { c.SNPVerificationOptions.CheckRevocations = false }, want: ErrSNPPolicyRequired}, + {name: "missing collateral getter", mutate: func(c *VerifierConfig) { c.SNPVerificationOptions.Getter = nil }, want: ErrSNPPolicyRequired}, + {name: "missing expected product", mutate: func(c *VerifierConfig) { c.SNPVerificationOptions.Product = nil }, want: ErrSNPPolicyRequired}, + {name: "debug permitted", mutate: func(c *VerifierConfig) { c.SNPValidationOptions.GuestPolicy.Debug = true }, want: ErrSNPPolicyRequired}, + {name: "missing VMPL", mutate: func(c *VerifierConfig) { c.SNPValidationOptions.VMPL = nil }, want: ErrSNPPolicyRequired}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + config := testVerifierConfig(PlatformSNP, policyPath, &key.PublicKey) + tc.mutate(&config) + if _, err := NewEvidenceVerifier(config); !errors.Is(err, tc.want) { + t.Fatalf("NewEvidenceVerifier() error = %v, want %v", err, tc.want) + } + }) + } +} + +func TestNewEvidenceVerifierRequiresTDXTrustConfiguration(t *testing.T) { + key := testEATSigningKey(t) + policyPath := testPolicyPath(t) + tests := []struct { + name string + mutate func(*VerifierConfig) + }{ + {name: "missing policy", mutate: func(c *VerifierConfig) { c.TDXPolicy = nil }}, + {name: "revocation disabled", mutate: func(c *VerifierConfig) { c.TDXPolicy.RootOfTrust.CheckCrl = false }}, + {name: "collateral disabled", mutate: func(c *VerifierConfig) { c.TDXPolicy.RootOfTrust.GetCollateral = false }}, + {name: "TD attributes omitted", mutate: func(c *VerifierConfig) { c.TDXPolicy.Policy.TdQuoteBodyPolicy.TdAttributes = nil }}, + {name: "debug enabled", mutate: func(c *VerifierConfig) { c.TDXPolicy.Policy.TdQuoteBodyPolicy.TdAttributes[0] = 1 }}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + config := testVerifierConfig(PlatformTDX, policyPath, &key.PublicKey) + tc.mutate(&config) + if _, err := NewEvidenceVerifier(config); !errors.Is(err, ErrTDXPolicyRequired) { + t.Fatalf("NewEvidenceVerifier() error = %v, want ErrTDXPolicyRequired", err) + } + }) + } +} + +func TestVerifyEATProfile(t *testing.T) { + now := time.Unix(1_700_000_000, 0) + valid := eat.EATClaims{ + Issuer: testEATIssuer, + IssuedAt: now.Add(-time.Minute).Unix(), + ExpiresAt: now.Add(time.Minute).Unix(), + } + if err := verifyEATProfile(&valid, testEATIssuer, now); err != nil { + t.Fatalf("verifyEATProfile() error = %v", err) + } + + tests := []struct { + name string + mutate func(*eat.EATClaims) + }{ + {name: "wrong issuer", mutate: func(c *eat.EATClaims) { c.Issuer = "other-issuer" }}, + {name: "missing issued at", mutate: func(c *eat.EATClaims) { c.IssuedAt = 0 }}, + {name: "expires before issuance", mutate: func(c *eat.EATClaims) { c.ExpiresAt = c.IssuedAt }}, + {name: "lifetime too long", mutate: func(c *eat.EATClaims) { c.ExpiresAt = c.IssuedAt + 301 }}, + {name: "issued beyond skew", mutate: func(c *eat.EATClaims) { + c.IssuedAt = now.Add(31 * time.Second).Unix() + c.ExpiresAt = now.Add(time.Minute).Unix() + }}, + {name: "expired beyond skew", mutate: func(c *eat.EATClaims) { + c.IssuedAt = now.Add(-time.Minute).Unix() + c.ExpiresAt = now.Add(-31 * time.Second).Unix() + }}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + claims := valid + tc.mutate(&claims) + if err := verifyEATProfile(&claims, testEATIssuer, now); err == nil { + t.Fatal("verifyEATProfile() unexpectedly succeeded") + } + }) + } +} + +func TestVerifyEvidenceRejectsClaimForDifferentPlatform(t *testing.T) { + expected := testEvidenceBinding() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + + tests := []struct { + configured Platform + claim string + }{ + {configured: PlatformSNP, claim: "TDX"}, + {configured: PlatformTDX, claim: "SNP"}, + } + + for _, tc := range tests { + t.Run(string(tc.configured)+"-rejects-"+tc.claim, func(t *testing.T) { + token, err := eat.EncodeToCBOR(&eat.EATClaims{ + Nonce: append([]byte(nil), expected.Nonce[:]...), + PlatformType: tc.claim, + RawReport: []byte("peer-controlled-report"), + }, key, "test-issuer") + if err != nil { + t.Fatal(err) + } + verifier, err := NewEvidenceVerifier(testVerifierConfig(tc.configured, testPolicyPath(t), &key.PublicKey)) + if err != nil { + t.Fatal(err) + } + if err := verifier.VerifyEvidence(token, expected); !errors.Is(err, ErrPlatformMismatch) { + t.Fatalf("VerifyEvidence() error = %v, want ErrPlatformMismatch", err) + } + }) + } +} + +func TestVerifyEvidenceRejectsUnsupportedPlatformClaim(t *testing.T) { + expected := testEvidenceBinding() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + verifier, err := NewEvidenceVerifier(testVerifierConfig(PlatformSNP, testPolicyPath(t), &key.PublicKey)) + if err != nil { + t.Fatal(err) + } + for _, claim := range []string{"SNP-vTPM", "vTPM", "Azure", "Unknown"} { + t.Run(claim, func(t *testing.T) { + token, err := eat.EncodeToCBOR(&eat.EATClaims{ + Nonce: append([]byte(nil), expected.Nonce[:]...), + PlatformType: claim, + RawReport: []byte("peer-controlled-report"), + }, key, "test-issuer") + if err != nil { + t.Fatal(err) + } + if err := verifier.VerifyEvidence(token, expected); !errors.Is(err, ErrUnsupportedPlatform) { + t.Fatalf("VerifyEvidence() error = %v, want ErrUnsupportedPlatform", err) + } + }) + } +} + +func testPolicyPath(t *testing.T) string { + t.Helper() + path := filepath.Join(t.TempDir(), "policy.corim") + if err := os.WriteFile(path, []byte("test policy placeholder"), 0o600); err != nil { + t.Fatal(err) + } + return path +} + +func TestVerifyEvidenceTDXDelegatesToIndependentModule(t *testing.T) { + quoteAny, err := tdxabi.QuoteToProto(tdxtestdata.RawQuote) + if err != nil { + t.Fatal(err) + } + quote, ok := quoteAny.(*tdxpb.QuoteV4) + if !ok { + t.Fatalf("unexpected TDX quote type %T", quoteAny) + } + expected := testEvidenceBinding() + copy(expected.ReportData[:], quote.GetTdQuoteBody().GetReportData()) + key := testEATSigningKey(t) + claims, err := eat.NewEATClaims(tdxtestdata.RawQuote, expected.Nonce[:], asbattestation.TDX) + if err != nil { + t.Fatal(err) + } + token, err := eat.EncodeToCBOR(claims, key, testEATIssuer) + if err != nil { + t.Fatal(err) + } + config := testVerifierConfig(PlatformTDX, testPolicyPath(t), &key.PublicKey) + config.TDXRuntimeOptions = &tdxmodule.RuntimeOptions{Getter: rejectingTDXGetter{}} + verifier, err := NewEvidenceVerifier(config) + if err != nil { + t.Fatal(err) + } + if err := verifier.VerifyEvidence(token, expected); !errors.Is(err, ErrTDXVerification) { + t.Fatalf("VerifyEvidence() error = %v, want ErrTDXVerification", err) + } +} + +func TestVerifyEvidenceDirectSNPProviderEndToEnd(t *testing.T) { + expected := testEvidenceBinding() + fixture := testDirectSNPEvidence(t, expected) + policyPath := testSNPPolicyPath(t, fixture.measurement) + + key := testEATSigningKey(t) + config := testVerifierConfig(PlatformSNP, policyPath, &key.PublicKey) + config.SNPVerificationOptions = fixture.verificationOptions + config.SNPValidationOptions = fixture.validationOptions + verifier, err := NewEvidenceVerifier(config) + if err != nil { + t.Fatal(err) + } + + for _, tc := range []struct { + name string + evidence []byte + }{ + {name: "direct provider protobuf", evidence: fixture.providerEvidence}, + {name: "AMD ABI report and certificate table", evidence: fixture.rawEvidence}, + } { + t.Run(tc.name, func(t *testing.T) { + claims := testSignedEnvelopeClaims(tc.evidence, expected.Nonce[:], "SNP") + token, err := eat.EncodeToCBOR(claims, key, "test-issuer") + if err != nil { + t.Fatalf("EncodeToCBOR() error = %v", err) + } + if err := verifier.VerifyEvidence(token, expected); err != nil { + t.Fatalf("VerifyEvidence() error = %v", err) + } + }) + } +} + +func TestVerifyEvidenceDirectSNPRejectsBindingAndPolicyMismatch(t *testing.T) { + expected := testEvidenceBinding() + fixture := testDirectSNPEvidence(t, expected) + key := testEATSigningKey(t) + claims := testSignedEnvelopeClaims(fixture.providerEvidence, expected.Nonce[:], "SNP") + token, err := eat.EncodeToCBOR(claims, key, "test-issuer") + if err != nil { + t.Fatal(err) + } + + config := testVerifierConfig(PlatformSNP, testSNPPolicyPath(t, fixture.measurement), &key.PublicKey) + config.SNPVerificationOptions = fixture.verificationOptions + config.SNPValidationOptions = fixture.validationOptions + verifier, err := NewEvidenceVerifier(config) + if err != nil { + t.Fatal(err) + } + wrong := expected + wrong.ReportData[0] ^= 0xff + if err := verifier.VerifyEvidence(token, wrong); err == nil { + t.Fatal("expected mismatched SNP report data to fail") + } + + wrongMeasurement := append([]byte(nil), fixture.measurement...) + wrongMeasurement[0] ^= 0xff + config.PolicyPath = testSNPPolicyPath(t, wrongMeasurement) + verifier, err = NewEvidenceVerifier(config) + if err != nil { + t.Fatal(err) + } + if err := verifier.VerifyEvidence(token, expected); err == nil { + t.Fatal("expected mismatched SNP measurement policy to fail") + } +} + +func TestVerifyEvidenceDirectSNPRejectsTamperedSignatureAndDebugPolicy(t *testing.T) { + expected := testEvidenceBinding() + fixture := testDirectSNPEvidence(t, expected) + key := testEATSigningKey(t) + config := testVerifierConfig(PlatformSNP, testSNPPolicyPath(t, fixture.measurement), &key.PublicKey) + config.SNPVerificationOptions = fixture.verificationOptions + config.SNPValidationOptions = fixture.validationOptions + + var tampered sevsnp.Attestation + if err := proto.Unmarshal(fixture.providerEvidence, &tampered); err != nil { + t.Fatal(err) + } + tampered.Report.Signature[0] ^= 0xff + tamperedEvidence, err := proto.Marshal(&tampered) + if err != nil { + t.Fatal(err) + } + claims := testSignedEnvelopeClaims(tamperedEvidence, expected.Nonce[:], "SNP") + tamperedToken, err := eat.EncodeToCBOR(claims, key, testEATIssuer) + if err != nil { + t.Fatal(err) + } + verifier, err := NewEvidenceVerifier(config) + if err != nil { + t.Fatal(err) + } + if err := verifier.VerifyEvidence(tamperedToken, expected); !errors.Is(err, ErrSNPVerification) { + t.Fatalf("VerifyEvidence() error = %v, want ErrSNPVerification", err) + } + + config.SNPValidationOptions.GuestPolicy.Debug = true + if _, err := NewEvidenceVerifier(config); !errors.Is(err, ErrSNPPolicyRequired) { + t.Fatalf("NewEvidenceVerifier() error = %v, want ErrSNPPolicyRequired", err) + } +} + +// testSignedEnvelopeClaims constructs only the fields consumed by the Cocos +// compatibility verifier. This keeps verifier tests independent of the legacy +// Cocos evidence producer's platform-specific claim extraction. +func testSignedEnvelopeClaims(report, nonce []byte, platform string) *eat.EATClaims { + return &eat.EATClaims{ + Nonce: append([]byte(nil), nonce...), + PlatformType: platform, + RawReport: append([]byte(nil), report...), + } +} + +func TestVerifyEvidenceDirectSNPRejectsSNPvTPMWrapper(t *testing.T) { + expected := testEvidenceBinding() + fixture := testDirectSNPEvidence(t, expected) + var snpAttestation sevsnp.Attestation + if err := proto.Unmarshal(fixture.providerEvidence, &snpAttestation); err != nil { + t.Fatal(err) + } + wrapper, err := proto.Marshal(&attest.Attestation{ + TeeAttestation: &attest.Attestation_SevSnpAttestation{ + SevSnpAttestation: &snpAttestation, + }, + }) + if err != nil { + t.Fatal(err) + } + key := testEATSigningKey(t) + token, err := eat.EncodeToCBOR(&eat.EATClaims{ + Nonce: append([]byte(nil), expected.Nonce[:]...), + PlatformType: "SNP", + RawReport: wrapper, + }, key, "test-issuer") + if err != nil { + t.Fatal(err) + } + config := testVerifierConfig(PlatformSNP, testSNPPolicyPath(t, fixture.measurement), &key.PublicKey) + config.SNPVerificationOptions = fixture.verificationOptions + config.SNPValidationOptions = fixture.validationOptions + verifier, err := NewEvidenceVerifier(config) + if err != nil { + t.Fatal(err) + } + if err := verifier.VerifyEvidence(token, expected); !errors.Is(err, ErrInvalidSNPEvidence) { + t.Fatalf("VerifyEvidence() error = %v, want ErrInvalidSNPEvidence", err) + } +} + +type directSNPTestEvidence struct { + rawEvidence []byte + providerEvidence []byte + measurement []byte + verificationOptions *sevverify.Options + validationOptions *sevvalidate.Options +} + +func testDirectSNPEvidence(t *testing.T, expected eaattestation.EvidenceBinding) directSNPTestEvidence { + t.Helper() + now := time.Now() + signer, err := sevtesting.DefaultTestOnlyCertChain(sevtesting.GetProductName(), now) + if err != nil { + t.Fatal(err) + } + rawBuffer := sevtesting.TestRawReport(expected.ReportData) + rawReport := append([]byte(nil), rawBuffer[:sevabi.ReportSize]...) + binary.LittleEndian.PutUint64(rawReport[0x08:0x10], sevabi.SnpPolicyToBytes(sevabi.SnpPolicy{})) + measurement := make([]byte, sevabi.MeasurementSize) + for i := range measurement { + measurement[i] = byte(0x40 + i) + } + copy(rawReport[0x90:0xc0], measurement) + r, s, err := signer.Sign(sevabi.SignedComponent(rawReport)) + if err != nil { + t.Fatal(err) + } + if err := sevabi.SetSignature(r, s, rawReport); err != nil { + t.Fatal(err) + } + certificateTable, err := signer.CertTableBytes() + if err != nil { + t.Fatal(err) + } + rawEvidence := append(append([]byte(nil), rawReport...), certificateTable...) + + snpAttestation, err := sevabi.ReportCertsToProto(rawEvidence) + if err != nil { + t.Fatal(err) + } + product := sevabi.DefaultSevProduct() + snpAttestation.Product = proto.Clone(product).(*sevsnp.SevProduct) + providerEvidence, err := proto.Marshal(snpAttestation) + if err != nil { + t.Fatal(err) + } + + root := sevtrust.AMDRootCertsProduct(sevtesting.GetProductLine()) + root.ProductCerts = &sevtrust.ProductCerts{Ark: signer.Ark, Ask: signer.Ask} + crlDER, err := x509.CreateRevocationList(rand.Reader, &x509.RevocationList{ + Number: big.NewInt(1), + ThisUpdate: now.Add(-time.Minute), + NextUpdate: now.Add(time.Hour), + }, signer.Ark, signer.Keys.Ark) + if err != nil { + t.Fatal(err) + } + root.CRL, err = x509.ParseRevocationList(crlDER) + if err != nil { + t.Fatal(err) + } + vmpl := 0 + return directSNPTestEvidence{ + rawEvidence: rawEvidence, + providerEvidence: providerEvidence, + measurement: measurement, + verificationOptions: &sevverify.Options{ + CheckRevocations: true, + DisableCertFetching: true, + Getter: rejectingHTTPSGetter{}, + TrustedRoots: map[string][]*sevtrust.AMDRootCerts{ + sevtesting.GetProductLine(): {root}, + }, + Product: proto.Clone(product).(*sevsnp.SevProduct), + }, + validationOptions: &sevvalidate.Options{ + GuestPolicy: sevabi.SnpPolicy{}, + VMPL: &vmpl, + }, + } +} + +func testSNPPolicyPath(t *testing.T, measurement []byte) string { + t.Helper() + policy, err := corimgen.GenerateCoRIM(corimgen.Options{ + Platform: "snp", + Measurement: hex.EncodeToString(measurement), + }) + if err != nil { + t.Fatal(err) + } + path := filepath.Join(t.TempDir(), "snp-policy.corim") + if err := os.WriteFile(path, policy, 0o600); err != nil { + t.Fatal(err) + } + return path +} + +func TestLoadCoRIMVerifiesConfiguredSignature(t *testing.T) { + key := testEATSigningKey(t) + payload, err := corimgen.GenerateCoRIM(corimgen.Options{ + Platform: "snp", + SigningKey: key, + }) + if err != nil { + t.Fatal(err) + } + path := filepath.Join(t.TempDir(), "signed-policy.corim") + if err := os.WriteFile(path, payload, 0o600); err != nil { + t.Fatal(err) + } + + if _, err := loadCoRIM(path, &key.PublicKey, time.Now()); err != nil { + t.Fatalf("loadCoRIM() error = %v", err) + } + if _, err := loadCoRIM(path, nil, time.Now()); !errors.Is(err, ErrCoRIMTrustRequired) { + t.Fatalf("loadCoRIM() error = %v, want ErrCoRIMTrustRequired", err) + } + wrongKey := testEATSigningKey(t) + if _, err := loadCoRIM(path, &wrongKey.PublicKey, time.Now()); !errors.Is(err, ErrCoRIMVerification) { + t.Fatalf("loadCoRIM() error = %v, want ErrCoRIMVerification", err) + } +} + +func TestLoadCoRIMRejectsUnsignedPolicyWhenKeyConfigured(t *testing.T) { + path := testSNPPolicyPath(t, make([]byte, sevabi.MeasurementSize)) + key := testEATSigningKey(t) + if _, err := loadCoRIM(path, &key.PublicKey, time.Now()); !errors.Is(err, ErrCoRIMVerification) { + t.Fatalf("loadCoRIM() error = %v, want ErrCoRIMVerification", err) + } +} + +func TestValidateCoRIMValidity(t *testing.T) { + now := time.Unix(1_700_000_000, 0) + notBefore := now.Add(-time.Minute) + if err := validateCoRIMValidity(&corim.Validity{NotBefore: ¬Before, NotAfter: now.Add(time.Minute)}, now); err != nil { + t.Fatal(err) + } + future := now.Add(time.Second) + if err := validateCoRIMValidity(&corim.Validity{NotBefore: &future, NotAfter: now.Add(time.Minute)}, now); !errors.Is(err, ErrCoRIMVerification) { + t.Fatalf("future validity error = %v", err) + } + if err := validateCoRIMValidity(&corim.Validity{NotAfter: now.Add(-time.Second)}, now); !errors.Is(err, ErrCoRIMVerification) { + t.Fatalf("expired validity error = %v", err) + } +} From bd9537d3e780d06e251fbabbb0240a7a82ef86d2 Mon Sep 17 00:00:00 2001 From: thinksyncs Date: Sun, 30 Aug 2026 18:02:43 +0900 Subject: [PATCH 6/8] ci(attestation): add module release gates --- .github/workflows/asb-core.yaml | 53 ++++ .github/workflows/attestation-modules.yaml | 122 +++++++++ .../workflows/attestation-release-gate.yaml | 240 ++++++++++++++++++ .../hardware-attestation-red-team.yaml | 32 ++- .github/workflows/main.yaml | 16 +- .github/workflows/security-red-team.yaml | 2 +- PUBLICATION_TODO.md | 23 ++ README.md | 9 +- docs/attestation-module-boundary.md | 80 ++++++ docs/attestation-module-migration-v2.md | 84 ++++++ docs/security-red-team-tests.md | 30 ++- modules/attestation/snp/README.md | 3 +- scripts/check-asb-core-boundary.sh | 40 +++ scripts/check-attestation-release.sh | 11 + scripts/check-attestation-v2-boundary.sh | 17 ++ scripts/check-attestation-v2-release.sh | 34 +++ scripts/check-cocos-release.sh | 39 +++ 17 files changed, 813 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/asb-core.yaml create mode 100644 .github/workflows/attestation-modules.yaml create mode 100644 .github/workflows/attestation-release-gate.yaml create mode 100644 docs/attestation-module-boundary.md create mode 100644 docs/attestation-module-migration-v2.md create mode 100755 scripts/check-asb-core-boundary.sh create mode 100755 scripts/check-attestation-release.sh create mode 100755 scripts/check-attestation-v2-boundary.sh create mode 100755 scripts/check-attestation-v2-release.sh create mode 100755 scripts/check-cocos-release.sh diff --git a/.github/workflows/asb-core.yaml b/.github/workflows/asb-core.yaml new file mode 100644 index 00000000..90e28d5f --- /dev/null +++ b/.github/workflows/asb-core.yaml @@ -0,0 +1,53 @@ +name: ASB Core Boundary + +on: + workflow_dispatch: + push: + branches: [main] + paths: + - ".github/workflows/asb-core.yaml" + - "go.mod" + - "go.sum" + - "Makefile" + - "scripts/check-asb-core-boundary.sh" + - "scripts/check-attestation-v2-boundary.sh" + - "pkg/atls/**" + - "pkg/agtp/**" + - "pkg/clients/**" + - "pkg/tls/**" + pull_request: + branches: [main] + paths: + - ".github/workflows/asb-core.yaml" + - "go.mod" + - "go.sum" + - "Makefile" + - "scripts/check-asb-core-boundary.sh" + - "scripts/check-attestation-v2-boundary.sh" + - "pkg/atls/**" + - "pkg/agtp/**" + - "pkg/clients/**" + - "pkg/tls/**" + +permissions: + contents: read + +jobs: + boundary: + runs-on: ${{ matrix.os }} + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + env: + GOWORK: "off" + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.26.6 + cache-dependency-path: go.sum + - run: go mod verify + - run: make check-attestation-v2-boundary + - run: make test-asb-core diff --git a/.github/workflows/attestation-modules.yaml b/.github/workflows/attestation-modules.yaml new file mode 100644 index 00000000..2307d280 --- /dev/null +++ b/.github/workflows/attestation-modules.yaml @@ -0,0 +1,122 @@ +name: Attestation Modules + +on: + workflow_dispatch: + push: + branches: [main] + paths: + - ".github/workflows/attestation-modules.yaml" + - ".github/workflows/attestation-release-gate.yaml" + - "Makefile" + - "go.mod" + - "go.sum" + - "go.work" + - "go.work.sum" + - "integrations/cocos/**" + - "modules/attestation/**" + - "pkg/atls/**" + - "pkg/attestation/**" + - "scripts/check-attestation-*.sh" + - "scripts/check-cocos-release.sh" + pull_request: + branches: [main] + paths: + - ".github/workflows/attestation-modules.yaml" + - ".github/workflows/attestation-release-gate.yaml" + - "Makefile" + - "go.mod" + - "go.sum" + - "go.work" + - "go.work.sum" + - "integrations/cocos/**" + - "modules/attestation/**" + - "pkg/atls/**" + - "pkg/attestation/**" + - "scripts/check-attestation-*.sh" + - "scripts/check-cocos-release.sh" + +permissions: + contents: read + +jobs: + platform-module: + runs-on: ${{ matrix.os }} + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + module: [snp, tdx] + env: + GOWORK: "off" + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.26.6 + cache-dependency-path: modules/attestation/${{ matrix.module }}/go.sum + - working-directory: modules/attestation/${{ matrix.module }} + run: | + if go mod edit -json | grep -Eq '"Replace"[[:space:]]*:[[:space:]]*\['; then + echo "release blocked: nested module go.mod contains a replacement" >&2 + exit 1 + fi + go mod tidy -diff + go mod verify + - working-directory: modules/attestation/${{ matrix.module }} + run: go test -race -count=1 ./... + - working-directory: modules/attestation/${{ matrix.module }} + run: go vet ./... + + cocos-integration: + runs-on: ${{ matrix.os }} + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + env: + GOWORK: "off" + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.26.6 + cache-dependency-path: integrations/cocos/go.sum + - working-directory: integrations/cocos + run: | + go mod tidy -diff + go mod verify + - working-directory: integrations/cocos + run: go test -race -count=1 ./... + - working-directory: integrations/cocos + run: go vet ./... + + workspace: + runs-on: ${{ matrix.os }} + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.26.6 + cache-dependency-path: | + go.sum + go.work.sum + integrations/cocos/go.sum + modules/attestation/snp/go.sum + modules/attestation/tdx/go.sum + - name: Parse and test the development workspace + run: | + test "$(go env GOWORK)" = "$GITHUB_WORKSPACE/go.work" + go work edit -json >/dev/null + go list -m all >/dev/null + go test \ + ./pkg/attestation/... \ + ./modules/attestation/snp/... \ + ./modules/attestation/tdx/... \ + ./integrations/cocos/... diff --git a/.github/workflows/attestation-release-gate.yaml b/.github/workflows/attestation-release-gate.yaml new file mode 100644 index 00000000..ff6e0245 --- /dev/null +++ b/.github/workflows/attestation-release-gate.yaml @@ -0,0 +1,240 @@ +name: Attestation Release Gate + +on: + workflow_dispatch: + inputs: + target: + description: Pre-tag gate to run + required: true + default: all + type: choice + options: + - all + - root + - snp + - tdx + - cocos + pull_request: + branches: [main] + paths: + - ".github/workflows/attestation-release-gate.yaml" + - ".github/workflows/attestation-modules.yaml" + - "Makefile" + - "go.mod" + - "go.sum" + - "go.work" + - "go.work.sum" + - "integrations/cocos/**" + - "modules/attestation/**" + - "pkg/atls/**" + - "pkg/agtp/**" + - "pkg/attestation/**" + - "pkg/clients/**" + - "pkg/tls/**" + - "scripts/check-asb-core-boundary.sh" + - "scripts/check-attestation-*.sh" + - "scripts/check-cocos-release.sh" + push: + tags: + - "v*" + - "modules/attestation/snp/v*" + - "modules/attestation/tdx/v*" + - "integrations/cocos/v*" + +permissions: + contents: read + +jobs: + verify-release-state: + runs-on: ubuntu-latest + timeout-minutes: 35 + env: + GOWORK: "off" + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v5 + with: + go-version: 1.26.6 + cache-dependency-path: | + go.sum + integrations/cocos/go.sum + modules/attestation/snp/go.sum + modules/attestation/tdx/go.sum + - name: Run pre-tag gates or verify tagged module + shell: bash + env: + GH_TOKEN: ${{ github.token }} + PREFLIGHT_TARGET: ${{ inputs.target || 'all' }} + run: | + set -euo pipefail + + numeric_identifier='(0|[1-9][0-9]*)' + prerelease_identifier='(0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)' + root_version_re="^v2\\.${numeric_identifier}\\.${numeric_identifier}(-${prerelease_identifier}(\\.${prerelease_identifier})*)?$" + nested_version_re="^v0\\.${numeric_identifier}\\.${numeric_identifier}(-${prerelease_identifier}(\\.${prerelease_identifier})*)?$" + + reject_replacements() { + local directory=$1 + if (cd "$directory" && GOWORK=off go mod edit -json) | + grep -Eq '"Replace"[[:space:]]*:[[:space:]]*\['; then + echo "release blocked: $directory/go.mod contains a replacement" >&2 + return 1 + fi + } + + run_root_gate() { + make check-attestation-release + } + + run_nested_gate() { + local module=$1 + local directory="modules/attestation/$module" + local expected="github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/$module" + local actual + + reject_replacements "$directory" + actual=$(cd "$directory" && go list -m -f '{{.Path}}') + test "$actual" = "$expected" + ( + cd "$directory" + go mod tidy -diff + go mod verify + go test -race -count=1 ./... + go vet ./... + ) + } + + run_cocos_development_gate() { + ( + cd integrations/cocos + go mod tidy -diff + go mod verify + go test -race -count=1 ./... + go vet ./... + ) + } + + run_cocos_release_gate() { + make check-cocos-release + ( + cd integrations/cocos + go test -race -count=1 ./... + go vet ./... + ) + } + + run_preflight() { + case "$1" in + all) + run_root_gate + run_nested_gate snp + run_nested_gate tdx + run_cocos_development_gate + ;; + root) run_root_gate ;; + snp) run_nested_gate snp ;; + tdx) run_nested_gate tdx ;; + cocos) run_cocos_development_gate ;; + *) + echo "unsupported pre-tag target: $1" >&2 + return 1 + ;; + esac + } + + if [[ "$GITHUB_EVENT_NAME" != "push" ]]; then + unset GH_TOKEN + run_preflight "$PREFLIGHT_TARGET" + exit 0 + fi + + tag=$GITHUB_REF_NAME + case "$tag" in + modules/attestation/snp/v*) + module=snp + kind=nested + version=${tag##*/} + [[ "$version" =~ $nested_version_re ]] || { + echo "SNP module tag must contain an exact v0 semantic version" >&2 + exit 1 + } + ;; + modules/attestation/tdx/v*) + module=tdx + kind=nested + version=${tag##*/} + [[ "$version" =~ $nested_version_re ]] || { + echo "TDX module tag must contain an exact v0 semantic version" >&2 + exit 1 + } + ;; + integrations/cocos/v*) + kind=cocos + version=${tag##*/} + [[ "$version" =~ $nested_version_re ]] || { + echo "Cocos integration tag must contain an exact v0 semantic version" >&2 + exit 1 + } + ;; + v*) + kind=root + version=$tag + [[ "$version" =~ $root_version_re ]] || { + echo "root tag must contain an exact v2 semantic version" >&2 + exit 1 + } + ;; + *) + echo "unsupported release tag: $tag" >&2 + exit 1 + ;; + esac + + ref_json=$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${tag}") + if [[ $(jq -r '.object.type' <<<"$ref_json") != "tag" ]]; then + echo "release blocked: $tag is not an annotated tag" >&2 + exit 1 + fi + + tag_object_sha=$(jq -r '.object.sha' <<<"$ref_json") + tag_json=$(gh api "repos/${GITHUB_REPOSITORY}/git/tags/${tag_object_sha}") + if [[ $(jq -r '.tag' <<<"$tag_json") != "$tag" ]]; then + echo "release blocked: signed tag name does not match pushed ref $tag" >&2 + exit 1 + fi + if [[ $(jq -r '.verification.verified' <<<"$tag_json") != "true" ]]; then + reason=$(jq -r '.verification.reason // "unknown"' <<<"$tag_json") + echo "release blocked: GitHub did not verify the tag signature ($reason)" >&2 + exit 1 + fi + if [[ $(jq -r '.object.type' <<<"$tag_json") != "commit" ]]; then + echo "release blocked: annotated tag does not point directly to a commit" >&2 + exit 1 + fi + + target_sha=$(jq -r '.object.sha' <<<"$tag_json") + commit_json=$(gh api "repos/${GITHUB_REPOSITORY}/commits/${target_sha}") + if [[ $(jq -r '.commit.verification.verified' <<<"$commit_json") != "true" ]]; then + reason=$(jq -r '.commit.verification.reason // "unknown"' <<<"$commit_json") + echo "release blocked: GitHub did not verify the target commit signature ($reason)" >&2 + exit 1 + fi + + unset GH_TOKEN + git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main + if ! git merge-base --is-ancestor "$target_sha" refs/remotes/origin/main; then + echo "release blocked: tag target is not reachable from origin/main" >&2 + exit 1 + fi + if [[ $(git rev-parse HEAD) != "$target_sha" ]]; then + echo "release blocked: checkout does not match the verified tag target" >&2 + exit 1 + fi + + case "$kind" in + root) run_root_gate ;; + nested) run_nested_gate "$module" ;; + cocos) run_cocos_release_gate ;; + esac diff --git a/.github/workflows/hardware-attestation-red-team.yaml b/.github/workflows/hardware-attestation-red-team.yaml index 91e329e9..50533d7e 100644 --- a/.github/workflows/hardware-attestation-red-team.yaml +++ b/.github/workflows/hardware-attestation-red-team.yaml @@ -11,12 +11,20 @@ on: description: "Hardware attestation platform" required: true type: choice - default: auto + default: snp options: - - auto - snp - - snp-vtpm - tdx + corim_policy_path: + description: "Absolute path to the runner's CoRIM policy" + required: true + platform_policy_path: + description: "Absolute path to the runner's go-sev/go-tdx policy JSON" + required: true + corim_public_key_path: + description: "Optional absolute path to the signed-CoRIM public key" + required: false + default: "" expected_host_data_hex: description: "Optional SEV-SNP HostData value, hex encoded" required: false @@ -47,15 +55,20 @@ jobs: uses: actions/setup-go@v5 with: go-version: 1.26.6 - cache-dependency-path: go.sum + cache-dependency-path: integrations/cocos/go.sum - - name: Run hardware attestation replay gate + - name: Run full hardware attestation module gate + working-directory: integrations/cocos env: ASB_ATTESTATION_PLATFORM: ${{ inputs.platform }} ASB_EXPECTED_HOST_DATA_HEX: ${{ inputs.expected_host_data_hex }} ASB_REQUIRE_KERNEL_HASHES: ${{ inputs.require_kernel_hashes }} ASB_KERNEL_HASHES_EVIDENCE: ${{ inputs.kernel_hashes_evidence }} - GOTOOLCHAIN: go1.26.0+auto + ASB_CORIM_POLICY_PATH: ${{ inputs.corim_policy_path }} + ASB_PLATFORM_POLICY_PATH: ${{ inputs.platform_policy_path }} + ASB_CORIM_PUBLIC_KEY_PATH: ${{ inputs.corim_public_key_path }} + GOWORK: "off" + GOTOOLCHAIN: go1.26.6+auto run: | set -euo pipefail @@ -65,8 +78,15 @@ jobs: args=( "--platform" "${ASB_ATTESTATION_PLATFORM}" "--evidence-dir" "${evidence_dir}" + "--corim-policy" "${ASB_CORIM_POLICY_PATH}" + "--platform-policy" "${ASB_PLATFORM_POLICY_PATH}" + "--require-full-verification" ) + if [[ -n "${ASB_CORIM_PUBLIC_KEY_PATH}" ]]; then + args+=("--corim-public-key" "${ASB_CORIM_PUBLIC_KEY_PATH}") + fi + if [[ -n "${ASB_EXPECTED_HOST_DATA_HEX}" ]]; then args+=("--expected-host-data-hex" "${ASB_EXPECTED_HOST_DATA_HEX}") fi diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index fc981341..3ed0ea65 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -59,9 +59,9 @@ jobs: - name: Run tests for ${{ matrix.module }} run: | if [[ "${{ matrix.module }}" == "manager" ]]; then - sudo GOTOOLCHAIN=go1.26.0+auto go test -v --race -covermode=atomic -coverprofile coverage/${{ matrix.module }}.out ./${{ matrix.module }}/... + sudo GOTOOLCHAIN=go1.26.6+auto go test -v --race -covermode=atomic -coverprofile coverage/${{ matrix.module }}.out ./${{ matrix.module }}/... else - GOTOOLCHAIN=go1.26.0+auto go test -v --race -covermode=atomic -coverprofile coverage/${{ matrix.module }}.out ./${{ matrix.module }}/... + GOTOOLCHAIN=go1.26.6+auto go test -v --race -covermode=atomic -coverprofile coverage/${{ matrix.module }}.out ./${{ matrix.module }}/... fi - name: Upload coverage artifact @@ -111,7 +111,7 @@ jobs: - name: Run Direct-Agent core tests run: | - GOTOOLCHAIN=go1.26.0+auto go test \ + GOTOOLCHAIN=go1.26.6+auto go test \ ./pkg/atls/... \ ./pkg/clients/... \ ./pkg/agtp/... \ @@ -120,7 +120,7 @@ jobs: - name: Run Direct-Agent security red-team tests run: | - GOTOOLCHAIN=go1.26.0+auto go test -v -race -count=1 \ + GOTOOLCHAIN=go1.26.6+auto go test -v -race -count=1 \ ./pkg/atls/identitypolicy \ ./pkg/clients \ ./pkg/production \ @@ -131,22 +131,22 @@ jobs: ASB_DISCOVERY_SOAK: "1" ASB_DISCOVERY_SOAK_DURATION: "30s" run: | - GOTOOLCHAIN=go1.26.0+auto go test -v -race -count=1 \ + GOTOOLCHAIN=go1.26.6+auto go test -v -race -count=1 \ ./pkg/agtp/discovery/peer -run '^TestPeerServiceSoak$' - name: Run protected-change consumer integration run: | - GOTOOLCHAIN=go1.26.0+auto go test -v -race -count=1 \ + GOTOOLCHAIN=go1.26.6+auto go test -v -race -count=1 \ ./examples/protected-change-consumer - name: Run v1 and draft-06 v2 A2A negative integration run: | - GOTOOLCHAIN=go1.26.0+auto go test -v -race -count=1 \ + GOTOOLCHAIN=go1.26.6+auto go test -v -race -count=1 \ ./examples/a2a-multiprocess - name: Run bounded fuzz smoke run: | - GOTOOLCHAIN=go1.26.0+auto go test -run '^$' \ + GOTOOLCHAIN=go1.26.6+auto go test -run '^$' \ -fuzz=FuzzVerifySessionIdentityJWTRejectsMalformedCompactTokens \ -fuzztime=10s ./pkg/agtp diff --git a/.github/workflows/security-red-team.yaml b/.github/workflows/security-red-team.yaml index 0f3790b5..f85f87a7 100644 --- a/.github/workflows/security-red-team.yaml +++ b/.github/workflows/security-red-team.yaml @@ -68,7 +68,7 @@ jobs: - name: Run Direct-Agent security red-team tests run: | - GOTOOLCHAIN=go1.26.0+auto go test -v -race -count=1 \ + GOTOOLCHAIN=go1.26.6+auto go test -v -race -count=1 \ ./pkg/agtp \ ./pkg/atls/identitypolicy \ ./pkg/atls/sbaipv2 \ diff --git a/PUBLICATION_TODO.md b/PUBLICATION_TODO.md index 5a975be4..3035eb7b 100644 --- a/PUBLICATION_TODO.md +++ b/PUBLICATION_TODO.md @@ -16,6 +16,29 @@ examples, and local documentation must use the v2 module path before that release. Repository links, schema identifiers, and Git remotes use the canonical repository URL without `/v2`. +## Attestation module release gates + +The ASB core package boundary is platform-neutral and must not depend on the +Cocos, SNP, or TDX nested modules. The repository root still contains legacy +platform and runtime packages. The Cocos integration retains replacements only +for prepublication testing. + +Before the v2 release candidate: + +- merge the isolated v2/module boundary branch and pass pull-request CI; +- run the manual `Attestation Release Gate` with target `all` on the merged + commit; +- sign and push `modules/attestation/snp/v0.1.0` and + `modules/attestation/tdx/v0.1.0` on the merged commit; +- wait for both tag-triggered nested-module gates to succeed; +- pass the root release gate with `GOWORK=off`; +- sign and push `v2.0.0-rc.1`, wait for its tag-triggered gate to succeed, and + only then run `gh release create v2.0.0-rc.1 --verify-tag --prerelease` + against the existing remote tag. + +No live SNP or TDX qualification has been completed. The prerelease must not +claim that either platform module or the Cocos integration is production-ready. + ## Recorded CI and Red-Team Status Latest recorded signed implementation checkpoint (GitHub signature status: diff --git a/README.md b/README.md index 358ecf33..34e572a3 100644 --- a/README.md +++ b/README.md @@ -102,12 +102,19 @@ slice. revocation, attestation, distributed replay, and exact action binding. - `docs/azure-sev-snp-attestation-bridge.md`: experimental Azure Attestation token-to-ASB bridge boundary and live confidential-VM qualification. +- `docs/attestation-module-boundary.md`: platform-neutral ASB boundary, + separate SNP/TDX modules, Cocos integration, and test lanes. +- `docs/attestation-module-migration-v2.md`: v2 API and release-order migration. - `docs/redis-failover-runbook.md`: private multi-node replay topology, replication acknowledgement, and real failover gate. - `formal/`: ProVerif and TLA+ models, recorded results, and model-to-implementation traceability. - `pkg/clients`, `pkg/atls`, and `pkg/atls/identitypolicy`: Direct-Agent acceptance implementation. +- `modules/attestation/snp` and `modules/attestation/tdx`: independent, + experimental `v0.x` hardware appraisers. They are not production-qualified. +- `integrations/cocos`: experimental Cocos evidence and compatibility + composition outside the ASB root module. - `pkg/production`: supported attested and software-only fail-closed compositions, plus TLS Redis/Valkey replay and shared operation/result adapters. @@ -338,7 +345,7 @@ go test ./pkg/agtp ./pkg/agtp/gatewayroute Focused Direct-Agent red-team check: ```sh -GOTOOLCHAIN=go1.26.0+auto go test -v -race -count=1 \ +GOTOOLCHAIN=go1.26.6+auto go test -v -race -count=1 \ ./pkg/atls/identitypolicy \ ./pkg/clients ``` diff --git a/docs/attestation-module-boundary.md b/docs/attestation-module-boundary.md new file mode 100644 index 00000000..959e174b --- /dev/null +++ b/docs/attestation-module-boundary.md @@ -0,0 +1,80 @@ +# ASB attestation module boundary + +Status: implemented for the v2 release candidate. The boundary and deterministic +fixtures can be tested without confidential-computing hardware. The SNP and TDX +modules remain experimental `v0.x`; neither has completed live qualification. + +## Ownership + +ASB owns session and identity binding. It derives `REPORT_DATA` and the nonce +from the accepted TLS 1.3 session, verifies the exported-authenticator binder, +and fails closed when evidence is present without an injected verifier. + +The platform modules own quote parsing, endorsement and collateral checks, TCB +and debug policy, measurements, and comparison with the `REPORT_DATA` supplied +by ASB. A deployment selects one platform locally. Peer evidence cannot select +an appraiser. + +The Cocos-specific composition is a separate nested module: + +```text +ASB v2 core + -> eaattestation.EvidenceSource and EvidenceVerifier only + +integrations/cocos + -> ASB v2 interfaces + -> modules/attestation/snp or modules/attestation/tdx + -> legacy EAT and CoRIM compatibility appraisers +``` + +The platform-neutral ASB core package set must not import `integrations/cocos` +or either nested platform module. This keeps the IETF-facing protocol path +testable on a MacBook and ordinary CI without Cocos or vendor hardware +dependencies. The repository root still contains legacy platform and runtime +packages; the core boundary check is not a claim that every root package is +platform-neutral. + +`integrations/cocos` verifies the signed EAT envelope and then verifies the raw +hardware evidence. An EAT signature does not replace quote authentication, +endorsement validation, collateral and revocation checks, or local platform +policy. + +## Current evidence + +The SNP module accepts the provider's `sevsnp.Attestation` protobuf and the AMD +ABI report with an optional certificate table. Its deterministic fixture covers +successful signature and policy verification plus negative binding, signature, +measurement, and debug-policy cases. This fixes the former direct-SNP format +mismatch, but it is not a live AMD result. + +The TDX module has a deterministic QuoteV4 authentication and local-policy +success fixture. A strict collateral fixture reaches TCB appraisal and is +rejected for a TCB mismatch. There is no strict collateral success fixture and +no live TDX result. + +The compatibility appraisers use repository-local CoMID measurement keys. +They are not IETF-assigned code points and do not define a portable SNP or TDX +CoMID profile. + +## Test lanes + +MacBook and ordinary CI: + +```sh +make test-asb-core +(cd modules/attestation/snp && GOWORK=off go test -race ./... && GOWORK=off go vet ./...) +(cd modules/attestation/tdx && GOWORK=off go test -race ./... && GOWORK=off go vet ./...) +(cd integrations/cocos && GOWORK=off go test -race ./... && GOWORK=off go vet ./...) +``` + +These tests use fixtures and injected collateral. They do not collect a quote +from `/dev/sev-guest` or a TDX guest device. + +Live qualification belongs to a separate self-hosted CI or RunPod-style lane. +The runner is usable only if the guest exposes the required device and the +verifier can retrieve matching vendor collateral. A GPU instance name alone is +not evidence of SNP or TDX availability. Qualification must retain the runner +image, platform policy, verifier version, collateral result, and test hashes. + +Passing offline tests supports the module boundary and deterministic appraisal +claims only. It does not make the platform modules production-ready. diff --git a/docs/attestation-module-migration-v2.md b/docs/attestation-module-migration-v2.md new file mode 100644 index 00000000..5ffdfb3d --- /dev/null +++ b/docs/attestation-module-migration-v2.md @@ -0,0 +1,84 @@ +# Attestation module migration for ASB v2 + +## Decision + +ASB v2 accepts injected platform-neutral evidence sources and verifiers. It no +longer constructs a hardware verifier from a policy path or selects a platform +from peer evidence. + +The public dependency graph is intentionally one-way: + +```text +modules/attestation/snp v0.x ----\ + -> integrations/cocos v0.x +modules/attestation/tdx v0.x ----/ ^ + | +ASB root /v2 --------------------------------------+ +``` + +ASB root has no dependency on the Cocos integration or the two platform +modules. During development, `integrations/cocos/go.mod` uses local replacements +because the ASB v2 release candidate does not yet exist. Those replacements are +not permitted in a Cocos module release. + +## API migration + +The v1 evidence provider accepted a concrete attestation-service client and a +platform. In v2, adapt the deployment client to `EvidenceSource` and inject it: + +```go +source, err := evidencesource.NewEvidenceSource(attestationClient, localPlatform) +if err != nil { + return err +} + +provider, err := atls.NewProvider(source) +``` + +The v1 `atls.NewEvidenceVerifier(policyPath)` constructor is removed. A path +does not specify an EAT trust key, issuer, platform, collateral source, +revocation behavior, or measurement policy. Cocos deployments construct +`platformmodule.NewEvidenceVerifier` in the external integration module and +inject it through `eaattestation.VerificationPolicy`. + +There is no v1-shaped v2 overload. Applications that need the old constructor +can remain on the v1.1.x line while updating their composition root. + +The root `make` target no longer builds the Cocos agent. Build the moved +command with `make -C integrations/cocos agent`; it preserves the +`agents-secure-binding-agent` binary name used by the inherited systemd +packaging files. The standalone ingress command was removed because the Cocos +agent owns and starts the per-computation ingress proxy. + +## Release order + +1. Merge the boundary and module changes after pull-request CI passes. +2. Run the `Attestation Release Gate` manually with target `all` on the merged + commit. This runs the root and nested-module preflight plus the Cocos + development gate; the Cocos release gate remains blocked while replacements + are present. +3. Sign and push `modules/attestation/snp/v0.1.0` and + `modules/attestation/tdx/v0.1.0` on the same immutable merged commit. +4. Wait for each tag-triggered gate to verify the annotated tag, its target + commit, `main` ancestry, module metadata, and tests. +5. Verify the root with `GOWORK=off`; its module graph must contain no Cocos, + SNP-module, or TDX-module dependency. +6. Sign and push `v2.0.0-rc.1`. Wait for its tag-triggered gate to succeed, then + create the prerelease from that existing remote tag: + + ```sh + gh release create v2.0.0-rc.1 \ + --verify-tag --prerelease --latest=false \ + --title "v2.0.0-rc.1" \ + --notes "Release candidate only. SNP, TDX, and Cocos hardware qualification is incomplete; no production-ready claim is made." + ``` + +7. After the root RC exists, replace the temporary Cocos development versions + with the published root and platform versions, remove all replacements, and + pass `make check-cocos-release` plus pull-request CI. +8. Sign and push `integrations/cocos/v0.1.0`, then wait for its tag-triggered + gate to succeed before creating any Cocos release. + +The root RC release notes must say that hardware qualification is incomplete. +The RC does not assert that SNP, TDX, or the Cocos integration is +production-ready. diff --git a/docs/security-red-team-tests.md b/docs/security-red-team-tests.md index 98f2259d..737f77a7 100644 --- a/docs/security-red-team-tests.md +++ b/docs/security-red-team-tests.md @@ -244,6 +244,15 @@ rejected, not accepted. ## Command +The ASB core/module split and the meaning of each test layer are described in +[attestation-module-boundary.md](attestation-module-boundary.md). + +Run the hardware-independent ASB core checks on macOS or Linux with: + +```sh +make test-asb-core +``` + Run the focused client red-team tests with: ```sh @@ -262,11 +271,22 @@ Run the response-cache red-team tests with: env GOCACHE=/tmp/go-build-asb go test -count=1 -run 'ResponseCachePolicyRedTeam' ./pkg/agtp ``` -Hardware-backed attestation replay coverage is intentionally separated from the -dependency-free CI profile. Use the manual `Hardware Attestation Red Team` -workflow with a confidential self-hosted runner, or run the same gate directly -on such a runner: +Hardware-backed attestation replay coverage is separate from the ASB core CI +profile. Use the manual `Hardware Attestation Red Team` workflow with a +confidential self-hosted runner and explicitly select `snp` or `tdx`. The +workflow requires CoRIM reference values and a platform verification policy, +then exercises the external Cocos integration. Run the same gate directly on a +suitable runner with, for example: ```sh -go run ./cmd/hardware-attestation-redteam --platform auto +cd integrations/cocos +GOWORK=off go run ./cmd/hardware-attestation-redteam \ + --platform snp \ + --corim-policy /runner/policy/reference-values.corim \ + --platform-policy /runner/policy/snp-policy.json \ + --corim-public-key /runner/policy/corim-public-key.pem \ + --require-full-verification \ + --evidence-dir /runner/evidence/asb-snp ``` + +No live SNP or TDX result is recorded for this change. diff --git a/modules/attestation/snp/README.md b/modules/attestation/snp/README.md index 5a7eb28a..e54ee3b7 100644 --- a/modules/attestation/snp/README.md +++ b/modules/attestation/snp/README.md @@ -7,7 +7,8 @@ production-ready. This module accepts either a `sevsnp.Attestation` protobuf or an AMD ABI report with its certificate table. `Verify` checks the certificate chain, report signature, CRL, local SNP policy, debug state, VMPL, and the caller-supplied -`REPORT_DATA`. Its default network getter is bounded and restricted to AMD KDS. +`REPORT_DATA`. The provided `NewKDSGetter` is bounded and restricted to AMD +KDS; callers must select it or inject another reviewed collateral getter. It does not implement ASB, TLS, EAT, or CoRIM. Those concerns stay in the root composition layer. diff --git a/scripts/check-asb-core-boundary.sh b/scripts/check-asb-core-boundary.sh new file mode 100755 index 00000000..448739b0 --- /dev/null +++ b/scripts/check-asb-core-boundary.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Copyright (c) 2026 ToppyMicroServices OÜ +# SPDX-License-Identifier: Apache-2.0 + +set -eu + +repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) +cd "$repo_root" + +dependencies=$(GOWORK=off go list -deps -test \ + ./pkg/atls/... \ + ./pkg/clients \ + ./pkg/clients/http \ + ./pkg/clients/grpc \ + ./pkg/agtp/... \ + ./pkg/tls) + +forbidden_dependencies=$( + printf '%s\n' "$dependencies" | awk ' + /^github\.com\/google\/go-sev-guest(\/|$)/ || + /^github\.com\/google\/go-tdx-guest(\/|$)/ || + /^github\.com\/google\/go-tpm-tools(\/|$)/ || + /^github\.com\/google\/go-tpm(\/|$)/ || + /^github\.com\/virtee\/sev-snp-measure-go(\/|$)/ || + /^github\.com\/veraison\/corim(\/|$)/ || + /\/pkg\/attestation(\/|$)/ || + /\/integrations\/cocos(\/|$)/ || + /\/modules\/attestation\/(snp|tdx)(\/|$)/ { + print + } + ' +) + +if [ -n "$forbidden_dependencies" ]; then + printf '%s\n' "ASB core imports concrete attestation dependencies:" >&2 + printf '%s\n' "$forbidden_dependencies" >&2 + exit 1 +fi + +printf '%s\n' "ASB core dependency boundary passed" diff --git a/scripts/check-attestation-release.sh b/scripts/check-attestation-release.sh new file mode 100755 index 00000000..64e4cfec --- /dev/null +++ b/scripts/check-attestation-release.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -eu + +module_path=$(awk '$1 == "module" { print $2; exit }' go.mod) +if [ "$module_path" != "github.com/ToppyMicroServices/agents-secure-binding/v2" ]; then + echo "release blocked: unsupported root module identity $module_path" >&2 + exit 1 +fi + +exec sh ./scripts/check-attestation-v2-release.sh diff --git a/scripts/check-attestation-v2-boundary.sh b/scripts/check-attestation-v2-boundary.sh new file mode 100755 index 00000000..af9bf5e6 --- /dev/null +++ b/scripts/check-attestation-v2-boundary.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu + +expected_module="github.com/ToppyMicroServices/agents-secure-binding/v2" +actual_module=$(awk '$1 == "module" { print $2; exit }' go.mod) + +if [ "$actual_module" != "$expected_module" ]; then + echo "v2 boundary check failed: root module is $actual_module, want $expected_module" >&2 + exit 1 +fi + +old_module="github.com/thinksyncs/agents-secure-binding" +if git grep -n -F "$old_module" -- '*.go' '*.proto' go.mod >/dev/null; then + echo "v2 boundary check failed: Go or protobuf sources still refer to $old_module" >&2 + exit 1 +fi diff --git a/scripts/check-attestation-v2-release.sh b/scripts/check-attestation-v2-release.sh new file mode 100755 index 00000000..bf18f118 --- /dev/null +++ b/scripts/check-attestation-v2-release.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +set -eu + +repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) +cd "$repo_root" + +sh ./scripts/check-attestation-v2-boundary.sh +sh ./scripts/check-asb-core-boundary.sh + +if GOWORK=off go mod edit -json | grep -Eq '"Replace"[[:space:]]*:[[:space:]]*\['; then + echo "release blocked: root go.mod contains a replacement" >&2 + exit 1 +fi + +concrete_modules='github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/' +cocos_module='github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos' + +if grep -F "$concrete_modules" go.mod >/dev/null || grep -F "$cocos_module" go.mod >/dev/null; then + echo "release blocked: root go.mod depends on a concrete attestation module" >&2 + exit 1 +fi + +GOWORK=off go mod tidy -diff +GOWORK=off go mod verify +module_graph=$(GOWORK=off go list -m all) +if printf '%s\n' "$module_graph" | grep -F "$concrete_modules" >/dev/null || \ + printf '%s\n' "$module_graph" | grep -F "$cocos_module" >/dev/null; then + echo "release blocked: root module graph contains a concrete attestation module" >&2 + exit 1 +fi + +GOWORK=off go list ./... >/dev/null +GOWORK=off go test ./... diff --git a/scripts/check-cocos-release.sh b/scripts/check-cocos-release.sh new file mode 100755 index 00000000..fd2dcf1a --- /dev/null +++ b/scripts/check-cocos-release.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +set -eu + +repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) +module_dir="$repo_root/integrations/cocos" +cd "$module_dir" + +expected_module="github.com/ToppyMicroServices/agents-secure-binding/integrations/cocos" +actual_module=$(awk '$1 == "module" { print $2; exit }' go.mod) +if [ "$actual_module" != "$expected_module" ]; then + echo "Cocos release blocked: module is $actual_module, want $expected_module" >&2 + exit 1 +fi + +if GOWORK=off go mod edit -json | grep -Eq '"Replace"[[:space:]]*:[[:space:]]*\['; then + echo "Cocos release blocked: integrations/cocos/go.mod contains a replacement" >&2 + exit 1 +fi + +root_version=$(awk '$1 == "github.com/ToppyMicroServices/agents-secure-binding/v2" { print $2; exit }' go.mod) +if ! printf '%s\n' "$root_version" | grep -Eq '^v2\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then + echo "Cocos release blocked: ASB root must use a published v2 version" >&2 + exit 1 +fi + +for module in snp tdx; do + module_path="github.com/ToppyMicroServices/agents-secure-binding/modules/attestation/$module" + module_version=$(awk -v path="$module_path" '$1 == path { print $2; exit }' go.mod) + if ! printf '%s\n' "$module_version" | grep -Eq '^v0\.[0-9]+\.[0-9]+$'; then + echo "Cocos release blocked: $module_path must use a tagged v0.x version" >&2 + exit 1 + fi +done + +GOWORK=off go mod tidy -diff +GOWORK=off go mod verify +GOWORK=off go list ./... >/dev/null +GOWORK=off go test ./... From 8d65ee6c9074d95df15ff1e063676b2d42e9e89e Mon Sep 17 00:00:00 2001 From: thinksyncs Date: Sun, 30 Aug 2026 18:04:47 +0900 Subject: [PATCH 7/8] chore(module): format migrated imports --- examples/a2a-multiprocess/wire_fixture_v2_test.go | 2 +- pkg/authorityquorum/asbbinding/profile_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/a2a-multiprocess/wire_fixture_v2_test.go b/examples/a2a-multiprocess/wire_fixture_v2_test.go index 578143ce..303ea29a 100644 --- a/examples/a2a-multiprocess/wire_fixture_v2_test.go +++ b/examples/a2a-multiprocess/wire_fixture_v2_test.go @@ -21,10 +21,10 @@ import ( "testing" "time" - "github.com/golang-jwt/jwt/v5" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/sbaipv2" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/golang-jwt/jwt/v5" ) type draft06V2WireFixture struct { diff --git a/pkg/authorityquorum/asbbinding/profile_test.go b/pkg/authorityquorum/asbbinding/profile_test.go index 2a8a7144..2571883c 100644 --- a/pkg/authorityquorum/asbbinding/profile_test.go +++ b/pkg/authorityquorum/asbbinding/profile_test.go @@ -10,10 +10,10 @@ import ( "testing" "time" - "github.com/golang-jwt/jwt/v5" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/atls/identitypolicy" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/authorityquorum" "github.com/ToppyMicroServices/agents-secure-binding/v2/pkg/clients" + "github.com/golang-jwt/jwt/v5" ) const testAudience = "reveal.example" From a247a31022acb3c1046585598c32e506ce30fb87 Mon Sep 17 00:00:00 2001 From: thinksyncs Date: Sun, 30 Aug 2026 18:10:06 +0900 Subject: [PATCH 8/8] fix(ci): satisfy CoRIM platform lint --- pkg/attestation/corimgen/generator.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pkg/attestation/corimgen/generator.go b/pkg/attestation/corimgen/generator.go index 7d60f2dc..2c964edf 100644 --- a/pkg/attestation/corimgen/generator.go +++ b/pkg/attestation/corimgen/generator.go @@ -32,6 +32,8 @@ const ( TDXMRSEAMMKey uint64 = 0x2001 TDXRTMR0MKey uint64 = 0x2010 maximumTDXRuntimeRegisters = 4 + platformSNP = "snp" + platformTDX = "tdx" ) // Legacy TDX Defaults. @@ -104,11 +106,11 @@ func GenerateCoRIM(opts Options) ([]byte, error) { // applyDefaults applies platform-specific defaults to options. func applyDefaults(opts *Options) { - if opts.Platform == "snp" { + if opts.Platform == platformSNP { if opts.Measurement == "" { opts.Measurement = SNPDefaultMeasurement } - } else if opts.Platform == "tdx" { + } else if opts.Platform == platformTDX { if opts.Measurement == "" { opts.Measurement = TDXDefaultMrTd } @@ -195,14 +197,14 @@ func createReferenceValue(opts Options) (*comid.ReferenceValue, error) { // Initialize measurements slice with the platform's primary measurement. refVal.Measurements = comid.Measurements{*mval} - if opts.Platform == "snp" { + if opts.Platform == platformSNP { if err := addSNPReferenceValues(refVal, opts); err != nil { return nil, err } } // Platform-specific additions - if opts.Platform == "tdx" { + if opts.Platform == platformTDX { if opts.SVN != 0 { return nil, fmt.Errorf("TDX scalar SVN is unsupported; use the platform policy's 16-byte minimum_tee_tcb_svn") } @@ -250,9 +252,9 @@ func createReferenceValue(opts Options) (*comid.ReferenceValue, error) { func mainMeasurementKey(platform string) (uint64, error) { switch platform { - case "snp": + case platformSNP: return SNPMeasurementMKey, nil - case "tdx": + case platformTDX: return TDXMRTDMKey, nil default: return 0, fmt.Errorf("unsupported CoRIM platform %q", platform) @@ -310,7 +312,7 @@ func newRawValueMeasurement(key uint64, raw []byte) (*comid.Measurement, error) func platformMeasurementAlgorithm(platform string) (uint64, error) { switch platform { - case "snp", "tdx": + case platformSNP, platformTDX: // SNP MEASUREMENT and the TDX MRTD/RTMR fields are SHA-384 values. return swid.Sha384, nil default: