├── src ├── pytdxmeasure │ ├── __init__.py │ ├── pytdxmeasure │ │ ├── __init__.py │ │ ├── rtmr.py │ │ ├── cli.py │ │ ├── binaryblob.py │ │ ├── ccel.py │ │ ├── actor.py │ │ ├── tdreport.py │ │ ├── tdeventlog.py │ │ └── utility.py │ ├── requirements.txt │ ├── tdx_tdreport │ ├── tdx_verify_rtmr │ ├── tdx_eventlogs │ ├── setup.py │ ├── README.md │ └── .gitignore ├── cvm-image-rewriter │ ├── plugins │ │ ├── 97-sample │ │ │ ├── files │ │ │ │ └── etc │ │ │ │ │ └── test │ │ │ ├── pre-stage │ │ │ │ └── host_run.sh │ │ │ ├── post-stage │ │ │ │ ├── clean_up.sh │ │ │ │ └── host_run.sh │ │ │ ├── cloud-init │ │ │ │ ├── x-shellscript │ │ │ │ │ └── 02-ima-register-file-hash.sh │ │ │ │ └── cloud-config │ │ │ │ │ └── 99-example.yaml │ │ │ └── README.md │ │ ├── 98-ima-enable-simple │ │ │ ├── cloud-init │ │ │ │ └── x-shellscript │ │ │ │ │ └── .placehoder │ │ │ ├── post-stage │ │ │ │ └── clean_up.sh │ │ │ ├── pre-stage │ │ │ │ ├── file_list │ │ │ │ ├── guest_enable_ima_fix.sh │ │ │ │ └── host_run.sh │ │ │ ├── files │ │ │ │ └── etc │ │ │ │ │ └── ima │ │ │ │ │ └── ima-policy │ │ │ └── README.md │ │ ├── 06-install-tdx-guest-kernel │ │ │ ├── pre-stage │ │ │ │ ├── artifacts │ │ │ │ │ └── .placeholder │ │ │ │ └── host_run.sh │ │ │ ├── post-stage │ │ │ │ └── clean_up.sh │ │ │ └── README.md │ │ ├── 60-initrd-update │ │ │ ├── files │ │ │ │ └── opt │ │ │ │ │ └── example.txt │ │ │ ├── post-stage │ │ │ │ └── clean_up.sh │ │ │ ├── README.md │ │ │ └── pre-stage │ │ │ │ └── host_run.sh │ │ ├── 05-readonly-data │ │ │ ├── files │ │ │ │ └── etc │ │ │ │ │ └── readonly_file_demo │ │ │ ├── post-stage │ │ │ │ └── clean_up.sh │ │ │ ├── README.md │ │ │ └── pre-stage │ │ │ │ ├── file_list │ │ │ │ └── host_run.sh │ │ ├── 08-ccnp-uds-directory-permission │ │ │ ├── files │ │ │ │ └── usr │ │ │ │ │ └── lib │ │ │ │ │ └── tmpfiles.d │ │ │ │ │ └── ccnp.conf │ │ │ └── README.md │ │ ├── 07-device-permission │ │ │ ├── files │ │ │ │ └── etc │ │ │ │ │ └── udev │ │ │ │ │ └── rules.d │ │ │ │ │ └── 90-tdx.rules │ │ │ └── README.md │ │ ├── 04-user-authkey │ │ │ ├── post-stage │ │ │ │ └── clean_up.sh │ │ │ ├── README.md │ │ │ └── pre-stage │ │ │ │ └── host_run.sh │ │ ├── 02-motd-welcome │ │ │ ├── README.md │ │ │ └── files │ │ │ │ └── etc │ │ │ │ └── motd │ │ ├── 03-netplan │ │ │ ├── files │ │ │ │ └── etc │ │ │ │ │ └── netplan │ │ │ │ │ └── netplan.yaml │ │ │ └── README.md │ │ ├── 01-resize-image │ │ │ ├── README.md │ │ │ └── pre-stage │ │ │ │ └── host_run.sh │ │ └── 99-byebye │ │ │ └── post-stage │ │ │ └── host_run.sh │ ├── .gitignore │ ├── cloud-init │ │ ├── README.md │ │ ├── meta-data.template │ │ └── user-data.basic │ ├── scripts │ │ └── common.sh │ ├── tdx-libvirt-ubuntu-host.xml.template │ ├── virt-test.sh │ └── README.md ├── python │ ├── requirements.txt │ ├── cctrusted_vm │ │ ├── __init__.py │ │ ├── tdx.py │ │ └── sdk.py │ ├── td_report_cli.py │ ├── pyproject.toml │ ├── cc_imr_cli.py │ ├── README.md │ ├── .gitignore │ ├── cc_event_log_cli.py │ ├── cc_quote_cli.py │ └── tests │ │ ├── conftest.py │ │ ├── test_sdk.py │ │ └── tdx_check.py ├── rust │ ├── cctrusted_vm │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── cvm.rs │ │ ├── deny.toml │ │ └── Cargo.toml │ ├── sample │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── cc-sample-eventlog.rs │ │ │ ├── cc-sample-measurement.rs │ │ │ └── cc-sample-quote.rs │ └── README.md └── golang │ ├── cctrusted_vm │ ├── cmd │ │ ├── Makefile │ │ ├── main.go │ │ └── app │ │ │ ├── cvm.go │ │ │ ├── imr.go │ │ │ ├── report.go │ │ │ ├── root.go │ │ │ └── eventlog.go │ ├── tdx │ │ ├── event_recorder_test.go │ │ ├── device_test.go │ │ ├── cvm.go │ │ ├── device.go │ │ ├── event_recorder.go │ │ └── quote_handler.go │ ├── sdk │ │ ├── sdk_test.go │ │ └── sdk.go │ ├── go.mod │ ├── go.sum │ └── cvm.go │ └── README.md ├── .gitignore ├── docs ├── imr-cli-output.png ├── cvm-customizations.png ├── cvm-image-rewriter-flow.png ├── google_tdx_tpm_dump_imr.png ├── google_tdx_tpm_dump_quote.png ├── cc-trusted-primitives-vendor.png └── google_tdx_tpm_dump_eventlog.png ├── .github ├── workflows │ ├── doclint.yaml │ ├── pylicense.yaml │ ├── publish-pypi-packages.yaml │ ├── pylint.yaml │ ├── pr-check-rust.yaml │ ├── vmsdk-test-rust.yaml │ └── vmsdk-test-python.yaml ├── .license_check.ini └── cspell.json ├── setupenv.sh └── README.md /src/pytdxmeasure/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/golang/cctrusted_vm/cmd/bin -------------------------------------------------------------------------------- /src/pytdxmeasure/pytdxmeasure/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/97-sample/files/etc/test: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pytdxmeasure/requirements.txt: -------------------------------------------------------------------------------- 1 | wheel 2 | setuptools 3 | -------------------------------------------------------------------------------- /src/python/requirements.txt: -------------------------------------------------------------------------------- 1 | evidence_api 2 | pytest 3 | tpm2-pytss 4 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/.gitignore: -------------------------------------------------------------------------------- 1 | *.img 2 | *.qcow2 3 | NOT_RUN 4 | *.log 5 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/cloud-init/README.md: -------------------------------------------------------------------------------- 1 | # Cloud-init script and configs -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/98-ima-enable-simple/cloud-init/x-shellscript/.placehoder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/rust/cctrusted_vm/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod cvm; 2 | pub mod sdk; 3 | pub mod tdvm; 4 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/06-install-tdx-guest-kernel/pre-stage/artifacts/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/60-initrd-update/files/opt/example.txt: -------------------------------------------------------------------------------- 1 | example file copied to initrd -------------------------------------------------------------------------------- /docs/imr-cli-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cc-api/cc-trusted-vmsdk/HEAD/docs/imr-cli-output.png -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/05-readonly-data/files/etc/readonly_file_demo: -------------------------------------------------------------------------------- 1 | This is a readonly file demo. -------------------------------------------------------------------------------- /docs/cvm-customizations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cc-api/cc-trusted-vmsdk/HEAD/docs/cvm-customizations.png -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/97-sample/pre-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "pre-stage host_run \"haha\" " -------------------------------------------------------------------------------- /docs/cvm-image-rewriter-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cc-api/cc-trusted-vmsdk/HEAD/docs/cvm-image-rewriter-flow.png -------------------------------------------------------------------------------- /docs/google_tdx_tpm_dump_imr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cc-api/cc-trusted-vmsdk/HEAD/docs/google_tdx_tpm_dump_imr.png -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/97-sample/post-stage/clean_up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "post-stage clean_up \"haha\"" -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/97-sample/post-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "post-stage host_run \"haha\"" -------------------------------------------------------------------------------- /docs/google_tdx_tpm_dump_quote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cc-api/cc-trusted-vmsdk/HEAD/docs/google_tdx_tpm_dump_quote.png -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/08-ccnp-uds-directory-permission/files/usr/lib/tmpfiles.d/ccnp.conf: -------------------------------------------------------------------------------- 1 | D /run/ccnp/uds 0757 - - - -------------------------------------------------------------------------------- /docs/cc-trusted-primitives-vendor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cc-api/cc-trusted-vmsdk/HEAD/docs/cc-trusted-primitives-vendor.png -------------------------------------------------------------------------------- /docs/google_tdx_tpm_dump_eventlog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cc-api/cc-trusted-vmsdk/HEAD/docs/google_tdx_tpm_dump_eventlog.png -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/07-device-permission/files/etc/udev/rules.d/90-tdx.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="misc",KERNEL=="tdx_guest",MODE="0666" -------------------------------------------------------------------------------- /src/pytdxmeasure/tdx_tdreport: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pytdxmeasure.cli import TDXTDReportCmd 4 | 5 | TDXTDReportCmd().run() 6 | -------------------------------------------------------------------------------- /src/pytdxmeasure/tdx_verify_rtmr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pytdxmeasure.cli import TDXVerifyCmd 4 | 5 | TDXVerifyCmd().run() 6 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/cmd/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | all: 4 | mkdir bin 5 | go build -o bin/vm-tool main.go 6 | clean: 7 | rm -rf bin 8 | -------------------------------------------------------------------------------- /src/pytdxmeasure/tdx_eventlogs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # shellcheck disable=SC1071 3 | 4 | from pytdxmeasure.cli import TDXEventLogsCmd 5 | 6 | TDXEventLogsCmd().run() 7 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/97-sample/cloud-init/x-shellscript/02-ima-register-file-hash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "=========== example for customized x-shellscript ===========" 4 | -------------------------------------------------------------------------------- /src/python/cctrusted_vm/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Package to provide Evidence API for confidential VM 3 | """ 4 | 5 | # pylint: disable=syntax-error 6 | from cctrusted_vm.sdk import* 7 | from cctrusted_vm.tdx import * 8 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/cloud-init/meta-data.template: -------------------------------------------------------------------------------- 1 | # date '+%Y-%m-%d-%H-%M-%S' 2 | # replace by "tdx-inst-Yr-Mon-Day-Hour-Min-Sec" 3 | instance-id: 4 | 5 | local-hostname: tdx-guest 6 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/04-user-authkey/post-stage/clean_up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname "$(readlink -f "$0")") 4 | CLD_DIR="$DIR/../cloud-init" 5 | 6 | if [[ -d "$CLD_DIR" ]]; then 7 | rm -rf "$CLD_DIR" 8 | fi -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/05-readonly-data/post-stage/clean_up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname "$(readlink -f "$0")") 4 | CLD_DIR="$DIR/../cloud-init" 5 | 6 | if [[ -d "$CLD_DIR" ]]; then 7 | rm -rf "$CLD_DIR" 8 | fi -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/60-initrd-update/post-stage/clean_up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname "$(readlink -f "$0")") 4 | ETC_DIR="$DIR/../files/etc" 5 | 6 | if [[ -d "$ETC_DIR" ]]; then 7 | rm -rf "$ETC_DIR" 8 | fi -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/06-install-tdx-guest-kernel/post-stage/clean_up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname "$(readlink -f "$0")") 4 | CLD_DIR="$DIR/../cloud-init" 5 | 6 | if [[ -d "$CLD_DIR" ]]; then 7 | rm -rf "$CLD_DIR" 8 | fi -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/07-device-permission/README.md: -------------------------------------------------------------------------------- 1 | # Device permission 2 | 3 | This plugin is used to setup udev rules for CCNP device plugin. It is typically not required for users to customize unless additional udev rules are needed. -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/05-readonly-data/README.md: -------------------------------------------------------------------------------- 1 | # Readonly data 2 | 3 | This plugin is used to fix some file permissions to read-only. Users can specify the names of files and directories that need to be fixed in the `pre-stage/file_list`. -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/cmd/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2024 NAME HERE 3 | */ 4 | package main 5 | 6 | import "github.com/cc-api/cc-trusted-vmsdk/src/golang/cctrusted_vm/cmd/app" 7 | 8 | func main() { 9 | app.Execute() 10 | } 11 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/60-initrd-update/README.md: -------------------------------------------------------------------------------- 1 | # Initrd update 2 | 3 | This plugins is used to update the initrd image. To change the initrd image, place the necessary files in the `files/` directory. The plugin will copy these files to the initramfs. -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/02-motd-welcome/README.md: -------------------------------------------------------------------------------- 1 | # Motd welcome 2 | 3 | This plugin is used to set the information in `/etc/motd`, which will be displayed after successful login. Users can customize the welcome information by updating the content of `files/etc/motd`. -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/03-netplan/files/etc/netplan/netplan.yaml: -------------------------------------------------------------------------------- 1 | network: 2 | version: 2 3 | renderer: networkd 4 | ethernets: 5 | en: 6 | match: 7 | name: "en*" 8 | dhcp4: true 9 | dhcp-identifier: mac -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/08-ccnp-uds-directory-permission/README.md: -------------------------------------------------------------------------------- 1 | # CCNP UDS directory permission 2 | 3 | This plugin is used to prepare shared unix domain socket directories for CCNP. It is typically not required for users to customize unless additional Unix domain socket directories are needed. -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/03-netplan/README.md: -------------------------------------------------------------------------------- 1 | # Netplan 2 | 3 | This plugin is used to add network configuration file `netplan.yaml` in the `/etc/netplan/` directory, which will configure the network for the guest image. Users can customize `netplan.yaml` by updating the contents of `files/etc/netplan/netplan.yaml` -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/97-sample/cloud-init/cloud-config/99-example.yaml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | 3 | merge_how: 4 | - name: list 5 | settings: [append] 6 | - name: dict 7 | settings: [no_replace, recurse_list] 8 | 9 | runcmd: 10 | - echo "===========example for customized cloud-config ===========" -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/01-resize-image/README.md: -------------------------------------------------------------------------------- 1 | # Resize the image 2 | 3 | This plugin is used to extend the guest image size to given value from the 4 | environment variable `${GUEST_SIZE}`. 5 | 6 | If `${GUEST_SIZE}` is not specified like below, then this plugin will be skipped. 7 | ``` 8 | export GUEST_SIZE=50G 9 | ``` -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/98-ima-enable-simple/post-stage/clean_up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname "$(readlink -f "$0")") 4 | CLD_SH_REGISTER_FILE_HASH="01-ima-register-file-hash.sh" 5 | CLD_SH="$DIR/../cloud-init/x-shellscript/$CLD_SH_REGISTER_FILE_HASH" 6 | 7 | if [[ -f "$CLD_SH" ]]; then 8 | rm "$CLD_SH" 9 | fi -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/99-byebye/post-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURR_DIR=$(dirname "$(readlink -f "$0")") 4 | TOP_DIR="${CURR_DIR}/../../../" 5 | SCRIPTS_DIR="${TOP_DIR}/scripts" 6 | 7 | # shellcheck disable=SC1091 8 | source "${SCRIPTS_DIR}/common.sh" 9 | 10 | ok "Success to create guest image ${GUEST_IMG}..." 11 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/cmd/app/cvm.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import sdk "github.com/cc-api/cc-trusted-vmsdk/src/golang/cctrusted_vm/sdk" 4 | 5 | var instance sdk.SDK 6 | var cvmErr error 7 | 8 | func GetSDK() (sdk.SDK, error) { 9 | return instance, cvmErr 10 | } 11 | 12 | func init() { 13 | instance, cvmErr = sdk.GetSDKInstance(nil) 14 | } 15 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/tdx/event_recorder_test.go: -------------------------------------------------------------------------------- 1 | package tdx 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestEventLogRecoder(t *testing.T) { 10 | r := &TDXEventLogRecorder{} 11 | r.ProbeRecorder() 12 | l, err := r.FullEventLog() 13 | assert.Nil(t, err) 14 | assert.NotNil(t, l) 15 | } 16 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/02-motd-welcome/files/etc/motd: -------------------------------------------------------------------------------- 1 | 2 | =================================================== 3 | Welcome to Confidential Computing Guest Environment 4 | 5 | This image is optimized for Confidential Cloud Native Foundation. 6 | Please visits https://github.com/intel/confidential-cloud-native-primitives. 7 | =================================================== 8 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/05-readonly-data/pre-stage/file_list: -------------------------------------------------------------------------------- 1 | # This config contains the files that should be readonly 2 | # In the file: 3 | # - an empty line is allowd. 4 | # - comment lines are started with '#'. 5 | # - a line can be a certain file. 6 | # - a line can be a diretory, which MUST ends with '/'. 7 | 8 | # a file line example 9 | /etc/readonly_file_demo 10 | 11 | # directories example 12 | # /opt/ -------------------------------------------------------------------------------- /src/golang/README.md: -------------------------------------------------------------------------------- 1 | # VMSDK 2 | 3 | ## Sample Tool 4 | 5 | Build the sample toll 6 | 7 | ``` 8 | make -C cctrusted_vm/cmd 9 | ls cctrusted_vm/cmd/bin/vm-tool 10 | ``` 11 | 12 | Get CC Report. 13 | 14 | ``` 15 | vm-tool report 16 | ``` 17 | 18 | Get Event log. 19 | 20 | ``` 21 | vm-tool eventlog dump 22 | ``` 23 | 24 | Get IMR 25 | 26 | ``` 27 | vm-tool imr 28 | ``` 29 | 30 | Show more demos 31 | 32 | ``` 33 | vm-tool help 34 | ``` 35 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/97-sample/README.md: -------------------------------------------------------------------------------- 1 | # Sample 2 | 3 | This is a sample plugin to show the structure of the plugin and how to customize it. 4 | 5 | Example Output: 6 | ``` 7 | # output of the cloud-init 8 | =========== example for customized x-shellscript =========== 9 | 10 | # output of the pre-stage 11 | pre-stage host_run "haha" 12 | 13 | # output of the post-stage 14 | post-stage host_run "haha" 15 | post-stage clean_up "haha" 16 | ``` 17 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/98-ima-enable-simple/pre-stage/file_list: -------------------------------------------------------------------------------- 1 | # This config contains the files that should be labelled with hash 2 | # In the file: 3 | # - an empty line is allowd. 4 | # - comment lines are started with '#'. 5 | # - a line can be a certain file. 6 | # - a line can be a diretory, which MUST ends with '/'. 7 | 8 | # a file line example 9 | /etc/ima/ima-policy 10 | 11 | # directories example 12 | /bin/ 13 | /dev/ 14 | /etc/ 15 | /usr/lib/ 16 | /usr/share/ 17 | #/run/ 18 | /var/lib/ 19 | -------------------------------------------------------------------------------- /.github/workflows/doclint.yaml: -------------------------------------------------------------------------------- 1 | name: Document Scan 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | scan_doc: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: streetsidesoftware/cspell-action@v2 16 | with: 17 | files: | 18 | **/*.md 19 | *.md 20 | config: .github/cspell.json 21 | verbose: true 22 | incremental_files_only: false 23 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/04-user-authkey/README.md: -------------------------------------------------------------------------------- 1 | # User authkey 2 | 3 | This plugin is used to set the SSH login user and public key using environment variables `${CVM_USER}` and `${CVM_AUTH_KEY}`. 4 | 5 | The default value of `${CVM_USER}` is "cvm", and users can customize it as shown below. 6 | ``` 7 | export CVM_USER= 8 | ``` 9 | 10 | The `${CVM_AUTH_KEY}` has no default value, users need to set it themselves. If `${CVM_AUTH_KEY}` is not specified like below, this plugin will be skipped. 11 | 12 | ``` 13 | export CVM_AUTH_KEY= 14 | ``` -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/01-resize-image/pre-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURR_DIR=$(dirname "$(readlink -f "$0")") 4 | TOP_DIR="${CURR_DIR}/../../../" 5 | SCRIPTS_DIR="${TOP_DIR}/scripts" 6 | # shellcheck disable=SC1091 7 | source "${SCRIPTS_DIR}/common.sh" 8 | 9 | info "Guest Image is at ${GUEST_IMG}..." 10 | 11 | if [[ -z ${GUEST_SIZE} ]]; then 12 | warn "SKIP: Guest size is not defined via environment variable 'GUEST_SIZE'" 13 | else 14 | qemu-img resize "${GUEST_IMG}" "${GUEST_SIZE}" 15 | virt-customize -a "${GUEST_IMG}" \ 16 | --run-command 'growpart /dev/sda 1' \ 17 | --run-command 'resize2fs /dev/sda1' 18 | fi 19 | -------------------------------------------------------------------------------- /src/rust/sample/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cctrusted-sample" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | 7 | [[bin]] 8 | name = "cc-sample-quote" 9 | path = "src/cc-sample-quote.rs" 10 | 11 | [[bin]] 12 | name = "cc-sample-measurement" 13 | path = "src/cc-sample-measurement.rs" 14 | 15 | [[bin]] 16 | name = "cc-sample-eventlog" 17 | path = "src/cc-sample-eventlog.rs" 18 | 19 | [dependencies] 20 | cctrusted_vm = { path = "../cctrusted_vm" } 21 | evidence_api = { git = "https://github.com/cc-api/evidence-api.git", branch = "main" } 22 | anyhow = "1.0" 23 | log = "0.4.20" 24 | env_logger = "0.10.1" 25 | base64 = "0.13.0" 26 | rand = "0.8.5" -------------------------------------------------------------------------------- /src/python/cctrusted_vm/tdx.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provide addtional TDVM SDK inherited from general CVM SDK. 3 | """ 4 | 5 | from evidence_api.tdx.report import TdReport 6 | from cctrusted_vm.sdk import CCTrustedVmSdk 7 | from cctrusted_vm.cvm import TdxVM 8 | 9 | class CCTrustedTdvmSdk(CCTrustedVmSdk): 10 | 11 | """Inherited SDK class for TDVM only.""" 12 | 13 | def get_tdreport(self) -> TdReport: 14 | """Get TDX Report 15 | 16 | TDREPORT data structure generated by TDG.MR.REPORT instruction in Intel TDX. 17 | 18 | Returns: 19 | TdReport object 20 | """ 21 | assert isinstance(self._cvm, TdxVM) 22 | return self._cvm.tdreport 23 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/60-initrd-update/pre-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname "$(readlink -f "$0")") 4 | TEMPLATE="#!/bin/sh\n\n. /usr/share/initramfs-tools/hook-functions\n" 5 | 6 | HOOKS_DIR="$DIR/../files/etc/initramfs-tools/hooks" 7 | SCRIPT_NAME=initrd-custom-update.sh 8 | SCRIPT_PATH=$HOOKS_DIR/$SCRIPT_NAME 9 | 10 | mkdir -p "$HOOKS_DIR" 11 | echo -e "$TEMPLATE" > "$SCRIPT_PATH" 12 | chmod a+x "$SCRIPT_PATH" 13 | 14 | mapfile -t files < <(find "$DIR/../files" -type f) 15 | for f in "${files[@]}" 16 | do 17 | if [[ $f == *$SCRIPT_NAME ]]; then 18 | continue 19 | fi 20 | path=${f#*/files} 21 | echo "copy_exec $path" >> "$SCRIPT_PATH" 22 | done 23 | 24 | -------------------------------------------------------------------------------- /src/rust/cctrusted_vm/deny.toml: -------------------------------------------------------------------------------- 1 | [advisories] 2 | vulnerability = "deny" 3 | unmaintained = "warn" 4 | yanked = "warn" 5 | notice = "warn" 6 | 7 | [licenses] 8 | unlicensed = "warn" 9 | allow = [ 10 | "MIT", 11 | "Apache-2.0", 12 | "ISC", 13 | "BSD-3-Clause", 14 | "Unicode-DFS-2016", 15 | ] 16 | 17 | copyleft = "warn" 18 | allow-osi-fsf-free = "neither" 19 | default = "deny" 20 | confidence-threshold = 0.8 21 | 22 | [[licenses.clarify]] 23 | name = "ring" 24 | expression = "MIT AND ISC AND OpenSSL" 25 | license-files = [ 26 | { path = "LICENSE", hash = 0xbd0eed23 } 27 | ] 28 | 29 | [bans] 30 | multiple-versions = "warn" 31 | wildcards = "allow" 32 | 33 | [sources] 34 | unknown-registry = "warn" 35 | unknown-git = "warn" -------------------------------------------------------------------------------- /.github/workflows/pylicense.yaml: -------------------------------------------------------------------------------- 1 | name: Python License Check 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'src/**/*.py' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | python-license-scan: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-python@v4 15 | - name: License Check 16 | run: | 17 | set -ex 18 | sudo su -c 'source setupenv.sh && \ 19 | python3 -m pip install liccheck && \ 20 | for f in $(find -type f -name "requirements.txt"); do 21 | python3 -m pip install -r $f; 22 | liccheck -s .github/.license_check.ini -r $f; 23 | done' 24 | -------------------------------------------------------------------------------- /src/pytdxmeasure/pytdxmeasure/rtmr.py: -------------------------------------------------------------------------------- 1 | """ 2 | RTMR data structures 3 | """ 4 | 5 | from .binaryblob import BinaryBlob 6 | 7 | 8 | class RTMR(BinaryBlob): 9 | """ 10 | Data structure for RTMR registers. 11 | A RTMR register manages a 48-bytes (384-bits) hash value. 12 | """ 13 | RTMR_COUNT = 4 14 | RTMR_LENGTH_BY_BYTES = 48 15 | 16 | def __init__(self, data: bytearray = bytearray(RTMR_LENGTH_BY_BYTES), 17 | base_addr=0): 18 | super().__init__(data, base_addr) 19 | 20 | def __eq__(self, other): 21 | bytearray_1, _ = self.get_bytes(0, RTMR.RTMR_LENGTH_BY_BYTES) 22 | bytearray_2, _ = other.get_bytes(0, RTMR.RTMR_LENGTH_BY_BYTES) 23 | 24 | return bytearray(bytearray_1) == bytearray(bytearray_2) 25 | -------------------------------------------------------------------------------- /src/rust/cctrusted_vm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cctrusted_vm" 3 | version = "0.4.0" 4 | edition = "2021" 5 | authors = ["Chen Hairong ", "Lu Ken "] 6 | repository = "https://github.com/cc-api/cc-trusted-vmsdk" 7 | description = "Evidence API VM SDK" 8 | license = "Apache-2.0" 9 | 10 | [lib] 11 | name = "cctrusted_vm" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | evidence_api = { git = "https://github.com/cc-api/evidence-api.git", branch = "main" } 16 | anyhow = "1.0" 17 | log = "0.4.20" 18 | nix = "0.26.2" 19 | base64 = "0.13.0" 20 | rand = "0.8.5" 21 | sha2 = "0.10" 22 | tempfile = "3.0" 23 | vsock = "0.4.0" 24 | tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } 25 | tokio-stream = "0.1.14" 26 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/cloud-init/user-data.basic: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | 3 | merge_how: 4 | - name: list 5 | settings: [append] 6 | - name: dict 7 | settings: [no_replace, recurse_list] 8 | 9 | power_state: 10 | delay: now 11 | mode: poweroff 12 | message: Bye Bye 13 | timeout: 1 14 | condition: True 15 | 16 | runcmd: 17 | - sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config 18 | - sed -i 's/console=tty1 console=ttyS0/console=hvc0/g' /etc/default/grub.d/50-cloudimg-settings.cfg 19 | # copy stuff into initrd 20 | - update-initramfs -u -k all 21 | # update grub for enabling ima 22 | - update-grub 23 | 24 | # Uncomment this section if you plan to change user/password 25 | # user: tdx 26 | # password: 123456 27 | # chpasswd: { expire: False } 28 | -------------------------------------------------------------------------------- /.github/workflows/publish-pypi-packages.yaml: -------------------------------------------------------------------------------- 1 | name: Publish the package to PyPI 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | 7 | jobs: 8 | publish_pypi: 9 | name: Publish package 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v3 14 | 15 | - name: Checkout action repository 16 | uses: actions/checkout@v3 17 | 18 | - name: Publish package 19 | env: 20 | PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }} 21 | PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 22 | run: | 23 | python3 -m pip install --upgrade twine build 24 | pushd src/python/ 25 | python3 -m build 26 | python3 -m twine upload -u $PYPI_USERNAME -p $PYPI_PASSWORD --repository pypi dist/* 27 | popd 28 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/scripts/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Common Scripts 4 | # 5 | 6 | info() { 7 | echo -e "\e[1;33m$*\e[0;0m" 8 | } 9 | 10 | ok() { 11 | echo -e "\e[1;32mSUCCESS: $*\e[0;0m" 12 | } 13 | 14 | error() { 15 | echo -e "\e[1;31mERROR: $*\e[0;0m" 16 | exit 1 17 | } 18 | 19 | warn() { 20 | echo -e "\e[1;33mWARN: $*\e[0;0m" 21 | } 22 | 23 | # Check whether the tools are installed from the provided list 24 | # 25 | # args: 26 | # array of tool name - the name list to be checked 27 | check_tools() { 28 | arr=("$@") 29 | is_missing=false 30 | for i in "${arr[@]}"; 31 | do 32 | [[ "$(command -v "$i")" ]] || { info "MISSING: $i is not installed" 1>&2 ; is_missing=true ;} 33 | done 34 | 35 | [[ $is_missing != true ]] || { error "Please install missing tools"; } 36 | } 37 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/sdk/sdk_test.go: -------------------------------------------------------------------------------- 1 | package vmsdk 2 | 3 | import ( 4 | "bytes" 5 | "log" 6 | "testing" 7 | 8 | "github.com/cc-api/evidence-api/common/golang/evidence_api" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestSDKReport(t *testing.T) { 14 | var buf bytes.Buffer 15 | log.SetOutput(&buf) 16 | 17 | sdk, err := GetSDKInstance(nil) 18 | assert.Nil(t, err) 19 | report, err := sdk.GetCCReport("", "", nil) 20 | assert.Nil(t, err) 21 | report.Dump(evidence_api.QuoteDumpFormatHuman) 22 | 23 | } 24 | 25 | func TestSDKFullEventLog(t *testing.T) { 26 | var buf bytes.Buffer 27 | log.SetOutput(&buf) 28 | 29 | sdk, err := GetSDKInstance(nil) 30 | assert.Nil(t, err) 31 | 32 | el, err := sdk.GetCCEventLog(0, 0) 33 | assert.Nil(t, err) 34 | el.Dump(evidence_api.QuoteDumpFormatHuman) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /.github/.license_check.ini: -------------------------------------------------------------------------------- 1 | # Authorized and unauthorized licenses in LOWER CASE 2 | [Licenses] 3 | authorized_licenses: 4 | 3-clause bsd 5 | apache 6 | apache 2.0 7 | apache software 8 | apache software license 9 | apache-2.0 10 | bsd 11 | bsd license 12 | GNU Lesser General Public License v2 or later (LGPLv2+) 13 | gnu lgpl 14 | isc license 15 | isc license (iscl) 16 | lgpl with exceptions or zpl 17 | mit 18 | mit license 19 | mozilla public license 2.0 (mpl 2.0) 20 | MPL-2.0 21 | new bsd 22 | new bsd license 23 | python software foundation 24 | python software foundation license 25 | simplified bsd 26 | zpl 2.1 27 | 28 | unauthorized_licenses: 29 | gpl v3 30 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cc-api/cc-trusted-vmsdk/src/golang/cctrusted_vm 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | github.com/mdlayher/vsock v1.2.1 7 | github.com/stretchr/testify v1.8.4 8 | ) 9 | 10 | require ( 11 | github.com/mdlayher/socket v0.4.1 // indirect 12 | golang.org/x/net v0.9.0 // indirect 13 | golang.org/x/sync v0.1.0 // indirect 14 | golang.org/x/sys v0.7.0 // indirect 15 | ) 16 | 17 | require ( 18 | github.com/cc-api/evidence-api/common/golang/evidence_api v0.0.0-20240729064808-21e12aa810c8 //indirect 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | github.com/spf13/cobra v1.8.0 23 | github.com/spf13/pflag v1.0.5 // indirect 24 | gopkg.in/yaml.v3 v3.0.1 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/tdx/device_test.go: -------------------------------------------------------------------------------- 1 | package tdx 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "testing" 8 | 9 | "github.com/cc-api/evidence-api/common/golang/evidence_api/tdx" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestReport15(t *testing.T) { 15 | var buf bytes.Buffer 16 | log.SetOutput(&buf) 17 | 18 | spec := tdx.TdxDeviceSpecs[tdx.TDX_VERSION_1_5_DEVICE] 19 | device := TDXDevice{spec: spec} 20 | res := device.ProbeDevice() 21 | t.Log(buf.String()) 22 | fmt.Println(res) 23 | assert.Equal(t, true, res) 24 | 25 | nonce := []byte{"IXUKoBO1UM3c1wopN4sY"} 26 | userData := []byte{"MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4"} 27 | tdreport, err := device.TdReport(nonce, userData) 28 | assert.Nil(t, err) 29 | t.Log(tdreport) 30 | 31 | quote, err := device.Quote(tdreport) 32 | assert.Nil(t, err) 33 | assert.NotNil(t, quote) 34 | } 35 | -------------------------------------------------------------------------------- /src/python/td_report_cli.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Command line to dump the integrated measurement register 4 | """ 5 | import logging 6 | import os 7 | from evidence_api.api import EvidenceApi 8 | from cctrusted_vm.cvm import ConfidentialVM 9 | from cctrusted_vm.tdx import CCTrustedTdvmSdk 10 | 11 | LOG = logging.getLogger(__name__) 12 | 13 | logging.basicConfig(level=logging.NOTSET, format="%(name)s %(levelname)-8s %(message)s") 14 | 15 | def main(): 16 | """Example to call get_tdreport and dump the result to stdout.""" 17 | if ConfidentialVM.detect_cc_type() != EvidenceApi.TYPE_CC_TDX: 18 | LOG.error("This is not a TD VM!") 19 | return 20 | if os.geteuid() != 0: 21 | LOG.error("Please run as root which is required for this example!") 22 | return 23 | 24 | tdreport = CCTrustedTdvmSdk.inst().get_tdreport() 25 | tdreport.dump() 26 | 27 | if __name__ == "__main__": 28 | main() 29 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/05-readonly-data/pre-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname "$(readlink -f "$0")") 4 | FILE_LIST="$DIR/file_list" 5 | CLD_SH_READONLY_FILE="01-file-readonly.sh" 6 | CLD_SH="$DIR/../cloud-init/x-shellscript/$CLD_SH_READONLY_FILE" 7 | CLD_SH_TEMPLATE="" 8 | injects="" 9 | 10 | read -r -d '' CLD_SH_TEMPLATE << EOM 11 | #!/bin/bash 12 | # replaced by required files 13 | PLACEHOLDER 14 | EOM 15 | 16 | # filter specified files & directories 17 | while IFS= read -r line || [ -n "$line" ]; do 18 | if [[ $line == "#"* ]] || [[ $line == "" ]]; then 19 | continue 20 | fi 21 | if [[ $line == *"/" ]]; then 22 | injects+="chmod -R a=r $line""\n" 23 | else 24 | injects+="chmod a=r $line""\n" 25 | fi 26 | done <"$FILE_LIST" 27 | 28 | mkdir -p "$DIR/../cloud-init/x-shellscript" 29 | # shellcheck disable=SC2001 30 | echo "$CLD_SH_TEMPLATE" | sed -e "s@PLACEHOLDER@$injects@g" > "$CLD_SH" -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/cmd/app/imr.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/hex" 5 | "log" 6 | 7 | "github.com/cc-api/evidence-api/common/golang/evidence_api" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var imrCmd = &cobra.Command{ 13 | Use: "imr", 14 | Short: "Retrieve the IMR of the cc vm", 15 | RunE: func(cmd *cobra.Command, args []string) error { 16 | sdk, err := GetSDK() 17 | if err != nil { 18 | return err 19 | } 20 | report, err := sdk.GetCCReport(nil, nil, nil) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | group := report.IMRGroup() 26 | l := log.Default() 27 | l.Printf("Measurement Count: %d\n", group.MaxIndex+1) 28 | alg := evidence_api.GetDefaultTPMAlg() 29 | for index, digest := range group.Group { 30 | l.Printf("Index: %v\n", index) 31 | l.Printf("Algorithms: %v\n", alg) 32 | l.Printf("HASH: %v\n", hex.EncodeToString(digest.Hash)) 33 | } 34 | return nil 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /src/python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "cctrusted_vm" 3 | version = "0.5.0" 4 | authors = [ 5 | { name="Lu, Ken", email="ken.lu@intel.com" }, 6 | { name="Zhang, Wenhui", email="wenhui.zhang@bytedance.com" }, 7 | ] 8 | description = "Evidence API VM SDK" 9 | readme = "README.md" 10 | license = { text="Apache Software License" } 11 | requires-python = ">=3.6" 12 | classifiers = [ 13 | "Programming Language :: Python :: 3", 14 | "License :: OSI Approved :: Apache Software License", 15 | "Operating System :: POSIX :: Linux", 16 | ] 17 | dependencies = [ 18 | "evidence_api", 19 | "pytest", 20 | ] 21 | 22 | [project.urls] 23 | "Homepage" = "https://github.com/cc-api/cc-trusted-vmsdk" 24 | "Bug Tracker" = "https://github.com/cc-api/cc-trusted-vmsdk/issues" 25 | 26 | [build-system] 27 | requires = ["setuptools", "wheel"] 28 | build-backend = "setuptools.build_meta" 29 | 30 | [tool.setuptools.packages.find] 31 | where=["."] 32 | include=["cctrusted_vm*"] 33 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/98-ima-enable-simple/pre-stage/guest_enable_ima_fix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FSTAB_FILE=/etc/fstab 4 | FSTAB_FILE_NEW=$FSTAB_FILE".new" 5 | awk '{if($3 == "ext4" && $4 !~ /iversion/) $4 = $4",iversion"; print}' \ 6 | $FSTAB_FILE \ 7 | > $FSTAB_FILE_NEW 8 | mv $FSTAB_FILE_NEW $FSTAB_FILE 9 | 10 | GRUB_FILE=/etc/default/grub.d/50-cloudimg-settings.cfg 11 | GRUB_FILE_NEW=$GRUB_FILE".new" 12 | 13 | # Remove ima_appraise and rootflags if exists 14 | sed -i 's/ima_appraise=\(fix\|enforce\|log\|off\)//' $GRUB_FILE 15 | sed -i 's/ima_hash=sha384//' $GRUB_FILE 16 | sed -i 's/rootflags=i_version//' $GRUB_FILE 17 | sed -i 's/console=tty1 console=ttyS0//' $GRUB_FILE 18 | sed -i 's/console=hvc0//' $GRUB_FILE 19 | 20 | awk '{if($1 ~ /GRUB_CMDLINE_LINUX_DEFAULT/) sub("=\"","=\"console=tty1 console=ttyS0 ima_appraise=fix ima_hash=sha384 rootflags=i_version "); print}' \ 21 | $GRUB_FILE \ 22 | > $GRUB_FILE_NEW 23 | mv $GRUB_FILE_NEW $GRUB_FILE 24 | 25 | update-grub 26 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/06-install-tdx-guest-kernel/README.md: -------------------------------------------------------------------------------- 1 | # Install TDX guest kernel 2 | 3 | This plugin is used to install a TDX guest kernel from a given local repository. 4 | 5 | # Prerequisite 6 | 7 | Prepare the local repository and confirm that there are Debian packages related to the TDX kernel in the `/jammy/amd64/` directory of this repository. It is recommended to place this local repository in the `pre-stage/artifacts/` directory. 8 | ``` 9 | mkdir -p ./pre-stage/artifacts 10 | mv ./pre-stage/artifacts/ 11 | ``` 12 | 13 | Set `${CVM_TDX_GUEST_REPO}` to the repository absolute path, or this plugin will be skipped. 14 | ``` 15 | export CVM_TDX_GUEST_REPO=$(pwd)/pre-stage/artifacts/ 16 | 17 | # Or 18 | export CVM_TDX_GUEST_REPO= 19 | ``` 20 | 21 | 22 | _NOTE: IF the original image is smaller than 1.5G, please set the environment variable GUEST\_SIZE to a larger value, as this will result in the execution of plugin 01._ 23 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/cmd/app/report.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/binary" 5 | "math" 6 | "math/rand" 7 | 8 | "github.com/cc-api/evidence-api/common/golang/evidence_api" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var reportCmd = &cobra.Command{ 14 | Use: "report", 15 | Short: "Retrieve the cc report and dump it", 16 | RunE: func(cmd *cobra.Command, args []string) error { 17 | sdk, err := GetSDK() 18 | if err != nil { 19 | return err 20 | } 21 | 22 | nonce := makeNonce() 23 | userData := makeUserData() 24 | report, err := sdk.GetCCReport(nonce, userData, nil) 25 | if err != nil { 26 | return err 27 | } 28 | report.Dump(evidence_api.QuoteDumpFormat(FlagFormat)) 29 | return nil 30 | }, 31 | } 32 | 33 | func makeNonce() []byte { 34 | num := uint64(rand.Int63n(math.MaxInt64)) 35 | b := make([]byte, 8) 36 | binary.LittleEndian.PutUint64(b, num) 37 | return b 38 | } 39 | 40 | func makeUserData() []byte { 41 | b := []byte("demo user data") 42 | return b 43 | } 44 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/04-user-authkey/pre-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Go to this dir 4 | pushd "$(dirname "$(readlink -f "$0")")" || exit 0 5 | 6 | # shellcheck disable=SC1091 7 | source ../../../scripts/common.sh 8 | 9 | # Check CVM_USER, CVM_AUTH_KEY 10 | CVM_USER="${CVM_USER:-cvm}" 11 | info "Config user: $CVM_USER" 12 | 13 | if [[ -z "$CVM_AUTH_KEY" ]]; then 14 | warn "SKIP: CVM_AUTH_KEY is not defined via environment variable 'CVM_AUTH_KEY'" 15 | exit 0 16 | fi 17 | info "ssh pubkey: $CVM_AUTH_KEY" 18 | 19 | # Generate cloud-config 20 | mkdir -p ../cloud-init/cloud-config/ 21 | cat > ../cloud-init/cloud-config/04-user-authkey.yaml << EOL 22 | #cloud-config 23 | merge_how: 24 | - name: list 25 | settings: [append] 26 | - name: dict 27 | settings: [no_replace, recurse_list] 28 | users: 29 | - default 30 | - name: $CVM_USER 31 | groups: sudo 32 | sudo: ALL=(ALL) NOPASSWD:ALL 33 | shell: /bin/bash 34 | ssh_authorized_keys: 35 | - $CVM_AUTH_KEY 36 | EOL 37 | 38 | popd || exit 0 39 | -------------------------------------------------------------------------------- /setupenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURR_DIR=$(pwd) 4 | 5 | # Check if the script is being run as root 6 | if [ "$EUID" -ne 0 ]; then 7 | echo "Please run the script as root" 8 | exit 1 9 | fi 10 | 11 | # setup virtualenv and PYTHONPATH 12 | apt-get update 13 | apt-get install -y python3-virtualenv pkg-config libtss-dev libtss2-dev 14 | 15 | if [[ -d ${CURR_DIR}/venv ]]; then 16 | echo "===========> Remove ${CURR_DIR}/venv and create a new one" 17 | rm -rf {CURR_DIR}/venv 18 | fi 19 | 20 | python3 -m virtualenv -p python3 ${CURR_DIR}/venv 21 | source ${CURR_DIR}/venv/bin/activate 22 | python3 -m pip install "evidence_api @ git+https://github.com/cc-api/evidence-api.git#subdirectory=common/python" 23 | python3 -m pip install -r $CURR_DIR/src/python/requirements.txt 24 | if [ ! $? -eq 0 ]; then 25 | echo "Failed to install python PIP packages, please check your proxy (https_proxy) or setup PyPi mirror." 26 | deactivate 27 | rm ${CURR_DIR}/venv -fr 28 | return 1 29 | fi 30 | 31 | export PYTHONPATH=$PYTHONPATH:$CURR_DIR/src/python 32 | -------------------------------------------------------------------------------- /src/pytdxmeasure/setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Setup script 3 | """ 4 | import io 5 | from setuptools import setup 6 | 7 | 8 | def load_readme(): 9 | """ 10 | Load content from README.md 11 | """ 12 | with io.open('README.md', 'rt', encoding='utf8') as fobj: 13 | readme = fobj.read() 14 | return readme 15 | 16 | 17 | def load_requirements(): 18 | """ 19 | Load content from requirements.txt 20 | """ 21 | with open('requirements.txt', encoding='utf-8') as fobj: 22 | return fobj.read().splitlines() 23 | 24 | 25 | setup( 26 | name='pytdxmeasure', 27 | version='0.0.6', 28 | packages=['pytdxmeasure'], 29 | package_data={ 30 | '': ['tdx_eventlogs', 'tdx_tdreport', 'tdx_verify_rtmr'] 31 | }, 32 | include_package_data=True, 33 | python_requires='>=3.6.8', 34 | license='Apache License 2.0', 35 | scripts=['tdx_eventlogs', 'tdx_tdreport', 'tdx_verify_rtmr'], 36 | long_description=load_readme(), 37 | long_description_content_type='text/markdown', 38 | install_requires=load_requirements() 39 | ) 40 | -------------------------------------------------------------------------------- /src/rust/README.md: -------------------------------------------------------------------------------- 1 | # Rust SDK for Evidence API in Confidential VM 2 | 3 | This is the Rust version of our VM SDK to help you using the Evidence API in your Rust programs. The sub folder "cctrusted_vm" include all the source code for the VMSDK. The sub folder "sample" includes some commandline examples for your reference. 4 | 5 | # Run CLI Samples 6 | 7 | We can try the CLI samples like this: 8 | 9 | ```bash 10 | cd sample 11 | 12 | # get measurement 13 | cargo run --bin cc-sample-measurement 14 | 15 | # get event log 16 | cargo run --bin cc-sample-eventlog 17 | 18 | # get quote 19 | cargo run --bin cc-sample-quote 20 | ``` 21 | 22 | Or, after build successfully, we can also run the CLIs directly: 23 | 24 | ```bash 25 | cd sample 26 | 27 | # build the release version 28 | cargo build --release 29 | 30 | # get measurement 31 | target/release/cc-sample-measurement 32 | 33 | # get event log 34 | target/release/cc-sample-eventlog 35 | 36 | # get quote 37 | target/release/cc-sample-quote 38 | ``` 39 | 40 | # Run Test 41 | 42 | The test is simple: 43 | 44 | ```bash 45 | cd cctrusted_vm 46 | cargo test 47 | ``` 48 | -------------------------------------------------------------------------------- /.github/cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "ignorepaths": [ 4 | ".github/", 5 | ".git/" 6 | ], 7 | "ignorewords": [], 8 | "ignoreRegExpList": [ 9 | "/```[\\s\\S]*?```/g", 10 | "/`[\\s\\S]*?`/g" 11 | ], 12 | "import": [], 13 | "dictionarydefinitions": [], 14 | "dictionaries": [], 15 | "words": [ 16 | "authkey", 17 | "ccnp", 18 | "cctrusted", 19 | "cmdline", 20 | "commandline", 21 | "containerd", 22 | "daemonset", 23 | "distro", 24 | "Eventlog", 25 | "grpcurl", 26 | "GHCI", 27 | "kustomization", 28 | "LASA", 29 | "netplan", 30 | "NFD", 31 | "OVMF", 32 | "proto", 33 | "qcow", 34 | "quoteserver", 35 | "RTMR", 36 | "TDREPORT", 37 | "TDVF", 38 | "TDVM", 39 | "udev", 40 | "PCCS", 41 | "DCAP", 42 | "distros", 43 | "PCKID", 44 | "pccs", 45 | "pckcache", 46 | "sysfs", 47 | "UEFI", 48 | "ACPI", 49 | "MRTD", 50 | "CCEL", 51 | "Bootloader", 52 | "VMSDK" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/pylint.yaml: -------------------------------------------------------------------------------- 1 | name: Python Code Scan 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'src/**/*.py' 9 | pull_request: 10 | paths: 11 | - 'src/**/*.py' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | codescan: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - uses: actions/setup-python@v4 21 | 22 | - name: Analyze python code 23 | run: | 24 | set -ex 25 | sudo su -c 'source setupenv.sh && \ 26 | python3 -m pip install --upgrade pip && \ 27 | python3 -m pip install pylint pydocstyle && \ 28 | for f in $(find -type f -name "requirements.txt" -not -path "./venv/*"); do 29 | python3 -m pip install -r $f; 30 | done && \ 31 | python_files=$(find ./ -name "*.py" -not -path "./venv/*" -print) && \ 32 | if [[ -n "$python_files" ]]; then 33 | echo "$python_files" | xargs -n 1 python3 -m pylint --rcfile=.github/pylintrc 34 | #echo "$python_files" | xargs -n 1 python3 -m pydocstyle --convention=google 35 | else 36 | echo "No python files found." 37 | fi' 38 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/cmd/app/root.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "vm-tool", 12 | Short: "A demo tool to use vmsdk of golang ", 13 | Long: `vm-tool 14 | A sample implementation to use vmsdk to get evidence, such as 15 | * cc quote 16 | * event log 17 | * integrated measuremt registers`, 18 | } 19 | 20 | // Execute adds all child commands to the root command and sets flags appropriately. 21 | // This is called by main.main(). It only needs to happen once to the rootCmd. 22 | func Execute() { 23 | err := rootCmd.Execute() 24 | if err != nil { 25 | os.Exit(1) 26 | } 27 | } 28 | 29 | var ( 30 | FlagFormat string 31 | ) 32 | 33 | const ( 34 | FlagFormatHuman = "human" 35 | FlagFormatRaw = "raw" 36 | ) 37 | 38 | func init() { 39 | // Persistent Flags 40 | rootCmd.PersistentFlags().StringVarP( 41 | &FlagFormat, "out-format", "f", FlagFormatHuman, 42 | "the format of the output: human/raw") 43 | 44 | // sub-commands 45 | // 1. report command 46 | rootCmd.AddCommand(reportCmd) 47 | // 2. imr command 48 | rootCmd.AddCommand(imrCmd) 49 | // 3. eventlog command 50 | rootCmd.AddCommand(eventLogCmd) 51 | 52 | } 53 | -------------------------------------------------------------------------------- /.github/workflows/pr-check-rust.yaml: -------------------------------------------------------------------------------- 1 | name: Rust Code Scan 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'src/rust/cctrusted_vm/src/**.rs' 9 | - 'src/rust/sample/src/**.rs' 10 | - '.github/workflows/pr-check-rust.yaml' 11 | pull_request: 12 | paths: 13 | - 'src/rust/cctrusted_vm/src/**.rs' 14 | - 'src/rust/sample/src/**.rs' 15 | - '.github/workflows/pr-check-rust.yaml' 16 | workflow_dispatch: 17 | 18 | jobs: 19 | codescan: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout PR 23 | uses: actions/checkout@v3 24 | 25 | - name: Set up Rust action 26 | uses: actions-rs/toolchain@v1 27 | with: 28 | toolchain: 1.70.0 29 | 30 | - name: Install dependencies 31 | run: | 32 | sudo apt update && yes | DEBIAN_FRONTEND=noninteractive sudo apt install -y libcryptsetup-dev clang protobuf-compiler protobuf-c-compiler libprotobuf-c-dev libprotobuf-c1 build-essential pkg-config libssl-dev 33 | 34 | - name: Run cargo check for VM SDK 35 | run: | 36 | cd src/rust/cctrusted_vm/ 37 | cargo check 38 | cargo fmt -- --check 39 | cargo clippy 40 | cargo install --locked cargo-deny 41 | cargo deny check 42 | -------------------------------------------------------------------------------- /.github/workflows/vmsdk-test-rust.yaml: -------------------------------------------------------------------------------- 1 | name: VMSDK Rust Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'src/rust/**/*' 9 | - '.github/workflows/vmsdk-test-rust.yaml' 10 | pull_request: 11 | paths: 12 | - 'src/rust/**/*' 13 | - '.github/workflows/vmsdk-test-rust.yaml' 14 | workflow_dispatch: 15 | 16 | env: 17 | CARGO_TERM_COLOR: always 18 | VMSDK_RUST_TEST_DIR: vmsdk_rust_test 19 | 20 | jobs: 21 | vmsdk_rust_test: 22 | runs-on: [self-hosted, configfs-tsm] 23 | defaults: 24 | run: 25 | working-directory: ${{env.VMSDK_RUST_TEST_DIR}} 26 | steps: 27 | - name: Clean up intermediate files 28 | continue-on-error: true 29 | run: | 30 | # Remove the intermediate files that could be left 31 | # by previous run with sudo. Otherwise, the checkout 32 | # will fail with permission issue. 33 | sudo rm -f src/rust/cctrusted_vm/Cargo.lock 34 | sudo rm -fr src/rust/cctrusted_vm/target 35 | - name: Checkout repo 36 | uses: actions/checkout@v4 37 | with: 38 | path: ${{env.VMSDK_RUST_TEST_DIR}} 39 | - name: Run tests 40 | run: | 41 | cd src/rust/cctrusted_vm/ 42 | sudo bash -c "export CARGO_TERM_COLOR=always && cargo test --verbose" 43 | -------------------------------------------------------------------------------- /src/python/cc_imr_cli.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Command line to dump the integrated measurement register 4 | """ 5 | import logging 6 | import os 7 | from evidence_api.api import EvidenceApi 8 | from cctrusted_vm.cvm import ConfidentialVM 9 | from cctrusted_vm.sdk import CCTrustedVmSdk 10 | 11 | LOG = logging.getLogger(__name__) 12 | 13 | logging.basicConfig(level=logging.NOTSET, format='%(name)s %(levelname)-8s %(message)s') 14 | 15 | def main(): 16 | """Example to call get_cc_measurement and dump the result to stdout.""" 17 | if ConfidentialVM.detect_cc_type() == EvidenceApi.TYPE_CC_NONE: 18 | LOG.error("This is not a confidential VM!") 19 | return 20 | if os.geteuid() != 0: 21 | LOG.error("Please run as root which is required for this example!") 22 | return 23 | 24 | count = CCTrustedVmSdk.inst().get_measurement_count() 25 | LOG.info("Measurement Count: %d", count) 26 | for index in range(CCTrustedVmSdk.inst().get_measurement_count()): 27 | alg = CCTrustedVmSdk.inst().get_default_algorithms() 28 | imr = CCTrustedVmSdk.inst().get_cc_measurement([index, alg.alg_id]) 29 | digest_obj = imr.digest(alg.alg_id) 30 | 31 | hash_str = "" 32 | for hash_item in digest_obj.hash: 33 | hash_str += "".join([f"{hash_item:02x}", " "]) 34 | 35 | LOG.info("Algorithms: %s", str(alg)) 36 | LOG.info("HASH: %s", hash_str) 37 | 38 | if __name__ == "__main__": 39 | main() 40 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/98-ima-enable-simple/pre-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname "$(readlink -f "$0")") 4 | FILE_LIST="$DIR/file_list" 5 | CLD_SH_REGISTER_FILE_HASH="01-ima-register-file-hash.sh" 6 | CLD_SH="$DIR/../cloud-init/x-shellscript/$CLD_SH_REGISTER_FILE_HASH" 7 | CLD_SH_TEMPLATE="" 8 | injects="" 9 | 10 | read -r -d '' CLD_SH_TEMPLATE << EOM 11 | #!/bin/bash 12 | 13 | echo "=========== register file hash started ===========" 14 | 15 | GRUB_FILE=/etc/default/grub.d/50-cloudimg-settings.cfg 16 | 17 | # replaced by required files 18 | PLACEHOLDER 19 | 20 | sed -i 's/ima_appraise=fix/ima_appraise=log/' \$GRUB_FILE 21 | sed -i 's/console=tty1 console=ttyS0/console=hvc0/' \$GRUB_FILE 22 | 23 | echo "=========== register file hash finished ===========" 24 | 25 | # the command is executed in basic script 26 | # update-grub 27 | EOM 28 | 29 | # filter specified files & directories 30 | while IFS= read -r line || [ -n "$line" ]; do 31 | if [[ $line == "#"* ]] || [[ $line == "" ]]; then 32 | continue 33 | fi 34 | if [[ $line == *"/" ]]; then 35 | injects+="find $line -type f -uid 0 -exec dd if='{}' of=/dev/null count=0 status=none \\\;""\n" 36 | else 37 | name=$(basename "$line") 38 | path=${line%/*}/ 39 | injects+="find $path -type f -name $name -uid 0 -exec dd if='{}' of=/dev/null count=0 status=none \\\;""\n" 40 | fi 41 | done <"$FILE_LIST" 42 | 43 | # generate script for cloud-init 44 | # shellcheck disable=SC2001 45 | echo "$CLD_SH_TEMPLATE" | sed -e "s@PLACEHOLDER@$injects@g" > "$CLD_SH" 46 | 47 | # enable ima update 48 | virt-customize -a "${GUEST_IMG}" \ 49 | --run-command 'mkdir -p /etc/ima' \ 50 | --run "$DIR"/guest_enable_ima_fix.sh 51 | 52 | -------------------------------------------------------------------------------- /.github/workflows/vmsdk-test-python.yaml: -------------------------------------------------------------------------------- 1 | name: VMSDK Python Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'src/python/**/*' 9 | - 'setupenv.sh' 10 | - '.github/workflows/vmsdk-test-python.yaml' 11 | pull_request: 12 | paths: 13 | - 'src/python/**/*' 14 | - 'setupenv.sh' 15 | - '.github/workflows/vmsdk-test-python.yaml' 16 | workflow_dispatch: 17 | 18 | env: 19 | VMSDK_PYTEST_DIR: 'vmsdk_pytest' 20 | 21 | jobs: 22 | vmsdk_pytest: 23 | runs-on: [self-hosted, tdx-guest] 24 | defaults: 25 | run: 26 | working-directory: ${{env.VMSDK_PYTEST_DIR}} 27 | steps: 28 | - name: Clean up intermediate files 29 | continue-on-error: true 30 | run: | 31 | # Remove the intermediate files that could be left 32 | # by previous run with sudo. Otherwise, the checkout 33 | # will fail with permission issue. 34 | sudo rm -fr ./* 35 | - name: Checkout repo 36 | uses: actions/checkout@v4 37 | with: 38 | path: ${{env.VMSDK_PYTEST_DIR}} 39 | - name: Run PyTest for VMSDK 40 | run: | 41 | set -ex 42 | # Set the "PYTHONDONTWRITEBYTECODE" and "no:cacheprovider" to prevent 43 | # generated some intermediate files by root. Othwerwise, these 44 | # files will fail the action/checkout in the next round of running 45 | # due to the permission issue. 46 | sudo su -c "source setupenv.sh && \ 47 | pushd src/python/tests && \ 48 | export PYTHONDONTWRITEBYTECODE=1 && \ 49 | python3 -m pytest -p no:cacheprovider -v test_sdk.py && \ 50 | popd" 51 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/06-install-tdx-guest-kernel/pre-stage/host_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURR_DIR=$(dirname "$(readlink -f "$0")") 4 | TOP_DIR="${CURR_DIR}/../../../" 5 | SCRIPTS_DIR="${TOP_DIR}/scripts" 6 | # shellcheck disable=SC1091 7 | source "${SCRIPTS_DIR}/common.sh" 8 | ARTIFACTS_GUEST=/srv 9 | 10 | # check environment variable 'CVM_TDX_GUEST_REPO' 11 | if [[ -z "$CVM_TDX_GUEST_REPO" ]]; then 12 | warn "SKIP: TDX guest repo is not defined via environment variable 'CVM_TDX_GUEST_REPO' " 13 | exit 0 14 | fi 15 | 16 | info "TDX Guest Repo is at ${CVM_TDX_GUEST_REPO}..." 17 | 18 | # check if the repo exists 19 | if [[ ! -d "$CVM_TDX_GUEST_REPO" ]]; then 20 | warn "SKIP: TDX guest local repo CVM_TDX_GUEST_REPO does not exist." 21 | exit 0 22 | fi 23 | 24 | # Check if it is a valid TDX repo 25 | if ! compgen -G "$CVM_TDX_GUEST_REPO/jammy/amd64/linux-image-*mvp*.deb"; then 26 | warn "SKIP: $CVM_TDX_GUEST_REPO is invalid." 27 | exit 0 28 | fi 29 | 30 | info "TDX guest local repo $CVM_TDX_GUEST_REPO check passed" 31 | 32 | # Copy TDX local repo from host to guest 33 | virt-copy-in -a "${GUEST_IMG}" "$CVM_TDX_GUEST_REPO" "$ARTIFACTS_GUEST" 34 | ok "TDX guest local repo $CVM_TDX_GUEST_REPO copied to guest $ARTIFACTS_GUEST" 35 | 36 | # Generate cloud-config 37 | mkdir -p "${CURR_DIR}/../cloud-init/x-shellscript/" 38 | cat > "${CURR_DIR}/../cloud-init/x-shellscript/07-install-tdx-guest-kernel.sh" << EOL 39 | #!/bin/bash 40 | 41 | PACKAGE_DIR=""$ARTIFACTS_GUEST"/$(basename "$CVM_TDX_GUEST_REPO")/jammy/" 42 | pushd \$PACKAGE_DIR || exit 0 43 | apt install ./amd64/linux-image-unsigned-*.deb ./amd64/linux-modules-*.deb \ 44 | ./amd64/linux-headers-*.deb ./all/linux-headers-*.deb --allow-downgrades -y 45 | popd || exit 0 46 | EOL 47 | 48 | ok "Cloud config cloud-init/x-shellscript/07-install-tdx-guest-kernel.sh generated" -------------------------------------------------------------------------------- /src/rust/sample/src/cc-sample-eventlog.rs: -------------------------------------------------------------------------------- 1 | use evidence_api::api::*; 2 | use evidence_api::tcg::EventLogEntry; 3 | use cctrusted_vm::sdk::API; 4 | use log::*; 5 | 6 | fn main() { 7 | // set log level 8 | env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); 9 | 10 | // retrieve cc eventlog with API "get_cc_eventlog" 11 | let eventlogs = match API::get_cc_eventlog(Some(0), None) { 12 | Ok(r) => r, 13 | Err(e) => { 14 | error!("error getting TDX report: {:?}", e); 15 | return; 16 | } 17 | }; 18 | 19 | info!("event log count: {}", eventlogs.len()); 20 | // for eventlog in &eventlogs { 21 | // eventlog.show(); 22 | // } 23 | 24 | // replay cc eventlog with API "replay_cc_eventlog" 25 | let replay_results = match API::replay_cc_eventlog(eventlogs) { 26 | Ok(r) => r, 27 | Err(e) => { 28 | error!("error replay eventlog: {:?}", e); 29 | return; 30 | } 31 | }; 32 | 33 | // show replay results 34 | for replay_result in replay_results { 35 | replay_result.show(); 36 | } 37 | 38 | // retrieve cc eventlog in batch 39 | let mut eventlogs1: Vec = Vec::new(); 40 | let mut start = 0; 41 | let batch_size = 10; 42 | loop { 43 | let event_logs = match API::get_cc_eventlog(Some(start), Some(batch_size)) { 44 | Ok(q) => q, 45 | Err(e) => { 46 | error!("error get eventlog: {:?}", e); 47 | return; 48 | } 49 | }; 50 | for event_log in &event_logs { 51 | eventlogs1.push(event_log.clone()); 52 | } 53 | if !event_logs.is_empty() { 54 | start += event_logs.len() as u32; 55 | } else { 56 | break; 57 | } 58 | } 59 | 60 | info!("event log count: {}", eventlogs1.len()); 61 | } 62 | -------------------------------------------------------------------------------- /src/rust/sample/src/cc-sample-measurement.rs: -------------------------------------------------------------------------------- 1 | use evidence_api::api::*; 2 | use evidence_api::tcg::TcgAlgorithmRegistry; 3 | use cctrusted_vm::sdk::API; 4 | 5 | use log::*; 6 | 7 | fn main() { 8 | // set log level 9 | env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); 10 | 11 | // get CVM default algorithm with API "get_default_algorithm" 12 | info!("call Evidence API [get_default_algorithm] to get CVM supported algorithm!"); 13 | let defalt_algo = match API::get_default_algorithm() { 14 | Ok(algorithm) => { 15 | info!("supported algorithm: {}", algorithm.algo_id_str); 16 | algorithm 17 | } 18 | Err(e) => { 19 | error!("error get algorithm: {:?}", e); 20 | return; 21 | } 22 | }; 23 | 24 | // get number of measurement registers in CVM 25 | info!("call Evidence API [get_measurement_count] to get number of measurement registers in CVM!"); 26 | let count = match API::get_measurement_count() { 27 | Ok(count) => { 28 | info!("measurement registers count: {}", count); 29 | count 30 | } 31 | Err(e) => { 32 | error!("error get measurement count: {:?}", e); 33 | return; 34 | } 35 | }; 36 | 37 | // retrive and show measurement registers in CVM 38 | info!("call Evidence API [get_cc_measurement] to get measurement register content in CVM!"); 39 | for index in 0..count { 40 | let tcg_digest = match API::get_cc_measurement(index, defalt_algo.algo_id) { 41 | Ok(tcg_digest) => tcg_digest, 42 | Err(e) => { 43 | error!("error get measurement: {:?}", e); 44 | return; 45 | } 46 | }; 47 | info!( 48 | "show index = {}, algo = {:?}, hash = {:02X?}", 49 | index, 50 | tcg_digest.get_algorithm_id_str(), 51 | tcg_digest.get_hash() 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/98-ima-enable-simple/files/etc/ima/ima-policy: -------------------------------------------------------------------------------- 1 | # PROC_SUPER_MAGIC 2 | dont_measure fsmagic=0x9fa0 3 | # SYSFS_MAGIC 4 | dont_measure fsmagic=0x62656572 5 | # DEBUGFS_MAGIC 6 | dont_measure fsmagic=0x64626720 7 | # TMPFS_MAGIC 8 | dont_measure fsmagic=0x1021994 9 | # RAMFS_MAGIC 10 | dont_measure fsmagic=0x858458f6 11 | dont_measure fsmagic=0x1cd1 12 | dont_measure fsmagic=0x42494e4d 13 | # SECURITYFS_MAGIC 14 | dont_measure fsmagic=0x73636673 15 | # SELINUX_MAGIC 16 | dont_measure fsmagic=0xf97cff8c 17 | dont_measure fsmagic=0x43415d53 18 | dont_measure fsmagic=0x6e736673 19 | # CGROUP_SUPER_MAGIC 20 | dont_measure fsmagic=0x27e0eb 21 | dont_measure fsmagic=0x63677270 22 | # Don't measure log, audit or tmp files 23 | # dont_measure obj_type=var_log_t 24 | # dont_measure obj_type=auditd_log_t 25 | # dont_measure obj_type=tmp_t 26 | 27 | # PROC_SUPER_MAGIC 28 | dont_appraise fsmagic=0x9fa0 29 | # SYSFS_MAGIC 30 | dont_appraise fsmagic=0x62656572 31 | # DEBUGFS_MAGIC 32 | dont_appraise fsmagic=0x64626720 33 | # TMPFS_MAGIC 34 | dont_appraise fsmagic=0x1021994 35 | # RAMFS_MAGIC 36 | dont_appraise fsmagic=0x858458f6 37 | dont_appraise fsmagic=0x1cd1 38 | dont_appraise fsmagic=0x42494e4d 39 | # SECURITYFS_MAGIC 40 | dont_appraise fsmagic=0x73636673 41 | dont_appraise fsmagic=0xf97cff8c 42 | dont_appraise fsmagic=0x43415d53 43 | dont_appraise fsmagic=0x6e736673 44 | # CGROUP_SUPER_MAGIC 45 | dont_appraise fsmagic=0x27e0eb 46 | dont_appraise fsmagic=0x63677270 47 | # OVERLAYFS_MAGIC 48 | # when containers are used we almost always want to ignore them 49 | dont_appraise fsmagic=0x794c7630 50 | # Don't measure log, audit or tmp files 51 | # dont_appraise obj_type=var_log_t 52 | # dont_appraise obj_type=auditd_log_t 53 | # dont_appraise obj_type=tmp_t 54 | 55 | # Define the default measurement policy and appraisal 56 | # Refer: https://sourceforge.net/p/linux-ima/wiki/Home/ 57 | measure func=BPRM_CHECK 58 | measure func=FILE_MMAP mask=MAY_EXEC 59 | measure func=MODULE_CHECK uid=0 60 | 61 | appraise 62 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/tdx-libvirt-ubuntu-host.xml.template: -------------------------------------------------------------------------------- 1 | 2 | tdx-guest 3 | REPLACE_MEM_SIZE 4 | 5 | 6 | 7 | REPLACE_VCPU_NUM 8 | 9 | hvm 10 | /usr/share/qemu/OVMF.fd 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | destroy 22 | restart 23 | destroy 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | /usr/bin/qemu-system-x86_64 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | 54 | 55 | 0x10000000 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/rust/sample/src/cc-sample-quote.rs: -------------------------------------------------------------------------------- 1 | use evidence_api::api::*; 2 | use evidence_api::api_data::*; 3 | use evidence_api::cc_type::TeeType; 4 | use evidence_api::tdx::quote::TdxQuote; 5 | use cctrusted_vm::sdk::API; 6 | 7 | use log::*; 8 | use rand::Rng; 9 | 10 | fn main() { 11 | // set log level 12 | env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); 13 | 14 | /*** 15 | * Note: in real user case, the nonce should come from attestation server 16 | * side to prevent replay attack and the data should be generate by API caller 17 | * according to user define spec 18 | */ 19 | let nonce = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); 20 | let data = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); 21 | 22 | // retrieve cc report with API "get_cc_report" 23 | info!("call Evidence API [get_cc_report] to retrieve cc report!"); 24 | let report = match API::get_cc_report(Some(nonce), Some(data), ExtraArgs {}) { 25 | Ok(q) => q, 26 | Err(e) => { 27 | error!("error getting TDX report: {:?}", e); 28 | return; 29 | } 30 | }; 31 | 32 | info!("cc report size {}", report.cc_report.len()); 33 | 34 | // dump the cc report with API "dump_cc_report" 35 | // info!("call Evidence API [dump_cc_report] to dump cc report!"); 36 | // API::dump_cc_report(&report.cc_report); 37 | 38 | // parse the cc report with API "parse_cc_report" 39 | if report.cc_type == TeeType::TDX { 40 | let tdx_quote: TdxQuote = match CcReport::parse_cc_report(report.cc_report) { 41 | Ok(q) => q, 42 | Err(e) => { 43 | error!("error parse tdx quote: {:?}", e); 44 | return; 45 | } 46 | }; 47 | info!( 48 | "version = {}, report_data = {}", 49 | tdx_quote.header.version, 50 | base64::encode(tdx_quote.body.report_data) 51 | ); 52 | 53 | // show data of the struct TdxQuoteHeader 54 | info!("call struct show function to show data of the struct TdxQuoteHeader!"); 55 | tdx_quote.header.show(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/pytdxmeasure/pytdxmeasure/cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Dump command line 3 | """ 4 | 5 | from abc import abstractmethod 6 | import logging 7 | import logging.config 8 | from .actor import VerifyActor, TDEventLogActor 9 | from .tdreport import TdReport 10 | 11 | from .ccel import CCEL 12 | 13 | __author__ = "cpio" 14 | 15 | LOG = logging.getLogger(__name__) 16 | 17 | 18 | class TDXMeasurementCmdBase: 19 | """ 20 | Base class for TDX measurements commands. 21 | """ 22 | 23 | def __init__(self): 24 | logging.basicConfig(level=logging.DEBUG, format='%(message)s') 25 | 26 | @abstractmethod 27 | def run(self): 28 | """ 29 | Interface to be impelemented by child classes 30 | """ 31 | raise NotImplementedError 32 | 33 | 34 | class TDXEventLogsCmd(TDXMeasurementCmdBase): 35 | """ 36 | Cmd executor for dump TDX event logs. 37 | """ 38 | 39 | def run(self): 40 | """ 41 | Run cmd 42 | """ 43 | 44 | LOG.info("=> Read CCEL ACPI Table") 45 | ccelobj = CCEL.create_from_acpi_file() 46 | if ccelobj is None: 47 | return 48 | ccelobj.dump() 49 | 50 | actor = TDEventLogActor(ccelobj.log_area_start_address, 51 | ccelobj.log_area_minimum_length) 52 | 53 | LOG.info("") 54 | LOG.info("=> Read Event Log Data - Address: 0x%X(0x%X)", 55 | ccelobj.log_area_start_address, 56 | ccelobj.log_area_minimum_length) 57 | actor.dump_td_event_logs() 58 | 59 | LOG.info("") 60 | LOG.info("=> Replay Rolling Hash - RTMR") 61 | actor.dump_rtmrs() 62 | 63 | 64 | class TDXVerifyCmd(TDXMeasurementCmdBase): 65 | """ 66 | Cmd executor for verify RTMR 67 | """ 68 | 69 | def run(self): 70 | """ 71 | Run cmd 72 | """ 73 | LOG.info("=> Verify RTMR") 74 | VerifyActor().verify_rtmr() 75 | 76 | 77 | class TDXTDReportCmd(TDXMeasurementCmdBase): 78 | """ 79 | Cmd executor to dump TD report. 80 | """ 81 | 82 | def run(self): 83 | """ 84 | Run cmd 85 | """ 86 | 87 | LOG.info("=> Dump TD Report") 88 | TdReport.get_td_report().dump() 89 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/cmd/app/eventlog.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/hex" 5 | "log" 6 | 7 | "github.com/cc-api/evidence-api/common/golang/evidence_api" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var ( 13 | start *int 14 | count *int 15 | ) 16 | 17 | var eventLogCmd = &cobra.Command{ 18 | Use: "eventlog", 19 | Short: "Handle event log, with sub commands", 20 | Long: `Get event log of boot-time (and run-time if necessary)`, 21 | } 22 | 23 | var eventLogDumpCmd = &cobra.Command{ 24 | Use: "dump", 25 | Short: "Dump the retrieved eventlog", 26 | RunE: func(cmd *cobra.Command, args []string) error { 27 | el, err := filterEventLog() 28 | if err != nil { 29 | return err 30 | } 31 | 32 | log.Println("Total ", len(el), " of event logs fetched.") 33 | for _, e := range el { 34 | e.Dump() 35 | } 36 | return nil 37 | }, 38 | } 39 | 40 | var eventLogReplayCmd = &cobra.Command{ 41 | Use: "replay", 42 | Short: "Replay the retrieved eventlog, printing the result", 43 | RunE: func(cmd *cobra.Command, args []string) error { 44 | sdk, err := GetSDK() 45 | if err != nil { 46 | return err 47 | } 48 | 49 | l := log.Default() 50 | el, err := filterEventLog() 51 | if err != nil { 52 | return err 53 | } 54 | 55 | replay := sdk.ReplayCCEventLog(el) 56 | // Or direct `replay := el.Replay()` 57 | for idx, elem := range replay { 58 | for alg, v := range elem { 59 | l.Printf("Index: %v\n", idx) 60 | l.Printf("Algorithms: %v\n", alg) 61 | l.Printf("HASH: %v\n", hex.EncodeToString(v)) 62 | } 63 | } 64 | return nil 65 | }, 66 | } 67 | 68 | func filterEventLog() ([]evidence_api.FormatedTcgEvent, error) { 69 | sdk, err := GetSDK() 70 | if err != nil { 71 | return nil, err 72 | } 73 | el, err := sdk.GetCCEventLog(int32(*start), int32(*count)) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return el, nil 78 | } 79 | 80 | func init() { 81 | start = eventLogCmd.Flags().IntP("start", "s", 0, "the start index of the event log") 82 | count = eventLogCmd.Flags().IntP("count", "c", 0, "the count of the event log") 83 | eventLogCmd.MarkFlagsRequiredTogether("start", "count") 84 | 85 | eventLogCmd.AddCommand(eventLogDumpCmd) 86 | eventLogCmd.AddCommand(eventLogReplayCmd) 87 | } 88 | -------------------------------------------------------------------------------- /src/python/README.md: -------------------------------------------------------------------------------- 1 | 2 | # SDK for Evidence API in Confidential VM 3 | 4 | In confidential VM, the trusted primitives (measurement, eventlog, quote) normally 5 | can be accessed via device node like /dev/tpm0, sysfs etc, and different vendor 6 | may provides the different definitions. 7 | 8 | This VMSDK following the Evidence API design 9 | shields the difference introduced by the platform and provides user with unified usage 10 | in the confidential virtual machine environments. 11 | 12 | ![](/docs/cc-trusted-primitives-vendor.png) 13 | 14 | _NOTE: `VMSDK` currently supports Python, and it will provide support on Rust and Golang later._ 15 | 16 | ## How to use VMSDK 17 | 18 | VMSDK is supposed to provide trusted primitives (measurement, eventlog, quote) of CVM. 19 | All below steps are supposed to run in a CVM, such as Intel® TD. 20 | 21 | ### Installation 22 | 23 | `VMSDK` package is already available in PyPI. You can install the SDK simply by: 24 | 25 | ``` 26 | $ pip install cctrusted-vm 27 | ``` 28 | 29 | If you would like to run from source code. Try: 30 | 31 | ``` 32 | $ git clone https://github.com/cc-api/cc-trusted-vmsdk.git 33 | $ cd cc-trusted-vmsdk 34 | $ source setupenv.sh 35 | ``` 36 | 37 | ### Run CLI tool 38 | 39 | It provides 3 CLI tools for quick usage of Python VMSDK. 40 | 41 | - [cc_event_log_cli.py](./python/cc_event_log_cli.py): Print event log of CVM. 42 | - [cc_imr_cli.py](./python/cc_imr_cli.py): Print algorithm and hash od Integrity Measurement Registers (IMR). 43 | - [cc_quote_cli.py](./python/cc_quote_cli.py): Print quote of CVM. 44 | 45 | 46 | How to run the CLI tool: 47 | 48 | ``` 49 | $ git clone https://github.com/cc-api/cc-trusted-vmsdk.git 50 | $ cd cc-trusted-vmsdk 51 | $ sudo su 52 | $ source setupenv.sh 53 | $ python3 ./src/python/cc_imr_cli.py 54 | ``` 55 | _NOTE: The CLI tool needs to run via root user._ 56 | 57 | Below is example output of `cc_imr_cli.py`. 58 | 59 | ![](/docs/imr-cli-output.png) 60 | 61 | 62 | ### Run Tests 63 | 64 | It provides test cases for Python VMSDK. Run tests with below commands. 65 | 66 | ``` 67 | $ git clone https://github.com/cc-api/cc-trusted-vmsdk.git 68 | $ cd cc-trusted-vmsdk 69 | $ sudo su 70 | $ source setupenv.sh 71 | $ python3 -m pip install pytest 72 | $ python3 -m pytest -v ./src/python/tests/test_sdk.py 73 | ``` 74 | 75 | _NOTE: The tests need to run via root user._ 76 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/tdx/cvm.go: -------------------------------------------------------------------------------- 1 | package tdx 2 | 3 | import ( 4 | cctrusted_vm "github.com/cc-api/cc-trusted-vmsdk/src/golang/cctrusted_vm" 5 | 6 | "github.com/cc-api/evidence-api/common/golang/evidence_api" 7 | "github.com/cc-api/evidence-api/common/golang/evidence_api/tdx" 8 | ) 9 | 10 | var _ cctrusted_vm.ConfidentialVM = (*TdxVM)(nil) 11 | 12 | type TdxVM struct { 13 | cctrusted_vm.Device 14 | cctrusted_vm.EventRecorder 15 | evidence_api.IMARecorder 16 | } 17 | 18 | func NewTdxVM(args *cctrusted_vm.CVMInitArgs) *TdxVM { 19 | vm := &TdxVM{ 20 | Device: &TDXDevice{}, 21 | IMARecorder: &evidence_api.DefaultIMARecorder{}, 22 | } 23 | r := &TDXEventLogRecorder{} 24 | if args != nil { 25 | if args.RedirectedAcpiTableFile != "" { 26 | r.RedirectAcpiTableFile(args.RedirectedAcpiTableFile) 27 | } 28 | if args.RedirectedAcpiTableDataFile != "" { 29 | r.RedirectAcpiTableDataFile(args.RedirectedAcpiTableDataFile) 30 | } 31 | } 32 | vm.EventRecorder = r 33 | return vm 34 | } 35 | 36 | // DefaultAlgorithm implements cctrusted_vm.ConfidentialVM. 37 | func (t *TdxVM) DefaultAlgorithm() evidence_api.TCG_ALG { 38 | return evidence_api.TPM_ALG_SHA384 39 | } 40 | 41 | // MaxImrIndex implements cctrusted_vm.ConfidentialVM. 42 | func (t *TdxVM) MaxImrIndex() int { 43 | return tdx.RTMRMaxIndex 44 | } 45 | 46 | // CVMContext implements cctrusted_vm.ConfidentialVM. 47 | func (t *TdxVM) CVMContext() cctrusted_vm.CVMContext { 48 | return cctrusted_vm.CVMContext{ 49 | VMType: t.CCType(), 50 | Version: t.Version(), 51 | } 52 | } 53 | 54 | // Probe implements cctrusted_vm.ConfidentialVM, 55 | // probing tdx device, eventlog and ima 56 | func (t *TdxVM) Probe() error { 57 | // probe tdx device 58 | if err := t.ProbeDevice(); err != nil { 59 | return err 60 | } 61 | 62 | // probe eventlog 63 | if err := t.ProbeRecorder(); err != nil { 64 | return err 65 | } 66 | 67 | // probe ima 68 | if err := t.ProbeIMARecorder(); err != nil { 69 | return err 70 | } 71 | return nil 72 | } 73 | 74 | // TdxVMInitFunc creates and inits a tdx confidential VM 75 | func TdxVMInitFunc(args *cctrusted_vm.CVMInitArgs) (cctrusted_vm.ConfidentialVM, error) { 76 | tdx := NewTdxVM(args) 77 | if err := tdx.Probe(); err != nil { 78 | return nil, err 79 | } 80 | return tdx, nil 81 | } 82 | 83 | func init() { 84 | cctrusted_vm.RegisterCVMInitFunc(TdxVMInitFunc) 85 | } 86 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/tdx/device.go: -------------------------------------------------------------------------------- 1 | package tdx 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "github.com/cc-api/evidence-api/common/golang/evidence_api/tdx" 8 | 9 | "github.com/cc-api/evidence-api/common/golang/evidence_api" 10 | 11 | cctrusted_vm "github.com/cc-api/cc-trusted-vmsdk/src/golang/cctrusted_vm" 12 | ) 13 | 14 | var _ cctrusted_vm.Device = (*TDXDevice)(nil) 15 | 16 | type TDXDevice struct { 17 | spec tdx.TDXDeviceSpec 18 | cctrusted_vm.GenericDevice 19 | QuoteHandler 20 | } 21 | 22 | // Version implements cctrusted_vm.Device. 23 | func (t *TDXDevice) Version() evidence_api.DeviceVersion { 24 | return t.spec.Version 25 | } 26 | 27 | // CCType implements cctrusted_vm.Device. 28 | func (t *TDXDevice) CCType() evidence_api.CC_Type { 29 | return evidence_api.TYPE_CC_TDX 30 | } 31 | 32 | // Name implements cctrusted_vm.Device. 33 | func (t *TDXDevice) Name() string { 34 | return t.spec.DevicePath 35 | } 36 | 37 | // ProbeDevice implements cctrusted_vm.Device, probe valid tdx device. 38 | func (t *TDXDevice) ProbeDevice() error { 39 | for _, spec := range tdx.TdxDeviceSpecs { 40 | _, err := os.Stat(spec.DevicePath) 41 | if err != nil { 42 | continue 43 | } 44 | 45 | t.spec = spec 46 | err = t.initDevice() 47 | if err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | return errors.New("no valid tdx device found") 53 | } 54 | 55 | func (t *TDXDevice) initDevice() error { 56 | h, err := GetQuoteHandler(t.spec) 57 | if err != nil { 58 | return err 59 | } 60 | t.QuoteHandler = h 61 | return nil 62 | } 63 | 64 | // Report implements cctrusted_vm.Device, get CC report 65 | func (t *TDXDevice) Report(nonce, userData []byte, extraArgs map[string]any) (evidence_api.CcReport, error) { 66 | var resp evidence_api.CcReport 67 | var err error 68 | 69 | // call parent Report() func to retrieve cc report using Configfs-tsm 70 | resp, err = t.GenericDevice.Report(nonce, userData, extraArgs) 71 | if err == nil { 72 | return resp, nil 73 | } 74 | 75 | // get tdx report 76 | tdreport, err := t.TdReport(nonce, userData) 77 | if err != nil { 78 | return evidence_api.CcReport{}, err 79 | } 80 | // get tdx quote, aka. CC report 81 | quote, err := t.Quote(tdreport) 82 | if err != nil { 83 | return evidence_api.CcReport{}, err 84 | } 85 | 86 | resp = evidence_api.CcReport{ 87 | Outblob: quote, 88 | } 89 | 90 | return resp, nil 91 | } 92 | -------------------------------------------------------------------------------- /src/pytdxmeasure/README.md: -------------------------------------------------------------------------------- 1 | ## TDX Measurement Tool 2 | 3 | The measurement tool runs within TD guest to get `RTMR` value from TDREPORT via 4 | Linux attestation driver, and gets the full TD event log from CCEL ACPI table. 5 | Then it uses the TD event log to verify the RTMR value or change. 6 | 7 | CSP or tenant developer could use it to analyze and debug the TDX measurement 8 | before providing the TDX guest VM. 9 | 10 | ![](https://github.com/intel/tdx-tools/blob/main/doc/tdx_measurement.png) 11 | 12 | ### Overview 13 | 14 | The `RTMR` stands for Run-time Measurement Register, recording measurement for the component participating in the booting process. 15 | As of 2023.4, TDX supports four RTMRs, including RTMR[0], RTMR[1], RTMR[2] and RTMR[3]. 16 | 17 | The same RTMR may store measurement for different section in `direct boot` or `grub boot`. 18 | 19 | 1. Direct boot 20 | - RTMR[0]: It stores the measurement for the TDVF configuration. Changes on a part of the tdvm launch parameters, such memory size, will affect the final measurement. 21 | - RTMR[1]: It stores the measurement for the kernel and cmdline passed to the kernel. 22 | - RTMR[2] and RTMR[3]: They are reserved and can be used by the guest software to extend the measurement. 23 | 24 | 2. Grub boot 25 | - RTMR[0]: It works as it does in the direct boot. 26 | - RTMR[1]: It stores the measurement for the OS loader, such as grub. 27 | - RTMR[2]: It works as it does in the direct boot. 28 | - RTMR[3]: It is reserved and can be used by the guest software to extend the measurement. 29 | 30 | More details can be found in the [Articles-906357](https://lwn.net/Articles/906357/) and [Commit 9d2b64a](https://github.com/intel/tdx/commit/9d2b64a6b798668f9cc069992e3926bccffd0cfa#) 31 | 32 | ### Prerequisites 33 | 34 | The Log Area Start Address (LASA) is from ACPI CCEL table. Please see [GHCI specification](https://cdrdv2.intel.com/v1/dl/getContent/726790). 35 | 36 | ### Run 37 | 38 | 1. Get Event Log 39 | 40 | ``` 41 | ./tdx_eventlogs 42 | ``` 43 | 44 | The example output for the event log in [grub boot](https://github.com/intel/tdx-tools/blob/main/doc/measure_log_grub_boot.txt) 45 | and [direct boot](https://github.com/intel/tdx-tools/blob/main/doc/measure_log_direct_boot.txt) 46 | 47 | 2. Get TD Report 48 | 49 | ``` 50 | ./tdx_tdreport 51 | ``` 52 | 53 | 3. Verify the RTMR 54 | 55 | ``` 56 | ./tdx_verify_rtmr 57 | ``` 58 | 59 | ### Installation 60 | 61 | Build and install TDX Measurement Tool: 62 | 63 | ```sh 64 | python3 setup.py bdist_wheel 65 | pip3 install dist/*.whl --force-reinstall 66 | ``` 67 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/tdx/event_recorder.go: -------------------------------------------------------------------------------- 1 | package tdx 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | 8 | cctrusted_vm "github.com/cc-api/cc-trusted-vmsdk/src/golang/cctrusted_vm" 9 | ) 10 | 11 | const ( 12 | DEFAULT_ACPI_TABLE_FILE = "/sys/firmware/acpi/tables/CCEL" 13 | DEFAULT_ACPI_TABLE_DATA_FILE = "/sys/firmware/acpi/tables/data/CCEL" 14 | ) 15 | 16 | var _ cctrusted_vm.EventRecorder = (*TDXEventLogRecorder)(nil) 17 | 18 | type TDXEventLogRecorder struct { 19 | acpiTableFile string 20 | acpiTableDataFile string 21 | rawEventLog []byte 22 | redirected bool 23 | // redirectedAcpiTableFile is the alternative 24 | // of the original `acpiTableFile`, if which 25 | // can not be accessed 26 | redirectedAcpiTableFile string 27 | // redirectedAcpiTableDataFile is the alternative 28 | // of the original `acpiTableDataFile`, if which 29 | // can not be accessed 30 | redirectedAcpiTableDataFile string 31 | } 32 | 33 | // FullEventLog implements cctrusted_vm.EventRecorder. 34 | func (t *TDXEventLogRecorder) FullEventLog() ([]byte, error) { 35 | path := t.acpiTableDataFile 36 | if t.redirected { 37 | path = t.redirectedAcpiTableDataFile 38 | } 39 | log, err := os.ReadFile(path) 40 | if err != nil { 41 | return nil, err 42 | } 43 | t.rawEventLog = log 44 | return t.rawEventLog, nil 45 | } 46 | 47 | // ProbeRecorder implements cctrusted_vm.EventRecorder. 48 | func (t *TDXEventLogRecorder) ProbeRecorder() error { 49 | t.acpiTableFile = DEFAULT_ACPI_TABLE_FILE 50 | t.acpiTableDataFile = DEFAULT_ACPI_TABLE_DATA_FILE 51 | if _, err := os.Stat(t.acpiTableFile); err != nil { 52 | if _, e := os.Stat(t.redirectedAcpiTableFile); e != nil { 53 | return fmt.Errorf("event log file open file: %v & %v", err, e) 54 | } 55 | t.redirected = true 56 | } 57 | if _, err := os.Stat(t.acpiTableDataFile); err != nil { 58 | if _, e := os.Stat(t.redirectedAcpiTableDataFile); e != nil { 59 | return fmt.Errorf("event log file open file: %v & %v", err, e) 60 | } 61 | } 62 | 63 | path := t.acpiTableFile 64 | if t.redirected { 65 | path = t.redirectedAcpiTableFile 66 | } 67 | 68 | if err := t.isValidCCELTable(path); err != nil { 69 | return err 70 | } 71 | 72 | return nil 73 | } 74 | 75 | func (t *TDXEventLogRecorder) isValidCCELTable(file string) error { 76 | d, err := os.ReadFile(file) 77 | if err != nil { 78 | return err 79 | } 80 | if len(d) < 4 || string(d[:4]) != "CCEL" { 81 | return errors.New("invalid CCEL table") 82 | } 83 | return nil 84 | } 85 | 86 | func (t *TDXEventLogRecorder) RedirectAcpiTableFile(file string) { 87 | t.redirectedAcpiTableFile = file 88 | } 89 | 90 | func (t *TDXEventLogRecorder) RedirectAcpiTableDataFile(file string) { 91 | t.redirectedAcpiTableDataFile = file 92 | } 93 | -------------------------------------------------------------------------------- /src/python/.gitignore: -------------------------------------------------------------------------------- 1 | # use the template from https://github.com/github/gitignore 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | 136 | # pytype static type analyzer 137 | .pytype/ 138 | 139 | # Cython debug symbols 140 | cython_debug/ -------------------------------------------------------------------------------- /src/pytdxmeasure/.gitignore: -------------------------------------------------------------------------------- 1 | # use the template from https://github.com/github/gitignore 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | 136 | # pytype static type analyzer 137 | .pytype/ 138 | 139 | # Cython debug symbols 140 | cython_debug/ -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cc-api/evidence-api/common/golang/evidence_api v0.0.0-20240729064808-21e12aa810c8 h1:IjmvJTssPaDuC/qkSDaLFv6u+9MFFH+nmTr4Duj6KhM= 2 | github.com/cc-api/evidence-api/common/golang/evidence_api v0.0.0-20240729064808-21e12aa810c8/go.mod h1:R1LPex62L4Ftnnw5vWp5sFNI5j8tnHtf0oQKvLvm9Pw= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 7 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 8 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 9 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 10 | github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= 11 | github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= 12 | github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= 13 | github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 16 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 17 | github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= 18 | github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= 19 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 20 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 21 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 22 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 23 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= 24 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 25 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 26 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 27 | golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= 28 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 29 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 30 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 31 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 32 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 33 | -------------------------------------------------------------------------------- /src/python/cc_event_log_cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Command line to dump the cc event logs 3 | """ 4 | import logging 5 | import argparse 6 | import os 7 | from evidence_api.api import EvidenceApi 8 | from evidence_api.eventlog import TcgEventLog 9 | from evidence_api.tcgcel import TcgTpmsCelEvent 10 | from evidence_api.tcg import TcgAlgorithmRegistry 11 | from cctrusted_vm.cvm import ConfidentialVM 12 | from cctrusted_vm.sdk import CCTrustedVmSdk 13 | 14 | 15 | LOG = logging.getLogger(__name__) 16 | 17 | logging.basicConfig(level=logging.NOTSET, format='%(name)s %(levelname)-8s %(message)s') 18 | 19 | def main(): 20 | """Example cc event log fetching utility.""" 21 | if ConfidentialVM.detect_cc_type() == EvidenceApi.TYPE_CC_NONE: 22 | LOG.error("This is not a confidential VM!") 23 | return 24 | if os.geteuid() != 0: 25 | LOG.error("Please run as root which is required for this example!") 26 | return 27 | 28 | parser = argparse.ArgumentParser( 29 | description="The example utility to fetch CC event logs") 30 | parser.add_argument('-s', type=int, 31 | help='index of first event log to fetch', dest='start') 32 | parser.add_argument("-c", type=int, help="number of event logs to fetch", 33 | dest="count") 34 | parser.add_argument("-f", type=str, help="enable canonical tlv format", default="false", 35 | dest="cel_format") 36 | args = parser.parse_args() 37 | 38 | event_logs = CCTrustedVmSdk.inst().get_cc_eventlog(args.start, args.count) 39 | if event_logs is None: 40 | LOG.error("No event log fetched. Check debug log for issues.") 41 | return 42 | LOG.info("Total %d of event logs fetched.", len(event_logs)) 43 | 44 | res = EvidenceApi.replay_cc_eventlog(event_logs) 45 | # pylint: disable-next=C0301 46 | LOG.info("Note: If the underlying platform is TDX, the IMR index showing is cc measurement register instead of TDX measurement register.") 47 | # pylint: disable-next=C0301 48 | LOG.info("Please refer to the spec https://www.intel.com/content/www/us/en/content-details/726790/guest-host-communication-interface-ghci-for-intel-trust-domain-extensions-intel-tdx.html") 49 | LOG.info("Replayed result of collected event logs:") 50 | # pylint: disable-next=C0201 51 | for k in sorted(res.keys()): 52 | LOG.info("IMR[%d]: ", k) 53 | for alg, h in res.get(k).items(): 54 | LOG.info(" %s: ", TcgAlgorithmRegistry.get_algorithm_string(alg)) 55 | LOG.info(" %s", h.hex()) 56 | 57 | LOG.info("Dump collected event logs:") 58 | for event in event_logs: 59 | if isinstance(event, TcgTpmsCelEvent): 60 | if args.cel_format.lower() == 'true': 61 | TcgTpmsCelEvent.encode(event, TcgEventLog.TCG_FORMAT_CEL_TLV).dump() 62 | else: 63 | event.to_pcclient_format().dump() 64 | else: 65 | event.dump() 66 | 67 | if __name__ == "__main__": 68 | main() 69 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/plugins/98-ima-enable-simple/README.md: -------------------------------------------------------------------------------- 1 | # IMA enable simple 2 | 3 | IMA is short Integrity Measurement Architecture. This plugin is a simple demo to enable IMA feature in the guest image, which can be regarded as a reference implementation. Please create you own IMA plugin in the production env. 4 | 5 | In this case, we have following assumption 6 | - users have their customized IMA policy, instead of built-in policy. 7 | - only option `ima_appraise` is included. 8 | - feature i_version mount is enabled. 9 | 10 | More info and options related to IMA can be found in Ref[1]. 11 | 12 | Let us go through this plugin and help you write a customized one. 13 | 14 | ## Customized IMA policy 15 | 16 | The policy MUST be placed in `files/etc/ima/` and named by `ima-policy`, otherwise it can not be recognized and loaded into the guest kernel. The policy file will be copied to `/etc/ima/ima-policy` in the guest image. 17 | 18 | This plugin uses the policy same as the built-in policy `tcb_appraise`. Other built-in policies can be found in Ref[2]. 19 | 20 | It is recommend to use a customized policy other than those default ones in production env. 21 | 22 | ## Feature IMA enabling 23 | 24 | To enable IMA, this plugin conducts following 2 steps. 25 | 26 | 1. Enabling IMA update and enabling i_version mount 27 | 28 | To enable IMA update, the script `host_run.sh` in the host invokes script `guest_enable_ima_fix.sh`, which will be executed in the guest image, by `virt-customize`. 29 | 30 | The script `guest_enable_ima_fix.sh` updates `/etc/fstab`, where we only enable i_version on partitions with ext4 format in the case, and append option `rootflags=i_version` in kernel cmdline to enable i_version mount on root partition. Additionally, it also appends option `ima_appraise=fix` in config file `/etc/default/grub.d/50-cloudimg-settings.cfg` and run `update-grub` to update kernel cmdline. If you guest image has other config files, please replace the config file path. 31 | 32 | Here, the IMA update is enabled, guest image can update IMA data at next launch, which typically refers to the next launch by the cloud-init in the following step. 33 | 34 | 2. Registering files hashes 35 | 36 | Registering files hashes will be completed by script `01-ima-register-file-hash.sh` in `cloud-init/x-shellscript`, which is generated by `host_run.sh`. The two-digital prefix of the script is necessary and indicates the execution order, that is to say, the script with smaller prefix will be executed earlier. 37 | 38 | Script `01-ima-register-file-hash.sh` registers files' hashes and update option `ima_appraise=fix` to `ima_appraise=log` in the kernel cmdline, to enable IMA appraisal at next launch. 39 | 40 | This script will not be invoked by us. They will be integrated into `user-data` with other config or scripts for `cloud-init`. Then the `cloud-init` will copy them into the guest image and execute they in order at most once. 41 | 42 | Ref: 43 | - [1] https://sourceforge.net/p/linux-IMA/wiki/Home/#enabling-IMA-measurement 44 | - [2] https://wiki.gentoo.org/wiki/Integrity_Measurement_Architecture/Recipes 45 | - [3] https://wiki.gentoo.org/wiki/Integrity_Measurement_Architecture 46 | -------------------------------------------------------------------------------- /src/pytdxmeasure/pytdxmeasure/binaryblob.py: -------------------------------------------------------------------------------- 1 | """ 2 | Manage the binary blob 3 | """ 4 | import logging 5 | import string 6 | import struct 7 | 8 | LOG = logging.getLogger(__name__) 9 | 10 | __author__ = "cpio" 11 | 12 | 13 | class BinaryBlob: 14 | """ 15 | Manage the binary blob. 16 | """ 17 | 18 | def __init__(self, data, base=0): 19 | self._data = data 20 | self._base_address = base 21 | 22 | @property 23 | def length(self): 24 | """ 25 | Length of binary in bytes 26 | """ 27 | return len(self._data) 28 | 29 | @property 30 | def data(self): 31 | """ 32 | Raw data of binary blob 33 | """ 34 | return self._data 35 | 36 | def to_hex_string(self): 37 | """ 38 | To hex string 39 | """ 40 | return "".join(f"{b:02x}" % b for b in self._data) 41 | 42 | def get_uint16(self, pos): 43 | """ 44 | Get UINT16 integer 45 | """ 46 | assert pos + 2 <= self.length 47 | return (struct.unpack(" # pylint: disable=line-too-long 4 | 5 | """ 6 | 7 | import os 8 | import logging 9 | 10 | from .binaryblob import BinaryBlob 11 | 12 | __author__ = "cpio" 13 | 14 | LOG = logging.getLogger(__name__) 15 | 16 | 17 | class CCEL(BinaryBlob): 18 | """ 19 | Manage the CCEL ACPI table. It should run within TD guest. 20 | """ 21 | 22 | @property 23 | def revision(self): 24 | """ 25 | Revision value in integer 26 | """ 27 | revision, _ = self.get_uint8(8) 28 | return revision 29 | 30 | @property 31 | def checksum(self): 32 | """ 33 | Checksum value in integer 34 | """ 35 | checksum, _ = self.get_uint8(9) 36 | return checksum 37 | 38 | @property 39 | def oem_id(self): 40 | """ 41 | OEM ID value in byte array 42 | """ 43 | oem_id, _ = self.get_bytes(10, 6) 44 | return oem_id 45 | 46 | @property 47 | def cc_type(self): 48 | """ 49 | Confidential Computing type in integer 50 | """ 51 | cc_type, _ = self.get_uint8(36) 52 | return cc_type 53 | 54 | @property 55 | def cc_subtype(self): 56 | """ 57 | Confidential Computing specific sub-type in integer 58 | """ 59 | cc_subtype, _ = self.get_uint8(37) 60 | return cc_subtype 61 | 62 | @property 63 | def log_area_minimum_length(self): 64 | """ 65 | LAML value in integer 66 | """ 67 | laml, _ = self.get_uint64(40) 68 | return laml 69 | 70 | @property 71 | def log_area_start_address(self): 72 | """ 73 | LASA value in integer 74 | """ 75 | lasa, _ = self.get_uint64(48) 76 | return lasa 77 | 78 | def dump(self): 79 | """ 80 | Dump the full information 81 | """ 82 | super().dump() 83 | 84 | if not self.is_valid(): 85 | LOG.error("CCEL is not valid") 86 | return 87 | 88 | LOG.info("Revision: %d", self.revision) 89 | LOG.info("Length: %d", self.length) 90 | LOG.info("Checksum: %02X", self.checksum) 91 | LOG.info("OEM ID: %s", self.oem_id) 92 | LOG.info("CC Type: %s", self.cc_type) 93 | LOG.info("CC Sub-type: %s", self.cc_subtype) 94 | LOG.info("Log Lenght: 0x%08X", self.log_area_minimum_length) 95 | LOG.info("Log Address: 0x%08X", self.log_area_start_address) 96 | 97 | def is_valid(self): 98 | """ 99 | Judge whether the CCEL data is valid. 100 | - Check the signature 101 | - Check the length 102 | """ 103 | return self.length > 0 and \ 104 | self.data[0:4] == b'CCEL' and \ 105 | self.length == self.data[4] 106 | 107 | @staticmethod 108 | def create_from_acpi_file(acpi_file="/sys/firmware/acpi/tables/CCEL"): 109 | """ 110 | Read the CCEL table from the /sys/firmware/acpi/tables/CCEL 111 | """ 112 | if not os.path.exists(acpi_file): 113 | LOG.error("Could not find the ACPI file %s", acpi_file) 114 | return None 115 | 116 | try: 117 | with open(acpi_file, "rb") as fobj: 118 | data = fobj.read() 119 | assert len(data) > 0 and data[0:4] == b'CCEL', \ 120 | "Invalid CCEL table" 121 | return CCEL(data) 122 | except (PermissionError, OSError): 123 | LOG.error("Need root permission to open file %s", acpi_file) 124 | return None 125 | -------------------------------------------------------------------------------- /src/python/tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Local conftest.py containing directory-specific hook implementations.""" 2 | 3 | import pytest 4 | from evidence_api.api import EvidenceApi 5 | from evidence_api.tcg import TcgAlgorithmRegistry 6 | from evidence_api.tdx.rtmr import TdxRTMR 7 | from cctrusted_vm.cvm import ConfidentialVM 8 | from cctrusted_vm.sdk import CCTrustedVmSdk 9 | import tdx_check 10 | 11 | cnf_default_alg = { 12 | EvidenceApi.TYPE_CC_TDX: TcgAlgorithmRegistry.TPM_ALG_SHA384 13 | } 14 | """Configurations of default algorithm. 15 | The configurations could be different for different confidential VMs. 16 | e.g. TDX use sha384 as the default. 17 | """ 18 | 19 | cnf_measurement_cnt = { 20 | EvidenceApi.TYPE_CC_TDX: TdxRTMR.RTMR_COUNT 21 | } 22 | """Configurations of measurement count. 23 | The configurations could be different for different confidential VMs. 24 | """ 25 | 26 | cnf_measurement_check = { 27 | EvidenceApi.TYPE_CC_TDX: tdx_check.tdx_check_measurement_imrs 28 | } 29 | """Configurations of measurement check functions. 30 | The configurations could be different for different confidential VMs. 31 | """ 32 | 33 | cnf_quote_check_valid_input = { 34 | EvidenceApi.TYPE_CC_TDX: tdx_check.tdx_check_quote_with_valid_input 35 | } 36 | """Configurations of quote check functions for valid input. 37 | The configurations could be different for different confidential VMs. 38 | """ 39 | 40 | cnf_quote_check_invalid_input = { 41 | EvidenceApi.TYPE_CC_TDX: tdx_check.tdx_check_quote_with_invalid_input 42 | } 43 | """Configurations of quote check functions for invalid input. 44 | The configurations could be different for different confidential VMs. 45 | """ 46 | 47 | cnf_replay_eventlog_check_valid_input = { 48 | EvidenceApi.TYPE_CC_TDX: tdx_check.tdx_check_replay_eventlog_with_valid_input 49 | } 50 | """Configurations of replay_eventlog check functions for valid input. 51 | The configurations could be different for different confidential VMs. 52 | """ 53 | 54 | cnf_replay_eventlog_check_invalid_input = { 55 | EvidenceApi.TYPE_CC_TDX: tdx_check.tdx_check_replay_eventlog_with_invalid_input 56 | } 57 | """Configurations of replay_eventlog check functions for invalid input. 58 | The configurations could be different for different confidential VMs. 59 | """ 60 | 61 | @pytest.fixture(scope="module") 62 | def vm_sdk(): 63 | """Get VMSDK instance.""" 64 | return CCTrustedVmSdk.inst() 65 | 66 | @pytest.fixture(scope="module") 67 | def default_alg_id(): 68 | """Get default algorithm.""" 69 | cc_type = ConfidentialVM.detect_cc_type() 70 | return cnf_default_alg[cc_type] 71 | 72 | @pytest.fixture(scope="module") 73 | def measurement_count(): 74 | """Get measurement count.""" 75 | cc_type = ConfidentialVM.detect_cc_type() 76 | return cnf_measurement_cnt[cc_type] 77 | 78 | @pytest.fixture(scope="module") 79 | def check_measurement(): 80 | """Return checker for measurement.""" 81 | cc_type = ConfidentialVM.detect_cc_type() 82 | return cnf_measurement_check[cc_type] 83 | 84 | @pytest.fixture(scope="module") 85 | def check_quote_valid_input(): 86 | """Return checker for quote when input is valid.""" 87 | cc_type = ConfidentialVM.detect_cc_type() 88 | return cnf_quote_check_valid_input[cc_type] 89 | 90 | @pytest.fixture(scope="module") 91 | def check_quote_invalid_input(): 92 | """Return checker for quote when input is invalid.""" 93 | cc_type = ConfidentialVM.detect_cc_type() 94 | return cnf_quote_check_invalid_input[cc_type] 95 | 96 | @pytest.fixture(scope="module") 97 | def check_replay_eventlog_valid_input(): 98 | """Return checker for replay_eventlog when input is valid.""" 99 | cc_type = ConfidentialVM.detect_cc_type() 100 | return cnf_replay_eventlog_check_valid_input[cc_type] 101 | 102 | @pytest.fixture(scope="module") 103 | def check_replay_eventlog_invalid_input(): 104 | """Return checker for replay_eventlog when input is invalid.""" 105 | cc_type = ConfidentialVM.detect_cc_type() 106 | return cnf_replay_eventlog_check_invalid_input[cc_type] 107 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/sdk/sdk.go: -------------------------------------------------------------------------------- 1 | package vmsdk 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "sync" 8 | 9 | cctrusted_vm "github.com/cc-api/cc-trusted-vmsdk/src/golang/cctrusted_vm" 10 | _ "github.com/cc-api/cc-trusted-vmsdk/src/golang/cctrusted_vm/tdx" 11 | 12 | "github.com/cc-api/evidence-api/common/golang/evidence_api" 13 | "github.com/cc-api/evidence-api/common/golang/evidence_api/tdx" 14 | ) 15 | 16 | var _ evidence_api.EvidenceAPI = (*SDK)(nil) 17 | 18 | type SDK struct { 19 | cvm cctrusted_vm.ConfidentialVM 20 | } 21 | 22 | // DumpCCReport implements evidence_api.EvidenceAPI. 23 | func (s *SDK) DumpCCReport(reportBytes []byte) error { 24 | vmCtx := s.cvm.CVMContext() 25 | switch vmCtx.VMType { 26 | case evidence_api.TYPE_CC_TDX: 27 | report, err := tdx.NewTdxReportFromBytes(reportBytes) 28 | if err != nil { 29 | return err 30 | } 31 | report.Dump(evidence_api.QuoteDumpFormatHuman) 32 | default: 33 | } 34 | return nil 35 | } 36 | 37 | // GetCCMeasurement implements evidence_api.EvidenceAPI. 38 | func (s *SDK) GetCCMeasurement(index int, alg evidence_api.TCG_ALG) (evidence_api.TcgDigest, error) { 39 | emptyRet := evidence_api.TcgDigest{} 40 | report, err := s.GetCCReport(nil, nil, nil) 41 | if err != nil { 42 | return emptyRet, err 43 | } 44 | group := report.IMRGroup() 45 | if index > group.MaxIndex { 46 | return emptyRet, fmt.Errorf("index %d larger than max index %d", index, group.MaxIndex) 47 | } 48 | entry := group.Group[index] 49 | if entry.AlgID != alg { 50 | return emptyRet, fmt.Errorf("required alg %v, but got alg %v", alg, entry.AlgID) 51 | } 52 | return entry, nil 53 | } 54 | 55 | // GetMeasurementCount implements evidence_api.EvidenceAPI. 56 | func (s *SDK) GetMeasurementCount() (int, error) { 57 | return s.cvm.MaxImrIndex() + 1, nil 58 | } 59 | 60 | // ReplayCCEventLog implements evidence_api.EvidenceAPI. 61 | func (s *SDK) ReplayCCEventLog(formatedEventLogs []evidence_api.FormatedTcgEvent) map[int]map[evidence_api.TCG_ALG][]byte { 62 | return evidence_api.ReplayFormatedEventLog(formatedEventLogs) 63 | } 64 | 65 | // GetDefaultAlgorithm implements evidence_api.EvidenceAPI. 66 | func (s *SDK) GetDefaultAlgorithm() (evidence_api.TCG_ALG, error) { 67 | return s.cvm.DefaultAlgorithm(), nil 68 | } 69 | 70 | // SelectEventlog implements EvidenceAPI. 71 | func (s *SDK) GetCCEventLog(params ...int32) ([]evidence_api.FormatedTcgEvent, error) { 72 | el, err := s.internelEventlog() 73 | if err != nil { 74 | return nil, err 75 | } 76 | el.Parse() 77 | 78 | var start int32 79 | var count int32 80 | 81 | // Fetch optional params according to user specification 82 | if len(params) > 2 || len(params) == 0 { 83 | log.Fatalf("Invalid params specified. Using default values.") 84 | start = 0 85 | count = int32(len(el.EventLog())) 86 | } else if len(params) == 2 { 87 | start = params[0] 88 | count = params[1] 89 | } else { 90 | start = params[0] 91 | count = int32(len(el.EventLog())) - start 92 | } 93 | 94 | if start != 0 || count != 0 { 95 | el, err = el.Select(int(start), int(count)) 96 | if err != nil { 97 | return nil, err 98 | } 99 | } 100 | 101 | return el.EventLog(), nil 102 | } 103 | 104 | func (s *SDK) internelEventlog() (*evidence_api.EventLogger, error) { 105 | if s.cvm == nil { 106 | return nil, errors.New("no available cvm in sdk") 107 | } 108 | 109 | eventLogBytes, err := s.cvm.FullEventLog() 110 | if err != nil { 111 | return nil, err 112 | } 113 | 114 | imaLogBytes, err := s.cvm.FullIMALog() 115 | if err != nil { 116 | return nil, err 117 | } 118 | 119 | el := evidence_api.NewEventLogger(eventLogBytes, imaLogBytes, evidence_api.TCG_PCCLIENT_FORMAT) 120 | return el, nil 121 | } 122 | 123 | // Report implements EvidenceAPI. 124 | func (s *SDK) GetCCReport(nonce, userData []byte, extraArgs map[string]any) (evidence_api.Report, error) { 125 | if s.cvm == nil { 126 | return nil, errors.New("no available cvm in sdk") 127 | } 128 | 129 | reportStruct, err := s.cvm.Report(nonce, userData, extraArgs) 130 | if err != nil { 131 | return nil, err 132 | } 133 | 134 | vmCtx := s.cvm.CVMContext() 135 | switch vmCtx.VMType { 136 | case evidence_api.TYPE_CC_TDX: 137 | report, err := tdx.NewTdxReportFromBytes(reportStruct.Outblob) 138 | if err != nil { 139 | return nil, err 140 | } 141 | return report, nil 142 | default: 143 | } 144 | return nil, errors.New("parse" + vmCtx.VMType.String() + "report failed") 145 | } 146 | 147 | var once sync.Once 148 | 149 | var instance SDK 150 | var sdkErr error 151 | 152 | func GetSDKInstance(args *cctrusted_vm.CVMInitArgs) (SDK, error) { 153 | once.Do(func() { 154 | cvm, err := cctrusted_vm.GetCVMInstance(args) 155 | if err != nil { 156 | sdkErr = err 157 | } else { 158 | instance.cvm = cvm 159 | } 160 | }) 161 | return instance, sdkErr 162 | } 163 | -------------------------------------------------------------------------------- /src/python/tests/test_sdk.py: -------------------------------------------------------------------------------- 1 | """Containing unit test cases for sdk class""" 2 | 3 | from evidence_api.ccreport import CcReport, CcReportData, CcReportSignature 4 | from evidence_api.tcg import TcgImrEvent, TcgPcClientImrEvent 5 | from evidence_api.tcgcel import TcgTpmsCelEvent 6 | import pytest 7 | 8 | def test_get_default_algorithms(vm_sdk, default_alg_id): 9 | """Test get_default_algorithms() function.""" 10 | algo = vm_sdk.get_default_algorithms() 11 | assert algo is not None 12 | assert algo.alg_id == default_alg_id 13 | 14 | def test_get_measurement_count(vm_sdk, measurement_count): 15 | """Test get_measurement_count() function.""" 16 | count = vm_sdk.get_measurement_count() 17 | assert count is not None 18 | assert count == measurement_count 19 | 20 | def test_get_cc_measurement_with_invalid_input(vm_sdk): 21 | """Test get_cc_measurement() function with invalid input.""" 22 | # calling get_cc_measurement() with invalid IMR index 23 | measurement = vm_sdk.get_cc_measurement([-1, 0xC]) 24 | assert measurement is None 25 | 26 | # calling get_cc_measurement() with invalid algorithm ID 27 | measurement = vm_sdk.get_cc_measurement([0, None]) 28 | assert measurement is not None 29 | 30 | def test_get_cc_measurement_with_valid_input(vm_sdk, check_measurement): 31 | """Test get_cc_measurement() function with valid input.""" 32 | count = vm_sdk.get_measurement_count() 33 | for index in range(count): 34 | alg = vm_sdk.get_default_algorithms() 35 | digest_obj = vm_sdk.get_cc_measurement([index, alg.alg_id]) 36 | assert digest_obj is not None 37 | check_measurement() 38 | 39 | def test_get_cc_eventlog_with_invalid_input(vm_sdk): 40 | """Test get_cc_eventlog() function with invalid input. 41 | 42 | The test logic currently has an assumption that the return result of get_cc_eventlog 43 | doesn't change if the input parameters are the same within a short period of time. 44 | """ 45 | event_num = len(vm_sdk.get_cc_eventlog()) 46 | idx_min = 0 47 | idx_max = event_num - 1 48 | cnt_min = 1 49 | cnt_max = event_num 50 | 51 | # calling get_cc_eventlog with invalid "start" 52 | with pytest.raises(ValueError): 53 | invalid_start = idx_min - 1 54 | vm_sdk.get_cc_eventlog(start=invalid_start, count=1) 55 | with pytest.raises(ValueError): 56 | invalid_start = idx_max + 2 57 | vm_sdk.get_cc_eventlog(start=invalid_start, count=1) 58 | # a special case works as current design 59 | invalid_start = idx_max + 1 60 | eventlog = vm_sdk.get_cc_eventlog(start=invalid_start, count=1) 61 | assert len(eventlog) == 0 62 | 63 | # calling get_cc_eventlog with invalid "count" 64 | with pytest.raises(ValueError): 65 | invalid_count = cnt_min - 1 66 | vm_sdk.get_cc_eventlog(start=idx_min, count=invalid_count) 67 | with pytest.raises(ValueError): 68 | invalid_count = cnt_max + 1 69 | vm_sdk.get_cc_eventlog(start=idx_min, count=invalid_count) 70 | with pytest.raises(ValueError): 71 | vm_sdk.get_cc_eventlog(start=idx_max, count=2) 72 | 73 | def test_get_cc_eventlog_with_valid_input(vm_sdk): 74 | """Test get_eventlog() funtion with valid input.""" 75 | eventlog = vm_sdk.get_cc_eventlog() 76 | 77 | # Check 1: the eventlog should not be None. 78 | assert eventlog is not None 79 | 80 | # Check 2: the object type should be correct. 81 | assert isinstance(eventlog, list) 82 | event_count = 0 83 | for e in eventlog: 84 | event_count += 1 85 | assert isinstance(e, (TcgImrEvent, TcgPcClientImrEvent, TcgTpmsCelEvent)) 86 | 87 | def test_get_cc_report_with_valid_input(vm_sdk, check_quote_valid_input): 88 | """Test get_cc_report() function with valid input.""" 89 | quote = vm_sdk.get_cc_report(None, None, None) 90 | 91 | # Check 1: the quote should not be None. 92 | assert quote is not None 93 | 94 | # Check 2: the object type should be correct. 95 | assert isinstance(quote, CcReport) 96 | quoted_data = quote.get_quoted_data() 97 | assert quoted_data is not None 98 | assert isinstance(quoted_data, CcReportData) 99 | sigature_data = quote.get_sig() 100 | assert sigature_data is not None 101 | assert isinstance(sigature_data, CcReportSignature) 102 | 103 | # Check 3: platform specific check. 104 | check_quote_valid_input() 105 | 106 | def test_get_cc_report_with_invalid_input(check_quote_invalid_input): 107 | """Test get_cc_report() function with invalid input.""" 108 | check_quote_invalid_input() 109 | 110 | def test_replay_cc_eventlog_with_valid_input(check_replay_eventlog_valid_input): 111 | """Test replay_cc_eventlog() function with valid input.""" 112 | check_replay_eventlog_valid_input() 113 | 114 | def test_replay_cc_eventlog_with_invalid_input(check_replay_eventlog_invalid_input): 115 | """Test replay_cc_eventlog() function with invalid input.""" 116 | check_replay_eventlog_invalid_input() 117 | -------------------------------------------------------------------------------- /src/golang/cctrusted_vm/cvm.go: -------------------------------------------------------------------------------- 1 | package vmsdk 2 | 3 | import ( 4 | "crypto/sha512" 5 | "encoding/base64" 6 | "errors" 7 | "os" 8 | "path/filepath" 9 | "strconv" 10 | 11 | "github.com/cc-api/evidence-api/common/golang/evidence_api" 12 | ) 13 | 14 | const ( 15 | TSM_PREFIX = "/sys/kernel/config/tsm/report" 16 | ) 17 | 18 | type Device interface { 19 | ProbeDevice() error 20 | Report(nonce, userData []byte, extraArgs map[string]any) (evidence_api.CcReport, error) 21 | Name() string 22 | CCType() evidence_api.CC_Type 23 | Version() evidence_api.DeviceVersion 24 | } 25 | 26 | type GenericDevice struct { 27 | Device 28 | } 29 | 30 | func (d *GenericDevice) Report(nonce, userData []byte, extraArgs map[string]any) (evidence_api.CcReport, error) { 31 | var err error 32 | if _, err = os.Stat(TSM_PREFIX); os.IsNotExist(err) { 33 | return evidence_api.CcReport{}, errors.New("Configfs TSM is not supported in the current environment.") 34 | } 35 | 36 | // concatenate nonce and userData 37 | // check if the data is base64 encoded, if yes, decode before doing hash 38 | hasher := sha512.New() 39 | if nonce != nil { 40 | val, err := base64.StdEncoding.DecodeString(string(nonce)) 41 | if err != nil { 42 | hasher.Write(nonce) 43 | } else { 44 | hasher.Write(val) 45 | } 46 | } 47 | if userData != nil { 48 | val, err := base64.StdEncoding.DecodeString(string(userData)) 49 | if err != nil { 50 | hasher.Write(userData) 51 | } else { 52 | hasher.Write(val) 53 | } 54 | } 55 | reportData := []byte(hasher.Sum(nil)) 56 | 57 | tempdir, err := os.MkdirTemp(TSM_PREFIX, "report_") 58 | if err != nil { 59 | return evidence_api.CcReport{}, errors.New("Failed to init entry in Configfs TSM.") 60 | } 61 | defer os.RemoveAll(tempdir) 62 | 63 | if _, err = os.Stat(filepath.Join(tempdir, "inblob")); !os.IsNotExist(err) { 64 | err = os.WriteFile(filepath.Join(tempdir, "inblob"), reportData, 0400) 65 | if err != nil { 66 | return evidence_api.CcReport{}, errors.New("Failed to push report data into inblob.") 67 | } 68 | } 69 | 70 | if v, ok := extraArgs["privilege"]; ok { 71 | if val, ok := v.(int); ok { 72 | err = os.WriteFile(filepath.Join(tempdir, "privlevel"), []byte(strconv.Itoa(val)), 0400) 73 | if err != nil { 74 | return evidence_api.CcReport{}, errors.New("Failed to push privilege data to privlevel file.") 75 | } 76 | } 77 | } 78 | 79 | var outblob, provider, auxblob []byte 80 | var generation int 81 | if _, err = os.Stat(filepath.Join(tempdir, "outblob")); !os.IsNotExist(err) { 82 | outblob, err = os.ReadFile(filepath.Join(tempdir, "outblob")) 83 | if err != nil { 84 | return evidence_api.CcReport{}, errors.New("Failed to get outblob.") 85 | } 86 | } 87 | 88 | if _, err = os.Stat(filepath.Join(tempdir, "generation")); !os.IsNotExist(err) { 89 | rawGeneration, err := os.ReadFile(filepath.Join(tempdir, "generation")) 90 | if err != nil { 91 | return evidence_api.CcReport{}, errors.New("Failed to get generation info.") 92 | } 93 | generation, _ = strconv.Atoi(string(rawGeneration)) 94 | // Check if the outblob has been corrupted during file open 95 | if generation > 1 { 96 | return evidence_api.CcReport{}, errors.New("Found corrupted generation.") 97 | } 98 | } 99 | 100 | if _, err = os.Stat(filepath.Join(tempdir, "provider")); !os.IsNotExist(err) { 101 | provider, err = os.ReadFile(filepath.Join(tempdir, "provider")) 102 | if err != nil { 103 | return evidence_api.CcReport{}, errors.New("Failed to get provider info.") 104 | } 105 | } 106 | 107 | if _, err = os.Stat(filepath.Join(tempdir, "auxblob")); !os.IsNotExist(err) { 108 | auxblob, err = os.ReadFile(filepath.Join(tempdir, "auxblob")) 109 | if err != nil { 110 | return evidence_api.CcReport{}, errors.New("Failed to get auxblob info.") 111 | } 112 | } 113 | 114 | return evidence_api.CcReport{ 115 | Outblob: outblob, 116 | Provider: string(provider), 117 | Generation: generation, 118 | Auxblob: auxblob, 119 | }, nil 120 | } 121 | 122 | type EventRecorder interface { 123 | ProbeRecorder() error 124 | FullEventLog() ([]byte, error) 125 | } 126 | 127 | type CVMContext struct { 128 | VMType evidence_api.CC_Type 129 | Version evidence_api.DeviceVersion 130 | } 131 | 132 | type ConfidentialVM interface { 133 | Probe() error 134 | CVMContext() CVMContext 135 | MaxImrIndex() int 136 | DefaultAlgorithm() evidence_api.TCG_ALG 137 | Device 138 | EventRecorder 139 | evidence_api.IMARecorder 140 | } 141 | 142 | type CVMInitArgs struct { 143 | // RedirectedAcpiTableFile is the alternative 144 | // of the original `DEFAULT_ACPI_TABLE_FILE`, if which 145 | // can not be accessed 146 | RedirectedAcpiTableFile string 147 | // RedirectedAcpiTableDataFile is the alternative 148 | // of the original `DEFAULT_ACPI_TABLE_DATA_FILE`, if which 149 | // can not be accessed 150 | RedirectedAcpiTableDataFile string 151 | } 152 | 153 | type CVMInitFunc func(*CVMInitArgs) (ConfidentialVM, error) 154 | 155 | var cvmInitFuncs []CVMInitFunc 156 | 157 | func RegisterCVMInitFunc(fn CVMInitFunc) { 158 | cvmInitFuncs = append(cvmInitFuncs, fn) 159 | } 160 | 161 | func GetCVMInstance(args *CVMInitArgs) (ConfidentialVM, error) { 162 | for _, fn := range cvmInitFuncs { 163 | cvm, err := fn(args) 164 | if err != nil { 165 | continue 166 | } 167 | return cvm, nil 168 | } 169 | return nil, errors.New("no available confidential vm") 170 | } 171 | -------------------------------------------------------------------------------- /src/python/cctrusted_vm/sdk.py: -------------------------------------------------------------------------------- 1 | """ 2 | The VMSDK implementation for ``CCTrusted`` API. 3 | """ 4 | import logging 5 | 6 | # pylint: disable=unused-import 7 | from evidence_api.api import EvidenceApi 8 | from evidence_api.imr import TcgIMR 9 | from evidence_api.ccreport import CcReport 10 | from evidence_api.eventlog import EventLogs 11 | from evidence_api.eventlog import TcgEventLog 12 | from evidence_api.tcg import TcgAlgorithmRegistry 13 | from cctrusted_vm.cvm import ConfidentialVM 14 | 15 | 16 | LOG = logging.getLogger(__name__) 17 | 18 | class CCTrustedVmSdk(EvidenceApi): 19 | 20 | """Evidence API implementation for a general CVM.""" 21 | 22 | _inst = None 23 | 24 | @classmethod 25 | def inst(cls): 26 | """Singleton instance function.""" 27 | if cls._inst is None: 28 | cls._inst = cls() 29 | return cls._inst 30 | 31 | def __init__(self): 32 | """Contrustor of CCTrustedCVM.""" 33 | self._cvm = ConfidentialVM.inst() 34 | 35 | def get_default_algorithms(self) -> TcgAlgorithmRegistry: 36 | """Get the default Digest algorithms supported by trusted foundation. 37 | 38 | Different trusted foundation may support different algorithms, for example 39 | the Intel TDX use SHA384, TPM uses SHA256. 40 | 41 | Beyond the default digest algorithm, some trusted foundation like TPM 42 | may support multiple algorithms. 43 | 44 | Returns: 45 | The default algorithms. 46 | """ 47 | return TcgAlgorithmRegistry(self._cvm.default_algo_id) 48 | 49 | def get_measurement_count(self) -> int: 50 | """Get the count of measurement register. 51 | 52 | Different trusted foundation may provide different count of measurement 53 | register. For example, Intel TDX TDREPORT provides the 4 measurement 54 | register by default. TPM provides 24 measurement (0~16 for SRTM and 17~24 55 | for DRTM). 56 | 57 | Beyond the real mesurement register, some SDK may extend virtual measurement 58 | reigster for additional trust chain like container, namespace, cluster in 59 | cloud native paradiagm. 60 | 61 | Returns: 62 | The count of measurement registers 63 | """ 64 | return len(self._cvm.imrs) 65 | 66 | def get_cc_measurement(self, imr_select:[int, int]) -> TcgIMR: 67 | """Get measurement register according to given selected index and algorithms 68 | 69 | Each trusted foundation in CC environment provides the multiple measurement 70 | registers, the count is update to ``get_measurement_count()``. And for each 71 | measurement register, it may provides multiple digest for different algorithms. 72 | 73 | Args: 74 | imr_select ([int, int]): The first is index of measurement register, 75 | the second is the alrogithms ID 76 | 77 | Returns: 78 | The object of TcgIMR 79 | """ 80 | imr_index = imr_select[0] 81 | algo_id = imr_select[1] 82 | 83 | if imr_index not in self._cvm.imrs: 84 | LOG.error("Invalid select index for IMR.") 85 | return None 86 | 87 | if algo_id is None or algo_id is TcgAlgorithmRegistry.TPM_ALG_ERROR: 88 | algo_id = self._cvm.default_algo_id 89 | 90 | # Re-do the processing to fetch the latest measurements 91 | self._cvm.process_cc_report() 92 | 93 | return self._cvm.imrs[imr_index] 94 | 95 | def get_cc_report( 96 | self, 97 | nonce: bytearray = None, 98 | data: bytearray = None, 99 | extraArgs = None 100 | ) -> CcReport: 101 | """Get the CcReport (i.e. quote) for given nonce and data. 102 | 103 | The CcReport is signing of attestation data (IMR values or hashes of IMR 104 | values), made by a trusted foundation (TPM) using a key trusted by the 105 | verifier. 106 | 107 | Different trusted foundation may use different quote format. 108 | 109 | Args: 110 | nonce (bytearray): against replay attacks. 111 | data (bytearray): user data 112 | extraArgs: for TPM, it will be given list of IMR/PCRs 113 | 114 | Returns: 115 | The ``CcReport`` object. Return None if it fails. 116 | """ 117 | return self._cvm.get_cc_report(nonce, data, extraArgs) 118 | 119 | def get_cc_eventlog(self, start:int = None, count:int = None) -> list: 120 | """Get eventlog for given index and count. 121 | 122 | TCG log in Eventlog. Verify to spoof events in the TCG log, hence defeating 123 | remotely-attested measured-boot. 124 | To measure the full CC runtime environment, the eventlog may include addtional 125 | OS type and cloud native type event beyond the measured-boot. 126 | 127 | Args: 128 | start(int): the first index of event log to fetch 129 | count(int): the number of event logs to fetch 130 | 131 | Returns: 132 | Parsed event logs following TCG Spec. 133 | """ 134 | # Re-do the processing to fetch the latest event logs 135 | self._cvm.process_eventlog() 136 | 137 | event_logs = EventLogs(self._cvm.boot_time_event_log, self._cvm.runtime_event_log, 138 | self._cvm.cc_type, TcgEventLog.TCG_FORMAT_PCCLIENT) 139 | 140 | event_logs.select(start, count) 141 | 142 | return event_logs.event_logs 143 | -------------------------------------------------------------------------------- /src/cvm-image-rewriter/virt-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Create TD VM from libvirt template 4 | # 5 | set -e 6 | set -m 7 | set -x 8 | 9 | CURR_DIR=$(readlink -f "$(dirname "$0")") 10 | 11 | GUEST_IMG="tdx-guest-ubuntu22.04.qcow2" 12 | GUEST_NAME="tdx-guest" 13 | GUEST_KERNEL="/boot/vmlinuz" 14 | OVMF_CODE="/usr/share/qemu/OVMF.fd" 15 | 16 | GUEST_ROOTDIR=/tmp/libvirt-vms 17 | TEMPLATE="${CURR_DIR}/tdx-libvirt-ubuntu-host.xml.template" 18 | FORCE=false 19 | VCPU_NUM=1 20 | MEM_SIZE=4 21 | 22 | # TDX socket 23 | TDX_SOCKET="/var/local/run/libvirt/libvirt-sock" 24 | 25 | # log file 26 | LOG="/tmp/vm_log_$(date +"%FT%H%M").log" 27 | 28 | usage() { 29 | cat << EOM 30 | Usage: $(basename "$0") [OPTION]... 31 | -i Default is tdx-guest-ubuntu22.04.qcow2 under current directory 32 | -n Name of TD guest 33 | -k Name of kernel 34 | -t