heads icon indicating copy to clipboard operation
heads copied to clipboard

Use a USB security key as a TPM work-alike in the absence of a physical TPM

Open MrChromebox opened this issue 5 years ago • 72 comments

On machines without a TPM, we'd still like some way for the BIOS to attest that it has not been modified. With a USB security key, we can have the BIOS use its own ROM measurement converted to a SHA256sum and truncated so it fits within an HOTP secret. Like with a TPM, a malicious BIOS with access to the correct measurements can send pre-known good measurements to the USB security key.

This approach provides one big drawback in that we have to truncate the SHA256sum to 20 characters so that it fits within the limitations of HOTP secrets. This means the possibility of collisions is much higher but again, an attacker could also capture and spoof an existing ROM's measurements if they have prior access to it, either with this approach or with a TPM.

This supersedes #493

Signed-off-by: Matt DeVillier [email protected]

MrChromebox avatar Sep 16 '20 22:09 MrChromebox

@MrChromebox 20 chars limitation was fixed a long time ago in firmware, exposed here: https://github.com/osresearch/heads/pull/493#issuecomment-487379379 so no need to truncate prematurely sha256sum to 20 characters (can be 40 characters) in newer upstreamed firmware ( https://github.com/osresearch/heads/pull/493#issuecomment-487379379 ) and consequently produced Librem Keys/Nitrokey Pro versions manufactured since 2019.

Questions of how to deal with old keys is here: https://github.com/osresearch/heads/pull/493#issuecomment-487379379 with returns if users really want this from both Purism (@kylerankin ) /Nitrokey? (@jans23) ?

Else a big fat warning could be given to users if using a USB Security dongle's firmware (detectable) requiring to truncate secret in insecure ways, giving users a risk choice they should be clear about prior of using such technology for tamper evidence.

tlaurion avatar Sep 17 '20 14:09 tlaurion

@MrChromebox : i'm so confused by the build failing. Logs (now capture) saying that the compiler was killed. Too memory hungry?!

From :https://circleci.com/api/v1.1/project/github/MrChromebox/heads/11/output/127/0?file=true&allocation-id=5f628bc03ecc101f341bc0a2-0-build%2FCDC2F19

/bin/bash ../../gcc-8.3.0/gcc/../move-if-change tmp-automata.c insn-automata.c
g++ -fno-PIE -c   -O2  -fomit-frame-pointer -m64  -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wmissing-format-attribute -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings   -DHAVE_CONFIG_H -I. -I. -I../../gcc-8.3.0/gcc -I../../gcc-8.3.0/gcc/. -I../../gcc-8.3.0/gcc/../include -I../../gcc-8.3.0/gcc/../libcpp/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include  -I../../gcc-8.3.0/gcc/../libdecnumber -I../../gcc-8.3.0/gcc/../libdecnumber/dpd -I../libdecnumber -I../../gcc-8.3.0/gcc/../libbacktrace   -o ipa-hsa.o -MT ipa-hsa.o -MMD -MP -MF ./.deps/ipa-hsa.TPo ../../gcc-8.3.0/gcc/ipa-hsa.c
echo timestamp > s-automata
g++ -fno-PIE -c   -O2  -fomit-frame-pointer -m64  -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wmissing-format-attribute -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings   -DHAVE_CONFIG_H -I. -I. -I../../gcc-8.3.0/gcc -I../../gcc-8.3.0/gcc/. -I../../gcc-8.3.0/gcc/../include -I../../gcc-8.3.0/gcc/../libcpp/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include  -I../../gcc-8.3.0/gcc/../libdecnumber -I../../gcc-8.3.0/gcc/../libdecnumber/dpd -I../libdecnumber -I../../gcc-8.3.0/gcc/../libbacktrace   -o ipa-ref.o -MT ipa-ref.o -MMD -MP -MF ./.deps/ipa-ref.TPo ../../gcc-8.3.0/gcc/ipa-ref.c
g++ -fno-PIE -c   -O2  -fomit-frame-pointer -m64  -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wmissing-format-attribute -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings   -DHAVE_CONFIG_H -I. -I. -I../../gcc-8.3.0/gcc -I../../gcc-8.3.0/gcc/. -I../../gcc-8.3.0/gcc/../include -I../../gcc-8.3.0/gcc/../libcpp/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include  -I../../gcc-8.3.0/gcc/../libdecnumber -I../../gcc-8.3.0/gcc/../libdecnumber/dpd -I../libdecnumber -I../../gcc-8.3.0/gcc/../libbacktrace   -o ipa-utils.o -MT ipa-utils.o -MMD -MP -MF ./.deps/ipa-utils.TPo ../../gcc-8.3.0/gcc/ipa-utils.c
g++ -fno-PIE -c   -O2  -fomit-frame-pointer -m64  -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wmissing-format-attribute -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings   -DHAVE_CONFIG_H -I. -I. -I../../gcc-8.3.0/gcc -I../../gcc-8.3.0/gcc/. -I../../gcc-8.3.0/gcc/../include -I../../gcc-8.3.0/gcc/../libcpp/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include  -I../../gcc-8.3.0/gcc/../libdecnumber -I../../gcc-8.3.0/gcc/../libdecnumber/dpd -I../libdecnumber -I../../gcc-8.3.0/gcc/../libbacktrace   -o ipa.o -MT ipa.o -MMD -MP -MF ./.deps/ipa.TPo ../../gcc-8.3.0/gcc/ipa.c
g++ -fno-PIE -c   -O2  -fomit-frame-pointer -m64  -DIN_GCC  -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wmissing-format-attribute -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings   -DHAVE_CONFIG_H -I. -I. -I../../gcc-8.3.0/gcc -I../../gcc-8.3.0/gcc/. -I../../gcc-8.3.0/gcc/../include -I../../gcc-8.3.0/gcc/../libcpp/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include -I/root/project/build/coreboot-4.12/util/crossgcc/xgcc/include  -I../../gcc-8.3.0/gcc/../libdecnumber -I../../gcc-8.3.0/gcc/../libdecnumber/dpd -I../libdecnumber -I../../gcc-8.3.0/gcc/../libbacktrace   -o ira.o -MT ira.o -MMD -MP -MF ./.deps/ira.TPo ../../gcc-8.3.0/gcc/ira.c
g++: fatal error: Killed signal terminated program cc1plus
compilation terminated.
make[1]: *** [Makefile:1110: insn-emit.o] Error 1
make[1]: *** Waiting for unfinished jobs....
/bin/bash ../../gcc-8.3.0/gcc/../move-if-change tmp-attrtab.c    insn-attrtab.c
/bin/bash ../../gcc-8.3.0/gcc/../move-if-change tmp-dfatab.c     insn-dfatab.c
/bin/bash ../../gcc-8.3.0/gcc/../move-if-change tmp-latencytab.c insn-latencytab.c
echo timestamp > s-attrtab
rm gcc.pod
make[1]: Leaving directory '/root/project/build/coreboot-4.12/util/crossgcc/build-i386-elf-GCC/gcc'
make: *** [Makefile:4244: all-gcc] Error 2

tlaurion avatar Sep 17 '20 14:09 tlaurion

Not a cryptographer here, but my intuition is that it might be better to use sha1sum vs sha256sum (tpm 1.1 measurements are sha1 anyway, so would be equivalent) resulting in hash that would fit under those 40 chars buffer that we have available.

Better to truncate a 64 char into 40 (or worse, twenty...) or use lower checksuming techniques to fill buffer available?

tlaurion avatar Sep 17 '20 14:09 tlaurion

Questions of how to deal with old keys is here: #493 (comment) with returns if users really want this from both Purism (@kylerankin ) /Nitrokey? (@jans23) ?

Loosing backward compatibility to old Nitrokeys is ok for me.

jans23 avatar Sep 17 '20 15:09 jans23

My preference is to detect the firmware version and default to the strongest method their hardware/firmware happens to support instead of presenting users with scary prompts or expecting them to understand the relative strengths of hashing algorithms.

On Thu, Sep 17, 2020 at 08:27:08AM -0700, jans23 wrote:

Questions of how to deal with old keys is here: #493 (comment) with returns if users really want this from both Purism (@kylerankin ) /Nitrokey? (@jans23) ?

Loosing backward compatibility to old Nitrokeys is ok for me.

-- You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/osresearch/heads/pull/836#issuecomment-694311877

kylerankin avatar Sep 18 '20 21:09 kylerankin

@tlaurion @jans23: Kyle and I tested here, and all our of LKs are using firmware >= 0.8, so they all support 40-byte keys. So we're fine dropping support for/breaking older NK/LK firmwares.

I'll update the PR to use 40-byte truncation; Kyle and I agree that SHA256 truncated is still better than SHA1 not.

MrChromebox avatar Sep 21 '20 19:09 MrChromebox

@MrChromebox could you rebase on master to trigger a build please?

tlaurion avatar Oct 02 '20 19:10 tlaurion

@MrChromebox Is the librem_mini taking a change in its board config, or should you provide an additional board config added in CircleCI to be able to test this platform and being pushed at the same time to give example for future platform integrations?

tlaurion avatar Oct 16 '20 20:10 tlaurion

@jans23 @MrChromebox @alex-nitrokey @kylerankin:

Shouldn't this feature be activated per additional boards, by explicitely specifying its configuration flags, for all platforms not having a TPM where TPM-like remote attestation can be approximated with HOTPKEY module supported USB Security dongle? If this is the case, then code logic should check for: export CONFIG_TPM=n && export CONFIG_HOTPKEY=y before applying different codepaths.

@MrChromebox I reviewed https://github.com/osresearch/heads/blob/master/boards/librem_mini/librem_mini.config#L25-L29 and it is there, but :

  • [ ] code is not validating that both export CONFIG_TPM=n && export CONFIG_HOTPKEY=y board configuration parameters are explicited.
  • [ ] I would also want to see in board config header differenciations for that board config for future port of other platforms, so that future developers can see in board config that this board has no TPM and requires HOTPKEY module supported USB Security dongle realize its function (Opening port for X200 and other vTPM deactivated boards by Intel ME deactivation)

tlaurion avatar Oct 16 '20 20:10 tlaurion

@MrChromebox Is the librem_mini taking a change in its board config, or should you provide an additional board config added in CircleCI to be able to test this platform and being pushed at the same time to give example for future platform integrations?

I'm not understanding this question at all. There is a single librem_mini board config. It does not have a TPM. It will always use a HOTP key.

MrChromebox avatar Oct 16 '20 20:10 MrChromebox

part of the issue here is that there is no handling for boards without a TPM (or with a deactivated vTPM) -- they just fail miserably because /dev/tpm0 doesn't exist. I'm happy to split this into two PRs -- handing of boards without a TPM, and handling of boards without a TPM and with a HOTP key, but we need to define what the former looks like first

MrChromebox avatar Oct 16 '20 21:10 MrChromebox

@MrChromebox Is the librem_mini taking a change in its board config, or should you provide an additional board config added in CircleCI to be able to test this platform and being pushed at the same time to give example for future platform integrations?

I'm not understanding this question at all. There is a single librem_mini board config. It does not have a TPM. It will always use a HOTP key.

Sorry for the confusion @MrChromebox, I wrote this prior of reviewing board.

After reviewing board config, my point here is to clearly state what makes it different as a board in terms of configuration: without a TPM while requiring HOTPKEY supported USB Security dongle.

tlaurion avatar Oct 17 '20 14:10 tlaurion

part of the issue here is that there is no handling for boards without a TPM (or with a deactivated vTPM) -- they just fail miserably because /dev/tpm0 doesn't exist. I'm happy to split this into two PRs -- handing of boards without a TPM, and handling of boards without a TPM and with a HOTP key, but we need to define what the former looks like first

@MrChromebox Agreed that there are some codepaths not dealing properly with boards without a TPM. My tests back in 2018 with the KGPE-D16 and x200 were with generic-init, so I have not thoroughly tested newer gui-init post https://github.com/osresearch/heads/issues/477

tlaurion avatar Oct 17 '20 14:10 tlaurion

We will have to reflect on a way to disable hid functions of USB Security dongles inside of heads guys! @MrChromebox @jans23 @szszszsz @alex-nitrokey @daringer @kylerankin

tlaurion avatar Oct 18 '20 14:10 tlaurion

Hi @tlaurion ! Could you elaborate on disabling the HID interface mentioned in https://github.com/osresearch/heads/pull/836#issuecomment-711176858? I do not see such need coming from this ticket context, unless I am missing something.

szszszsz avatar Oct 19 '20 07:10 szszszsz

Hi @tlaurion ! Could you elaborate on disabling the HID interface mentioned in #836 (comment)? I do not see such need coming from this ticket context, unless I am missing something.

@MrChromebox @jans23 @szszszsz @alex-nitrokey @daringer @kylerankin: it's not specific to this PR, but HID without TPM released Disk Unlock Key opens the door to rubber ducky attacks where Heads could be instructed to go into recovery (early 'r' keypress, for example), sed files and reenter GUI by calling gui-init, showing measurements as being good and where Heads scripts are not validated for integrity at this point, being trusted solely per ROM measurements through flashrom backup when TPM is disabled (insufficient).

The only protection Heads offers on this codepath is that entering recovery changes PCR4, invalidating TPM released content, or through TPM based HOTP/TOTP remote attestation.

When TPM + TPM released Disk Unlock Key codepath is active, the user, even if receiving HOTP:Success, would still have to enter TPM nvram passphrase (valid only if attestation is good) to be able to boot system's default boot option. In this Disk Unlock Key released by TPM scenario, the HID based rubber ducky attacks would fail with the Disk Unlock Key passphrase typed correctly (which will only release secret if LUKS header+ TPM extended measurements is good and if Disk Unlock Key passphrase to unlock nvram is valid) being the last guard against booting that insecure environment.

In the current PR, if someone manages to create a rubber ducky with HID programmed to enter key presses, the screen would flicker, and shell scripts could be modified without the user being aware of it. Actual codepaths would validate the firmware parts through flashrom, not actual ramfs modified scripts, and boot into default boot option without TPM measurements being invalidated.

My point here is if there is no TPM released Disk Unlock key + this actual PR + USB Security dongle based firmware integrity validation, hid functions could invalidate Heads security without the user knowing, even more for platform activating USB early in init to support USB keyboards.

Do we have a way to not have HID feature (keyboard like permitted inputs) outside of deactivating USB support altogether (And USB Security dongle, HOTP if Disk Unlock Key is not supported when early USB Keyboard support is enabled while TPM is disabled and HOTP support is enabled ) ?

tlaurion avatar Oct 19 '20 15:10 tlaurion

I do not know enough about it yet, to have a solution at hand, but honestly I am thinking about looking at this topic for some time. I was thinking about looking at how usbguard is working and if we can include something like this. On a clean boot there should be no new device plugged into the heads machine other than those present during initialization as we do not want any fiddling by an malicious device anyway. To conclude imho a blocking of unknown devices is a good idea anyway.

alex-nitrokey avatar Oct 20 '20 09:10 alex-nitrokey

  1. To add to https://github.com/osresearch/heads/pull/836#issuecomment-712713397, perhaps we could add device authentication via OpenPGP to avoid vid:pid:sn triple spoofing, then accept its input.
  2. Another idea is to tunnel the HOTP requests to the USB Security dongle over CCID instead of HID.

szszszsz avatar Oct 20 '20 09:10 szszszsz

My point here is that a rubber ducky specially crafted to fit Librem Key/Nitrokey Pro 3D printed shells to have green led flashing to simulate functionning could break the protection offered by this PR if hid is not blocked. Otherwise, vid:pid:sn triple spoofing can make KO this security mechanism.

1. To add to [#836 (comment)](https://github.com/osresearch/heads/pull/836#issuecomment-712713397), perhaps we could add device authentication via OpenPGP to avoid vid:pid:sn triple spoofing, then accept its input.

Authentication is a good idea only if #771 is implemented and user has a backup of his private key he can reinject at will in a USB Securty dongle replacement if prior is lost, else he locks himself out of Heads. Not desirable.

2. Another idea is to tunnel the HOTP requests to the USB Security dongle over CCID instead of HID.

I think this one is the better approach. While HID still needs to be somehow disabled inside of Heads (no input accepted from USB Security dongle) else sed is going to win.

  1. Another approach here would be to implement not only /boot measurements, but all shell scripts measurements prior of permitting boot. #862 is going that path setting the basis. We could definitely have an extended validation inside of Heads; reusing the concept of hashes, produced by the build system as of now as an example, and be able, at the moment of booting system default, to revalidate that ramfs scripts are still valid.

Thoughts? @MrChromebox @jans23 @szszszsz @alex-nitrokey @daringer @kylerankin:

tlaurion avatar Oct 24 '20 20:10 tlaurion

I think this one is the better approach. While HID still needs to be somehow disabled inside of Heads (no input accepted from USB Security dongle) else sed is going to win.

agreed. Problem is, how do we do that effectively? Using a blocklist for HID would mean newer security dongles would require a Heads update to be fully secure

MrChromebox avatar Oct 24 '20 20:10 MrChromebox

I think this one is the better approach. While HID still needs to be somehow disabled inside of Heads (no input accepted from USB Security dongle) else sed is going to win.

agreed. Problem is, how do we do that effectively? Using a blocklist for HID would mean newer security dongles would require a Heads update to be fully secure

Well... FWUPD firmware upgrades are around the corner, in all cases. :) And newer produced USB Security dongles should be provided after newer Heads firmwares being deployed in all case. HID ID can be faked, btw. I have no solution for that problem, unfortunately, as of now. Disk Unlock Key released by the TPM is the true last line of protection here, not being released if recovery shell was accessed.

I have no idea how to replicate such solution in a TPM-less platform, since USB device cannot be considered truly in the root of trust, being accessible too late to accomplish the same result from tools that could be already compromised. The issue here is HID keyboard being permitted inside of Heads prior of HOTP challenge being made.

tlaurion avatar Oct 25 '20 05:10 tlaurion

Any update here @szszszsz @jans23 @kylerankin ? This PR is not merged because I cannot accept it as a block in its current form, where I restated possible attack vector here in more detail.

tlaurion avatar Dec 03 '20 16:12 tlaurion

@MrChromebox @szszszsz I also thought that HOTP secret size was increased upstream in firmware so that there is no need of truncation under Heads?

tlaurion avatar Dec 26 '20 17:12 tlaurion

It is indeed @tlaurion:

  • current maximum HOTP secret length implemented in firmware for both Nitrokey Pro / Librem Key and Nitrokey Storage is 40 bytes;
  • length in nitrokey-hotp-verification appears to be hardcoded to 20 bytes - nitrokey-hotp-verification/blob/master/operations.c#L55, which is currently maximum handled if not patched in Heads; this should be easily changeable.

cc @jans23

szszszsz avatar Dec 28 '20 18:12 szszszsz

current maximum HOTP secret length implemented in firmware for both Nitrokey Pro / Librem Key and Nitrokey Storage is 40 bytes

but that's not the case for all shipped Nitro/Librem keys, is it? I'd need to see what the earliest firmware is we shipped on the LK and if it supports 40-byte HOTP secrets

MrChromebox avatar Dec 30 '20 20:12 MrChromebox

@MrChromebox https://github.com/osresearch/heads/pull/493#issuecomment-487379379

tlaurion avatar Dec 30 '20 22:12 tlaurion

@tlaurion yes so I just need to figure out if we shipped any LKs with firmware 0.7 or earlier

MrChromebox avatar Dec 30 '20 23:12 MrChromebox

@MrChromebox : or as stated https://github.com/osresearch/heads/pull/493#issuecomment-495834923 react to firmware version and trim only then.

tlaurion avatar Dec 31 '20 00:12 tlaurion

@szszszsz and on HID issue?

tlaurion avatar Dec 31 '20 00:12 tlaurion

@tlaurion I'm not sure how we got back on the 20 vs 40-byte truncation debate, but looking at the current patch set, it truncates to 40 bytes, which means I've already confirmed that it's not an issue with the firmware on any shipped Librem Keys

MrChromebox avatar Jan 05 '21 22:01 MrChromebox