jeremyhahn / go-trusted-platform

Platform software for Trusted Computing - TPM 2.0, Certificate Authority, and Web Services required to perform Local and Remote Attestation, provision, deploy, manage, and secure connected devices and networks at scale.
Apache License 2.0
5 stars 1 forks source link

ACME device attestation and TPM residency of AKs #2

Closed NickDarvey closed 2 weeks ago

NickDarvey commented 3 weeks ago

I see you're working on implementing acme-device-attest. I'm curious, how does that flow can guarantee TPM residency of AKs as described in TPM 2.0 Keys for Device Identity and Attestation?

jeremyhahn commented 2 weeks ago

The device-attest-01 challenge does not implement a workflow mentioned in the TPM 2.0 Keys for Device Identity and Attestation. It implements a workflow that more closely aligns with the WebAuthn standard which supports more than just TPMs.

I've created a new challenge type for this project, device-01, which does follow the TCG specs you mentioned. It currently supports both of the Identity Provisioning use cases mentioned for OEM's, and I will add more in the future.

I actually just got the first end to end test working today. You can be the first to give it a try and let me know what you think:

ACME Server:

cd /tmp
mkdir server
cd server
cp ~/sources/go-trusted-platform/configs/platform/config.debug.server.yaml config.yaml
~/sources/go-trusted-platform/tpadm webservice \
    --debug \
    --init \
    --platform-dir trusted-data \
    --config-dir trusted-data/etc \
    --log-dir trusted-data/log \
    --ca-dir trusted-data/ca \
    --raw-so-pin test \
    --raw-pin test

ACME Client:

cd /tmp
mkdir client
cd client
cp ~/sources/go-trusted-platform/configs/platform/config.debug.client.yaml config.yaml
~/sources/go-trusted-platform/tpadm webservice \
    --debug \
    --init \
    --platform-dir trusted-data \
    --config-dir trusted-data/etc \
    --log-dir trusted-data/log \
    --ca-dir trusted-data/ca \
    --raw-so-pin test \
    --raw-pin test

Both the ACME server and client will start up using the embedded TPM simulator. The platform software fully provisions the TPM, including an EK, SRK, IAK, and IDevID keys. Then, the client will see the ACME client config and use the configured ACME server endpoint to request an EK, IAK, IDevID certificates (with all of the proper TCG OIDs) and finally, a web server TLS certificate using a standalone pkcs8 key.

The EK is retrieved using a custom endorse-01 challenge to support automated OEM factory device provisioning use cases.

The IAK and IDevID is retrieved using a custom device-01 challenge that uses the TCG workflow you mentioned above, using a modified version of the http-01 challenge, where instead of returning the key authorization as in http-01, the TCG-CSR-IDEVID is returned with the key authorization used in the qualifying attestation data.

Finally, a TLS certificate is requested & issued, using a separate pkcs8 key, for its web services which will be running on localhost:8444. You can inspect the certificate in the browser to confirm it's issued by the Enterprise CA.

I've run into an issue fully implementing the device-attest-01 challenge and sent @brandonweeks an email to address the issue. The device-attest-01 requires sending the attestation statement to the ACME server during the "Accept" call, which is not compliant with RFC-8555. As such, the Golang ACME client library I'm using does not support sending the device-attest-01 payload, and is in fact hard coded to send an empty object per RFC 8555. It looks like in the past this library did support sending a custom payload, but that support was removed in the latest versions.

It seems like there are 3 paths to pursue with device-attest-01:

  1. Piggyback http-01 or dns-01 similar to how I implemented device-01 , or use some other async procedure.

  2. Change RFC 8555 to support an ACME client sending the challenge response along with the "Accept" response. I think this is the most desirable because there is no reason to require an async verification process for device attestations. This eliminates the need for clients to open firewall ports or make configuration changes to verify anything, since everything needed is contained within the attestation. This also shaves off a round trip in the process which reduces latency.

  3. Leave the spec as is and force ACME clients to deviate from RFC 8555. I actually started to go down this path, but ran into a wall when the JOSE library being used to sign JWS requests is base64 encoding the ACME replay-nonce, causing the validation to fail, and there is no way to change the behavior in the library. That means going further down the rabbit hole, creating a custom serializer, signer, etc, finding a new ACME client library, or creating an entire low level client to replace the crypto/acme client altogether. Ugh.

I just pushed the latest code to the feature/acme branch if you want to check it out.

NickDarvey commented 2 weeks ago

Ahh! Okay, that makes sense. Great timing! I'll give this a go and read about your endorse-01 and device-01 challenges.

brandonweeks commented 2 weeks ago

I'll take a closer look when I get a chance but drive by comment in the mean time:

It implements a workflow that more closely aligns with the WebAuthn standard which supports more than just TPMs.

The intent is not to align with WebAuthn per se. It is just that every other attestation schemed covered by the acme-device-attest document is able to attest without prerequisite steps.

In order to make the TPM specifications generalizable to both enterprise and privacy preserving use cases the concept of a third party attestation/privacy CA is included. Until a subject device has been issued an AK certificate, it is unable to participate in any attestation flow, including the ACME flow described by acme-device-attest.

There are a lot of considerations in implementing an attestation CA, including important privacy considerations. It seems inappropriate for a acme-device-attest to prescribe a specific design, especially in such a short document.

NickDarvey commented 2 weeks ago

I notice that the ACME client starts as a web server

tpadm webservice

Is this necessary? Does the client have to be addressable for endorse-01 and device-01 to work?

jeremyhahn commented 2 weeks ago

The ACME client is starting with the web service because the web server needs a TLS certificate before it can start. Therefore, it fires up the ACME client first to request it, but before that can happen, the attestation procedure outlined above takes place so that when a TLS certificate is requested, the ACME server can perform the device-attest-01 challenge. As Brandon mentioned, that challenge expects the ACME server to already know about the permanent-identifier and how to verifiy the attestation statement, which implies some kind of enrollment has already happened. For this reason, endorse-01 and device-01 were created, to allow the device to be enrolled first, so the CA has the root certificates, permanent-identifiers or whatever else it needs to verify the device-attest-01 challenge, and also verify that it is talking to a known device (via the IDevID cert).

Yes, both endorse-01 and device-01 require HTTP as they are slightly modified versions of http-01, however, they start their own web servers and tear them down once the challenge has been completed. Once the TLS certificate is obtained, the main web services are started.

I currently have http-8080 and http-8081 as challenge options to work around privileged port requirements, and will probably just consolidate them into something like http-x that reads the port from the config file, and make endorse-01 and device-01 ports configurable in the same manner, that way anyone in control of the Enterprise CA deployment can define the challenge to happen on any port they want.

NickDarvey commented 2 weeks ago

Yes, both endorse-01 and device-01 require HTTP as they are slightly modified versions of http-01

Ahh okay. That makes sense, though it might make things tricky in my scenario where my devices are NATed.

jeremyhahn commented 2 weeks ago

The same efforts to make the NATed devices work would be required for http-01 as well. dns-01 doesn't seem like a good candidate due to the attestation statement payload size. The most convenient solution for device attestation is to send the attestation payload when the client accepts the challenge, as defined in device-attest-01. I also prefer this approach and don't see any reason why it can't / shouldn't be supported.

If you have other suggestions for a challenge type that would make the an async validation process more automation and administration friendly for NATed devices, I'm open to suggestions.

I pinged the Boulder team. I'm interested in their thoughts on this as well. https://github.com/letsencrypt/boulder/issues/7798

NickDarvey commented 2 weeks ago

Cheers @jeremyhahn — that sounds sensible. I've subscribed to that issue, will see what comes of it. I look fowrard to seeing what you do in this space

jeremyhahn commented 1 week ago

I confirmed via the ACME mailing list that the empty payload object is not a strict RFC requirement and opened a PR to allow the golang acme client to support our use case.

https://github.com/golang/crypto/pull/305 https://go-review.googlesource.com/c/crypto/+/628635

Upon merge, I'll finish wiring up the last remaining piece for acme-device-attest and refactor the custom enrollment challenges to reduce a round trip.

I'm going to leave this issue open until complete.

hslatman commented 1 week ago

You may be able to use acmez as the ACME client in the meantime. An example using it with device-attest-01 is available here: https://github.com/mholt/acmez/blob/527e47cae3f84fa3a92d1d9b9c21c5eb0b44359a/examples/attestation/main.go.