Try   HackMD

Feature: Remote attestation

WARNING: DRAFT / WIP

EXPIRES JAN 5 2022! FOR DISCUSSION / ENTERTAINMENT PURPOSES ONLY! KEEP OUT OF REACH OF CHILDREN! DO NOT EAT! LIGHT FUSE, GET AWAY! BEWARE OF THE LEOPARD!

Abstract

Remote Attestation is fundamental to Enarx's confidential computing model. This document describes the plan for performing hardware-based attestation from an Enarx Keep to a remote Verifier.

Motivation

Verifying the attestation message is how we establish that a remote keep is actually executing inside of a TEE and that the contents of the keep (and the connection to it) are private - unreadable by the remote host or anyone in the middle.

A new Keep also has no secrets or identifying information, so a successful attestation should provide the Keep with something it can use for identification and authorization during the rest of its lifetime.

Goals

Make a TLS connection to a remote Verifier service and send an attestation message enhanced with other data (proposed/placeholder name: "Keep Identity Verification Request", or KIVR[1]), which contains at least:

  1. An attestation message verifying the keep's contents,
  2. Channel Binding data verifying the endpoint of the TLS session,
  3. An X.509 public key / Certificate Signing Request, to be signed by the Verifier so the Keep can use it for later identification and authentication.

This request has to be carefully constructed to ensure that the attestation message can be used to verify the validity of the public key, and that the public key can be used to verify the rest of the contents of the request.

Rationale

Why X.509?

X.509 certificates are the most widely-used system for managing identity and authorization, so it makes sense to use them here.

COSE is a work-in-progress standard for native CBOR encoding of certificates etc, and might someday be able to replace X.509, but most of the world still uses X.509 certificates for this and interoperability would suffer if we didn't use that.

Why not just put all the data in X.509 CSR extensions?

In current implementations, the format and contents of the CPU-signed attestation message is essentially an opaque binary blob, typically 1 or 2 memory pages (4-8kb) in size. Inserting this into an X.509 CSR might be technically feasible, but would require using extensions that are under-specified and may not be supported by existing tools.

There are also X.509 implementations that reject or mishandle "large" certificates - Mbed TLS, for example, limited extension size and CSR size to 2kb and 4kb.

Why not use TEEP?

The IETF draft "Trusted Execution Environment Provisioning (TEEP) Protocol" is conceptually very similar and has the same general goals, but is designed for a much broader range of use cases, which makes it much more complex. It's also a work-in-progress draft, which makes it a moving target.

We're definitely tracking the progress of TEEP, and will continue to take inspiration from it and strive for interoperability wherever possible, but defining our own simple, single-message attestation process gives us freedom and flexibility to get things working quickly.

Specification

This section describes what we'll need to define and implement to make attestation work.

Keep instantiation

The Enarx host tools should be able to enarx start a new keep and pass it two pieces of data:

  1. The URL of the Verifier to contact, and
  2. A CA certificate (or bundle) to be used for certificate validation when connecting to the Verifier.

Proposed CLI flags / environment variables:

  1. --verifier=URL or ENARX_VERIFIER=URL
  2. --cacert=PEMFILE or ENARX_CA_BUNDLE=PEMFILE (like curl --cacert)

Attestation Procedure

Once a new Keep has started, it performs the following steps to attest itself to the Verifier.

  1. Generate a public/private keypair + CSR
    • Keys could be any type usable by TLS 1.3; RSA is probably the easy choice
  2. Request attestation from the CPU, injecting a hash of the CSR
    • Details of this process are CPU/backend dependent
    • Some backends (which?) limit injected data to 64 bytes, so SHA256(csr) is probably the best choice
  3. Establish a HTTPS/TLS 1.3 connection to the Verifier
    • This generates the TLS 1.3 "exporter" channel binding key material
  4. Construct Keep Identity Verification Request (KIVR)
    • csr: CSR, attest: Attestation message
    • See below for KIVR format details
  5. Add Channel Binding to KIVR
    • binding: HMAC(exporter, pubkey)
      • RFC2104 notes that using keys smaller than the output size of the hash function "is strongly discouraged as it would decrease the security strength of the function."
      • exporter data is 32 bytes, so HMAC-SHA256 might be a good choice
  6. Send KIVR to Verifier
    • Probably just an HTTP POST, but the Verifier specification hasn't been written yet, so the endpoint / method / etc are TBD.
  7. If verification is successful, Verifier will return a signed X.509 certificate
    • Certificate is generated from the CSR but may contain additional data/metadata
    • Details to be defined in the Verifier spec

Keep Identity Verification Request

The KIVR format is a simple, extensible metadata bundle with a well-defined canonical encoding, specifically for the purpose of sending attestation data and metadata from a TEE/Keep to a Verifier.

This section defines version 0 of the KIVR format.

  • Proposed format: Deterministically Encoded CBOR map with string or int labels
    • String labels ensure easy mapping to/from JSON objects, Rust structs, etc.
    • Similar to COSE and TEEP we also define integer labels for standard items
    • TBD: complete details for deterministic encoding:
      • Which RFC8949 "Canonical CBOR" key-sorting algorithm do we use?
      • Do we allow/require CBOR tags for certain keys?
      • Are string/int labels equivalent? Which is the canonical form?
  • Identification, versioning, and metadata
    • CBOR tag ID: (TBD)
    • Negative integers and string labels starting with "." are reserved for metadata
    • .ver / -1: KIVR version label. Optional; assume 0 if missing
      • NOTE: TEEP uses 3 as a label for version. Should we follow that?
  • Currently-defined labels and values
    • csr: Required. X.509 Certificate Signing Request
      • TODO: DER or PEM? allow either and let verifier guess? Use CBOR type to define format (Text=PEM, Binary=DER)?
    • attest: Required. Binary data; the attestation report generated by the host CPU.
    • binding: Required. Binary data. The HMAC data that binds this request to the TLS session.
      • TODO: make self-describing, or add HMAC type/details in separate key?

Table of defined KIVR labels

Int Name CBOR type Value/format Purpose/Notes
-1 .ver int KIVR version Assume 0 if not present
0 - - - Reserved
1 csr binary X.509 CSR Certificate Signing Request from Keep
2 attest binary CPU-generated blob Attestation of Keep memory contents
3 channel binary HMAC output Channel binding / verify TLS endpoint

Rejected Ideas / Non-Goals

Pre-Shared Keys / Embedded Certificates

Our trust model assumes that everything inside the keep before attestation is complete will be exposed to the host system or other eavesdroppers. Thus the keep image must not contain any secrets or other private data.

Future work

Certificate annotation for verifying the workload

Some use cases would require that the issued certificate contain a hash or other data that identifies intended / acceptable workloads. This procedure could be defined in a later specification but isn't required here.

Full schema / format definition for the KIVR format

Once we've sorted out details of the KIVR contents / format, it would probably make sense to write up a CDDL definition for it.

References / Further reading


  1. Pronounced like "liver", but with a 'k' - "kivver" ↩︎