lowRISC / opentitan

OpenTitan: Open source silicon root of trust
https://www.opentitan.org
Apache License 2.0
2.6k stars 783 forks source link

[signing] Create a keyset definition for handling keys #18313

Open cfrantz opened 1 year ago

cfrantz commented 1 year ago

Description

Introduction

Currently, we have a number of private keys present in the codebase for the earlgrey product under //sw/device/silicon_creator/rom/keys/fake. These keys are used by the sign_bin rule (//rules:opentitan.bzl`) to apply RSA signatures to firmware images.

Soon, we will need to generate the real earlgrey product keys in an HSM and import the public components of those keys into the codebase. Part of the key generation process will be to initialize personal security tokens (e.g. Nitrokey or Yubikey) that hold the less privileged DEV and TEST keys, thus allowing developers to unilaterally sign firmware for DEV and TEST devices. Such signing will be required in order to run test code on sample silicon (ie: the so-called "fake keys" will no longer work).

In order to facilitate automated build and test flows, we'll need to have a better management strategy for the keys than "a bunch of files on disk". I'm assuming that we'll want building test code for silicon to be no more complicated than "insert your signing token into the usb port and type bazel build //the/target.

Background

Keys stored in an HSM are accessed and wielded via their HSM label. Signing a firmware image generally consists of 4 steps:

  1. Preparation: the software binary is built and the manifest header is updated with various information, including the public modulus of the signing key. This is called the "pre-signed binary".
  2. Creation of the digest: after the pre-signed binary is prepared, we compute a SHA256 digest over the signed region of the payload.
  3. Signing: the digest is signed by an RSA private key.
  4. Finalization: the signature is injected into the manifest header of the pre-signed binary, yielding the signed binary.

Proposal

In order to make this process easy, we should always refer to keys by a nickname. The nickname refers to the public component of the key during the preparation step and refers to the HSM resource during the signing step. I'd suggest that the nickname should be the same as the HSM label; I think the existing key names like test_key_0 (etc) are perfectly good nicknames.

We should group these keys together into a keyset that encapsulates the properties of a group of keys together. I've been thinking about something like this:

keyset(
    name = "fake_keys",
    keys = {
        # Mapping of key files to nicknames.
        "test_key_0_rsa_3072_exp_f4.der": "test_key_0",
        ...
    },
    # Locations are either "local" (private keys on disk) or "offline" (private keys in an hsm)
    location = "local",
    # Which tool is used to perform signing with this keyset: opentitantool or hsmtool.
    tool = "opentitantool",
)

When we want to sign something, we'll give the keyset name and key nickname to the sign_bin rule. To maintain compatibility with existing bazel infrastructure, the sign_bin rule will also accept a private key file and key nickname.

The opentitan_flash_binary rule currently accepts a dict of key nicknames to key files. This can easily be changed to a dict of key nicnames to keyset names.

The main advantage of migrating to using keysets is that the keyset carries with it the tooling information to perform the signing. The signing rules can use that information to invoke the correct tool to perform the signing operation (ie: opentitantool for fake keys, hsmtool for keys held in HSMs or tokens).

To discuss

cdgori commented 1 year ago

This is great.

Does this proposal need to consider access controls to a given keyset as well, or is that orthogonal (or somehow implied by location == "offline")?

At the risk of stating something that is probably really obvious to everyone - we want signing anything with PROD keys to be a significant choice and not one that can easily be triggered by a typo / wrong-argument (or likely automated at all, in all honesty).

DEV and TEST keys I am far less concerned about, though we might have to have some access controls there ultimately as well.

cfrantz commented 1 year ago

Access control to key material is implied by location = "offline".

Any user can attempt to sign with offline keys, but unless that user has a token and the unlock credentials for that token, such signing is impossible -- they will only ever get as far as generating the pre-signing artifacts.

cdgori commented 1 year ago

Thanks for the extra info. That all sounds like best practices to me.

msfschaffner commented 1 year ago

@cfrantz anything to update here?

cfrantz commented 1 year ago

19072 was a first attempt at addressing this, but I was not confident about merging it.

I'd like to address this as part of the current build rules refactoring and SiVal enablement.

msfschaffner commented 1 year ago

Ok triaging into SV0. Please move to another SV milestone as appropriate.