From 49c9d40e0f56479be1058d436c97a6c4fc892374 Mon Sep 17 00:00:00 2001 From: Chris Frantz Date: Wed, 4 Dec 2024 23:56:38 -0800 Subject: [PATCH] [signing] Support SPX signing with `hsmtool` 1. Enhance the signing rules to use the spx signing commands added to hsmtool. 2. Change the PKCS#11 provider from sc-hsm-embedded to opensc. - sc-hsm-embedded supports RSA3072 and ECDSA P256 signatures, but does not support CKO_DATA objects. - opensc supports ECDSA P256 signatures and CKO_DATA objects, but does not support RSA3072 signatures. We no longer use RSA3072 signatures for signing code; we _can_ use CKO_DATA objects to hold SPX keys for signing. Signed-off-by: Chris Frantz --- rules/signing.bzl | 54 ++++++++++++++++++++++++++---- signing/tokens/BUILD | 6 ++-- third_party/hsm/BUILD.opensc.bazel | 46 +++++++++++++++++++++++++ third_party/hsm/repos.bzl | 7 ++++ 4 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 third_party/hsm/BUILD.opensc.bazel diff --git a/rules/signing.bzl b/rules/signing.bzl index dc6aaeb5d0aa04..f82727124d968f 100644 --- a/rules/signing.bzl +++ b/rules/signing.bzl @@ -284,8 +284,17 @@ def _presigning_artifacts(ctx, opentitantool, src, manifest, ecdsa_key, rsa_key, executable = opentitantool, mnemonic = "PreSigningSpxMessage", ) - # TODO(cfrantz): After adding SPX support to hsmtool, append an appropriate - # signing directive here. + signing_directives.append(struct( + command = "spx-sign", + id = None, + label = spx_key.name, + little_endian = False, + # TODO(cfrantz): Update the format/domain when we can support pre-hashed signing. + format = "PlainText", + domain = "Pure", + output = "{}.spx_sig".format(basename), + input = "{}.spx-message".format(basename), + )) return struct(pre = pre, digest = digest, spxmsg = spxmsg, script = signing_directives) @@ -364,7 +373,7 @@ def _local_sign(ctx, tool, digest, ecdsa_key, rsa_key, spxmsg = None, spx_key = fail("Expected an ECDSA or RSA key") def _hsmtool_sign(ctx, tool, digest, ecdsa_key, rsa_key, spxmsg = None, spx_key = None, profile = None): - """Sign a digest with a local on-disk RSA private key. + """Sign a digest with a token-provided private key. Args: ctx: The rule context. @@ -378,8 +387,6 @@ def _hsmtool_sign(ctx, tool, digest, ecdsa_key, rsa_key, spxmsg = None, spx_key Returns: file, file, file: The RSA and SPX signature files. """ - if spxmsg or spx_key: - fail("hsmtool currently does not support SPX+ signing") if not profile: fail("Missing the `hsmtool` profile") @@ -387,11 +394,13 @@ def _hsmtool_sign(ctx, tool, digest, ecdsa_key, rsa_key, spxmsg = None, spx_key cmd = "rsa" sig = ctx.actions.declare_file(paths.replace_extension(digest.basename, ".rsa-sig")) label = rsa_key.name + mnemonic = "HsmtoolRsaSign" retval = (None, sig, None) elif ecdsa_key: cmd = "ecdsa" sig = ctx.actions.declare_file(paths.replace_extension(digest.basename, ".ecdsa-sig")) label = ecdsa_key.name + mnemonic = "HsmtoolEcdsaSign" retval = (sig, None, None) else: fail("Expected either rsa_key or ecdsa_key; got neither") @@ -416,9 +425,40 @@ def _hsmtool_sign(ctx, tool, digest, ecdsa_key, rsa_key, spxmsg = None, spx_key "no-sandbox": "", }, env = tool.env, - mnemonic = "HsmtoolRsaSign", + mnemonic = mnemonic, ) - return retval + + spx_sig = None + if spxmsg and spx_key: + spx_sig = ctx.actions.declare_file(paths.replace_extension(spxmsg.basename, ".spx-sig")) + ctx.actions.run( + outputs = [spx_sig], + inputs = [spxmsg, tool.tool] + tool.data, + arguments = [ + "--quiet", + "--lockfile=/tmp/hsmtool.lock", + "--profile={}".format(profile), + "spx", + "sign", + "--format=plain-text", + "--label={}".format(spx_key.name), + "--output={}".format(spx_sig.path), + spxmsg.path, + ], + executable = tool.tool, + execution_requirements = { + "no-sandbox": "", + }, + env = tool.env, + mnemonic = "HsmtoolSpxSign", + ) + + if rsa_key: + return None, sig, spx_sig + elif ecdsa_key: + return sig, None, spx_sig + else: + fail("Expected an ECDSA or RSA key") def _post_signing_attach(ctx, opentitantool, pre, ecdsa_sig, rsa_sig, spx_sig): """Attach signatures to an unsigned binary. diff --git a/signing/tokens/BUILD b/signing/tokens/BUILD index 603731365f0762..bc2110c7d3b4a6 100644 --- a/signing/tokens/BUILD +++ b/signing/tokens/BUILD @@ -15,9 +15,11 @@ signing_tool( signing_tool( name = "nitrokey", - data = ["@sc_hsm//:gen_dir"], + data = ["@opensc//:gen_dir"], env = { - "HSMTOOL_MODULE": "$(location @sc_hsm//:gen_dir)/lib/libsc-hsm-pkcs11.so", + "LD_LIBRARY_PATH": "$(location @opensc//:gen_dir)/lib", + "HSMTOOL_MODULE": "$(location @opensc//:gen_dir)/lib/opensc-pkcs11.so", + "HSMTOOL_SPX_MODULE": "pkcs11-ef", }, location = "token", tool = "//sw/host/hsmtool", diff --git a/third_party/hsm/BUILD.opensc.bazel b/third_party/hsm/BUILD.opensc.bazel new file mode 100644 index 00000000000000..7a00b5ea34b4d2 --- /dev/null +++ b/third_party/hsm/BUILD.opensc.bazel @@ -0,0 +1,46 @@ +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +load("@rules_foreign_cc//foreign_cc:configure.bzl", "configure_make") + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "all_srcs", + srcs = glob(["**"]), +) + +configure_make( + name = "opensc", + args = ["-j"], + autoreconf = True, + autoreconf_options = ["-fi"], + configure_in_place = True, + configure_options = [ + # Lie about the completions dir so that "make install" won't get + # confused about what is already installed on the host. + # This doesn't affect the build, as the installation happens in + # a bazel-supplied target directory, but it does prevent make from + # seeing the system's `/usr/share/bash-completion/completions` + # directory and complaining that it will be unable to install there. + "--with-completiondir=xxx-completions", + ], + lib_source = ":all_srcs", + out_shared_libs = [ + # It would be nice to configure this package to build `opensc-pkcs11.so` + # as a shared object that statically links `libopensc`, but I don't see + # a way to do that via the configure script. Users of this library + # will have to set LD_LIBRARY_PATH appropriately to see the needed + # shared libraries (e.g. `//signing/tokens:nitrokey`). + "libopensc.so", + "libopensc.so.12", + "opensc-pkcs11.so", + ], +) + +filegroup( + name = "gen_dir", + srcs = [":opensc"], + output_group = "gen_dir", +) diff --git a/third_party/hsm/repos.bzl b/third_party/hsm/repos.bzl index d011ad78a35f2c..8831153117e9e8 100644 --- a/third_party/hsm/repos.bzl +++ b/third_party/hsm/repos.bzl @@ -23,6 +23,13 @@ def hsm_repos(): strip_prefix = "sc-hsm-embedded-2.12", sha256 = "707fca9df630708e0e59a7d4a8a7a016c56c83a585957f0fd9f806c0762f1944", ) + http_archive( + name = "opensc", + build_file = Label("//third_party/hsm:BUILD.opensc.bazel"), + url = "https://github.com/OpenSC/OpenSC/archive/refs/tags/0.26.0.tar.gz", + strip_prefix = "OpenSC-0.26.0", + sha256 = "c692ac7639fa398f7f07b1070ea5358344000d49d08dcb825296d4cec94c6b1f", + ) http_archive( name = "cloud_kms_hsm", build_file = Label("//third_party/hsm:BUILD.cloud_kms_hsm.bazel"),