parallaxsecond / rust-tss-esapi

TSS 2.0 Enhanced System API (ESAPI) Rust wrapper
https://docs.rs/tss-esapi/
Apache License 2.0
86 stars 51 forks source link

Add example of certify #458

Closed Firstyear closed 5 months ago

Firstyear commented 11 months ago

This is the foundations of an example showing how to use "certify". However, it's worth noting that today it doesn't work because of various subtleties that I don't understand :(

It's also quite messy because there appear to be some issues around the sessions in the ak/ek abstractions.

Any advice on how to fix and improve the example would be most welcome! As well, perhaps there are some opportunities here to improve the abstractions?

ionut-arm commented 11 months ago

Hey! I've implemented something like this in our abstraction layer for our work on attestation in TLS, it's in my fork on the attested-tls branch. More specifically, you can have a look at the TransientKeyContext::certify implementation that uses TpmStatement as the type holding onto all the information necessary to verify the result of the attestation. This hasn't been upstreamed yet, and I'm not sure if we will upstream it in the end, but it might be a good place to start.

One thing to note is that the docs are a bit outdated - I've modified the exact serialisation format so it's not aligned with the WebAuthn spec anymore, but it's still pretty close. The new format is described here.

If you have more questions, you can find us more easily on Slack.

Firstyear commented 11 months ago

Thanks @ionut-arm - I'll join later, but no guarantees I'm in a convenient same timezone (Australia, UTC+10 here).

I'm working on https://github.com/kanidm/kanidm and https://github.com/openSUSE/himmelblau where we want to have machine bound and attested certificates, which is why I'm pursuing certify. It's also the reason I was looking at HMAC for TPM binding password caches on endpoints, and the duplicable keys for the servers that replicate.

Generally I try to extend my work into examples to help future users of the library too, which is why I have tried to be submitting these as I go.

Funny you mention Webauthn though, because I'm the author of https://crates.io/crates/webauthn-rs

Firstyear commented 11 months ago

I have a few questions if that's okay.

Generally I think while this was good to read, it's probably confused me even more 🤣 would you mind reviewing the example to help me work out what I'm actually missing here?

ionut-arm commented 11 months ago

Generally I think while this was good to read, it's probably confused me even more 🤣 would you mind reviewing the example to help me work out what I'm actually missing here?

Oh, apologies for that, then 😅 I'll have a look at the example (and your links above) and see if I can offer some suggestions.

ionut-arm commented 10 months ago

Hi! It took a bit longer than hoped for, thanks for the patience!

I ran your example locally and tweaked it a bit. I got the RSA example to work by using AuthSession::Password as the first session handle when certifying - that's because the first session is used to authenticate the object being certified, and you seem to create it without a policy assigned to it, so Password suffices.

For ECC keys, though, it was an issue on our side, for which I've raised #464 - essentially the way we populated ECC parameters was wrong. One thing that helped quite a lot in tracing the issue was using env_logger to print out errors - might be worth initialising that at the beginning of the examples!

To answer some of your questions from above:

I see you call start_auth_session with the rootkey handle here: https://github.com/ionut-arm/rust-tss-esapi/blob/attested-tls/tss-esapi/src/abstraction/transient/mod.rs#L751 - I actually can't seem to work out what this does or achieves with the TPM, can you explain what this does?

For this you'll need to read about tpmKey and how the session key (used to encrypt and authenticate commands) is computed (see TPM2_StartAuthSession here and section 19.6.11 here). Essentially, that key is used to encrypt a salt value that's then used to derive the session key.

You support key auth on the keys that exist under the root_key_handle. Is this required for certification?

Nope, no requirements of that sort on the object being certified I think.

I can't find the "bridge" between KeyMaterial and then getting that to an ObjectWrapper

Not sure I get this one - the KeyMaterial is generally part of the ObjectWrapper, and the user of the library would be in charge of keeping them together or regenerating the ObjectWrappr around the material.

You use AuthSession::Password here - is https://github.com/ionut-arm/rust-tss-esapi/blob/attested-tls/tss-esapi/src/abstraction/web_authn.rs#L37 but Endorsement here https://github.com/ionut-arm/rust-tss-esapi/blob/attested-tls/tss-esapi/src/abstraction/transient/key_attestation.rs#L187 I'm not sure I understand this because everything else in the abstractions branch uses the endorsement auth?

As mentioned above, the first auth is for the certified key, the second for the certifying key. In our case, we keep both protected by passwords. In the cases where we do use Endorsement, that's because we have to operate with objects that are tied to the Endorsement hierarchy.

Where does activate credential fit into this? I can't see it being called anywhere in the workflow? Generally activate credential is the thing I'm fuzziest on, I can't find anything that can coherently explain what it does.

Activate credential is not directly related to Certify, they're somewhat complementary. The whole activate credential loop is meant to be separate from certification, because you want to separate the One True Identity of your TPM (the EK) from the key you use to sign Certify results with (the AK). That's for enhanced privacy, so you can have different AKs for different purposes. So with Activate Credential you prove to someone on the other end that you do have a TPM-resident AK, and then they can trust you when you Certify with that AK. I can provide more details on what happens during the whole activate credential flow.

Firstyear commented 10 months ago

Hi! It took a bit longer than hoped for, thanks for the patience!

Thanks for the help!

I ran your example locally and tweaked it a bit. I got the RSA example to work by using AuthSession::Password as the first session handle when certifying - that's because the first session is used to authenticate the object being certified, and you seem to create it without a policy assigned to it, so Password suffices.

So what does the second session handle do then? 🤔

For ECC keys, though, it was an issue on our side, for which I've raised #464 - essentially the way we populated ECC parameters was wrong. One thing that helped quite a lot in tracing the issue was using env_logger to print out errors - might be worth initialising that at the beginning of the examples!

Yeah, I had been forgetting that. Worth saying that printing out the errors hasn't helped me a whole lot, especially on the TPM front, they tend not to "give much detail".

Where does activate credential fit into this? I can't see it being called anywhere in the workflow? Generally activate credential is the thing I'm fuzziest on, I can't find anything that can coherently explain what it does.

Activate credential is not directly related to Certify, they're somewhat complementary. The whole activate credential loop is meant to be separate from certification, because you want to separate the One True Identity of your TPM (the EK) from the key you use to sign Certify results with (the AK). That's for enhanced privacy, so you can have different AKs for different purposes. So with Activate Credential you prove to someone on the other end that you do have a TPM-resident AK, and then they can trust you when you Certify with that AK. I can provide more details on what happens during the whole activate credential flow.

I wonder if this is where I'm getting stuck on my question of "how to show the x509 chain back to the endorsement key".

Which also begs the question, can you certify directly with the endorsement key?

Anyway, I've update the branch with the "working" attestation with an AK, but I still don't know how to "chain this back" to the EK for an external verifier. I also put in the latest commit an attempt to certify directly with the endorsement key, because that's what I have seen in Webauthn (I've never seen a TPM Webauthn Credential with a chain, it's always against the endorsement key (?)).

ionut-arm commented 10 months ago

So what does the second session handle do then?

It authenticates the attesting key :D

I wonder if this is where I'm getting stuck on my question of "how to show the x509 chain back to the endorsement key".

Which also begs the question, can you certify directly with the endorsement key?

You generally cannot, for privacy purposes. For many platforms the EK is restricted to decryption so that it can only activate credentials. Also, the whole activate credential flow can only give one party the assurance that your AK is in the same TPM as the EK (since nothing gets signed, unlike when generating an x509 cert).

That's why in our use of attestation we don't bother with the EK, we simply endorse the AK to the verifier with a pinky swear that it's a TPM key. But Keylime, for example, does implement this whole activation dance.

Firstyear commented 10 months ago

So what does the second session handle do then?

It authenticates the attesting key :D

Which I think I did work out later :)

I wonder if this is where I'm getting stuck on my question of "how to show the x509 chain back to the endorsement key". Which also begs the question, can you certify directly with the endorsement key?

You generally cannot, for privacy purposes. For many platforms the EK is restricted to decryption so that it can only activate credentials. Also, the whole activate credential flow can only give one party the assurance that your AK is in the same TPM as the EK (since nothing gets signed, unlike when generating an x509 cert).

That's why in our use of attestation we don't bother with the EK, we simply endorse the AK to the verifier with a pinky swear that it's a TPM key. But Keylime, for example, does implement this whole activation dance.

Is this also needed for webauthn? I think my question is "how does the ek relate to the ak so that a provider can validate the content?"

In webauthn we always get the ek cert and it signs over the collected client data hash, so I'm wondering how that works?

Firstyear commented 10 months ago

Ahhh turns out some tpms have EK's that can directly sign:

https://github.com/tpm2dev/tpm.dev.tutorials/blob/master/Attestation/README.md#attestation-protocol-patterns-and-actual-protocols-signing-only-eks

Firstyear commented 5 months ago

Okay, I've been re-reading this, looking at my code and I was still confused. I took a step back to look at how webauthn-rs verifies tpm attestations and checked the specs again. So I want to "validate" my thinking about how the flow here is working. This was pieced together from a lot of places so it could be wrong still.

We have a hypothetical user machine which has a TPM. We want this machine to be part of our authentication/trust CA so the machine begins by loading it's EK object and then creating a new AK under the endorsement hierachy.

The EK public and AK name are now sent to our trust CA These use TPM_MakeCredential to create a new encrypted IdObject that our TPM can load.

The TPM in the user machine now loads the IdObject with ActivateCredential. This allows it access to a private key that our trust servers know and can guarantee can only exist in this TPM.

The private key that was issued might also have a x509 certificate associated to it's public key, which was signed by our trust CA.

Now our TPM can use the issued and loaded IdObject to certify (attest) other keys that it may create. Since the IdObject is only able to be loaded by that specific TPM, and we trust it's behaviour, we can now trust attestations the TPM produces.

So the point of certify isn't to bind the EK/AK to a key. It's so that when an EK/AK are used with makeCredential/ActivateCredential and we trust those are in the TPM, we can then use that to chain our trust via certify to other objects.

Does that make sense?

If so, should I rework the example to show the MakeCredential / ActivateCredential process at the start and then certify after?

Firstyear commented 5 months ago

Closing because I cooked the git branch somehow.