├── doc ├── parsec-corim-tpm.md ├── play-by-ear.md ├── parsec-evidence-tpm.md ├── mbedtls-build.md ├── parsec-evidence-cca.md ├── format-definitions │ ├── parsec-platform-evidence-tpm.md │ └── parsec-key-evidence-tpm.md └── parsec-build.md ├── demo ├── verifier │ ├── mock_verifier.Dockerfile │ └── wiremock │ │ └── mappings │ │ ├── new_session.json │ │ ├── discovery.json │ │ └── challenge_response.json ├── attester │ ├── corim.json │ ├── handshake.sh │ ├── start.sh │ ├── endorse.sh │ ├── parsec-config.toml │ ├── comid-pcr.json │ └── attester.Dockerfile ├── demo.env ├── demo-network.md ├── build │ ├── veraison.mk │ └── veraison_deployment.cfg.yml ├── Makefile ├── README.md ├── docker-compose.yml └── relying-party │ └── relying-party.Dockerfile ├── corim-templates ├── corim.json ├── README.md ├── comid-key.json └── comid-pcr.json ├── parsec-config.toml ├── README.md └── LICENSE /doc/parsec-corim-tpm.md: -------------------------------------------------------------------------------- 1 | TODO: describe the CoRIM profile to convey Parsec TPM endorsements and ref-values. 2 | -------------------------------------------------------------------------------- /demo/verifier/mock_verifier.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM wiremock/wiremock 2 | 3 | # Copy the stubs (canned API responses) into the image 4 | COPY wiremock /home/wiremock 5 | -------------------------------------------------------------------------------- /corim-templates/corim.json: -------------------------------------------------------------------------------- 1 | { 2 | "corim-id": "B3EC060E-2A5B-4BC2-8F71-1DAB08CE5BE9", 3 | "profiles": [ 4 | "tag:github.com/parallaxsecond,2023-03-03:tpm" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /demo/attester/corim.json: -------------------------------------------------------------------------------- 1 | { 2 | "corim-id": "B3EC060E-2A5B-4BC2-8F71-1DAB08CE5BE9", 3 | "profiles": [ 4 | "tag:github.com/parallaxsecond,2023-03-03:tpm" 5 | ] 6 | } -------------------------------------------------------------------------------- /demo/attester/handshake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -xuf -o pipefail 4 | 5 | ssl_client2 client_att_type=eat server_addr=$(getent hosts relying-party | cut -d ' ' -f1) server_port=4433 6 | -------------------------------------------------------------------------------- /demo/demo.env: -------------------------------------------------------------------------------- 1 | DEMO_SUBNET="172.30.0.0/16" 2 | PFE_DEMO_ADDR="172.30.0.10" 3 | VFE_DEMO_ADDR="172.30.0.11" 4 | RP_DEMO_ADDR="172.30.0.12" 5 | ATTESTER_DEMO_ADDR="172.30.0.13" 6 | 7 | VERAISON_SUBNET="172.40.0.0/16" 8 | PFE_VERAISON_ADDR="172.40.0.10" 9 | VFE_VERAISON_ADDR="172.40.0.11" 10 | VTS_VERAISON_ADDR="172.40.0.12" -------------------------------------------------------------------------------- /demo/attester/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -xuf -o pipefail 4 | 5 | # Start and initialize TPM server 6 | tpm_server & 7 | 8 | until tpm2_startup -c -T mssim; do 9 | sleep 1 10 | done 11 | 12 | # TODO: Initialize PCRs with some data 13 | 14 | # Start Parsec service 15 | parsec -c /etc/parsec/config.toml 16 | -------------------------------------------------------------------------------- /demo/demo-network.md: -------------------------------------------------------------------------------- 1 | ```mermaid 2 | flowchart TD 3 | subgraph VrsnNet [veraison internal network] 4 | end 5 | 6 | subgraph DemoNet [demo network] 7 | end 8 | 9 | subgraph Vrsn [Veraison] 10 | direction TB 11 | VrsnNet 12 | VFE 13 | VTS 14 | PFE 15 | end 16 | 17 | subgraph Attester [Attester] 18 | Parsec 19 | Client["TLS client"] 20 | end 21 | 22 | subgraph rp [Relying Party] 23 | Server["TLS server"] 24 | end 25 | 26 | 27 | Attester --- DemoNet 28 | rp --- DemoNet 29 | VFE --- DemoNet 30 | PFE --- DemoNet 31 | VFE --- VrsnNet 32 | PFE --- VrsnNet 33 | VTS --- VrsnNet 34 | ``` 35 | -------------------------------------------------------------------------------- /demo/attester/endorse.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -xeuf -o pipefail 4 | 5 | # Create AIK endorsement 6 | mkdir -p endorsement/comid 7 | parsec-tool create-endorsement -c "D10E4BD6-7E02-4D2C-BF1A-69AE22680478" > endorsement/comid-key.json 8 | cp ~/comid-pcr.json ~/corim.json endorsement/ 9 | 10 | # Create the endorsement bundle and endorse 11 | pushd endorsement 12 | cocli comid create -o comid -t comid-key.json 13 | cocli comid create -o comid -t comid-pcr.json 14 | cocli corim create -t corim.json -M comid -o corim-parsec-tpm.cbor 15 | cocli corim submit -f corim-parsec-tpm.cbor \ 16 | -s 'http://pfe:8888/endorsement-provisioning/v1/submit' \ 17 | -m 'application/corim-unsigned+cbor; profile="tag:github.com/parallaxsecond,2023-03-03:tpm"' 18 | -------------------------------------------------------------------------------- /demo/build/veraison.mk: -------------------------------------------------------------------------------- 1 | VERAISON_ROOT := .veraison-services 2 | THIS_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 3 | 4 | # The command line can override VERAISON_BRANCH to point to a branch other than 5 | # main 6 | VERAISON_BRANCH ?= main 7 | 8 | $(VERAISON_ROOT): 9 | git clone --branch $(VERAISON_BRANCH) https://github.com/veraison/services $@ 10 | 11 | .PHONY: veraison-build-containers 12 | veraison-build-containers: $(VERAISON_ROOT) 13 | @docker network rm veraison-net || true 14 | cp $(THIS_DIR)/veraison_deployment.cfg.yml $(VERAISON_ROOT)/deployments/docker/src/config.yaml.template 15 | $(MAKE) -C "$ ear-ok.jwt 30 | ``` 31 | 32 | * Verify: 33 | ```sh 34 | cat ear-ok.jwt | step crypto jwt verify --key=pkey.json --subtle 35 | ``` 36 | 37 | Note the presence of the `"ear.veraison.key-attestation"` claim. 38 | 39 | ## Create (and verify) an "unsuccessful" EAR 40 | 41 | * Create: 42 | ```sh 43 | step crypto jwt sign --subtle --alg=ES256 --key=skey.json ear-claims-key-attestation-ko.json > ear-ko.jwt 44 | ``` 45 | 46 | * Verify: 47 | ```sh 48 | cat ear-ko.jwt | step crypto jwt verify --key=pkey.json --subtle 49 | ``` 50 | 51 | Note the absence of the `"ear.veraison.key-attestation"` claim. 52 | -------------------------------------------------------------------------------- /corim-templates/comid-pcr.json: -------------------------------------------------------------------------------- 1 | { 2 | "tag-identity": { 3 | "id": "99019224-57AA-44BC-BEF8-D36BDD6BD035" 4 | }, 5 | "entities": [ 6 | { 7 | "name": "Parsec", 8 | "regid": "https://github.com/parallaxsecond", 9 | "roles": [ 10 | "tagCreator", 11 | "creator", 12 | "maintainer" 13 | ] 14 | } 15 | ], 16 | "triples": { 17 | "reference-values": [ 18 | { 19 | "environment": { 20 | "class": { 21 | "id": { 22 | "type": "uuid", 23 | "value": "CD1F0E55-26F9-460D-B9D8-F7FDE171787C" 24 | } 25 | } 26 | }, 27 | "measurements": [ 28 | { 29 | "key": { 30 | "type": "uint", 31 | "value": 0 32 | }, 33 | "value": { 34 | "digests": [ 35 | "sha-256:h0KPxSKAPTEGXnvOPPA/5HUJZjHl4Hu9eg/eYMTPJcc=", 36 | "sha-384:QoS1aUymwNLPR4mguVrIAlyBjeUjBDZL580pgbLS7caFsyInfsJYGZYkE9jJssH1" 37 | ] 38 | } 39 | }, 40 | { 41 | "key": { 42 | "type": "uint", 43 | "value": 1 44 | }, 45 | "value": { 46 | "digests": [ 47 | "sha-256:rqg3uI4yCrzUdvWDmVLV4aYSwOSiJcuSBdIAcebDd0U=", 48 | "sha-384:IQe752H8pS2VE2oTVNt6TdV7Gya+DT2nHZ6yOYazS6YVq/ZRTPNeWp6lWgMtBop4" 49 | ] 50 | } 51 | } 52 | ] 53 | } 54 | ] 55 | } 56 | } -------------------------------------------------------------------------------- /doc/parsec-evidence-tpm.md: -------------------------------------------------------------------------------- 1 | # TPM Parsec Evidence 2 | 3 | This document describes the format of the credential produced by the Parsec 4 | service as a key attestation credential for TPM-backed keys. 5 | 6 | The Parsec service produces a blob of binary data as a response to a key 7 | attestation operation issued by a client. The service takes in a nonce and uses 8 | it as freshness for the two attestation tokens it produces. 9 | 10 | The attestation token produced by Parsec is a CBOR encoded structure, namely a 11 | [Combined Attestation Bundle 12 | (CAB)](https://datatracker.ietf.org/doc/draft-bft-rats-kat/). The full 13 | high-level format is shown below. 14 | 15 | ``` 16 | parsecTpmAttestation = { 17 | "kat" => parsecTpmKeyStmtFormat, 18 | "pat" => parsecTpmPlatStmtFormat 19 | } 20 | ``` 21 | 22 | The definition of `parsecTpmKeyStmtFormat` can be found in 23 | [this](format-definitions/parsec-key-evidence-tpm.md) document, while the 24 | definition of `parsecTpmPlatStmtFormat` can be found in 25 | [this](format-definitions/parsec-platform-evidence-tpm.md) document. 26 | 27 | The CAB produced by Parsec is then encapsulated in a [Conceptual Message 28 | Wrapper](https://datatracker.ietf.org/doc/draft-ftbs-rats-msg-wrap/), which is 29 | simply a CBOR-encoded array with the first element identifying the type of the 30 | second element. In this specific case, the type is represented by the string 31 | `application/vnd.parallaxsecond.key-attestation.tpm`, a media type string: 32 | 33 | ``` 34 | [ 35 | "application/vnd.parallaxsecond.key-attestation.tpm" 36 | bytes .cbor parsecTpmAttestation 37 | ] 38 | ``` 39 | -------------------------------------------------------------------------------- /demo/Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := help 2 | 3 | # command line can override this 4 | ENV_FILE ?= demo.env 5 | 6 | DC := docker-compose --env-file $(ENV_FILE) 7 | 8 | NODES := $(shell $(DC) config --services | sed -e 's/_/-/g' | xargs) 9 | 10 | define interactive_shell_template 11 | sh-$(1): start ; docker exec -ti $(1) bash 12 | endef 13 | 14 | $(foreach node,$(NODES),$(eval $(call interactive_shell_template,$(node)))) 15 | 16 | define logs 17 | logs-$(1): start ; $(DC) logs $(1) 18 | endef 19 | 20 | ifdef BUILD 21 | BUILD_FLAGS := --build 22 | endif 23 | 24 | $(foreach node,$(NODES),$(eval $(call logs,$(node)))) 25 | 26 | start: veraison-build-containers 27 | $(DC) up -d $(BUILD_FLAGS) 28 | 29 | stop: 30 | $(DC) down --remove-orphans 31 | 32 | show-logs: 33 | $(DC) logs 34 | 35 | top: 36 | $(DC) top 37 | 38 | endorse: start 39 | $(DC) exec attester /root/endorse.sh 40 | 41 | handshake: start 42 | $(DC) exec attester /root/handshake.sh 43 | 44 | help: 45 | @echo "available targets:" 46 | @echo 47 | @echo " start start the demo environment (containers, network, volumes, etc.)" 48 | @echo " do \"make BUILD=true start\" to force building the containers" 49 | @echo " endorse collect keys and reference values from the attester and provision the verifier" 50 | @echo " handshake run an attested TLS handshake and exchange minimal application data between attester and verifier" 51 | @echo " stop tear down the demo environment" 52 | @echo " show-logs show logs for all the containers" 53 | @echo " log- show logs for the specific container" 54 | @echo " sh- start an interactive shell in the container" 55 | @echo 56 | @echo "nodes: $(NODES)" 57 | 58 | include build/veraison.mk -------------------------------------------------------------------------------- /demo/verifier/wiremock/mappings/challenge_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": { 3 | "method": "POST", 4 | "urlPath": "/challenge-response/v1/session/12345678" 5 | }, 6 | "response": { 7 | "status": 200, 8 | "headers": { 9 | "Content-Type": "application/vnd.veraison.challenge-response-session+json" 10 | }, 11 | "jsonBody": { 12 | "nonce": "MDEyMzQ1Njc=", 13 | "expiry": "2030-10-12T07:20:50.52Z", 14 | "accept": [ 15 | "application/psa-attestation-token", 16 | "application/vnd.1", 17 | "application/vnd.2", 18 | "application/vnd.3" 19 | ], 20 | "status": "complete", 21 | "evidence": { 22 | "type": "application/psa-attestation-token", 23 | "value": "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=" 24 | }, 25 | "result": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXIucmF3LWV2aWRlbmNlIjoiTnpRM01qWTVOek0yTlRZek56UUsiLCJlYXIudmVyaWZpZXItaWQiOnsiYnVpbGQiOiJ2dHMgMC4wLjEiLCJkZXZlbG9wZXIiOiJodHRwczovL3ZlcmFpc29uLXByb2plY3Qub3JnIn0sImVhdF9wcm9maWxlIjoidGFnOmdpdGh1Yi5jb20sMjAyMzp2ZXJhaXNvbi9lYXIiLCJpYXQiOjEuNjY2NTI5MTg0ZSswOSwianRpIjoiMzhkZWI4NmI4NzRkZmRmNDU3YWY1OWUwMWZjZjM3ZTdhYjMwMGNlMTUxZjNkMTlhOTE5OWMyMzI4MjQxMTlmZiIsIm5iZiI6MTY3ODcxOTIzMiwic3VibW9kcyI6eyJQQVJTRUNfVFBNIjp7ImVhci5hcHByYWlzYWwtcG9saWN5LWlkIjoicG9saWN5Oi8vcGFyc2VjLzYwYTAwNjhkIiwiZWFyLnN0YXR1cyI6ImFmZmlybWluZyIsImVhci50cnVzdHdvcnRoaW5lc3MtdmVjdG9yIjp7ImV4ZWN1dGFibGVzIjoyLCJoYXJkd2FyZSI6MiwiaW5zdGFuY2UtaWRlbnRpdHkiOjJ9LCJlYXIudmVyYWlzb24ua2V5LWF0dGVzdGF0aW9uIjp7ImFrcHViIjoiTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFZDNKbGI0RkxPWko1MWVIeGVCLXNid21hUEZ5aHNPTlRVWU5MQ0xaZUMxY2xrTTJ2ajNhVFlienpTc19CSGw0SFRvUW12ZDRFdm01bE9VVkVsaGZlUlEifX19fQ.55BUrxOUO-fu5lvDXiXyhFTsf3qNES66YZMxvx30ynS7Kjzp_h3ARngIew1QZPsdHDC-HowxSO617Jr_S5au0A" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | This demo showcases a TLS-protected exchange that uses attestation Evidence instead of X.509 credentials to identify and authenticate a client to a server. 4 | 5 | In [RATS terms](https://www.rfc-editor.org/rfc/rfc9334.html#figure-1), the TLS client is the Attester, while the TLS server is the Relying Party. 6 | 7 | The Relying Party delegates checking the trustworthiness of the Evidence presented by the Attester to an attestation Verifier, using a [background check](https://www.rfc-editor.org/rfc/rfc9334.html#figure-6) arrangement. 8 | 9 | A successful handshake is predicated on the provisioning of the Reference Values and Endorsements associated with the Attester to the Verifier. 10 | 11 | ```mermaid 12 | flowchart TB 13 | subgraph Attester 14 | end 15 | 16 | subgraph RP ["Relying Party"] 17 | end 18 | 19 | subgraph Verifier 20 | end 21 | 22 | subgraph RVP[Reference Value &
Endorsement Provider] 23 | end 24 | 25 | RP <-- Attested TLS --> Attester 26 | RP -- "Veraison
Verification API" --> Verifier 27 | RVP -- "Veraison
Provisioning API" --> Verifier 28 | ``` 29 | 30 | ## Actions 31 | 32 | > **Note**: All the `make(1)` targets below must be invoked in the current directory. 33 | 34 | 1. Start up the demo environment: 35 | 36 | > **Warning**: The first time it is run, this will build several Docker container images. On a M1 laptop the process has been observed to take up to 20 minutes. 37 | 38 | ```shell 39 | make start 40 | ``` 41 | 42 | 2. Obtain verification keys and PCR golden values from the TPM, repackage into [CoRIM](https://datatracker.ietf.org/doc/draft-ietf-rats-corim), and send to the Verifier: 43 | 44 | ```shell 45 | make endorse 46 | ``` 47 | 48 | 3. Run an [Attested TLS](https://datatracker.ietf.org/doc/draft-fossati-tls-attestation) handshake between client and server: 49 | 50 | ```shell 51 | make handshake 52 | ``` 53 | 54 | 4. Tear down the demo environment: 55 | 56 | ```shell 57 | make stop 58 | ``` 59 | -------------------------------------------------------------------------------- /demo/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | vts: 5 | image: "veraison/vts" 6 | volumes: 7 | - ./logs:/opt/veraison/logs 8 | networks: 9 | veraison: 10 | ipv4_address: ${VTS_VERAISON_ADDR} 11 | container_name: vts 12 | 13 | vfe: 14 | image: "veraison/verification" 15 | volumes: 16 | - ./logs:/opt/veraison/logs 17 | networks: 18 | veraison: 19 | ipv4_address: ${VFE_VERAISON_ADDR} 20 | demo: 21 | ipv4_address: ${VFE_DEMO_ADDR} 22 | extra_hosts: 23 | - "vts-service:${VTS_VERAISON_ADDR}" 24 | depends_on: 25 | vts: 26 | condition: service_started 27 | container_name: vfe 28 | 29 | pfe: 30 | image: "veraison/provisioning" 31 | volumes: 32 | - ./logs:/opt/veraison/logs 33 | networks: 34 | veraison: 35 | ipv4_address: ${PFE_VERAISON_ADDR} 36 | demo: 37 | ipv4_address: ${PFE_DEMO_ADDR} 38 | extra_hosts: 39 | - "vts-service:${VTS_VERAISON_ADDR}" 40 | depends_on: 41 | vts: 42 | condition: service_started 43 | container_name: pfe 44 | 45 | relying-party: 46 | build: 47 | context: relying-party 48 | dockerfile: relying-party.Dockerfile 49 | stdin_open: true 50 | tty: true 51 | networks: 52 | demo: 53 | ipv4_address: ${RP_DEMO_ADDR} 54 | extra_hosts: 55 | - "vfe:${VFE_DEMO_ADDR}" 56 | depends_on: 57 | vfe: 58 | condition: service_started 59 | container_name: relying-party 60 | 61 | attester: 62 | build: 63 | context: attester 64 | dockerfile: attester.Dockerfile 65 | stdin_open: true 66 | tty: true 67 | networks: 68 | demo: 69 | ipv4_address: ${ATTESTER_DEMO_ADDR} 70 | extra_hosts: 71 | - "pfe:${PFE_DEMO_ADDR}" 72 | - "relying-party:${RP_DEMO_ADDR}" 73 | depends_on: 74 | pfe: 75 | condition: service_started 76 | container_name: attester 77 | 78 | networks: 79 | veraison: 80 | ipam: 81 | config: 82 | - subnet: "${VERAISON_SUBNET}" 83 | demo: 84 | ipam: 85 | config: 86 | - subnet: "${DEMO_SUBNET}" 87 | -------------------------------------------------------------------------------- /demo/attester/comid-pcr.json: -------------------------------------------------------------------------------- 1 | { 2 | "tag-identity": { 3 | "id": "99019224-57AA-44BC-BEF8-D36BDD6BD035" 4 | }, 5 | "entities": [ 6 | { 7 | "name": "Parsec", 8 | "regid": "https://github.com/parallaxsecond", 9 | "roles": [ 10 | "tagCreator", 11 | "creator", 12 | "maintainer" 13 | ] 14 | } 15 | ], 16 | "triples": { 17 | "reference-values": [ 18 | { 19 | "environment": { 20 | "class": { 21 | "id": { 22 | "type": "uuid", 23 | "value": "D10E4BD6-7E02-4D2C-BF1A-69AE22680478" 24 | } 25 | } 26 | }, 27 | "measurements": [ 28 | { 29 | "key": { 30 | "type": "uint", 31 | "value": 0 32 | }, 33 | "value": { 34 | "digests": [ 35 | "sha-256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" 36 | ] 37 | } 38 | }, 39 | { 40 | "key": { 41 | "type": "uint", 42 | "value": 1 43 | }, 44 | "value": { 45 | "digests": [ 46 | "sha-256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" 47 | ] 48 | } 49 | }, 50 | { 51 | "key": { 52 | "type": "uint", 53 | "value": 2 54 | }, 55 | "value": { 56 | "digests": [ 57 | "sha-256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" 58 | ] 59 | } 60 | } 61 | ] 62 | } 63 | ] 64 | } 65 | } -------------------------------------------------------------------------------- /doc/mbedtls-build.md: -------------------------------------------------------------------------------- 1 | # Getting the mbedTLS prototype installed on MacOSX 2 | 3 | ## Install stock mbedTLS 4 | 5 | ```sh 6 | brew install mbedtls 7 | ``` 8 | 9 | ## Build and install QCBOR 10 | 11 | ```sh 12 | git clone https://github.com/laurencelundblade/QCBOR 13 | cd QCBOR 14 | make 15 | sudo make install 16 | ``` 17 | 18 | ## Build and install t_cose 19 | 20 | ```sh 21 | git clone https://github.com/laurencelundblade/t_cose 22 | cd t_cose 23 | env CRYPTO_LIB=/opt/homebrew/lib/libmbedcrypto.a CRYPTO_INC="-I /opt/homebrew/include" make -f Makefile.psa -e 24 | sudo make -f Makefile.psa install 25 | ``` 26 | 27 | ## Build ctoken 28 | 29 | ```sh 30 | git clone https://github.com/laurencelundblade/ctoken.git 31 | cd ctoken 32 | env CRYPTO_LIB=/opt/homebrew/lib/libmbedcrypto.a CRYPTO_INC="-I /opt/homebrew/include" make -f Makefile.psa -e 33 | ``` 34 | 35 | Oddly, there is no `install` target in ctoken. A bit of DIY is needed: 36 | 37 | ```sh 38 | sudo mkdir -p /usr/local/include/ctoken 39 | sudo install -m 644 inc/ctoken/ctoken* /usr/local/include/ctoken 40 | sudo install -m 644 libctoken.a /usr/local/lib 41 | ``` 42 | 43 | ## Build the Veraison C Client 44 | 45 | The mbedTLS demo uses [Veraison](https://github.com/veraison) for verification of attestation evidence. The 46 | `ssl_server2` example program needs to make REST API calls to a suitable Veraison endpoint. In order to do this, 47 | it consumes the C client for the Veraison challenge-response API. 48 | 49 | The C client library is actually a wrapper of the Rust client library, so you must first 50 | [install the Rust toolchain](https://www.rust-lang.org/tools/install). 51 | 52 | Install the Veraison C API client as follows: 53 | 54 | ```sh 55 | git clone https://github.com/veraison/rust-apiclient 56 | cd rust-apiclient 57 | cargo build 58 | ``` 59 | 60 | Again, a bit of DIY is needed in order to install the C wrapper pieces into a conventional location so that 61 | mbedTLS can find the include files and the link library: 62 | 63 | ```sh 64 | sudo mkdir -p /usr/local/include/veraison 65 | sudo install -m 644 c-wrapper/veraison_client_wrapper.h /usr/local/include/veraison/ 66 | sudo install -m 644 ./target/debug/libveraison_apiclient_ffi.a /usr/local/lib/ 67 | ``` 68 | 69 | ## Build modified mbedTLS 70 | 71 | ``` 72 | git clone https://github.com/hannestschofenig/mbedtls 73 | cd mbedtls 74 | git checkout tls-attestation 75 | make CFLAGS="-DCTOKEN_LABEL_CNF=8 -DCTOKEN_TEMP_LABEL_KAK_PUB=2500" LDFLAGS="-lqcbor -lctoken -lt_cose -lveraison_apiclient_ffi" 76 | ``` 77 | 78 | ## Running the EAT example 79 | 80 | ```bash 81 | cd programs/ssl 82 | ``` 83 | 84 | * Server side: 85 | 86 | ```bash 87 | ./ssl_server2 attestation_callback=1 force_version=tls13 auth_mode=required 88 | ``` 89 | * Client side: 90 | 91 | ```bash 92 | ./ssl_client2 client_att_type=eat 93 | ``` 94 | 95 | ## Running the EAT example 96 | 97 | ```bash 98 | cd programs/ssl 99 | ``` 100 | 101 | * Server side: 102 | 103 | ```bash 104 | ./ssl_server2 attestation_callback=1 force_version=tls13 auth_mode=required 105 | ``` 106 | * Client side: 107 | 108 | ```bash 109 | ./ssl_client2 client_att_type=eat 110 | ``` 111 | -------------------------------------------------------------------------------- /doc/parsec-evidence-cca.md: -------------------------------------------------------------------------------- 1 | # CCA Parsec Evidence 2 | 3 | This document describes the format of the credential produced by the Parsec 4 | service as a key attestation credential for applications running in an Arm CCA 5 | Realm. 6 | 7 | The Parsec service produces a blob of binary data as a response to a key 8 | attestation operation issued by a client. The service takes in a nonce and uses 9 | it as freshness for the attestation token. 10 | 11 | The attestation token produced by Parsec is a CBOR encoded structure, namely a 12 | [Combined Attestation Bundle 13 | (CAB)](https://datatracker.ietf.org/doc/draft-bft-rats-kat/). The format is 14 | described below in CDDL. 15 | 16 | ``` 17 | parsecCcaAttestation = { 18 | "kat" => UCCS-KAT, 19 | "pat" => CCA-token 20 | } 21 | 22 | UCCS-KAT = ({ 23 | &(eat_nonce: 10) => bstr .size (8..64) 24 | &(cnf: 8) => ik-pub 25 | }) 26 | 27 | ik-pub = cnf-map 28 | 29 | cnf-map = { 30 | &(cose-key: 1) => COSE_Key 31 | } 32 | ``` 33 | 34 | - `pat`: An Arm CCA Attestation Token as defined in the [Arm CCA Realm 35 | Management Monitor 36 | specification](https://developer.arm.com/documentation/den0137/latest) (see 37 | "Attestation Token Format" section). Note that this token represents the 38 | entire credential issued by the Root of Trust, composed of a CCA platform 39 | token and a CCA Realm token. The Platform Attestation Token (PAT) referenced 40 | by this document should not be confused with the CCA platform token. 41 | - `kat`: A sidecar token used to link the application identity key to the PAT. 42 | - `eat_nonce`: A nonce issued by the challenger to prove the freshness of the 43 | attestation token. 44 | - `cnf`: The public part of the application identity key, encoded as a 45 | [COSE_Key](https://www.rfc-editor.org/rfc/rfc9052#name-key-objects). 46 | 47 | PAT contains a collection of tokens, which are individually signed. The PAT is 48 | also cryptographically linked to the Key Attestation Token (KAT) to provide the 49 | assurance needed for the origin of the application identity key. The linking 50 | MUST be performed by hashing the encoded KAT with the SHA512 algorithm, and 51 | using the resulting fingerprint as a challenge when obtaining the PAT. 52 | 53 | ## Verification procedure 54 | 55 | The verification procedure is as follows: 56 | 57 | - Verify that the `parsecCcaAttestation` token is in the right format (as 58 | defined above). 59 | - Verify that the `pat` is a valid Arm CCA attestation token. The exact steps 60 | for this are omitted from this document, and should instead be gathered from 61 | alternative sources. 62 | - Verify that the `kat` and `pat` are correctly linked together. 63 | - Extract the `kat` from the `parsecCcaAttestation` token. 64 | - Hash the `kat` using the SHA512 algorithm. 65 | - Verify that the challenge claim (denoted `cca-realm-challenge` in the 66 | specification) included in the `pat` is equal to the fingerprint computed 67 | in the previous step. 68 | - Verify that the `kat` is using the correct CBOR tag. 69 | - Verify that the `eat_nonce` included in the KAT is the one provided by the 70 | challenger. 71 | - If succesful, return the application identity key described under `cnf`. 72 | 73 | ## CMW encoding 74 | 75 | The CAB produced by Parsec is encapsulated in a [Conceptual Message 76 | Wrapper](https://datatracker.ietf.org/doc/draft-ftbs-rats-msg-wrap/), which is 77 | simply a CBOR-encoded array with the first element identifying the type of the 78 | second element. In this specific case, the type is represented by the string 79 | `application/vnd.parallaxsecond.key-attestation.tpm`, a media type string: 80 | 81 | ``` 82 | [ 83 | "application/vnd.parallaxsecond.key-attestation.cca" 84 | bytes .cbor parsecCcaAttestation 85 | ] 86 | ``` 87 | -------------------------------------------------------------------------------- /demo/relying-party/relying-party.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | # TODO(paulhowardarm) - Some of the contents here are common with the attester Dockerfile, and we should look at 4 | # making either a common base image or a Docker include. 5 | 6 | ENV DEBIAN_FRONTEND=nonintercative 7 | ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig 8 | 9 | RUN apt update 10 | RUN apt install -y autoconf-archive libcmocka0 libcmocka-dev procps 11 | RUN apt install -y iproute2 build-essential git pkg-config gcc libtool automake libssl-dev uthash-dev doxygen libjson-c-dev 12 | RUN apt install -y --fix-missing wget python3 cmake clang 13 | RUN apt install -y libini-config-dev libcurl4-openssl-dev curl libgcc1 14 | RUN apt install -y python3-distutils libclang-6.0-dev protobuf-compiler python3-pip 15 | RUN pip3 install Jinja2 16 | RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata 17 | WORKDIR /tmp 18 | 19 | # Install Rust toolchain 20 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 21 | ENV PATH="/root/.cargo/bin:/opt/rust/bin:${PATH}" 22 | 23 | # Install regular MbedTLS (used for building purposes) 24 | RUN git clone https://github.com/ARMmbed/mbedtls.git 25 | RUN cd mbedtls \ 26 | && git checkout v3.0.0 \ 27 | && ./scripts/config.py crypto \ 28 | && make \ 29 | && make install 30 | ENV MBEDTLS_PATH=/tmp/mbedtls 31 | ENV MBEDTLS_INCLUDE_DIR=$MBEDTLS_PATH/include 32 | 33 | # Build and install QCBOR 34 | RUN git clone https://github.com/laurencelundblade/QCBOR 35 | RUN cd QCBOR \ 36 | && make \ 37 | && make install 38 | 39 | # Build and install t_cose 40 | RUN git clone https://github.com/laurencelundblade/t_cose 41 | RUN cd t_cose \ 42 | && env CRYPTO_LIB=/usr/local/lib/libmbedcrypto.a CRYPTO_INC="-I $MBEDTLS_INCLUDE_DIR" QCBOR_LIB="-lqcbor -lm" make -f Makefile.psa -e \ 43 | && make -f Makefile.psa install 44 | 45 | # Build and install ctoken 46 | RUN git clone https://github.com/laurencelundblade/ctoken.git 47 | RUN cd ctoken \ 48 | && env CRYPTO_LIB=/usr/local/lib/libmbedcrypto.a CRYPTO_INC="-I $MBEDTLS_INCLUDE_DIR" QCBOR_LIB="-lqcbor -lm" make -f Makefile.psa -e \ 49 | && mkdir -p /usr/local/include/ctoken \ 50 | && install -m 644 inc/ctoken/ctoken* /usr/local/include/ctoken \ 51 | && install -m 644 libctoken.a /usr/local/lib 52 | 53 | # Build and install the C client to Veraison, which is a wrapper of the Rust client 54 | RUN git clone https://github.com/veraison/rust-apiclient.git 55 | RUN cd rust-apiclient \ 56 | && cargo build \ 57 | && mkdir -p /usr/local/include/veraison \ 58 | && install -m 644 c-wrapper/veraison_client_wrapper.h /usr/local/include/veraison \ 59 | && install -m 644 ./target/debug/libveraison_apiclient_ffi.a /usr/local/lib 60 | 61 | # Build and install libjwt/jansson, required by c-ear library 62 | RUN git clone --depth 1 --branch v1.15.2 https://github.com/benmcollins/libjwt.git 63 | RUN cd libjwt \ 64 | && mkdir _build \ 65 | && cd _build \ 66 | && cmake -DUSE_INSTALLED_JANSSON=OFF -DJANSSON_BUILD_DOCS=OFF .. \ 67 | && cmake --build . --target install 68 | 69 | # Build and install the C EAR library (Entity Attestation Results) 70 | RUN git clone https://github.com/veraison/c-ear.git 71 | RUN cd c-ear \ 72 | && mkdir _build \ 73 | && cd _build \ 74 | && cmake .. \ 75 | && cmake --build . --target install 76 | 77 | # Build relying party MbedTLS 78 | RUN cd mbedtls \ 79 | && make clean \ 80 | && git reset --hard HEAD \ 81 | && git remote add paulh https://github.com/paulhowardarm/mbedtls.git \ 82 | && git fetch paulh ph-tls-attestation \ 83 | && git checkout ph-tls-attestation \ 84 | && make CFLAGS="-DCTOKEN_LABEL_CNF=8 -DCTOKEN_TEMP_LABEL_KAK_PUB=2500" LDFLAGS="-lctoken -lt_cose -lqcbor -lveraison_apiclient_ffi -lear -ljwt -ljansson -lm -lssl -lcrypto -lgcc_s -lutil -lrt -lpthread -ldl -lc" \ 85 | && install -m 755 programs/ssl/ssl_server2 /usr/local/bin 86 | 87 | WORKDIR /root/ 88 | 89 | CMD ssl_server2 attestation_callback=1 force_version=tls13 auth_mode=required server_port=4433 veraison_endpoint="http://vfe:8080" 90 | -------------------------------------------------------------------------------- /doc/format-definitions/parsec-platform-evidence-tpm.md: -------------------------------------------------------------------------------- 1 | # Parsec TPM explicit platform attestation 2 | 3 | This document covers the technical details of explicit platform attestation 4 | using a TPM2.0. 5 | 6 | The attestation and verification process revolve around a signed attestation 7 | token, and the extra data required to verify it. 8 | 9 | ## TPM Platform Attestation Statement Format 10 | 11 | While for key attestation we can use the format supplied by WebAuthn for that 12 | use-case, and while other WebAuthn attestation statement formats are defined for 13 | platform attestations such as the [Android SafetyNet 14 | format](https://www.w3.org/TR/webauthn-2/#sctn-android-safetynet-attestation), 15 | no such format exists for TPM platform attestation. However, since the format 16 | was made with extensibility in mind, defining our own format within the overall 17 | scheme is straight-forward. 18 | 19 | ``` 20 | parsecTpmPlatStmtFormat = { 21 | tpmVer: "2.0", 22 | kid: bytes, 23 | sig: bytes, 24 | attestInfo: bytes, 25 | } 26 | ``` 27 | 28 | - `tpmVer`: The version of the TPM specification to which the signature 29 | conforms. 30 | - `kid`: A UEID identifier that is shared between attester and verifier to 31 | uniquely identify the AIK (for example, a thumbprint of the public part of 32 | the key). 33 | - `sig`: The attestation signature, in the form of a TPMT_SIGNATURE structure as 34 | specified in 35 | [TPMv2-Part2](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part2_Structures_pub.pdf) 36 | section 11.3.4. 37 | - `attestInfo`: The TPMS_ATTEST structure over which the above signature was 38 | computed, as specified in 39 | [TPMv2-Part2](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part2_Structures_pub.pdf) 40 | section 10.12.8. 41 | 42 | The actual attestation token produced by the TPM - the `TPMS_ATTEST` structure - 43 | is generated by a call to `TPM2_Quote` (see 44 | [TPMv2-Part3](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part3_Commands_pub.pdf) 45 | section 18.4) parameterized by a preconfigured (by the platform owner) list of 46 | PCRs. 47 | 48 | Freshness of the attestation is given by a nonce provided by the RP - 49 | `relyingPartyNonce`. The nonce is passed in as the `extraData` field of 50 | TPMS_ATTEST. 51 | 52 | ## Verification procedure 53 | 54 | The inputs to the verification procedure are as follows: 55 | 56 | - an attestation statement in the format described above 57 | - the `relyingPartyNonce` sent to the attester 58 | - a database of reference values for various platforms 59 | 60 | The steps for verifying the attestation: 61 | 62 | - Verify that the attestation token is valid CBOR conforming to the syntax 63 | defined above and perform CBOR decoding on it to extract the contained 64 | fields. 65 | - Verify that `kid` identifies an endorsed key. 66 | - Verify that the signing algorithm defined in `sig` is consistent with the key 67 | identified by `kid`. 68 | - Verify the `sig` is a valid signature over `attestInfo` using the key 69 | identified by `kid`. 70 | - Verify that `attestInfo` is valid: 71 | - Verify that `magic` is set to TPM_GENERATED_VALUE. 72 | - Verify that `type` is set to TPM_ST_ATTEST_QUOTE. 73 | - Verify that `attested` contains a TPMS_QUOTE_INFO structure as specified in 74 | [TPMv2-Part2] section 10.12.4. 75 | - Verify that `extraData` is set to `relyingPartyNonce`. 76 | - Verify that the platform UUID obtained earlier is valid and represents a 77 | platform found in the database. 78 | - Retrieve the reference values defined for this platform. Compute the digest 79 | of the concatenation of all relevant PCRs using the hash algorithm used 80 | when generating `sig`. The PCRs are concatenated as described in 81 | "Selecting Multiple PCR", section 17.5 of [TPMv2 82 | Part1](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part1_Architecture_pub.pdf). 83 | Verify that this digest is equal to `pcrDigest` in `attested` and that the 84 | hash algorithm defined in `pcrSelect` is aligned with the one in `sig`. 85 | - Note that the remaining fields in the "Standard Attestation Structure" 86 | [TPMv2-Part1] section 31.2, i.e., `qualifiedSigner`, `clockInfo` and 87 | `firmwareVersion` are ignored. These fields MAY be used as an input to 88 | risk engines. 89 | - If successful, return the identity endorsed through `kid`. 90 | -------------------------------------------------------------------------------- /doc/format-definitions/parsec-key-evidence-tpm.md: -------------------------------------------------------------------------------- 1 | # Parsec TPM key attestation data format 2 | 3 | This document describes the data encoding for key attestation tokens produced 4 | using a TPM. The format relies on the [Attestation 5 | Object](https://www.w3.org/TR/webauthn/#sctn-attestation) format defined in 6 | WebAuthN, using the [TPM attestation statement 7 | format](https://www.w3.org/TR/webauthn-2/#sctn-tpm-attestation). All data 8 | structures below are expected to be encoded in [CTAP canonical CBOR 9 | encoding](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#ctap2-canonical-cbor-encoding-form). 10 | 11 | ## TPM Attestation Statement Format 12 | 13 | The attestation statement format defined for TPM backends is defined below. 14 | 15 | CDDL representation of Parsec TPM Key Attestation Statement Format: 16 | 17 | ``` 18 | parsecTpmKeyStmtFormat = { 19 | tpmVer: "2.0", 20 | kid: bytes, 21 | sig: bytes, 22 | certInfo: bytes, 23 | pubArea: bytes 24 | } 25 | ``` 26 | 27 | - `tpmVer`: The version of the TPM specification to which the signature 28 | conforms. 29 | - `kid`: A UEID identifier that is shared between attester and verifier to 30 | uniquely identify the AIK (for example, a thumbprint of the public part of 31 | the attesting key used to produce `sig`). 32 | - `sig`: The attestation signature, in the form of a TPMT_SIGNATURE structure as 33 | specified in 34 | [TPMv2-Part2](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part2_Structures_pub.pdf) 35 | section 11.3.4. 36 | - `certInfo`: The TPMS_ATTEST structure over which the above signature was 37 | computed, as specified in 38 | [TPMv2-Part2](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part2_Structures_pub.pdf) 39 | section 10.12.8. 40 | - `pubArea`: The TPMT_PUBLIC structure (see 41 | [TPMv2-Part2](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part2_Structures_pub.pdf) 42 | section 12.2.4) used by the TPM to represent the credential public key. 43 | 44 | The signature (`sig`) described above is that produced using the TPM2_Certify 45 | operation. In order to ensure freshness, the WebAuthN spec mandates the use of 46 | `extraData` in the operation. 47 | 48 | Freshness of the attestation is given by a nonce provided by the RP - 49 | `relyingPartyNonce`. The nonce is passed in as the `extraData` field of 50 | TPMS_ATTEST. 51 | 52 | ## Verification procedure 53 | 54 | Given the verification procedure inputs `parsecTpmKeyStmtFormat` and 55 | `relyingPartyNonce`, the verification procedure is as follows: 56 | 57 | - Verify that `parsecTpmKeyStmtFormat` is valid CBOR conforming to the syntax 58 | defined above and perform CBOR decoding on it to extract the contained 59 | fields. 60 | - Verify that `kid` identifies an endorsed key. 61 | - Verify that the signing algorithm defined in `sig` is consistent with the key 62 | identified by `kid`. 63 | - Verify the `sig` is a valid signature over `certInfo` using the key identified 64 | by `kid`. 65 | - Validate that `certInfo` is valid: 66 | - Verify that `magic` is set to TPM_GENERATED_VALUE. 67 | - Verify that `type` is set to TPM_ST_ATTEST_CERTIFY. 68 | - Verify that `extraData` is set to `relyingPartyNonce`. 69 | - Verify that `attested` contains a TPMS_CERTIFY_INFO structure as specified 70 | in 71 | [TPMv2-Part2](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part2_Structures_pub.pdf) 72 | section 10.12.3, whose name field contains a valid Name for `pubArea`, as 73 | computed using the algorithm in the `nameAlg` field of `pubArea` using the 74 | procedure specified in [TPMv2-Part1] section 16. 75 | - Note that the remaining fields in the "Standard Attestation Structure" 76 | [TPMv2 77 | Part1](https://trustedcomputinggroup.org/wp-content/uploads/TCG_TPM2_r1p59_Part1_Architecture_pub.pdf) 78 | section 31.2, i.e., `qualifiedSigner`, `clockInfo` and `firmwareVersion` 79 | are ignored. These fields MAY be used as an input to risk engines. 80 | - Perform the policy-dictated checks relating to the state of the identity key 81 | using `pubArea`. These checks could include, for example: 82 | - Verifying that the key was not imported by checking that the 83 | `sensitiveDataOrigin` field is set in `objectAttributes`. 84 | - Verifying that the key cannot be exported by checking that the `fixedTPM` 85 | field is set in `objectAttributes`. 86 | - If successful, return the identity endorsed through `kid`. 87 | 88 | **Note**: The above steps only serve as verification for the key attestation 89 | steps, **not** for platform attestation. 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hardware-Backed Attestation in TLS 2 | 3 | ## Welcome! 4 | 5 | This repository holds miscellaneous materials related to a proof-of-concept of the "Using 6 | Attestation in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)" internet 7 | draft ([link](https://datatracker.ietf.org/doc/draft-fossati-tls-attestation/)). 8 | 9 | ## What's Here? 10 | 11 | This repository is a central space for open collaboration on the proof-of-concept. 12 | 13 | There isn't very much source code held directly in this repository. Code for the contributing 14 | components is pulled from other GitHub repositories, some of which have been branched or forked in 15 | order to develop the code for this specific proof-of-concept. What you'll find here are the Docker 16 | recipes and build scripts that pull all of the strands together into a complete, end-to-end demo 17 | that is easy to build and execute. There is also a body of design documents that have been used to 18 | establish the interfaces between individual components for those behaviours that are not otherwise 19 | governed by the above internet draft and related standards. 20 | 21 | ## How Can I Run the Demo? 22 | 23 | Everything that you need is here. Simply [install Docker](https://docs.docker.com/get-docker/) and 24 | follow this simple sequence of [steps](demo/README.md). 25 | 26 | ## The Background: Why Are We Doing This? 27 | 28 | Authentication of remote workloads and services is a difficult process with high security stakes. 29 | Software-based solutions (such as those leveraging PKI) which are currently the norm fail to 30 | reliably convey the security state of the workload in the face of impersonation and persistent 31 | attackers. This is most apparent in cases where the underlying platform is particularly exposed and 32 | out of the control of the owner, such as in cloud computing and IoT. Hardware features have thus 33 | been introduced to enable remotely verifiable “trust metrics” using attestation. Such 34 | hardware-backed features provide a cryptographic proof of the software stack, and strong guarantees 35 | that the cryptographic keys used by the workload are properly protected from exfiltration. 36 | 37 | However, remote attestation comes with its own need to share and verify metadata, which must be 38 | engineered into existing software. While the protocol used to exchange this metadata is largely 39 | irrelevant to the actual attestation procedure, its positioning in the networking stack can enable 40 | specific use-cases and enhance the performance of the entire system. An appealing approach is to 41 | allow the creation of secure channels (such as TLS connections) using attestation metadata as the 42 | authentication mechanism. Current designs either rely on running an attestation protocol on top of 43 | an existing secure channel, or modify the semantics of certificates to convey attestation 44 | information when establishing the secure channel. 45 | 46 | Our work focuses on standardizing attestation metadata as first-class credentials in TLS. This new 47 | approach allows native, opaque metadata to be conveyed for authentication during the TLS handshake 48 | instead of (or together with) x509 certificates. Supporting flexibility in deployments without 49 | compromising on security has been a prime goal. Thus, we aim to cater to interaction models in which 50 | either the client, the server, or both can attest themselves, leveraging any hardware backend, and 51 | using different verification topologies. 52 | 53 | To showcase the standardization effort, we are also developing an open-source, end-to-end 54 | proof-of-concept implementation of one of the interaction models supported. The PoC builds on top of 55 | two Linux Foundation projects – [Parsec](https://parsec.community/) to abstract the root of trust 56 | attestation primitives, and [Veraison](https://github.com/veraison) to consume and verify the new 57 | evidence formats. It also enhances [mbedTLS](https://github.com/Mbed-TLS/mbedtls) to support a 58 | subset of the newly defined TLS extensions. As a hardware root of trust, the proof of concept is 59 | currently using a TPM2.0, with support for others being considered. 60 | 61 | ## Additional Documentation 62 | 63 | This repository maintains the following additional documents that relate to some internal details of 64 | the proof-of-concept. These documents are collaborative resources that are intended to be useful for 65 | the individuals and teams who are working on the implementation. They are not required reading if 66 | you only want to run the demo. 67 | 68 | - [Getting the mbedTLS prototype installed on MacOSX](doc/mbedtls-build.md) 69 | - [Getting the Parsec prototype installed](doc/parsec-build.md) 70 | - [TPM Parsec Evidence](doc/parsec-evidence-tpm.md) 71 | - [CCA Parsec Evidence](doc/parsec-evidence-cca.md) 72 | - [How to synthesise attestation results produced by Veraison](doc/play-by-ear.md) 73 | -------------------------------------------------------------------------------- /doc/parsec-build.md: -------------------------------------------------------------------------------- 1 | # Getting the Parsec prototype installed 2 | 3 | Building and running the Parsec PoC involves two independent components: the Parsec service which 4 | interacts with the RoT, and the client library that the TLS implementation uses to communicate with 5 | the service. More information about the Parsec service can be found 6 | [here](https://parallaxsecond.github.io/parsec-book/). 7 | 8 | Both components require a Rust toolchain to be available locally. You can find information on 9 | installing Rust [here](https://www.rust-lang.org/tools/install). 10 | 11 | ## Parsec service 12 | 13 | This section captures the steps needed to set up the Parsec service from scratch. 14 | 15 | ### Prerequisites 16 | 17 | The Parsec service serves as an abstraction layer over Root of Trust (RoT) APIs, meaning it must be 18 | able to interact with those APIs. The service executable thus requires linking against the correct 19 | libraries that enable communication with the RoT. In the case of our PoC, the RoT of choice is a 20 | TPM2.0. We recommend using: 21 | 22 | - [tpm2-tss](https://github.com/tpm2-software/tpm2-tss) libraries - version 2.4.6 23 | - [ibmswtpm2](https://sourceforge.net/projects/ibmswtpm2/) - version 1637 24 | 25 | Follow the steps described [here](https://github.com/tpm2-software/tpm2-tss/blob/2.4.6/INSTALL.md) 26 | to build and install the TSS2 libraries. No special configuration options are needed. 27 | 28 | Build and install the TPM2 tools following the instructions found 29 | [here](https://tpm2-tools.readthedocs.io/en/latest/#build-and-installation-instructions). (Note: the 30 | `tpm2-tabrmd` is not needed for our use-case.) 31 | 32 | To build and install the software TPM emulator (taken and modified from 33 | [here](https://github.com/tpm2-software/tpm2-software-container/blob/master/modules/ibmtpm1637.m4)): 34 | 35 | ```bash 36 | cd /tmp 37 | wget -L "https://downloads.sourceforge.net/project/ibmswtpm2/ibmtpm1637.tar.gz" 38 | mkdir -p ibmtpm1637 39 | tar xv --no-same-owner -f ibmtpm1637.tar.gz -C ibmtpm1637 40 | rm ibmtpm1637.tar.gz 41 | cd ibmtpm1637/src 42 | sed -i 's/-DTPM_NUVOTON/-DTPM_NUVOTON $(CFLAGS)/' makefile 43 | CFLAGS="-DNV_MEMORY_SIZE=32768 -DMIN_EVICT_OBJECTS=7" make -j$(nproc) 44 | sudo cp tpm_server /usr/local/bin 45 | rm -fr /tmp/ibmtpm1637 46 | ``` 47 | 48 | ### Building the service 49 | 50 | To fetch and build the Parsec service: 51 | 52 | ```bash 53 | git clone -b attested-tls https://github.com/ionut-arm/parsec.git 54 | cd parsec 55 | cargo build --features=tpm-provider,direct-authenticator 56 | export PARSEC=$(pwd)/target/debug/parsec 57 | ``` 58 | 59 | Once the build steps are done, the Parsec executable can be found at `$PARSEC`. 60 | 61 | ### Running the service 62 | 63 | To run the service, the software TPM2.0 server must first be started: 64 | 65 | ```bash 66 | pushd /tmp 67 | tpm_server & 68 | tpm2_startup -c -T mssim 69 | popd 70 | ``` 71 | 72 | The Parsec service relies on a configuration file to instruct it what type of RoT to use, whether to 73 | provision an attesting key and so on. An example configuration file that can be used to run this PoC 74 | can be found [here](parsec-config.toml). Once this file has been created, say at path 75 | `$PARSEC_CONFIG`, the service can be run: 76 | 77 | ```bash 78 | RUST_LOG=info $PARSEC -c $PARSEC_CONFIG 79 | ``` 80 | 81 | ### Cleaning up 82 | 83 | When stopping the service, a few extra steps need to be taken. 84 | 85 | The service can simply be stopped through `pkill`, followed by the software TPM: 86 | 87 | ```bash 88 | pkill parsec 89 | pkill tpm_server 90 | ``` 91 | 92 | If the system needs to be preserved to continue the testing at a later point, then no other changes 93 | need to be made (though it's important to note that all persistent values are in `/tmp/`, so a 94 | reboot could have them deleted). Otherwise, to fully clean up the data and allow a fresh run: 95 | 96 | ```bash 97 | rm -rf /tmp/mappings 98 | rm /tmp/NVChip 99 | rm /tmp/parsec.sock 100 | ``` 101 | 102 | Build artifacts and executables will still be available in the repo directory. 103 | 104 | ## Parsec client 105 | 106 | Parsec clients exist for multiple languages, however the PoC used here revolves around the C client. 107 | In particular, because of the cryptography-centric nature of Parsec APIs, and because of its origin 108 | in the PSA Cryptography API, most of the Parsec functionality is available in C via the PSA Crypto 109 | API, via a Secure Element (SE) driver interface that plugs into the PSA Crypto implementation. 110 | However, because attestation is *not* part of the PSA Crypto API, attestation primitives are exposed 111 | separately, directly to C clients. 112 | 113 | In order to build this hybrid C client library, a local copy of mbedTLS is needed: 114 | 115 | ```bash 116 | git clone https://github.com/ARMmbed/mbedtls.git 117 | cd mbedtls 118 | git checkout v3.0.0 119 | ./scripts/config.py crypto 120 | ./scripts/config.py set MBEDTLS_PSA_CRYPTO_SE_C 121 | export MBEDTLS_PATH=$(pwd) 122 | ``` 123 | 124 | After which the Parsec client can be built: 125 | 126 | ```bash 127 | git clone -b attested-tls https://github.com/ionut-arm/parsec-se-driver.git 128 | cd parsec-se-driver 129 | MBEDTLS_INCLUDE_DIR=$MBEDTLS_PATH/include cargo build 130 | ``` 131 | 132 | And the library can then be found in `./target/debug/libparsec_se_driver.a`. 133 | -------------------------------------------------------------------------------- /demo/attester/attester.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | ARG TARGETARCH 4 | ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig 5 | ENV DEBIAN_FRONTEND noninteractive 6 | 7 | RUN apt update 8 | RUN apt install -y autoconf-archive libcmocka0 libcmocka-dev procps 9 | RUN apt install -y iproute2 build-essential git pkg-config gcc libtool automake libssl-dev uthash-dev doxygen libjson-c-dev 10 | RUN apt install -y --fix-missing wget python3 cmake clang 11 | RUN apt install -y libini-config-dev libcurl4-openssl-dev curl libgcc1 12 | RUN apt install -y python3-distutils libclang-12-dev protobuf-compiler python3-pip 13 | RUN apt install -y openssl 14 | RUN pip3 install Jinja2 15 | RUN apt-get -y install tzdata 16 | 17 | WORKDIR /tmp 18 | 19 | # Download and install TSS 2.0 20 | RUN git clone https://github.com/tpm2-software/tpm2-tss.git --branch 3.2.2 21 | RUN cd tpm2-tss \ 22 | && ./bootstrap \ 23 | && ./configure \ 24 | && make -j$(nproc) \ 25 | && make install \ 26 | && ldconfig 27 | RUN rm -rf tpm2-tss 28 | 29 | # Download and install TPM 2.0 Tools verison 4.1.1 30 | RUN git clone https://github.com/tpm2-software/tpm2-tools.git --branch 4.1.1 31 | RUN cd tpm2-tools \ 32 | && ./bootstrap \ 33 | && ./configure --prefix=/usr \ 34 | && make -j$(nproc) \ 35 | && make install 36 | RUN rm -rf tpm2-tools 37 | 38 | # Download and install software TPM 39 | ARG ibmtpm_name=ibmtpm1637 40 | RUN wget -L "https://downloads.sourceforge.net/project/ibmswtpm2/$ibmtpm_name.tar.gz" 41 | RUN sha256sum $ibmtpm_name.tar.gz | grep ^dd3a4c3f7724243bc9ebcd5c39bbf87b82c696d1c1241cb8e5883534f6e2e327 42 | RUN mkdir -p $ibmtpm_name \ 43 | && tar -xvf $ibmtpm_name.tar.gz -C $ibmtpm_name \ 44 | && chown -R root:root $ibmtpm_name \ 45 | && rm $ibmtpm_name.tar.gz 46 | WORKDIR $ibmtpm_name/src 47 | RUN sed -i 's/-DTPM_NUVOTON/-DTPM_NUVOTON $(CFLAGS)/' makefile 48 | RUN CFLAGS="-DNV_MEMORY_SIZE=32768 -DMIN_EVICT_OBJECTS=7" make -j$(nproc) \ 49 | && cp tpm_server /usr/local/bin 50 | RUN rm -rf $ibmtpm_name/src $ibmtpm_name 51 | 52 | WORKDIR /tmp 53 | 54 | # Install Rust toolchain 55 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 56 | ENV PATH="/root/.cargo/bin:/opt/rust/bin:${PATH}" 57 | 58 | # Install Parsec service 59 | RUN git clone -b attested-tls https://github.com/ionut-arm/parsec.git \ 60 | && cd parsec \ 61 | && git checkout 1ac2060531b391ff1f335369dc4d1e4f17aee1aa \ 62 | && cargo build --release --features=tpm-provider \ 63 | && cp ./target/release/parsec /usr/bin/ 64 | RUN mkdir /etc/parsec/ 65 | COPY parsec-config.toml /etc/parsec/config.toml 66 | 67 | # At runtime, Parsec is configured with the socket in /tmp/ 68 | ENV PARSEC_SERVICE_ENDPOINT="unix:/tmp/parsec.sock" 69 | 70 | # Install MbedTLS (used for building purposes) 71 | RUN git clone https://github.com/ARMmbed/mbedtls.git 72 | RUN cd mbedtls \ 73 | && git checkout v3.0.0 \ 74 | && ./scripts/config.py crypto \ 75 | && ./scripts/config.py set MBEDTLS_PSA_CRYPTO_SE_C \ 76 | && make \ 77 | && make install 78 | ENV MBEDTLS_PATH=/tmp/mbedtls 79 | ENV MBEDTLS_INCLUDE_DIR=$MBEDTLS_PATH/include 80 | 81 | # Build and install the Parsec C client 82 | RUN git clone -b attested-tls https://github.com/ionut-arm/parsec-se-driver.git 83 | RUN cd parsec-se-driver \ 84 | && cargo build --release \ 85 | && install -m 644 target/release/libparsec_se_driver.a /usr/local/lib \ 86 | && mkdir -p /usr/local/include/parsec \ 87 | && install -m 644 include/* /usr/local/include/parsec 88 | 89 | # Build and install QCBOR 90 | RUN git clone https://github.com/laurencelundblade/QCBOR 91 | RUN cd QCBOR \ 92 | && make \ 93 | && make install 94 | 95 | # Build and install t_cose 96 | RUN git clone https://github.com/laurencelundblade/t_cose 97 | RUN cd t_cose \ 98 | && env CRYPTO_LIB=/usr/local/lib/libmbedcrypto.a CRYPTO_INC="-I $MBEDTLS_INCLUDE_DIR" QCBOR_LIB="-lqcbor -lm" make -f Makefile.psa -e \ 99 | && make -f Makefile.psa install 100 | 101 | # Build and install ctoken 102 | RUN git clone https://github.com/laurencelundblade/ctoken.git 103 | RUN cd ctoken \ 104 | && env CRYPTO_LIB=/usr/local/lib/libmbedcrypto.a CRYPTO_INC="-I $MBEDTLS_INCLUDE_DIR" QCBOR_LIB="-lqcbor -lm" make -f Makefile.psa -e \ 105 | && mkdir -p /usr/local/include/ctoken \ 106 | && install -m 644 inc/ctoken/ctoken* /usr/local/include/ctoken \ 107 | && install -m 644 libctoken.a /usr/local/lib 108 | 109 | # Build attester MbedTLS 110 | RUN cd mbedtls \ 111 | && make clean \ 112 | && git reset --hard HEAD \ 113 | && git remote add ionut https://github.com/ionut-arm/mbedtls.git \ 114 | && git fetch ionut parsec-attestation \ 115 | && git checkout f4ac7593826a506fc509c83cad73786acab1d442 \ 116 | && make CFLAGS="-DCTOKEN_LABEL_CNF=8 -DCTOKEN_TEMP_LABEL_KAK_PUB=2500" LDFLAGS="-lctoken -lt_cose -lqcbor -lm -lparsec_se_driver -lpthread -ldl" \ 117 | && install -m 755 programs/ssl/ssl_client2 /usr/local/bin 118 | 119 | # Install Parsec tool 120 | RUN git clone -b attested-tls https://github.com/ionut-arm/parsec-tool.git \ 121 | && cd parsec-tool \ 122 | && git checkout 45feaf20fcb0a886b2e20f4f19333b86608e215d \ 123 | && cargo build --release \ 124 | && cp target/release/parsec-tool /usr/bin/parsec-tool 125 | 126 | # Install Go toolchain 127 | RUN wget -c https://dl.google.com/go/go1.20.4.linux-$TARGETARCH.tar.gz -O - | tar -xz -C /usr/local 128 | ENV PATH $PATH:/usr/local/go/bin:/root/go/bin 129 | 130 | # Install cocli 131 | RUN go install github.com/veraison/corim/cocli@rc0-v2.0.0 132 | 133 | # Introduce scripts 134 | COPY endorse.sh /root/ 135 | COPY handshake.sh /root/ 136 | COPY start.sh /root/ 137 | 138 | # Introduced platform endorsement templates 139 | COPY comid-pcr.json /root/ 140 | COPY corim.json /root/ 141 | 142 | WORKDIR /root/ 143 | 144 | CMD /root/start.sh 145 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------