├── example-setup ├── vm-config │ ├── meta-data │ ├── network-config │ ├── cmdline │ ├── etc-acpi-rsdp │ ├── etc-acpi-tables │ ├── etc-table-loader │ ├── cmcctl.service │ ├── calculate-srtm-pcrs.cfg │ ├── cmcd.service │ ├── cmcctl-conf-host.json │ ├── cmcctl-conf-vm.json │ ├── est-server-conf.json │ ├── metadata-ca.pem │ ├── cmcd-conf.json │ └── user-data ├── tpm │ └── tpm-ek-certs.db ├── metadata-templates │ ├── manifest.description.json │ ├── company.description.json │ ├── device.description.json │ ├── snp.version.json │ ├── tdx.policy.json │ ├── sgx.policy.json │ ├── snp.policy.json │ └── manifest.json ├── pki-input-ids │ ├── certifier_A.json │ ├── certifier_B.json │ ├── developer_A.json │ ├── developer_B.json │ ├── evaluator_A.json │ ├── evaluator_B.json │ ├── operator_A.json │ ├── operator_B.json │ ├── user_sub_ca.json │ ├── device_sub_ca.json │ ├── cfssl-est-tls.json │ ├── ca.json │ ├── ocsp_subcas.json │ ├── ocsp_users.json │ ├── ocsp_devices.json │ ├── certs_users.sql │ ├── certs_devices.sql │ ├── certs_subcas.sql │ └── ca-config.json ├── pki-input │ ├── cfssl-leaf.json │ ├── cfssl-est-tls.json │ ├── cfssl-ca.json │ └── cfssl-ca-config.json ├── cas │ ├── README.md │ └── SectigoRSAOrganizationValidationSecureServerCA.pem ├── configs │ ├── kernel-setup-header.json │ ├── cmcctl-conf.json │ ├── est-server-conf-ids.json │ ├── est-server-conf.json │ ├── cmcd-conf-selfsigned.json │ ├── cmcctl-conf-sgx.json │ └── cmcd-conf.json ├── policies │ ├── policies-overwrite.js │ ├── policies.js │ └── policies-ids.js ├── sgx │ ├── enclave.json │ └── tdx-pccs-conf.json └── docker │ ├── tdx-qgs.dockerfile │ ├── tdx-pccs.dockerfile │ └── cmc.dockerfile ├── tools ├── fvextract │ ├── .gitignore │ ├── README.md │ └── main.go ├── backend │ ├── Makefile │ ├── main.go │ └── README.md ├── snptool │ ├── cpuid_amd64.go │ ├── cpuid_amd64.s │ └── config.go ├── mrtool │ ├── cgo │ │ ├── Precomp.h │ │ ├── 7zVersion.h │ │ ├── PiBootMode.h │ │ ├── Compiler.h │ │ ├── SecMain.h │ │ ├── MeasureBootPeCoff.h │ │ ├── LzHash.h │ │ ├── glue.go │ │ ├── Bra86.c │ │ ├── Bra.h │ │ └── LICENSE │ ├── precomputetpm │ │ └── helpers.go │ ├── tcg │ │ └── mrindex.go │ ├── README.md │ ├── main.go │ └── precomputetdx │ │ └── mrseam.go ├── hextobin │ ├── README.md │ └── main.go ├── schema-generator │ └── README.md ├── fmspc-retrieval-tool │ └── main.go ├── azuretool │ └── tdreport.go ├── measure-bundle │ └── config.go └── metasign │ ├── json.go │ └── cbor.go ├── cmcd ├── Makefile ├── api.go ├── systemd.go └── main.go ├── provision ├── cpuid_arm64.go ├── cpuid_riscv64.go ├── estserver │ └── main.go └── tpm_test.go ├── grpcapi ├── Makefile └── version.go ├── bin ├── vm-ssh ├── cmc-docker-rm ├── vm-scp ├── vm-swtpm-loop ├── vm-estserver ├── vm-cmcctl ├── generate-metadata-sgx ├── precompute-metadata-vm ├── generate-metadata-tdx ├── generate-metadata-vm ├── vm-update-cmcd ├── generate-metadata-tpm ├── vm-swtpm ├── vm-extract-acpi-tables ├── generate-metadata-snp ├── vm-update-metadata ├── run-tdx-pccs ├── run-tdx-qgs ├── vm-retrieve-artifacts ├── sign-metadata ├── build-edk2 ├── generate-device-description ├── vm-start ├── snp-ec2-setup ├── setup-pki ├── setup-cmc ├── vm-retrieve-gpt └── generate-app-manifest-tpm ├── env.bash ├── env.zsh ├── doc ├── api │ └── json │ │ ├── api │ │ ├── PSSOptions.json │ │ ├── TLSCertRequest.json │ │ ├── SocketError.json │ │ ├── PeerCacheRequest.json │ │ ├── MeasureResponse.json │ │ ├── TLSSignResponse.json │ │ ├── PeerCacheResponse.json │ │ ├── TLSCertResponse.json │ │ ├── AttestationRequest.json │ │ ├── AttestationResponse.json │ │ ├── TLSSignRequest.json │ │ └── VerificationRequest.json │ │ └── attestedtls │ │ ├── AtlsHandshakeComplete.json │ │ ├── AtlsHandshakeRequest.json │ │ └── AtlsHandshakeResponse.json ├── tools.md ├── dev.md ├── ids-example-setup.md ├── policies.md ├── setup-snp.md ├── setup-tdx.md └── build-and-install.md ├── cmcctl ├── Makefile ├── commands.go ├── token.go └── main.go ├── attestationpolicies └── duktape │ ├── policies.h │ └── policies.c ├── cmc ├── sw.go ├── tpm.go ├── sgx.go ├── snp.go ├── tdx.go ├── azure.go └── metadata_test.go ├── verifier ├── duktapepolicies.go ├── jspolicies.go └── tdreport.go ├── .gitignore ├── internal ├── snp.go ├── helpers_test.go ├── auth.go └── hash.go ├── attestationreport ├── provision.go └── serializer.go ├── attestedhttp └── server.go ├── jsoncanonicalizer └── es6numfmt.go ├── measure └── rootfs.go ├── attestedtls └── backend.go └── README.md /example-setup/vm-config/meta-data: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example-setup/vm-config/network-config: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/fvextract/.gitignore: -------------------------------------------------------------------------------- 1 | /fvextract 2 | -------------------------------------------------------------------------------- /tools/backend/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | go build --tags="json1" 3 | -------------------------------------------------------------------------------- /tools/snptool/cpuid_amd64.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func cpuid(op uint32) (eax, ebx, ecx, edx uint32) 4 | -------------------------------------------------------------------------------- /example-setup/tpm/tpm-ek-certs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-AISEC/cmc/HEAD/example-setup/tpm/tpm-ek-certs.db -------------------------------------------------------------------------------- /cmcd/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | go build 3 | 4 | minimal: 5 | go build -ldflags="-s -w" -tags nodefaults,socket,tpm,snp,tdx,sw 6 | -------------------------------------------------------------------------------- /example-setup/vm-config/cmdline: -------------------------------------------------------------------------------- 1 | BOOT_IMAGE=/vmlinuz-6.8.0-57-generic root=LABEL=cloudimg-rootfs ro console=tty1 console=ttyS0 -------------------------------------------------------------------------------- /example-setup/vm-config/etc-acpi-rsdp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-AISEC/cmc/HEAD/example-setup/vm-config/etc-acpi-rsdp -------------------------------------------------------------------------------- /example-setup/vm-config/etc-acpi-tables: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-AISEC/cmc/HEAD/example-setup/vm-config/etc-acpi-tables -------------------------------------------------------------------------------- /example-setup/vm-config/etc-table-loader: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-AISEC/cmc/HEAD/example-setup/vm-config/etc-table-loader -------------------------------------------------------------------------------- /provision/cpuid_arm64.go: -------------------------------------------------------------------------------- 1 | package provision 2 | 3 | func cpuid(op uint32) (eax, ebx, ecx, edx uint32) { 4 | return 0, 0, 0, 0 5 | } 6 | -------------------------------------------------------------------------------- /provision/cpuid_riscv64.go: -------------------------------------------------------------------------------- 1 | package provision 2 | 3 | func cpuid(op uint32) (eax, ebx, ecx, edx uint32) { 4 | return 0, 0, 0, 0 5 | } 6 | -------------------------------------------------------------------------------- /example-setup/metadata-templates/manifest.description.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Manifest Description", 3 | "name": "", 4 | "manifest": "" 5 | } -------------------------------------------------------------------------------- /grpcapi/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: grpcapi.go 3 | 4 | grpcapi.go: grpcapi.proto 5 | protoc --go_out=. --go_opt=paths=source_relative \ 6 | --go-grpc_out=. --go-grpc_opt=paths=source_relative \ 7 | $< 8 | -------------------------------------------------------------------------------- /bin/vm-ssh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | dir=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P) 6 | 7 | data="${dir}/vm" 8 | 9 | exec ssh \ 10 | -F "${data}/ssh/config" \ 11 | vm-ubuntu \ 12 | "$@" -------------------------------------------------------------------------------- /example-setup/pki-input-ids/certifier_A.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Certifier A", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Certifier" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/certifier_B.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Certifier B", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Certifier" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/developer_A.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Developer A", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Developer" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/developer_B.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Developer A", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Developer" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/evaluator_A.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Evaluator A", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Evaluator" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/evaluator_B.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Evaluator B", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Evaluator" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/operator_A.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Operator A", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Operator" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/operator_B.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Operator B", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Operator" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /tools/mrtool/cgo/Precomp.h: -------------------------------------------------------------------------------- 1 | /* Precomp.h -- StdAfx 2 | 2013-11-12 : Igor Pavlov : Public domain */ 3 | 4 | #ifndef __7Z_PRECOMP_H 5 | #define __7Z_PRECOMP_H 6 | 7 | #include "Compiler.h" 8 | /* #include "7zTypes.h" */ 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /env.bash: -------------------------------------------------------------------------------- 1 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)" 2 | 3 | case "${PATH}" in ""|"${dir}/bin"*) ;; *) PATH="${dir}/bin:${PATH}";; esac 4 | 5 | function _cmc-docker () { 6 | _command_offset 1 7 | } 8 | 9 | complete -F _cmc-docker 'cmc-docker' -------------------------------------------------------------------------------- /example-setup/metadata-templates/company.description.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "Company Description", 3 | "name" : "", 4 | "version" : "", 5 | "description" : "", 6 | "validity" : { 7 | "notBefore": "", 8 | "notAfter": "" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tools/snptool/cpuid_amd64.s: -------------------------------------------------------------------------------- 1 | //go:build amd64 2 | // +build amd64 3 | 4 | TEXT ·cpuid(SB), 7, $0 5 | XORQ CX, CX 6 | MOVL op+0(FP), AX 7 | CPUID 8 | MOVL AX, eax+8(FP) 9 | MOVL BX, ebx+12(FP) 10 | MOVL CX, ecx+16(FP) 11 | MOVL DX, edx+20(FP) 12 | RET 13 | -------------------------------------------------------------------------------- /bin/cmc-docker-rm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dir=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P) 4 | 5 | name="cmc$(printf "%s" "${dir}" | tr '/' '-')" 6 | 7 | docker stop "${name}" 8 | docker rm "${name}" 9 | docker image rm "${name}" 10 | docker network rm "${name}" 11 | -------------------------------------------------------------------------------- /bin/vm-scp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | dir=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P) 6 | 7 | data="${dir}/vm" 8 | 9 | # Usage: vm-scp vm-ubuntu:/path/to/file /path/to/destination 10 | exec scp \ 11 | -F "${data}/ssh/config" \ 12 | "$@" -------------------------------------------------------------------------------- /example-setup/pki-input/cfssl-leaf.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "CMC Test Leaf Certificate", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Test Organizational Unit" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /example-setup/metadata-templates/device.description.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "Device Description", 3 | "name" : "", 4 | "version" : "", 5 | "validity" : { 6 | "notBefore": "", 7 | "notAfter": "" 8 | }, 9 | "description" : "", 10 | "location" : "", 11 | "descriptions": [ 12 | ] 13 | } -------------------------------------------------------------------------------- /tools/hextobin/README.md: -------------------------------------------------------------------------------- 1 | # hextobin 2 | 3 | Simple tool that extracts hex-strings between a start delimiter and 4 | an end delimiter and converts them to binary. 5 | 6 | ## Usage Example 7 | 8 | ```sh 9 | ./hextobin -in log -out target.bin -start STARTDELIM -end ENDDELIM 10 | ``` 11 | -------------------------------------------------------------------------------- /bin/vm-swtpm-loop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "[\033[31;1mFAILED\033[0m] %s\n" "$0"' EXIT 6 | 7 | while true 8 | do 9 | printf "Starting swptm...\n" 10 | vm-swtpm "$@" 11 | printf "Sleeping for 900 ms...\n" 12 | sleep .9 13 | done 14 | 15 | -------------------------------------------------------------------------------- /example-setup/pki-input-ids/user_sub_ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Test User SubCA", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Device SubCA" 9 | } 10 | ], 11 | "key": { 12 | "algo": "ecdsa", 13 | "size": 384 14 | } 15 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/device_sub_ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Test Device SubCA", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Device SubCA" 9 | } 10 | ], 11 | "key": { 12 | "algo": "ecdsa", 13 | "size": 384 14 | } 15 | } -------------------------------------------------------------------------------- /example-setup/metadata-templates/snp.version.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "fw": { 4 | "build": 0, 5 | "major": 0, 6 | "minor": 0 7 | }, 8 | "tcb": { 9 | "bl": 0, 10 | "tee": 0, 11 | "snp": 0, 12 | "ucode": 0, 13 | "fmc": 0 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /bin/vm-estserver: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | 8 | cd "${dir}" 9 | 10 | exec "${dir}/provision/estserver/estserver" -config "${dir}/example-setup/vm-config/est-server-conf.json" -------------------------------------------------------------------------------- /env.zsh: -------------------------------------------------------------------------------- 1 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)" 2 | 3 | case "${PATH}" in ""|"${dir}/bin"*) ;; *) PATH="${dir}/bin:${PATH}";; esac 4 | 5 | function _cmc-docker () { 6 | CURRENT=$((CURRENT - 1)) 7 | _normal 8 | } 9 | 10 | compdef _cmc-docker cmc-docker 11 | 12 | compdef vm-ssh='ssh' 13 | compdef vm-scp='scp' 14 | -------------------------------------------------------------------------------- /example-setup/vm-config/cmcctl.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=CMC cmcctl aTLS listener 3 | Wants=cmcd.service 4 | After=cmcd.service 5 | 6 | [Service] 7 | Type=simple 8 | Restart=always 9 | RestartSec=10 10 | User=root 11 | ExecStart=cmcctl serve -config /etc/cmcctl-conf-vm.json 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /bin/vm-cmcctl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | 8 | cmd="${1:-request}" 9 | 10 | cd "${dir}" 11 | 12 | exec "${dir}/cmcctl/cmcctl" "${cmd}" -config "${dir}/example-setup/vm-config/cmcctl-conf-host.json" "$@" -------------------------------------------------------------------------------- /example-setup/cas/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This folder contains required CA certificates for making HTTPS requests: 4 | - SectigoRSAOrganizationValidationSecureServerCA.pem: CA for making requests to Intel TDX API services. 5 | Must be stored in Intel TDX CVMs in `/etc/ssl/certs` or mapped into Intel SGX enclaves (see [enclave-config](../enclave.json). 6 | -------------------------------------------------------------------------------- /example-setup/metadata-templates/tdx.policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "quoteVersion": 0, 3 | "tdId": { 4 | "mrOwner": "", 5 | "mrOwnerConfig": "", 6 | "mrConfigId": "" 7 | }, 8 | "xfam": "", 9 | "tdAttributes": { 10 | "debug": false, 11 | "septVEDisable": false, 12 | "pks": false, 13 | "kl": false 14 | } 15 | } -------------------------------------------------------------------------------- /example-setup/configs/kernel-setup-header.json: -------------------------------------------------------------------------------- 1 | { 2 | "typeOfLoader": 176, 3 | "loadFlags": 129, 4 | "setupMoveSize": 32768, 5 | "code32Start": 1048576, 6 | "ramdiskImage": 0, 7 | "ramdiskSize": 0, 8 | "bootsectKludge": 0, 9 | "heapEndPtr": 65024, 10 | "extLoaderVer": 0, 11 | "extLoaderType": 0, 12 | "cmdLinePtr": 131072, 13 | "ramdiskMax": 0 14 | } -------------------------------------------------------------------------------- /example-setup/pki-input-ids/cfssl-est-tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "CMC Test EST TLS Certificate", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Test Organizational Unit" 9 | } 10 | ], 11 | "hosts": [ 12 | "localhost", 13 | "127.0.0.1" 14 | ] 15 | } -------------------------------------------------------------------------------- /bin/generate-metadata-sgx: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | bin="${dir}/bin" 10 | 11 | "${bin}/generate-device-description" "sgx-device.test.de" 12 | 13 | "${bin}/precompute-rtm-manifest-sgx" 14 | -------------------------------------------------------------------------------- /example-setup/vm-config/calculate-srtm-pcrs.cfg: -------------------------------------------------------------------------------- 1 | PeCoffTypeOfLoader = 0xB0 2 | PeCoffLoadFlags = 0x81 3 | PeCoffSetupMoveSize = 0x8000 4 | PeCoffCode32Start = 0x100000 5 | PeCoffRamdiskImage = 0x0 6 | PeCoffRamdiskSize = 0x0 7 | PeCoffBootsectKludge = 0x0 8 | PeCoffHeapEndPtr = 0xfe00 9 | PeCoffExtLoaderVer = 0x0 10 | PeCoffExtLoaderType = 0x0 11 | PeCoffCmdLinePtr = 0x00020000 -------------------------------------------------------------------------------- /example-setup/pki-input/cfssl-est-tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "CMC Test EST TLS Certificate", 3 | "names": [ 4 | { 5 | "C": "DE", 6 | "L": "Test City", 7 | "O": "Test Company", 8 | "OU": "Test Organizational Unit" 9 | } 10 | ], 11 | "hosts": [ 12 | "localhost", 13 | "127.0.0.1", 14 | "10.0.2.2" 15 | ] 16 | } -------------------------------------------------------------------------------- /example-setup/vm-config/cmcd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=cmcd 3 | Wants=network-online.target systemd-hostnamed.service 4 | After=network-online.target systemd-hostnamed.service 5 | 6 | [Service] 7 | Type=notify 8 | NotifyAccess=all 9 | Restart=always 10 | RestartSec=10 11 | User=root 12 | ExecStart=cmcd -config /etc/cmcd-conf.json 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /example-setup/pki-input-ids/ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Test IDS Root CA", 3 | "key": { 4 | "algo": "ecdsa", 5 | "size": 521 6 | }, 7 | "names": [ 8 | { 9 | "C": "DE", 10 | "L": "Test City", 11 | "O": "Test Company", 12 | "OU": "Root CA" 13 | } 14 | ], 15 | "expiry": "87660h", 16 | "usages": ["cert sign"], 17 | "ca_constraint": {"is_ca": true} 18 | } -------------------------------------------------------------------------------- /doc/api/json/api/PSSOptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/PSSOptions", 4 | "$defs": { 5 | "PSSOptions": { 6 | "properties": { 7 | "SaltLength": { 8 | "type": "integer" 9 | } 10 | }, 11 | "type": "object", 12 | "required": [ 13 | "SaltLength" 14 | ] 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /doc/api/json/api/TLSCertRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/TLSCertRequest", 4 | "$defs": { 5 | "TLSCertRequest": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | } 10 | }, 11 | "type": "object", 12 | "required": [ 13 | "version" 14 | ] 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /bin/precompute-metadata-vm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | bin="${dir}/bin" 10 | 11 | "${bin}/generate-device-description" "test-vm.test.de" 12 | 13 | "${bin}/precompute-rtm-manifest-vm" 14 | 15 | "${bin}/precompute-os-manifest-vm" -------------------------------------------------------------------------------- /example-setup/metadata-templates/sgx.policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "quoteVersion": 0, 3 | "isvProdId": 0, 4 | "isvSvn": 0, 5 | "mrSigner": "", 6 | "attributes": { 7 | "initted": false, 8 | "debug": false, 9 | "mode64Bit": false, 10 | "provisionKey": false, 11 | "eInitToken": false, 12 | "kss": false, 13 | "legacy": false, 14 | "avx": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example-setup/metadata-templates/snp.policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "reportMinVersion": 0, 3 | "reportMaxVersion": 0, 4 | "policy": { 5 | "type": "SNP Policy", 6 | "SingleSocket": false, 7 | "Debug": false, 8 | "Migration": false, 9 | "Smt": false, 10 | "AbiMajor": 0, 11 | "AbiMinor": 0 12 | }, 13 | "versions": [ 14 | ] 15 | } -------------------------------------------------------------------------------- /bin/generate-metadata-tdx: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | bin="${dir}/bin" 10 | 11 | "${bin}/generate-device-description" "tdx-device.test.de" 12 | 13 | "${bin}/precompute-rtm-manifest-tdx" 14 | 15 | "${bin}/precompute-os-manifest-tdx" 16 | -------------------------------------------------------------------------------- /cmcctl/Makefile: -------------------------------------------------------------------------------- 1 | SGX_SDK ?= /opt/intel/sgxsdk 2 | SGX_LIBRARY_PATH := $(SGX_SDK)/lib4 3 | Enclave_Include_Paths := -I$(SGX_SDK)/include 4 | 5 | 6 | .PHONY: all minimal egocmc 7 | all: 8 | go build 9 | 10 | minimal: 11 | go build -ldflags="-s -w" -tags nodefaults,socket,tpm,snp,tdx,sw 12 | 13 | egocmc: 14 | ego-go build -tags nodefaults,libapi,tpm,snp,tdx,sgx,sw && ego sign ../example-setup/sgx/enclave.json 15 | 16 | 17 | -------------------------------------------------------------------------------- /example-setup/metadata-templates/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Manifest", 3 | "name": "", 4 | "version" : "", 5 | "validity" : { 6 | "notBefore": "", 7 | "notAfter": "" 8 | }, 9 | "description": "", 10 | "developerCommonName": "", 11 | "baseLayers": [ 12 | "" 13 | ], 14 | "certLevel" : 0, 15 | "caFingerprints": [], 16 | "referenceValues": [ 17 | ] 18 | } 19 | 20 | -------------------------------------------------------------------------------- /example-setup/pki-input/cfssl-ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "CA": { 3 | "expiry": "876000h", 4 | "pathlen": 0 5 | }, 6 | "CN": "Test Root CA", 7 | "key": { 8 | "algo": "ecdsa", 9 | "size": 256 10 | }, 11 | "names": [ 12 | { 13 | "C": "DE", 14 | "L": "Test City", 15 | "O": "Test Company", 16 | "OU": "Root CA" 17 | } 18 | ], 19 | "usages": ["cert sign"], 20 | "ca_constraint": {"is_ca": true} 21 | } 22 | -------------------------------------------------------------------------------- /example-setup/policies/policies-overwrite.js: -------------------------------------------------------------------------------- 1 | // Parse the verification result 2 | var obj = JSON.parse(json); 3 | 4 | var success = true; 5 | 6 | // Basic checks 7 | if (obj.type != "Verification Result") { 8 | console.log("[PolicyEngine] Invalid type"); 9 | success = false; 10 | } 11 | 12 | // Overwrite the result from failed to warning 13 | obj.summary.status = "warn" 14 | 15 | var ret = JSON.stringify(obj); 16 | 17 | ret -------------------------------------------------------------------------------- /example-setup/pki-input-ids/ocsp_subcas.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "OCSP_CAs", 3 | "key": { 4 | "algo": "ecdsa", 5 | "size": 256 6 | }, 7 | "names": [ 8 | { 9 | "C": "DE", 10 | "L": "Garching", 11 | "O": "Fraunhofer AISEC", 12 | "OU": "OCSP for SubCAs" 13 | } 14 | ], 15 | "usages": [ 16 | "digital signature", 17 | "ocsp signing" 18 | ], 19 | "expiry": "2160h" 20 | } 21 | -------------------------------------------------------------------------------- /example-setup/pki-input-ids/ocsp_users.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "OCSP_Users", 3 | "key": { 4 | "algo": "ecdsa", 5 | "size": 256 6 | }, 7 | "names": [ 8 | { 9 | "C": "DE", 10 | "L": "Garching", 11 | "O": "Fraunhofer AISEC", 12 | "OU": "OCSP for User CA" 13 | } 14 | ], 15 | "usages": [ 16 | "digital signature", 17 | "ocsp signing" 18 | ], 19 | "expiry": "336h" 20 | } 21 | -------------------------------------------------------------------------------- /example-setup/pki-input-ids/ocsp_devices.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "OCSP_Devices", 3 | "key": { 4 | "algo": "ecdsa", 5 | "size": 256 6 | }, 7 | "names": [ 8 | { 9 | "C": "DE", 10 | "L": "Garching", 11 | "O": "Fraunhofer AISEC", 12 | "OU": "OCSP for Device CA" 13 | } 14 | ], 15 | "usages": [ 16 | "digital signature", 17 | "ocsp signing" 18 | ], 19 | "expiry": "336h" 20 | } 21 | -------------------------------------------------------------------------------- /bin/generate-metadata-vm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | bin="${dir}/bin" 10 | 11 | "${bin}/generate-device-description" "test-vm.test.de" 12 | 13 | "${bin}/generate-rtm-manifest-tpm" "vm-ssh" 14 | 15 | "${bin}/generate-os-manifest-tpm" "vm-ssh" 16 | 17 | "${bin}/generate-app-manifest-tpm" "vm-ssh" -------------------------------------------------------------------------------- /doc/api/json/api/SocketError.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/SocketError", 4 | "$defs": { 5 | "SocketError": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "msg": { 11 | "type": "string" 12 | } 13 | }, 14 | "type": "object", 15 | "required": [ 16 | "version", 17 | "msg" 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /doc/api/json/api/PeerCacheRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/PeerCacheRequest", 4 | "$defs": { 5 | "PeerCacheRequest": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "peer": { 11 | "type": "string" 12 | } 13 | }, 14 | "type": "object", 15 | "required": [ 16 | "version", 17 | "peer" 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /doc/api/json/api/MeasureResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/MeasureResponse", 4 | "$defs": { 5 | "MeasureResponse": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "success": { 11 | "type": "boolean" 12 | } 13 | }, 14 | "type": "object", 15 | "required": [ 16 | "version", 17 | "success" 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /bin/vm-update-cmcd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "[\033[31;1mFAILED\033[0m] %s\n" "$0"' EXIT 6 | dir=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P) 7 | 8 | data="${dir}/vm" 9 | 10 | vm-ssh systemctl stop cmcd 11 | vm-ssh systemctl stop cmcctl 12 | 13 | go build -C "${dir}/cmcd" 14 | go build -C "${dir}/cmcctl" 15 | 16 | vm-scp cmcd/cmcd vm-ubuntu:/usr/bin/ 17 | vm-scp cmcctl/cmcctl vm-ubuntu:/usr/bin/ 18 | 19 | vm-ssh systemctl start cmcd 20 | vm-ssh systemctl start cmcctl 21 | -------------------------------------------------------------------------------- /bin/generate-metadata-tpm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | bin="${dir}/bin" 10 | 11 | "${bin}/generate-device-description" "tpm-testdevice.test.de" 12 | 13 | "${bin}/generate-rtm-manifest-tpm" 14 | 15 | "${bin}/generate-os-manifest-tpm" 16 | 17 | "${bin}/generate-app-manifest-tpm" 18 | 19 | "${bin}/generate-container-manifest" "docker" "ubuntu:24.04" 20 | -------------------------------------------------------------------------------- /doc/api/json/api/TLSSignResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/TLSSignResponse", 4 | "$defs": { 5 | "TLSSignResponse": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "signedContent": { 11 | "type": "string", 12 | "contentEncoding": "base64" 13 | } 14 | }, 15 | "type": "object", 16 | "required": [ 17 | "version", 18 | "signedContent" 19 | ] 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /doc/api/json/api/PeerCacheResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/PeerCacheResponse", 4 | "$defs": { 5 | "PeerCacheResponse": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "cache": { 11 | "items": { 12 | "type": "string" 13 | }, 14 | "type": "array" 15 | } 16 | }, 17 | "type": "object", 18 | "required": [ 19 | "version", 20 | "cache" 21 | ] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /example-setup/vm-config/cmcctl-conf-host.json: -------------------------------------------------------------------------------- 1 | { 2 | "addr": [ 3 | "https://localhost:4443" 4 | ], 5 | "report": "data/attestation-report", 6 | "result": "data/attestation-result.json", 7 | "nonce": "data/nonce", 8 | "identityCas": [ 9 | "data/pki/ca.pem" 10 | ], 11 | "estTlsCa": "data/pki/ca.pem", 12 | "mtls": false, 13 | "attest": "server", 14 | "measurementLog": true, 15 | "logLevel": "trace", 16 | "api": "socket", 17 | "apiSerializer": "json", 18 | "cmcAddr": "localhost:9955", 19 | "keyConfig": "EC256" 20 | } 21 | -------------------------------------------------------------------------------- /example-setup/vm-config/cmcctl-conf-vm.json: -------------------------------------------------------------------------------- 1 | { 2 | "addr": [ 3 | "0.0.0.0:4443" 4 | ], 5 | "mtls": false, 6 | "report": "/var/cmcctl/attestation-report", 7 | "result": "/var/cmcctl/attestation-result.json", 8 | "nonce": "/var/cmcctl/nonce", 9 | "identityCas": [ 10 | "/var/ca.pem" 11 | ], 12 | "estTlsCa": "/var/ca.pem", 13 | "attest": "server", 14 | "measurementLog": true, 15 | "logLevel": "trace", 16 | "api": "socket", 17 | "apiSerializer": "json", 18 | "cmcAddr": "localhost:9955", 19 | "keyConfig": "EC256" 20 | } 21 | -------------------------------------------------------------------------------- /doc/api/json/attestedtls/AtlsHandshakeComplete.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/AtlsHandshakeComplete", 4 | "$defs": { 5 | "AtlsHandshakeComplete": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "success": { 11 | "type": "boolean" 12 | }, 13 | "error": { 14 | "type": "string" 15 | } 16 | }, 17 | "type": "object", 18 | "required": [ 19 | "version", 20 | "success" 21 | ] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /doc/api/json/api/TLSCertResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/TLSCertResponse", 4 | "$defs": { 5 | "TLSCertResponse": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "certificate": { 11 | "items": { 12 | "type": "string", 13 | "contentEncoding": "base64" 14 | }, 15 | "type": "array" 16 | } 17 | }, 18 | "type": "object", 19 | "required": [ 20 | "version", 21 | "certificate" 22 | ] 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /bin/vm-swtpm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "[\033[31;1mFAILED\033[0m] %s\n" "$0"' EXIT 6 | dir=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P) 7 | 8 | data="${dir}/vm" 9 | 10 | tpm_bin_dir="${data}/swtpm/src/bin" 11 | tpm_state="${data}/swtpm/swtpm-state" 12 | tpm_sock="${data}/swtpm/swtpm-sock" 13 | 14 | # Start swtpm 15 | printf "[\033[34;1m EXEC \033[0m] Starting swtpm\n" 16 | case ":${PATH}:" in *":${tpm_bin_dir}:"*) ;; *) PATH="${tpm_bin_dir}:${PATH}";; esac 17 | 18 | exec swtpm socket \ 19 | --tpmstate dir="${tpm_state}" \ 20 | --ctrl type=unixio,path="${tpm_sock}" \ 21 | --log level=20 \ 22 | --tpm2 -------------------------------------------------------------------------------- /doc/api/json/api/AttestationRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/AttestationRequest", 4 | "$defs": { 5 | "AttestationRequest": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "nonce": { 11 | "type": "string", 12 | "contentEncoding": "base64" 13 | }, 14 | "cached": { 15 | "items": { 16 | "type": "string" 17 | }, 18 | "type": "array" 19 | } 20 | }, 21 | "type": "object", 22 | "required": [ 23 | "version", 24 | "nonce" 25 | ] 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /example-setup/vm-config/est-server-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "tokenAddr": "127.0.0.1:9001", 3 | "estAddr": "0.0.0.0:9000", 4 | "estCaKey": "data/pki/ca-key.pem", 5 | "estCaChain": [ 6 | "data/pki/ca.pem" 7 | ], 8 | "tlsKey": "data/pki/est-tls-key.pem", 9 | "tlsCaChain": [ 10 | "data/pki/est-tls.pem", 11 | "data/pki/ca.pem" 12 | ], 13 | "metadataCas": [ 14 | "data/pki/ca.pem", 15 | "example-setup/vm-config/metadata-ca.pem" 16 | ], 17 | "httpFolder": "example-setup/vm-config/vm-metadata", 18 | "verifyEkCert": false, 19 | "tpmEkCertDb": "data/tpm-ek-certs.db", 20 | "logLevel": "trace", 21 | "authMethods": [ "none" ] 22 | } 23 | -------------------------------------------------------------------------------- /example-setup/configs/cmcctl-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "addr": [ 3 | "localhost:4443" 4 | ], 5 | "cmcAddr": "localhost:9955", 6 | "report": "attestation-report", 7 | "result": "attestation-result.json", 8 | "nonce": "nonce", 9 | "identityCas": [ 10 | "../data/pki/ca.pem" 11 | ], 12 | "estTlsCa": "../data/pki/ca.pem", 13 | "mtls": true, 14 | "attest": "mutual", 15 | "measurementLog": true, 16 | "publish": "http://localhost:8080/results", 17 | "logLevel": "trace", 18 | "api": "socket", 19 | "apiSerializer": "cbor", 20 | "keyConfig": "EC256", 21 | "tokenStore": "../data/token_store", 22 | "provisionToken": "../data/provision_token" 23 | } 24 | -------------------------------------------------------------------------------- /example-setup/configs/est-server-conf-ids.json: -------------------------------------------------------------------------------- 1 | { 2 | "tokenAddr": "127.0.0.1:9001", 3 | "estAddr": "0.0.0.0:9000", 4 | "estCaKey": "../../data-ids/pki/device_sub_ca-key.pem", 5 | "estCaChain": [ 6 | "../../data-ids/pki/device_sub_ca.pem", 7 | "../../data-ids/pki/ca.pem" 8 | ], 9 | "tlsKey": "../../data-ids/pki/est-tls-key.pem", 10 | "tlsCaChain": [ 11 | "../../data-ids/pki/est-tls.pem", 12 | "../../data-ids/pki/ca.pem" 13 | ], 14 | "metadataCas": [ 15 | "../../data/pki/ca.pem" 16 | ], 17 | "httpFolder": "../../data-ids/", 18 | "verifyEkCert": false, 19 | "tpmEkCertDb": "../../data-ids/tpm-ek-certs.db", 20 | "logLevel": "trace" 21 | } 22 | -------------------------------------------------------------------------------- /tools/schema-generator/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | The schema generator generates JSON schema definitions for a golang cmc package for documentation. 4 | 5 | ## Build 6 | 7 | ```sh 8 | go build 9 | ``` 10 | 11 | ## Run 12 | 13 | The `package` flag specifies the package the definition shall be generated for. the `out` flag 14 | specifies the output folder. If the folder does not exist, it will be created. If the folder 15 | exists, content will be overwritten. 16 | 17 | ``` 18 | ./schema-generator [-package ] [-out ] 19 | ``` 20 | 21 | ## Update CMC documentation 22 | 23 | ``` 24 | ./schema-generator -package api -out ../../doc/api/json && \ 25 | ./schema-generator -package attestationreport -out ../../doc/api/json && \ 26 | ./schema-generator -package attestedtls -out ../../doc/api/json 27 | ``` -------------------------------------------------------------------------------- /example-setup/configs/est-server-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "estAddr": "0.0.0.0:9000", 3 | "estCaKey": "../../data/pki/ca-key.pem", 4 | "estCaChain": [ 5 | "../../data/pki/ca.pem" 6 | ], 7 | "tlsKey": "../../data/pki/est-tls-key.pem", 8 | "tlsCaChain": [ 9 | "../../data/pki/est-tls.pem", 10 | "../../data/pki/ca.pem" 11 | ], 12 | "metadataCas": [ 13 | "../../data/pki/ca.pem" 14 | ], 15 | "httpFolder": "../../data/metadata-signed", 16 | "verifyEkCert": false, 17 | "tpmEkCertDb": "../../data/tpm-ek-certs.db", 18 | "logLevel": "trace", 19 | "authMethods": [ 20 | "none" 21 | ], 22 | "tokenPath": "../../data/token_store", 23 | "publishAddr": "http://localhost:8080/results", 24 | "publishFile": "attestation-result.json" 25 | } 26 | -------------------------------------------------------------------------------- /tools/fvextract/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This tool extracts an UEFI firmware volume (FV) from dumped guest memory. 4 | 5 | During guest initialization, QEMU extracts the firmware volumes and places them in guest memory. The 6 | volumes are then measured into the PCRs or TDX MRs. This tool can be used to spot differences between 7 | calculated measurements and the actual measurements. 8 | 9 | ## Build 10 | 11 | ```sh 12 | go build 13 | ``` 14 | 15 | ## Usage 16 | 17 | Dump QEMU guest memory of a running VM through switching to QEMU monitor mode (CTRL+A,C): 18 | ```sh 19 | dump-guest-memory /path/to/output/file 20 | ``` 21 | 22 | Run `extractfv` tool to extract the FV from the dumped memory: 23 | ``` 24 | extractfv -ref -out -in -fv [-refsize ] 25 | ``` -------------------------------------------------------------------------------- /example-setup/sgx/enclave.json: -------------------------------------------------------------------------------- 1 | { 2 | "exe": "../../cmcctl/cmcctl", 3 | "key": "../../data/pki/private-ego.pem", 4 | "debug": false, 5 | "heapSize": 512, 6 | "executableHeap": false, 7 | "productID": 1, 8 | "securityVersion": 1, 9 | "mounts": [ 10 | { 11 | "source": "../data", 12 | "target": "data", 13 | "type": "hostfs", 14 | "readOnly": false 15 | }, 16 | { 17 | "source": "../example-setup/configs", 18 | "target": "configs", 19 | "type": "hostfs", 20 | "readOnly": true 21 | } 22 | ], 23 | "env": null, 24 | "files": [ 25 | { 26 | "source": "../example-setup/cas/SectigoRSAOrganizationValidationSecureServerCA.pem", 27 | "target": "/etc/ssl/certs/SectigoRSAOrganizationValidationSecureServerCA.pem" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /example-setup/vm-config/metadata-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICBzCCAa6gAwIBAgIUQXvEe89/4sJ8VXXBsE0O6/O+xq0wCgYIKoZIzj0EAwIw 3 | YTELMAkGA1UEBhMCREUxEjAQBgNVBAcTCVRlc3QgQ2l0eTEVMBMGA1UEChMMVGVz 4 | dCBDb21wYW55MRAwDgYDVQQLEwdSb290IENBMRUwEwYDVQQDEwxUZXN0IFJvb3Qg 5 | Q0EwIBcNMjUwNDI5MTAyOTAwWhgPMjEyNTA0MDUxMDI5MDBaMGExCzAJBgNVBAYT 6 | AkRFMRIwEAYDVQQHEwlUZXN0IENpdHkxFTATBgNVBAoTDFRlc3QgQ29tcGFueTEQ 7 | MA4GA1UECxMHUm9vdCBDQTEVMBMGA1UEAxMMVGVzdCBSb290IENBMFkwEwYHKoZI 8 | zj0CAQYIKoZIzj0DAQcDQgAEqYPfgEcJGT0oSNmNeFJHDBuZYF24HkzaWTAKancD 9 | /Vp6Yh7V93Ogog0/QkdAyDIX4RaNmG8DnjSHjlUqcsYqDqNCMEAwDgYDVR0PAQH/ 10 | BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFD1HKzkiOyk9faT2cyG0 11 | aBHTUBY6MAoGCCqGSM49BAMCA0cAMEQCIBYcmavuE39iAFCfUfOInKk9V4+oki8d 12 | 8mSiFSu7l2UUAiAfFbkyR154uxEsgtUcQX0N6BEpuexhC1SqBS3tPWInng== 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /attestationpolicies/duktape/policies.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | 17 | char* Validate(uint8_t *ar, size_t ar_size, uint8_t *policies, size_t policies_size); -------------------------------------------------------------------------------- /example-setup/pki-input-ids/certs_users.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE certificates ( 2 | serial_number blob NOT NULL, 3 | authority_key_identifier blob NOT NULL, 4 | ca_label blob, 5 | status blob NOT NULL, 6 | reason int, 7 | expiry timestamp, 8 | revoked_at timestamp, 9 | pem blob NOT NULL, 10 | PRIMARY KEY(serial_number, authority_key_identifier) 11 | ); 12 | 13 | CREATE TABLE ocsp_responses ( 14 | serial_number blob NOT NULL, 15 | authority_key_identifier blob NOT NULL, 16 | body blob NOT NULL, 17 | expiry timestamp, 18 | PRIMARY KEY(serial_number, authority_key_identifier), 19 | FOREIGN KEY(serial_number, authority_key_identifier) REFERENCES certificates(serial_number, authority_key_identifier) 20 | ); 21 | -------------------------------------------------------------------------------- /example-setup/pki-input-ids/certs_devices.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE certificates ( 2 | serial_number blob NOT NULL, 3 | authority_key_identifier blob NOT NULL, 4 | ca_label blob, 5 | status blob NOT NULL, 6 | reason int, 7 | expiry timestamp, 8 | revoked_at timestamp, 9 | pem blob NOT NULL, 10 | PRIMARY KEY(serial_number, authority_key_identifier) 11 | ); 12 | 13 | CREATE TABLE ocsp_responses ( 14 | serial_number blob NOT NULL, 15 | authority_key_identifier blob NOT NULL, 16 | body blob NOT NULL, 17 | expiry timestamp, 18 | PRIMARY KEY(serial_number, authority_key_identifier), 19 | FOREIGN KEY(serial_number, authority_key_identifier) REFERENCES certificates(serial_number, authority_key_identifier) 20 | ); 21 | -------------------------------------------------------------------------------- /example-setup/pki-input-ids/certs_subcas.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE certificates ( 2 | serial_number blob NOT NULL, 3 | authority_key_identifier blob NOT NULL, 4 | ca_label blob, 5 | status blob NOT NULL, 6 | reason int, 7 | expiry timestamp, 8 | revoked_at timestamp, 9 | pem blob NOT NULL, 10 | PRIMARY KEY(serial_number, authority_key_identifier) 11 | ); 12 | 13 | CREATE TABLE ocsp_responses ( 14 | serial_number blob NOT NULL, 15 | authority_key_identifier blob NOT NULL, 16 | body blob NOT NULL, 17 | expiry timestamp, 18 | PRIMARY KEY(serial_number, authority_key_identifier), 19 | FOREIGN KEY(serial_number, authority_key_identifier) REFERENCES certificates(serial_number, authority_key_identifier) 20 | ); 21 | -------------------------------------------------------------------------------- /bin/vm-extract-acpi-tables: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | data="${dir}/vm" 10 | hostname=$(hostname) 11 | 12 | acpi_dump_vm="/root/acpi_dump" 13 | acpi_host_dir="${data}/images/acpi/${hostname}" 14 | acpi_dump_host="${acpi_host_dir}/acpi_dump" 15 | 16 | echo "Copying acpi_dump to ${acpi_dump_host}" 17 | 18 | mkdir -p "${acpi_host_dir}/decompiled" 19 | cd "${acpi_host_dir}" 20 | 21 | cmc-docker vm-ssh rm -rf "${acpi_dump_vm}" 22 | 23 | cmc-docker vm-ssh acpidump -o "${acpi_dump_vm}" 24 | 25 | cmc-docker vm-scp vm-ubuntu:"${acpi_dump_vm}" "${acpi_dump_host}" 26 | 27 | acpixtract -a -f "${acpi_dump_host}" 28 | 29 | for table in *.dat; do 30 | iasl -d "${table}" 31 | done 32 | 33 | grep -A5 QWordMemory "${acpi_host_dir}/dsdt.dsl" -------------------------------------------------------------------------------- /cmc/sw.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:build !nodefaults || sw 17 | 18 | package cmc 19 | 20 | import "github.com/Fraunhofer-AISEC/cmc/swdriver" 21 | 22 | func init() { 23 | drivers["sw"] = &swdriver.Sw{} 24 | } 25 | -------------------------------------------------------------------------------- /doc/tools.md: -------------------------------------------------------------------------------- 1 | # Tools 2 | 3 | Helper programs that support generating and signing reference values and interacting with trusted 4 | and confidential computing hardware. 5 | 6 | ## backend 7 | 8 | A simple HTTP web server with attached database for storing attestation results. This app is for 9 | demonstration purposes only and not to be used in production. 10 | For more information, see the [README](../tools/backend/README.md) 11 | 12 | ## mrtool 13 | 14 | Parse and precompute reference hashes (TPM PCRs, Intel TDX RTMR/MRTD, AMD SEV-SNP measurements). 15 | For more information, see the [README](../tools/mrtool/README.md) 16 | 17 | ## azuretool 18 | 19 | Azure-specific helper for retrieving SNP/TDX attestation reports on Azure machines. 20 | 21 | ## tdxtool 22 | 23 | Tool to generate and parse TDX quotes and metadata. 24 | 25 | ## snptool 26 | 27 | Tool to generate and parse SNP attestation reports and metadata. -------------------------------------------------------------------------------- /doc/api/json/api/AttestationResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/AttestationResponse", 4 | "$defs": { 5 | "AttestationResponse": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "report": { 11 | "type": "string", 12 | "contentEncoding": "base64" 13 | }, 14 | "metadata": { 15 | "additionalProperties": { 16 | "type": "string", 17 | "contentEncoding": "base64" 18 | }, 19 | "type": "object" 20 | }, 21 | "cacheMisses": { 22 | "items": { 23 | "type": "string" 24 | }, 25 | "type": "array" 26 | } 27 | }, 28 | "type": "object", 29 | "required": [ 30 | "version", 31 | "report" 32 | ] 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /grpcapi/version.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 - 2024 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package grpcapi 17 | 18 | // The version of the API 19 | const ( 20 | apiVersion = "1.2.0" 21 | ) 22 | 23 | func GetVersion() string { 24 | return apiVersion 25 | } 26 | -------------------------------------------------------------------------------- /cmc/tpm.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:build !nodefaults || tpm 17 | 18 | package cmc 19 | 20 | import "github.com/Fraunhofer-AISEC/cmc/tpmdriver" 21 | 22 | func init() { 23 | drivers["tpm"] = &tpmdriver.Tpm{} 24 | } 25 | -------------------------------------------------------------------------------- /cmc/sgx.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:build amd64 && (!nodefaults || sgx) 17 | 18 | package cmc 19 | 20 | import "github.com/Fraunhofer-AISEC/cmc/sgxdriver" 21 | 22 | func init() { 23 | drivers["sgx"] = &sgxdriver.Sgx{} 24 | } 25 | -------------------------------------------------------------------------------- /cmc/snp.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:build amd64 && (!nodefaults || snp) 17 | 18 | package cmc 19 | 20 | import "github.com/Fraunhofer-AISEC/cmc/snpdriver" 21 | 22 | func init() { 23 | drivers["snp"] = &snpdriver.Snp{} 24 | } 25 | -------------------------------------------------------------------------------- /cmc/tdx.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:build amd64 && (!nodefaults || tdx) 17 | 18 | package cmc 19 | 20 | import "github.com/Fraunhofer-AISEC/cmc/tdxdriver" 21 | 22 | func init() { 23 | drivers["tdx"] = &tdxdriver.Tdx{} 24 | } 25 | -------------------------------------------------------------------------------- /cmc/azure.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:build amd64 && (!nodefaults || azure) 17 | 18 | package cmc 19 | 20 | import "github.com/Fraunhofer-AISEC/cmc/azuredriver" 21 | 22 | func init() { 23 | drivers["azure"] = &azuredriver.Azure{} 24 | } 25 | -------------------------------------------------------------------------------- /example-setup/vm-config/cmcd-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmcAddr": "0.0.0.0:9955", 3 | "provisionAddr": "https://10.0.2.2:9000/", 4 | "provisionAuth": [ "none" ], 5 | "provisionMode": "est", 6 | "metadata": [ 7 | "https://10.0.2.2:9000/vm-metadata" 8 | ], 9 | "drivers": [ "tpm", "sw" ], 10 | "ima": true, 11 | "imaPcr": 10, 12 | "keyConfig": "EC256", 13 | "api": "socket", 14 | "logLevel": "trace", 15 | "storage": "/var/cmc/internal/", 16 | "cache": "/var/cmc/cache/", 17 | "peerCache": "/var/cmc/peer-cache", 18 | "measurementLog": true, 19 | "ctr": true, 20 | "ctrDriver": "sw", 21 | "ctrLog": "/tmp/container_measurements", 22 | "ctrPcr": 11, 23 | "estTlsCas": [ 24 | "/var/ca.pem" 25 | ], 26 | "identityCas": [ 27 | "/var/ca.pem" 28 | ], 29 | "metadataCas": [ 30 | "/var/metadata-ca.pem", 31 | "/var/ca.pem" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/7zVersion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MY_VER_MAJOR 19 4 | #define MY_VER_MINOR 00 5 | #define MY_VER_BUILD 0 6 | #define MY_VERSION_NUMBERS "19.00" 7 | #define MY_VERSION MY_VERSION_NUMBERS 8 | 9 | #ifdef MY_CPU_NAME 10 | #define MY_VERSION_CPU MY_VERSION " (" MY_CPU_NAME ")" 11 | #else 12 | #define MY_VERSION_CPU MY_VERSION 13 | #endif 14 | 15 | #define MY_DATE "2019-02-21" 16 | #undef MY_COPYRIGHT 17 | #undef MY_VERSION_COPYRIGHT_DATE 18 | #define MY_AUTHOR_NAME "Igor Pavlov" 19 | #define MY_COPYRIGHT_PD "Igor Pavlov : Public domain" 20 | #define MY_COPYRIGHT_CR "Copyright (c) 1999-2018 Igor Pavlov" 21 | 22 | #ifdef USE_COPYRIGHT_CR 23 | #define MY_COPYRIGHT MY_COPYRIGHT_CR 24 | #else 25 | #define MY_COPYRIGHT MY_COPYRIGHT_PD 26 | #endif 27 | 28 | #define MY_COPYRIGHT_DATE MY_COPYRIGHT " : " MY_DATE 29 | #define MY_VERSION_COPYRIGHT_DATE MY_VERSION_CPU " : " MY_COPYRIGHT " : " MY_DATE 30 | -------------------------------------------------------------------------------- /doc/api/json/attestedtls/AtlsHandshakeRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/AtlsHandshakeRequest", 4 | "$defs": { 5 | "AtlsHandshakeRequest": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "attest": { 11 | "type": "integer", 12 | "enum": [ 13 | 0, 14 | 1, 15 | 2, 16 | 3 17 | ], 18 | "description": "Type of attestation: 0 (Mutual) 1 (Client) 2 (Server) 3 (None)" 19 | }, 20 | "cached": { 21 | "items": { 22 | "type": "string" 23 | }, 24 | "type": "array" 25 | }, 26 | "extendedReport": { 27 | "type": "boolean" 28 | } 29 | }, 30 | "type": "object", 31 | "required": [ 32 | "version", 33 | "attest" 34 | ] 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /example-setup/pki-input/cfssl-ca-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "signing": { 3 | "profiles": { 4 | "leafcert": { 5 | "expiry": "876000h", 6 | "key": { 7 | "algo": "ecdsa", 8 | "size": 256 9 | }, 10 | "usages": [ 11 | "signing", 12 | "digital signature", 13 | "content commitment", 14 | "code signing" 15 | ], 16 | "ocsp_url": "127.0.0.1:8888" 17 | }, 18 | "tlscert": { 19 | "expiry": "876000h", 20 | "key": { 21 | "algo": "ecdsa", 22 | "size": 256 23 | }, 24 | "usages": [ 25 | "digital signature", 26 | "server auth" 27 | ] 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bin/generate-metadata-snp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | bin="${dir}/bin" 10 | 11 | ovmf="${dir}/vm/images/OVMF.fd" 12 | vcpus=2 13 | vmm_type=qemu 14 | 15 | # Parse arguments 16 | while [[ $# -gt 0 ]]; do 17 | case "$1" in 18 | --vcpus) 19 | vcpus="$2" 20 | shift 2 21 | ;; 22 | --vmm-type) 23 | vmm_type="$2" 24 | shift 2 25 | ;; 26 | --ovmf) 27 | ovmf="$2" 28 | shift 2 29 | ;; 30 | *) 31 | echo "Unknown option: $1" 32 | echo "Usage: $0 [--vcpus #] [--vmm-type TYPE] [--ovmf PATH]" 33 | exit 1 34 | ;; 35 | esac 36 | done 37 | 38 | "${bin}/generate-device-description" "snp-device.test.de" 39 | 40 | "${bin}/precompute-rtm-manifest-snp" --vcpus "${vcpus}" --vmm-type "${vmm_type}" --ovmf "${ovmf}" 41 | -------------------------------------------------------------------------------- /example-setup/configs/cmcd-conf-selfsigned.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmcAddr": "localhost:9955", 3 | "provisionMode": "selfsigned", 4 | "metadata": [ 5 | "file://../data/metadata-signed" 6 | ], 7 | "drivers": [ "tpm", "sw" ], 8 | "ima": true, 9 | "imaPcr": 10, 10 | "keyConfig": "EC256", 11 | "api": "socket", 12 | "policyEngine": "js", 13 | "policyOverwrite": false, 14 | "logLevel": "trace", 15 | "storage": "../data/cmc-internal/", 16 | "cache": "../data/cmc-cache/", 17 | "peerCache": "../data/cmc-peer-cache", 18 | "measurementLog": true, 19 | "ctr": true, 20 | "ctrDriver": "sw", 21 | "ctrLog": "/tmp/container_measurements", 22 | "ctrPcr": 11, 23 | "estTlsCas": [ 24 | "../data/pki/ca.pem" 25 | ], 26 | "identityCas": [ 27 | "../data/pki/ca.pem" 28 | ], 29 | "metadataCas": [ 30 | "../data/pki/ca.pem" 31 | ], 32 | "caKey": "../data/pki/ca-key.pem" 33 | } 34 | -------------------------------------------------------------------------------- /doc/api/json/attestedtls/AtlsHandshakeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/AtlsHandshakeResponse", 4 | "$defs": { 5 | "AtlsHandshakeResponse": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "error": { 11 | "type": "string" 12 | }, 13 | "report": { 14 | "type": "string", 15 | "contentEncoding": "base64" 16 | }, 17 | "metadata": { 18 | "additionalProperties": { 19 | "type": "string", 20 | "contentEncoding": "base64" 21 | }, 22 | "type": "object" 23 | }, 24 | "cacheMisses": { 25 | "items": { 26 | "type": "string" 27 | }, 28 | "type": "array" 29 | } 30 | }, 31 | "type": "object", 32 | "required": [ 33 | "version" 34 | ] 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /doc/api/json/api/TLSSignRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/TLSSignRequest", 4 | "$defs": { 5 | "PSSOptions": { 6 | "properties": { 7 | "SaltLength": { 8 | "type": "integer" 9 | } 10 | }, 11 | "type": "object", 12 | "required": [ 13 | "SaltLength" 14 | ] 15 | }, 16 | "TLSSignRequest": { 17 | "properties": { 18 | "version": { 19 | "type": "string" 20 | }, 21 | "content": { 22 | "type": "string", 23 | "contentEncoding": "base64" 24 | }, 25 | "hashType": { 26 | "type": "integer" 27 | }, 28 | "pssOpts": { 29 | "$ref": "#/$defs/PSSOptions" 30 | } 31 | }, 32 | "type": "object", 33 | "required": [ 34 | "version", 35 | "content", 36 | "hashType", 37 | "pssOpts" 38 | ] 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /verifier/duktapepolicies.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:build duktapepolicies 17 | 18 | package verifier 19 | 20 | import ( 21 | "github.com/Fraunhofer-AISEC/cmc/attestationpolicies/duktape" 22 | ) 23 | 24 | func init() { 25 | policyEngines[PolicyEngineSelect_DukTape] = &duktape.DukTapePolicyEngine{} 26 | } 27 | -------------------------------------------------------------------------------- /verifier/jspolicies.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:build !nodefaults || jspolicies 17 | 18 | package verifier 19 | 20 | import ( 21 | "github.com/Fraunhofer-AISEC/cmc/attestationpolicies/jspolicies" 22 | ) 23 | 24 | func init() { 25 | policyEngines[PolicyEngineSelect_JS] = &jspolicies.JsPolicyEngine{} 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bkp 3 | *.swp 4 | .vscode/ 5 | 6 | /data/ 7 | /artifacts/ 8 | /vm/ 9 | cmcd/cmcd 10 | cmcctl/cmcctl 11 | cmcctl/token 12 | cmcctl/ca.pem 13 | provision/estserver/estserver 14 | **/nonce 15 | **/attestation-report 16 | **/attestation-result.json 17 | tools/backend/backend 18 | tools/backend/monitoring.db 19 | tools/metasign/metasign 20 | tools/metaconv/metaconv 21 | tools/fmspc-retrieval-tool/fmspc-retrieval-tool 22 | tools/measurefs/measurefs 23 | tools/containerd-shim-cmc-v1/containerd-shim-cmc-v1 24 | tools/measure-bundle/measure-bundle 25 | tools/schema-generator/schema-generator 26 | tools/parsetdxmrs/parsetdxmrs 27 | tools/tdxtool/tdxtool 28 | tools/snptool/snptool 29 | tools/azuretool/azuretool 30 | tools/hextobin/hextobin 31 | tools/mrtool/mrtool 32 | tools/pkghasher/pkghasher 33 | tpm/test_encrypted_ak.json 34 | example-setup/data-cmc/ 35 | example-setup/**/*.cbor 36 | example-setup/metadata-signed/* 37 | tpmdriver/test_encrypted_ak.json 38 | est/server/server 39 | attestationreport/cache/* 40 | /tmp/* 41 | -------------------------------------------------------------------------------- /bin/vm-update-metadata: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | 8 | cd "${dir}" 9 | 10 | [[ $# -lt 2 ]] || { 11 | echo "Usage: $(basename $0) " 12 | exit 1 13 | } 14 | 15 | mode="${1:-parse}" 16 | 17 | if [[ "${mode}" == "parse" ]]; then 18 | echo "Parsing metadata..." 19 | generate-metadata-vm 20 | elif [[ "${mode}" == "precompute" ]]; then 21 | echo "Precomputing metadata..." 22 | precompute-metadata-vm 23 | else 24 | echo "Unknown mode ${mode}" 25 | exit 1 26 | fi 27 | 28 | sign-metadata json 29 | 30 | cp "${dir}/data/metadata-signed/"* "${dir}/example-setup/vm-config/vm-metadata/" 31 | cp "${dir}/data/pki/ca.pem" "${dir}/example-setup/vm-config/metadata-ca.pem" 32 | 33 | # Delete cmc internal data to enforce new certificate generation 34 | vm-ssh rm -r /var/cmc 35 | 36 | # Restarting cmcd is required for fetching the updated metadata 37 | vm-ssh systemctl restart cmcd 38 | -------------------------------------------------------------------------------- /cmcd/api.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Contains the API definitions for the CoAP and unix domain socket API 17 | // The gRPC API is in a separate file 18 | package main 19 | 20 | import ( 21 | "github.com/Fraunhofer-AISEC/cmc/cmc" 22 | ) 23 | 24 | var servers = map[string]Server{} 25 | 26 | type Server interface { 27 | Serve(addr string, cmc *cmc.Cmc) error 28 | } 29 | -------------------------------------------------------------------------------- /tools/backend/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | log "github.com/sirupsen/logrus" 20 | ) 21 | 22 | func main() { 23 | 24 | log.SetLevel(log.TraceLevel) 25 | 26 | log.Infof("Starting cmc-backend %v", getVersion()) 27 | 28 | c, err := getConfig() 29 | if err != nil { 30 | log.Fatalf("Failed to load config: %v", err) 31 | } 32 | 33 | s := newServer(c) 34 | s.serve() 35 | } 36 | -------------------------------------------------------------------------------- /example-setup/configs/cmcctl-conf-sgx.json: -------------------------------------------------------------------------------- 1 | { 2 | "addr": [ 3 | "localhost:4443" 4 | ], 5 | "provAddr": "https://localhost:9000/", 6 | "provisionMode": "est", 7 | "provisionAuth": [ "none" ], 8 | "report": "data/attestation-report", 9 | "result": "data/attestation-result.json", 10 | "nonce": "data/nonce", 11 | "identityCas": [ 12 | "data/pki/ca.pem" 13 | ], 14 | "metadataCas": [ 15 | "data/pki/ca.pem" 16 | ], 17 | "estCa": "data/pki/ca.pem", 18 | "mtls": true, 19 | "attest": "mutual", 20 | "measurementLog": false, 21 | "publish": "http://nuc-02.sos.aisec.fraunhofer.de:8080/results", 22 | "logLevel": "trace", 23 | "api": "libapi", 24 | "apiSerializer": "cbor", 25 | "keyConfig": "EC256", 26 | "metadata": [ 27 | "file://../data/metadata-signed" 28 | ], 29 | "drivers": [ 30 | "sgx" 31 | ], 32 | "ima": false, 33 | "storage": "data/cmcctl-internal", 34 | "cache": "data/cmcctl-cache", 35 | "peerCache": "data/cmc-peer-cache", 36 | "provisionToken": "../data/provision_token" 37 | } 38 | -------------------------------------------------------------------------------- /example-setup/configs/cmcd-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmcAddr": "localhost:9955", 3 | "provisionMode": "est", 4 | "provisionAuth": [ "none" ], 5 | "provisionAddr": "https://localhost:9000/", 6 | "provisionToken": "../data/provision_token", 7 | "metadata": [ 8 | "file://../data/metadata-signed" 9 | ], 10 | "drivers": [ "tpm", "sw" ], 11 | "ima": true, 12 | "imaPcr": 10, 13 | "excludePcrs": [ ], 14 | "keyConfig": "EC256", 15 | "api": "socket", 16 | "policyEngine": "js", 17 | "policyOverwrite": false, 18 | "logLevel": "trace", 19 | "storage": "../data/cmc-internal/", 20 | "cache": "../data/cmc-cache/", 21 | "peerCache": "../data/cmc-peer-cache", 22 | "measurementLog": true, 23 | "ctr": true, 24 | "ctrDriver": "sw", 25 | "ctrLog": "/tmp/container_measurements", 26 | "ctrPcr": 11, 27 | "estTlsCas": [ 28 | "../data/pki/ca.pem" 29 | ], 30 | "identityCas": [ 31 | "../data/pki/ca.pem" 32 | ], 33 | "metadataCas": [ 34 | "../data/pki/ca.pem" 35 | ], 36 | "caKey": "../data/pki/ca-key.pem" 37 | } 38 | -------------------------------------------------------------------------------- /example-setup/policies/policies.js: -------------------------------------------------------------------------------- 1 | // Parse the verification result 2 | var obj = JSON.parse(json); 3 | 4 | var success = true; 5 | 6 | // Basic checks 7 | if (obj.type != "Verification Result") { 8 | console.log("[PolicyEngine] Invalid type"); 9 | success = false; 10 | } 11 | if (obj.summary.status != "success") { 12 | console.log("[PolicyEngine] Attestation not successful") 13 | success = false; 14 | } 15 | 16 | // Check a certain certificate property in the RTM Manifest 17 | var found = false 18 | for (var i = 0; i < obj.metadata.devDescResult.signatureValidation.length; i++) { 19 | for (var j = 0; j < obj.metadata.devDescResult.signatureValidation[i].certs.length; j++) { 20 | for (var k = 0; k < obj.metadata.devDescResult.signatureValidation[i].certs[j].length; k++) { 21 | if (obj.metadata.devDescResult.signatureValidation[i].certs[j][k].subject.commonName == "CMC Test Leaf Certificate") { 22 | found = true; 23 | } 24 | } 25 | } 26 | } 27 | if (!found) { 28 | console.log("[PolicyEngine] Failed to find certificate 'CMC Test Leaf Certificate'"); 29 | success = false; 30 | } 31 | 32 | success 33 | -------------------------------------------------------------------------------- /bin/run-tdx-pccs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "[\033[31;1mFAILED\033[0m] %s\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | case "${PATH}" in ""|"${dir}/bin:"*) ;; *) PATH="${dir}/bin:${PATH}";; esac 8 | 9 | config="${dir}/example-setup/sgx/tdx-pccs-conf.json" 10 | pki="${dir}/data/pki" 11 | 12 | container_image="tdx-pccs:latest" 13 | container_name="tdx-pccs" 14 | 15 | docker_file_path="${dir}/example-setup/docker/${container_name}.dockerfile" 16 | docker_network="tdx-host" 17 | 18 | docker build -t "${container_image}" -f "${docker_file_path}" "${dir}" 19 | 20 | docker network ls | grep -q "${docker_network}" || docker network create "${docker_network}" 21 | 22 | docker create \ 23 | --user "pccs:pccs" \ 24 | -v "${pki}/tdx-pccs-private.pem":/opt/intel/pccs/ssl_key/private.pem \ 25 | -v "${pki}/tdx-pccs-cert.pem":/opt/intel/pccs/ssl_key/file.crt \ 26 | -v "${config}":/opt/intel/pccs/config/default.json \ 27 | --network "${docker_network}" \ 28 | -p 8081:8081 \ 29 | --name "${container_name}" \ 30 | "${container_image}" 31 | 32 | docker start "${container_name}" 33 | -------------------------------------------------------------------------------- /doc/dev.md: -------------------------------------------------------------------------------- 1 | # Developer Documentation 2 | 3 | ### Integration into Go Projects 4 | 5 | The attested TLS or HTTPS libraries can be used within own go projects to provide attestation 6 | for TLS or HTTPS connections, as described in [Integration](./go-integration.md). This is 7 | the easiest way of making using of the CMC attestation framework. 8 | 9 | ### Integration into Generic Projects 10 | 11 | As the *cmcd* provides gRPC, CoAP, and socket APIs, it can be integrated into projects in any 12 | programming language. Attestation reports can be generated and verified using the `generate` 13 | and `verify` endpoint of the *cmcd*. See [APIs and Protocols](#apis-and-protocols). 14 | 15 | ### APIs and Protocols 16 | 17 | For a description of the `cmcd` gRPC, CoaP and socket APIs, refer to [CMCD API](./cmcd-api.md). 18 | These can be used to get certificates and generate and verify attestation reports. 19 | 20 | For a description of the attested TLS attestation protocol, refer to 21 | [Attestation Protocol](./attestation-protocol.md). This is an internal protocol, but can serve 22 | as a blueprint for own attested TLS implementations in other programming languages. -------------------------------------------------------------------------------- /provision/estserver/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | log "github.com/sirupsen/logrus" 20 | ) 21 | 22 | func main() { 23 | 24 | config, err := getConfig() 25 | if err != nil { 26 | log.Fatalf("Failed to load configuration: %v", err) 27 | } 28 | 29 | log.Info("Starting EST server..") 30 | 31 | s, err := NewServer(config) 32 | if err != nil { 33 | log.Fatalf("Failed to create new server: %v", err) 34 | } 35 | 36 | err = s.Serve() 37 | if err != nil { 38 | log.Fatalf("Failed to serve: %v", err) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/PiBootMode.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Present the boot mode values in PI. 3 | 4 | Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
5 | SPDX-License-Identifier: BSD-2-Clause-Patent 6 | 7 | @par Revision Reference: 8 | PI Version 1.2.1A 9 | 10 | **/ 11 | 12 | #ifndef __PI_BOOT_MODE_H__ 13 | #define __PI_BOOT_MODE_H__ 14 | 15 | /// 16 | /// EFI boot mode 17 | /// 18 | typedef UINT32 EFI_BOOT_MODE; 19 | 20 | // 21 | // 0x21 - 0xf..f are reserved. 22 | // 23 | #define BOOT_WITH_FULL_CONFIGURATION 0x00 24 | #define BOOT_WITH_MINIMAL_CONFIGURATION 0x01 25 | #define BOOT_ASSUMING_NO_CONFIGURATION_CHANGES 0x02 26 | #define BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS 0x03 27 | #define BOOT_WITH_DEFAULT_SETTINGS 0x04 28 | #define BOOT_ON_S4_RESUME 0x05 29 | #define BOOT_ON_S5_RESUME 0x06 30 | #define BOOT_WITH_MFG_MODE_SETTINGS 0x07 31 | #define BOOT_ON_S2_RESUME 0x10 32 | #define BOOT_ON_S3_RESUME 0x11 33 | #define BOOT_ON_FLASH_UPDATE 0x12 34 | #define BOOT_IN_RECOVERY_MODE 0x20 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /example-setup/docker/tdx-qgs.dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y wget libcurl4 5 | 6 | # Install SGX ubuntu repository 7 | RUN echo 'deb [signed-by=/etc/apt/keyrings/intel-sgx-keyring.asc arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu mantic main' | tee /etc/apt/sources.list.d/intel-sgx-mantic.list && \ 8 | echo 'Package: *' | tee /etc/apt/preferences.d/intel-sgx-mantic-pin-4000 && \ 9 | echo 'Pin: release o=Intel\ Corporation,n=mantic,c=main' && \ 10 | echo 'Pin-Priority: 499' && \ 11 | wget https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key && \ 12 | mkdir -p /etc/apt/keyrings && \ 13 | cat intel-sgx-deb.key | tee /etc/apt/keyrings/intel-sgx-keyring.asc > /dev/null && \ 14 | apt-get update 15 | 16 | # Install QGS 17 | RUN apt-get install -y --no-install-recommends tdx-qgs libsgx-dcap-default-qpl 18 | 19 | # Create directory for vsock 20 | RUN mkdir -p /var/run/tdx-qgs/ 21 | 22 | # Configure QGS 23 | RUN sed -i "s/localhost:8081/tdx-pccs:8081/" /etc/sgx_default_qcnl.conf && \ 24 | sed -i 's/"use_secure_cert": true/"use_secure_cert": false/' /etc/sgx_default_qcnl.conf && \ 25 | sed -i "s/port = 4050//" /etc/qgs.conf 26 | 27 | # Run QGS 28 | WORKDIR /opt/intel/tdx-qgs 29 | CMD ./qgs --no-daemon 30 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/Compiler.h: -------------------------------------------------------------------------------- 1 | /* Compiler.h 2 | 2017-04-03 : Igor Pavlov : Public domain */ 3 | 4 | #ifndef __7Z_COMPILER_H 5 | #define __7Z_COMPILER_H 6 | 7 | #ifdef _MSC_VER 8 | 9 | #ifdef UNDER_CE 10 | #define RPC_NO_WINDOWS_H 11 | /* #pragma warning(disable : 4115) // '_RPC_ASYNC_STATE' : named type definition in parentheses */ 12 | #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union 13 | #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int 14 | #endif 15 | 16 | #if _MSC_VER >= 1300 17 | #pragma warning(disable : 4996) // This function or variable may be unsafe 18 | #else 19 | #pragma warning(disable : 4511) // copy constructor could not be generated 20 | #pragma warning(disable : 4512) // assignment operator could not be generated 21 | #pragma warning(disable : 4514) // unreferenced inline function has been removed 22 | #pragma warning(disable : 4702) // unreachable code 23 | #pragma warning(disable : 4710) // not inlined 24 | #pragma warning(disable : 4714) // function marked as __forceinline not inlined 25 | #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information 26 | #endif 27 | 28 | #endif 29 | 30 | #define UNUSED_VAR(x) (void)x; 31 | /* #define UNUSED_VAR(x) x=x; */ 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /cmcd/systemd.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/coreos/go-systemd/daemon" 23 | ) 24 | 25 | func notifySystemd() error { 26 | 27 | if _, ok := os.LookupEnv("NOTIFY_SOCKET"); !ok { 28 | log.Debugf("Not running under systemd, skipping notification") 29 | return nil 30 | } 31 | 32 | sent, err := daemon.SdNotify(false, daemon.SdNotifyReady) 33 | if err != nil { 34 | return fmt.Errorf("systemd notify returned error: %v", err) 35 | } 36 | if !sent { 37 | return fmt.Errorf("systemd notify returned false") 38 | } 39 | 40 | log.Debugf("Notified systemd: cmcd is ready") 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /bin/run-tdx-qgs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "[\033[31;1mFAILED\033[0m] %s\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | case "${PATH}" in ""|"${dir}/bin:"*) ;; *) PATH="${dir}/bin:${PATH}";; esac 8 | 9 | container_image="tdx-qgs-image" 10 | container_name="tdx-qgs" 11 | 12 | docker_file_path="${dir}/example-setup/docker/${container_name}.dockerfile" 13 | docker_network="tdx-host" 14 | qgs_vsock_path="/var/run/tdx-qgs" 15 | 16 | docker build -t "${container_image}" -f "${docker_file_path}" "${dir}" 17 | 18 | docker network ls | grep -q "${docker_network}" || docker network create "${docker_network}" 19 | 20 | if [[ -n "$(docker ps -aq -f name="^${container_name}\$")" ]]; then 21 | docker rm "${container_name}" 22 | fi 23 | 24 | docker create \ 25 | --device=/dev/sgx_enclave \ 26 | --device=/dev/sgx_provision \ 27 | -v /dev/log:/dev/log \ 28 | -v "${qgs_vsock_path}":"${qgs_vsock_path}" \ 29 | -it \ 30 | --add-host=host.docker.internal:host-gateway \ 31 | --network "${docker_network}" \ 32 | --name "${container_name}" \ 33 | "${container_image}" \ 34 | /opt/intel/tdx-qgs/qgs --no-daemon 35 | 36 | docker start "${container_name}" 37 | 38 | while [ ! -S /var/run/tdx-qgs/qgs.socket ]; do 39 | sleep 0.1 40 | done 41 | sudo chmod 777 "${qgs_vsock_path}/qgs.socket" -------------------------------------------------------------------------------- /internal/snp.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package internal 17 | 18 | import ( 19 | "fmt" 20 | ) 21 | 22 | type AkType byte 23 | 24 | const ( 25 | UNKNOWN = iota 26 | VCEK 27 | VLEK 28 | ) 29 | 30 | func (t AkType) String() string { 31 | switch t { 32 | case VCEK: 33 | return "vcek" 34 | case VLEK: 35 | return "vlek" 36 | default: 37 | return "unknown" 38 | } 39 | } 40 | 41 | func GetAkType(keySelection uint32) (AkType, error) { 42 | arkey := (keySelection >> 2) & 0x7 43 | if arkey == 0 { 44 | log.Debug("VCEK is used to sign attestation report") 45 | return VCEK, nil 46 | } else if arkey == 1 { 47 | log.Debug("VLEK is used to sign attestation report") 48 | return VLEK, nil 49 | } 50 | return UNKNOWN, fmt.Errorf("unknown AK type %v", arkey) 51 | } 52 | -------------------------------------------------------------------------------- /tools/mrtool/precomputetpm/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package precomputetpm 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | ) 22 | 23 | func detectDriverFileType(file string) (DriverFileType, error) { 24 | 25 | f, err := os.Open(file) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer f.Close() 30 | 31 | buf := make([]byte, 2) 32 | if _, err := f.Read(buf); err != nil { 33 | log.Fatal(err) 34 | } 35 | 36 | switch { 37 | case buf[0] == 0x4D && buf[1] == 0x5A: 38 | log.Debug("Detected file type: PE/COFF") 39 | return PECOFF, nil 40 | case buf[0] == 0x55 && buf[1] == 0xAA: 41 | log.Debug("Detected file type: Option ROM") 42 | return OptionROM, nil 43 | default: 44 | return Unknown, fmt.Errorf("unknown driver file type %02x %02x", buf[0], buf[1]) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /cmcctl/commands.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | func generate(c *config) { 19 | c.api.generate(c) 20 | } 21 | 22 | func verify(c *config) { 23 | c.api.verify(c) 24 | } 25 | 26 | func measure(c *config) { 27 | c.api.measure(c) 28 | } 29 | 30 | func dial(c *config) { 31 | c.api.dial(c) 32 | } 33 | 34 | func listen(c *config) { 35 | c.api.listen(c) 36 | } 37 | 38 | func request(c *config) { 39 | c.api.request(c) 40 | } 41 | 42 | func serve(c *config) { 43 | c.api.serve(c) 44 | } 45 | 46 | func token(c *config) { 47 | err := createToken(c) 48 | if err != nil { 49 | log.Fatalf("Failed to get token: %v", err) 50 | } 51 | } 52 | 53 | func provision(c *config) { 54 | err := retrieveProvisioningData(c) 55 | if err != nil { 56 | log.Fatalf("Failed to get provisioning data: %v", err) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cmcctl/token.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 - 2024 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "os" 22 | 23 | "github.com/Fraunhofer-AISEC/cmc/provision/est" 24 | ) 25 | 26 | func createToken(c *config) error { 27 | 28 | if c.TokenStore == "" { 29 | return errors.New("path to token store must be specified via config") 30 | } 31 | 32 | token, err := est.CreateAndCacheToken(c.TokenStore) 33 | if err != nil { 34 | return fmt.Errorf("failed to create and cache token: %w", err) 35 | } 36 | 37 | // If path to token is provided, store in file, otherwise print to stdout 38 | if c.ProvisionToken == "" { 39 | fmt.Printf("%v", token) 40 | } else { 41 | err = os.WriteFile(c.ProvisionToken, []byte(token), 0644) 42 | if err != nil { 43 | return fmt.Errorf("failed to store token: %w", err) 44 | } 45 | } 46 | 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /doc/ids-example-setup.md: -------------------------------------------------------------------------------- 1 | # Quick Demo Setup IDS Use Case 2 | The CMC repository contains a complete local example setup including a demo CA and all configurations and metadata matching the requirements for representing the certification scheme from the the International Data Spaces (IDS) . 3 | 4 | The setup script `example-setup/setup-cmc-ids` sets up everything to quickly test remote attestation with the IDS specific roles and CAs. 5 | It was tested on Ubuntu 22.04 LTS. 6 | 7 | ## Setup 8 | > :warning: **Note:** You should run this only for testing on a development machine 9 | 10 | ```sh 11 | git clone https://github.com/Fraunhofer-AISEC/cmc.git 12 | /example-setup/setup-cmc-ids 13 | ``` 14 | with `` as the relative or absolute path to the cloned `cmc` repository and 15 | `` as an arbitrary folder where metadata and configuration files are stored. 16 | 17 | ## Run the CMC 18 | 19 | ```sh 20 | DATA= 21 | # Start the EST server that supplies the certificates and metadata for the cmcd 22 | server -config $DATA/est-server-conf-ids.json 23 | 24 | # Build and run the cmcd 25 | cmcd -config $DATA/cmcd-conf.json -addr https://127.0.0.1:9000/metadata-signed 26 | 27 | # Run cmcctl to retrieve an attestation report (stored in current folder unless otherwise specified) 28 | cmcctl -mode generate 29 | 30 | # Run cmcctl to verify the attestation report (stored in current folder unless otherwise specified) 31 | cmcctl -mode verify -ca $DATA/pki/ca.pem -policies $DATA/policies-ids.js 32 | ``` 33 | -------------------------------------------------------------------------------- /tools/backend/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | > :warning: **Note:** This is a simple, unsecure Proof-of-Concept implementation for demonstration purposes only 4 | 5 | Monitoring server for managing attestation results. The server provides a simple HTTP REST API to 6 | store and retrieve attestation results from the CMC. 7 | 8 | The server provides the following REST API: 9 | ```sh 10 | POST /results # Inserts a new result 11 | GET /statistics # Retrieve attestation statistics 12 | GET /results # Retrieves all attestation results 13 | GET /results/:name/latest # Retrieves latest result from the prover :name 14 | GET /resultsbyid/:id # Retrieves a result by its :id 15 | GET /envelopes/:name # Retrieves all result envelopes containing the results from the prover :name 16 | GET /latestresults # Retrieves all latest results from all provers 17 | GET /devices # Retrieves all prover names 18 | ``` 19 | 20 | ## Build 21 | 22 | ```sh 23 | go build 24 | ``` 25 | 26 | ## Test 27 | 28 | Query all devices: 29 | 30 | ```sh 31 | curl http://localhost:8080/results 32 | ``` 33 | 34 | Query single device by ID: 35 | 36 | ```sh 37 | curl http://localhost:8080/results/ 38 | ``` 39 | 40 | Add new device with: 41 | 42 | ```sh 43 | curl http://localhost:8080/results \ 44 | --include \ 45 | --header "Content-Type: application/json" \ 46 | --header "Authorization: Bearer " \ 47 | --request "POST" \ 48 | --data @attestation-result.json 49 | ``` 50 | -------------------------------------------------------------------------------- /bin/vm-retrieve-artifacts: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | trap '[ $? -eq 0 ] && exit 0; printf "[\033[31;1mFAILED\033[0m] %s\n" "$0"' EXIT 4 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 5 | 6 | set -e 7 | 8 | [[ $# -eq 0 ]] || { 9 | echo "Usage: $(basename $0)" 10 | exit 1 11 | } 12 | 13 | user="root" 14 | artifacts="${dir}/vm/artifacts/" 15 | 16 | echo "Writing artifacts to ${artifacts}" 17 | 18 | cmdline="${artifacts}/cmdline" 19 | initrd="initrd.img" 20 | efi_shim="shimx64.efi" 21 | grub="grubx64.efi" 22 | kernel_version=$(vm-ssh uname -r) 23 | kernel="vmlinuz-${kernel_version}" 24 | 25 | echo "Retrieved kernel version ${kernel_version}" 26 | 27 | # Retrieve artifacts 28 | mkdir -p "${artifacts}" 29 | 30 | # Fetch efivars 31 | echo "Fetching efivars..." 32 | vm-ssh 'tar czf - -C /sys/firmware/efi efivars' | tar xzf - -C "${artifacts}" 33 | 34 | # Fetch commandline 35 | echo "Fetching cmdline..." 36 | vm-ssh cat /proc/cmdline > "${artifacts}/cmdline" 37 | 38 | # Fetch GPT table 39 | vm-retrieve-gpt 40 | 41 | # Fetch kernel image 42 | echo "Fetching kernel..." 43 | vm-ssh "cat /boot/${kernel}" > "${artifacts}/vmlinuz" 44 | 45 | # Fetch initrd 46 | echo "Fetching initrd..." 47 | vm-ssh "cat /boot/${initrd}" > "${artifacts}/initrd" 48 | 49 | # Fetch shim 50 | echo "Fetching shim..." 51 | vm-ssh "cat /boot/efi/EFI/ubuntu/${efi_shim}" > "${artifacts}/${efi_shim}" 52 | 53 | # Fetch GRUB 54 | echo "Fetching grub..." 55 | vm-ssh "cat /boot/efi/EFI/ubuntu/${grub}" > "${artifacts}/${grub}" 56 | 57 | # Get grub files 58 | echo "Fetching grub files" 59 | vm-ssh 'tar czf - -C /boot/ grub' | tar xzf - -C "${artifacts}" -------------------------------------------------------------------------------- /bin/sign-metadata: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | if [[ "$#" -ne 1 ]]; then 10 | echo "Usage: $(basename "$0") " 11 | exit 1 12 | fi 13 | 14 | data="${dir}/data" 15 | input="${data}/metadata-raw" 16 | tmp="${data}/metadata-tmp" 17 | out="${data}/metadata-signed" 18 | ser="${1}" 19 | 20 | if [[ ! -d "${input}" ]]; then 21 | echo "Input directory ${input} does not exist. Did you run the setup-cmc script? Abort.." 22 | exit 1 23 | fi 24 | 25 | echo "Using ${data} as directory for local data" 26 | 27 | key="${data}/pki/signing-cert-key.pem" 28 | chain="${data}/pki/signing-cert.pem,${data}/pki/ca.pem" 29 | 30 | # Clean start 31 | rm -rf "${tmp}" 32 | rm -rf "${out}" 33 | 34 | mkdir -p "${tmp}" 35 | mkdir -p "${out}" 36 | 37 | # Iterate over all input metadata 38 | for path in "${input}/"*; do 39 | [[ -e "${path}" ]] || continue 40 | 41 | out_file="$(basename "${path}" | sed 's/\.[^.]*$//').${ser}" 42 | 43 | # Convert to CBOR if specified, otherwise just copy 44 | if [[ "${ser,,}" = "cbor" ]]; then 45 | "${dir}/tools/metaconv/metaconv" \ 46 | -in "${path}" \ 47 | -out "${tmp}/${out_file}" \ 48 | -outform cbor 49 | else 50 | cp "${path}" "${tmp}/${out_file}" 51 | fi 52 | 53 | # Sign metadata object 54 | "${dir}/tools/metasign/metasign" \ 55 | -in "${tmp}/${out_file}" \ 56 | -out "${out}/${out_file}" \ 57 | -keys "${key}" \ 58 | -x5cs "${chain}" 59 | done -------------------------------------------------------------------------------- /tools/fmspc-retrieval-tool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | 10 | "github.com/Fraunhofer-AISEC/cmc/internal" 11 | "github.com/Fraunhofer-AISEC/cmc/verifier" 12 | ) 13 | 14 | var ( 15 | pckCertUrl = "https://api.trustedservices.intel.com/sgx/certification/v4/pckcert?encrypted_ppid=%s&cpusvn=%s&pceid=%s&pcesvn=%s" 16 | ) 17 | 18 | func main() { 19 | encrypted_ppid := flag.String("encrypted_ppid", "", "Encrypted PPID string") 20 | pceid := flag.String("pceid", "", "PCEID string") 21 | cpusvn := flag.String("cpusvn", "", "CPUSVN string") 22 | pcesvn := flag.String("pcesvn", "", "PCESVN string") 23 | flag.Parse() 24 | 25 | if encrypted_ppid == nil || pceid == nil || cpusvn == nil || pcesvn == nil { 26 | return 27 | } 28 | 29 | pckCertUrl = fmt.Sprintf(pckCertUrl, *encrypted_ppid, *cpusvn, *pceid, *pcesvn) 30 | 31 | resp, err := http.Get(pckCertUrl) 32 | if err != nil { 33 | fmt.Println("failed to retrieve PCK certificate") 34 | return 35 | } 36 | 37 | defer resp.Body.Close() 38 | body, err := io.ReadAll(resp.Body) 39 | if err != nil { 40 | fmt.Println("failed to read response body") 41 | return 42 | } 43 | 44 | cert, err := internal.ParseCertsPem(body) 45 | if err != nil { 46 | fmt.Println("failed to parse PEM certificate") 47 | return 48 | } 49 | 50 | sgxExtensions, err := verifier.ParseSGXExtensions(cert[0].Extensions[verifier.SGX_EXTENSION_INDEX].Value[4:]) 51 | if err != nil { 52 | fmt.Println("failed to parse SGX extensions") 53 | return 54 | } 55 | fmt.Print(hex.EncodeToString(sgxExtensions.Fmspc.Value)) 56 | } 57 | -------------------------------------------------------------------------------- /cmcd/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "strings" 20 | 21 | "github.com/Fraunhofer-AISEC/cmc/cmc" 22 | "github.com/sirupsen/logrus" 23 | ) 24 | 25 | var ( 26 | log = logrus.WithField("service", "cmcd") 27 | ) 28 | 29 | func main() { 30 | 31 | c, err := GetConfig() 32 | if err != nil { 33 | log.Fatalf("Failed to load config: %v", err) 34 | } 35 | 36 | log.Infof("Running cmc %v", cmc.GetVersion()) 37 | 38 | cmc, err := cmc.NewCmc(&c.Config) 39 | if err != nil { 40 | log.Fatalf("Failed to init CMC: %v", err) 41 | } 42 | 43 | server, ok := servers[strings.ToLower(c.Api)] 44 | if !ok { 45 | log.Fatalf("API '%v' is not implemented", c.Api) 46 | } 47 | 48 | err = notifySystemd() 49 | if err != nil { 50 | log.Fatalf("Failed to notify systemd: %v", err) 51 | } 52 | 53 | err = server.Serve(c.CmcAddr, cmc) 54 | if err != nil { 55 | log.Fatalf("Failed to serve: %v", err) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/SecMain.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-2-Clause-Patent */ 2 | 3 | #pragma once 4 | 5 | #include 6 | #include "PiHob.h" 7 | 8 | #pragma pack(1) 9 | typedef struct { 10 | EFI_HOB_GUID_TYPE GuidHeader; 11 | UINT16 HostBridgeDevId; 12 | 13 | UINT64 PcdConfidentialComputingGuestAttr; 14 | BOOLEAN SevEsIsEnabled; 15 | 16 | UINT32 BootMode; 17 | BOOLEAN S3Supported; 18 | 19 | BOOLEAN SmmSmramRequire; 20 | BOOLEAN Q35SmramAtDefaultSmbase; 21 | UINT16 Q35TsegMbytes; 22 | 23 | UINT64 FirstNonAddress; 24 | UINT8 PhysMemAddressWidth; 25 | UINT32 Uc32Base; 26 | UINT32 Uc32Size; 27 | 28 | BOOLEAN PcdSetNxForStack; 29 | UINT64 PcdTdxSharedBitMask; 30 | 31 | UINT64 PcdPciMmio64Base; 32 | UINT64 PcdPciMmio64Size; 33 | UINT32 PcdPciMmio32Base; 34 | UINT32 PcdPciMmio32Size; 35 | UINT64 PcdPciIoBase; 36 | UINT64 PcdPciIoSize; 37 | 38 | UINT64 PcdEmuVariableNvStoreReserved; 39 | UINT32 PcdCpuBootLogicalProcessorNumber; 40 | UINT32 PcdCpuMaxLogicalProcessorNumber; 41 | UINT32 DefaultMaxCpuNumber; 42 | 43 | UINT32 S3AcpiReservedMemoryBase; 44 | UINT32 S3AcpiReservedMemorySize; 45 | } EFI_HOB_PLATFORM_INFO; 46 | #pragma pack() 47 | 48 | uint8_t * 49 | extract_lzma_fvmain_new (EFI_FIRMWARE_VOLUME_HEADER *Fv, size_t *extracted_size); -------------------------------------------------------------------------------- /doc/api/json/api/VerificationRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$ref": "#/$defs/VerificationRequest", 4 | "$defs": { 5 | "VerificationRequest": { 6 | "properties": { 7 | "version": { 8 | "type": "string" 9 | }, 10 | "nonce": { 11 | "type": "string", 12 | "contentEncoding": "base64" 13 | }, 14 | "report": { 15 | "type": "string", 16 | "contentEncoding": "base64" 17 | }, 18 | "metadata": { 19 | "additionalProperties": { 20 | "type": "string", 21 | "contentEncoding": "base64" 22 | }, 23 | "type": "object" 24 | }, 25 | "metadataCas": { 26 | "items": { 27 | "type": "string", 28 | "contentEncoding": "base64" 29 | }, 30 | "type": "array" 31 | }, 32 | "identityCas": { 33 | "items": { 34 | "type": "string", 35 | "contentEncoding": "base64" 36 | }, 37 | "type": "array" 38 | }, 39 | "peer": { 40 | "type": "string" 41 | }, 42 | "cacheMisses": { 43 | "items": { 44 | "type": "string" 45 | }, 46 | "type": "array" 47 | }, 48 | "policies": { 49 | "type": "string", 50 | "contentEncoding": "base64" 51 | } 52 | }, 53 | "type": "object", 54 | "required": [ 55 | "version", 56 | "nonce", 57 | "report", 58 | "metadataCas", 59 | "identityCas" 60 | ] 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /bin/build-edk2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | data="${dir}/vm" 10 | vm_config="${dir}/example-setup/vm-config" 11 | 12 | debug=0 13 | edk_output_dir="RELEASE_CLANGPDB" 14 | while [[ $# -gt 0 ]]; do 15 | case "$1" in 16 | -d|--debug) 17 | debug=1 18 | edk_output_dir="DEBUG_CLANGPDB" 19 | shift 20 | ;; 21 | *) 22 | shift 23 | ;; 24 | esac 25 | done 26 | 27 | edk2="${data}/edk2" 28 | 29 | # Build edk2 OVMF firmware 30 | [ -d "${edk2}" ] || { 31 | printf "[\033[34;1m EXEC \033[0m] Building edk2 firmware (%s)..\n" "${edk_output_dir}" 32 | git clone https://github.com/tianocore/edk2.git "${edk2}" 33 | cd "${edk2}" 34 | git checkout edk2-stable202408.01 35 | git submodule update --init 36 | } 37 | 38 | cd "${edk2}" 39 | 40 | make -j "$(nproc)" -C BaseTools/ 41 | 42 | . ./edksetup.sh --reconfig 43 | 44 | build_params=( "-DTPM2_ENABLE=TRUE" 45 | "-DFD_SIZE_2MB" 46 | "-n $(nproc)" 47 | "-a X64" 48 | "-t CLANGPDB" 49 | "-p OvmfPkg/OvmfPkgX64.dsc") 50 | 51 | if [[ "${debug}" -eq 1 ]]; then 52 | build_params+=("-b DEBUG" 53 | "-DDEBUG_ON_SERIAL_PORT=TRUE") 54 | else 55 | build_params+=("-b RELEASE") 56 | fi 57 | 58 | # Build OVMF 59 | build ${build_params[@]} 60 | 61 | mkdir -p "${data}/images" 62 | 63 | # Copy OVMF to images 64 | install -D -m 0644 "${edk2}/Build/OvmfX64/${edk_output_dir}/FV/OVMF.fd" "${data}/images/" 65 | -------------------------------------------------------------------------------- /tools/snptool/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "strings" 20 | 21 | "github.com/sirupsen/logrus" 22 | "github.com/urfave/cli/v3" 23 | ) 24 | 25 | type Config struct { 26 | In string 27 | Out string 28 | LogLevel string 29 | Vmpl int 30 | } 31 | 32 | func GetConfig(cmd *cli.Command) (*Config, error) { 33 | 34 | c := &Config{} 35 | 36 | if cmd.IsSet(inFlag) { 37 | c.In = cmd.String(inFlag) 38 | } 39 | if cmd.IsSet(outFlag) { 40 | c.Out = cmd.String(outFlag) 41 | } 42 | 43 | if cmd.IsSet(logLevelFlag) { 44 | c.LogLevel = cmd.String(logLevelFlag) 45 | } 46 | if c.LogLevel != "" { 47 | l, ok := logLevels[strings.ToLower(c.LogLevel)] 48 | if !ok { 49 | log.Warnf("LogLevel %v does not exist. Default to info level", c.LogLevel) 50 | l = logrus.InfoLevel 51 | } 52 | logrus.SetLevel(l) 53 | } else { 54 | logrus.SetLevel(logrus.InfoLevel) 55 | } 56 | 57 | c.Vmpl = cmd.Int(vmplFlag) 58 | 59 | return c, nil 60 | } 61 | -------------------------------------------------------------------------------- /bin/generate-device-description: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | name="${1:-testdevice.test.de}" 10 | 11 | data="${dir}/data" 12 | input="${dir}/example-setup/metadata-templates" 13 | out="${data}/metadata-raw" 14 | 15 | if [[ ! -d "${data}" ]]; then 16 | echo "Data directory ${data} does not exist. Did you run the setup-cmc script? Abort.." 17 | exit 1 18 | fi 19 | 20 | if [[ ! -d "${input}" ]]; then 21 | echo "Data directory ${input} does not exist. Did you run the setup-cmc script? Abort.." 22 | exit 1 23 | fi 24 | 25 | mkdir -p "${out}" 26 | 27 | echo "Using ${data} as directory for local data" 28 | 29 | # Load device description template 30 | json=$(cat "${input}/device.description.json") 31 | 32 | # Insert description properties 33 | setjson "json" "name" "${name}" 34 | setjson "json" "version" "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" 35 | setjson "json" "validity.notBefore" "$(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "-1 day")" 36 | setjson "json" "validity.notAfter" "$(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "+2 years")" 37 | setjson "json" "description" "Device Description" 38 | setjson "json" "location" "Munich" 39 | 40 | # Save device description 41 | echo "Writing ${out}/device.description.json" 42 | printf "%s\n" "${json}" > "${out}/device.description.json" 43 | 44 | # Load device config 45 | json=$(cat "${input}/device.config.json") 46 | 47 | # Save device config 48 | echo "Writing ${out}/device.config.json" 49 | printf "%s\n" "${json}" > "${out}/device.config.json" -------------------------------------------------------------------------------- /bin/vm-start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "[\033[31;1mFAILED\033[0m] %s\n" "$0"' EXIT 6 | dir=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P) 7 | 8 | data="${dir}/vm" 9 | 10 | id="${1:-0}" 11 | 12 | image="${data}/images/noble-server-cloudimg-amd64.img" 13 | 14 | ovmf="${data}/images/OVMF.fd" 15 | tpm_sock="${data}/swtpm/swtpm-sock" 16 | 17 | [[ -f "${ovmf}" ]] || { 18 | echo "OVMF file ${ovmf} does not exist. Exit.." 19 | exit 20 | } 21 | 22 | [[ -f "${image}" ]] || { 23 | echo "Image file ${image} does not exist. Exit.." 24 | exit 25 | } 26 | 27 | [[ -S "${tpm_sock}" ]] || { 28 | echo "TPM socket ${tpm_sock} does not exist. Exit.." 29 | exit 30 | } 31 | 32 | qemu_opts=( 33 | "-name machine${id},process=machine${id}" 34 | "-machine q35,accel=kvm,vmport=off,kernel_irqchip=split" 35 | "-cpu kvm64-v1" 36 | "-m 4096M" 37 | "-nographic" 38 | "-serial mon:stdio" 39 | "-drive if=pflash,format=raw,unit=0,file=${ovmf},readonly=on" 40 | "-device virtio-rng-pci" 41 | "-device virtio-scsi-pci,id=scsi" 42 | "-device scsi-hd,drive=hd0 -drive if=none,id=hd0,file=${image}" 43 | "-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::9955-:9955,hostfwd=tcp::4443-:4443" 44 | "-device virtio-net-pci,disable-legacy=on,netdev=net0,romfile=" 45 | "-chardev socket,id=chrtpm,path=${tpm_sock}" 46 | "-tpmdev emulator,id=tpm0,chardev=chrtpm" 47 | "-device tpm-tis,tpmdev=tpm0" 48 | "-drive file=${dir}/vm/images/seed.iso,media=cdrom,readonly=on" 49 | ) 50 | 51 | # Start vm 52 | printf "[\033[34;1m EXEC \033[0m] Starting VM..\n" 53 | 54 | exec "${data}/qemu/build/qemu-system-x86_64" ${qemu_opts[@]} 55 | -------------------------------------------------------------------------------- /doc/policies.md: -------------------------------------------------------------------------------- 1 | # Attestation Policies 2 | 3 | The basic attestation report validation verifies all signatures, certificate chains and reference 4 | values against the measurements. To enable custom policies, such as the verification of certain 5 | certificate properties, the blacklisting of certain software artifacts with known vulnerabilities 6 | or the enforcement of a four eyes principle mandating different PKIs for the manifests, the 7 | attestation report module implements a generic policies interface. 8 | 9 | The current implementation contains the `attestationpolicies` module which implements a javascript 10 | engine. This allows passing arbitrary javascript files via the `cmcctl` `-policies` parameter. 11 | The policies javascript file is then used to evaluate arbitrary attributes of the JSON 12 | attestation result output by the `cmcd` and stored by the `cmcctl`. The attestation result 13 | can be referenced via the `json` variable in the script. The javascript code must return a single 14 | boolean indicating success or failure of the custom policy validation. A minimal policies file, 15 | verifying only the `type` field of the attesation result could look as follows: 16 | 17 | ```js 18 | // Parse the verification result 19 | var obj = JSON.parse(json); 20 | var success = true; 21 | 22 | // Check the type field of the verification result 23 | if (obj.type != "Verification Result") { 24 | console.log("Invalid type"); 25 | success = false; 26 | } 27 | 28 | success 29 | ``` 30 | 31 | Via the `overwritePolicies` configuration parameter, fields of the verification result can even 32 | be overwritten, e.g. from `fail` to `warn` to allow less strict checking. This should be used 33 | with care. 34 | -------------------------------------------------------------------------------- /cmc/metadata_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package cmc 17 | 18 | import ( 19 | "testing" 20 | 21 | "github.com/sirupsen/logrus" 22 | ) 23 | 24 | func Test_isNewer(t *testing.T) { 25 | type args struct { 26 | t string 27 | ref string 28 | } 29 | tests := []struct { 30 | name string 31 | args args 32 | want bool 33 | wantErr bool 34 | }{ 35 | {"Newer", args{"2023-03-15T23:59:14Z", "2022-12-11T15:19:00Z"}, true, false}, 36 | {"Older", args{"2022-12-11T15:19:00Z", "2023-03-15T23:59:14Z"}, false, false}, 37 | {"Error", args{"abc", "2022-12-11T15:19:00Z"}, false, true}, 38 | } 39 | logrus.SetLevel(logrus.TraceLevel) 40 | for _, tt := range tests { 41 | t.Run(tt.name, func(t *testing.T) { 42 | got, err := isNewer(tt.args.t, tt.args.ref) 43 | if (err != nil) != tt.wantErr { 44 | t.Errorf("isNewer() error = %v, wantErr %v", err, tt.wantErr) 45 | return 46 | } 47 | if got != tt.want { 48 | t.Errorf("isNewer() = %v, want %v", got, tt.want) 49 | } 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/MeasureBootPeCoff.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-2-Clause-Patent */ 2 | 3 | #pragma once 4 | 5 | #include "stdbool.h" 6 | #include 7 | #include "UefiBaseType.h" 8 | 9 | // https://github.com/torvalds/linux/blob/master/Documentation/x86/boot.rst 10 | typedef enum __attribute__ ((__packed__)) 11 | { 12 | LILO = 0x00, 13 | LOADLIN = 0x10, 14 | BOOTSECT_LOADER = 0x20, 15 | SYSLINUX = 0x30, 16 | PXE = 0x40, 17 | ELILO = 0x50, 18 | GRUB = 0x70, 19 | UBOOT = 0x80, 20 | XEN = 0x90, 21 | GUJIN = 0xA0, 22 | QEMU = 0xB0, 23 | UCBOOTLOADER = 0xC0, 24 | KEXEC = 0xD0, 25 | EXTENDED = 0xE0, 26 | SPECIAL = 0xF0, 27 | MINIMAL_LINUX_BOOTLOADER = 0x11, 28 | OVMF = 0x12, 29 | UNDEFINED = 0xFF 30 | } type_of_loader_t; 31 | 32 | // https://github.com/torvalds/linux/blob/master/Documentation/x86/boot.rst 33 | typedef struct __attribute__((packed)) 34 | { 35 | uint8_t not_implemented[0x210]; 36 | uint8_t type_of_loader; 37 | uint8_t loadflags; 38 | uint16_t setup_move_size; 39 | uint32_t code32_start; 40 | uint32_t ramdisk_image; 41 | uint32_t ramdisk_size; 42 | uint32_t bootsect_kludge; 43 | uint16_t heap_end_ptr; 44 | uint8_t ext_loader_ver; 45 | uint8_t ext_loader_type; 46 | uint32_t cmd_line_ptr; 47 | uint32_t ramdisk_max; 48 | } kernel_setup_hdr_t; 49 | 50 | void 51 | SetDebug(); 52 | 53 | int 54 | LoadPeImage (uint8_t **buf, uint64_t *size, const char *filename); 55 | 56 | EFI_STATUS 57 | MeasurePeImage (const EVP_MD *md, uint8_t *hash, const uint8_t *buf, const UINTN buf_size); 58 | 59 | uint64_t 60 | MeasurePeImageSha256(uint8_t *hash, const uint8_t *buf, const uint64_t buf_size); 61 | 62 | uint64_t 63 | MeasurePeImageSha384(uint8_t *hash, const uint8_t *buf, const uint64_t buf_size); -------------------------------------------------------------------------------- /tools/mrtool/tcg/mrindex.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package tcg 17 | 18 | // UEFI Spec 2.10 Section 38.4.1: 19 | // TPM PCR Index | CC Measurement Register Index | TDX-measurement register 20 | // 0 | 0 | MRTD 21 | // 1, 7 | 1 | RTMR[0] 22 | // 2~6 | 2 | RTMR[1] 23 | // 8~15 | 3 | RTMR[2] 24 | const ( 25 | MR_LEN = 6 26 | INDEX_MRTD = 0 27 | INDEX_RTMR0 = 1 28 | INDEX_RTMR1 = 2 29 | INDEX_RTMR2 = 3 30 | INDEX_RTMR3 = 4 31 | INDEX_MRSEAM = 5 // Additional reference value, not part of UEFI spec 32 | ) 33 | 34 | func IndexToMr(index int) string { 35 | switch index { 36 | case INDEX_MRTD: 37 | return "MRTD" 38 | case INDEX_RTMR0: 39 | return "RTMR0" 40 | case INDEX_RTMR1: 41 | return "RTMR1" 42 | case INDEX_RTMR2: 43 | return "RTMR2" 44 | case INDEX_RTMR3: 45 | return "RTMR3" 46 | case INDEX_MRSEAM: 47 | return "MRSEAM" 48 | default: 49 | return "unknown" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bin/snp-ec2-setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | data="${dir}/vm" 10 | 11 | edk2="${data}/edk2-aws" 12 | measured_boot_tools="${data}/measured-boot-tools" 13 | 14 | # Install prerequisites 15 | printf "[\033[34;1m EXEC \033[0m] Checking prerequisites..\n" 16 | packages=() 17 | for pkg in "${packages[@]}"; do 18 | if ! dpkg-query -W -f='${Status}' "${pkg}" 2>/dev/null | grep -q "installed"; then 19 | echo "Fatal: missing package: ${pkg}" 20 | exit 1 21 | fi 22 | done 23 | 24 | # Build edk2 OVMF firmware 25 | [ -d "${edk2}" ] || { 26 | printf "[\033[34;1m EXEC \033[0m] Building aws edk2 firmware..\n" 27 | git clone https://github.com/aws/uefi.git "${edk2}" 28 | cd "${edk2}" 29 | # TODO correct version git checkout edk2-stable202502 30 | 31 | # Build OVMF 32 | nix-build --pure 33 | 34 | # Copy OVMF to images 35 | install -D -m 0644 "${edk2}/result/ovmf_img.fd" "${data}/images/OVMF-AWS.fd" 36 | } 37 | 38 | # Build CMC 39 | printf "[\033[34;1m EXEC \033[0m] Building CMC..\n" 40 | go build -C "${dir}/cmcctl" 41 | go build -C "${dir}/cmcd" 42 | go build -C "${dir}/provision/estserver" 43 | go build -C "${dir}/tools/metaconv" 44 | go build -C "${dir}/tools/metasign" 45 | 46 | # Create a folder for the cmc configuration and metadata 47 | mkdir -p "${data}" 48 | 49 | # Generate an example PKI 50 | [ -d "${dir}/data/pki" ] || { 51 | printf "[\033[34;1m EXEC \033[0m] Generating PKI..\n" 52 | "${dir}/bin/setup-pki${script_suffix}" 53 | } 54 | 55 | # TODO Create cloud-init configuration 56 | 57 | # Finished successfully 58 | printf "[\033[32;1m OK \033[0m] $0\n" 59 | -------------------------------------------------------------------------------- /attestationreport/provision.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package attestationreport 17 | 18 | import ( 19 | "crypto/x509" 20 | 21 | "github.com/Fraunhofer-AISEC/cmc/internal" 22 | "github.com/Fraunhofer-AISEC/go-attestation/attest" 23 | ) 24 | 25 | type Provisioner interface { 26 | CaCerts() ([]*x509.Certificate, error) 27 | SimpleEnroll(csr *x509.CertificateRequest) (*x509.Certificate, error) 28 | TpmActivateEnroll( 29 | tpmManufacturer, ekCertUrl string, 30 | tpmMajor, tpmMinor int, 31 | csr *x509.CertificateRequest, 32 | akParams attest.AttestationParameters, 33 | ekPublic, ekCertDer []byte, 34 | ) ([]byte, []byte, []byte, error) 35 | TpmCertifyEnroll( 36 | csr *x509.CertificateRequest, 37 | ikParams attest.CertificationParameters, 38 | akPublic []byte, 39 | report []byte, 40 | metadata [][]byte, 41 | ) (*x509.Certificate, error) 42 | CcEnroll(csr *x509.CertificateRequest, report []byte, metadata [][]byte) (*x509.Certificate, error) 43 | GetSnpCa(codeName string, akType internal.AkType) ([]*x509.Certificate, error) 44 | GetSnpVcek(codeName string, chipId [64]byte, tcb uint64) (*x509.Certificate, error) 45 | } 46 | -------------------------------------------------------------------------------- /example-setup/vm-config/user-data: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | users: 3 | - name: root 4 | ssh-authorized-keys: 5 | - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZweThKfTBRjSDMa8RNt6UMwl3djUMlFLNpuySKCIpV 6 | shell: /bin/bash 7 | lock_passwd: true 8 | 9 | chpasswd: 10 | expire: false 11 | users: 12 | - {name: root, password: root, type: text} 13 | 14 | disable_root: false 15 | 16 | ssh_pwauth: false 17 | 18 | apt: 19 | sources: 20 | docker: 21 | keyid: '9DC858229FC7DD38854AE2D88D81803C0EBFCD88' 22 | keyserver: 'https://download.docker.com/linux/ubuntu/gpg' 23 | source: 'deb [ signed-by=$KEY_FILE] https://download.docker.com/linux/ubuntu $RELEASE stable' 24 | 25 | # will be done anyway since packages and apt source provided 26 | package_update: true 27 | 28 | packages: 29 | - tpm2-tools 30 | - docker-ce 31 | - docker-ce-cli 32 | - containerd.io 33 | 34 | # enable ipv4 forwarding for docker 35 | write_files: 36 | - path: /etc/sysctl.d/enabled_ipv4_forwarding.conf 37 | content: | 38 | net.ipv4.conf.all.forwarding=1 39 | 40 | runcmd: 41 | - mount /dev/sr0 /mnt/ 42 | - mkdir /var/cmcctl 43 | - cp /mnt/cmcd /usr/bin/ 44 | - cp /mnt/cmcctl /usr/bin/ 45 | - cp /mnt/mrtool /usr/bin/ 46 | - cp /mnt/parse-srtm-pcrs /usr/bin/ 47 | - cp /mnt/parse-ima-pcr /usr/bin/ 48 | - cp /mnt/cmcd-conf.json /etc/cmcd-conf.json 49 | - cp /mnt/cmcctl-conf-vm.json /etc/cmcctl-conf-vm.json 50 | - cp /mnt/cmcd.service /etc/systemd/system/cmcd.service 51 | - cp /mnt/cmcctl.service /etc/systemd/system/cmcctl.service 52 | - cp /mnt/ca.pem /var/ca.pem 53 | - cp /mnt/metadata-ca.pem /var/metadata-ca.pem 54 | - systemctl daemon-reload 55 | - systemctl enable cmcd.service 56 | - systemctl start cmcd.service 57 | - systemctl enable cmcctl.service 58 | - systemctl start cmcctl.service 59 | 60 | 61 | -------------------------------------------------------------------------------- /internal/helpers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package internal 17 | 18 | import ( 19 | "reflect" 20 | "testing" 21 | ) 22 | 23 | func TestFilterInts(t *testing.T) { 24 | type args struct { 25 | list []int 26 | excludeList []int 27 | } 28 | tests := []struct { 29 | name string 30 | args args 31 | want []int 32 | }{ 33 | { 34 | name: "Exclude PCRs Success", 35 | args: args{ 36 | list: []int{0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 37 | excludeList: []int{0, 6, 14, 15}, 38 | }, 39 | want: []int{1, 2, 3, 4, 7, 8, 9, 10, 11, 12, 13}, 40 | }, 41 | { 42 | name: "Exclude No PCRs Success", 43 | args: args{ 44 | list: []int{0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 45 | excludeList: []int{}, 46 | }, 47 | want: []int{0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 48 | }, 49 | } 50 | for _, tt := range tests { 51 | t.Run(tt.name, func(t *testing.T) { 52 | if got := FilterInts(tt.args.list, tt.args.excludeList); !reflect.DeepEqual(got, tt.want) { 53 | t.Errorf("FilterInts() = %v, want %v", got, tt.want) 54 | } 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tools/mrtool/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | > :warning: **Note:** The tool is work in progress and not to be used in production! 4 | 5 | For more tools, see also: https://github.com/Fraunhofer-AISEC/measured-boot-tools 6 | 7 | ## Overview 8 | 9 | The *mrtool* parses or precomputes the values of Trusted Platform Module (TPM) Platform 10 | Configuration Registers (PCRs), Intel TDX measurement registers (MRTD and RTMRs), as well as the 11 | AMD SEV-SNP measurement. 12 | 13 | The tool can be used to calculate the golden reference values for remote attestation 14 | based on the built UEFI firmware, Linux kernel, kernel commandline, configuration parameters 15 | and user space. 16 | 17 | ## Build 18 | 19 | ```sh 20 | go build 21 | ``` 22 | 23 | ## Usage 24 | 25 | See `mrtool -help`. 26 | 27 | ## Examples 28 | 29 | Parse the IMA eventlog and output reference values for PCR10 30 | ```sh 31 | mrtool parse ima --mrs 10 32 | ``` 33 | 34 | Parse the TPM eventlog for PCRs 0-9 and output the eventlog, the final PCR values and the aggregated 35 | PCR value over all PCRs 36 | ```sh 37 | mrtool parse tpm --mrs 0,1,2,3,4,5,6,7,8,9 38 | ``` 39 | 40 | Parse the TDX CC eventlog for RTMR0-3 41 | ```sh 42 | mrtool parse tdx --mrs 1,2,3,4 43 | ``` 44 | 45 | Calculate the `boot_aggregate` with an eventlog from a custom location. Then precompute the IMA 46 | reference values for the `boot_aggregate` and for all files in the paths `/usr/bin`, `/usr/sbin` 47 | and `/usr/lib`. 48 | ```sh 49 | boot_aggregate=$(mrtool parse tpm \ 50 | --mrs 0,1,2,3,4,5,6,7,8,9 \ 51 | --eventlog ./binary_bios_measurements \ 52 | --print-eventlog=false \ 53 | --print-aggregate=true \ 54 | | jq -r .sha256) 55 | 56 | mrtool precompute ima \ 57 | --mrs 10 \ 58 | --path /usr/bin,/usr/sbin,/usr/lib \ 59 | --template ima-ng \ 60 | --boot-aggregate "${boot_aggregate}" 61 | ``` 62 | 63 | 64 | -------------------------------------------------------------------------------- /doc/setup-snp.md: -------------------------------------------------------------------------------- 1 | # AMD SEV-SNP Setup 2 | 3 | Describes the setup to run the CMC within AMD SEV-SNP Confidential VMs. Currently, QEMU and 4 | AWS EC2 VMs are supported. 5 | 6 | ## Prerequisites 7 | 8 | Building the AWS Firmware (OVMF) to calculate the reference values for attestation of AWS AMD SEV-SNP 9 | virtual machines requires [Nix](https://nixos.org/download/) 10 | 11 | ## SNP Setup 12 | 13 | Creates the PKI and metadata for running the CMC within SNP Confidential VMs. 14 | ```sh 15 | source env.bash 16 | ``` 17 | 18 | ### QEMU VMs 19 | ```sh 20 | cmc-docker vm-setup 21 | 22 | cmc-docker setup-cmc snp 23 | ``` 24 | 25 | ### AWS EC2 VMs 26 | ```sh 27 | cmc-docker snp-ec2-setup 28 | 29 | cmc-docker setup-cmc-ec2 30 | ``` 31 | 32 | ## SNP Build 33 | 34 | Everything can be built and installed according to the 35 | [Build Documentation](./build-and-install.md). 36 | 37 | ## SNP Run 38 | 39 | the `cmcd` and the `cmcctl` including their [configuration files](./run.md) must be installed 40 | into the CVM. Then the CMC can be run as described in 41 | [Running the CMC](./run.md). Potentially you also want to create e.g. systemd services 42 | to automatically run the components. 43 | 44 | --- 45 | 46 | 47 | ## AMD SEV-SNP Manual Metadata Generation 48 | 49 | Refer to the documentation of the AMD SNP 50 | [virtee sev-snp-measure tool](https://github.com/virtee/sev-snp-measure). 51 | 52 | An example can be found in the [generate-rtm-manifest-snp](../bin/generate-rtm-manifest-snp) script. 53 | 54 | ### Further documentation 55 | 56 | Building and installing the individual components with various flags is described in the 57 | [Build](./build-and-install.md) documentation. A more detailed description on how to configure and 58 | run the components is described in the [Run](./run.md) documentation. For building own applications 59 | using the *cmcd*, refer to the [Developer Documentation](./dev.md). -------------------------------------------------------------------------------- /internal/auth.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package internal 17 | 18 | import ( 19 | "fmt" 20 | "strings" 21 | ) 22 | 23 | type AuthMethod uint32 24 | 25 | const ( 26 | AuthNone AuthMethod = 0 27 | AuthToken AuthMethod = 1 << iota 28 | AuthCertificate 29 | AuthAttestation 30 | ) 31 | 32 | func ParseAuthMethods(input []string) (AuthMethod, error) { 33 | var methods AuthMethod 34 | for _, part := range input { 35 | switch strings.TrimSpace(strings.ToLower(part)) { 36 | case "none": 37 | methods |= AuthNone 38 | case "token": 39 | methods |= AuthToken 40 | case "certificate": 41 | methods |= AuthCertificate 42 | case "attestation": 43 | methods |= AuthAttestation 44 | default: 45 | return 0, fmt.Errorf("unknown auth method: %q", part) 46 | } 47 | } 48 | return methods, nil 49 | } 50 | 51 | func (a AuthMethod) Has(method AuthMethod) bool { 52 | return a&method != 0 53 | } 54 | 55 | func (a AuthMethod) String() string { 56 | if a == AuthNone { 57 | return "none" 58 | } 59 | 60 | var parts []string 61 | if a.Has(AuthToken) { 62 | parts = append(parts, "token") 63 | } 64 | if a.Has(AuthCertificate) { 65 | parts = append(parts, "certificate") 66 | } 67 | if a.Has(AuthAttestation) { 68 | parts = append(parts, "attestation") 69 | } 70 | 71 | return strings.Join(parts, ",") 72 | } 73 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/LzHash.h: -------------------------------------------------------------------------------- 1 | /* LzHash.h -- HASH functions for LZ algorithms 2 | 2015-04-12 : Igor Pavlov : Public domain */ 3 | 4 | #ifndef __LZ_HASH_H 5 | #define __LZ_HASH_H 6 | 7 | #define kHash2Size (1 << 10) 8 | #define kHash3Size (1 << 16) 9 | #define kHash4Size (1 << 20) 10 | 11 | #define kFix3HashSize (kHash2Size) 12 | #define kFix4HashSize (kHash2Size + kHash3Size) 13 | #define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) 14 | 15 | #define HASH2_CALC hv = cur[0] | ((UInt32)cur[1] << 8); 16 | 17 | #define HASH3_CALC {\ 18 | UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ 19 | h2 = temp & (kHash2Size - 1); \ 20 | hv = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } 21 | 22 | #define HASH4_CALC {\ 23 | UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ 24 | h2 = temp & (kHash2Size - 1); \ 25 | temp ^= ((UInt32)cur[2] << 8); \ 26 | h3 = temp & (kHash3Size - 1); \ 27 | hv = (temp ^ (p->crc[cur[3]] << 5)) & p->hashMask; } 28 | 29 | #define HASH5_CALC {\ 30 | UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ 31 | h2 = temp & (kHash2Size - 1); \ 32 | temp ^= ((UInt32)cur[2] << 8); \ 33 | h3 = temp & (kHash3Size - 1); \ 34 | temp ^= (p->crc[cur[3]] << 5); \ 35 | h4 = temp & (kHash4Size - 1); \ 36 | hv = (temp ^ (p->crc[cur[4]] << 3)) & p->hashMask; } 37 | 38 | /* #define HASH_ZIP_CALC hv = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ 39 | #define HASH_ZIP_CALC hv = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; 40 | 41 | #define MT_HASH2_CALC \ 42 | h2 = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); 43 | 44 | #define MT_HASH3_CALC {\ 45 | UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ 46 | h2 = temp & (kHash2Size - 1); \ 47 | h3 = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } 48 | 49 | #define MT_HASH4_CALC {\ 50 | UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ 51 | h2 = temp & (kHash2Size - 1); \ 52 | temp ^= ((UInt32)cur[2] << 8); \ 53 | h3 = temp & (kHash3Size - 1); \ 54 | h4 = (temp ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /tools/fvextract/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | const ( 12 | PEIFV = "peifv" 13 | DXEFV = "dxefv" 14 | 15 | PEIFV_SIZE = 0xE0000 16 | DXEFV_SIZE = 0xE80000 17 | ) 18 | 19 | func main() { 20 | 21 | refFile := flag.String("ref", "", "firmware volume reference file") 22 | inputFile := flag.String("in", "", "dumped qemu memory") 23 | outputFile := flag.String("out", "", "extracted uefi firmware volume") 24 | volume := flag.String("fv", "", "firmware volume to extract [peifv, dxefv]") 25 | refSize := flag.Int("refsize", 256, "This number of bytes will be used to find the reference volume in the dumped memory") 26 | flag.Parse() 27 | 28 | if *inputFile == "" { 29 | fmt.Println("Input file not specified") 30 | flag.Usage() 31 | return 32 | } 33 | if *outputFile == "" { 34 | fmt.Println("Output file not specified") 35 | flag.Usage() 36 | return 37 | } 38 | if *refFile == "" { 39 | fmt.Println("Reference file not specified") 40 | flag.Usage() 41 | } 42 | 43 | var volumeSize int 44 | if strings.EqualFold(*volume, PEIFV) { 45 | volumeSize = PEIFV_SIZE 46 | } else if strings.EqualFold(*volume, DXEFV) { 47 | volumeSize = DXEFV_SIZE 48 | } else { 49 | fmt.Printf("Unknown firmware volume type %q. Allowed: peifv, dxefv\n", *volume) 50 | return 51 | } 52 | 53 | ref, err := os.ReadFile(*refFile) 54 | if err != nil { 55 | fmt.Printf("Failed to read reference file: %v\n", err) 56 | return 57 | } 58 | 59 | in, err := os.ReadFile(*inputFile) 60 | if err != nil { 61 | fmt.Printf("Failed to read input file: %v\n", err) 62 | return 63 | } 64 | 65 | index := bytes.Index(in, ref[:*refSize]) 66 | if index == -1 { 67 | fmt.Printf("Failed to find index\n") 68 | return 69 | } 70 | 71 | fmt.Printf("Index: %x\n", index) 72 | 73 | err = os.WriteFile(*outputFile, in[index:index+volumeSize], 0644) 74 | if err != nil { 75 | fmt.Printf("Failed to write output file %v: %v\n", outputFile, err) 76 | return 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /example-setup/sgx/tdx-pccs-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "HTTPS_PORT" : 8081, 3 | "hosts" : "0.0.0.0", 4 | "uri": "https://api.trustedservices.intel.com/sgx/certification/v4/", 5 | "ApiKey": "2b1aabacc53a46c8b47665bb7472d62b", 6 | "proxy" : "", 7 | "RefreshSchedule": "0 0 1 * * *", 8 | "UserTokenHash" : "a680eadb53bd0ba34a50b3cec66d0f4a1f932368459ce1b3797480fe85c6441c9ee6d498fcaf34c964a5191b2f0833ebae05f53ade9a2003967c332e1ef1dd7e", 9 | "AdminTokenHash" : "1a7bedcf99e038a35bc6931f8cc7d7ebec930f556b37cb30e3f9d973e076a1999947fc8d75a3a2777b8d2306cfc7803c198a543edbeac01259e871e95fc2f110", 10 | "CachingFillMode" : "LAZY", 11 | "OPENSSL_FIPS_MODE" : false, 12 | "LogLevel" : "debug", 13 | "DB_CONFIG" : "sqlite", 14 | "sqlite" : { 15 | "database" : "database", 16 | "username" : "username", 17 | "password" : "password", 18 | "options" : { 19 | "host": "localhost", 20 | "dialect": "sqlite", 21 | "pool": { 22 | "max": 5, 23 | "min": 0, 24 | "acquire": 30000, 25 | "idle": 10000 26 | }, 27 | "define": { 28 | "freezeTableName": true 29 | }, 30 | "logging" : false, 31 | "storage": "pckcache.db" 32 | } 33 | }, 34 | "mysql" : { 35 | "database" : "pckcache", 36 | "username" : "root", 37 | "password" : "mypass", 38 | "options" : { 39 | "host": "localhost", 40 | "port": "3306", 41 | "dialect": "mysql", 42 | "pool": { 43 | "max": 5, 44 | "min": 0, 45 | "acquire": 30000, 46 | "idle": 10000 47 | }, 48 | "define": { 49 | "freezeTableName": true 50 | }, 51 | "logging" : false 52 | }, 53 | "ssl":{ 54 | "required": false, 55 | "ca":"/if_required/path/to/your_ssl_ca" 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /bin/setup-pki: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | input="${dir}/example-setup/pki-input" 10 | out="${dir}/data/pki" 11 | 12 | if [[ ! -d "${input}" ]]; then 13 | echo "Input directory ${input} does not exist" 14 | exit 1 15 | fi 16 | 17 | echo "Using ${input} as input directory" 18 | echo "Using ${out} as output directory" 19 | 20 | mkdir -p "${out}" 21 | 22 | # Set up root CA 23 | cfssl gencert -initca "${input}/cfssl-ca.json" | cfssljson -bare "${out}/ca" 24 | 25 | # Generate key and CSR for metadata signing 26 | cfssl genkey -config "${input}/cfssl-ca-config.json" -profile leafcert "${input}/cfssl-leaf.json" | cfssljson -bare "${out}/signing-cert" 27 | 28 | # Sign CSR and generate certificate for metadata signing 29 | cfssl sign -ca "${out}/ca.pem" -ca-key "${out}/ca-key.pem" -profile leafcert -config "${input}/cfssl-ca-config.json" "${out}/signing-cert.csr" | cfssljson -bare "${out}/signing-cert" 30 | 31 | # Generate key and CSR for EST server TLS connections 32 | cfssl genkey -config "${input}/cfssl-ca-config.json" -profile tlscert "${input}/cfssl-est-tls.json" | cfssljson -bare "${out}/est-tls" 33 | 34 | # Sign CSR and generate certificate for EST server TLS connections 35 | cfssl sign -ca "${out}/ca.pem" -ca-key "${out}/ca-key.pem" -profile tlscert -config "${input}/cfssl-ca-config.json" "${out}/est-tls.csr" | cfssljson -bare "${out}/est-tls" 36 | 37 | # Generate certificates to use with Intel TDX PCCS 38 | pccs_key="${out}/tdx-pccs-private.pem" 39 | pccs_csr="${out}/tdx-pccs-csr.pem" 40 | pccs_crt="${out}/tdx-pccs-cert.pem" 41 | openssl genrsa -out "${pccs_key}" 2048 42 | openssl req -new -key "${pccs_key}" -out "${pccs_csr}" \ 43 | -subj "/C=DE/ST=BY/L=Munich/O=TestCompany/OU=TestUnit/CN=localhost/emailAddress=test@pccs.de" 44 | openssl x509 -req -days 1095 -in "${pccs_csr}" -signkey "${pccs_key}" -out "${pccs_crt}" 45 | rm -rf "${pccs_csr}" 46 | chmod 644 "${pccs_key}" 47 | chmod 644 "${pccs_crt}" 48 | -------------------------------------------------------------------------------- /example-setup/pki-input-ids/ca-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "signing": { 3 | "profiles": { 4 | "intermediate": { 5 | "expiry": "43830h", 6 | "usages": [ 7 | "signing", 8 | "cert sign", 9 | "crl sign", 10 | "ocsp signing", 11 | "digital signature", 12 | "client auth", 13 | "server auth" 14 | ], 15 | "ca_constraint": { 16 | "is_ca": true, 17 | "max_path_len": 0, 18 | "max_path_len_zero": true 19 | }, 20 | "ocsp_url": "127.0.0.1:8887" 21 | }, 22 | "user": { 23 | "key": { 24 | "algo": "ecdsa", 25 | "size": 256 26 | }, 27 | "expiry": "17532h", 28 | "usages": [ 29 | "signing", 30 | "digital signature", 31 | "content commitment", 32 | "code signing" 33 | ], 34 | "ocsp_url": "127.0.0.1:8888" 35 | }, 36 | "device": { 37 | "key": { 38 | "algo": "ecdsa", 39 | "size": 256 40 | }, 41 | "expiry": "26298h", 42 | "usages": [ 43 | "signing", 44 | "digital signature", 45 | "content commitment", 46 | "code signing" 47 | ], 48 | "ocsp_url": "127.0.0.1:8889" 49 | }, 50 | "tlscert": { 51 | "expiry": "876000h", 52 | "key": { 53 | "algo": "ecdsa", 54 | "size": 256 55 | }, 56 | "usages": [ 57 | "digital signature", 58 | "server auth" 59 | ] 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/glue.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package cgo 17 | 18 | /* 19 | #cgo CFLAGS: -I${SRCDIR} 20 | #cgo LDFLAGS: -lcrypto 21 | #include "MeasureBootPeCoff.h" 22 | */ 23 | import "C" 24 | import ( 25 | "fmt" 26 | "unsafe" 27 | 28 | "github.com/sirupsen/logrus" 29 | ) 30 | 31 | type HashAlg int 32 | 33 | const ( 34 | SHA256 HashAlg = iota 35 | SHA384 36 | UNKNOWN 37 | ) 38 | 39 | func MeasurePeImage(alg HashAlg, buf []byte) ([]byte, error) { 40 | 41 | l := logrus.GetLevel() 42 | if l == logrus.DebugLevel || l == logrus.TraceLevel { 43 | C.SetDebug() 44 | } 45 | 46 | var hash []byte 47 | 48 | switch alg { 49 | case SHA256: 50 | hash = make([]byte, 32) 51 | v := C.MeasurePeImageSha256( 52 | (*C.uint8_t)(unsafe.Pointer(&hash[0])), 53 | (*C.uint8_t)(unsafe.Pointer(&buf[0])), 54 | C.uint64_t(len(buf)), 55 | ) 56 | if v != 0x0 { 57 | return nil, fmt.Errorf("failed to measure PE image. Error Code: %x", uint64(v)) 58 | } 59 | case SHA384: 60 | hash = make([]byte, 48) 61 | v := C.MeasurePeImageSha384( 62 | (*C.uint8_t)(unsafe.Pointer(&hash[0])), 63 | (*C.uint8_t)(unsafe.Pointer(&buf[0])), 64 | C.uint64_t(len(buf)), 65 | ) 66 | if v != 0x0 { 67 | return nil, fmt.Errorf("failed to measure PE image. Error Code: %x", uint64(v)) 68 | } 69 | default: 70 | return nil, fmt.Errorf("unknown hash algorithm %v", alg) 71 | } 72 | 73 | return hash[:], nil 74 | } 75 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/Bra86.c: -------------------------------------------------------------------------------- 1 | /* Bra86.c -- Converter for x86 code (BCJ) 2 | 2017-04-03 : Igor Pavlov : Public domain */ 3 | 4 | #include "Precomp.h" 5 | 6 | #include "Bra.h" 7 | 8 | #define Test86MSByte(b) ((((b) + 1) & 0xFE) == 0) 9 | 10 | SizeT 11 | x86_Convert ( 12 | Byte *data, 13 | SizeT size, 14 | UInt32 ip, 15 | UInt32 *state, 16 | int encoding 17 | ) 18 | { 19 | SizeT pos = 0; 20 | UInt32 mask = *state & 7; 21 | 22 | if (size < 5) { 23 | return 0; 24 | } 25 | 26 | size -= 4; 27 | ip += 5; 28 | 29 | for ( ; ;) { 30 | Byte *p = data + pos; 31 | const Byte *limit = data + size; 32 | for ( ; p < limit; p++) { 33 | if ((*p & 0xFE) == 0xE8) { 34 | break; 35 | } 36 | } 37 | 38 | { 39 | SizeT d = (SizeT)(p - data - pos); 40 | pos = (SizeT)(p - data); 41 | if (p >= limit) { 42 | *state = (d > 2 ? 0 : mask >> (unsigned)d); 43 | return pos; 44 | } 45 | 46 | if (d > 2) { 47 | mask = 0; 48 | } else { 49 | mask >>= (unsigned)d; 50 | if ((mask != 0) && ((mask > 4) || (mask == 3) || Test86MSByte (p[(size_t)(mask >> 1) + 1]))) { 51 | mask = (mask >> 1) | 4; 52 | pos++; 53 | continue; 54 | } 55 | } 56 | } 57 | 58 | if (Test86MSByte (p[4])) { 59 | UInt32 v = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); 60 | UInt32 cur = ip + (UInt32)pos; 61 | pos += 5; 62 | if (encoding) { 63 | v += cur; 64 | } else { 65 | v -= cur; 66 | } 67 | 68 | if (mask != 0) { 69 | unsigned sh = (mask & 6) << 2; 70 | if (Test86MSByte ((Byte)(v >> sh))) { 71 | v ^= (((UInt32)0x100 << sh) - 1); 72 | if (encoding) { 73 | v += cur; 74 | } else { 75 | v -= cur; 76 | } 77 | } 78 | 79 | mask = 0; 80 | } 81 | 82 | p[1] = (Byte)v; 83 | p[2] = (Byte)(v >> 8); 84 | p[3] = (Byte)(v >> 16); 85 | p[4] = (Byte)(0 - ((v >> 24) & 1)); 86 | } else { 87 | mask = (mask >> 1) | 4; 88 | pos++; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /bin/setup-cmc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | print_usage() { 10 | echo "Usage: $(basename "$0") [-i] [-h] [-c] " 11 | echo " -h: print help" 12 | echo " -i: use ids pki instead of simple pki" 13 | echo " -c: use cbor instead of json for metadata" 14 | echo " driver: tpm, sgx, snp, tdx, tdx-azure, ec2, vm" 15 | } 16 | 17 | script_suffix="" 18 | serializer="json" 19 | 20 | while getopts 'i:h:c' flag; do 21 | case "${flag}" in 22 | i) 23 | script_suffix="-ids" 24 | shift 25 | ;; 26 | c) 27 | serializer="cbor" 28 | shift 29 | ;; 30 | h) 31 | print_usage 32 | exit 0 ;; 33 | *) print_usage 34 | exit 1 ;; 35 | esac 36 | done 37 | 38 | if [[ "$#" -lt 1 ]]; then 39 | print_usage 40 | exit 1 41 | fi 42 | 43 | driver="${1}" 44 | 45 | if [[ ! "${driver}" =~ ^(sgx|tpm|snp|tdx|tdx-azure|ec2|vm)$ ]]; then 46 | echo "Error: Invalid driver. Expected one of sgx, tpm, snp, tdx, tdx-azure, ec2, vm" 47 | exit 1 48 | fi 49 | 50 | data="${dir}/data" 51 | cmc="${dir}" 52 | 53 | if [[ ! -d "${cmc}" ]]; then 54 | echo "CMC directory ${cmc} does not exist. Did you clone the repository? Abort.." 55 | exit 1 56 | fi 57 | 58 | if [[ -d "${data}" ]]; then 59 | echo "Data directory ${data} does already exist. Please choose a new directory. Abort.." 60 | exit 1 61 | fi 62 | 63 | echo "Using CMC: ${cmc}" 64 | echo "Using ${data} as directory for local data" 65 | 66 | # Build CMC 67 | go build -C "${dir}/cmcctl" 68 | go build -C "${dir}/cmcd" 69 | go build -C "${dir}/provision/estserver" 70 | go build -C "${dir}/tools/metaconv" 71 | go build -C "${dir}/tools/metasign" 72 | 73 | # Create a folder for the cmc configuration and metadata 74 | mkdir -p "${data}" 75 | 76 | # Generate an example PKI 77 | "${dir}/bin/setup-pki${script_suffix}" 78 | 79 | # Generate example metadata for the current machine 80 | "${dir}/bin/generate-metadata-${driver}" 81 | 82 | echo "signing metadata with serializer ${serializer}" 83 | 84 | # Sign the metadata 85 | "${dir}/bin/sign-metadata${script_suffix}" "${serializer}" 86 | -------------------------------------------------------------------------------- /tools/azuretool/tdreport.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "bytes" 20 | "encoding/binary" 21 | "fmt" 22 | ) 23 | 24 | type TdReport struct { 25 | ReportMacStruct ReportMacStruct 26 | TeeTcbInfo TeeTcbInfo 27 | Reserved [17]byte 28 | TdInfo TdInfo 29 | } 30 | 31 | type ReportMacStruct struct { 32 | ReportType ReportType 33 | Reserved1 [12]byte 34 | CpuSvn [16]byte 35 | TeeTcbInfoHash [48]byte 36 | TeeInfoHash [48]byte 37 | ReportData [64]byte 38 | Reserved2 [32]byte 39 | Mac [32]byte 40 | } 41 | 42 | type TeeTcbInfo struct { 43 | Valid [8]byte 44 | TeeTcbSvn [16]byte 45 | MrSeam [48]byte 46 | MrSignerSeam [48]byte 47 | Attributes [8]byte 48 | TeeTcbSvn2 [16]byte 49 | Reserved [95]byte 50 | } 51 | 52 | type TdInfo struct { 53 | Attributes [8]byte 54 | Xfam [8]byte 55 | Mrtd [48]byte 56 | MrOwner [48]byte 57 | MrOwnerconfig [48]byte 58 | RtMr0 [48]byte 59 | RtMr1 [48]byte 60 | RtMr2 [48]byte 61 | RtMr3 [48]byte 62 | Reserved [64]byte 63 | } 64 | 65 | type ReportType struct { 66 | Type byte 67 | SubType byte 68 | Version byte 69 | Reserved byte 70 | } 71 | 72 | func DecodeTdReport(data []byte) (*TdReport, error) { 73 | 74 | var tdReport TdReport 75 | 76 | buf := bytes.NewBuffer(data) 77 | 78 | err := binary.Read(buf, binary.LittleEndian, &tdReport) 79 | if err != nil { 80 | return nil, fmt.Errorf("failed to decode TDREPORT: %w", err) 81 | } 82 | 83 | return &tdReport, nil 84 | } 85 | -------------------------------------------------------------------------------- /verifier/tdreport.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package verifier 17 | 18 | import ( 19 | "bytes" 20 | "encoding/binary" 21 | "fmt" 22 | ) 23 | 24 | type TdReport struct { 25 | ReportMacStruct ReportMacStruct 26 | TeeTcbInfo TeeTcbInfo 27 | Reserved [17]byte 28 | TdInfo TdInfo 29 | } 30 | 31 | type ReportMacStruct struct { 32 | ReportType TdReportType 33 | Reserved1 [12]byte 34 | CpuSvn [16]byte 35 | TeeTcbInfoHash [48]byte 36 | TeeInfoHash [48]byte 37 | ReportData [64]byte 38 | Reserved2 [32]byte 39 | Mac [32]byte 40 | } 41 | 42 | type TeeTcbInfo struct { 43 | Valid [8]byte 44 | TeeTcbSvn [16]byte 45 | MrSeam [48]byte 46 | MrSignerSeam [48]byte 47 | Attributes [8]byte 48 | TeeTcbSvn2 [16]byte 49 | Reserved [95]byte 50 | } 51 | 52 | type TdInfo struct { 53 | Attributes [8]byte 54 | Xfam [8]byte 55 | Mrtd [48]byte 56 | MrOwner [48]byte 57 | MrOwnerconfig [48]byte 58 | RtMr0 [48]byte 59 | RtMr1 [48]byte 60 | RtMr2 [48]byte 61 | RtMr3 [48]byte 62 | Reserved [64]byte 63 | } 64 | 65 | type TdReportType struct { 66 | Type byte 67 | SubType byte 68 | Version byte 69 | Reserved byte 70 | } 71 | 72 | func DecodeTdReport(data []byte) (*TdReport, error) { 73 | 74 | var tdReport TdReport 75 | 76 | buf := bytes.NewBuffer(data) 77 | 78 | err := binary.Read(buf, binary.LittleEndian, &tdReport) 79 | if err != nil { 80 | return nil, fmt.Errorf("failed to decode TDREPORT: %w", err) 81 | } 82 | 83 | return &tdReport, nil 84 | } 85 | -------------------------------------------------------------------------------- /attestedhttp/server.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package attestedhttp 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "net/http" 22 | 23 | ar "github.com/Fraunhofer-AISEC/cmc/attestationreport" 24 | atls "github.com/Fraunhofer-AISEC/cmc/attestedtls" 25 | "github.com/Fraunhofer-AISEC/cmc/cmc" 26 | ) 27 | 28 | // Wrapper for http.Server 29 | type Server struct { 30 | *http.Server 31 | 32 | // Additional aTLS parameters 33 | Attest atls.AttestSelect 34 | MutualTls bool 35 | CmcAddr string 36 | CmcApi atls.CmcApiSelect 37 | ApiSerializer ar.Serializer 38 | Cmc *cmc.Cmc 39 | CmcPolicies []byte 40 | ResultCb func(result *ar.VerificationResult) 41 | } 42 | 43 | func (s *Server) ListenAndServe() error { 44 | 45 | if s.Server.TLSConfig == nil { 46 | return errors.New("failed to listen: no TLS config provided") 47 | } 48 | 49 | // Listen: TLS connection 50 | ln, err := atls.Listen("tcp", s.Server.Addr, s.Server.TLSConfig, 51 | atls.WithCmcAddr(s.CmcAddr), 52 | atls.WithCmcPolicies(s.CmcPolicies), 53 | atls.WithCmcApi(s.CmcApi), 54 | atls.WithApiSerializer(s.ApiSerializer), 55 | atls.WithMtls(s.MutualTls), 56 | atls.WithAttest(s.Attest), 57 | atls.WithResultCb(s.ResultCb), 58 | atls.WithCmc(s.Cmc)) 59 | if err != nil { 60 | log.Fatalf("Failed to listen for connections: %v", err) 61 | } 62 | defer ln.Close() 63 | 64 | log.Infof("Serving HTTPS under %v", s.Server.Addr) 65 | 66 | err = s.Server.Serve(ln) 67 | if err != nil { 68 | return fmt.Errorf("failed to serve: %w", err) 69 | } 70 | 71 | log.Info("Finished serving HTTPS") 72 | 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /tools/measure-bundle/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "flag" 20 | "fmt" 21 | "strings" 22 | 23 | "github.com/sirupsen/logrus" 24 | "golang.org/x/exp/maps" 25 | ) 26 | 27 | type config struct { 28 | config string 29 | rootfs string 30 | args string 31 | logLevel logrus.Level 32 | } 33 | 34 | var ( 35 | logLevels = map[string]logrus.Level{ 36 | "panic": logrus.PanicLevel, 37 | "fatal": logrus.FatalLevel, 38 | "error": logrus.ErrorLevel, 39 | "warn": logrus.WarnLevel, 40 | "info": logrus.InfoLevel, 41 | "debug": logrus.DebugLevel, 42 | "trace": logrus.TraceLevel, 43 | } 44 | ) 45 | 46 | func getConfig() (*config, error) { 47 | 48 | configPath := flag.String("config", "", "OCI runtime bundle config path") 49 | rootfsPath := flag.String("rootfs", "", "OCI runtime bundle rootfs path") 50 | args := flag.String("args", "", "OCI runtime bundle additional args") 51 | logLevel := flag.String("log-level", "info", 52 | fmt.Sprintf("Possible logging: %v", strings.Join(maps.Keys(logLevels), ","))) 53 | flag.Parse() 54 | 55 | if *configPath == "" { 56 | flag.Usage() 57 | log.Fatal("OCI runtime bundle config path not provided") 58 | } 59 | if *rootfsPath == "" { 60 | flag.Usage() 61 | log.Fatal("OCI runtime bundle rootfs path not provided") 62 | } 63 | 64 | l, ok := logLevels[strings.ToLower(*logLevel)] 65 | if !ok { 66 | flag.Usage() 67 | log.Fatalf("LogLevel %v does not exist", *logLevel) 68 | } 69 | 70 | c := &config{ 71 | config: *configPath, 72 | rootfs: *rootfsPath, 73 | args: *args, 74 | logLevel: l, 75 | } 76 | 77 | return c, nil 78 | } 79 | -------------------------------------------------------------------------------- /bin/vm-retrieve-gpt: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | 4 | set -euo pipefail 5 | 6 | trap '[ $? -eq 0 ] && exit 0; printf "[\033[31;1mFAILED\033[0m] %s\n" "$0"' EXIT 7 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 8 | 9 | set -euo pipefail 10 | 11 | [[ $# -eq 0 ]] || { 12 | echo "Usage: $(basename $0)" 13 | exit 1 14 | } 15 | 16 | vm="vm-ubuntu" 17 | device="/dev/sda" 18 | target="${dir}/vm/artifacts/" 19 | user="root" 20 | 21 | echo "Fetching GPT header and table..." 22 | gpt_header_vm="/${user}/gpt_header" 23 | gpt_table_vm="/${user}/gpt_part_table" 24 | gpt_header="${target}/gpt_header" 25 | gpt_table="${target}/gpt_table" 26 | tmp_entry="${target}/tmp_entry" 27 | ev_efi_gpt_event="${target}/ev_efi_gpt_event" 28 | entry_size=128 29 | gpt_entries=128 30 | 31 | # Extract GPT header and partition entries from image 32 | vm-ssh dd if="${device}" of="${gpt_header_vm}" bs=1 skip=512 count=92 33 | vm-ssh dd if="${device}" of="${gpt_table_vm}" bs=512 skip=2 count=32 34 | 35 | vm-scp "${vm}:${gpt_header_vm}" "${gpt_header}" 36 | vm-scp "${vm}:${gpt_table_vm}" "${gpt_table}" 37 | 38 | # Add GPT header to reference measurement 39 | cat "${gpt_header}" > "${ev_efi_gpt_event}" 40 | 41 | # Get number of entries 42 | num_entries=0 43 | for i in $(seq 0 $((gpt_entries - 1))); do 44 | offset=$((i * entry_size)) 45 | # Extract entry 46 | dd if="${gpt_table}" bs=1 skip=${offset} count="${entry_size}" of="${tmp_entry}" status=none 47 | # Add if non-zero 48 | if ! cmp -s "${tmp_entry}" <(head -c ${entry_size} < /dev/zero); then 49 | let num_entries=num_entries+1 50 | fi 51 | done 52 | 53 | # Add number of entries 54 | echo "Adding ${num_entries} entries..." 55 | printf '%016x\n' "${num_entries}" | \ 56 | sed 's/../& /g' | \ 57 | awk '{for(i=8;i>=1;i--) printf "%s", $i}' | \ 58 | xxd -r -p >> "${ev_efi_gpt_event}" 59 | 60 | # Add non-zero GPT partition table entries to reference measurement 61 | for i in $(seq 0 $((gpt_entries - 1))); do 62 | offset=$((i * entry_size)) 63 | # Extract entry 64 | dd if="${gpt_table}" bs=1 skip=${offset} count="${entry_size}" of="${tmp_entry}" status=none 65 | # Add if non-zero 66 | if ! cmp -s "${tmp_entry}" <(head -c ${entry_size} < /dev/zero); then 67 | cat "${tmp_entry}" >> "${ev_efi_gpt_event}" 68 | fi 69 | done 70 | rm "${tmp_entry}" "${gpt_table}" "${gpt_header}" -------------------------------------------------------------------------------- /example-setup/cas/SectigoRSAOrganizationValidationSecureServerCA.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGGTCCBAGgAwIBAgIQE31TnKp8MamkM3AZaIR6jTANBgkqhkiG9w0BAQwFADCB 3 | iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl 4 | cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV 5 | BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx 6 | MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBlTELMAkGA1UEBhMCR0IxGzAZBgNV 7 | BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE 8 | ChMPU2VjdGlnbyBMaW1pdGVkMT0wOwYDVQQDEzRTZWN0aWdvIFJTQSBPcmdhbml6 9 | YXRpb24gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0B 10 | AQEFAAOCAQ8AMIIBCgKCAQEAnJMCRkVKUkiS/FeN+S3qU76zLNXYqKXsW2kDwB0Q 11 | 9lkz3v4HSKjojHpnSvH1jcM3ZtAykffEnQRgxLVK4oOLp64m1F06XvjRFnG7ir1x 12 | on3IzqJgJLBSoDpFUd54k2xiYPHkVpy3O/c8Vdjf1XoxfDV/ElFw4Sy+BKzL+k/h 13 | fGVqwECn2XylY4QZ4ffK76q06Fha2ZnjJt+OErK43DOyNtoUHZZYQkBuCyKFHFEi 14 | rsTIBkVtkuZntxkj5Ng2a4XQf8dS48+wdQHgibSov4o2TqPgbOuEQc6lL0giE5dQ 15 | YkUeCaXMn2xXcEAG2yDoG9bzk4unMp63RBUJ16/9fAEc2wIDAQABo4IBbjCCAWow 16 | HwYDVR0jBBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFBfZ1iUn 17 | Z/kxwklD2TA2RIxsqU/rMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/ 18 | AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYG 19 | BFUdIAAwCAYGZ4EMAQICMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNl 20 | cnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNy 21 | bDB2BggrBgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRy 22 | dXN0LmNvbS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZ 23 | aHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAThNA 24 | lsnD5m5bwOO69Bfhrgkfyb/LDCUW8nNTs3Yat6tIBtbNAHwgRUNFbBZaGxNh10m6 25 | pAKkrOjOzi3JKnSj3N6uq9BoNviRrzwB93fVC8+Xq+uH5xWo+jBaYXEgscBDxLmP 26 | bYox6xU2JPti1Qucj+lmveZhUZeTth2HvbC1bP6mESkGYTQxMD0gJ3NR0N6Fg9N3 27 | OSBGltqnxloWJ4Wyz04PToxcvr44APhL+XJ71PJ616IphdAEutNCLFGIUi7RPSRn 28 | R+xVzBv0yjTqJsHe3cQhifa6ezIejpZehEU4z4CqN2mLYBd0FUiRnG3wTqN3yhsc 29 | SPr5z0noX0+FCuKPkBurcEya67emP7SsXaRfz+bYipaQ908mgWB2XQ8kd5GzKjGf 30 | FlqyXYwcKapInI5v03hAcNt37N3j0VcFcC3mSZiIBYRiBXBWdoY5TtMibx3+bfEO 31 | s2LEPMvAhblhHrrhFYBZlAyuBbuMf1a+HNJav5fyakywxnB2sJCNwQs2uRHY1ihc 32 | 6k/+JLcYCpsM0MF8XPtpvcyiTcaQvKZN8rG61ppnW5YCUtCC+cQKXA0o4D/I+pWV 33 | idWkvklsQLI+qGu41SWyxP7x09fn1txDAXYw+zuLXfdKiXyaNb78yvBXAfCNP6CH 34 | MntHWpdLgtJmwsQt6j8k9Kf5qLnjatkYYaA7jBU= 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /attestationpolicies/duktape/policies.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #define _POSIX_C_SOURCE 200809L 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "duktape.h" 24 | 25 | // required because duktape does not provide I/O bindings 26 | static duk_ret_t native_print(duk_context *ctx) { 27 | printf("%s\n", duk_to_string(ctx, 0)); 28 | return 0; 29 | } 30 | 31 | char* Validate(uint8_t *ar, size_t ar_size, uint8_t *policies, size_t policies_size) { 32 | char ar_str[ar_size + 1]; 33 | memcpy(ar_str, ar, ar_size); 34 | ar_str[ar_size] = '\0'; 35 | 36 | char policies_str[policies_size + 1]; 37 | memcpy(policies_str, policies, policies_size); 38 | policies_str[policies_size] = '\0'; 39 | 40 | duk_context *ctx = duk_create_heap_default(); 41 | 42 | // Optional: provide print() in JS 43 | duk_push_c_function(ctx, native_print, 1); 44 | duk_put_global_string(ctx, "print"); 45 | 46 | // Push `json` variable for policy 47 | duk_push_global_object(ctx); 48 | duk_push_string(ctx, "json"); 49 | duk_push_string(ctx, ar_str); 50 | duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE); 51 | 52 | // Evaluate policies 53 | if (duk_peval_string(ctx, policies_str) != 0) { 54 | const char *err = duk_safe_to_string(ctx, -1); 55 | fprintf(stderr, "Policy error: %s\n", err); 56 | duk_destroy_heap(ctx); 57 | return strdup("false"); 58 | } 59 | 60 | // Convert result to string 61 | const char *result_str = duk_safe_to_string(ctx, -1); 62 | char *ret = strdup(result_str); 63 | 64 | duk_destroy_heap(ctx); 65 | return ret; 66 | } 67 | -------------------------------------------------------------------------------- /jsoncanonicalizer/es6numfmt.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2006-2019 WebPKI.org (http://webpki.org). 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | // This package converts numbers in IEEE-754 double precision into the 18 | // format specified for JSON in EcmaScript Version 6 and forward. 19 | // The core application for this is canonicalization: 20 | // https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-02 21 | 22 | package jsoncanonicalizer 23 | 24 | import ( 25 | "errors" 26 | "math" 27 | "strconv" 28 | "strings" 29 | ) 30 | 31 | const invalidPattern uint64 = 0x7ff0000000000000 32 | 33 | func NumberToJSON(ieeeF64 float64) (res string, err error) { 34 | ieeeU64 := math.Float64bits(ieeeF64) 35 | 36 | // Special case: NaN and Infinity are invalid in JSON 37 | if (ieeeU64 & invalidPattern) == invalidPattern { 38 | return "null", errors.New("Invalid JSON number: " + strconv.FormatUint(ieeeU64, 16)) 39 | } 40 | 41 | // Special case: eliminate "-0" as mandated by the ES6-JSON/JCS specifications 42 | if ieeeF64 == 0 { // Right, this line takes both -0 and 0 43 | return "0", nil 44 | } 45 | 46 | // Deal with the sign separately 47 | var sign string = "" 48 | if ieeeF64 < 0 { 49 | ieeeF64 = -ieeeF64 50 | sign = "-" 51 | } 52 | 53 | // ES6 has a unique "g" format 54 | var format byte = 'e' 55 | if ieeeF64 < 1e+21 && ieeeF64 >= 1e-6 { 56 | format = 'f' 57 | } 58 | 59 | // The following should do the trick: 60 | es6Formatted := strconv.FormatFloat(ieeeF64, format, -1, 64) 61 | 62 | // Minor cleanup 63 | exponent := strings.IndexByte(es6Formatted, 'e') 64 | if exponent > 0 { 65 | // Go outputs "1e+09" which must be rewritten as "1e+9" 66 | if es6Formatted[exponent+2] == '0' { 67 | es6Formatted = es6Formatted[:exponent+2] + es6Formatted[exponent+3:] 68 | } 69 | } 70 | return sign + es6Formatted, nil 71 | } 72 | -------------------------------------------------------------------------------- /measure/rootfs.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package measure 17 | 18 | import ( 19 | "archive/tar" 20 | "bytes" 21 | "crypto/sha256" 22 | "fmt" 23 | "io" 24 | "os" 25 | "path/filepath" 26 | "time" 27 | ) 28 | 29 | func GetRootfsMeasurement(rootfsPath string) ([]byte, error) { 30 | var buf bytes.Buffer 31 | tw := tar.NewWriter(&buf) 32 | 33 | rootfsPath = filepath.Clean(rootfsPath) + string(os.PathSeparator) 34 | 35 | err := filepath.Walk(rootfsPath, func(file string, fi os.FileInfo, err error) error { 36 | if err != nil { 37 | return err 38 | } 39 | 40 | var link string 41 | if fi.Mode()&os.ModeSymlink != 0 { 42 | link, err = os.Readlink(file) 43 | if err != nil { 44 | return err 45 | } 46 | } 47 | 48 | header, err := tar.FileInfoHeader(fi, link) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | header.ModTime = time.Unix(0, 0) 54 | 55 | relativePath, err := filepath.Rel(rootfsPath, file) 56 | if err != nil { 57 | return err 58 | } 59 | header.Name = relativePath 60 | 61 | if err := tw.WriteHeader(header); err != nil { 62 | return err 63 | } 64 | 65 | if !fi.IsDir() && fi.Mode()&os.ModeSymlink == 0 { 66 | f, err := os.Open(file) 67 | if err != nil { 68 | return err 69 | } 70 | defer f.Close() 71 | 72 | if _, err := io.Copy(tw, f); err != nil { 73 | return err 74 | } 75 | } 76 | 77 | return nil 78 | }) 79 | if err != nil { 80 | return nil, fmt.Errorf("failed to walk through rootfs: %w", err) 81 | } 82 | 83 | if err := tw.Close(); err != nil { 84 | return nil, fmt.Errorf("failed to close tar writer: %w", err) 85 | } 86 | 87 | hasher := sha256.New() 88 | hasher.Write(buf.Bytes()) 89 | hash := hasher.Sum(nil) 90 | 91 | return hash, nil 92 | } 93 | -------------------------------------------------------------------------------- /internal/hash.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package internal 17 | 18 | import ( 19 | "crypto" 20 | "crypto/sha256" 21 | "crypto/sha512" 22 | "fmt" 23 | "hash" 24 | ) 25 | 26 | // ExtendSha256 performs the extend operation Digest = HASH(Digest | Data) using the 27 | // SHA256 hashing algorithm, as performed e.g. by Trusted Platform Modules (TPMs) 28 | func ExtendSha256(hash []byte, data []byte) []byte { 29 | concat := append(hash, data...) 30 | h := sha256.Sum256(concat) 31 | ret := make([]byte, 32) 32 | copy(ret, h[:]) 33 | return ret 34 | } 35 | 36 | // ExtendSha384 performs the extend operation Digest = HASH(Digest | Data) using the 37 | // SHA384 hashing algorithm, as performed e.g. by Trusted Platform Modules (TPMs) 38 | func ExtendSha384(hash []byte, data []byte) []byte { 39 | concat := append(hash, data...) 40 | h := sha512.Sum384(concat) 41 | ret := make([]byte, 48) 42 | copy(ret, h[:]) 43 | return ret 44 | } 45 | 46 | func Extend(alg crypto.Hash, digest, data []byte) ([]byte, error) { 47 | if !alg.Available() { 48 | return nil, fmt.Errorf("hash algorithm not available: %v", alg) 49 | } 50 | 51 | var h hash.Hash = alg.New() 52 | if _, err := h.Write(digest); err != nil { 53 | return nil, fmt.Errorf("hashing failed: %w", err) 54 | } 55 | if _, err := h.Write(data); err != nil { 56 | return nil, fmt.Errorf("hashing failed: %w", err) 57 | } 58 | return h.Sum(nil), nil 59 | } 60 | 61 | func Hash(alg crypto.Hash, data []byte) ([]byte, error) { 62 | if !alg.Available() { 63 | return nil, fmt.Errorf("hash algorithm not available: %v", alg) 64 | } 65 | 66 | var h hash.Hash = alg.New() 67 | if _, err := h.Write(data); err != nil { 68 | return nil, fmt.Errorf("hashing failed: %w", err) 69 | } 70 | return h.Sum(nil), nil 71 | } 72 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/Bra.h: -------------------------------------------------------------------------------- 1 | /* Bra.h -- Branch converters for executables 2 | 2013-01-18 : Igor Pavlov : Public domain */ 3 | 4 | #ifndef __BRA_H 5 | #define __BRA_H 6 | 7 | #include "7zTypes.h" 8 | 9 | EXTERN_C_BEGIN 10 | 11 | /* 12 | These functions convert relative addresses to absolute addresses 13 | in CALL instructions to increase the compression ratio. 14 | 15 | In: 16 | data - data buffer 17 | size - size of data 18 | ip - current virtual Instruction Pinter (IP) value 19 | state - state variable for x86 converter 20 | encoding - 0 (for decoding), 1 (for encoding) 21 | 22 | Out: 23 | state - state variable for x86 converter 24 | 25 | Returns: 26 | The number of processed bytes. If you call these functions with multiple calls, 27 | you must start next call with first byte after block of processed bytes. 28 | 29 | Type Endian Alignment LookAhead 30 | 31 | x86 little 1 4 32 | ARMT little 2 2 33 | ARM little 4 0 34 | PPC big 4 0 35 | SPARC big 4 0 36 | IA64 little 16 0 37 | 38 | size must be >= Alignment + LookAhead, if it's not last block. 39 | If (size < Alignment + LookAhead), converter returns 0. 40 | 41 | Example: 42 | 43 | UInt32 ip = 0; 44 | for () 45 | { 46 | ; size must be >= Alignment + LookAhead, if it's not last block 47 | SizeT processed = Convert(data, size, ip, 1); 48 | data += processed; 49 | size -= processed; 50 | ip += processed; 51 | } 52 | */ 53 | 54 | #define x86_Convert_Init(state) { state = 0; } 55 | SizeT 56 | x86_Convert ( 57 | Byte *data, 58 | SizeT size, 59 | UInt32 ip, 60 | UInt32 *state, 61 | int encoding 62 | ); 63 | 64 | SizeT 65 | ARM_Convert ( 66 | Byte *data, 67 | SizeT size, 68 | UInt32 ip, 69 | int encoding 70 | ); 71 | 72 | SizeT 73 | ARMT_Convert ( 74 | Byte *data, 75 | SizeT size, 76 | UInt32 ip, 77 | int encoding 78 | ); 79 | 80 | SizeT 81 | PPC_Convert ( 82 | Byte *data, 83 | SizeT size, 84 | UInt32 ip, 85 | int encoding 86 | ); 87 | 88 | SizeT 89 | SPARC_Convert ( 90 | Byte *data, 91 | SizeT size, 92 | UInt32 ip, 93 | int encoding 94 | ); 95 | 96 | SizeT 97 | IA64_Convert ( 98 | Byte *data, 99 | SizeT size, 100 | UInt32 ip, 101 | int encoding 102 | ); 103 | 104 | EXTERN_C_END 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /attestationreport/serializer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package attestationreport 17 | 18 | import ( 19 | "encoding/json" 20 | "fmt" 21 | 22 | "github.com/fxamacker/cbor/v2" 23 | "github.com/plgd-dev/go-coap/v3/message" 24 | ) 25 | 26 | // SignConfig allows to specify options for signing with the specified serializer 27 | type SignConfig struct { 28 | UseAk bool // Use the AK instead of the IK for signing 29 | } 30 | 31 | // Verifier can either be a list of trusted CA certificates, or a trusted public key, 32 | // or nil. In this case, the system cert store is used 33 | type Verifier interface{} 34 | 35 | // Serializer is a generic interface providing methods for data serialization and 36 | // de-serialization. This enables to generate and verify attestation reports in 37 | // different formats, such as JSON/JWS or CBOR/COSE 38 | type Serializer interface { 39 | GetPayload(raw []byte) ([]byte, error) 40 | Marshal(v any) ([]byte, error) 41 | Unmarshal(data []byte, v any) error 42 | Sign(data []byte, driver Driver, sel KeySelection) ([]byte, error) 43 | Verify(data []byte, verifier Verifier) (MetadataResult, []byte, bool) 44 | String() string 45 | } 46 | 47 | func DetectSerialization(payload []byte) (Serializer, error) { 48 | if json.Valid(payload) { 49 | return JsonSerializer{}, nil 50 | } else if err := cbor.Wellformed(payload); err == nil { 51 | return CborSerializer{}, nil 52 | } else { 53 | return nil, fmt.Errorf("failed to detect serialization") 54 | } 55 | } 56 | 57 | // GetMediaType returns the media type that corresponds to the serializer 58 | func GetMediaType(s Serializer) message.MediaType { 59 | switch s.(type) { 60 | case JsonSerializer: 61 | return message.AppJSON 62 | case CborSerializer: 63 | return message.AppCBOR 64 | default: 65 | log.Fatalf("internal error: unknown serializer type %T", s) 66 | } 67 | // Will not be reached, required to vaoid compiler error 68 | return message.TextPlain 69 | } 70 | -------------------------------------------------------------------------------- /tools/mrtool/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "context" 20 | "os" 21 | 22 | "github.com/sirupsen/logrus" 23 | "github.com/urfave/cli/v3" 24 | 25 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/global" 26 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/parseima" 27 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/parsetdx" 28 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/parsetpm" 29 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/precomputeima" 30 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/precomputesnp" 31 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/precomputetdx" 32 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/precomputetpm" 33 | ) 34 | 35 | var ( 36 | log = logrus.WithField("service", "mrtool") 37 | ) 38 | 39 | func main() { 40 | cmd := &cli.Command{ 41 | Name: "mrtool", 42 | Usage: "A tool to precompute and parse measurement registers (TPM PCRs, Intel TDX MRTD/RTMRs, AMD SEV-SNP MR) " + 43 | "to be used as reference values for attesting (confidential) virtual machines", 44 | Flags: global.Flags, 45 | Commands: []*cli.Command{ 46 | { 47 | Name: "parse", 48 | Usage: "parse measurements", 49 | Commands: []*cli.Command{ 50 | parsetpm.Command, 51 | parsetdx.Command, 52 | parseima.Command, 53 | }, 54 | Action: func(ctx context.Context, c *cli.Command) error { 55 | return cli.ShowSubcommandHelp(c) 56 | }, 57 | }, 58 | { 59 | Name: "precompute", 60 | Usage: "precompute measurements", 61 | Commands: []*cli.Command{ 62 | precomputetpm.Command, 63 | precomputesnp.Command, 64 | precomputetdx.Command, 65 | precomputeima.Command, 66 | }, 67 | Action: func(ctx context.Context, c *cli.Command) error { 68 | return cli.ShowSubcommandHelp(c) 69 | }, 70 | }, 71 | }, 72 | } 73 | 74 | err := cmd.Run(context.Background(), os.Args) 75 | if err != nil { 76 | log.Fatal(err) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tools/metasign/json.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "crypto" 20 | "crypto/ecdsa" 21 | "crypto/ed25519" 22 | "crypto/x509" 23 | "encoding/base64" 24 | "fmt" 25 | 26 | "github.com/go-jose/go-jose/v4" 27 | log "github.com/sirupsen/logrus" 28 | ) 29 | 30 | type JsonSerializer struct{} 31 | 32 | func (s JsonSerializer) Sign(data []byte, keys []crypto.PrivateKey, x5cs [][]*x509.Certificate) ([]byte, error) { 33 | 34 | if len(keys) != len(x5cs) { 35 | return nil, fmt.Errorf("length of keys (%v) not equal to length of certificate chains (%v)", keys, x5cs) 36 | } 37 | 38 | var sig *jose.JSONWebSignature 39 | for i := range keys { 40 | 41 | var alg jose.SignatureAlgorithm 42 | switch keys[i].(type) { 43 | case *ecdsa.PrivateKey: 44 | log.Trace("Key type ECDSA") 45 | alg = jose.SignatureAlgorithm("ES256") 46 | case ed25519.PrivateKey: 47 | log.Trace("Key type EdDSA") 48 | alg = jose.SignatureAlgorithm("EdDSA") 49 | default: 50 | return nil, fmt.Errorf("key type %v not supported", keys[i]) 51 | } 52 | log.Tracef("Using signature algorithm: %v", alg) 53 | 54 | var opts jose.SignerOptions 55 | key := jose.SigningKey{Algorithm: alg, Key: keys[i]} 56 | certchain := make([]string, 0) 57 | for _, cert := range x5cs[i] { 58 | certchain = append(certchain, base64.StdEncoding.EncodeToString(cert.Raw)) 59 | } 60 | signer, err := jose.NewSigner(key, opts.WithHeader("x5c", certchain)) 61 | if err != nil { 62 | return nil, fmt.Errorf("failed to create signer: %v", err) 63 | } 64 | if i == 0 { 65 | sig, err = signer.Sign(data) 66 | if err != nil { 67 | return nil, fmt.Errorf("failed to create signature: %v", err) 68 | } 69 | } else { 70 | s, err := signer.Sign(data) 71 | if err != nil { 72 | return nil, fmt.Errorf("failed to create signature: %v", err) 73 | } 74 | sig.Signatures = append(sig.Signatures, s.Signatures[0]) 75 | } 76 | } 77 | 78 | output := sig.FullSerialize() 79 | return []byte(output), nil 80 | } 81 | -------------------------------------------------------------------------------- /attestedtls/backend.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package attestedtls 17 | 18 | import ( 19 | "encoding/binary" 20 | "errors" 21 | "fmt" 22 | "io" 23 | "net" 24 | ) 25 | 26 | // Writes byte array to provided channel by first sending length information, then data. 27 | // Used for transmitting the attestation reports between peers 28 | func Write(msg []byte, c net.Conn) error { 29 | 30 | length := len(msg) 31 | lenbuf := make([]byte, 4) 32 | binary.BigEndian.PutUint32(lenbuf, uint32(length)) 33 | 34 | n, err := c.Write(lenbuf) 35 | if err != nil { 36 | return fmt.Errorf("failed to write length to %v: %w", c.RemoteAddr().String(), err) 37 | } 38 | if n != len(lenbuf) { 39 | return fmt.Errorf("could only send %v of %v bytes to %v", n, len(lenbuf), 40 | c.RemoteAddr().String()) 41 | } 42 | 43 | n, err = c.Write(msg) 44 | if err != nil { 45 | return fmt.Errorf("failed to write payload to %v: %w", c.RemoteAddr().String(), err) 46 | } 47 | if n != len(msg) { 48 | return fmt.Errorf("could only send %v of %v bytes to %v", n, len(msg), 49 | c.RemoteAddr().String()) 50 | } 51 | 52 | return err 53 | } 54 | 55 | // Receives byte array from provided channel by first receiving length information, then data. 56 | // Used for transmitting the attestation reports between peers 57 | func Read(c net.Conn) ([]byte, error) { 58 | 59 | lenbuf := make([]byte, 4) 60 | _, err := c.Read(lenbuf) 61 | 62 | if err != nil { 63 | return nil, fmt.Errorf("failed to receive message: no length: %v", err) 64 | } 65 | 66 | len := int(binary.BigEndian.Uint32(lenbuf)) // Max size of 4GB 67 | log.Tracef("Receiving TCP Message length: %v", len) 68 | 69 | if len == 0 { 70 | return nil, errors.New("message length is zero") 71 | } 72 | 73 | buf := make([]byte, len) 74 | n, err := io.ReadFull(c, buf) 75 | if err != nil { 76 | return nil, fmt.Errorf("failed read full message: %w", err) 77 | } 78 | if n != len { 79 | return nil, fmt.Errorf("could only read %v of %v bytes", n, len) 80 | } 81 | 82 | return buf, nil 83 | } 84 | -------------------------------------------------------------------------------- /tools/hextobin/main.go: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the License); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an AS IS BASIS, WITHOUT 13 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package main 18 | 19 | import ( 20 | "encoding/hex" 21 | "flag" 22 | "fmt" 23 | "os" 24 | "strings" 25 | "unicode" 26 | ) 27 | 28 | func main() { 29 | inputFile := flag.String("in", "", "hex input file") 30 | outputFile := flag.String("out", "", "binary output file") 31 | startDelim := flag.String("start", "", "start delimiter") 32 | endDelim := flag.String("end", "", "end delimiter") 33 | flag.Parse() 34 | 35 | if *inputFile == "" { 36 | fmt.Println("Input file not specified") 37 | return 38 | } 39 | if *outputFile == "" { 40 | fmt.Println("Output file not specified") 41 | return 42 | } 43 | if *startDelim != "" && *endDelim == "" { 44 | fmt.Println("Start delimiter, bot no end delimiter specified") 45 | return 46 | } 47 | if *startDelim == "" && *endDelim != "" { 48 | fmt.Println("End delimiter, but no start delimiter specified") 49 | return 50 | } 51 | 52 | in, err := os.ReadFile(*inputFile) 53 | if err != nil { 54 | fmt.Printf("Failed to read input file %v: %v\n", *inputFile, err) 55 | return 56 | } 57 | 58 | hexStr := strings.Map(func(r rune) rune { 59 | if unicode.IsGraphic(r) { 60 | return r 61 | } 62 | return -1 63 | }, string(in)) 64 | 65 | if *startDelim != "" { 66 | 67 | fmt.Printf("Extracting string between '%v' and '%v'\n", *startDelim, *endDelim) 68 | 69 | hexStr = Split(hexStr, *startDelim, *endDelim) 70 | } 71 | 72 | bin, err := hex.DecodeString(hexStr) 73 | if err != nil { 74 | fmt.Printf("Failed to decode string: %v\n", err) 75 | } 76 | 77 | err = os.WriteFile(*outputFile, bin, 0644) 78 | if err != nil { 79 | fmt.Printf("Failed to write output file %v: %v\n", outputFile, err) 80 | return 81 | } 82 | } 83 | 84 | func Split(str, before, after string) string { 85 | a := strings.SplitAfterN(str, before, 2) 86 | b := strings.SplitAfterN(a[len(a)-1], after, 2) 87 | if len(b) == 1 { 88 | return b[0] 89 | } 90 | return b[0][0 : len(b[0])-len(after)] 91 | } 92 | -------------------------------------------------------------------------------- /example-setup/docker/tdx-pccs.dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Intel Corporation 2 | 3 | # Declare nodejs version you want to use 4 | ARG NODE_VERSION=20.11.1 5 | 6 | # Use multi-stage builds to reduce final image size 7 | FROM docker.io/library/debian AS builder 8 | 9 | # Define arguments used across multiple stages 10 | ARG DCAP_VERSION=DCAP_1.21 11 | ARG NODE_VERSION 12 | 13 | # update and install packages, nodejs 14 | RUN DEBIAN_FRONTEND=noninteractive \ 15 | apt-get update -yq \ 16 | && apt-get upgrade -yq \ 17 | && apt-get install -yq --no-install-recommends \ 18 | build-essential \ 19 | ca-certificates \ 20 | curl \ 21 | gnupg \ 22 | git \ 23 | zip \ 24 | python3 \ 25 | && apt-get clean \ 26 | && rm -rf /var/lib/apt/lists/* 27 | 28 | # Install nvm (Node Version Manager) 29 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash 30 | 31 | # Set NVM_DIR so we can use it in subsequent commands 32 | ENV NVM_DIR /root/.nvm 33 | 34 | # Install specific version of Node using nvm 35 | # Source nvm in each RUN command to ensure it's available 36 | RUN . "$NVM_DIR/nvm.sh" && nvm install $NODE_VERSION && nvm use $NODE_VERSION 37 | 38 | # Set PATH to include the node and npm binaries 39 | ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH 40 | 41 | # Clone the specific branch or tag 42 | RUN git clone --recurse-submodules https://github.com/intel/SGXDataCenterAttestationPrimitives.git -b ${DCAP_VERSION} --depth 1 43 | 44 | # Build libPCKCertSelection library 45 | WORKDIR /SGXDataCenterAttestationPrimitives/tools/PCKCertSelection/ 46 | RUN make \ 47 | && mkdir -p ../../QuoteGeneration/pccs/lib \ 48 | && cp ./out/libPCKCertSelection.so ../../QuoteGeneration/pccs/lib/ \ 49 | && make clean 50 | 51 | # Build PCCS 52 | WORKDIR /SGXDataCenterAttestationPrimitives/QuoteGeneration/pccs/ 53 | RUN npm config set proxy $http_proxy \ 54 | && npm config set https-proxy $https_proxy \ 55 | && npm config set engine-strict true \ 56 | && npm install 57 | 58 | # Start final image build 59 | FROM docker.io/library/debian:12-slim 60 | 61 | ARG NODE_VERSION 62 | 63 | # Create user and group before copying files 64 | ARG USER=pccs 65 | RUN useradd -M -U -r ${USER} -s /bin/false 66 | 67 | # Copy only necessary files from builder stage 68 | COPY --from=builder /root/.nvm/versions/node/v$NODE_VERSION/bin/node /usr/bin/node 69 | COPY --from=builder --chown=${USER}:${USER} /SGXDataCenterAttestationPrimitives/QuoteGeneration/pccs/ /opt/intel/pccs/ 70 | 71 | # Set the working directory and switch user 72 | WORKDIR /opt/intel/pccs/ 73 | USER ${USER} 74 | 75 | # Define entrypoint 76 | ENTRYPOINT ["/usr/bin/node", "pccs_server.js"] 77 | 78 | -------------------------------------------------------------------------------- /tools/metasign/cbor.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "crypto" 20 | "crypto/ecdsa" 21 | "crypto/rand" 22 | "crypto/x509" 23 | "fmt" 24 | 25 | "github.com/veraison/go-cose" 26 | ) 27 | 28 | type CborSerializer struct{} 29 | 30 | func (s CborSerializer) Sign(data []byte, keys []crypto.PrivateKey, x5cs [][]*x509.Certificate) ([]byte, error) { 31 | 32 | if len(keys) != len(x5cs) { 33 | return nil, fmt.Errorf("length of keys (%v) not equal to length of certificate chains (%v)", keys, x5cs) 34 | } 35 | 36 | // TODO cryptographic agility 37 | alg := cose.AlgorithmES256 38 | 39 | // create message to be signed 40 | msg := cose.NewSignMessage() 41 | msg.Payload = data 42 | 43 | signers := make([]cose.Signer, 0) 44 | for i := range keys { 45 | // create signer TODO cryptographic agility 46 | signer, err := cose.NewSigner(alg, keys[i].(*ecdsa.PrivateKey)) 47 | if err != nil { 48 | return nil, fmt.Errorf("failed to create signer: %v", err) 49 | } 50 | signers = append(signers, signer) 51 | 52 | // Get certificate chain 53 | certchain := make([][]byte, 0) 54 | for _, cert := range x5cs[i] { 55 | certchain = append(certchain, cert.Raw) 56 | } 57 | 58 | // create a signature holder 59 | sigHolder := cose.NewSignature() 60 | sigHolder.Headers.Protected.SetAlgorithm(alg) 61 | // https://datatracker.ietf.org/doc/draft-ietf-cose-x509/08/ section 2 62 | // If multiple certificates are conveyed, a CBOR array of byte strings is used, 63 | // with each certificate being in its own byte string (DER Encoded) 64 | sigHolder.Headers.Unprotected[cose.HeaderLabelX5Chain] = certchain 65 | 66 | msg.Signatures = append(msg.Signatures, sigHolder) 67 | } 68 | 69 | err := msg.Sign(rand.Reader, nil, signers...) 70 | if err != nil { 71 | return nil, fmt.Errorf("failed to sign cbor object: %v", err) 72 | } 73 | 74 | coseRaw, err := msg.MarshalCBOR() 75 | if err != nil { 76 | return nil, fmt.Errorf("failed to marshal cbor object: %v", err) 77 | } 78 | 79 | return coseRaw, nil 80 | } 81 | -------------------------------------------------------------------------------- /provision/tpm_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package provision 17 | 18 | import ( 19 | "testing" 20 | ) 21 | 22 | func Test_parseIntelEkCert(t *testing.T) { 23 | type args struct { 24 | data []byte 25 | } 26 | tests := []struct { 27 | name string 28 | args args 29 | wantErr bool 30 | }{ 31 | {"Success", args{data}, false}, 32 | } 33 | for _, tt := range tests { 34 | t.Run(tt.name, func(t *testing.T) { 35 | _, err := parseIntelEkCert(tt.args.data) 36 | if (err != nil) != tt.wantErr { 37 | t.Errorf("parseIntelEkCert() error = %v, wantErr %v", err, tt.wantErr) 38 | return 39 | } 40 | }) 41 | } 42 | } 43 | 44 | var ( 45 | data = []byte(`{"pubhash":"J6GJXWBKxtcujC63BintQ7CO0Xrd52jOyJgOYWCiDeU%3D","certificate":"MIID_jCCA6OgAwIBAgIURBxd_Z9W-PRYdfqWflLmgBIG0QwwCgYIKoZIzj0EAwIwgZoxCzAJBgNVBAYMAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTQwMgYDVQQLDCtUUE0gRUsgaW50ZXJtZWRpYXRlIGZvciBHTEtfRVBJRF9QUk9EIHBpZDo3MRYwFAYDVQQDDA13d3cuaW50ZWwuY29tMB4XDTE5MDMxNDAwMDAwMFoXDTQ5MTIzMTIzNTk1OVowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCMm9yLNegGlE6BftbFFJsbjGDS8yGhnRrm1FH2yjinNSl0xVZ8YjkSdgwUgJBjxjKZ7Jv_1jZP_3XvvgJ97WhmjggJeMIICWjAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEB_wQEAwIFIDAQBgNVHSUECTAHBgVngQUIATAkBgNVHQkBAQAEGjAYMBYGBWeBBQIQMQ0wCwwDMi4wAgEAAgFnMFAGA1UdEQEB_wRGMESkQjBAMRYwFAYFZ4EFAgEMC2lkOjQ5NEU1NDQzMQ4wDAYFZ4EFAgIMA0dMSzEWMBQGBWeBBQIDDAtpZDowMDAyMDAwMDAfBgNVHSMEGDAWgBRRxzy838mQqmd0F81feLo31KjXMTBfBgNVHR8EWDBWMFSgUqBQhk5odHRwczovL3RydXN0ZWRzZXJ2aWNlcy5pbnRlbC5jb20vY29udGVudC9DUkwvZWtjZXJ0L0dMS0VQSURQUk9EX0VLX0RldmljZS5jcmwwdwYIKwYBBQUHAQEEazBpMGcGCCsGAQUFBzAChltodHRwczovL3RydXN0ZWRzZXJ2aWNlcy5pbnRlbC5jb20vY29udGVudC9DUkwvZWtjZXJ0L0dMS0VQSURQUk9EX0VLX1BsYXRmb3JtX1B1YmxpY19LZXkuY2VyMIGxBgNVHSAEgakwgaYwgaMGCiqGSIb4TQEFAgEwgZQwWgYIKwYBBQUHAgEWTmh0dHBzOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50L0NSTC9la2NlcnQvRUtjZXJ0UG9saWN5U3RhdGVtZW50LnBkZjA2BggrBgEFBQcCAjAqDChUQ1BBIFRydXN0ZWQgUGxhdGZvcm0gTW9kdWxlIEVuZG9yc2VtZW50MAoGCCqGSM49BAMCA0kAMEYCIQDCh21dXWaB2SyoLPmQsm3b2_RyrOKV2_v9SCHSp4auNgIhAI117kgiqfBgkaueqPZ28_XsbsjYkU3_OGRNOfJOv3cM"}`) 46 | ) 47 | -------------------------------------------------------------------------------- /tools/mrtool/cgo/LICENSE: -------------------------------------------------------------------------------- 1 | SPDX-License-Identifier: BSD-2-Clause-Patent 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | Subject to the terms and conditions of this license, each copyright holder 14 | and contributor hereby grants to those receiving rights under this license 15 | a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable 16 | (except for failure to satisfy the conditions of this license) patent 17 | license to make, have made, use, offer to sell, sell, import, and otherwise 18 | transfer this software, where such license applies only to those patent 19 | claims, already acquired or hereafter acquired, licensable by such copyright 20 | holder or contributor that are necessarily infringed by: 21 | 22 | (a) their Contribution(s) (the licensed copyrights of copyright holders and 23 | non-copyrightable additions of contributors, in source or binary form) 24 | alone; or 25 | 26 | (b) combination of their Contribution(s) with the work of authorship to 27 | which such Contribution(s) was added by such copyright holder or 28 | contributor, if, at the time the Contribution is added, such addition 29 | causes such combination to be necessarily infringed. The patent license 30 | shall not apply to any other combinations which include the 31 | Contribution. 32 | 33 | Except as expressly stated above, no rights or licenses from any copyright 34 | holder or contributor is granted under this license, whether expressly, by 35 | implication, estoppel or otherwise. 36 | 37 | DISCLAIMER 38 | 39 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 40 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 42 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 43 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 44 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 45 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 46 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 48 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 49 | POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /doc/setup-tdx.md: -------------------------------------------------------------------------------- 1 | # Intel TDX Setup 2 | 3 | Describes the setup to run the CMC within Intel TDX Confidential VMs, also calles Trusted Domains (TDs) 4 | 5 | All commands can be run in docker with `cmc-docker` prepended (e.g. `cmc-docker setup-cmc tdx` 6 | instead of `setup-cmc tdx`). This omits installing prerequisites. 7 | 8 | ## Prerequisites 9 | 10 | Install the measured-boot-tools as described in [prerequisites](./setup.md#prerequisites) 11 | 12 | ## TDX Setup 13 | 14 | Creates the PKI and metadata for running the CMC within TDX Confidential VMs (also called 15 | Trusted Domains (TDs)): 16 | ```sh 17 | source env.bash 18 | cmc-docker setup-cmc tdx 19 | ``` 20 | 21 | This script calls [generate-rtm-manifest-tdx](../bin/generate-rtm-manifest-tdx) and 22 | [generate-os-manifest-tdx](../bin/generate-os-manifest-tdx). In both scripts, the 23 | path to all artifacts required for precomputing the measurement registers must be set 24 | (see [Intel TDX Manual Metadata Generation](./setup-tdx.md#intel-tdx-manual-metadata-generation)) 25 | for more information. 26 | 27 | ## TDX Build 28 | 29 | Everything can be built and installed according to the 30 | [Build Documentation](./build-and-install.md). 31 | 32 | ## TDX services 33 | 34 | Creating TDX quotes requires the Intel TDX Quote Generation Service (QGS) and the Intel 35 | TDX Provisioning Certification Caching Service (PCCS), which we provide as docker containers: 36 | ```sh 37 | run-tdx-pccs 38 | run-tdx-qgs 39 | ``` 40 | 41 | ## TDX Run 42 | 43 | the `cmcd` and the `cmcctl` including their [configuration files](./run.md) must be installed 44 | into the CVM image of your choice. Then the CMC can be run as described in 45 | [Running the CMC](./run.md). Potentially you also want to create e.g. systemd services 46 | to automatically run the components. 47 | 48 | 49 | --- 50 | 51 | 52 | ## Intel TDX Manual Metadata Generation 53 | 54 | The reference values for Intel TDX consist of a fingerprint of the Intel Root CA certificate, 55 | several measurement registers and CVM attributes. 56 | 57 | Precomputing the measurement registers requires a reproducibly built OVMF, kernel, the kernel 58 | cmdline and configuration parameters as input. 59 | 60 | For a complete setup including precomputing all measurement registers, see 61 | [generate-rtm-manifest-tdx](../bin/generate-rtm-manifest-sgx) and 62 | [generate-os-manifest-tdx](../bin/generate-os-manifest-tdx). 63 | 64 | ### Further documentation 65 | 66 | Building and installing the individual components with various flags is described in the 67 | [Build](./build-and-install.md) documentation. A more detailed description on how to configure and 68 | run the components is described in the [Run](./run.md) documentation. For building own applications 69 | using the *cmcd*, refer to the [Developer Documentation](./dev.md). -------------------------------------------------------------------------------- /example-setup/policies/policies-ids.js: -------------------------------------------------------------------------------- 1 | // Parse the verification result 2 | var obj = JSON.parse(json); 3 | 4 | var success = true; 5 | 6 | //Generic function for checking roles in SignatureResults 7 | function checkRoles(signatureResults, expectedRoles){ 8 | if (signatureResults.length != expectedRoles.length) { 9 | console.log("[PolicyEngine] Number of expected roles does not match number of signatures") 10 | return false; 11 | } 12 | 13 | for (i = 0; i < signatureResults.length; i++) { 14 | if (signatureResults[i].certs.length == 0) { 15 | console.log("[PolicyEngine] Cert chain for signed artifact is empty") 16 | return false; 17 | } 18 | 19 | if (signatureResults[i].certs.length == 0) { 20 | console.log("[PolicyEngine] No validated cert chain provided for signed artifact") 21 | return false; 22 | } 23 | 24 | if (signatureResults[i].certs[0].length == 0) { 25 | console.log("[PolicyEngine] Cert chain for signed artifact is empty") 26 | return false; 27 | } 28 | 29 | //initial cert should be the same in each chain if multiple validated chains are provided -> it is sufficient to verify the role in certs[0][0] 30 | if (signatureResults[i].certs[0][0].subject.organizationalUnit != expectedRoles[i]){ 31 | console.log("[PolicyEngine] Role " + i + " in cert does not match: " + signatureResults[i].certs[0][0].subject.organizationalUnit + " vs. " + expectedRoles[i]) 32 | return false; 33 | } 34 | } 35 | return true; 36 | } 37 | 38 | //Verify AR was signed by a device 39 | if (!checkRoles(obj.reportSignatureCheck, ["device"])) { 40 | console.log("[PolicyEngine] Role check for Attestation Report Signature failed") 41 | success = false; 42 | } else { 43 | console.log("[PolicyEngine] Role check for Attestation Report Signature successful") 44 | } 45 | 46 | //Verify roles used to sign manifests 47 | for (i=0; i < obj.metadata.manifestResults.length; i++){ 48 | if (!checkRoles(obj.metadata.manifestResults[i].signatureValidation, ["Developer", "Evaluator", "Certifier"])) { 49 | console.log("[PolicyEngine] Role check for Manifest Signature", obj.metadata.manifestResults[i].name, "failed") 50 | success = false; 51 | } else { 52 | console.log("[PolicyEngine] Role check for Manifest Signature successful") 53 | } 54 | } 55 | 56 | //Verify roles used to sign Company Description 57 | if (obj.metadata.compDescResult) { 58 | if (!checkRoles(obj.metadata.compDescResult.signatureValidation, ["Operator", "Evaluator", "Certifier"])) { 59 | console.log("[PolicyEngine] Role check for Company Description Signatures failed") 60 | success = false; 61 | } else { 62 | console.log("[PolicyEngine] Role check for Company Description Signatures successful") 63 | } 64 | } else { 65 | console.log("[PolicyEngine] Company description not present") 66 | success = false 67 | } 68 | 69 | success 70 | -------------------------------------------------------------------------------- /example-setup/docker/cmc.dockerfile: -------------------------------------------------------------------------------- 1 | # Fixed ubuntu 24.04 2 | FROM ubuntu@sha256:1e622c5f073b4f6bfad6632f2616c7f59ef256e96fe78bf6a595d1dc4376ac02 3 | 4 | ARG DEBIAN_FRONTEND=noninteractive \ 5 | GO_VERSION=1.24.1 \ 6 | GOPATH=/go \ 7 | PATH="/usr/local/go/bin:$GOPATH/bin:$PATH" 8 | 9 | # Install dependencies 10 | RUN apt-get update && apt-get install -y wget lsb-release moreutils golang-cfssl \ 11 | build-essential sqlite3 zlib1g-dev libssl-dev jq yq git curl ca-certificates \ 12 | python3-venv libjson-glib-dev libjson-glib-1.0-0 nasm acpica-tools uuid-dev \ 13 | genisoimage gnutls-bin guestfs-tools guestmount libtasn1-dev libgnutls28-dev \ 14 | socat libseccomp-dev python-is-python3 libtool expect clang llvm lld xxd 15 | 16 | # Required for QEMU 17 | RUN apt-get install -y git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev \ 18 | ninja-build libnfs-dev libiscsi-dev flex git-email libaio-dev libbluetooth-dev \ 19 | libcapstone-dev libbrlapi-dev libbz2-dev libcap-ng-dev libcurl4-gnutls-dev \ 20 | libgtk-3-dev libibverbs-dev libjpeg8-dev libncurses5-dev libnuma-dev librbd-dev \ 21 | librdmacm-dev libsasl2-dev libsdl2-dev libseccomp-dev libsnappy-dev libssh-dev \ 22 | libvde-dev libvdeplug-dev libvte-2.91-dev libxen-dev liblzo2-dev valgrind \ 23 | bison xfslibs-dev 24 | 25 | # Install Go 26 | RUN curl -fsSL "https://go.dev/dl/go$GO_VERSION.linux-amd64.tar.gz" -o go.tar.gz \ 27 | && tar -C /usr/local -xzf go.tar.gz \ 28 | && rm go.tar.gz 29 | 30 | # Install SGX dependencies and EGo framework 31 | RUN mkdir -p /etc/apt/keyrings && \ 32 | wget -qO- https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | tee /etc/apt/keyrings/intel-sgx-keyring.asc > /dev/null && \ 33 | echo "deb [signed-by=/etc/apt/keyrings/intel-sgx-keyring.asc arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/intel-sgx.list && \ 34 | apt-get update && \ 35 | EGO_DEB=ego_1.7.0_amd64_ubuntu-$(lsb_release -rs).deb && \ 36 | wget https://github.com/edgelesssys/ego/releases/download/v1.7.0/$EGO_DEB && \ 37 | apt-get install -y ./$EGO_DEB build-essential libssl-dev && \ 38 | apt-get install -y libsgx-dcap-default-qpl 39 | 40 | # Install measured-boot-tools 41 | RUN git clone https://github.com/Fraunhofer-AISEC/measured-boot-tools.git && \ 42 | cd measured-boot-tools && \ 43 | make && \ 44 | make install 45 | 46 | # Download Intel SGX/TDX API services Sectigo public trusted CA to /etc/ssl/certs 47 | RUN wget -q \ 48 | http://crt.sectigo.com/SectigoRSAOrganizationValidationSecureServerCA.crt \ 49 | -O /etc/ssl/certs/SectigoRSAOrganizationValidationSecureServerCA.crt && \ 50 | update-ca-certificates 51 | 52 | # Configure QGS 53 | RUN sed -i "s/localhost:8081/tdx-pccs:8081/" /etc/sgx_default_qcnl.conf && \ 54 | sed -i 's/"use_secure_cert": true/"use_secure_cert": false/' /etc/sgx_default_qcnl.conf 55 | 56 | WORKDIR "/" 57 | -------------------------------------------------------------------------------- /bin/generate-app-manifest-tpm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | trap '[ $? -eq 0 ] && exit 0; printf "%s failed\n" "$0"' EXIT 6 | dir="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd -P)" 7 | source "${dir}/bin/utils.sh" 8 | 9 | parse_srtm_pcrs_prefix="${1:-sudo}" 10 | 11 | data="${dir}/data" 12 | input="${dir}/example-setup/metadata-templates" 13 | out="${data}/metadata-raw" 14 | 15 | name="de.test.host-apps" 16 | device_description="${out}/device.description.json" 17 | 18 | if [[ ! -d "${data}" ]]; then 19 | echo "Data directory ${data} does not exist. Did you run the setup-cmc script? Abort.." 20 | exit 1 21 | fi 22 | 23 | if [[ ! -f "${device_description}" ]]; then 24 | echo "Error: Device description ${device_description} does not exist. Run generate-device-description first" 25 | exit 1 26 | fi 27 | 28 | echo "Using ${data} as directory for local data" 29 | 30 | # Parse the IMA measurement lists 31 | referenceValues=$("${parse_srtm_pcrs_prefix}" parse-ima-pcr) 32 | 33 | # App Manifest: load 34 | json=$(cat "${input}/manifest.json") 35 | 36 | # App Manifest: Insert reference values 37 | while IFS= read -r element; do 38 | json=$(echo "${json}" | jq --argjson element "${element}" '.referenceValues += [$element]') 39 | done < <(echo "${referenceValues}" | jq -c '.[]') 40 | 41 | # App Manifest: Set other properties 42 | setjson "json" "name" "${name}" 43 | setjson "json" "version" "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" 44 | setjson "json" "developerCommonName" "test developer" 45 | setjson "json" "validity.notBefore" "$(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "-1 day")" 46 | setjson "json" "validity.notAfter" "$(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "+2 years")" 47 | setjson "json" "description" "App Manifest" 48 | setarr "json" "baseLayers" "de.test.os" 49 | setjson "json" "certLevel" 1 50 | 51 | # App Manifest: Store 52 | echo "Writing ${out}/app.manifest.json" 53 | printf "%s\n" "${json}" > "${out}/app.manifest.json" 54 | 55 | # App Description: Create corresponding app description 56 | appdesc=$(cat "${input}/manifest.description.json") 57 | appdesc=$(echo "${appdesc}" | jq ".name = \"${name}.description\"") 58 | appdesc=$(echo "${appdesc}" | jq ".manifest = \"${name}\"") 59 | 60 | # Device Description: Add/replace app description to/in device description 61 | devdesc=$(cat "${device_description}") 62 | exists=$(echo "${devdesc}" | jq "any(.descriptions[]; .name == \"${name}.description\")") 63 | if [[ "${exists}" = false ]]; then 64 | echo "Adding app description to device description" 65 | else 66 | echo "Replacing existing app description" 67 | devdesc=$(echo "$devdesc" | jq ".descriptions |= map(select(.name != \"${name}.description\"))") 68 | fi 69 | devdesc=$(echo "${devdesc}" | jq --argjson desc "[${appdesc}]" '.descriptions += $desc') 70 | 71 | # Device Description: Store 72 | echo "Writing ${device_description}" 73 | printf "%s\n" "${devdesc}" > "${device_description}" 74 | -------------------------------------------------------------------------------- /tools/mrtool/precomputetdx/mrseam.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package precomputetdx 17 | 18 | import ( 19 | "crypto/sha512" 20 | "encoding/hex" 21 | "fmt" 22 | "os" 23 | 24 | ar "github.com/Fraunhofer-AISEC/cmc/attestationreport" 25 | "github.com/Fraunhofer-AISEC/cmc/tools/mrtool/tcg" 26 | ) 27 | 28 | /** 29 | * Calculates the measurement register for the TDX SEAM module (MRSEAM) 30 | * 31 | * The MRTD contains the digest of the TDX-module as measured by the SEAM loader 32 | */ 33 | func PrecomputeMrSeam(c *Config) (*ar.ReferenceValue, []*ar.ReferenceValue, error) { 34 | log.Debugf("Precomputing MRSEAM...") 35 | 36 | var mrseam []byte 37 | refvals := make([]*ar.ReferenceValue, 0) 38 | 39 | if c.TdxModule != "" { 40 | data, err := os.ReadFile(c.TdxModule) 41 | if err != nil { 42 | return nil, nil, fmt.Errorf("faild to read tdx-module: %w", err) 43 | } 44 | hash := sha512.Sum384(data) 45 | 46 | refvals = append(refvals, &ar.ReferenceValue{ 47 | Type: "TDX Reference Value", 48 | SubType: "TDX-Module", 49 | Index: tcg.INDEX_MRSEAM, 50 | Sha384: hash[:], 51 | Description: "MRSEAM: SEAMLDR Measurement: TDX-Module", 52 | }) 53 | mrseam = hash[:] 54 | } else if c.MrSeam != "" { 55 | hash, err := hex.DecodeString(c.MrSeam) 56 | if err != nil { 57 | return nil, nil, fmt.Errorf("failed to decode hash: %w", err) 58 | } 59 | if len(hash) != sha512.Size384 { 60 | return nil, nil, fmt.Errorf("malformed sha384 hash length: (is: %v, expected: %v)", len(hash), sha512.Size384) 61 | } 62 | 63 | refvals = append(refvals, &ar.ReferenceValue{ 64 | Type: "TDX Reference Value", 65 | SubType: "TDX-Module", 66 | Index: tcg.INDEX_MRSEAM, 67 | Sha384: hash[:], 68 | Description: "MRSEAM: TDX Module Measurement: SEAMLDR Measurement: TDX-Module", 69 | }) 70 | mrseam = hash[:] 71 | } else { 72 | return nil, nil, fmt.Errorf("tdx-module or mrseam must be specified for MRSEAM") 73 | } 74 | 75 | // Create MRSEAM final reference value 76 | mrseamSummary := &ar.ReferenceValue{ 77 | Type: "TDX Reference Value", 78 | SubType: "MRSEAM Summary", 79 | Description: "MRSEAM", 80 | Index: tcg.INDEX_MRSEAM, 81 | Sha384: mrseam, 82 | } 83 | 84 | return mrseamSummary, refvals, nil 85 | } 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CMC 2 | 3 | [![build](https://github.com/Fraunhofer-AISEC/cmc/actions/workflows/build.yml/badge.svg)](https://github.com/Fraunhofer-AISEC/cmc/actions/workflows/build.yml) 4 | [![](https://godoc.org/github.com/Fraunhofer-AISEC/cmc?status.svg)](https://pkg.go.dev/github.com/Fraunhofer-AISEC/cmc) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/Fraunhofer-AISEC/cmc)](https://goreportcard.com/report/github.com/Fraunhofer-AISEC/cmc) 6 | 7 | The CMC repository provides software to enable remote attestation of computing platforms, 8 | as well as secure attested TLS and HTTPS channels between those platforms. Currently, the CMC 9 | repository supports Trusted Platform Modules (TPMs), AMD SEV-SNP, Intel SGX, as well as Intel TDX. 10 | The goal is to make attestation easy for verifiers without prior knowledge of the peer's software 11 | stack. This is achieved through a set of trusted CAs and self-contained attestation reports that 12 | include signed metadata and reference hash values. 13 | 14 | *A detailed description of the architecture can be found in our* 15 | *[paper](https://dl.acm.org/doi/pdf/10.1145/3600160.3600171) and in the* 16 | *[documentation](./doc)* 17 | 18 | ## Quick Start 19 | 20 | Refer to [Setup](./doc/setup.md) for instructions on how to setup, build, configure and run the CMC 21 | on various hardware platforms. 22 | 23 | For a quick demo without installing software or requiring actual hardware, use Docker and the 24 | Virtual Machine (VM) with attached swTPM as described in [Setup](./doc/setup.md). 25 | 26 | ## Documentation 27 | 28 | The following contents can be found in the [doc](./doc/) folder: 29 | 30 | ### Setup 31 | 32 | For detailed instructions on how to setup TPM, Intel SGX, Intel TDX or AMD SEV-SNP platforms 33 | including PKI and metadata generation, refer to the [Setup Documentation](./doc/setup.md) 34 | 35 | ### Build And Install 36 | 37 | See [Build And Install Documentation](./doc/build-and-install.md) for instructions on how to build 38 | and install the go binaries. 39 | 40 | ### Run 41 | 42 | For configuring and running the go binaries, refer to the 43 | [Run Documentation](./doc/run.md). 44 | 45 | ### Architecture 46 | 47 | An overview of the architecture is given in [Architecture](./doc/architecture.md). 48 | 49 | ### Metadata 50 | 51 | Detailed information on how to generate, sign and parse metadata is given in 52 | [Metadata](./doc/metadata.md). 53 | 54 | ### Developer Documentation 55 | 56 | Refer to [Developer Documentation](./doc/dev.md) for instructions on developing custom applications 57 | using attested TLS or attested HTTPS, as well as for developing the CMC. 58 | 59 | ### Additional Demo Setups 60 | 61 | For an alternative demo setup with a more complex PKI and policies based on the requirements of 62 | the International Data Spaces (IDS), see [IDS Example Setup](./doc/ids-example-setup.md) 63 | 64 | ### Tools 65 | 66 | A description of tools and helper programs can be found in the 67 | [Tools Documentation](./doc/tools.md). 68 | -------------------------------------------------------------------------------- /doc/build-and-install.md: -------------------------------------------------------------------------------- 1 | # Build 2 | 3 | All binaries can be built with the [*go*-toolchain](https://go.dev/doc/install). 4 | 5 | > Note: Building SGX enclaves requires a custom compiler and different commands, 6 | > see [SGX Setup](./setup-sgx.md) 7 | 8 | ## Build and Install all Binaries 9 | 10 | ```sh 11 | # Clone the CMC repo 12 | git clone https://github.com/Fraunhofer-AISEC/cmc 13 | 14 | # Build all components 15 | cd cmc 16 | go build ./... 17 | 18 | # Install all components to $GOPATH/bin 19 | go install ./... 20 | ``` 21 | 22 | You can run all binaries by adding the following line to your `$HOME/.profile`: 23 | ```sh 24 | export PATH=$PATH:$HOME/go/bin 25 | ``` 26 | 27 | ## Build the single artifacts 28 | 29 | ### Build the Provisioning Server 30 | 31 | ```sh 32 | cd provision/estserver 33 | go build 34 | ``` 35 | 36 | ### Build the cmcd 37 | 38 | ```sh 39 | cd cmcd 40 | go build 41 | ``` 42 | 43 | ### Build cmcctl 44 | 45 | ```sh 46 | cd cmcctl 47 | go build 48 | 49 | # Build cmcctl as SGX enclave 50 | make egocmc 51 | ``` 52 | 53 | ### Build the mrtool 54 | 55 | ```sh 56 | cd tools/mrtool 57 | go build 58 | ``` 59 | 60 | ## Customize Builds 61 | 62 | ### Reduce General Size 63 | 64 | The size of all binaries can be reduced via go linker flags: 65 | ```sh 66 | go build ldflags="-s -w" 67 | ``` 68 | For more information see the go documentation. 69 | 70 | ### Reduce Size by Disabling Features 71 | 72 | The size of the binaries can further be reduced by a considerable amount through disabling 73 | unused features during build time. The `go build` command builds each binary with all features 74 | enabled. The project uses the go build system with build tags to disable features. 75 | 76 | To disable all features, use the custom `nodefaults` tag. You can then enable the features you 77 | want to build via additional tags. 78 | 79 | Currently supported tags for the `cmcd` and `cmcctl` are: 80 | - `nodefaults` Disables all features 81 | - `grpc` Enables the gRPC API 82 | - `coap` Enables the CoAP API 83 | - `socket` Enables the socket API 84 | - `libapi` Enables the library API 85 | - `tpm` Enables the TPM driver 86 | - `snp` Enables the SNP driver 87 | - `tdx` Enables the Intel TDX driver 88 | - `sgx` Enables the Intel SGX driver 89 | - `sw` Enables the container driver 90 | 91 | To build the cmcd/cmcctl with `socket` and `tpm` support: 92 | ```sh 93 | go build -tags nodefaults,socket,tpm 94 | ``` 95 | 96 | > Note: disabling features during build-time but specifying to use them in the configuration files 97 | > will lead to errors during runtime 98 | 99 | ### Regenerate Protobuf gRPC Interface 100 | 101 | see: https://grpc.io/docs/languages/go/quickstart/ for newer versions 102 | 103 | ```sh 104 | sudo apt install -y protobuf-compiler 105 | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 106 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 107 | cd grpcapi/ 108 | make 109 | ``` 110 | -------------------------------------------------------------------------------- /cmcctl/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 - 2025 Fraunhofer AISEC 2 | // Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "flag" 20 | "fmt" 21 | "os" 22 | "strings" 23 | ) 24 | 25 | var ( 26 | cmds = map[string]func(*config){ 27 | "generate": generate, 28 | "verify": verify, 29 | "measure": measure, 30 | "dial": dial, 31 | "listen": listen, 32 | "request": request, 33 | "serve": serve, 34 | "token": token, 35 | "provision": provision, 36 | } 37 | ) 38 | 39 | func init() { 40 | flag.Usage = func() { 41 | fmt.Fprintf(flag.CommandLine.Output(), "USAGE:\n\tcmcctl command [options]\n\n") 42 | fmt.Fprintf(flag.CommandLine.Output(), "COMMANDS:\n") 43 | fmt.Fprintf(flag.CommandLine.Output(), " generate\tGenerate attestation report\n") 44 | fmt.Fprintf(flag.CommandLine.Output(), " verify\tVerify attestation report\n") 45 | fmt.Fprintf(flag.CommandLine.Output(), " dial\t\tEstablish attested TLS client\n") 46 | fmt.Fprintf(flag.CommandLine.Output(), " listen\tEstablish attested TLS server\n") 47 | fmt.Fprintf(flag.CommandLine.Output(), " request\tPerform an attested HTTPS request\n") 48 | fmt.Fprintf(flag.CommandLine.Output(), " serve\t\tEstablish an attested HTTPS server\n") 49 | fmt.Fprintf(flag.CommandLine.Output(), " token\t\tRequest a bootstrap token for EST certificate requests\n") 50 | fmt.Fprintf(flag.CommandLine.Output(), " measure\tRecord measurements\n") 51 | fmt.Fprintf(flag.CommandLine.Output(), " provision\tRetrieve provisioning data for CVMs\n") 52 | fmt.Fprintf(flag.CommandLine.Output(), "\nOPTIONS:\n") 53 | flag.PrintDefaults() 54 | } 55 | } 56 | 57 | func main() { 58 | 59 | cmd, name, err := getCommand() 60 | if err != nil { 61 | flag.Usage() 62 | log.Fatalf("%v", err) 63 | return 64 | } 65 | 66 | c, err := getConfig(os.Args[1]) 67 | if err != nil { 68 | flag.Usage() 69 | log.Fatalf("Failed to get config: %v", err) 70 | return 71 | } 72 | 73 | log.Infof("Running command %v", name) 74 | 75 | cmd(c) 76 | } 77 | 78 | func getCommand() (func(*config), string, error) { 79 | 80 | if len(os.Args) < 2 { 81 | return nil, "", fmt.Errorf("expected command") 82 | } 83 | 84 | cmd := os.Args[1] 85 | 86 | cmdFunc, ok := cmds[strings.ToLower(cmd)] 87 | if !ok { 88 | return nil, "", fmt.Errorf("undefined command %v", cmd) 89 | } 90 | 91 | return cmdFunc, cmd, nil 92 | } 93 | --------------------------------------------------------------------------------