Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Trusted Platform Module 2.0 (TPM2) pin support #17

Closed
wants to merge 1 commit into from

Conversation

martinezjavier
Copy link
Contributor

This adds support for a Trusted Platform Module 2.0 (TPM2) clevis pin.
It encrypts the JWK using a TPM2 chip, and then at decryption time the
JWK is decrypted using the same TPM2 chip to allow clevis to decrypt
the secret stored in the JWE. That way the ciphertext is bound to a TPM
so it can only be decripted in a particular machine.

Encrypting data using the tpm2 pin works the same than with other pins:

$ clevis encrypt tpm2 '{}' < PT > JWE

The pin has reasonable defaults for its configuration, but a different
hierarchy, hash, and key algorithms can be choosen if the defaults are
not suitable.

Decryption also works the same than with other pins, only needs the JWE:

$ clevis decrypt tpm2 < JWE > PT

The public and private key pair of the encrypted JWK are stored in the
JWE object, so those can be fetched at decryption time to unseal the
JWK using the TPM2 chip.

Signed-off-by: Javier Martinez Canillas javierm@redhat.com

@martinezjavier
Copy link
Contributor Author

@npmccallum one thing pointing out is that the default algorithm used for the primary key is ECC (TPM_ALG_ECC).

A better option would be to use a symmetric algorithm (TPM_ALG_SYMCIPHER) instead. But unfortunately only asymmetric algorithms were supported for primary keys in previous version of the TCG TPM specification so using symcipher as default may not work on all TPM2 chips (please refer to this tpm2-tools project issue).

The TPM chips have properties that specify the implemented version of the spec, so you can query them, i.e:

$ tpm2_dump_capability -c properties-fixed | head -n8
TPM_PT_FAMILY_INDICATOR:
  as UINT32:                0x08322e3000
  as string:                "2.0"
TPM_PT_LEVEL:               0
TPM_PT_REVISION:            1.16
TPM_PT_DAY_OF_YEAR:         0x0000012f
TPM_PT_YEAR:                0x000007de
TPM_PT_MANUFACTURER:        0x494e5443

So we could query the specification version but as @webmeister mentioned, I also didn't find where exactly in the specification is mentioned which algorithms are supported for primary keys on each version of the spec. So I'm not sure if we can rely on something like that to choose which key algorithm use by default.

I also didn't have access to a TPM2 that implements a 01.38 revision to test if symmetric parent keys were working. That's why I left ECC as the default for now, which should work on all TPM2 chips.

@npmccallum
Copy link
Contributor

$ sudo tpm2_dump_capability -c properties-fixed
Failed to initialize tcti context: 0x1

Any ideas?

@martinezjavier
Copy link
Contributor Author

martinezjavier commented Sep 26, 2017

@npmccallum yes, the tpm2-tools support different TCTI modules (transport layers). By default it attempts to use the D-Bus based resource manager (tpm2-abrmd) but you can also access the TPM device directly. Could you please try the following command instead:

$ sudo tpm2_dump_capability -c properties-fixed -T device

@npmccallum
Copy link
Contributor

This is on a 6mo Dell XPS 13:

$ sudo tpm2_dump_capability -c properties-fixed -T device
TPM_PT_FAMILY_INDICATOR:
  as UINT32:                0x08322e3000
  as string:                "2.0"
TPM_PT_LEVEL:               0
TPM_PT_REVISION:            1.00
TPM_PT_DAY_OF_YEAR:         0x000000a7
TPM_PT_YEAR:                0x000007df
TPM_PT_MANUFACTURER:        0x4e544300
TPM_PT_VENDOR_STRING_1:
  as UINT32:                0x726c7300
  as string:                "rls"
TPM_PT_VENDOR_STRING_2:
  as UINT32:                0x4e504354
  as string:                "NPCT"
TPM_PT_VENDOR_STRING_3:
  as UINT32:                0x20000000
  as string:                " "
TPM_PT_VENDOR_STRING_4:
  as UINT32:                0x20000000
  as string:                " "
TPM_PT_VENDOR_TPM_TYPE:     0x00000001
TPM_PT_FIRMWARE_VERSION_1:  0x00010003
TPM_PT_FIRMWARE_VERSION_2:  0x00010000
TPM_PT_INPUT_BUFFER:        0x00000400
TPM_PT_HR_TRANSIENT_MIN:    0x00000003
TPM_PT_HR_PERSISTENT_MIN:   0x00000007
TPM_PT_HR_LOADED_MIN:       0x00000003
TPM_PT_ACTIVE_SESSIONS_MAX: 0x00000040
TPM_PT_PCR_COUNT:           0x00000018
TPM_PT_PCR_SELECT_MIN:      0x00000003
TPM_PT_CONTEXT_GAP_MAX:     0x0000ffff
TPM_PT_NV_COUNTERS_MAX:     0x00000010
TPM_PT_NV_INDEX_MAX:        0x00000800
TPM_PT_MEMORY:              0x00000006
TPM_PT_CLOCK_UPDATE:        0x00400000
TPM_PT_CONTEXT_HASH:        0x0000000b
TPM_PT_CONTEXT_SYM:         0x00000006
TPM_PT_CONTEXT_SYM_SIZE:    0x00000080
TPM_PT_ORDERLY_COUNT:       0x000000ff
TPM_PT_MAX_COMMAND_SIZE:    0x00000800
TPM_PT_MAX_RESPONSE_SIZE:   0x00000800
TPM_PT_MAX_DIGEST:          0x00000020
TPM_PT_MAX_OBJECT_CONTEXT:  0x00000392
TPM_PT_MAX_SESSION_CONTEXT: 0x000000e9
TPM_PT_PS_FAMILY_INDICATOR: 0x00000001
TPM_PT_PS_LEVEL:            0x00000000
TPM_PT_PS_REVISION:         0x00000100
TPM_PT_PS_DAY_OF_YEAR:      0x00000000
TPM_PT_PS_YEAR:             0x00000000
TPM_PT_SPLIT_MAX:           0x00000080
TPM_PT_TOTAL_COMMANDS:      0x0000006b
TPM_PT_LIBRARY_COMMANDS:    0x00000065
TPM_PT_VENDOR_COMMANDS:     0x00000006
TPM_PT_NV_BUFFER_MAX:       0x00000400

Thoughts?

@npmccallum
Copy link
Contributor

I question whether or not we can predict algorithm support from TPM_PT_REVISION. I suspect vendors will implement the algorithms and forget to bump the revision. Is there a way to test for symcipher directly? What precisely are the drawbacks for using EC?

@martinezjavier
Copy link
Contributor Author

@npmccallum right, that's what I questioned too. I don't think there's a way to explicitly query the algorithms that are supported by a given TPM2 chip. AFAICT, the spec only defines the set of mandatory algorithms on each spec version. But even there, it doesn't specify if these are only for created objects or also for primary keys.

But even older versions of the spec mentions that asymmetric algorithms can be used for primary keys, and RSA and ECC are listed as mandatory asymmetric algorithms.

The reason I mentioned symcipher as possibly a better default option was performance. But since ECDH is used, I guess is less of a concern?

@npmccallum
Copy link
Contributor

What is the current performance impact of ECC? Can you help define this for me? Is there any way we can mitigate the performance hit?

@martinezjavier
Copy link
Contributor Author

@npmccallum the problem is that I didn't have access to many TPMs to have a conclusive answer.

But a colleague tested the clevis tpm2 pin (which defaults to ECC) on his system and said that encryption/decryption took several seconds. Now, I don't know if the performance on those chips are really bad regardless of the used algorithm since I don't have access to test.

On my test system (Intel PTT firmware based TPM2) I get much faster times:

$ time echo hi | clevis encrypt tpm2 '{"pcr_ids":"7"}' > hi.jwe

real    0m0.500s
user    0m0.056s
sys     0m0.100s

$ time clevis decrypt < hi.jwe 
hi

real    0m0.959s
user    0m0.041s
sys     0m0.100s

I've also tested on a system with a hardware TPM2 and the times were similar than my test system. Neither system supports using symmetric primary keys so I couldn't compare the performance. So more than the default algorithm used, I'm worried about the performance variability between different TPM2 chips.

One optimization that I did compared with the previous PR is that the primary key isn't recalculated on each operation (which may be slow specially for RSA). But instead the pin only calculates the PK the first time and make it persistent in the TPM, so future operations don't have to.

@npmccallum
Copy link
Contributor

This seems reasonable. Can you defined "make it persistent in the TPM" for me?

At the Linux Security Summit two weeks ago, I sat in a talk on TPM where the recommended advice was to export the primary key and load it during decryption. Are you persisting the PK on the NVRAM?

@martinezjavier
Copy link
Contributor Author

Yes, the objects in the TPM2 can either be transient or persistent. Transient objects are the ones that are only in the TPM RAM and can be removed by either a TPM2_FlushContext() command or a TPM2 reset.

Persistent objects are stored in the TPM NVRAM and there's a TPM2_EvictControl() command to either make a transient object persistent or to evict a persistent object.

I also read the same recommendation than you, and that's why the previous implementation didn't store the primary keys in the TPM and was recalculated on decryption (since the PK derivation function is deterministic).

But then someone found this TPM2 chip that's very slow and also I noticed that RSA primary key creation time took several seconds even on my test system.

Notice how the first operation takes a lot because the RSA PK is created and the second one is fast due the PK already being in the TPM2:

$ time echo foo | clevis encrypt tpm2 '{"key":"rsa","pcr_ids":"7"}' > foo.jwe

real    0m13.341s
user    0m0.071s
sys     0m0.127s

$ time echo bar | clevis encrypt tpm2 '{"key":"rsa","pcr_ids":"7"}' > bar.jwe

real    0m0.489s
user    0m0.050s
sys     0m0.086s

For ECC is faster, but still creating the PK increases the operation time:

$ time echo foo | clevis encrypt tpm2 '{"key":"ecc","pcr_ids":"7"}' > foo.jwe

real    0m1.083s
user    0m0.090s
sys     0m0.112s

$ time echo bar | clevis encrypt tpm2 '{"key":"ecc","pcr_ids":"7"}' > bar.jwe

real    0m0.486s
user    0m0.055s
sys     0m0.084s

@webmeister
Copy link

So we could query the specification version but as @webmeister mentioned, I also didn't find where exactly in the specification is mentioned which algorithms are supported for primary keys on each version of the spec. So I'm not sure if we can rely on something like that to choose which key algorithm use by default.

I'd be pretty certain that if your TPM claims to implement 01.38 (or newer), you can also rely on it supporting symmetric storage keys. But this version of the specification is just a year old now, so it will probably take a long time before the majority of TPMs implements it.

I don't think there's a way to explicitly query the algorithms that are supported by a given TPM2 chip.

You can query the supported algorithms, but only in a generic "is this algorithm supported at all" kind of way (TPM_CAP_ALGS). You cannot determine whether a specific command will accept a certain parameter. Of course, if symmetric keys are fast enough to create, you could just always start with one, and only when your command fails, fall back to slower keys.

Or another wild idea: Why do you need a storage key at all in your scenario? Can't you just use a symmetric primary key and do all your operations with it? There is no need for you to be able to move keys between TPMs, is there?

I sat in a talk on TPM where the recommended advice was to export the primary key and load it during decryption.

Exporting the primary key sounds like TPM2_ContextSave, right? This should indeed be a fast operation, but your exported copy is only valid for that power cycle, so you need to recreate it after each reboot (or store it in NVM using TPM2_EvictControl).

@webmeister
Copy link

In my example, it's likely that another program (i.e: the Anaconda installer) will use clevis to bind a LUKS volume against a tpm2 pin and not expect users to do this using the CLI after the installation.

Then Anaconda needs to know that PCR7 is connected to the secure boot state. If it is only Anaconda that needs this knowledge, this is fine. But if you expect other applications to have the same need, then it would make sense to encode this TPM-specific knowledge somewhere on a lower layer (clevis in this case?), so that not every application author has to read the obscure TPM specifications (or blindly copy the algorithm from another application).

You mean with some sort of man-in-the-middle in the device TCTI library (the clevis tpm2 pin explicitly use the device TCTI) that stores the command and sends it back to the TPM?

No, my assumption was that the attacker can access the sealed object that is stored on the disk (encrypted with a TPM-internal secret). Because if your assumption is that an attacker cannot access that data, there would be no need for you to use a TPM at all, you could just store your key in plain at the same location.

From the "the disk alone cannot be decrypted" scenario I guess that what you really assume is that the attacker can access the sealed object (e.g. because they have physical access to the disk) or perhaps the TPM (e.g. because they can execute code as a non-root user on the machine), but never both. But that scenario would also work with an inexpensive USB flash drive as key storage, no need for a fancy TPM. So to really get an advantage from the TPM, you need to use it for more than just a very complicated data storage ;-)

Because someone that doesn't have access to either the public and sensitive parts of the sealed objects (that are in the LUKSMeta header) to load it into the TPM or the used load context (which is temporary and deleted after unsealing) shouldn't be able to perform this kind of attack, right?

Correct.

If someone messes with the initramfs and has an application that extends the PCR7 before clevis is able to unlock, then it will fail indeed. But that's why I said that it's not meant to protect from someone with root access after the volume has been unlocked.

This is not so much about attacks, but again rather about cooperation between multiple applications. You are probably the first/only application at the moment, so there is nobody else to cooperate with. But that only means that you will not notice now, if you violate the expectations of other authors (that might be encoded in some specification). Do you know whether there is a specification that describes what you can and cannot do with PCR7?

I'll be on vacation for the rest of the week, so further discussion probably has to be moved to next week.

@martinezjavier
Copy link
Contributor Author

But if you expect other applications to have the same need, then it would make sense to encode this

TPM-specific knowledge somewhere on a lower layer (clevis in this case?), so that not every application author has to read the obscure TPM specifications (or blindly copy the algorithm from another application).

Yes, I get your point and I don't say that I disagree with you. I'm just not sure if clevis is supposed to only provide mechanisms and let users define the policies or also have some built-in policies, like your "seal-against-current-secure-boot-state" example. I'll let @npmccallum comment on this.

No, my assumption was that the attacker can access the sealed object that is stored on the disk (encrypted with a TPM-internal secret). Because if your assumption is that an attacker cannot access that data, there

I was talking about the "user with access to both the disk and TPM2 after boot" case. One option I can think of is to extend the PCR7 with a pre-defined hash (i.e: all 0) after the LUKS volume is unlocked.

That way someone who gets access to both the sealed data and the TPM2, won't be able to unseal the secret after the system was booted. But users will still be able to seal other data against that current PCR state, since the PCR7 digest will always be extended with the same value (hash on boot + 0s).

would be no need for you to use a TPM at all, you could just store your key in plain at the same location.

Well, the point of having the key sealed against the TPM2 and not in plain text is to bind the disk to the machine. If it was in plain text, then someone that steals the disk (or VM image) could just unlock it.

From the "the disk alone cannot be decrypted" scenario I guess that what you really assume is that the attacker can access the sealed object (e.g. because they have physical access to the disk) or perhaps the TPM (e.g. because they can execute code as a non-root user on the machine), but never both. But that

Correct, I assume that either one can access the TPM2 (because not only root can execute commands) or the sealed key (because the disk is stolen) but never both.

scenario would also work with an inexpensive USB flash drive as key storage, no need for a fancy TPM. So to really get an advantage from the TPM, you need to use it for more than just a very complicated data storage ;-)

That's true. But the advantage of the TPM2 is that it will already be present in most machines and also users don't have to plug it on boot. If a security mechanism depends on user to plug a device each time they need to boot a machine, most likely will fail due being hard to use. I think the TPM2 is a good trade off between security and easy of use.

This is not so much about attacks, but again rather about cooperation between multiple applications. You are probably the first/only application at the moment, so there is nobody else to cooperate with. But that only means that you will not notice now, if you violate the expectations of other authors (that might

I see, you are making a very good point and I've to admit that I haven't given a lot of thinking to this yet. But I think that the TPM2 being a sensitive shared resource, most applications using it should be trusted.

be encoded in some specification). Do you know whether there is a specification that describes what you can and cannot do with PCR7?

The most official documentation that I know of is this from Microsoft and is what follows shim.

I'll be on vacation for the rest of the week, so further discussion probably has to be moved to next week.

No worries, enjoy your vacation. This is blocked on a new release of the tpm2-tools anyways, so we have time to discuss these issues and further refine the implementation if needed.

I should also go on paternity leave soon (depending on when the baby decides to arrive), so I may become less active for a couple of weeks. Thanks, again!

@npmccallum
Copy link
Contributor

One thing we need to do in this patch is clearly define the threat model and what attacks we cannot protect against. This needs to be meticulously documented in the man page.

@martinezjavier
Copy link
Contributor Author

@npmccallum yes, I've been thinking about this. I was wrongly focused on the LUKS volume unlock case, but that's just a particular case of a more general one. Basically, what clevis assumes is that the attacker doesn't have access to the TPM2.

On encryption, a JWK is created, the data is encrypted with the JWK and the JWK is encrypted with the TPM2. Both the ciphertext and the wrapped JWK are stored in the JWE.

On decryption, both the ciphertext and the wrapped JWK are taken from the JWE and the TPM2 is used to decrypt the JWK which in turn is used to decrypt the ciphertext.

So the assumption is that the attacker won't have access to both the JWE and the TPM2. This assumption is not only true for the clevis tpm2 pin but for all pins AFAICT.

For example, in the tang and HTTP pins an attacker only needs access to both the JWE and the network service to decrypt the ciphertext. The assumption is that it won't have access to both.

The only difference between these pins and the tpm2 one, is that the {en,de}cryption backend is local to the machine. So it must be protected from the attackers.

And additional protection that the TPM2 has, is that the encrypted data could be "sealed" to a specific TPM2 PCR state. So if the current PCR state doesn't match the one specified on encryption, the data can't de decrypted even if the attacker has access to the TPM2.

@npmccallum
Copy link
Contributor

This is correct so far. The question for me is clearly delineating if/how the TPM2 can be protected. For network services this is pretty obvious (firewall, authentication, etc.). But it is much less clear for TPM.

Binding against PCRs only helps if the PCRs are extended with the relevant measurements. On Linux, they aren't (yet). Further, if the measurements are all stock executables (kernel, initramfs, init, etc.) then an attacker can reproduce these measurements by booting an alternate stock OS that has different root credentials. Thus, for example, this might be useful for a root volume in initramfs since, after initramfs, recovery would be impossible. But this can't be used for non-root volumes which are unlocked after boot because an attacker would then always have access to the TPM.

We need to think carefully about these cases and document the possible attacks.

@martinezjavier
Copy link
Contributor Author

We need to think carefully about these cases and document the possible attacks.

I think that rather than documenting all the possible attacks, what we should explain clearly is the fact that the Clevis security model relies on the fact that an attacker would not get access to both the encrypted secret and the decryption key.

And that in the case of the tpm2 pin, the key es encrypted using a TPM2 chip that's always present in the machine. While for other pins, the decryption key is located externally (a remove server, a pluggable authentication device, etc).

That way people can understand the limitations of the tpm2 pin an assess the risk and how to protect the TPM2 depending on their particular use case.

I've rebased the commit on top of latest master and also added some comments on this line to the clevis-encrypt-tpm2 man page:

# Threat model

The Clevis security model relies in the fact that an attacker will not be able to
access both the encrypted data and the decryption key.

For most Clevis pins, the decryption key is not locally stored, so the decryption
policy is only satisfied if the decryption key can be remotely accessed. It could
for example be stored in a remote server or in a hardware authentication device
that has to be plugged into the machine.

The tpm2 pin is different in this regard, since a key is wrapped by a TPM2 chip
that is always present in the machine. This does not mean that there are not use
cases for this pin, but it is important to understand the fact that an attacker
that has access to both the encrypted data and the local TPM2 chip will be able
to decrypt the data.

@npmccallum please let me know what you think.

@npmccallum
Copy link
Contributor

I think that is good. Perhaps we should also have a separate man page that discusses the security implications of automated decryption at a high level and then overviews the unlocking methods and their specific security properties?

@martinezjavier
Copy link
Contributor Author

@npmccallum agreed. That will be a more general man page so I think that will belong to a separate pull request as a follow-up.

@RashmicaG
Copy link

Hi @martinezjavier,
I'm interested in this pull request but unfortunately don't yet have access to a TPM to try it out :(

I like the idea of extending PCR7 once you've got the secret from the TPM. Have you read this article?

This is blocked on a new release of the tpm2-tools anyways

Do you have any idea on when the tpm2-tools release might come out?

@martinezjavier
Copy link
Contributor Author

@RashmicaG great, thanks for your interest.

I like the idea of extending PCR7 once you've got the secret from the TPM. Have you read this article?

Yes, I read Matthew's article. In fact, that's from where I got the idea of extending PCR7.

Do you have any idea on when the tpm2-tools release might come out?

The second release candidate (-rc1) of the tpm2-tools 3.0.0 was released yesterday. Hopefully the final version should be in a week or so.

@npmccallum
Copy link
Contributor

@martinezjavier Can we land -rc1 in rawhide ASAP?

@martinezjavier
Copy link
Contributor Author

This adds support for a Trusted Platform Module 2.0 (TPM2) clevis pin.
It encrypts the JWK using a TPM2 chip, and then at decryption time the
JWK is decrypted using the same TPM2 chip to allow clevis to decrypt
the secret stored in the JWE. That way the ciphertext is bound to a TPM
so it can only be decripted in a particular machine.

Encrypting data using the tpm2 pin works the same than with other pins:

$ clevis encrypt tpm2 '{}' < PT > JWE

The pin has reasonable defaults for its configuration, but a different
hierarchy, hash, and key algorithms can be choosen if the defaults are
not suitable.

Decryption also works the same than with other pins, only needs the JWE:

$ clevis decrypt tpm2 < JWE > PT

The public and private key pair of the encrypted JWK are stored in the
JWE object, so those can be fetched at decryption time to unseal the
JWK using the TPM2 chip.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
@npmccallum
Copy link
Contributor

Excellent! I will try to get this tested next week.

@martinezjavier
Copy link
Contributor Author

@npmccallum perfect, thanks a lot for your help!

Please let me know if have any issues or something isn't clear to you.

@martinezjavier
Copy link
Contributor Author

@npmccallum did you have time to test this?

@npmccallum
Copy link
Contributor

Not yet. I'm out of the office until Jan 8th. I'll make it a priority when I get back.

@martinezjavier
Copy link
Contributor Author

@npmccallum great, thanks a lot!

And also happy new year 😃

@martinezjavier
Copy link
Contributor Author

@npmccallum another gentle ping on this.

Copy link
Contributor

@npmccallum npmccallum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am testing this now.

curl \
jose \
nc

dracut_need_initqueue
}

installkernel() {
hostonly='' instmods =drivers/char/tpm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The space after instmods looks strange. Is it correct?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is.
From dracut's man page:

instmods installs one or more kernel modules in the initramfs.
can also be a whole subsystem, if prefixed with a
"="

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@npmccallum yes, as @circhioz said I took that from dracut's man page.

You can also look that other dracut modules are using it, i.e: $ grep "instmods =" /usr/lib/dracut/modules.d/ -R

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! Thanks!

@npmccallum
Copy link
Contributor

I tested this in the boot and non-root cases. Both work. The code looks clean. It is merged. Congratulations to everyone!

Also, I've release v9, which contains primarily this change along with some minor other fixes.

@npmccallum npmccallum closed this Feb 13, 2018
@martinezjavier
Copy link
Contributor Author

@npmccallum great, thanks a lot for your help!

@martinezjavier martinezjavier deleted the tpm2-pin branch March 22, 2018 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants