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 vendor commands to inject crypto materials #239

Merged
merged 11 commits into from
Dec 16, 2020

Conversation

jmichelp
Copy link
Collaborator

Fixes #195

The PR is rather big and involves many modifications so I tried to keep the individual commits clean (they could be reviewed separately instead of the PR as a whole):

  • Vendor command to configure the authenticator (attestation crypto materials, JTAG lockdown)
  • Capsule, HIL and syscall to enable JTAG lockdown
  • Configure tool to inject crypto materials in the authenticator
  • Cleanup in the build script (e.g. removing openssl code/dependancy)
  • Updated documentation
  • Version bump from v0.1 to v1.0

Fully tested on the nRF52840DK board. I had issues with the nRF52840 dongle I have so I'll test it in parallel of the review.
At the moment, the Makerdiairy dongle is excluded on purpose (the case is sealed so any mistake with the lockdown would require a destructive opening to recover the chip).

  • Tests pass
  • Appropriate changes to README are included in PR

README.md Outdated Show resolved Hide resolved
docs/install.md Outdated Show resolved Hide resolved
src/ctap/mod.rs Outdated Show resolved Hide resolved
src/ctap/mod.rs Outdated Show resolved Hide resolved
src/ctap/command.rs Outdated Show resolved Hide resolved
tools/configure.py Show resolved Hide resolved
tools/configure.py Outdated Show resolved Hide resolved
tools/configure.py Show resolved Hide resolved
tools/configure.py Show resolved Hide resolved
src/ctap/response.rs Show resolved Hide resolved
@kaczmarczyck
Copy link
Collaborator

I reviewed all code except the patches, and most comments are minor!

src/ctap/mod.rs Outdated

if params.attestation_material.is_some() {
let data = params.attestation_material.unwrap();
if !has_cert {
Copy link
Member

Choose a reason for hiding this comment

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

We don't want to return an error (e.g. invalid argument) if there is already attestation material in the store?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought about it. But what if, for example, writing the certificate works but fails for the private key?
I saw 3 solutions:

  1. modify the cbor command to allow setting certificate and private key individually or together
  2. accept but ignore an already set value which allows to recover from the described scenario
  3. delete the certificate if the private key fails.

Copy link
Member

Choose a reason for hiding this comment

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

I see. Both 2 and 3 look good (2 being the current code). For 2 we could additionally check that the ignored value is equal to the existing one. But I'm fine with the current code, was just asking.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is overwriting so bad on a non-locked-down device? Maybe just make writing conditional, and since lockdown depends on both being set, you should be good?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the code deals equally well with none of the 2 being set, or only 1 being set.

Copy link
Collaborator

Choose a reason for hiding this comment

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

So solution 4 would be to have a is_locked_down in persistent storage, that reflects that. And make writing to the certificate conditional on that. It would also allow to return the state of is_locked_down as part of this command as you planned.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Doing this means that we have one value stored at 2 places which can go out of sync.

Copy link
Member

Choose a reason for hiding this comment

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

From another thread I understood that we can't know if the device is locked, so we don't really have it in 2 places. If it's possible to fetch the value from some register (maybe adding a syscall to the new driver, e.g. command 0 could return whether the device is protected), then that would be the best solution no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I updated HIL + capsule + syscalls to have a get/set model and support different levels of protection. For the vendor specific command I keep a boolean because it doesn't make sense in our context to partially protect.

Regarding our implementation and allowing overwriting the certificate and the private key on a non-locked device, I still see an issue allowing it: it would invalidate all credentials that have been created using U2F. This would only make sense in combination with a reset command (although I'm not familiar enough with batch attestation and the implications it may have). With CTAP2.1 though, some credentials may persist even after a reset and could still risk to be invalidated.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the current implementation is solid, then.

src/ctap/storage.rs Outdated Show resolved Hide resolved
src/ctap/mod.rs Outdated Show resolved Hide resolved
patches/tock/07-firmware-protect.patch Show resolved Hide resolved
patches/tock/07-firmware-protect.patch Outdated Show resolved Hide resolved
src/ctap/command.rs Outdated Show resolved Hide resolved
src/ctap/mod.rs Show resolved Hide resolved
src/ctap/mod.rs Outdated Show resolved Hide resolved
ia0
ia0 previously approved these changes Dec 12, 2020
patches/tock/07-firmware-protect.patch Show resolved Hide resolved
_ => ProtectionLevel::Unknown,
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Probably not actionable, but ideally this kind of stuff should be in a shared library between the kernel and user-land. I don't know if Tock already has this kind of shared library.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't recall such shared library, but agree that it could be useful. CC @jrvanwhy

Choose a reason for hiding this comment

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

I do not believe there is any such library. If I recall my past communications with other Tock core WG members correctly, they had an explanation for why they don't want to share code between the kernel and libtock-rs.

How the OpenSK project structures code in its own repository is up to OpenSK, but that may not be helpful if you would like to upstream this driver in the future.

Discussing this on the Tock core WG call is an option as well.

Copy link
Member

Choose a reason for hiding this comment

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

they don't want to share code

Just to clarify, the idea here is not to share code but to share APIs (e.g. types, enum values), i.e. share what is used to define the interface of kernel and user-land.

FIDO2 certification automation moved this from In progress to Reviewer approved Dec 12, 2020
kaczmarczyck
kaczmarczyck previously approved these changes Dec 14, 2020
Copy link
Collaborator

@kaczmarczyck kaczmarczyck left a comment

Choose a reason for hiding this comment

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

I reviewed all code except for the patches!

Copy link
Collaborator

@gendx gendx left a comment

Choose a reason for hiding this comment

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

Mostly took a look at patches/tock, nothing major to flag assuming that a corresponding pull request will happen on https://github.com/tock/tock soon (if not yet started).

Comment on lines +8 to +9
-//! Minimal implementation to support activation of the reset button on
-//! nRF52-DK.
Copy link
Collaborator

Choose a reason for hiding this comment

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

What changed w.r.t. this comment? Is the code now complete, or does it support more features (but not all of them) than the reset button?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We already added support for voltage regulator and NFC pin protection.
This patch adds support for code read protection and the PR I'll prepare for Tock will add on top of that support for Nordic bootloader (2 words are stored in UICR and could be wiped, which could result in a bricked device).

@@ -0,0 +1,423 @@
diff --git a/boards/components/src/firmware_protection.rs b/boards/components/src/firmware_protection.rs
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there an upstream pull-request to discuss the capsule/component/chip additions (which should stay in this patch as shortly as possible)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not yet. I needed the feature for OpenSK first. Starting the PR for Tock is next but will clearly happen.

patches/tock/07-firmware-protect.patch Show resolved Hide resolved
patches/tock/07-firmware-protect.patch Show resolved Hide resolved
+//! # use kernel::static_init;
+//!
+//! let crp = static_init!(
+//! capsules::firware_protection::FirmwareProtection<'static>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

"firware" => "firmware"

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

+ }
+ }
+
+ fn set_protection(&self, level: ProtectionLevel) -> ReturnCode {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: might make sense to add a few blank lines to separate the logical parts within this function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

+ self.registers
+ .debugctrl
+ .write(DebugControl::CPUNIDEN::DISABLED + DebugControl::CPUFPBEN::DISABLED);
+ // TODO(jmichel): Kill bootloader if present
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Could make sense to be more specific than "kill" here, does it mean "disable" (temporarily), or "erase from flash", or something else?

Copy link
Collaborator Author

@jmichelp jmichelp Dec 14, 2020

Choose a reason for hiding this comment

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

Hopefully made it clearer.
When a bootloader is present and we want firmware protection we're left with 2 options:

  1. configure the bootloader to only accept signed updates, sign all our firmware, etc.
  2. prevent returning into bootloader and handle the firmware updates at Tock level.

I was experimenting with 2, thinking that erasing the 2 values stored in UICR would be enough but it bricked the dongles (still haven't found a way to recover them). I left a TODO at the moment but I guess the path will probably to modify deploy.py and go for signed firmware.

@jmichelp jmichelp dismissed stale reviews from kaczmarczyck and ia0 via f225b08 December 14, 2020 18:44
FIDO2 certification automation moved this from Reviewer approved to Review in progress Dec 14, 2020
Copy link
Member

@ia0 ia0 left a comment

Choose a reason for hiding this comment

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

LGTM for src/ctap/storage.rs. I glanced a bit at the rest though and LGTM to submit. We can improve details in Tock later.

FIDO2 certification automation moved this from Review in progress to Reviewer approved Dec 16, 2020
@jmichelp jmichelp merged commit 420d038 into google:master Dec 16, 2020
FIDO2 certification automation moved this from Reviewer approved to Done Dec 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Development

Successfully merging this pull request may close these issues.

Add vendor commands to inject crypto materials
5 participants