├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .goreleaser.yaml ├── CONTRIBUTING.md ├── INSTALL.md ├── LICENSE ├── LIMITATIONS.md ├── README.md ├── abi ├── abi.go ├── abi_test.go ├── amdsp.go ├── cpuid.go ├── cpuid_amd64.go └── cpuid_amd64.s ├── client ├── client.go ├── client_linux.go ├── client_macos.go ├── client_test.go ├── client_windows.go └── linuxabi │ └── linux_abi.go ├── go.mod ├── go.sum ├── kds ├── kds.go └── kds_test.go ├── proto ├── check.proto ├── check │ ├── check.pb.go │ └── doc.go ├── doc.go ├── fakekds.proto ├── fakekds │ ├── doc.go │ └── fakekds.pb.go ├── sevsnp.proto └── sevsnp │ ├── doc.go │ └── sevsnp.pb.go ├── testing ├── client │ └── client.go ├── data │ ├── data.go │ └── keys │ │ ├── ark_private_key.pem │ │ ├── ask_private_key.pem │ │ ├── asvk_private_key.pem │ │ ├── vcek_private_key.pem │ │ └── vlek_private_key.pem ├── fake_certs.go ├── fake_certs_test.go ├── fakekds.go ├── match.go ├── mocks.go └── test_cases.go ├── tools ├── attest │ ├── README.md │ └── attest.go ├── check │ ├── README.md │ ├── check.go │ └── check_test.go ├── lib │ ├── cmdline │ │ ├── cmdline.go │ │ └── cmdline_test.go │ └── report │ │ ├── report.go │ │ └── report_test.go └── show │ └── main.go ├── validate ├── validate.go └── validate_test.go └── verify ├── testdata ├── attestation.bin ├── milan.testcer ├── milanvlek.testcer ├── testdata.go └── vcek.testcer ├── trust ├── ask_ark_genoa.pem ├── ask_ark_genoa_vlek.pem ├── ask_ark_milan.pem ├── ask_ark_milan_vlek.pem ├── ask_ark_turin_vcek.pem ├── ask_ark_turin_vlek.pem ├── trust.go └── trust_test.go ├── verify.go └── verify_test.go /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2022 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | # use this file except in compliance with the License. You may obtain a copy of 6 | # the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations under 14 | # the License. 15 | # 16 | 17 | name: CI 18 | on: 19 | push: 20 | tags: 21 | - v* 22 | branches: [main] 23 | pull_request: 24 | branches: [main] 25 | 26 | jobs: 27 | build: 28 | strategy: 29 | matrix: 30 | go-version: [1.21.x, 1.22.x] 31 | os: [macos-latest, ubuntu-latest] 32 | 33 | name: Build/Test (${{ matrix.os}}, Go ${{ matrix.go-version }}) 34 | runs-on: ${{ matrix.os }} 35 | steps: 36 | - uses: actions/checkout@v3 37 | - name: Setup Go 38 | uses: actions/setup-go@v3 39 | with: 40 | go-version: ${{ matrix.go-version }} 41 | - name: Install Protoc 42 | uses: arduino/setup-protoc@v3 43 | with: 44 | repo-token: ${{ secrets.GITHUB_TOKEN }} 45 | version: "27.2" 46 | - name: Install protoc-gen-go 47 | run: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.31.0 48 | - name: Check Protobuf Generation 49 | run: | 50 | go generate ./... 51 | git diff -G'^[^/]' --exit-code 52 | - name: Generate all protobufs 53 | run: go generate ./... 54 | - name: Build all packages 55 | run: go build -v ./... 56 | - name: Test all packages 57 | run: go test -v -race ./... 58 | - name: Run Go Vet 59 | run: go vet ./... 60 | 61 | lint: 62 | strategy: 63 | matrix: 64 | go-version: [1.21.x] 65 | os: [ubuntu-latest] 66 | 67 | name: Lint ${{ matrix.dir }} (${{ matrix.os }}, Go ${{ matrix.go-version }}) 68 | runs-on: ${{ matrix.os }} 69 | steps: 70 | - uses: actions/checkout@v3 71 | - uses: actions/setup-go@v2 72 | with: 73 | go-version: ${{ matrix.go-version }} 74 | - name: Run golangci-lint 75 | uses: golangci/golangci-lint-action@v3.6.0 76 | with: 77 | version: latest 78 | working-directory: ./ 79 | args: > 80 | -D errcheck 81 | -E stylecheck 82 | -E goimports 83 | -E misspell 84 | -E revive 85 | -E gofmt 86 | -E goimports 87 | --out-format=colored-line-number 88 | --exclude-use-default=false 89 | --max-same-issues=0 90 | --max-issues-per-linter=0 91 | --timeout 2m 92 | 93 | lintc: 94 | strategy: 95 | matrix: 96 | go-version: [1.21.x] 97 | os: [ubuntu-latest] 98 | 99 | name: Lint CGO (${{ matrix.os}}, Go ${{ matrix.go-version }}) 100 | runs-on: ${{ matrix.os }} 101 | steps: 102 | - uses: actions/checkout@v3 103 | - uses: actions/setup-go@v2 104 | with: 105 | go-version: ${{ matrix.go-version }} 106 | - name: Check for CGO Warnings (gcc) 107 | run: CGO_CFLAGS=-Werror CC=gcc go build ./... 108 | - name: Check for CGO Warnings (clang) 109 | run: CGO_CFLAGS=-Werror CC=clang go build ./... 110 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | branches: 6 | tags: 7 | - 'v*' 8 | pull_request: 9 | 10 | jobs: 11 | release: 12 | strategy: 13 | matrix: 14 | go-version: [1.19.x] 15 | os: [ubuntu-latest] 16 | 17 | name: Release for (${{ matrix.os}}, Go ${{ matrix.go-version }}) 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | - uses: actions/setup-go@v3 24 | with: 25 | go-version: ${{ matrix.go-version }} 26 | cache: true 27 | - shell: bash 28 | run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_ENV 29 | - id: cache 30 | uses: actions/cache@v3 31 | with: 32 | path: dist/${{ matrix.os }} 33 | key: ${{ matrix.go }}-${{ env.sha_short }} 34 | - name: Build all packages 35 | run: go build -v ./... 36 | - name: Run GoReleaser 37 | uses: goreleaser/goreleaser-action@v3 38 | if: success() && startsWith(github.ref, 'refs/tags/') && steps.cache.outputs.cache-hit != 'true' 39 | with: 40 | version: latest 41 | args: release --clean 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*.* 3 | !*/ 4 | *~ 5 | external/* 6 | kdsdatabase.bin* 7 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - env: 3 | - CGO_ENABLED=0 4 | goos: 5 | - linux 6 | # - windows 7 | # - darwin 8 | goarch: 9 | - amd64 10 | id: "attest" 11 | main: ./tools/attest/attest.go 12 | binary: attest 13 | - env: 14 | - CGO_ENABLED=0 15 | goos: 16 | - linux 17 | # - windows 18 | # - darwin 19 | goarch: 20 | - amd64 21 | id: "check" 22 | main: ./tools/check/check.go 23 | binary: check 24 | 25 | 26 | archives: 27 | - format: tar.gz 28 | # this name template makes the OS and Arch compatible with the results of uname. 29 | name_template: >- 30 | {{ .ProjectName }}_ 31 | {{- title .Os }}_ 32 | {{- if eq .Arch "amd64" }}x86_64 33 | {{- else if eq .Arch "386" }}i386 34 | {{- else }}{{ .Arch }}{{ end }} 35 | {{- if .Arm }}v{{ .Arm }}{{ end }} 36 | # use zip for windows archives 37 | format_overrides: 38 | - goos: windows 39 | format: zip 40 | checksum: 41 | name_template: 'checksums.txt' 42 | snapshot: 43 | name_template: "{{ incpatch .Version }}-next" 44 | changelog: 45 | sort: asc 46 | filters: 47 | exclude: 48 | - '^docs:' 49 | - '^test:' 50 | 51 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json 52 | # vim: set ts=2 sw=2 tw=0 fo=cnqoj 53 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google.com/conduct/). 29 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation instructions 2 | 3 | go-sev-guest is a library and set of CLI tools for interacting with the 4 | `/dev/sev-guest` driver. There are thus a few requirements for its use. 5 | 6 | ## System requirements 7 | 8 | This driver is only available on AMD SEV-SNP enabled virtual machines. Do 9 | determine support, run 10 | 11 | ```shell 12 | dmesg | grep "SEV-SNP supported" 13 | ``` 14 | 15 | Your Linux distribution may build in support for the sev-guest driver, or may 16 | relegate it to a loadable kernel module. 17 | 18 | Ensure the module is loaded with 19 | 20 | ```shell 21 | modprobe sev-guest 22 | ``` 23 | 24 | If this command fails, check with your distribution for which installable 25 | package it may be distributed in, and install that. For example, Ubuntu may 26 | distribute `sev-guest` in `linux-modules-$(uname -r)`. 27 | 28 | ## Kernel config 29 | 30 | When building your own Linux kernel, on top of the other configuration options 31 | needed for SEV-SNP, you will need to have `CONFIG_VIRT_DRIVERS=y` and either 32 | `CONFIG_SEV_GUEST=y` or `CONFIG_SEV_GUEST=m` depending on whether you want the 33 | driver to be built in or a module. 34 | 35 | ## Device requires root permissions 36 | 37 | Unless your image has custom initialization rules to grant broader privileges to 38 | the sev-guest device, the Linux user that accesses `/dev/sev-guest` must have 39 | root privileges. 40 | 41 | To provide attestation report capabilities to a lesser-privileged user, you will 42 | need to create a privileged client that can act on their behalf. 43 | -------------------------------------------------------------------------------- /LIMITATIONS.md: -------------------------------------------------------------------------------- 1 | # Security limitations 2 | 3 | This document's goal is to provide a short discussion on what security 4 | properties access to /dev/sev-guest has, specifically in a Cloud setting. 5 | 6 | ## Initial assumptions 7 | 8 | For claims in this document, we assume that the VM firmware is vendored and 9 | signed by the vendor, or not signed at all. This will be the common use case. 10 | 11 | If a virtual machine monitor gives you access to provide your own launch image 12 | and signed IDBlock, then you have full control and responsibility over what that 13 | image does, and how careful it is to give workloads access to the SEV 14 | device. Since this library is for wrapping the Linux driver /dev/sev-guest which 15 | will be booted from a UEFI firmware, further discussion of other uses of SEV-SNP 16 | are out of scope. 17 | 18 | ## Implications of a vendored firmware 19 | 20 | Your Cloud service provider (CSP) may launch SEV-SNP VMs with their own build of 21 | UEFI firmware. This build and its signature in IDBlock are probably widely 22 | deployed across the CSP's fleet. Because the firmware and IDBlock are the same 23 | everywhere, the measurement in the attestation report will be the same 24 | everywhere. The IDBlock, if provided, may also be the same everywhere. 25 | 26 | The security of an attestation measurement or a derived key are proportional to 27 | the specificity of the initially measured image. If the initial image and its 28 | IDBlock is available to everyone and can run any workload, then keys bound to 29 | their measurement are known to everyone. An IDBlock signed by a different key 30 | will lead to different keys, but the IDBlock and ID Auth are also not secret 31 | in the SEV-SNP threat model. The measured image's behavior in granting 32 | authorization is what is important. 33 | 34 | At the moment, the basis for most VM firmware, Open Virtual Machine Firmware 35 | (OVMF), does not have the behavior to lock in such a specific measurement and 36 | authorization semantics in a way that is reflected in the SEV-SNP attestation 37 | report. Measured boot integrity is dynamic post-launch via the TPM 2.0 38 | specification, which for VMs is virtualized in software and not secret within 39 | the SEV-SNP threat model. 40 | 41 | Supposing we did have a boot stack that accounted for workload and its 42 | configuration in the SEV-SNP attestation report, software updates change the 43 | measurement and thus change the derived keys. In a self-updating VM, you'd need 44 | custom software to manage the implications of changing derived keys. 45 | 46 | ## MSG_KEY_REQ, or GetDerivedKey 47 | 48 | Keys are derived from the launch information discussed above and the specific 49 | machine's SEV-SNP key called the Versioned Chip Endorsement Key (VCEK). When you 50 | do not have full control over the machine that derives the key, and your launch 51 | image isn't fully linked to the workload you trust to have access to the key, 52 | you should not set `UseVCEK` to true. 53 | 54 | With `UseVCEK` set to false, you must be using an image that supports a migration 55 | agent (MA). The MA will register a root key that migrates with the image, called 56 | the VMRK. The security of this VMRK is entirely up to the MA's logic. If the 57 | key is meant to persist across full shutdown and restart, then you have to solve 58 | a hard problem: sealing that key to persist in a way that only the authorized 59 | workload should later have access to. That is the same problem that exists for 60 | VCEK. 61 | 62 | If you're okay with keys that migrate but aren't otherwise recoverable, then 63 | VMRK key-based derivation should meet your needs. To many, that possibility of 64 | unrecoverable data loss is too risky to choose this option either. 65 | 66 | Because of the danger in both root key selections, we do not recommend using 67 | this command unless you have full ownership of and secure physical access to the 68 | machine that will run it, and trust all parties that run software on that 69 | machine. 70 | -------------------------------------------------------------------------------- /abi/amdsp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package abi 16 | 17 | import "fmt" 18 | 19 | // SevFirmwareStatus is the type of all AMD-SP firmware status codes, as documented in the SEV API 20 | // https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf 21 | type SevFirmwareStatus int64 22 | 23 | // Unexported errors are not expected to leave the kernel. 24 | const ( 25 | // Success denotes successful completion of a firmware command. 26 | Success SevFirmwareStatus = 0 27 | // InvalidPlatformState is the code for the platform to be in the wrong state for a given command. 28 | InvalidPlatformState = 1 29 | // InvalidGuestState is the code for the guest to be in the wrong state for a given command. 30 | InvalidGuestState = 2 31 | // Platform owner error unexpected by guest command. 32 | // invalidConfig = 3 33 | // InvalidLength is the code for a provided buffer size is too small to complete the command. 34 | InvalidLength = 4 35 | // Platform owner error unexpected by guest command. 36 | // alreadyOwned = 5 37 | // Platform owner error unexpected by guest command. 38 | // invalidCertificate = 6 39 | // PolicyFailure is the code for when the guest policy disallows the command. 40 | PolicyFailure = 7 41 | // Inactive is the code for when a command is sent for a guest, but the guest is inactive. 42 | Inactive = 8 43 | // InvalidAddress is the code for when a provided address is invalid. 44 | InvalidAddress = 9 45 | // User error expected at launch, unexpected here. 46 | // badSignature = 10 47 | // User error expected at launch, unexpected here. 48 | // badMeasurement = 11 49 | // Kernel error, unexpected. 50 | // asidOwned = 12 51 | // Kernel error, unexpected. 52 | // invalidAsid = 13 53 | // Kernel error, unexpected. 54 | // wbinvdRequired = 14 55 | // Kernel error, unexpected. 56 | // dfFlushRequired = 15 57 | // Kernel error, unexpected. 58 | // invalidGuest = 16 59 | // InvalidCommand is the code for when the command code is invalid. 60 | InvalidCommand = 17 61 | // Kernel error, unexpected. 62 | // active = 18 63 | // HwErrorPlatform is the code for when the hardware failed but it's okay to update its buffers. 64 | HwErrorPlatform = 19 65 | // HwErrorUnsafe is the code for when the hardware failed and it's unsafe to update its buffers. 66 | HwErrorUnsafe = 20 67 | // Unsupported is for an unsupported feature. 68 | Unsupported = 21 69 | // InvalidParam is the code for an invalid parameter in a command. 70 | InvalidParam = 22 71 | // ResourceLimit is the code for when the firmware has reached a resource limit and can't complete the command. 72 | ResourceLimit = 23 73 | // SecureDataInvalid is the code for when a hardware integrity check has failed. 74 | SecureDataInvalid = 24 75 | // InvalidPageSize indicates an RMP error with the recorded page size. 76 | InvalidPageSize = 25 77 | // InvalidPageState indicates an RMP error with the recorded page state. 78 | InvalidPageState = 26 79 | // InvalidMdataEntry indicates an RMP error with the recorded metadata. 80 | InvalidMdataEntry = 27 81 | // InvalidPageOwner indicates an RMP error with ASID mismatch between accessors. 82 | InvalidPageOwner = 28 83 | // AeadOflow indicates that firmware memory capacity is reached in the AEAD cryptographic algorithm. 84 | AeadOflow = 29 85 | // Skip code 0x1E since AeaedOflow is 0x1D and rbModeExited is 0x1F. 86 | // reserved1e = 30 87 | // Kernel error, unexpected. 88 | // rbModeExited = 31 89 | // Kernel error, unexpected. 90 | // rmpInitRequired = 32 91 | // Platform management error, unexpected. 92 | // badSvn = 33 93 | // Platform management error, unexpected. 94 | // badVersion = 34 95 | // Platform management error, unexpected. 96 | // shutdownRequired = 35 97 | // Platform management error, unexpected. 98 | // updateFailed = 36 99 | // Platform management error, unexpected. 100 | // restoreRequired = 37 101 | ) 102 | 103 | // GuestRequestInvalidLength is set by the ccp driver and not the AMD-SP when an guest extended 104 | // request provides too few pages for the firmware to populate with data. 105 | const GuestRequestInvalidLength SevFirmwareStatus = 0x100000000 106 | 107 | // SevFirmwareErr is an error that interprets firmware status codes from the AMD secure processor. 108 | type SevFirmwareErr struct { 109 | Status SevFirmwareStatus 110 | } 111 | 112 | func (e *SevFirmwareErr) Error() string { 113 | if e.Status == Success { 114 | return "success" 115 | } 116 | if e.Status == InvalidPlatformState { 117 | return "platform state is invalid for this command" 118 | } 119 | if e.Status == InvalidGuestState { 120 | return "guest state is invalid for this command" 121 | } 122 | if e.Status == InvalidLength { 123 | return "memory buffer is too small (library bug, please report)" 124 | } 125 | if e.Status == PolicyFailure { 126 | return "request is not allowed by guest policy" 127 | } 128 | if e.Status == Inactive { 129 | return "guest is inactive" 130 | } 131 | if e.Status == InvalidAddress { 132 | return "address provided is invalid (library bug, please report)" 133 | } 134 | if e.Status == InvalidCommand { 135 | return "invalid command (library bug, please report)" 136 | } 137 | if e.Status == HwErrorPlatform { 138 | return "hardware condition has occurred affecting the platform (report to sysadmin)" 139 | } 140 | if e.Status == HwErrorUnsafe { 141 | return "hardware condition has occurred affecting the platform. Buffers unsafe (report to sysadmin)" 142 | } 143 | if e.Status == Unsupported { 144 | return "unsupported feature" 145 | } 146 | if e.Status == InvalidParam { 147 | return "invalid parameter (library bug, please report)" 148 | } 149 | if e.Status == ResourceLimit { 150 | return "SEV firmware has run out of recources necessary to complete the command" 151 | } 152 | if e.Status == SecureDataInvalid { 153 | return "part-specific SEV data failed integrity checks (report to sysadmin)" 154 | } 155 | if e.Status == InvalidPageSize { 156 | return "RMP: invalid page size" 157 | } 158 | if e.Status == InvalidPageState { 159 | return "RMP: invalid page state" 160 | } 161 | if e.Status == InvalidMdataEntry { 162 | return "RMP: invalid recorded metadata" 163 | } 164 | if e.Status == InvalidPageOwner { 165 | return "RMP: ASID mismatch between accessors" 166 | } 167 | if e.Status == AeadOflow { 168 | return "AMD-SP firmware memory would be over capacity for AEAD use" 169 | } 170 | if e.Status == GuestRequestInvalidLength { 171 | return "too few extended guest request data pages" 172 | } 173 | return fmt.Sprintf("unexpected firmware status (see SEV API spec): %x", uint64(e.Status)) 174 | } 175 | -------------------------------------------------------------------------------- /abi/cpuid.go: -------------------------------------------------------------------------------- 1 | //go:build !amd64 || gccgo 2 | // +build !amd64 gccgo 3 | 4 | package abi 5 | 6 | func init() { 7 | cpuid = func(uint32) (uint32, uint32, uint32, uint32) { 8 | return 0, 0, 0, 0 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /abi/cpuid_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build amd64 && !gccgo 2 | // +build amd64,!gccgo 3 | 4 | package abi 5 | 6 | func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) 7 | 8 | func init() { 9 | cpuid = asmCpuid 10 | } 11 | -------------------------------------------------------------------------------- /abi/cpuid_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //+build amd64,!gccgo 16 | 17 | // func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) 18 | TEXT ·asmCpuid(SB), 7, $0 19 | XORQ CX, CX 20 | MOVL op+0(FP), AX 21 | CPUID 22 | MOVL AX, eax+8(FP) 23 | MOVL BX, ebx+12(FP) 24 | MOVL CX, ecx+16(FP) 25 | MOVL DX, edx+20(FP) 26 | RET 27 | -------------------------------------------------------------------------------- /client/client_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build linux || freebsd || openbsd || netbsd 16 | 17 | // Package client provides an interface to the AMD SEV-SNP guest device commands. 18 | package client 19 | 20 | import ( 21 | "flag" 22 | "fmt" 23 | "strconv" 24 | "time" 25 | 26 | "github.com/google/go-configfs-tsm/configfs/linuxtsm" 27 | "github.com/google/go-configfs-tsm/report" 28 | "github.com/google/go-sev-guest/abi" 29 | labi "github.com/google/go-sev-guest/client/linuxabi" 30 | spb "github.com/google/go-sev-guest/proto/sevsnp" 31 | "golang.org/x/sys/unix" 32 | ) 33 | 34 | const ( 35 | // defaultSevGuestDevicePath is the platform's usual device path to the SEV guest. 36 | defaultSevGuestDevicePath = "/dev/sev-guest" 37 | installURL = "https://github.com/google/go-sev-guest/blob/main/INSTALL.md" 38 | ) 39 | 40 | // These flags should not be needed for long term health of the project as the Linux kernel 41 | // catches up with throttling-awareness. 42 | var ( 43 | throttleDuration = flag.Duration("self_throttle_duration", 2*time.Second, "Rate-limit library-initiated device commands to this duration") 44 | burstMax = flag.Int("self_throttle_burst", 1, "Rate-limit library-initiated device commands to this many commands per duration") 45 | defaultVMPL = flag.String("default_vmpl", "", "Default VMPL to use for attestation (empty for driver default)") 46 | ) 47 | 48 | // LinuxDevice implements the Device interface with Linux ioctls. 49 | type LinuxDevice struct { 50 | fd int 51 | lastCmd time.Time 52 | burst int 53 | } 54 | 55 | // Open opens the SEV-SNP guest device from a given path 56 | func (d *LinuxDevice) Open(path string) error { 57 | fd, err := unix.Open(path, unix.O_RDWR, 0) 58 | if err != nil { 59 | d.fd = -1 60 | return fmt.Errorf("could not open AMD SEV guest device at %s (see %s): %v", path, installURL, err) 61 | } 62 | d.fd = fd 63 | return nil 64 | } 65 | 66 | // OpenDevice opens the SEV-SNP guest device. 67 | func OpenDevice() (*LinuxDevice, error) { 68 | result := &LinuxDevice{} 69 | path := *sevGuestPath 70 | if UseDefaultSevGuest() { 71 | path = defaultSevGuestDevicePath 72 | } 73 | if err := result.Open(path); err != nil { 74 | return nil, err 75 | } 76 | return result, nil 77 | } 78 | 79 | // Close closes the SEV-SNP guest device. 80 | func (d *LinuxDevice) Close() error { 81 | if d.fd == -1 { // Not open 82 | return nil 83 | } 84 | if err := unix.Close(d.fd); err != nil { 85 | return err 86 | } 87 | // Prevent double-close. 88 | d.fd = -1 89 | return nil 90 | } 91 | 92 | // Ioctl sends a command with its wrapped request and response values to the Linux device. 93 | func (d *LinuxDevice) Ioctl(command uintptr, req any) (uintptr, error) { 94 | // TODO(Issue #40): Remove the workaround to the ENOTTY lockout when throttled 95 | // in Linux 6.1 by throttling ourselves first. 96 | if d.burst == 0 { 97 | sinceLast := time.Since(d.lastCmd) 98 | // Self-throttle for tests without guest OS throttle detection 99 | if sinceLast < *throttleDuration { 100 | time.Sleep(*throttleDuration - sinceLast) 101 | } 102 | } 103 | switch sreq := req.(type) { 104 | case *labi.SnpUserGuestRequest: 105 | abi := sreq.ABI() 106 | result, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(d.fd), command, uintptr(abi.Pointer())) 107 | abi.Finish(sreq) 108 | d.burst = (d.burst + 1) % *burstMax 109 | if d.burst == 0 { 110 | d.lastCmd = time.Now() 111 | } 112 | 113 | // TODO(Issue #5): remove the work around for the kernel bug that writes 114 | // uninitialized memory back on non-EIO. 115 | if errno != unix.EIO { 116 | sreq.FwErr = 0 117 | } 118 | if errno != 0 { 119 | return 0, errno 120 | } 121 | return result, nil 122 | } 123 | return 0, fmt.Errorf("unexpected request value: %v", req) 124 | } 125 | 126 | // Product returns the current CPU's associated AMD SEV product information. 127 | func (d *LinuxDevice) Product() *spb.SevProduct { 128 | return abi.SevProduct() 129 | } 130 | 131 | // LinuxIoctlQuoteProvider implements the QuoteProvider interface to fetch 132 | // attestation quote via the deprecated /dev/sev-guest ioctl. 133 | type LinuxIoctlQuoteProvider struct{} 134 | 135 | // IsSupported checks if TSM client can be created to use /dev/sev-guest ioctl. 136 | func (p *LinuxIoctlQuoteProvider) IsSupported() bool { 137 | d, err := OpenDevice() 138 | if err != nil { 139 | return false 140 | } 141 | d.Close() 142 | return true 143 | } 144 | 145 | // GetRawQuoteAtLevel returns byte format attestation plus certificate table via /dev/sev-guest ioctl. 146 | func (p *LinuxIoctlQuoteProvider) GetRawQuoteAtLevel(reportData [64]byte, level uint) ([]uint8, error) { 147 | d, err := OpenDevice() 148 | if err != nil { 149 | return nil, err 150 | } 151 | defer d.Close() 152 | // If there are no certificates, then just return the raw report. 153 | length, err := queryCertificateLength(d, int(level)) 154 | if err != nil { 155 | return GetRawReportAtVmpl(d, reportData, int(level)) 156 | } 157 | certs := make([]byte, length) 158 | report, _, err := getExtendedReportIn(d, reportData, int(level), certs) 159 | if err != nil { 160 | return nil, err 161 | } 162 | // Mix the platform info in with the auxblob. 163 | extended, err := abi.ExtendedPlatformCertTable(certs) 164 | if err != nil { 165 | return nil, fmt.Errorf("invalid certificate table: %v", err) 166 | } 167 | return append(report, extended...), nil 168 | } 169 | 170 | // GetRawQuote returns byte format attestation plus certificate table via /dev/sev-guest ioctl. 171 | func (p *LinuxIoctlQuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8, error) { 172 | if *defaultVMPL == "" { 173 | return p.GetRawQuoteAtLevel(reportData, 0) 174 | } 175 | vmpl, err := strconv.ParseUint(*defaultVMPL, 10, 32) 176 | if err != nil { 177 | return nil, fmt.Errorf("bad default_vmpl: %q", *defaultVMPL) 178 | } 179 | return p.GetRawQuoteAtLevel(reportData, uint(vmpl)) 180 | } 181 | 182 | // Product returns the current CPU's associated AMD SEV product information. 183 | // 184 | // Deprecated: Use ExtraPlatformInfoGUID from the cert table. 185 | func (*LinuxIoctlQuoteProvider) Product() *spb.SevProduct { 186 | return abi.SevProduct() 187 | } 188 | 189 | // LinuxConfigFsQuoteProvider implements the QuoteProvider interface to fetch 190 | // attestation quote via ConfigFS. 191 | type LinuxConfigFsQuoteProvider struct{} 192 | 193 | // IsSupported checks if TSM client can be created to use ConfigFS system. 194 | func (p *LinuxConfigFsQuoteProvider) IsSupported() bool { 195 | c, err := linuxtsm.MakeClient() 196 | if err != nil { 197 | return false 198 | } 199 | r, err := report.Create(c, &report.Request{}) 200 | if err != nil { 201 | return false 202 | } 203 | provider, err := r.ReadOption("provider") 204 | return err == nil && string(provider) == "sev_guest\n" 205 | } 206 | 207 | // GetRawQuoteAtLevel returns byte format attestation plus certificate table via ConfigFS. 208 | func (p *LinuxConfigFsQuoteProvider) GetRawQuoteAtLevel(reportData [64]byte, level uint) ([]uint8, error) { 209 | req := &report.Request{ 210 | InBlob: reportData[:], 211 | GetAuxBlob: true, 212 | Privilege: &report.Privilege{ 213 | Level: level, 214 | }, 215 | } 216 | resp, err := linuxtsm.GetReport(req) 217 | if err != nil { 218 | return nil, err 219 | } 220 | // Mix the platform info in with the auxblob. 221 | extended, err := abi.ExtendedPlatformCertTable(resp.AuxBlob) 222 | if err != nil { 223 | return nil, fmt.Errorf("invalid certificate table: %v", err) 224 | } 225 | return append(resp.OutBlob, extended...), nil 226 | } 227 | 228 | // GetRawQuote returns byte format attestation plus certificate table via ConfigFS. 229 | func (p *LinuxConfigFsQuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8, error) { 230 | req := &report.Request{ 231 | InBlob: reportData[:], 232 | GetAuxBlob: true, 233 | } 234 | if *defaultVMPL != "" { 235 | vmpl, err := strconv.ParseUint(*defaultVMPL, 10, 32) 236 | if err != nil { 237 | return nil, fmt.Errorf("bad default_vmpl: %q", *defaultVMPL) 238 | } 239 | req.Privilege = &report.Privilege{ 240 | Level: uint(vmpl), 241 | } 242 | } 243 | resp, err := linuxtsm.GetReport(req) 244 | if err != nil { 245 | return nil, err 246 | } 247 | // Mix the platform info in with the auxblob. 248 | extended, err := abi.ExtendedPlatformCertTable(resp.AuxBlob) 249 | if err != nil { 250 | return nil, fmt.Errorf("invalid certificate table: %v", err) 251 | } 252 | return append(resp.OutBlob, extended...), nil 253 | } 254 | 255 | // Product returns the current CPU's associated AMD SEV product information. 256 | // 257 | // Deprecated: Use ExtraPlatformInfoGUID from the cert table. 258 | func (*LinuxConfigFsQuoteProvider) Product() *spb.SevProduct { 259 | return abi.SevProduct() 260 | } 261 | 262 | // GetQuoteProvider returns a supported SEV-SNP QuoteProvider. 263 | func GetQuoteProvider() (QuoteProvider, error) { 264 | var provider QuoteProvider 265 | provider = &LinuxConfigFsQuoteProvider{} 266 | if provider.IsSupported() { 267 | return provider, nil 268 | } 269 | provider = &LinuxIoctlQuoteProvider{} 270 | if provider.IsSupported() { 271 | return provider, nil 272 | } 273 | return nil, fmt.Errorf("no supported SEV-SNP QuoteProvider found") 274 | } 275 | 276 | // GetLeveledQuoteProvider returns a supported SEV-SNP LeveledQuoteProvider. 277 | func GetLeveledQuoteProvider() (LeveledQuoteProvider, error) { 278 | var provider LeveledQuoteProvider 279 | provider = &LinuxConfigFsQuoteProvider{} 280 | if provider.IsSupported() { 281 | return provider, nil 282 | } 283 | provider = &LinuxIoctlQuoteProvider{} 284 | if provider.IsSupported() { 285 | return provider, nil 286 | } 287 | return nil, fmt.Errorf("no supported SEV-SNP LeveledQuoteProvider found") 288 | } 289 | -------------------------------------------------------------------------------- /client/client_macos.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build darwin 16 | 17 | package client 18 | 19 | import ( 20 | "fmt" 21 | 22 | spb "github.com/google/go-sev-guest/proto/sevsnp" 23 | ) 24 | 25 | // DefaultSevGuestDevicePath is the platform's usual device path to the SEV guest. 26 | const DefaultSevGuestDevicePath = "unknown" 27 | 28 | // MacOSDevice implements the Device interface with Linux ioctls. 29 | type MacOSDevice struct{} 30 | 31 | // Open is not supported on MacOS. 32 | func (*MacOSDevice) Open(_ string) error { 33 | return fmt.Errorf("MacOS is unsupported") 34 | } 35 | 36 | // OpenDevice fails on MacOS. 37 | func OpenDevice() (*MacOSDevice, error) { 38 | return nil, fmt.Errorf("MacOS is unsupported") 39 | } 40 | 41 | // Close is not supported on MacOS. 42 | func (*MacOSDevice) Close() error { 43 | return fmt.Errorf("MacOS is unsupported") 44 | } 45 | 46 | // Ioctl is not supported on MacOS. 47 | func (*MacOSDevice) Ioctl(_ uintptr, _ any) (uintptr, error) { 48 | return 0, fmt.Errorf("MacOS is unsupported") 49 | } 50 | 51 | // Product is not supported on MacOS. 52 | func (*MacOSDevice) Product() *spb.SevProduct { 53 | return &spb.SevProduct{} 54 | } 55 | 56 | // MacOSQuoteProvider implements the QuoteProvider interface with Linux's configfs-tsm. 57 | type MacOSQuoteProvider struct{} 58 | 59 | // IsSupported checks if the quote provider is supported. 60 | func (*MacOSQuoteProvider) IsSupported() bool { 61 | return false 62 | } 63 | 64 | // GetRawQuote returns byte format attestation plus certificate table via ConfigFS. 65 | func (*MacOSQuoteProvider) GetRawQuote(reportData [64]byte) ([]byte, error) { 66 | return nil, fmt.Errorf("MacOS is unsupported") 67 | } 68 | 69 | // GetRawQuoteAtLevel returns byte format attestation plus certificate table via ConfigFS. 70 | func (*MacOSQuoteProvider) GetRawQuoteAtLevel(reportData [64]byte, level uint) ([]byte, error) { 71 | return nil, fmt.Errorf("MacOS is unsupported") 72 | } 73 | 74 | // GetQuoteProvider returns a supported SEV-SNP QuoteProvider. 75 | func GetQuoteProvider() (QuoteProvider, error) { 76 | return nil, fmt.Errorf("MacOS is unsupported") 77 | } 78 | 79 | // GetLeveledQuoteProvider returns a supported SEV-SNP LeveledQuoteProvider. 80 | func GetLeveledQuoteProvider() (LeveledQuoteProvider, error) { 81 | return nil, fmt.Errorf("MacOS is unsupported") 82 | } 83 | -------------------------------------------------------------------------------- /client/client_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package client 16 | 17 | import ( 18 | "bytes" 19 | "crypto/x509" 20 | "flag" 21 | "fmt" 22 | "sync" 23 | "testing" 24 | "time" 25 | 26 | "github.com/google/go-cmp/cmp" 27 | "github.com/google/go-sev-guest/abi" 28 | labi "github.com/google/go-sev-guest/client/linuxabi" 29 | spb "github.com/google/go-sev-guest/proto/sevsnp" 30 | test "github.com/google/go-sev-guest/testing" 31 | "google.golang.org/protobuf/encoding/prototext" 32 | "google.golang.org/protobuf/testing/protocmp" 33 | ) 34 | 35 | var devMu sync.Once 36 | var device Device 37 | var qp QuoteProvider 38 | var tests []test.TestCase 39 | 40 | var guestPolicy = flag.Uint64("guest_policy", abi.SnpPolicyToBytes(abi.SnpPolicy{SMT: true}), 41 | "If --sev_guest_device_path is not 'default', this is the policy of the VM that is running this test") 42 | 43 | // Initializing a device with key generation is expensive. Just do it once for the test suite. 44 | func initDevice() { 45 | now := time.Date(2022, time.May, 3, 9, 0, 0, 0, time.UTC) 46 | for _, tc := range test.TestCases() { 47 | // Don't test faked errors when running real hardware tests. 48 | if !UseDefaultSevGuest() && tc.WantErr != "" { 49 | continue 50 | } 51 | tests = append(tests, tc) 52 | } 53 | ones32 := make([]byte, 32) 54 | for i := range ones32 { 55 | ones32[i] = 1 56 | } 57 | keys := map[string][]byte{ 58 | test.DerivedKeyRequestToString(&labi.SnpDerivedKeyReqABI{}): make([]byte, 32), 59 | test.DerivedKeyRequestToString(&labi.SnpDerivedKeyReqABI{GuestFieldSelect: 1}): ones32, 60 | } 61 | opts := &test.DeviceOptions{Keys: keys, Now: now} 62 | // Choose a mock device or a real device depending on the given flag. This is like testclient, 63 | // but without the circular dependency. 64 | if UseDefaultSevGuest() { 65 | sevTestDevice, err := test.TcDevice(tests, opts) 66 | if err != nil { 67 | panic(fmt.Sprintf("failed to create test device: %v", err)) 68 | } 69 | if err := sevTestDevice.Open("/dev/sev-guest"); err != nil { 70 | panic(err) 71 | } 72 | device = sevTestDevice 73 | qp = &test.QuoteProvider{Device: sevTestDevice} 74 | return 75 | } 76 | 77 | client, err := OpenDevice() 78 | if err != nil { // Unexpected 79 | panic(err) 80 | } 81 | device = client 82 | qp = &test.QuoteProvider{Device: device.(*test.Device)} 83 | } 84 | 85 | func cleanReport(report *spb.Report) { 86 | report.ReportId = make([]byte, abi.ReportIDSize) 87 | report.ReportIdMa = make([]byte, abi.ReportIDMASize) 88 | report.ChipId = make([]byte, abi.ChipIDSize) 89 | report.Measurement = make([]byte, abi.MeasurementSize) 90 | report.PlatformInfo = 0 91 | report.CommittedTcb = 0 92 | report.CommittedBuild = 0 93 | report.CommittedMinor = 0 94 | report.CommittedMajor = 0 95 | report.CurrentTcb = 0 96 | report.CurrentBuild = 0 97 | report.CurrentMinor = 0 98 | report.CurrentMajor = 0 99 | report.LaunchTcb = 0 100 | report.ReportedTcb = 0 101 | } 102 | 103 | func fixReportWants(report *spb.Report) { 104 | if !UseDefaultSevGuest() { 105 | // The GCE default policy isn't the same as for the mock tests. 106 | report.Policy = *guestPolicy 107 | } 108 | } 109 | 110 | func modifyReportBytes(raw []byte, process func(report *spb.Report)) error { 111 | report, err := abi.ReportToProto(raw) 112 | if err != nil { 113 | return err 114 | } 115 | process(report) 116 | result, err := abi.ReportToAbiBytes(report) 117 | if err != nil { 118 | return err 119 | } 120 | copy(raw, result) 121 | return nil 122 | } 123 | 124 | func cleanRawReport(raw []byte) error { 125 | return modifyReportBytes(raw, cleanReport) 126 | } 127 | 128 | func fixRawReportWants(raw []byte) error { 129 | return modifyReportBytes(raw, fixReportWants) 130 | } 131 | 132 | func TestOpenGetReportClose(t *testing.T) { 133 | devMu.Do(initDevice) 134 | for _, tc := range tests { 135 | t.Run(tc.Name, func(t *testing.T) { 136 | reportProto := &spb.Report{} 137 | if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil { 138 | t.Fatalf("test failure: %v", err) 139 | } 140 | fixReportWants(reportProto) 141 | 142 | // Does the proto report match expectations? 143 | attestation, err := GetQuoteProto(qp, tc.Input) 144 | if !test.Match(err, tc.WantErr) { 145 | t.Fatalf("GetReport(device, %v) = %v, %v. Want err: %v", tc.Input, attestation, err, tc.WantErr) 146 | } 147 | 148 | if tc.WantErr == "" { 149 | got := attestation.Report 150 | cleanReport(got) 151 | want := reportProto 152 | want.Signature = got.Signature // Zeros were placeholders. 153 | if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" { 154 | t.Errorf("GetReport(%v) expectation diff %s", tc.Input, diff) 155 | } 156 | } 157 | }) 158 | } 159 | } 160 | 161 | func TestOpenGetRawExtendedReportClose(t *testing.T) { 162 | devMu.Do(initDevice) 163 | for _, tc := range tests { 164 | t.Run(tc.Name, func(t *testing.T) { 165 | rawcerts, err := qp.GetRawQuote(tc.Input) 166 | if !test.Match(err, tc.WantErr) || (tc.WantErr == "" && len(rawcerts) < abi.ReportSize) { 167 | t.Fatalf("qp.GetRawQuote(%v) = %v, %v. Want err: %v", tc.Input, rawcerts, err, tc.WantErr) 168 | } 169 | if tc.WantErr == "" { 170 | raw := rawcerts[:abi.ReportSize] 171 | if err := cleanRawReport(raw); err != nil { 172 | t.Fatal(err) 173 | } 174 | got := abi.SignedComponent(raw) 175 | if err := fixRawReportWants(tc.Output[:]); err != nil { 176 | t.Fatal(err) 177 | } 178 | want := abi.SignedComponent(tc.Output[:]) 179 | if !bytes.Equal(got, want) { 180 | t.Errorf("qp.GetRawQuote(%v) = {data: %v, certs: _} want %v", tc.Input, got, want) 181 | } 182 | der, err := abi.ReportToSignatureDER(raw) 183 | if err != nil { 184 | t.Errorf("ReportToSignatureDER(%v) errored unexpectedly: %v", raw, err) 185 | } 186 | if UseDefaultSevGuest() { 187 | tcdev := device.(*test.Device) 188 | infoRaw, _ := abi.ReportSignerInfo(raw) 189 | info, _ := abi.ParseSignerInfo(infoRaw) 190 | reportSigner := tcdev.Signer.Vcek 191 | if info.SigningKey == abi.VlekReportSigner { 192 | reportSigner = tcdev.Signer.Vlek 193 | } 194 | if err := reportSigner.CheckSignature(x509.ECDSAWithSHA384, got, der); err != nil { 195 | t.Errorf("signature with test keys did not verify: %v", err) 196 | } 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func TestGetQuoteProto(t *testing.T) { 204 | devMu.Do(initDevice) 205 | for _, tc := range tests { 206 | t.Run(tc.Name, func(t *testing.T) { 207 | ereport, err := GetQuoteProto(qp, tc.Input) 208 | if !test.Match(err, tc.WantErr) { 209 | t.Fatalf("GetQuoteProto(qp, %v) = %v, %v. Want err: %v", tc.Input, ereport, err, tc.WantErr) 210 | } 211 | if tc.WantErr == "" { 212 | reportProto := &spb.Report{} 213 | if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil { 214 | t.Fatalf("test failure: %v", err) 215 | } 216 | fixReportWants(reportProto) 217 | 218 | got := ereport.Report 219 | cleanReport(got) 220 | want := reportProto 221 | want.Signature = got.Signature // Zeros were placeholders. 222 | if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" { 223 | t.Errorf("GetQuoteProto(qp, %v) = {data: %v, certs: _} want %v. Diff: %s", tc.Input, got, want, diff) 224 | } 225 | 226 | if UseDefaultSevGuest() { 227 | tcdev := device.(*test.Device) 228 | if !bytes.Equal(ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw) { 229 | t.Errorf("ARK certificate mismatch. Got %v, want %v", 230 | ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw) 231 | } 232 | if !bytes.Equal(ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw) { 233 | t.Errorf("ASK certificate mismatch. Got %v, want %v", 234 | ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw) 235 | } 236 | if !bytes.Equal(ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw) { 237 | t.Errorf("VCEK certificate mismatch. Got %v, want %v", 238 | ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw) 239 | } 240 | } 241 | } 242 | }) 243 | } 244 | } 245 | 246 | func TestGetDerivedKey(t *testing.T) { 247 | devMu.Do(initDevice) 248 | key1, err := GetDerivedKeyAcknowledgingItsLimitations(device, &SnpDerivedKeyReq{ 249 | UseVCEK: true, 250 | }) 251 | if err != nil { 252 | t.Fatalf("Could not get key1: %v", err) 253 | } 254 | key2, err := GetDerivedKeyAcknowledgingItsLimitations(device, &SnpDerivedKeyReq{ 255 | UseVCEK: true, 256 | GuestFieldSelect: GuestFieldSelect{ 257 | GuestPolicy: true, 258 | }, 259 | }) 260 | if err != nil { 261 | t.Fatalf("Could not get key2: %v", err) 262 | } 263 | key3, err := GetDerivedKeyAcknowledgingItsLimitations(device, &SnpDerivedKeyReq{ 264 | UseVCEK: true, 265 | }) 266 | if err != nil { 267 | t.Fatalf("Could not get key3: %v", err) 268 | } 269 | if bytes.Equal(key1.Data[:], key2.Data[:]) { 270 | t.Errorf("GetDerivedKey...(nothing) = %v = GetDerivedKey...(guestPolicy) = %v", key1.Data, key2.Data) 271 | } 272 | if !bytes.Equal(key1.Data[:], key3.Data[:]) { 273 | t.Errorf("GetDerivedKey...(nothing) = %v and %v. Expected equality", key1.Data, key3.Data) 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /client/client_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | 17 | package client 18 | 19 | import ( 20 | "fmt" 21 | 22 | spb "github.com/google/go-sev-guest/proto/sevsnp" 23 | ) 24 | 25 | // WindowsDevice implements the Device interface with Linux ioctls. 26 | type WindowsDevice struct{} 27 | 28 | // Open is not supported on Windows. 29 | func (*WindowsDevice) Open(_ string) error { 30 | return fmt.Errorf("Windows is unsupported") 31 | } 32 | 33 | // OpenDevice fails on Windows. 34 | func OpenDevice() (*WindowsDevice, error) { 35 | return nil, fmt.Errorf("Windows is unsupported") 36 | } 37 | 38 | // Close is not supported on Windows. 39 | func (*WindowsDevice) Close() error { 40 | return fmt.Errorf("Windows is unsupported") 41 | } 42 | 43 | // Ioctl is not supported on Windows. 44 | func (*WindowsDevice) Ioctl(_ uintptr, _ any) (uintptr, error) { 45 | // The GuestAttestation library on Windows is closed source. 46 | return 0, fmt.Errorf("Windows is unsupported") 47 | } 48 | 49 | // Product is not supported on Windows. 50 | func (*WindowsDevice) Product() *spb.SevProduct { 51 | return &spb.SevProduct{} 52 | } 53 | 54 | // WindowsQuoteProvider implements the QuoteProvider interface with Linux's configfs-tsm. 55 | type WindowsQuoteProvider struct{} 56 | 57 | // IsSupported checks if the quote provider is supported. 58 | func (*WindowsQuoteProvider) IsSupported() bool { 59 | return false 60 | } 61 | 62 | // GetRawQuote returns byte format attestation plus certificate table via ConfigFS. 63 | func (*WindowsQuoteProvider) GetRawQuote(reportData [64]byte) ([]byte, error) { 64 | return nil, fmt.Errorf("Windows is unsupported") 65 | } 66 | 67 | // GetRawQuoteAtLevel returns byte format attestation plus certificate table via ConfigFS. 68 | func (*WindowsQuoteProvider) GetRawQuoteAtLevel(reportData [64]byte, level uint) ([]byte, error) { 69 | return nil, fmt.Errorf("Windows is unsupported") 70 | } 71 | 72 | // GetQuoteProvider returns a supported SEV-SNP QuoteProvider. 73 | func GetQuoteProvider() (QuoteProvider, error) { 74 | return nil, fmt.Errorf("Windows is unsupported") 75 | } 76 | 77 | // GetLeveledQuoteProvider returns a supported SEV-SNP LeveledQuoteProvider. 78 | func GetLeveledQuoteProvider() (LeveledQuoteProvider, error) { 79 | return nil, fmt.Errorf("Windows is unsupported") 80 | } 81 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/google/go-sev-guest 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/golang/protobuf v1.5.0 7 | github.com/google/go-cmp v0.5.7 8 | github.com/google/go-configfs-tsm v0.2.2 9 | github.com/google/logger v1.1.1 10 | github.com/google/uuid v1.6.0 11 | go.uber.org/multierr v1.11.0 12 | golang.org/x/crypto v0.17.0 13 | golang.org/x/sys v0.15.0 14 | google.golang.org/protobuf v1.33.0 15 | ) 16 | 17 | require golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= 3 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 4 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 5 | github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= 6 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 7 | github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98= 8 | github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= 9 | github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= 10 | github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= 11 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 12 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 15 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 16 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 17 | golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= 18 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 19 | golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 20 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= 21 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 22 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 23 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 24 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 25 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 26 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 27 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 28 | -------------------------------------------------------------------------------- /kds/kds_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package kds 16 | 17 | import ( 18 | "encoding/hex" 19 | "fmt" 20 | "net/url" 21 | "strings" 22 | "testing" 23 | 24 | "github.com/google/go-cmp/cmp" 25 | "github.com/google/go-sev-guest/abi" 26 | pb "github.com/google/go-sev-guest/proto/sevsnp" 27 | "google.golang.org/protobuf/testing/protocmp" 28 | "google.golang.org/protobuf/types/known/wrapperspb" 29 | ) 30 | 31 | func TestProductCertChainURL(t *testing.T) { 32 | got := ProductCertChainURL(abi.VcekReportSigner, "Milan") 33 | want := "https://kdsintf.amd.com/vcek/v1/Milan/cert_chain" 34 | if got != want { 35 | t.Errorf("ProductCertChainURL(\"Milan\") = %q, want %q", got, want) 36 | } 37 | } 38 | 39 | func TestVCEKCertURL(t *testing.T) { 40 | hwid := make([]byte, abi.ChipIDSize) 41 | hwid[0] = 0xfe 42 | hwid[abi.ChipIDSize-1] = 0xc0 43 | got := VCEKCertURL("Milan", hwid, TCBVersion(0)) 44 | want := "https://kdsintf.amd.com/vcek/v1/Milan/fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0?blSPL=0&teeSPL=0&snpSPL=0&ucodeSPL=0" 45 | if got != want { 46 | t.Errorf("VCEKCertURL(\"Milan\", %v, 0) = %q, want %q", hwid, got, want) 47 | } 48 | } 49 | 50 | func TestParseProductBaseURL(t *testing.T) { 51 | tcs := []struct { 52 | name string 53 | url string 54 | wantProduct string 55 | wantURL *url.URL 56 | wantErr string 57 | }{ 58 | { 59 | name: "happy path", 60 | url: ProductCertChainURL(abi.VcekReportSigner, "Milan"), 61 | wantProduct: "Milan", 62 | wantURL: &url.URL{ 63 | Scheme: "https", 64 | Host: "kdsintf.amd.com", 65 | Path: "cert_chain", // The vcek/v1/Milan part is expected to be trimmed. 66 | }, 67 | }, 68 | { 69 | name: "bad host", 70 | url: "https://fakekds.com/vcek/v1/Milan/cert_chain", 71 | wantErr: "unexpected AMD KDS URL host \"fakekds.com\", want \"kdsintf.amd.com\"", 72 | }, 73 | { 74 | name: "bad scheme", 75 | url: "http://kdsintf.amd.com/vcek/v1/Milan/cert_chain", 76 | wantErr: "unexpected AMD KDS URL scheme \"http\", want \"https\"", 77 | }, 78 | { 79 | name: "bad path", 80 | url: "https://kdsintf.amd.com/vcek/v2/Milan/cert_chain", 81 | wantErr: "unexpected AMD KDS URL path \"/vcek/v2/Milan/cert_chain\", want prefix \"/vcek/v1/\"", 82 | }, 83 | } 84 | for _, tc := range tcs { 85 | t.Run(tc.name, func(t *testing.T) { 86 | parsed, err := parseBaseProductURL(tc.url) 87 | if (err == nil && tc.wantErr != "") || (err != nil && !strings.Contains(err.Error(), tc.wantErr)) { 88 | t.Fatalf("parseBaseProductURL(%q) = _, _, %v, want %q", tc.url, err, tc.wantErr) 89 | } 90 | if err == nil { 91 | if diff := cmp.Diff(parsed.simpleURL, tc.wantURL); diff != "" { 92 | t.Errorf("parseBaseProductURL(%q) returned unexpected diff (-want +got):\n%s", tc.url, diff) 93 | } 94 | if parsed.productLine != tc.wantProduct { 95 | t.Errorf("parseBaseProductURL(%q) = %q, _, _ want %q", tc.url, parsed.productLine, tc.wantProduct) 96 | } 97 | } 98 | }) 99 | } 100 | } 101 | 102 | func TestParseProductCertChainURL(t *testing.T) { 103 | tests := []struct { 104 | key abi.ReportSigner 105 | product string 106 | wantKey CertFunction 107 | }{ 108 | { 109 | key: abi.VcekReportSigner, 110 | product: "Milan", 111 | wantKey: VcekCertFunction, 112 | }, 113 | { 114 | key: abi.VlekReportSigner, 115 | product: "Milan", 116 | wantKey: VlekCertFunction, 117 | }, 118 | } 119 | for _, tc := range tests { 120 | url := ProductCertChainURL(tc.key, tc.product) 121 | got, key, err := ParseProductCertChainURL(url) 122 | if err != nil { 123 | t.Fatalf("ParseProductCertChainURL(%q) = _, _, %v, want nil", tc.product, err) 124 | } 125 | if got != tc.product || key != tc.wantKey { 126 | t.Errorf("ProductCertChainURL(%q) = %q, %v, nil want %q, %v", url, got, key, tc.product, tc.wantKey) 127 | } 128 | } 129 | } 130 | 131 | func TestParseVCEKCertURL(t *testing.T) { 132 | hwid := make([]byte, abi.ChipIDSize) 133 | hwidhex := hex.EncodeToString(hwid) 134 | tcs := []struct { 135 | name string 136 | url string 137 | want VCEKCert 138 | wantErr string 139 | }{ 140 | { 141 | name: "happy path", 142 | url: VCEKCertURL("Milan", hwid, TCBVersion(0)), 143 | want: func() VCEKCert { 144 | c := VCEKCertProduct("Milan") 145 | c.HWID = hwid 146 | c.TCB = 0 147 | return c 148 | }(), 149 | }, 150 | { 151 | name: "bad query format", 152 | url: fmt.Sprintf("https://kdsintf.amd.com/vcek/v1/Milan/%s?ha;ha", hwidhex), 153 | wantErr: "invalid AMD KDS URL query \"ha;ha\": invalid semicolon separator in query", 154 | }, 155 | { 156 | name: "bad query key", 157 | url: fmt.Sprintf("https://kdsintf.amd.com/vcek/v1/Milan/%s?fakespl=4", hwidhex), 158 | wantErr: "unexpected KDS TCB version URL argument \"fakespl\"", 159 | }, 160 | { 161 | name: "bad query argument numerical", 162 | url: fmt.Sprintf("https://kdsintf.amd.com/vcek/v1/Milan/%s?blSPL=-4", hwidhex), 163 | wantErr: "invalid KDS TCB version URL argument value \"-4\", want a value 0-255", 164 | }, 165 | { 166 | name: "bad query argument numerical", 167 | url: fmt.Sprintf("https://kdsintf.amd.com/vcek/v1/Milan/%s?blSPL=alpha", hwidhex), 168 | wantErr: "invalid KDS TCB version URL argument value \"alpha\", want a value 0-255", 169 | }, 170 | } 171 | for _, tc := range tcs { 172 | t.Run(tc.name, func(t *testing.T) { 173 | got, err := ParseVCEKCertURL(tc.url) 174 | if (err == nil && tc.wantErr != "") || (err != nil && !strings.Contains(err.Error(), tc.wantErr)) { 175 | t.Fatalf("ParseVCEKCertURL(%q) = _, %v, want %q", tc.url, err, tc.wantErr) 176 | } 177 | if err == nil { 178 | if diff := cmp.Diff(got, tc.want); diff != "" { 179 | t.Errorf("ParseVCEKCertURL(%q) returned unexpected diff (-want +got):\n%s", tc.url, diff) 180 | } 181 | } 182 | }) 183 | } 184 | } 185 | 186 | func TestProductName(t *testing.T) { 187 | tcs := []struct { 188 | name string 189 | input *pb.SevProduct 190 | want string 191 | }{ 192 | { 193 | name: "nil", 194 | want: "Milan-B1", 195 | }, 196 | { 197 | name: "unknown", 198 | input: &pb.SevProduct{ 199 | MachineStepping: &wrapperspb.UInt32Value{Value: 0x1A}, 200 | }, 201 | want: "badstepping", 202 | }, 203 | { 204 | name: "Milan-B0", 205 | input: &pb.SevProduct{ 206 | Name: pb.SevProduct_SEV_PRODUCT_MILAN, 207 | }, 208 | want: "UnknownStepping", 209 | }, 210 | { 211 | name: "Milan-B0", 212 | input: &pb.SevProduct{ 213 | Name: pb.SevProduct_SEV_PRODUCT_MILAN, 214 | MachineStepping: &wrapperspb.UInt32Value{Value: 0}, 215 | }, 216 | want: "Milan-B0", 217 | }, 218 | { 219 | name: "Genoa-FF", 220 | input: &pb.SevProduct{ 221 | Name: pb.SevProduct_SEV_PRODUCT_GENOA, 222 | MachineStepping: &wrapperspb.UInt32Value{Value: 0xff}, 223 | }, 224 | want: "badstepping", 225 | }, 226 | { 227 | name: "unknown milan stepping", 228 | input: &pb.SevProduct{ 229 | Name: pb.SevProduct_SEV_PRODUCT_MILAN, 230 | MachineStepping: &wrapperspb.UInt32Value{Value: 15}, 231 | }, 232 | want: "unmappedMilanStepping", 233 | }, 234 | { 235 | name: "unknown genoa stepping", 236 | input: &pb.SevProduct{ 237 | Name: pb.SevProduct_SEV_PRODUCT_GENOA, 238 | MachineStepping: &wrapperspb.UInt32Value{Value: 15}, 239 | }, 240 | want: "unmappedGenoaStepping", 241 | }, 242 | { 243 | name: "unknown", 244 | input: &pb.SevProduct{ 245 | Name: pb.SevProduct_SEV_PRODUCT_UNKNOWN, 246 | MachineStepping: &wrapperspb.UInt32Value{Value: 15}, 247 | }, 248 | want: "Unknown", 249 | }, 250 | } 251 | for _, tc := range tcs { 252 | t.Run(tc.name, func(t *testing.T) { 253 | if got := ProductName(tc.input); got != tc.want { 254 | t.Errorf("ProductName(%v) = %q, want %q", tc.input, got, tc.want) 255 | } 256 | }) 257 | } 258 | } 259 | 260 | func TestParseProductName(t *testing.T) { 261 | tcs := []struct { 262 | name string 263 | input string 264 | key abi.ReportSigner 265 | want *pb.SevProduct 266 | wantErr string 267 | }{ 268 | { 269 | name: "empty", 270 | wantErr: "unknown product name", 271 | }, 272 | { 273 | name: "Too big", 274 | input: "Milan-100", 275 | wantErr: "unknown product name", 276 | }, 277 | { 278 | name: "happy path Genoa", 279 | input: "Genoa-B1", 280 | want: &pb.SevProduct{ 281 | Name: pb.SevProduct_SEV_PRODUCT_GENOA, 282 | MachineStepping: &wrapperspb.UInt32Value{Value: 1}, 283 | }, 284 | }, 285 | { 286 | name: "bad revision Milan", 287 | input: "Milan-A1", 288 | wantErr: "unknown product name", 289 | }, 290 | { 291 | name: "vlek products have no stepping", 292 | input: "Genoa", 293 | key: abi.VlekReportSigner, 294 | want: &pb.SevProduct{ 295 | Name: pb.SevProduct_SEV_PRODUCT_GENOA, 296 | }, 297 | }, 298 | { 299 | name: "Unhandled report signer", 300 | input: "ignored", 301 | key: abi.NoneReportSigner, 302 | wantErr: "internal: unhandled reportSigner", 303 | }, 304 | } 305 | for _, tc := range tcs { 306 | t.Run(tc.name, func(t *testing.T) { 307 | got, err := ParseProductName(tc.input, tc.key) 308 | if (err == nil && tc.wantErr != "") || (err != nil && (tc.wantErr == "" || !strings.Contains(err.Error(), tc.wantErr))) { 309 | t.Fatalf("ParseProductName(%v) errored unexpectedly: %v, want %q", tc.input, err, tc.wantErr) 310 | } 311 | if tc.wantErr == "" { 312 | if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" { 313 | t.Fatalf("ParseProductName(%v) = %v, want %v\nDiff: %s", tc.input, got, tc.want, diff) 314 | } 315 | } 316 | }) 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /proto/check.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | // Package check represents an attestation validation policy. 18 | package check; 19 | 20 | import "google/protobuf/wrappers.proto"; 21 | import "sevsnp.proto"; 22 | 23 | option go_package = "github.com/google/go-sev-guest/proto/check"; 24 | 25 | // Policy is a representation of an attestation report validation policy. 26 | // Each field corresponds to a field on validate.Options. This format 27 | // is useful for providing programmatic inputs to the `check` CLI tool. 28 | message Policy { 29 | uint32 minimum_guest_svn = 1; 30 | // The component-wise maximum permissible guest policy, except 31 | // API version values, and SingleSocket are the minimum permissible. 32 | uint64 policy = 2; 33 | bytes family_id = 3; // Should be 16 bytes long 34 | bytes image_id = 4; // Should be 16 bytes long 35 | google.protobuf.UInt32Value vmpl = 5; 36 | uint64 minimum_tcb = 6; 37 | uint64 minimum_launch_tcb = 7; 38 | google.protobuf.UInt64Value platform_info = 8; 39 | bool require_author_key = 9; 40 | bytes report_data = 10; // Should be 64 bytes long 41 | bytes measurement = 11; // Should be 48 bytes long 42 | bytes host_data = 12; // Should be 32 bytes long 43 | bytes report_id = 13; // Should be 32 bytes long 44 | bytes report_id_ma = 14; // Should be 32 bytes long 45 | bytes chip_id = 15; // Should be 64 bytes long 46 | uint32 minimum_build = 16; 47 | string minimum_version = 17; // Should be "maj.min", both should be 0-255. 48 | bool permit_provisional_firmware = 18; 49 | bool require_id_block = 19; 50 | repeated bytes trusted_author_keys = 20; 51 | repeated bytes trusted_author_key_hashes = 21; 52 | repeated bytes trusted_id_keys = 22; 53 | repeated bytes trusted_id_key_hashes = 23; 54 | // The expected product that generated the attestation report. Stepping optional. 55 | sevsnp.SevProduct product = 24; 56 | } 57 | 58 | // RootOfTrust represents configuration for which hardware root of trust 59 | // certificates to use for verifying attestation report signatures. 60 | message RootOfTrust { 61 | // The expected AMD product the attestation was collected from. Default 62 | // "Milan". 63 | string product = 1 [deprecated = true]; 64 | 65 | // Paths to CA bundles for the AMD product. 66 | // Must be in PEM format, AS[V]K, then ARK certificates. 67 | // This is for verifing a report's signature, as opposed to validating trust 68 | // in the report's ID key or author key. 69 | // If empty, uses the verification library's embedded certificates from AMD. 70 | repeated string cabundle_paths = 2; 71 | 72 | // PEM format CA bundles for the AMD product. Combined with contents of cabundle_paths. 73 | repeated string cabundles = 3; 74 | 75 | // If true, download and check the CRL for revoked certificates. 76 | bool check_crl = 4; 77 | 78 | // If true, then check is not permitted to download necessary files for verification. 79 | bool disallow_network = 5; 80 | 81 | // The expected AMD product line the attestation was collected from. Default "Milan". 82 | string product_line = 6; 83 | } 84 | 85 | // Config is the overall message input for the check tool. This provides all 86 | // the flags that configure the tool, including the validation policy. 87 | message Config { 88 | // Configures which hardware keys to trust. Default uses library-embedded 89 | // certificates. 90 | RootOfTrust root_of_trust = 1; 91 | 92 | // The report validation policy. 93 | Policy policy = 2; 94 | } 95 | -------------------------------------------------------------------------------- /proto/check/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package check defines the message type for the check CLI tool's options. 16 | package check 17 | -------------------------------------------------------------------------------- /proto/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package proto contains protocol buffers that are exchanged between the client 16 | // and server, as well as convenience configuration definitions for tools. 17 | // 18 | // # Generating Protocol Buffer Code 19 | // 20 | // Anytime the Protocol Buffer definitions change, the generated Go code must be 21 | // regenerated. This can be done with "go generate". Just run: 22 | // 23 | // go generate ./... 24 | // 25 | // Upstream documentation: 26 | // https://developers.google.com/protocol-buffers/docs/reference/go-generated 27 | // 28 | // # Code Generation Dependencies 29 | // 30 | // To generate the Go code, your system must have "protoc" installed. See: 31 | // https://github.com/protocolbuffers/protobuf#protocol-compiler-installation 32 | // 33 | // The "protoc-gen-go" tool must also be installed. To install it, run: 34 | // 35 | // go install google.golang.org/protobuf/cmd/protoc-gen-go 36 | // 37 | // If you see a 'protoc-gen-go: program not found or is not executable' error 38 | // for the 'go generate' command, run the following: 39 | // 40 | // echo 'export PATH=$PATH:$GOPATH/bin' >> $HOME/.bashrc 41 | // source $HOME/.bashrc 42 | // 43 | // If you see 'google/protobuf/wrappers.proto not found', then you need to 44 | // similarly set your PROTOC_INSTALL_DIR environment variable to the protoc 45 | // installation directory which should have the "well-known types" in the 46 | // include subdirectory. 47 | package proto 48 | 49 | //go:generate protoc -I$PROTOC_INSTALL_DIR/include -I=. --go_out=. --go_opt=module=github.com/google/go-sev-guest/proto check.proto 50 | //go:generate protoc --go_out=. --go_opt=module=github.com/google/go-sev-guest/proto fakekds.proto 51 | //go:generate protoc --go_out=. --go_opt=module=github.com/google/go-sev-guest/proto sevsnp.proto 52 | -------------------------------------------------------------------------------- /proto/fakekds.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package fakekds; 18 | 19 | option go_package = "github.com/google/go-sev-guest/proto/fakekds"; 20 | 21 | // Certificates represents all known certificates for machines at particular 22 | // TCB values. This is useful to represent a test machine cluster's VCEK 23 | // certificates that haven't been provisioned with the /dev/sev device. 24 | message Certificates { 25 | message ChipTCBCerts { 26 | bytes chip_id = 1; // Should be 64 bytes 27 | map tcb_certs = 2; 28 | string hostname = 3; 29 | uint32 fms = 4; 30 | } 31 | repeated ChipTCBCerts chip_certs = 1; 32 | } 33 | -------------------------------------------------------------------------------- /proto/fakekds/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package fakekds defines the message types for representing a local cache of 16 | // KDS certificates for a small set of test machines. This is useful for creating 17 | // a reliable testing environment that does not depend on unbroken service from 18 | // the AMD KDS. 19 | package fakekds 20 | -------------------------------------------------------------------------------- /proto/fakekds/fakekds.pb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Code generated by protoc-gen-go. DO NOT EDIT. 16 | // versions: 17 | // protoc-gen-go v1.31.0 18 | // protoc v5.27.2 19 | // source: fakekds.proto 20 | 21 | package fakekds 22 | 23 | import ( 24 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 25 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 26 | reflect "reflect" 27 | sync "sync" 28 | ) 29 | 30 | const ( 31 | // Verify that this generated code is sufficiently up-to-date. 32 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 33 | // Verify that runtime/protoimpl is sufficiently up-to-date. 34 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 35 | ) 36 | 37 | // Certificates represents all known certificates for machines at particular 38 | // TCB values. This is useful to represent a test machine cluster's VCEK 39 | // certificates that haven't been provisioned with the /dev/sev device. 40 | type Certificates struct { 41 | state protoimpl.MessageState 42 | sizeCache protoimpl.SizeCache 43 | unknownFields protoimpl.UnknownFields 44 | 45 | ChipCerts []*Certificates_ChipTCBCerts `protobuf:"bytes,1,rep,name=chip_certs,json=chipCerts,proto3" json:"chip_certs,omitempty"` 46 | } 47 | 48 | func (x *Certificates) Reset() { 49 | *x = Certificates{} 50 | if protoimpl.UnsafeEnabled { 51 | mi := &file_fakekds_proto_msgTypes[0] 52 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 53 | ms.StoreMessageInfo(mi) 54 | } 55 | } 56 | 57 | func (x *Certificates) String() string { 58 | return protoimpl.X.MessageStringOf(x) 59 | } 60 | 61 | func (*Certificates) ProtoMessage() {} 62 | 63 | func (x *Certificates) ProtoReflect() protoreflect.Message { 64 | mi := &file_fakekds_proto_msgTypes[0] 65 | if protoimpl.UnsafeEnabled && x != nil { 66 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 67 | if ms.LoadMessageInfo() == nil { 68 | ms.StoreMessageInfo(mi) 69 | } 70 | return ms 71 | } 72 | return mi.MessageOf(x) 73 | } 74 | 75 | // Deprecated: Use Certificates.ProtoReflect.Descriptor instead. 76 | func (*Certificates) Descriptor() ([]byte, []int) { 77 | return file_fakekds_proto_rawDescGZIP(), []int{0} 78 | } 79 | 80 | func (x *Certificates) GetChipCerts() []*Certificates_ChipTCBCerts { 81 | if x != nil { 82 | return x.ChipCerts 83 | } 84 | return nil 85 | } 86 | 87 | type Certificates_ChipTCBCerts struct { 88 | state protoimpl.MessageState 89 | sizeCache protoimpl.SizeCache 90 | unknownFields protoimpl.UnknownFields 91 | 92 | ChipId []byte `protobuf:"bytes,1,opt,name=chip_id,json=chipId,proto3" json:"chip_id,omitempty"` // Should be 64 bytes 93 | TcbCerts map[uint64][]byte `protobuf:"bytes,2,rep,name=tcb_certs,json=tcbCerts,proto3" json:"tcb_certs,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` 94 | Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` 95 | Fms uint32 `protobuf:"varint,4,opt,name=fms,proto3" json:"fms,omitempty"` 96 | } 97 | 98 | func (x *Certificates_ChipTCBCerts) Reset() { 99 | *x = Certificates_ChipTCBCerts{} 100 | if protoimpl.UnsafeEnabled { 101 | mi := &file_fakekds_proto_msgTypes[1] 102 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 103 | ms.StoreMessageInfo(mi) 104 | } 105 | } 106 | 107 | func (x *Certificates_ChipTCBCerts) String() string { 108 | return protoimpl.X.MessageStringOf(x) 109 | } 110 | 111 | func (*Certificates_ChipTCBCerts) ProtoMessage() {} 112 | 113 | func (x *Certificates_ChipTCBCerts) ProtoReflect() protoreflect.Message { 114 | mi := &file_fakekds_proto_msgTypes[1] 115 | if protoimpl.UnsafeEnabled && x != nil { 116 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 117 | if ms.LoadMessageInfo() == nil { 118 | ms.StoreMessageInfo(mi) 119 | } 120 | return ms 121 | } 122 | return mi.MessageOf(x) 123 | } 124 | 125 | // Deprecated: Use Certificates_ChipTCBCerts.ProtoReflect.Descriptor instead. 126 | func (*Certificates_ChipTCBCerts) Descriptor() ([]byte, []int) { 127 | return file_fakekds_proto_rawDescGZIP(), []int{0, 0} 128 | } 129 | 130 | func (x *Certificates_ChipTCBCerts) GetChipId() []byte { 131 | if x != nil { 132 | return x.ChipId 133 | } 134 | return nil 135 | } 136 | 137 | func (x *Certificates_ChipTCBCerts) GetTcbCerts() map[uint64][]byte { 138 | if x != nil { 139 | return x.TcbCerts 140 | } 141 | return nil 142 | } 143 | 144 | func (x *Certificates_ChipTCBCerts) GetHostname() string { 145 | if x != nil { 146 | return x.Hostname 147 | } 148 | return "" 149 | } 150 | 151 | func (x *Certificates_ChipTCBCerts) GetFms() uint32 { 152 | if x != nil { 153 | return x.Fms 154 | } 155 | return 0 156 | } 157 | 158 | var File_fakekds_proto protoreflect.FileDescriptor 159 | 160 | var file_fakekds_proto_rawDesc = []byte{ 161 | 0x0a, 0x0d, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 162 | 0x07, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 0x22, 0xb5, 0x02, 0x0a, 0x0c, 0x43, 0x65, 0x72, 163 | 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x63, 0x68, 0x69, 164 | 0x70, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 165 | 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 166 | 0x61, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x68, 0x69, 0x70, 0x54, 0x43, 0x42, 0x43, 0x65, 0x72, 0x74, 167 | 0x73, 0x52, 0x09, 0x63, 0x68, 0x69, 0x70, 0x43, 0x65, 0x72, 0x74, 0x73, 0x1a, 0xe1, 0x01, 0x0a, 168 | 0x0c, 0x43, 0x68, 0x69, 0x70, 0x54, 0x43, 0x42, 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x17, 0x0a, 169 | 0x07, 0x63, 0x68, 0x69, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 170 | 0x63, 0x68, 0x69, 0x70, 0x49, 0x64, 0x12, 0x4d, 0x0a, 0x09, 0x74, 0x63, 0x62, 0x5f, 0x63, 0x65, 171 | 0x72, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x66, 0x61, 0x6b, 0x65, 172 | 0x6b, 0x64, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 173 | 0x2e, 0x43, 0x68, 0x69, 0x70, 0x54, 0x43, 0x42, 0x43, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x54, 0x63, 174 | 0x62, 0x43, 0x65, 0x72, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x74, 0x63, 0x62, 175 | 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 176 | 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 177 | 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 178 | 0x66, 0x6d, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x54, 0x63, 0x62, 0x43, 0x65, 0x72, 0x74, 0x73, 0x45, 179 | 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 180 | 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 181 | 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 182 | 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 183 | 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x67, 0x6f, 0x2d, 0x73, 0x65, 0x76, 0x2d, 0x67, 0x75, 0x65, 184 | 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 185 | 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 186 | } 187 | 188 | var ( 189 | file_fakekds_proto_rawDescOnce sync.Once 190 | file_fakekds_proto_rawDescData = file_fakekds_proto_rawDesc 191 | ) 192 | 193 | func file_fakekds_proto_rawDescGZIP() []byte { 194 | file_fakekds_proto_rawDescOnce.Do(func() { 195 | file_fakekds_proto_rawDescData = protoimpl.X.CompressGZIP(file_fakekds_proto_rawDescData) 196 | }) 197 | return file_fakekds_proto_rawDescData 198 | } 199 | 200 | var file_fakekds_proto_msgTypes = make([]protoimpl.MessageInfo, 3) 201 | var file_fakekds_proto_goTypes = []interface{}{ 202 | (*Certificates)(nil), // 0: fakekds.Certificates 203 | (*Certificates_ChipTCBCerts)(nil), // 1: fakekds.Certificates.ChipTCBCerts 204 | nil, // 2: fakekds.Certificates.ChipTCBCerts.TcbCertsEntry 205 | } 206 | var file_fakekds_proto_depIdxs = []int32{ 207 | 1, // 0: fakekds.Certificates.chip_certs:type_name -> fakekds.Certificates.ChipTCBCerts 208 | 2, // 1: fakekds.Certificates.ChipTCBCerts.tcb_certs:type_name -> fakekds.Certificates.ChipTCBCerts.TcbCertsEntry 209 | 2, // [2:2] is the sub-list for method output_type 210 | 2, // [2:2] is the sub-list for method input_type 211 | 2, // [2:2] is the sub-list for extension type_name 212 | 2, // [2:2] is the sub-list for extension extendee 213 | 0, // [0:2] is the sub-list for field type_name 214 | } 215 | 216 | func init() { file_fakekds_proto_init() } 217 | func file_fakekds_proto_init() { 218 | if File_fakekds_proto != nil { 219 | return 220 | } 221 | if !protoimpl.UnsafeEnabled { 222 | file_fakekds_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 223 | switch v := v.(*Certificates); i { 224 | case 0: 225 | return &v.state 226 | case 1: 227 | return &v.sizeCache 228 | case 2: 229 | return &v.unknownFields 230 | default: 231 | return nil 232 | } 233 | } 234 | file_fakekds_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 235 | switch v := v.(*Certificates_ChipTCBCerts); i { 236 | case 0: 237 | return &v.state 238 | case 1: 239 | return &v.sizeCache 240 | case 2: 241 | return &v.unknownFields 242 | default: 243 | return nil 244 | } 245 | } 246 | } 247 | type x struct{} 248 | out := protoimpl.TypeBuilder{ 249 | File: protoimpl.DescBuilder{ 250 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 251 | RawDescriptor: file_fakekds_proto_rawDesc, 252 | NumEnums: 0, 253 | NumMessages: 3, 254 | NumExtensions: 0, 255 | NumServices: 0, 256 | }, 257 | GoTypes: file_fakekds_proto_goTypes, 258 | DependencyIndexes: file_fakekds_proto_depIdxs, 259 | MessageInfos: file_fakekds_proto_msgTypes, 260 | }.Build() 261 | File_fakekds_proto = out.File 262 | file_fakekds_proto_rawDesc = nil 263 | file_fakekds_proto_goTypes = nil 264 | file_fakekds_proto_depIdxs = nil 265 | } 266 | -------------------------------------------------------------------------------- /proto/sevsnp.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | import "google/protobuf/wrappers.proto"; 18 | 19 | // Package sevsnp represents an SEV-SNP attestation report and its certificate 20 | // chain. 21 | package sevsnp; 22 | 23 | option go_package = "github.com/google/go-sev-guest/proto/sevsnp"; 24 | 25 | // Report represents an SEV-SNP ATTESTATION_REPORT, specified in SEV SNP API 26 | // documentation https://www.amd.com/system/files/TechDocs/56860.pdf 27 | message Report { 28 | uint32 version = 1; // Should be 2 for revision 1.55, and 3 for revision 1.56 29 | uint32 guest_svn = 2; 30 | uint64 policy = 3; 31 | bytes family_id = 4; // Should be 16 bytes long 32 | bytes image_id = 5; // Should be 16 bytes long 33 | uint32 vmpl = 6; 34 | uint32 signature_algo = 7; 35 | uint64 current_tcb = 8; 36 | uint64 platform_info = 9; 37 | uint32 signer_info = 10; // AuthorKeyEn, MaskChipKey, SigningKey 38 | bytes report_data = 11; // Should be 64 bytes long 39 | bytes measurement = 12; // Should be 48 bytes long 40 | bytes host_data = 13; // Should be 32 bytes long 41 | bytes id_key_digest = 14; // Should be 48 bytes long 42 | bytes author_key_digest = 15; // Should be 48 bytes long 43 | bytes report_id = 16; // Should be 32 bytes long 44 | bytes report_id_ma = 17; // Should be 32 bytes long 45 | uint64 reported_tcb = 18; 46 | bytes chip_id = 19; // Should be 64 bytes long 47 | uint64 committed_tcb = 20; 48 | // Each build, minor, major triple should be packed together in a uint32 49 | // packed together at 7:0, 15:8, 23:16 respectively 50 | uint32 current_build = 21; 51 | uint32 current_minor = 22; 52 | uint32 current_major = 23; 53 | uint32 committed_build = 24; 54 | uint32 committed_minor = 25; 55 | uint32 committed_major = 26; 56 | uint64 launch_tcb = 27; 57 | bytes signature = 28; // Should be 512 bytes long 58 | 59 | uint32 cpuid1eax_fms = 29; // The cpuid(1).eax & 0x0fff0fff representation of family/model/stepping 60 | } 61 | 62 | message CertificateChain { 63 | // The versioned chip endorsement key's certificate for the 64 | // key that signed this report. 65 | bytes vcek_cert = 1; 66 | 67 | // The versioned loaded endorsement key's certificate for the 68 | // key that signed this report. 69 | bytes vlek_cert = 6; 70 | 71 | // The AMD SEV or AMD SEV-VLEK certificate that signed the V?EK cert. 72 | bytes ask_cert = 2; 73 | 74 | // The AMD Root key certificate (signs the ASK cert). 75 | bytes ark_cert = 3; 76 | 77 | // A certificate the host may inject to endorse the measurement of the 78 | // firmware. 79 | bytes firmware_cert = 4 [deprecated = true]; 80 | 81 | // Non-standard certificates the host may inject. 82 | map extras = 7; 83 | } 84 | 85 | // The CPUID[EAX=1] version information includes product info as described in 86 | // the AMD KDS specification. The product name, model, and stepping values are 87 | // important for determining the required parameters to KDS when requesting the 88 | // endorsement key's certificate. 89 | message SevProduct { 90 | enum SevProductName { 91 | SEV_PRODUCT_UNKNOWN = 0; 92 | SEV_PRODUCT_MILAN = 1; 93 | SEV_PRODUCT_GENOA = 2; 94 | SEV_PRODUCT_TURIN = 3; 95 | } 96 | 97 | SevProductName name = 1; 98 | uint32 stepping = 2 [deprecated = true]; // Must be a 4-bit number 99 | google.protobuf.UInt32Value machine_stepping = 3; 100 | } 101 | 102 | message Attestation { 103 | Report report = 1; 104 | 105 | CertificateChain certificate_chain = 2; 106 | 107 | SevProduct product = 3; 108 | } 109 | -------------------------------------------------------------------------------- /proto/sevsnp/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package sevsnp implements a protocol buffer for representing SEV-SNP attestations. 16 | package sevsnp 17 | -------------------------------------------------------------------------------- /testing/client/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package client (in testing) allows tests to get a fake or real sev-guest device. 16 | package client 17 | 18 | import ( 19 | "testing" 20 | 21 | "github.com/google/go-sev-guest/abi" 22 | "github.com/google/go-sev-guest/client" 23 | test "github.com/google/go-sev-guest/testing" 24 | "github.com/google/go-sev-guest/verify/trust" 25 | "google.golang.org/protobuf/proto" 26 | ) 27 | 28 | // SkipUnmockableTestCase returns whether we have to skip a mocked failure test case on real hardware. 29 | func SkipUnmockableTestCase(tc *test.TestCase) bool { 30 | return !client.UseDefaultSevGuest() && tc.FwErr != 0 31 | } 32 | 33 | // GetSevGuest is a cross-platform testing helper function that retrieves the 34 | // appropriate SEV-guest device from the flags passed into "go test". 35 | // 36 | // If using a test guest device, this will also produce a fake AMD-SP that produces the signed 37 | // versions of given attestation reports based on different nonce input. Its returned roots of trust 38 | // are based on the fake's signing credentials. 39 | func GetSevGuest(tcs []test.TestCase, opts *test.DeviceOptions, tb testing.TB) (client.Device, map[string][]*trust.AMDRootCerts, map[string][]*trust.AMDRootCerts, trust.HTTPSGetter) { 40 | tb.Helper() 41 | if client.UseDefaultSevGuest() { 42 | sevTestDevice, err := test.TcDevice(tcs, opts) 43 | if err != nil { 44 | tb.Fatalf("failed to create test device: %v", err) 45 | } 46 | goodSnpRoot := map[string][]*trust.AMDRootCerts{ 47 | "Milan": { 48 | { 49 | Product: "Milan", // TODO(Issue#114): Remove 50 | ProductLine: "Milan", 51 | ProductCerts: &trust.ProductCerts{ 52 | Ask: sevTestDevice.Signer.Ask, 53 | Ark: sevTestDevice.Signer.Ark, 54 | Asvk: sevTestDevice.Signer.Asvk, 55 | }, 56 | }, 57 | }, 58 | } 59 | badSnpRoot := map[string][]*trust.AMDRootCerts{ 60 | "Milan": { 61 | { 62 | Product: "Milan", // TODO(Issue#114): Remove 63 | ProductLine: "Milan", 64 | ProductCerts: &trust.ProductCerts{ 65 | // No ASK, oops. 66 | Ask: sevTestDevice.Signer.Ark, 67 | Ark: sevTestDevice.Signer.Ark, 68 | Asvk: sevTestDevice.Signer.Ark, 69 | }, 70 | }, 71 | }, 72 | } 73 | fakekds, err := test.FakeKDSFromSigner(sevTestDevice.Signer) 74 | if err != nil { 75 | tb.Fatalf("failed to create fake KDS from signer: %v", err) 76 | } 77 | return sevTestDevice, goodSnpRoot, badSnpRoot, fakekds 78 | } 79 | 80 | client, err := client.OpenDevice() 81 | if err != nil { 82 | tb.Fatalf("Failed to open SEV guest device: %v", err) 83 | } 84 | kdsImpl := test.GetKDS(tb) 85 | 86 | badSnpRoot := make(map[string][]*trust.AMDRootCerts) 87 | for productLine, rootCerts := range trust.DefaultRootCerts { 88 | // Supplement the defaults with the missing x509 certificates. 89 | pc, err := trust.GetProductChain(productLine, abi.VcekReportSigner, kdsImpl) 90 | if err != nil { 91 | tb.Fatalf("failed to get product chain for %q: %v", productLine, err) 92 | } 93 | // By removing the ASK intermediate, we ensure that the attestation will never verify. 94 | badSnpRoot[productLine] = []*trust.AMDRootCerts{{ 95 | Product: productLine, // TODO(Issue#114): Remove 96 | ProductLine: productLine, 97 | ProductCerts: &trust.ProductCerts{ 98 | Ark: pc.Ark, 99 | Ask: pc.Ark, 100 | Asvk: pc.Ark, 101 | }, 102 | AskSev: rootCerts.ArkSev, 103 | ArkSev: rootCerts.AskSev, 104 | }} 105 | } 106 | return client, nil, badSnpRoot, kdsImpl 107 | } 108 | 109 | // GetSevQuoteProvider is a cross-platform testing helper function that retrieves the 110 | // appropriate SEV-guest device from the flags passed into "go test". 111 | // 112 | // If using a test guest device, this will also produce a fake AMD-SP that produces the signed 113 | // versions of given attestation reports based on different nonce input. Its returned roots of trust 114 | // are based on the fake's signing credentials. 115 | func GetSevQuoteProvider(tcs []test.TestCase, opts *test.DeviceOptions, tb testing.TB) (client.QuoteProvider, map[string][]*trust.AMDRootCerts, map[string][]*trust.AMDRootCerts, trust.HTTPSGetter) { 116 | tb.Helper() 117 | if client.UseDefaultSevGuest() { 118 | sevQp, err := test.TcQuoteProvider(tcs, opts) 119 | if err != nil { 120 | tb.Fatalf("failed to create test device: %v", err) 121 | } 122 | goodSnpRoot := map[string][]*trust.AMDRootCerts{ 123 | "Milan": { 124 | { 125 | Product: "Milan", // TODO(Issue#114): Remove 126 | ProductLine: "Milan", 127 | ProductCerts: &trust.ProductCerts{ 128 | Ask: sevQp.Device.Signer.Ask, 129 | Ark: sevQp.Device.Signer.Ark, 130 | Asvk: sevQp.Device.Signer.Asvk, 131 | }, 132 | }, 133 | }, 134 | "Genoa": { 135 | { 136 | Product: "Genoa", // TODO(Issue#114): Remove 137 | ProductLine: "Genoa", 138 | ProductCerts: &trust.ProductCerts{ 139 | Ask: sevQp.Device.Signer.Ask, 140 | Ark: sevQp.Device.Signer.Ark, 141 | Asvk: sevQp.Device.Signer.Asvk, 142 | }, 143 | }, 144 | }, 145 | } 146 | badSnpRoot := map[string][]*trust.AMDRootCerts{ 147 | "Milan": { 148 | { 149 | Product: "Milan", // TODO(Issue#114): Remove 150 | ProductLine: "Milan", 151 | ProductCerts: &trust.ProductCerts{ 152 | // No ASK, oops. 153 | Ask: sevQp.Device.Signer.Ark, 154 | Ark: sevQp.Device.Signer.Ark, 155 | Asvk: sevQp.Device.Signer.Ark, 156 | }, 157 | }, 158 | }, 159 | "Genoa": { 160 | { 161 | Product: "Genoa", // TODO(Issue#114): Remove 162 | ProductLine: "Genoa", 163 | ProductCerts: &trust.ProductCerts{ 164 | // No ASK, oops. 165 | Ask: sevQp.Device.Signer.Ark, 166 | Ark: sevQp.Device.Signer.Ark, 167 | Asvk: sevQp.Device.Signer.Ark, 168 | }, 169 | }, 170 | }, 171 | } 172 | fakekds, err := test.FakeKDSFromSigner(sevQp.Device.Signer) 173 | if err != nil { 174 | tb.Fatalf("failed to create fake KDS from signer: %v", err) 175 | } 176 | return sevQp, goodSnpRoot, badSnpRoot, fakekds 177 | } 178 | 179 | // If requested to use a different product than on the machine, fail. 180 | if opts.Product != nil && !proto.Equal(abi.SevProduct(), opts.Product) { 181 | return nil, nil, nil, nil 182 | } 183 | 184 | client, err := client.GetQuoteProvider() 185 | if err != nil { 186 | tb.Fatalf("Failed to open SEV guest device: %v", err) 187 | } 188 | kdsImpl := test.GetKDS(tb) 189 | 190 | badSnpRoot := make(map[string][]*trust.AMDRootCerts) 191 | for productLine, rootCerts := range trust.DefaultRootCerts { 192 | // Supplement the defaults with the missing x509 certificates. 193 | pc, err := trust.GetProductChain(productLine, abi.VcekReportSigner, kdsImpl) 194 | if err != nil { 195 | tb.Fatalf("failed to get product chain for %q: %v", productLine, err) 196 | } 197 | // By removing the ASK intermediate, we ensure that the attestation will never verify. 198 | badSnpRoot[productLine] = []*trust.AMDRootCerts{{ 199 | Product: productLine, // TODO(Issue#114): Remove 200 | ProductLine: productLine, 201 | ProductCerts: &trust.ProductCerts{ 202 | Ark: pc.Ark, 203 | Ask: pc.Ark, 204 | Asvk: pc.Ark, 205 | }, 206 | AskSev: rootCerts.ArkSev, 207 | ArkSev: rootCerts.AskSev, 208 | }} 209 | } 210 | return client, nil, badSnpRoot, kdsImpl 211 | } 212 | -------------------------------------------------------------------------------- /testing/data/data.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package data (in testing) allows tests to access data for testing purpose. 16 | package data 17 | 18 | import ( 19 | "crypto/ecdsa" 20 | "crypto/rsa" 21 | "crypto/x509" 22 | _ "embed" 23 | "encoding/pem" 24 | "log" 25 | ) 26 | 27 | //go:embed keys/vcek_private_key.pem 28 | var vcekPrivateKeyPEM []byte 29 | 30 | //go:embed keys/vlek_private_key.pem 31 | var vlekPrivateKeyPEM []byte 32 | 33 | //go:embed keys/ark_private_key.pem 34 | var arkPrivateKeyPEM []byte 35 | 36 | //go:embed keys/ask_private_key.pem 37 | var askPrivateKeyPEM []byte 38 | 39 | //go:embed keys/asvk_private_key.pem 40 | var asvkPrivateKeyPEM []byte 41 | 42 | // VCEKPrivateKey is the ECDSA private key using P-384 curve with a SHA384 digest for VCEK 43 | // Generated using: 44 | // 45 | // openssl ecparam -genkey -name secp384r1 | openssl pkcs8 -topk8 -nocrypt 46 | var VCEKPrivateKey = mustParseECDSAPrivateKey(vcekPrivateKeyPEM) 47 | 48 | // VLEKPrivateKey is the ECDSA private key using P-384 curve with a SHA384 digest for VLEK 49 | // Generated using: 50 | // 51 | // openssl ecparam -genkey -name secp384r1 | openssl pkcs8 -topk8 -nocrypt 52 | var VLEKPrivateKey = mustParseECDSAPrivateKey(vlekPrivateKeyPEM) 53 | 54 | // ARKPrivateKey is the RSA private key using 4096-bit length with a SHA256 digest for ARK 55 | // Generated using: 56 | // 57 | // openssl genrsa 4096 | openssl pkcs8 -topk8 -nocrypt 58 | var ARKPrivateKey = mustParseRSAPrivateKey(arkPrivateKeyPEM) 59 | 60 | // ASKPrivateKey is the ECDSA private key using 4096-bit length with a SHA256 digest for ASK 61 | // Generated using: 62 | // 63 | // openssl genrsa 4096 | openssl pkcs8 -topk8 -nocrypt 64 | var ASKPrivateKey = mustParseRSAPrivateKey(askPrivateKeyPEM) 65 | 66 | // ASVKPrivateKey is the ECDSA private key using 4096-bit length with a SHA256 digest for ASVK 67 | // Generated using: 68 | // 69 | // openssl genrsa 4096 | openssl pkcs8 -topk8 -nocrypt 70 | var ASVKPrivateKey = mustParseRSAPrivateKey(asvkPrivateKeyPEM) 71 | 72 | func mustParseECDSAPrivateKey(pemBytes []byte) *ecdsa.PrivateKey { 73 | privateKey := mustParsePKCS8PrivateKey(pemBytes) 74 | ecPrivateKey, ok := privateKey.(*ecdsa.PrivateKey) 75 | if !ok { 76 | log.Fatalf("Unexpected private key type, want ECDSA private key") 77 | } 78 | return ecPrivateKey 79 | } 80 | 81 | func mustParseRSAPrivateKey(pemBytes []byte) *rsa.PrivateKey { 82 | privateKey := mustParsePKCS8PrivateKey(pemBytes) 83 | rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey) 84 | if !ok { 85 | log.Fatalf("Unexpected private key type, want RSA private key") 86 | } 87 | return rsaPrivateKey 88 | } 89 | 90 | func mustParsePKCS8PrivateKey(pemBytes []byte) any { 91 | block, rest := pem.Decode(pemBytes) 92 | if block == nil { 93 | log.Fatal("Unable to decode key as PEM") 94 | } 95 | if len(rest) > 0 { 96 | log.Fatal("Unexpected trailing data in key file") 97 | } 98 | privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) 99 | if err != nil { 100 | log.Fatalf("Unable to parse PKCS8 private key: %v", err) 101 | } 102 | return privateKey 103 | } 104 | -------------------------------------------------------------------------------- /testing/data/keys/ark_private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | MkegxleNCeJpIn1Wsj7a0gVy2ub1Lg== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /testing/data/keys/ask_private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | MDTH4Mh5L36CtDc2VZJhlsKpBIwskqM= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /testing/data/keys/asvk_private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | nbAMwwicy/ojYIpDbKmVFEnX3Zy4TA== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /testing/data/keys/vcek_private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAnWpLdR3Yr2nV7M1dy 3 | HSCUp2ZMlIP50hdvOkkPNTEiuLYxhQkxXS/WO0/nfdGdQO2hZANiAAQ2I63wh+Ud 4 | nxnPb6Izu8aAH0qFFrF04n0YK1PjLl1lZgdsZQZWrtOnSr7dQtE300gtjBiBlGGB 5 | 0k2MuCt+OsrYgXeN+yTWfe52V28BvacmJedYbmTViN0hXhm6+1h1AU4= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /testing/data/keys/vlek_private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBunPPtTnRUcgPCyam+ 3 | Hrd6HkvDkDlciDPB04JX/V5e4LMd9Tp1NdNXiXQ6vA/hal2hZANiAAQsWkZc2y+J 4 | /oNbImA+tjkuORiCNzo84vz5zoIg3vT49hpTtP46r+30dVZVbN/GYBAqhi680De5 5 | qXjOW5ZdvtXhEVi0G6AYWJuh/h/bAQyNeMQACFoPaYkwIlcGXHMnD3g= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /testing/fake_certs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testing 16 | 17 | import ( 18 | "bytes" 19 | "crypto/x509" 20 | "testing" 21 | "time" 22 | 23 | "github.com/google/go-sev-guest/abi" 24 | "github.com/google/go-sev-guest/kds" 25 | "github.com/google/uuid" 26 | ) 27 | 28 | func TestCertificatesParse(t *testing.T) { 29 | signer, err := DefaultTestOnlyCertChain("Milan", time.Now()) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | certBytes, err := signer.CertTableBytes() 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | entries, err := abi.ParseSnpCertTableHeader(certBytes) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | var hasVcek bool 42 | var hasVlek bool 43 | var hasAsk bool 44 | var hasAsvk bool 45 | var hasArk bool 46 | if len(entries) != 5 { 47 | t.Errorf("ParseSnpCertTableHeader(_) returned %d entries, want 5", len(entries)) 48 | } 49 | for _, entry := range entries { 50 | if entry.GUID == uuid.MustParse(abi.VlekGUID) { 51 | hasVlek = true 52 | } 53 | if entry.GUID == uuid.MustParse(abi.VcekGUID) { 54 | hasVcek = true 55 | } 56 | if entry.GUID == uuid.MustParse(abi.AskGUID) { 57 | hasAsk = true 58 | } 59 | if entry.GUID == uuid.MustParse(abi.AsvkGUID) { 60 | hasAsvk = true 61 | } 62 | if entry.GUID == uuid.MustParse(abi.ArkGUID) { 63 | hasArk = true 64 | } 65 | der := certBytes[entry.Offset : entry.Offset+entry.Length] 66 | if _, err := x509.ParseCertificate(der); err != nil { 67 | t.Errorf("could not parse certificate of %v: %v", entry.GUID, err) 68 | } 69 | } 70 | if !hasVlek { 71 | t.Errorf("fake certs missing VLEK") 72 | } 73 | if !hasVcek { 74 | t.Errorf("fake certs missing VCEK") 75 | } 76 | if !hasAsk { 77 | t.Errorf("fake certs missing ASK") 78 | } 79 | if !hasArk { 80 | t.Errorf("fake certs missing ARK") 81 | } 82 | if !hasAsvk { 83 | t.Errorf("fake certs missing ASVK") 84 | } 85 | if _, err := kds.VcekCertificateExtensions(signer.Vcek); err != nil { 86 | t.Errorf("could not parse generated VCEK extensions: %v", err) 87 | } 88 | } 89 | 90 | func TestCertificatesExtras(t *testing.T) { 91 | b := &AmdSignerBuilder{ 92 | Extras: map[string][]byte{abi.ExtraPlatformInfoGUID: []byte("test")}, 93 | } 94 | s, err := b.TestOnlyCertChain() 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | certBytes, err := s.CertTableBytes() 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | entries, err := abi.ParseSnpCertTableHeader(certBytes) 103 | if err != nil { 104 | t.Fatal(err) 105 | } 106 | var hasXtra bool 107 | if len(entries) != 6 { 108 | t.Errorf("ParseSnpCertTableHeader(_) returned %d entries, want 6", len(entries)) 109 | } 110 | for _, entry := range entries { 111 | if entry.GUID == uuid.MustParse(abi.ExtraPlatformInfoGUID) { 112 | hasXtra = true 113 | got := certBytes[entry.Offset : entry.Offset+entry.Length] 114 | want := []byte("test") 115 | if !bytes.Equal(got, want) { 116 | t.Errorf("%v data is %v, want %v", abi.ExtraPlatformInfoGUID, got, want) 117 | } 118 | } 119 | } 120 | if !hasXtra { 121 | t.Errorf("fake certs missing extra cert") 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /testing/fakekds.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testing 16 | 17 | import ( 18 | "bytes" 19 | "encoding/pem" 20 | "flag" 21 | "fmt" 22 | "os" 23 | "strings" 24 | "testing" 25 | 26 | "github.com/google/go-sev-guest/abi" 27 | "github.com/google/go-sev-guest/kds" 28 | kpb "github.com/google/go-sev-guest/proto/fakekds" 29 | "github.com/google/go-sev-guest/verify/trust" 30 | "go.uber.org/multierr" 31 | "google.golang.org/protobuf/proto" 32 | ) 33 | 34 | var testUseKDS = flag.Bool("test_use_kds", false, "Deprecated: If true, tests will attempt to retrieve certificates from AMD KDS") 35 | 36 | type testKdsType struct { 37 | value string 38 | } 39 | 40 | func (t *testKdsType) String() string { return t.value } 41 | func (t *testKdsType) Set(value string) error { 42 | if value != "amd" && value != "cache" && value != "none" { 43 | return fmt.Errorf("--test_kds must be one of amd, cache, or none. Got %q", value) 44 | } 45 | t.value = value 46 | return nil 47 | } 48 | 49 | var testKds = testKdsType{value: "cache"} 50 | 51 | func init() { 52 | flag.Var(&testKds, "test_kds", "One of amd, cache, none. If amd, tests will "+ 53 | "attempt to retrieve certificates from AMD KDS. If cache, only embedded certificates "+ 54 | "will be available given a hostname and TCB version. If none, then no VCEK certificates will "+ 55 | "be retrieved.") 56 | } 57 | 58 | // TestUseKDS returns whether tests should use the network to connect the live AMD Key Distribution 59 | // service. 60 | func TestUseKDS() bool { 61 | return *testUseKDS || testKds.value == "amd" 62 | } 63 | 64 | // Insert your own KDS cache here with go:embed. 65 | var internalKDSCache []byte 66 | 67 | // RootBundle represents the two different CA bundles that the KDS can 68 | // return. 69 | type RootBundle struct { 70 | VcekBundle string 71 | VlekBundle string 72 | } 73 | 74 | // FakeKDS implements the verify.HTTPSGetter interface to provide certificates like AMD KDS, but 75 | // with certificates cached in a protobuf. 76 | type FakeKDS struct { 77 | Certs *kpb.Certificates 78 | // Two CERTIFICATE PEMs for ASK, then ARK or ASVK then ARK, per product 79 | RootBundles map[string]*RootBundle 80 | } 81 | 82 | // FakeKDSFromFile returns a FakeKDS from a path to a serialized fakekds.Certificates message. 83 | func FakeKDSFromFile(path string) (*FakeKDS, error) { 84 | result := &FakeKDS{ 85 | Certs: &kpb.Certificates{}, 86 | RootBundles: map[string]*RootBundle{ 87 | "Milan": { 88 | VcekBundle: string(trust.AskArkMilanVcekBytes), 89 | VlekBundle: string(trust.AskArkMilanVlekBytes), 90 | }, 91 | "Genoa": { 92 | VcekBundle: string(trust.AskArkGenoaVcekBytes), 93 | VlekBundle: string(trust.AskArkGenoaVlekBytes), 94 | }, 95 | "Turin": { 96 | VcekBundle: string(trust.AskArkTurinVcekBytes), 97 | VlekBundle: string(trust.AskArkTurinVlekBytes), 98 | }}, 99 | } 100 | 101 | contents, err := os.ReadFile(path) 102 | if os.IsNotExist(err) { 103 | return result, err 104 | } 105 | if err != nil { 106 | return nil, fmt.Errorf("could not load FakeKDS file %q: %v", path, err) 107 | } 108 | if err := proto.Unmarshal(contents, result.Certs); err != nil { 109 | return nil, fmt.Errorf("could not unmarshal FakeKDS file %q: %v", path, err) 110 | } 111 | return result, nil 112 | } 113 | 114 | // FakeKDSFromSigner returns a FakeKDS that produces the fake signer's certificates following the 115 | // AMD KDS REST API expectations. 116 | func FakeKDSFromSigner(signer *AmdSigner) (*FakeKDS, error) { 117 | certs := &kpb.Certificates{} 118 | rootBundles := map[string]*RootBundle{} 119 | certs.ChipCerts = []*kpb.Certificates_ChipTCBCerts{ 120 | { 121 | ChipId: signer.HWID[:], 122 | TcbCerts: map[uint64][]byte{ 123 | uint64(signer.TCB): signer.Vcek.Raw, 124 | }, 125 | Fms: abi.MaskedCpuid1EaxFromSevProduct(signer.Product), 126 | }, 127 | } 128 | productLine := kds.ProductLine(signer.Product) 129 | 130 | b := &strings.Builder{} 131 | if err := multierr.Combine( 132 | pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: signer.Ask.Raw}), 133 | pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: signer.Ark.Raw}), 134 | ); err != nil { 135 | return nil, fmt.Errorf("could not encode VCEK root certificates: %v", err) 136 | } 137 | rootBundles[productLine] = &RootBundle{VcekBundle: b.String()} 138 | if signer.Asvk != nil { 139 | b := &strings.Builder{} 140 | if err := multierr.Combine( 141 | pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: signer.Asvk.Raw}), 142 | pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: signer.Ark.Raw}), 143 | ); err != nil { 144 | return nil, fmt.Errorf("could not encode VLEK root certificates: %v", err) 145 | } 146 | rootBundles[productLine].VlekBundle = b.String() 147 | } 148 | 149 | return &FakeKDS{Certs: certs, RootBundles: rootBundles}, nil 150 | } 151 | 152 | // FindChipTcbCerts returns the TcbCerts associated with the given chipID in the database if they 153 | // exist. If not, returns nil. 154 | func FindChipTcbCerts(database *kpb.Certificates, chipID []byte) map[uint64][]byte { 155 | for _, cert := range database.ChipCerts { 156 | if bytes.Equal(cert.ChipId, chipID) { 157 | return cert.TcbCerts 158 | } 159 | } 160 | return nil 161 | } 162 | 163 | // Get translates a KDS url into the expected certificate as represented in the fake's certificate 164 | // database. 165 | func (f *FakeKDS) Get(url string) ([]byte, error) { 166 | // If a root cert request, return the embedded default root certs. 167 | product, key, err := kds.ParseProductCertChainURL(url) 168 | if err == nil { 169 | bundles, ok := f.RootBundles[product] 170 | if !ok { 171 | return nil, fmt.Errorf("no embedded CA bundle for product %q", product) 172 | } 173 | switch key { 174 | case kds.VcekCertFunction: 175 | return []byte(bundles.VcekBundle), nil 176 | case kds.VlekCertFunction: 177 | return []byte(bundles.VlekBundle), nil 178 | default: 179 | return nil, fmt.Errorf("internal: unsupperted key type for fake bundles: %q", key) 180 | } 181 | } 182 | vcek, err := kds.ParseVCEKCertURL(url) 183 | if err != nil { 184 | return nil, err 185 | } 186 | certs := FindChipTcbCerts(f.Certs, vcek.HWID) 187 | if certs == nil { 188 | return nil, fmt.Errorf("no certificate found at %q (unknown HWID %v)", url, vcek.HWID) 189 | } 190 | certbytes, ok := certs[vcek.TCB] 191 | if !ok { 192 | return nil, fmt.Errorf("no certificate found at %q (host present, bad TCB %v)", url, vcek.TCB) 193 | } 194 | return certbytes, nil 195 | } 196 | 197 | // GetKDS returns an HTTPSGetter that can produce the expected certificates for a given URL in the 198 | // test environment. 199 | func GetKDS(t testing.TB) trust.HTTPSGetter { 200 | if TestUseKDS() { 201 | return trust.DefaultHTTPSGetter() 202 | } 203 | fakeKds := &FakeKDS{ 204 | Certs: &kpb.Certificates{}, 205 | RootBundles: map[string]*RootBundle{"Milan": { 206 | VcekBundle: string(trust.AskArkMilanVcekBytes), 207 | VlekBundle: string(trust.AskArkMilanVlekBytes), 208 | }, 209 | "Genoa": { 210 | VcekBundle: string(trust.AskArkGenoaVcekBytes), 211 | VlekBundle: string(trust.AskArkGenoaVlekBytes), 212 | }, 213 | "Turin": { 214 | VcekBundle: string(trust.AskArkTurinVcekBytes), 215 | VlekBundle: string(trust.AskArkTurinVlekBytes), 216 | }}, 217 | } 218 | // Provide nothing if --test_kds=none. 219 | if testKds.value == "none" { 220 | return fakeKds 221 | } 222 | if err := proto.Unmarshal(internalKDSCache, fakeKds.Certs); err != nil { 223 | t.Fatalf("could not unmarshal embedded FakeKDS file: %v", err) 224 | } 225 | return fakeKds 226 | } 227 | -------------------------------------------------------------------------------- /testing/match.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testing 16 | 17 | import "strings" 18 | 19 | // Match returns true iff both errors match expectations closely enough 20 | func Match(got error, want string) bool { 21 | if got == nil { 22 | return want == "" 23 | } 24 | return want != "" && strings.Contains(got.Error(), want) 25 | } 26 | -------------------------------------------------------------------------------- /testing/mocks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testing 16 | 17 | import ( 18 | "context" 19 | "encoding/hex" 20 | "errors" 21 | "fmt" 22 | "sync" 23 | "syscall" 24 | "testing" 25 | 26 | "github.com/google/go-sev-guest/abi" 27 | labi "github.com/google/go-sev-guest/client/linuxabi" 28 | spb "github.com/google/go-sev-guest/proto/sevsnp" 29 | "golang.org/x/sys/unix" 30 | ) 31 | 32 | // GetReportResponse represents a mocked response to a command request. 33 | type GetReportResponse struct { 34 | Resp labi.SnpReportRespABI 35 | EsResult labi.EsResult 36 | FwErr abi.SevFirmwareStatus 37 | } 38 | 39 | // Device represents a sev-guest driver implementation with pre-programmed responses to commands. 40 | type Device struct { 41 | isOpen bool 42 | ReportDataRsp map[string]any 43 | Keys map[string][]byte 44 | Certs []byte 45 | Signer *AmdSigner 46 | SevProduct *spb.SevProduct 47 | } 48 | 49 | // Open changes the mock device's state to open. 50 | func (d *Device) Open(_ string) error { 51 | if d.isOpen { 52 | return errors.New("device already open") 53 | } 54 | d.isOpen = true 55 | return nil 56 | } 57 | 58 | // Close changes the mock device's state to closed. 59 | func (d *Device) Close() error { 60 | if !d.isOpen { 61 | return errors.New("device already closed") 62 | } 63 | d.isOpen = false 64 | return nil 65 | } 66 | 67 | func (d *Device) getReport(req *labi.SnpReportReqABI, rsp *labi.SnpReportRespABI, fwErr *uint64) (uintptr, error) { 68 | mockRspI, ok := d.ReportDataRsp[hex.EncodeToString(req.ReportData[:])] 69 | if !ok { 70 | return 0, fmt.Errorf("test error: no response for %v", req.ReportData) 71 | } 72 | mockRsp, ok := mockRspI.(*GetReportResponse) 73 | if !ok { 74 | return 0, fmt.Errorf("test error: incorrect response type %v", mockRspI) 75 | } 76 | esResult := uintptr(mockRsp.EsResult) 77 | if mockRsp.FwErr != 0 { 78 | *fwErr = uint64(mockRsp.FwErr) 79 | return esResult, syscall.Errno(unix.EIO) 80 | } 81 | report := mockRsp.Resp.Data[:abi.ReportSize] 82 | r, s, err := d.Signer.Sign(abi.SignedComponent(report)) 83 | if err != nil { 84 | return 0, fmt.Errorf("test error: could not sign report: %v", err) 85 | } 86 | if err := abi.SetSignature(r, s, report); err != nil { 87 | return 0, fmt.Errorf("test error: could not set signature: %v", err) 88 | } 89 | copy(rsp.Data[:], report) 90 | return esResult, nil 91 | } 92 | 93 | func (d *Device) getExtReport(req *labi.SnpExtendedReportReq, rsp *labi.SnpReportRespABI, fwErr *uint64) (uintptr, error) { 94 | if req.CertsLength == 0 { 95 | *fwErr = uint64(abi.GuestRequestInvalidLength) 96 | req.CertsLength = uint32(len(d.Certs)) 97 | return 0, syscall.Errno(unix.EIO) 98 | } 99 | ret, err := d.getReport(&req.Data, rsp, fwErr) 100 | if err != nil { 101 | return ret, err 102 | } 103 | if req.CertsLength < uint32(len(d.Certs)) { 104 | return 0, fmt.Errorf("test failure: cert buffer too small: %d < %d", req.CertsLength, len(d.Certs)) 105 | } 106 | copy(req.Certs, d.Certs) 107 | return ret, nil 108 | } 109 | 110 | // DerivedKeyRequestToString translates a DerivedKeyReqABI into a map key string representation. 111 | func DerivedKeyRequestToString(req *labi.SnpDerivedKeyReqABI) string { 112 | return fmt.Sprintf("%x %x %x %x %x", req.RootKeySelect, req.GuestFieldSelect, req.Vmpl, req.GuestSVN, req.TCBVersion) 113 | } 114 | 115 | func (d *Device) getDerivedKey(req *labi.SnpDerivedKeyReqABI, rsp *labi.SnpDerivedKeyRespABI, _ *uint64) (uintptr, error) { 116 | if len(d.Keys) == 0 { 117 | return 0, errors.New("test error: no keys") 118 | } 119 | key, ok := d.Keys[DerivedKeyRequestToString(req)] 120 | if !ok { 121 | return 0, fmt.Errorf("test error: unmapped key request %v", req) 122 | } 123 | copy(rsp.Data[:], key) 124 | return 0, nil 125 | } 126 | 127 | // Ioctl mocks commands with pre-specified responses for a finite number of requests. 128 | func (d *Device) Ioctl(command uintptr, req any) (uintptr, error) { 129 | switch sreq := req.(type) { 130 | case *labi.SnpUserGuestRequest: 131 | switch command { 132 | case labi.IocSnpGetReport: 133 | return d.getReport(sreq.ReqData.(*labi.SnpReportReqABI), sreq.RespData.(*labi.SnpReportRespABI), &sreq.FwErr) 134 | case labi.IocSnpGetDerivedKey: 135 | return d.getDerivedKey(sreq.ReqData.(*labi.SnpDerivedKeyReqABI), sreq.RespData.(*labi.SnpDerivedKeyRespABI), &sreq.FwErr) 136 | case labi.IocSnpGetExtendedReport: 137 | return d.getExtReport(sreq.ReqData.(*labi.SnpExtendedReportReq), sreq.RespData.(*labi.SnpReportRespABI), &sreq.FwErr) 138 | default: 139 | return 0, fmt.Errorf("invalid command 0x%x", command) 140 | } 141 | } 142 | return 0, fmt.Errorf("unexpected request: %v", req) 143 | } 144 | 145 | // Product returns the mocked product info or the default. 146 | func (d *Device) Product() *spb.SevProduct { 147 | if d.SevProduct == nil { 148 | return abi.DefaultSevProduct() 149 | } 150 | return d.SevProduct 151 | } 152 | 153 | // QuoteProvider represents a SEV-SNP backed configfs-tsm with pre-programmed responses to attestations. 154 | type QuoteProvider struct { 155 | Device *Device 156 | } 157 | 158 | // Product returns the mocked product info or the default. 159 | func (p *QuoteProvider) Product() *spb.SevProduct { 160 | return p.Device.Product() 161 | } 162 | 163 | // IsSupported returns true 164 | func (*QuoteProvider) IsSupported() bool { 165 | return true 166 | } 167 | 168 | // GetRawQuote returns the raw report assigned for given reportData. 169 | func (p *QuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8, error) { 170 | mockRspI, ok := p.Device.ReportDataRsp[hex.EncodeToString(reportData[:])] 171 | if !ok { 172 | return nil, fmt.Errorf("test error: no response for %v", reportData) 173 | } 174 | mockRsp, ok := mockRspI.(*GetReportResponse) 175 | if !ok { 176 | return nil, fmt.Errorf("test error: incorrect response type %v", mockRspI) 177 | } 178 | if mockRsp.FwErr != 0 { 179 | return nil, syscall.Errno(unix.EIO) 180 | } 181 | report := mockRsp.Resp.Data[:abi.ReportSize] 182 | r, s, err := p.Device.Signer.Sign(abi.SignedComponent(report)) 183 | if err != nil { 184 | return nil, fmt.Errorf("test error: could not sign report: %v", err) 185 | } 186 | if err := abi.SetSignature(r, s, report); err != nil { 187 | return nil, fmt.Errorf("test error: could not set signature: %v", err) 188 | } 189 | if p.Device.SevProduct == nil { 190 | return nil, fmt.Errorf("mock SevProduct must not be nil") 191 | } 192 | extended, err := abi.ExtendPlatformCertTable(p.Device.Certs, &abi.ExtraPlatformInfo{ 193 | Size: abi.ExtraPlatformInfoV0Size, 194 | Cpuid1Eax: abi.MaskedCpuid1EaxFromSevProduct(p.Device.SevProduct), 195 | }) 196 | if err != nil { 197 | return nil, err 198 | } 199 | return append(report, extended...), nil 200 | } 201 | 202 | // GetResponse controls how often (Occurrences) a certain response should be 203 | // provided. 204 | type GetResponse struct { 205 | Occurrences uint 206 | Body []byte 207 | Error error 208 | } 209 | 210 | // Getter is a mock for HTTPSGetter interface that sequentially 211 | // returns the configured responses for the provided URL. Responses are returned 212 | // as a queue, i.e., always serving from index 0. 213 | type Getter struct { 214 | mu sync.Mutex 215 | Responses map[string][]GetResponse 216 | } 217 | 218 | // SimpleGetter constructs a static server from url -> body responses. 219 | // For more elaborate tests, construct a custom Getter. 220 | func SimpleGetter(responses map[string][]byte) *Getter { 221 | getter := &Getter{ 222 | Responses: make(map[string][]GetResponse), 223 | } 224 | for key, value := range responses { 225 | getter.Responses[key] = []GetResponse{ 226 | { 227 | Occurrences: ^uint(0), 228 | Body: value, 229 | Error: nil, 230 | }, 231 | } 232 | } 233 | return getter 234 | } 235 | 236 | // Get the next response body and error. The response is also removed, 237 | // if it has been requested the configured number of times. 238 | func (g *Getter) Get(url string) ([]byte, error) { 239 | g.mu.Lock() 240 | defer g.mu.Unlock() 241 | resp, ok := g.Responses[url] 242 | if !ok || len(resp) == 0 { 243 | return nil, fmt.Errorf("404: %s", url) 244 | } 245 | body := resp[0].Body 246 | err := resp[0].Error 247 | resp[0].Occurrences-- 248 | if resp[0].Occurrences == 0 { 249 | g.Responses[url] = resp[1:] 250 | } 251 | return body, err 252 | } 253 | 254 | // GetContext checks whether the context expired, returns the context error if that's the case and 255 | // calls Get otherwise. 256 | func (g *Getter) GetContext(ctx context.Context, url string) ([]byte, error) { 257 | select { 258 | case <-ctx.Done(): 259 | return nil, ctx.Err() 260 | default: 261 | return g.Get(url) 262 | } 263 | } 264 | 265 | // Done checks that all configured responses have been consumed, and errors 266 | // otherwise. 267 | func (g *Getter) Done(t testing.TB) { 268 | g.mu.Lock() 269 | defer g.mu.Unlock() 270 | 271 | for key := range g.Responses { 272 | if len(g.Responses[key]) != 0 { 273 | t.Errorf("Prepared response for '%s' not retrieved.", key) 274 | } 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /tools/attest/README.md: -------------------------------------------------------------------------------- 1 | # `attest` CLI tool 2 | 3 | This binary is a thin wrapper around the `client` library to gather attestation 4 | reports in either AMD API format or in this module's `sevsnp` protobuf formats. 5 | 6 | The tool's input is the intended `REPORT_DATA` contents, which is 64 bytes of 7 | user-provided data to include in the attestation report. This is typically a 8 | nonce. 9 | 10 | The tool's output is the report in any specified format to either standard out 11 | or directly to a file. 12 | 13 | ## Example 14 | 15 | ``` 16 | $ go run . -inform base64 -in \ 17 | SGVsbG8gU0VWLVNOUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== \ 18 | -out attestation.bin 19 | ``` 20 | 21 | Or equivalently through stdin and default binary input format: 22 | 23 | ```shell 24 | $ echo \ 25 | “SGVsbG8gU0VWLVNOUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==” | \ 26 | base64 -d | go run . -out attestation.bin 27 | ``` 28 | 29 | If the host does not provide cached certificates, passing --extended will return 30 | empty certificates. It's still recommended to use --extended since the verification 31 | logic won't change once the host provides cached certificates. The verification will 32 | just not need to download them from the AMD Key Distribution Service (KDS). 33 | 34 | ## Usage 35 | 36 | ``` 37 | ./attest [options...] 38 | ``` 39 | 40 | ### `-extended` 41 | 42 | The flag requests that the tool uses the extended guest request to get both the 43 | attestation report and the host-provided certificates. If `-outform` is `bin`, 44 | then the output is the attestation report immediately followed by the 45 | certificate table. 46 | 47 | ### `-in` 48 | 49 | This flag provides the `REPORT_DATA` content directly on the command line. The 50 | contents will be interpreted by the value of the `-inform` flag. The `auto` inform 51 | will default to expecting a hexadecimal string. 52 | 53 | ### `-infile` 54 | 55 | A path to a file that contains `REPORT_DATA` contents. May be `-` for standard 56 | in. If neither `-in` nor `-infile` are specified, then the default input is 57 | standard in. The `auto` inform will default to expecting binary. 58 | 59 | ### `-inform` 60 | 61 | The format that input takes. One of 62 | 63 | * `bin`: for raw binary. Must have the expected number of bytes. 64 | * `hex`: for a byte string encoded as a hexadecimal string. Fewer bytes than 65 | expected will be zero-filled. 66 | * `base64`: for a byte string in base64 encoding. Fewer bytes than expected 67 | will be zero-filled. 68 | * `auto`: has different meanings whether input is from a file or from a 69 | command line argument. 70 | + If from an argument, then defaults to expecting a hexadecimal string. 71 | Will try base64 if hex decoding fails. 72 | + If from a file, then defaults to expecting binary. 73 | 74 | Default value is `auto`. 75 | 76 | ### `-outform` 77 | 78 | The format that output takes. This can be `bin` for AMD's specified structures 79 | in binary, `proto` for this module's protobuf message types serialized to bytes, 80 | or `textproto` for this module's protobuf message types in human readable text 81 | format. 82 | 83 | Default value is `bin`. 84 | 85 | ### `-out` 86 | 87 | Path to output file to write attestation report to. 88 | 89 | Default is empty, interpreted as stdout. 90 | 91 | ### `-vmpl` 92 | 93 | The VMPL at which the attestation report should be collected at. Must be between 94 | 0 and 3. 95 | 96 | Default value is 0. 97 | 98 | -------------------------------------------------------------------------------- /tools/attest/attest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package main implements a CLI tool for collecting attestation reports. 16 | package main 17 | 18 | import ( 19 | "errors" 20 | "flag" 21 | "fmt" 22 | "io" 23 | "os" 24 | "strconv" 25 | 26 | "github.com/google/go-sev-guest/abi" 27 | "github.com/google/go-sev-guest/client" 28 | pb "github.com/google/go-sev-guest/proto/sevsnp" 29 | "github.com/google/go-sev-guest/tools/lib/cmdline" 30 | "github.com/google/logger" 31 | "google.golang.org/protobuf/encoding/prototext" 32 | "google.golang.org/protobuf/proto" 33 | ) 34 | 35 | var ( 36 | inform = flag.String("inform", "auto", "The format of the reportData input. One of bin, hex, base64, or auto. "+ 37 | "Input forms that are not \"bin\" or \"auto\" with a file input will be zero-padded on the right to fill "+ 38 | "the expected byte size. If \"bin\" or \"auto\" from a file, then the size must be exact.") 39 | outform = flag.String("outform", "bin", 40 | "The format of the output attestation report. "+ 41 | "One of \"bin\", \"proto\", \"textproto\". "+ 42 | "The bin form is for AMD's specified data structures in binary.") 43 | extended = flag.Bool("extended", false, 44 | "Get both the attestation report and "+ 45 | "the host-provided certificate chain. "+ 46 | "If -outform=bin, then the binary appears in that order.") 47 | reportDataStr = flag.String("in", "", 48 | "A string of 64 bytes REPORT_DATA to include in the output attestation. "+ 49 | "Big-endian hex, octal, or binary start with 0x, 0o, or 0b respectively, detected with -inform=auto."+ 50 | "Little-endian base64 starts with 64x for auto to detect it, or without if -inform=base64. "+ 51 | "Little-endian hex is tried last with auto. Default -inform=auto. It is an error to use -inform=bin1") 52 | reportData = cmdline.Bytes("-in", abi.ReportDataSize, reportDataStr) 53 | reportDataFile = flag.String("infile", "", 54 | "Path to a file containing 64 bytes of REPORT_DATA to include "+ 55 | "in the output attestation. Stdin is \"-\". Default -inform=bin.") 56 | vmpl = flag.String("vmpl", "default", "The VMPL at which to collect an attestation report") 57 | out = flag.String("out", "", "Path to output file to write attestation report to. "+ 58 | "If unset, outputs to stdout.") 59 | verbose = flag.Bool("v", false, "Enable verbose logging.") 60 | vmplInt uint 61 | ) 62 | 63 | func indata() ([]byte, error) { 64 | if len(*reportData) == 0 && len(*reportDataFile) == 0 { 65 | // Default to stdin 66 | *reportDataFile = "-" 67 | } 68 | if len(*reportData) != 0 && len(*reportDataFile) != 0 { 69 | return nil, errors.New("cannot specify both of -in and -infile") 70 | } 71 | if len(*reportData) != 0 { 72 | return []byte(*reportData), nil 73 | } 74 | if *reportDataFile == "-" { 75 | return cmdline.ParseBytes("stdin", abi.ReportDataSize, os.Stdin, *inform, cmdline.Filey) 76 | } 77 | file, err := os.Open(*reportDataFile) 78 | if err != nil { 79 | return nil, fmt.Errorf("could not open %q: %v", *reportDataFile, err) 80 | } 81 | defer file.Close() 82 | return cmdline.ParseBytes("stdin", abi.ReportDataSize, file, *inform, cmdline.Filey) 83 | } 84 | 85 | func nonBinOut() func(proto.Message) ([]byte, error) { 86 | switch *outform { 87 | case "proto": 88 | return proto.Marshal 89 | case "textproto": 90 | return prototext.Marshal 91 | // unreachable panic since outform is checked in main 92 | default: 93 | panic(fmt.Sprintf("unknown -outform: %s", *outform)) 94 | } 95 | } 96 | 97 | func outputExtendedReport(data [abi.ReportDataSize]byte, out io.Writer) error { 98 | if *outform == "bin" { 99 | bin, err := getRaw(data) 100 | if err != nil { 101 | return err 102 | } 103 | out.Write(bin) 104 | return nil 105 | } 106 | attestation, err := getProto(data) 107 | if err != nil { 108 | return err 109 | } 110 | bytes, err := nonBinOut()(attestation) 111 | if err != nil { 112 | return err 113 | } 114 | out.Write(bytes) 115 | return nil 116 | } 117 | 118 | func getVmpl() (uint, error) { 119 | if *vmpl == "default" { 120 | return 0, fmt.Errorf("getVmpl should not be called on \"default\"") 121 | } 122 | vmplInt, err := strconv.ParseUint(*vmpl, 10, 32) 123 | if err != nil { 124 | return 0, fmt.Errorf("--vmpl must be a non-negative integer or \"default\"") 125 | } 126 | return uint(vmplInt), nil 127 | } 128 | 129 | func getRaw(data [abi.ReportDataSize]byte) ([]byte, error) { 130 | if *vmpl == "default" { 131 | qp, err := client.GetQuoteProvider() 132 | if err != nil { 133 | return nil, err 134 | } 135 | return qp.GetRawQuote(data) 136 | } 137 | qp, err := client.GetLeveledQuoteProvider() 138 | if err != nil { 139 | return nil, err 140 | } 141 | return qp.GetRawQuoteAtLevel(data, vmplInt) 142 | } 143 | 144 | func getProto(data [abi.ReportDataSize]byte) (*pb.Attestation, error) { 145 | if *vmpl == "default" { 146 | qp, err := client.GetQuoteProvider() 147 | if err != nil { 148 | return nil, err 149 | } 150 | return client.GetQuoteProto(qp, data) 151 | } 152 | qp, err := client.GetLeveledQuoteProvider() 153 | if err != nil { 154 | return nil, err 155 | } 156 | return client.GetQuoteProtoAtLevel(qp, data, vmplInt) 157 | } 158 | 159 | func outputReport(data [abi.ReportDataSize]byte, out io.Writer) error { 160 | if *outform == "bin" { 161 | bytes, err := getRaw(data) 162 | if err != nil { 163 | return err 164 | } 165 | if len(bytes) > abi.ReportSize { 166 | bytes = bytes[:abi.ReportSize] 167 | } 168 | out.Write(bytes) 169 | return nil 170 | } 171 | attestation, err := getProto(data) 172 | if err != nil { 173 | return err 174 | } 175 | bytes, err := nonBinOut()(attestation.Report) 176 | if err != nil { 177 | return err 178 | } 179 | out.Write(bytes) 180 | return nil 181 | } 182 | 183 | func outWriter() (io.Writer, *os.File, error) { 184 | if *out == "" { 185 | return os.Stdout, nil, nil 186 | } 187 | file, err := os.Create(*out) 188 | if err != nil { 189 | return nil, nil, err 190 | } 191 | return file, file, nil 192 | } 193 | 194 | func main() { 195 | logger.Init("", *verbose, false, os.Stderr) 196 | flag.Parse() 197 | // Second phase of parsing. 198 | cmdline.Parse(*inform) 199 | 200 | reportData, err := indata() 201 | if err != nil { 202 | logger.Fatal(err) 203 | } 204 | 205 | if !(*outform == "bin" || *outform == "proto" || *outform == "textproto") { 206 | logger.Fatalf("-outform is %s. Expect \"bin\", \"proto\", or \"textproto\"", 207 | *outform) 208 | } 209 | 210 | if *vmpl != "default" { 211 | vint, err := getVmpl() 212 | if err != nil || vint > 3 { 213 | logger.Fatalf("--vmpl=%s. Expect 0-3 or \"default\"", *vmpl) 214 | } 215 | vmplInt = vint 216 | } 217 | 218 | outwriter, filetoclose, err := outWriter() 219 | if err != nil { 220 | logger.Fatal(err) 221 | } 222 | defer func() { 223 | if filetoclose != nil { 224 | filetoclose.Close() 225 | } 226 | }() 227 | 228 | var reportData64 [abi.ReportDataSize]byte 229 | copy(reportData64[:], reportData) 230 | if *extended { 231 | if err := outputExtendedReport(reportData64, outwriter); err != nil { 232 | logger.Fatal(err) 233 | } 234 | } else { 235 | if err := outputReport(reportData64, outwriter); err != nil { 236 | logger.Fatal(err) 237 | } 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /tools/check/README.md: -------------------------------------------------------------------------------- 1 | # `check` CLI tool 2 | 3 | This binary is a thin wrapper around the `verify` and `validate` libraries to 4 | check attestation reports against expectations. 5 | 6 | The tool's input is an AMD SEV-SNP attestation report and associated certificates. 7 | 8 | The tool's output an error or "Success". 9 | 10 | ## Usage 11 | 12 | ``` 13 | ./check [options...] 14 | ``` 15 | 16 | ### `-in` 17 | 18 | This flag provides the path to the attestation file to check. Stdin is "-". 19 | 20 | ### `-inform` 21 | 22 | The format that input takes. One of 23 | 24 | * `bin`: for raw binary. This is the attestation report immediately followed 25 | by the certificate table if there is one. 26 | * `proto`: A binary serialized `sevsnp.Attestation` message. 27 | * `textproto`: The `sevsnp.Attestation` message in textproto format. 28 | 29 | Default value is `bin`. 30 | 31 | ### `quiet` 32 | 33 | If set, doesn't write to stdout. All results are communicated through exit code. 34 | 35 | ### `config` 36 | 37 | A path to a serialized `check.Config` protocol buffer message that represents 38 | values for each of the following flags. If any flags are additionally provided, 39 | they are interpreted to override the respective message field. 40 | 41 | If the path ends in `.textproto`, the message is deserialized with as the 42 | human-readable `prototext` format. 43 | 44 | ### `guest_policy` 45 | 46 | The most acceptable policy component-wise in its SEV-SNP API 64-bit number 47 | format. "Most acceptable" means the minimum API major.minor version, if debug 48 | is allowed, if singlesocket is required, if migrateMA is allowed, if SMT is 49 | allowed. 50 | 51 | ### `report_data` 52 | 53 | The expected exact `REPORT_DATA` value as a hex-encoded string. Unchecked if 54 | empty. Default empty. 55 | 56 | ### `host_data` 57 | 58 | The expected exact `HOST_DATA` value as a hex-encoded string. Unchecked if 59 | empty. Default empty. 60 | 61 | ### `family_id` 62 | 63 | The expected exact `FAMILY_ID` value as a hex-encoded string. Unchecked if 64 | empty. Default empty. 65 | 66 | ### `image_id` 67 | 68 | The expected exact `IMAGE_ID` value as a hex-encoded string. Unchecked if 69 | empty. Default empty. 70 | 71 | ### `report_id` 72 | 73 | The expected exact `REPORT_ID` value as a hex-encoded string. Unchecked if 74 | empty. Default empty. 75 | 76 | ### `report_id_ma` 77 | 78 | The expected exact `REPORT_ID_MA` value as a hex-encoded string. Unchecked if 79 | empty. Default empty. 80 | 81 | ### `measurement` 82 | 83 | The expected exact `MEASUREMENT` value as a hex-encoded string. Unchecked if 84 | empty. Default empty. 85 | 86 | ### `chip_id` 87 | 88 | The expected exact `CHIP_ID` value as a hex-encoded string. Unchecked if 89 | empty. Default empty. 90 | 91 | ### `-vmpl` 92 | 93 | The expected VMPL value. 94 | 95 | ### `minimum_tcb` 96 | 97 | The component-wise minimum TCB allowed for both the current, committed, and 98 | reported TCB values. Default `0`. 99 | 100 | ### `minimum_launch_tcb` 101 | 102 | The component-wise minimum TCB allowed for the launch TCB value. Default `0`. 103 | 104 | ### `provisional` 105 | 106 | If true, allows reported values to be greater than or equal to than committed 107 | values. Default `false` 108 | 109 | ### `platform_info` 110 | 111 | The maximum acceptable `PLATFORM_INFO` field bit-wise. If empty, left 112 | unchecked. Default empty. 113 | 114 | ### `require_author_key` 115 | 116 | If true, requires the attestation report to have `AUTHOR_KEY_EN` set to 1. Will 117 | also check `AUTHOR_KEY_DIGEST` against trusted author arguments. Implies 118 | `require_idblock` is true. 119 | 120 | ### `require_idblock` 121 | 122 | If true, checks that the `ID_KEY_DIGEST` is trusted, either directly against 123 | trusted id key arguments, or if the author key is present and the author key is 124 | trusted. 125 | 126 | ### `min_build` 127 | 128 | The minimum value allowed for both `CURRENT_BUILD` and `COMMITTED_BUILD`. 129 | 130 | ### `min_version` 131 | 132 | A `major.minor` version string that specifies the lexicographically minimum 133 | values allowed for `{CURRENT,COMMITTED}_{MAJOR,MINOR}`. 134 | 135 | ### `trusted_author_keys` 136 | 137 | A colon-separated list of paths to x.509 certificate files for trusted author 138 | keys. Combined with `trusted_author_key_hashes`. 139 | 140 | ### `trusted_author_key_hashes` 141 | 142 | A comma-separated list of hex-encoded strings for SHA384 digests of trusted 143 | author keys in SEV API format. Combined with `trusted_author_keys`. 144 | 145 | ### `trusted_id_keys` 146 | 147 | A colon-separated list of paths to x.509 certificate files for trusted id 148 | keys. Combined with `trusted_id_key_hashes`. 149 | 150 | ### `trusted_id_key_hashes` 151 | 152 | A comma-separated list of hex-encoded strings for SHA384 digests of trusted id 153 | keys in SEV API format. Combined with `trusted_id_keys`. 154 | 155 | ### `product` 156 | 157 | The name of the AMD product that produced the attestation report. Default 158 | `Milan`. 159 | 160 | ### `product_key_path` 161 | 162 | A colon-separated list of paths to CA bundles for the product. The expected 163 | format of each file is a ASK certificate followed by ARK certificate both in 164 | PEM format. 165 | 166 | ### `check_crl` 167 | 168 | Download the root key's certificate revocation list and check if the product 169 | signing key (ASK) has been revoked. Default `false`. 170 | 171 | ### `network` 172 | 173 | Fetch missing files (certificates or CRL) through the network. Default `true`. 174 | 175 | ## Examples 176 | 177 | For these examples, we use the `attest` tool to give clarity on the expected 178 | format of the input report. The `attest` tool is not required for `check` to 179 | work. 180 | 181 | ```shell 182 | $ echo -n "The best nonce" | ./attest > attestation.bin 183 | $ hexnonce=$(echo -n "The best nonce" | xxd -p) 184 | $ ./check -in attestation.bin -report_data=${hexnonce} 185 | ``` 186 | 187 | ## Exit code meaning 188 | 189 | * 0: Success 190 | * 1: Failure due to tool misuse 191 | * 2: Failure due to invalid signature 192 | * 3: Failure due to certificate fetch failure 193 | * 4: Failure due to certificate revocation list download failure 194 | * 5: Failure due to policy 195 | -------------------------------------------------------------------------------- /tools/lib/cmdline/cmdline.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package cmdline implements command-line utilities for tools. 16 | package cmdline 17 | 18 | import ( 19 | "encoding/base64" 20 | "encoding/hex" 21 | "flag" 22 | "fmt" 23 | "io" 24 | "os" 25 | "strings" 26 | "unicode/utf8" 27 | ) 28 | 29 | // InputType represents how data is coming in, either via file or string. 30 | type InputType int 31 | 32 | var allFlags []func(inform string) error 33 | 34 | const ( 35 | // Stringy indicates the input is coming from an argument string. 36 | // "auto" behavior prefers hexadecimal. 37 | Stringy = iota 38 | // Filey indicates the input is coming from a file. 39 | // "auto" behavior prefers binary. 40 | Filey 41 | ) 42 | 43 | func sizedBytes(flag, value string, byteSize int, decode func(string) ([]byte, error)) ([]byte, error) { 44 | bytes, err := decode(value) 45 | if err != nil { 46 | return nil, fmt.Errorf("%s=%s could not be decoded: %v", flag, value, err) 47 | } 48 | if len(bytes) > byteSize { 49 | return nil, fmt.Errorf("%s=%s (%v) is not representable in %d bytes", flag, value, bytes, byteSize) 50 | } 51 | sized := make([]byte, byteSize) 52 | copy(sized, bytes) 53 | return sized, nil 54 | } 55 | 56 | func parseBytesFromString(name string, byteSize int, in string, inform string) ([]byte, error) { 57 | if !utf8.ValidString(in) { 58 | return nil, fmt.Errorf("could not decode %s contents as a UTF-8 string. Try -inform=bin", name) 59 | } 60 | // Strict forms first. 61 | switch inform { 62 | case "hex": 63 | return sizedBytes(name, in, byteSize, hex.DecodeString) 64 | case "base64": 65 | return sizedBytes(name, in, byteSize, base64.StdEncoding.DecodeString) 66 | case "auto": 67 | // "auto" means to try hex encoding first, then base64. 68 | if b, err := sizedBytes(name, in, byteSize, hex.DecodeString); err == nil { 69 | return b, nil 70 | } 71 | return sizedBytes(name, in, byteSize, base64.StdEncoding.DecodeString) 72 | default: 73 | return nil, fmt.Errorf("unknown -inform=%s", inform) 74 | } 75 | } 76 | 77 | func isBinForm(inform string, intype InputType) bool { 78 | if inform == "bin" { 79 | return true 80 | } 81 | return (intype == Filey && inform == "auto") 82 | } 83 | 84 | // ParseBytes returns the denoted bytes from the reader `in` or an error. 85 | func ParseBytes(name string, byteSize int, in io.Reader, inform string, intype InputType) ([]byte, error) { 86 | inbytes, err := io.ReadAll(in) 87 | if err != nil { 88 | return nil, err 89 | } 90 | // Empty input is treated as an empty array, not a zero-filled byteSize array. 91 | // This allows initial values of nil to be distinguishable from 0. 92 | if len(inbytes) == 0 { 93 | return nil, nil 94 | } 95 | if isBinForm(inform, intype) { 96 | if len(inbytes) != byteSize { 97 | return nil, fmt.Errorf("binary input type had %d bytes. Expect exactly %d bytes", 98 | len(inbytes), byteSize) 99 | } 100 | return inbytes, nil 101 | } 102 | return parseBytesFromString(name, byteSize, strings.TrimSpace(string(inbytes)), inform) 103 | } 104 | 105 | // Bytes is a flag.Func parsing function that translates a string into 106 | // a specific byte-width array. 107 | // 108 | // A byte string can be represented as 109 | // * hexadecimal encoded string if -inform=hex or -inform=auto. 110 | // * base64 if -inform=base64 or -inform=auto 111 | // 112 | // Hex string decoding is attempted first with auto. The base64 encoding grammar 113 | // intersects with the hex encoding grammar, so -inform=auto can misbehave. 114 | func Bytes(name string, byteSize int, in *string) *[]byte { 115 | var empty []byte 116 | result := &empty 117 | allFlags = append(allFlags, func(inform string) error { 118 | // No input means to keep the initial value. 119 | if *in == "" { 120 | return nil 121 | } 122 | bytes, err := ParseBytes(name, byteSize, strings.NewReader(*in), inform, Stringy) 123 | if err != nil { 124 | return err 125 | } 126 | *result = bytes 127 | return nil 128 | }) 129 | return result 130 | } 131 | 132 | // Parse processes all flag data given the input format and the precondition 133 | // that all input flags have been parsed. 134 | func Parse(inform string) { 135 | for _, thunk := range allFlags { 136 | if err := thunk(inform); err != nil { 137 | fmt.Fprintf(os.Stderr, "%v\n\n", err) 138 | flag.Usage() 139 | os.Exit(1) 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tools/lib/cmdline/cmdline_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmdline 16 | 17 | import ( 18 | "bytes" 19 | "strings" 20 | "testing" 21 | ) 22 | 23 | func expect(err error, wantErr string) bool { 24 | if err == nil { 25 | return wantErr == "" 26 | } 27 | return wantErr != "" && strings.Contains(err.Error(), wantErr) 28 | } 29 | 30 | func TestParseBytes(t *testing.T) { 31 | tests := []struct { 32 | name string 33 | byteSize int 34 | in []byte 35 | inform string 36 | intype InputType 37 | want []byte 38 | wantErr string 39 | }{ 40 | { 41 | name: "binary as binary (intype stringy)", 42 | byteSize: 4, 43 | in: []byte{0x30, 0x31, 0x32, 0x33}, 44 | inform: "bin", 45 | intype: Stringy, 46 | want: []byte{0x30, 0x31, 0x32, 0x33}, 47 | }, 48 | { 49 | name: "binary as binary (intype filey)", 50 | byteSize: 4, 51 | in: []byte{0x30, 0x31, 0x32, 0x33}, 52 | inform: "bin", 53 | intype: Filey, 54 | want: []byte{0x30, 0x31, 0x32, 0x33}, 55 | }, 56 | { 57 | name: "binary as auto (intype filey)", 58 | byteSize: 4, 59 | in: []byte{1, 2, 3, 4}, 60 | inform: "auto", 61 | intype: Filey, 62 | want: []byte{1, 2, 3, 4}, 63 | }, 64 | { 65 | name: "binary as auto, not hex-encoded (intype stringy)", 66 | byteSize: 4, 67 | in: []byte{1, 2, 3, 4}, 68 | inform: "auto", 69 | intype: Stringy, 70 | wantErr: "could not be decoded", 71 | }, 72 | { 73 | name: "hex as hex (intype stringy)", 74 | byteSize: 4, 75 | in: []byte("0123"), 76 | inform: "hex", 77 | intype: Stringy, 78 | want: []byte{0x01, 0x23, 0, 0}, 79 | }, 80 | { 81 | name: "hex as hex (intype filey)", 82 | byteSize: 4, 83 | in: []byte("0123"), 84 | inform: "hex", 85 | intype: Filey, 86 | want: []byte{0x01, 0x23, 0, 0}, 87 | }, 88 | { 89 | name: "base64 as base64 (intype stringy)", 90 | byteSize: 4, 91 | in: []byte("MTIzNA=="), // echo -n "1234" | base64 92 | inform: "base64", 93 | intype: Stringy, 94 | want: []byte{0x31, 0x32, 0x33, 0x34}, // ASCII codes 95 | }, 96 | { 97 | name: "base64 as base64 (intype filey)", 98 | byteSize: 4, 99 | in: []byte("MTIzNA=="), // echo -n "1234" | base64 100 | inform: "base64", 101 | intype: Filey, 102 | want: []byte{0x31, 0x32, 0x33, 0x34}, // ASCII codes 103 | }, 104 | { 105 | name: "base64 as auto does not work with non-hex", 106 | byteSize: 4, 107 | in: []byte("MTIzNA=="), // echo -n "1234" | base64 108 | inform: "auto", 109 | intype: Filey, 110 | wantErr: "binary input type had 8 bytes. Expect exactly 4 bytes", 111 | }, 112 | { 113 | name: "hexy base64 as base64", 114 | byteSize: 4, 115 | in: []byte("1234"), 116 | inform: "base64", 117 | intype: Stringy, 118 | want: []byte{0xd7, 0x6d, 0xf8, 0}, 119 | }, 120 | { 121 | name: "hex auto (intype stringy)", 122 | byteSize: 4, 123 | in: []byte("1234"), 124 | inform: "auto", 125 | intype: Stringy, 126 | want: []byte{0x12, 0x34, 0, 0}, 127 | }, 128 | { 129 | name: "hex auto (intype filey)", 130 | byteSize: 4, 131 | in: []byte("1234"), 132 | inform: "auto", 133 | intype: Filey, 134 | want: []byte{0x31, 0x32, 0x33, 0x34}, // ASCII codes 135 | }, 136 | { 137 | name: "non-exact binary", 138 | byteSize: 4, 139 | in: []byte{2}, 140 | inform: "bin", 141 | intype: Filey, 142 | wantErr: "Expect exactly 4 bytes", 143 | }, 144 | { 145 | name: "chonky hexstring", 146 | byteSize: 4, 147 | in: []byte("0102030405"), 148 | inform: "hex", 149 | intype: Filey, 150 | wantErr: "test_input=0102030405 ([1 2 3 4 5]) is not representable in 4 bytes", 151 | }, 152 | { 153 | name: "\ufffd", 154 | byteSize: 4, 155 | // Example from https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt 156 | in: []byte{0xf0, 0x80, 0x80, 0x80}, 157 | inform: "hex", 158 | intype: Stringy, 159 | wantErr: "could not decode test_input contents as a UTF-8 string", 160 | }, 161 | { 162 | name: "bad inform", 163 | byteSize: 4, 164 | in: []byte{0}, 165 | inform: "wonk", 166 | intype: Filey, 167 | wantErr: "unknown -inform=wonk", 168 | }, 169 | } 170 | for _, tc := range tests { 171 | t.Run(tc.name, func(t *testing.T) { 172 | in := bytes.NewReader(tc.in) 173 | got, err := ParseBytes("test_input", tc.byteSize, in, tc.inform, tc.intype) 174 | if !expect(err, tc.wantErr) { 175 | t.Errorf("ParseBytes(%s, %d, %q, %q, %v) errored unexpectedly. Got %v. Want %v", 176 | tc.name, tc.byteSize, tc.in, tc.inform, tc.intype, err, tc.wantErr) 177 | } 178 | if err == nil && !bytes.Equal(got, tc.want) { 179 | t.Errorf("ParseBytes(%s, %d, %q, %q, %v) = %v. Want %v", 180 | tc.name, tc.byteSize, tc.in, tc.inform, tc.intype, got, tc.want) 181 | } 182 | }) 183 | } 184 | } 185 | 186 | func TestBytes(t *testing.T) { 187 | tests := []*struct { 188 | name string 189 | in string 190 | byteSize int 191 | want []byte 192 | }{ 193 | { 194 | name: "test_input", 195 | byteSize: 4, 196 | in: "1234", 197 | want: []byte{0x12, 0x34, 0, 0}, 198 | }, 199 | { 200 | name: "empty", 201 | byteSize: 4, 202 | in: "", 203 | want: []byte{}, 204 | }, 205 | } 206 | byteArray := make([]*[]byte, len(tests)) 207 | for i, tc := range tests { 208 | byteArray[i] = Bytes(tc.name, tc.byteSize, &tc.in) 209 | } 210 | Parse("auto") 211 | for i, tc := range tests { 212 | t.Run(tc.name, func(t *testing.T) { 213 | if !bytes.Equal(*byteArray[i], tc.want) { 214 | t.Errorf("Bytes(%s, %d, &%q) = %v. Want %v", tc.name, tc.byteSize, tc.in, *byteArray[i], tc.want) 215 | } 216 | }) 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /tools/lib/report/report.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package report provides functions for reading and writing attestation reports of various formats. 16 | package report 17 | 18 | import ( 19 | "fmt" 20 | "io" 21 | "os" 22 | 23 | "github.com/google/go-sev-guest/abi" 24 | "github.com/google/go-sev-guest/kds" 25 | "go.uber.org/multierr" 26 | "google.golang.org/protobuf/encoding/prototext" 27 | "google.golang.org/protobuf/proto" 28 | 29 | spb "github.com/google/go-sev-guest/proto/sevsnp" 30 | ) 31 | 32 | func parseAttestationBytes(b []byte) (*spb.Attestation, error) { 33 | // This format is the attestation report in AMD's specified ABI format, immediately 34 | // followed by the certificate table bytes. 35 | if len(b) < abi.ReportSize { 36 | return nil, fmt.Errorf("attestation contents too small (0x%x bytes). Want at least 0x%x bytes", len(b), abi.ReportSize) 37 | } 38 | reportBytes := b[0:abi.ReportSize] 39 | certBytes := b[abi.ReportSize:] 40 | 41 | report, err := abi.ReportToProto(reportBytes) 42 | if err != nil { 43 | return nil, fmt.Errorf("could not parse attestation report: %v", err) 44 | } 45 | 46 | certs := new(abi.CertTable) 47 | if err := certs.Unmarshal(certBytes); err != nil { 48 | return nil, fmt.Errorf("could not parse certificate table: %v", err) 49 | } 50 | return &spb.Attestation{Report: report, CertificateChain: certs.Proto()}, nil 51 | } 52 | 53 | // ParseAttestation parses an attestation report from a byte slice as a given format. 54 | func ParseAttestation(b []byte, inform string) (*spb.Attestation, error) { 55 | switch inform { 56 | case "bin": 57 | // May have empty certificate buffer to be just a report. 58 | return parseAttestationBytes(b) 59 | case "proto": 60 | result := &spb.Attestation{} 61 | aerr := proto.Unmarshal(b, result) 62 | var rerr error 63 | if aerr != nil { 64 | result.Report = &spb.Report{} 65 | rerr = proto.Unmarshal(b, result.Report) 66 | if rerr != nil { 67 | return nil, fmt.Errorf("could not parse as proto: %v", multierr.Append(aerr, rerr)) 68 | } 69 | } 70 | return result, nil 71 | case "textproto": 72 | result := &spb.Attestation{} 73 | aerr := prototext.Unmarshal(b, result) 74 | var rerr error 75 | if aerr != nil { 76 | result.Report = &spb.Report{} 77 | rerr = prototext.Unmarshal(b, result.Report) 78 | if rerr != nil { 79 | return nil, fmt.Errorf("could not parse as textproto: %v", multierr.Append(aerr, rerr)) 80 | } 81 | } 82 | return result, nil 83 | default: 84 | return nil, fmt.Errorf("unknown inform: %q", inform) 85 | } 86 | } 87 | 88 | // ReadAttestation reads an attestation report from a file. 89 | func ReadAttestation(infile, inform string) (*spb.Attestation, error) { 90 | var in io.Reader 91 | var f *os.File 92 | if infile == "-" { 93 | in = os.Stdin 94 | } else { 95 | file, err := os.Open(infile) 96 | if err != nil { 97 | return nil, fmt.Errorf("could not open %q: %v", infile, err) 98 | } 99 | f = file 100 | in = file 101 | } 102 | defer func() { 103 | if f != nil { 104 | f.Close() 105 | } 106 | }() 107 | 108 | contents, err := io.ReadAll(in) 109 | if err != nil { 110 | return nil, fmt.Errorf("could not read %q: %v", infile, err) 111 | } 112 | return ParseAttestation(contents, inform) 113 | } 114 | 115 | func asBin(report *spb.Attestation) ([]byte, error) { 116 | r, err := abi.ReportToAbiBytes(report.Report) 117 | if err != nil { 118 | return nil, err 119 | } 120 | certs := abi.CertsFromProto(report.CertificateChain).Marshal() 121 | return append(r, certs...), nil 122 | } 123 | 124 | func tcbBreakdown(tcb uint64) string { 125 | parts := kds.DecomposeTCBVersion(kds.TCBVersion(tcb)) 126 | return fmt.Sprintf("0x%x:{ucode: %d, snp: %d, tee: %d, bl: %d}", tcb, parts.UcodeSpl, parts.SnpSpl, 127 | parts.TeeSpl, parts.BlSpl) 128 | } 129 | 130 | func tcbText(report *spb.Attestation) ([]byte, error) { 131 | return []byte(fmt.Sprintf("current_tcb=%s\ncommitted_tcb=%s\nlaunch_tcb=%s\n", 132 | tcbBreakdown(report.Report.GetCurrentTcb()), 133 | tcbBreakdown(report.Report.GetCommittedTcb()), 134 | tcbBreakdown(report.Report.GetLaunchTcb()))), nil 135 | } 136 | 137 | // Transform returns the attestation in the outform marshalled format. 138 | func Transform(report *spb.Attestation, outform string) ([]byte, error) { 139 | switch outform { 140 | case "bin": 141 | return asBin(report) 142 | case "proto": 143 | return proto.Marshal(report) 144 | case "textproto": 145 | return prototext.MarshalOptions{Multiline: true, Indent: " "}.Marshal(report) 146 | case "tcb": 147 | return tcbText(report) 148 | default: 149 | return nil, fmt.Errorf("unknown outform: %q", outform) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /tools/lib/report/report_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package report 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "os" 21 | "path" 22 | "sync" 23 | "testing" 24 | "time" 25 | 26 | "github.com/google/go-cmp/cmp" 27 | "github.com/google/go-sev-guest/abi" 28 | "github.com/google/go-sev-guest/client" 29 | spb "github.com/google/go-sev-guest/proto/sevsnp" 30 | test "github.com/google/go-sev-guest/testing" 31 | "google.golang.org/protobuf/encoding/prototext" 32 | "google.golang.org/protobuf/proto" 33 | "google.golang.org/protobuf/testing/protocmp" 34 | ) 35 | 36 | var qp client.QuoteProvider 37 | var mu sync.Once 38 | 39 | type reports struct { 40 | attestation *spb.Attestation 41 | bincerts []byte 42 | binreport []byte 43 | protocerts []byte 44 | protoreport []byte 45 | textcerts []byte 46 | textreport []byte 47 | } 48 | 49 | var input *reports 50 | 51 | func initDevice() { 52 | now := time.Date(2022, time.May, 3, 9, 0, 0, 0, time.UTC) 53 | tests := test.TestCases() 54 | ones32 := make([]byte, 32) 55 | for i := range ones32 { 56 | ones32[i] = 1 57 | } 58 | opts := &test.DeviceOptions{Now: now, Product: abi.DefaultSevProduct()} 59 | tcqp, err := test.TcQuoteProvider(tests, opts) 60 | if err != nil { 61 | panic(fmt.Sprintf("failed to create test device: %v", err)) 62 | } 63 | qp = tcqp 64 | 65 | var zeros [abi.ReportDataSize]byte 66 | bincerts, err := qp.GetRawQuote(zeros) 67 | if err != nil { 68 | panic(fmt.Errorf("mock failed to quote: %v", err)) 69 | } 70 | if len(bincerts) < abi.ReportSize+abi.CertTableEntrySize { 71 | panic("mock failed to return cert table") 72 | } 73 | binreport := bincerts[:abi.ReportSize] 74 | attestation, err := ParseAttestation(bincerts, "bin") 75 | if err != nil { 76 | panic(fmt.Errorf("marshal failure: %v", err)) 77 | } 78 | protocerts, err := proto.Marshal(attestation) 79 | if err != nil { 80 | panic(fmt.Errorf("marshal failure: %v", err)) 81 | } 82 | protoreport, err := proto.Marshal(attestation.Report) 83 | if err != nil { 84 | panic(fmt.Errorf("marshal failure: %v", err)) 85 | } 86 | textcerts, err := prototext.MarshalOptions{Multiline: true, Indent: " "}.Marshal(attestation) 87 | if err != nil { 88 | panic(fmt.Errorf("marshal failure: %v", err)) 89 | } 90 | textreport, err := prototext.MarshalOptions{Multiline: true, Indent: " "}.Marshal(attestation.Report) 91 | if err != nil { 92 | panic(fmt.Errorf("marshal failure: %v", err)) 93 | } 94 | input = &reports{ 95 | attestation: attestation, 96 | bincerts: bincerts, 97 | binreport: binreport, 98 | protocerts: protocerts, 99 | protoreport: protoreport, 100 | textcerts: textcerts, 101 | textreport: textreport, 102 | } 103 | } 104 | 105 | func TestParseAttestation(t *testing.T) { 106 | mu.Do(initDevice) 107 | type testcase struct { 108 | input []byte 109 | inform string 110 | } 111 | good := []testcase{ 112 | {input.bincerts, "bin"}, 113 | {input.binreport, "bin"}, 114 | {input.protocerts, "proto"}, 115 | {input.protoreport, "proto"}, 116 | {input.textcerts, "textproto"}, 117 | {input.textreport, "textproto"}, 118 | } 119 | bad := []testcase{ 120 | {input.bincerts, "proto"}, 121 | {input.textcerts, "bin"}, 122 | {input.protoreport, "textproto"}, 123 | } 124 | for _, tc := range good { 125 | if _, err := ParseAttestation(tc.input, tc.inform); err != nil { 126 | t.Fatalf("ParseAttestation(_, %q) = _, %v. Expect nil", tc.inform, err) 127 | } 128 | } 129 | for _, tc := range bad { 130 | if _, err := ParseAttestation(tc.input, tc.inform); err == nil { 131 | t.Fatalf("ParseAttestation(_, %q) = _, nil. Expected an error", tc.inform) 132 | } 133 | } 134 | } 135 | 136 | func TestReadAttestation(t *testing.T) { 137 | mu.Do(initDevice) 138 | type testcase struct { 139 | input []byte 140 | inform string 141 | } 142 | good := []testcase{ 143 | {input.bincerts, "bin"}, 144 | {input.binreport, "bin"}, 145 | {input.protocerts, "proto"}, 146 | {input.protoreport, "proto"}, 147 | {input.textcerts, "textproto"}, 148 | {input.textreport, "textproto"}, 149 | } 150 | bad := []testcase{ 151 | {input.bincerts, "proto"}, 152 | {input.textcerts, "bin"}, 153 | {input.protoreport, "textproto"}, 154 | } 155 | for _, tc := range good { 156 | p := path.Join(t.TempDir(), "input") 157 | if err := os.WriteFile(p, tc.input, 0644); err != nil { 158 | t.Fatalf("Could not write test file %q: %v", p, err) 159 | } 160 | if _, err := ReadAttestation(p, tc.inform); err != nil { 161 | t.Fatalf("ParseAttestation(_, %q) = _, %v. Expect nil", tc.inform, err) 162 | } 163 | } 164 | for _, tc := range bad { 165 | p := path.Join(t.TempDir(), "input") 166 | if err := os.WriteFile(p, tc.input, 0644); err != nil { 167 | t.Fatalf("Could not write test file %q: %v", p, err) 168 | } 169 | if _, err := ReadAttestation(p, tc.inform); err == nil { 170 | t.Fatalf("ReadAttestation(_, %q) = _, nil. Expected an error", tc.inform) 171 | } 172 | } 173 | } 174 | 175 | func protoAttestationDiff(left, right []byte) string { 176 | leftp := &spb.Attestation{} 177 | rightp := &spb.Attestation{} 178 | if err := proto.Unmarshal(left, leftp); err != nil { 179 | return fmt.Sprintf("left parse: %v", err) 180 | } 181 | if err := proto.Unmarshal(right, rightp); err != nil { 182 | return fmt.Sprintf("right parse: %v", err) 183 | } 184 | return cmp.Diff(leftp, rightp, protocmp.Transform()) 185 | } 186 | 187 | func binAttestationDiff(left, right []byte) string { 188 | if diff := cmp.Diff(left[:abi.ReportSize], right[:abi.ReportSize]); diff != "" { 189 | return fmt.Sprintf("Report diff: %s", diff) 190 | } 191 | leftcerts := left[abi.ReportSize:] 192 | rightcerts := right[abi.ReportSize:] 193 | leftt := new(abi.CertTable) 194 | rightt := new(abi.CertTable) 195 | if err := leftt.Unmarshal(leftcerts); err != nil { 196 | return "bad left" 197 | } 198 | if err := rightt.Unmarshal(rightcerts); err != nil { 199 | return "bad right" 200 | } 201 | return cmp.Diff(leftt.Proto(), rightt.Proto(), protocmp.Transform()) 202 | } 203 | 204 | func TestTransform(t *testing.T) { 205 | mu.Do(initDevice) 206 | t.Run("bin", func(t *testing.T) { 207 | binout, err := Transform(input.attestation, "bin") 208 | if err != nil { 209 | t.Fatalf("Transform(_, \"bin\") = _, %v. Expect nil.", err) 210 | } 211 | if diff := binAttestationDiff(binout, input.bincerts); diff != "" { 212 | t.Fatalf("Transform(_, \"bin\") = %v, nil. Expect %v.\nDiff: %s", binout, input.bincerts, diff) 213 | } 214 | }) 215 | t.Run("proto", func(t *testing.T) { 216 | protoout, err := Transform(input.attestation, "proto") 217 | if err != nil { 218 | t.Fatalf("Transform(_, \"proto\") = _, %v. Expect nil.", err) 219 | } 220 | if diff := protoAttestationDiff(protoout, input.protocerts); diff != "" { 221 | t.Fatalf("Transform(_, \"proto\") = %v, nil. Expect %v.\nDiff: %s", protoout, input.protocerts, diff) 222 | } 223 | }) 224 | t.Run("textproto", func(t *testing.T) { 225 | textout, err := Transform(input.attestation, "textproto") 226 | if err != nil { 227 | t.Fatalf("Transform(_, \"textproto\") = _, %v. Expect nil.", err) 228 | } 229 | if !bytes.Equal(textout, input.textcerts) { 230 | t.Fatalf("Transform(_, \"textproto\") = %v, nil. Expect %v.", string(textout), string(input.textcerts)) 231 | } 232 | }) 233 | } 234 | -------------------------------------------------------------------------------- /tools/show/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // show reads an attestation report and outputs it in a preferred format. 16 | package main 17 | 18 | import ( 19 | "flag" 20 | "os" 21 | 22 | "github.com/google/go-sev-guest/tools/lib/report" 23 | "github.com/google/logger" 24 | ) 25 | 26 | var ( 27 | infile = flag.String("in", "-", "Path to attestation file, or - for stdin.") 28 | inform = flag.String("inform", "bin", "Format of the attestation file. "+ 29 | "One of bin, proto, textproto") 30 | outfile = flag.String("out", "-", "Path to output file, or - for stdout.") 31 | outform = flag.String("outform", "textproto", "Format of the output file. "+ 32 | "One of bin, proto, textproto, tcb. Tcb is human-readable.") 33 | ) 34 | 35 | func main() { 36 | logger.Init("", false, false, os.Stderr) 37 | flag.Parse() 38 | 39 | attestation, err := report.ReadAttestation(*infile, *inform) 40 | if err != nil { 41 | logger.Fatal(err) 42 | } 43 | 44 | bin, err := report.Transform(attestation, *outform) 45 | if err != nil { 46 | logger.Fatal(err) 47 | } 48 | 49 | out := os.Stdout 50 | if *outfile != "-" { 51 | out, err = os.OpenFile(*outfile, os.O_WRONLY|os.O_CREATE, 0644) 52 | if err != nil { 53 | logger.Fatalf("Could not open %q: %v", *outfile, err) 54 | } 55 | } 56 | 57 | if _, err := out.Write(bin); err != nil { 58 | logger.Fatalf("Could not write attestation to %q: %v", *outfile, err) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /verify/testdata/attestation.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/go-sev-guest/b60b35cc8d0330af09023824d110e28b81e61f60/verify/testdata/attestation.bin -------------------------------------------------------------------------------- /verify/testdata/milan.testcer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -----BEGIN CERTIFICATE----- 39 | MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC 40 | BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS 41 | BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg 42 | Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp 43 | Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy 44 | MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS 45 | BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j 46 | ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG 47 | 9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg 48 | W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta 49 | 1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2 50 | SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0 51 | 60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05 52 | gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg 53 | bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs 54 | +gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi 55 | Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ 56 | eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18 57 | fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j 58 | WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI 59 | rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG 60 | KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG 61 | SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI 62 | AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel 63 | ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw 64 | STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK 65 | dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq 66 | zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp 67 | KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e 68 | pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq 69 | HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh 70 | 3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn 71 | JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH 72 | CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4 73 | AFZEAwoKCQ== 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /verify/testdata/milanvlek.testcer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | 0YPk 38 | -----END CERTIFICATE----- 39 | -----BEGIN CERTIFICATE----- 40 | MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC 41 | BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS 42 | BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg 43 | Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp 44 | Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy 45 | MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS 46 | BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j 47 | ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG 48 | 9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg 49 | W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta 50 | 1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2 51 | SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0 52 | 60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05 53 | gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg 54 | bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs 55 | +gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi 56 | Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ 57 | eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18 58 | fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j 59 | WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI 60 | rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG 61 | KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG 62 | SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI 63 | AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel 64 | ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw 65 | STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK 66 | dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq 67 | zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp 68 | KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e 69 | pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq 70 | HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh 71 | 3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn 72 | JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH 73 | CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4 74 | AFZEAwoKCQ== 75 | -----END CERTIFICATE----- 76 | -------------------------------------------------------------------------------- /verify/testdata/testdata.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package testdata provides embedded binaries of example ABI material. 16 | package testdata 17 | 18 | import _ "embed" 19 | 20 | // These certificates are committed regardless of its expiration date since we adjust the 21 | // CurrentTime to compare against so that the validity with respect to time is always true. 22 | 23 | // VcekBytes is an example VCEK certificate as issued by the AMD KDS. 24 | // 25 | //go:embed vcek.testcer 26 | var VcekBytes []byte 27 | 28 | // MilanVcekBytes is the Milan product vcek cert_chain as issued by the AMD KDS. 29 | // 30 | // Deprecated: Use trust.AskArkMilanVcekBytes 31 | // 32 | //go:embed milan.testcer 33 | var MilanVcekBytes []byte 34 | 35 | // MilanVlekBytes is the Milan product vlek cert_chain as issued by the AMD KDS. 36 | // 37 | // Deprecated: Use trust.AskArkMilanVlekBytes 38 | // 39 | //go:embed milanvlek.testcer 40 | var MilanVlekBytes []byte 41 | 42 | // AttestationBytes is an example attestation report from a VM that was 43 | // launched without an ID_BLOCK. 44 | // 45 | //go:embed attestation.bin 46 | var AttestationBytes []byte 47 | -------------------------------------------------------------------------------- /verify/testdata/vcek.testcer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/go-sev-guest/b60b35cc8d0330af09023824d110e28b81e61f60/verify/testdata/vcek.testcer -------------------------------------------------------------------------------- /verify/trust/ask_ark_genoa.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -----BEGIN CERTIFICATE----- 39 | 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 73 | /wMz1R1BHg== 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /verify/trust/ask_ark_genoa_vlek.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | 2cko 38 | -----END CERTIFICATE----- 39 | -----BEGIN CERTIFICATE----- 40 | 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 74 | /wMz1R1BHg== 75 | -----END CERTIFICATE----- 76 | -------------------------------------------------------------------------------- /verify/trust/ask_ark_milan.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -----BEGIN CERTIFICATE----- 39 | MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC 40 | BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS 41 | BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg 42 | Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp 43 | Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy 44 | MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS 45 | BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j 46 | ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG 47 | 9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg 48 | W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta 49 | 1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2 50 | SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0 51 | 60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05 52 | gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg 53 | bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs 54 | +gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi 55 | Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ 56 | eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18 57 | fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j 58 | WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI 59 | rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG 60 | KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG 61 | SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI 62 | AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel 63 | ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw 64 | STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK 65 | dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq 66 | zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp 67 | KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e 68 | pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq 69 | HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh 70 | 3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn 71 | JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH 72 | CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4 73 | AFZEAwoKCQ== 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /verify/trust/ask_ark_milan_vlek.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | 0YPk 38 | -----END CERTIFICATE----- 39 | -----BEGIN CERTIFICATE----- 40 | MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC 41 | BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS 42 | BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg 43 | Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp 44 | Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy 45 | MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS 46 | BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j 47 | ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG 48 | 9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg 49 | W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta 50 | 1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2 51 | SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0 52 | 60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05 53 | gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg 54 | bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs 55 | +gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi 56 | Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ 57 | eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18 58 | fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j 59 | WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI 60 | rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG 61 | KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG 62 | SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI 63 | AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel 64 | ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw 65 | STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK 66 | dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq 67 | zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp 68 | KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e 69 | pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq 70 | HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh 71 | 3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn 72 | JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH 73 | CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4 74 | AFZEAwoKCQ== 75 | -----END CERTIFICATE----- 76 | -------------------------------------------------------------------------------- /verify/trust/ask_ark_turin_vcek.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -----BEGIN CERTIFICATE----- 39 | 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 73 | zpacMwFusA== 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /verify/trust/ask_ark_turin_vlek.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | SUxr 38 | -----END CERTIFICATE----- 39 | -----BEGIN CERTIFICATE----- 40 | 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 74 | zpacMwFusA== 75 | -----END CERTIFICATE----- 76 | 77 | -------------------------------------------------------------------------------- /verify/trust/trust_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package trust_test 16 | 17 | import ( 18 | "bytes" 19 | "context" 20 | "errors" 21 | "testing" 22 | "time" 23 | 24 | "github.com/google/go-sev-guest/abi" 25 | test "github.com/google/go-sev-guest/testing" 26 | "github.com/google/go-sev-guest/verify/trust" 27 | ) 28 | 29 | func TestRetryHTTPSGetter(t *testing.T) { 30 | testCases := map[string]struct { 31 | getter *test.Getter 32 | timeout time.Duration 33 | maxRetryDelay time.Duration 34 | }{ 35 | "immediate success": { 36 | getter: &test.Getter{ 37 | Responses: map[string][]test.GetResponse{ 38 | "https://fetch.me": { 39 | { 40 | Occurrences: 1, 41 | Body: []byte("content"), 42 | Error: nil, 43 | }, 44 | }, 45 | }, 46 | }, 47 | timeout: time.Second, 48 | maxRetryDelay: time.Millisecond, 49 | }, 50 | "second success": { 51 | getter: &test.Getter{ 52 | Responses: map[string][]test.GetResponse{ 53 | "https://fetch.me": { 54 | { 55 | Occurrences: 1, 56 | Body: []byte(""), 57 | Error: errors.New("fail"), 58 | }, 59 | { 60 | Occurrences: 1, 61 | Body: []byte("content"), 62 | Error: nil, 63 | }, 64 | }, 65 | }, 66 | }, 67 | timeout: time.Second, 68 | maxRetryDelay: time.Millisecond, 69 | }, 70 | "third success": { 71 | getter: &test.Getter{ 72 | Responses: map[string][]test.GetResponse{ 73 | "https://fetch.me": { 74 | { 75 | Occurrences: 2, 76 | Body: []byte(""), 77 | Error: errors.New("fail"), 78 | }, 79 | { 80 | Occurrences: 1, 81 | Body: []byte("content"), 82 | Error: nil, 83 | }, 84 | }, 85 | }, 86 | }, 87 | timeout: time.Second, 88 | maxRetryDelay: time.Millisecond, 89 | }, 90 | } 91 | 92 | for name, tc := range testCases { 93 | t.Run(name, func(t *testing.T) { 94 | r := &trust.RetryHTTPSGetter{ 95 | Timeout: tc.timeout, 96 | MaxRetryDelay: tc.maxRetryDelay, 97 | Getter: tc.getter, 98 | } 99 | 100 | body, err := r.Get("https://fetch.me") 101 | if !bytes.Equal(body, []byte("content")) { 102 | t.Errorf("expected '%s' but got '%s'", "content", body) 103 | } 104 | if err != nil { 105 | t.Errorf("expected no error, but got %s", err.Error()) 106 | } 107 | tc.getter.Done(t) 108 | }) 109 | } 110 | } 111 | 112 | func TestRetryHTTPSGetterAllFail(t *testing.T) { 113 | testGetter := &test.Getter{ 114 | Responses: map[string][]test.GetResponse{ 115 | "https://fetch.me": { 116 | { 117 | Occurrences: 1, 118 | Body: []byte(""), 119 | Error: errors.New("fail"), 120 | }, 121 | }, 122 | }, 123 | } 124 | r := &trust.RetryHTTPSGetter{ 125 | Timeout: 1 * time.Millisecond, 126 | MaxRetryDelay: 1 * time.Millisecond, 127 | Getter: testGetter, 128 | } 129 | 130 | body, err := r.Get("https://fetch.me") 131 | if !bytes.Equal(body, []byte("")) { 132 | t.Errorf("expected empty body but got %q", body) 133 | } 134 | if err == nil { 135 | t.Errorf("expected error, but got none") 136 | } 137 | testGetter.Done(t) 138 | } 139 | 140 | func TestRetryHTTPSGetterContext(t *testing.T) { 141 | testGetter := &test.Getter{ 142 | Responses: map[string][]test.GetResponse{ 143 | "https://fetch.me": { 144 | { 145 | Occurrences: 1, 146 | Body: []byte("content"), 147 | Error: nil, 148 | }, 149 | }, 150 | }, 151 | } 152 | r := &trust.RetryHTTPSGetter{ 153 | MaxRetryDelay: 1 * time.Millisecond, 154 | Getter: testGetter, 155 | } 156 | 157 | ctx, cancel := context.WithCancel(context.Background()) 158 | cancel() 159 | body, err := r.GetContext(ctx, "https://fetch.me") 160 | if !bytes.Equal(body, []byte("")) { 161 | t.Errorf("expected empty body but got %q", body) 162 | } 163 | if !errors.Is(err, context.Canceled) { 164 | t.Errorf("expected error %q, but got %q", context.Canceled, err) 165 | } 166 | } 167 | 168 | func TestGetProductChainForRaceDetector(t *testing.T) { 169 | testGetter := &test.Getter{ 170 | Responses: map[string][]test.GetResponse{ 171 | "https://kdsintf.amd.com/vcek/v1/test/cert_chain": { 172 | { 173 | Occurrences: 2, 174 | Body: trust.AskArkMilanVcekBytes, 175 | }, 176 | }, 177 | }, 178 | } 179 | 180 | // run GetProductChain concurrently to see if the race detector is triggered. 181 | errCh := make(chan error) 182 | go func() { 183 | _, err := trust.GetProductChain("test", abi.VcekReportSigner, testGetter) 184 | errCh <- err 185 | }() 186 | 187 | go func() { 188 | _, err := trust.GetProductChain("test", abi.VcekReportSigner, testGetter) 189 | errCh <- err 190 | }() 191 | 192 | var err error 193 | for i := 0; i < 2; i++ { 194 | err = errors.Join(err, <-errCh) 195 | } 196 | if err != nil { 197 | t.Fatalf("unexpected error: %v", err) 198 | } 199 | } 200 | 201 | type recordingGetter struct { 202 | getCalls int 203 | } 204 | 205 | func (r *recordingGetter) Get(_ string) ([]byte, error) { 206 | r.getCalls++ 207 | return []byte{}, nil 208 | } 209 | 210 | type recordingContextGetter struct { 211 | recordingGetter 212 | getContextCalls int 213 | } 214 | 215 | func (r *recordingContextGetter) GetContext(_ context.Context, _ string) ([]byte, error) { 216 | r.getContextCalls++ 217 | return []byte{}, nil 218 | } 219 | 220 | func TestGetWith(t *testing.T) { 221 | url := "" 222 | t.Run("HTTPSGetter uses Get", func(t *testing.T) { 223 | contextGetter := recordingContextGetter{} 224 | if _, err := trust.GetWith(context.Background(), &contextGetter.recordingGetter, url); err != nil { 225 | t.Fatalf("trust.GetWith returned an unexpected error: %v", err) 226 | } 227 | if contextGetter.getContextCalls != 0 { 228 | t.Errorf("wrong number of calls to GetContext: got %d, want 0", contextGetter.getContextCalls) 229 | } 230 | if contextGetter.recordingGetter.getCalls != 1 { 231 | t.Errorf("wrong number of calls to Get: got %d, want 1", contextGetter.getCalls) 232 | } 233 | }) 234 | t.Run("ContextHTTPSGetter uses GetContext", func(t *testing.T) { 235 | contextGetter := recordingContextGetter{} 236 | if _, err := trust.GetWith(context.Background(), &contextGetter, url); err != nil { 237 | t.Fatalf("trust.GetWith returned an unexpected error: %v", err) 238 | } 239 | if contextGetter.getContextCalls != 1 { 240 | t.Errorf("wrong number of calls to GetContext: got %d, want 1", contextGetter.getContextCalls) 241 | } 242 | if contextGetter.recordingGetter.getCalls != 0 { 243 | t.Errorf("wrong number of calls to Get: got %d, want 0", contextGetter.getCalls) 244 | } 245 | }) 246 | 247 | } 248 | 249 | // Ensure that the HTTPSGetters implement the expected interfaces. 250 | var ( 251 | _ = trust.HTTPSGetter(&trust.SimpleHTTPSGetter{}) 252 | _ = trust.HTTPSGetter(&trust.RetryHTTPSGetter{}) 253 | _ = trust.ContextHTTPSGetter(&trust.SimpleHTTPSGetter{}) 254 | _ = trust.ContextHTTPSGetter(&trust.RetryHTTPSGetter{}) 255 | ) 256 | --------------------------------------------------------------------------------