Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,7 @@ jobs:
- name: Set up Hatch
uses: pypa/hatch@257e27e51a6a5616ed08a39a408a21c35c9931bc # install
- name: Run CLI tests
run: hatch run cli:test
run: |
sudo apt update
sudo apt install softhsm2 gnutls-bin
hatch run cli:test
2 changes: 1 addition & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ jobs:
if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
sudo apt update
sudo apt install softhsm2 gnutls-bin
./scripts/pkcs11-tests/softhsm_setup setup
./scripts/tests/softhsm_setup setup
fi
hatch test -c -py ${{ matrix.python-version }} -m integration
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ All versions prior to 1.0.0 are untracked.
## [Unreleased]

### Added
-Added the `digest` subcommand to compute and print a model's digest. This enables other tools to easily pair the attestations with a model directory.
- Added the `digest` subcommand to compute and print a model's digest. This enables other tools to easily pair the attestations with a model directory.
- Added `--module-paths` option to PKCS #11 signing methods pkcs11-key and pkcs11-certificate.

### Changed
- Standardized CLI flags to use hyphens (e.g., `--trust-config` instead of `--trust_config`). Underscore variants are still accepted for backwards compatibility via token normalization.
Expand Down
45 changes: 0 additions & 45 deletions scripts/pkcs11-tests/test_pkcs11.sh

This file was deleted.

File renamed without changes.
112 changes: 112 additions & 0 deletions scripts/tests/test-pkcs11.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env bash

DIR=$(dirname "$0")

PATH=$PATH:${PWD}/${DIR}
TMPDIR=$(mktemp -d) || exit 1

cleanup() {
softhsm_setup teardown &>/dev/null
rm -rf "${TMPDIR}"
}
trap cleanup SIGTERM EXIT

if ! msg=$(softhsm_setup setup); then
echo -e "Could not setup softhsm:\n${msg}"
exit 77
fi
pkcs11uri=$(echo "${msg}" | sed -n 's|^keyuri: \(.*\)|\1|p')

model_sig=${TMPDIR}/model.sig
pub_key=${TMPDIR}/pubkey.pem
model_path=${TMPDIR}

# The SoftHSM PKCS #11 module is in a special path on Ubuntu
for p in "/usr/lib/pkcs11" "/usr/lib64/pkcs11" "/usr/lib/softhsm"; do
add_options+=" --module-paths ${p}"
done

if ! softhsm_setup getpubkey &>"${pub_key}"; then
echo -e "Could not get public key:\n${msg}"
echo "${pub_key}"
exit 77
fi

if ! python -m model_signing sign pkcs11-key \
--signature "${model_sig}" \
--pkcs11_uri "${pkcs11uri}" \
${add_options:+${add_options}} \
"${model_path}"; then
echo "Could not sign."
exit 77
fi

if ! python -m model_signing verify key \
--signature "${model_sig}" \
--public_key "${pub_key}" \
"${model_path}"; then
echo "Could not verify signature."
exit 77
fi

if type -P openssl >/dev/null; then
pub_key_cert=${TMPDIR}/pubkey-cert.pem
ca_key="${TMPDIR}/ca-key.pem"
ca_cert="${TMPDIR}/ca-cert.pem"
v3ext="${TMPDIR}/v3.ext"

if ! err=$(openssl req \
-new \
-x509 \
-nodes \
-days 3650\
-subj "/CN=MyRootCA" \
-keyout "${ca_key}" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign" \
-out "${ca_cert}" 2>&1); then
echo "Could not create CA certificate."
echo "${err}"
exit 77
fi
echo "Created CA."

cat <<- _EOF_ > "${v3ext}"
keyUsage=critical, digitalSignature
_EOF_

if ! err=$(openssl x509 \
-new \
-CAkey "${ca_key}" \
-CA "${ca_cert}" \
-force_pubkey "${pub_key}" \
-subj "/CN=MyCery" \
-extfile "${v3ext}" \
-out "${pub_key_cert}" 2>&1); then
echo "Could not create certificate for HSM public key."
echo "${err}"
exit 77
fi
echo "Signed HSM public key with CA key."

if ! python -m model_signing sign pkcs11-certificate \
--signature "${model_sig}" \
--pkcs11_uri "${pkcs11uri}" \
--signing_certificate "${pub_key_cert}" \
--certificate_chain "${pub_key_cert}" \
${add_options:+${add_options}} \
"${model_path}"; then
echo "Could not sign with pkcs11-certificate method."
exit 77
fi

if ! python -m model_signing verify certificate \
--signature "${model_sig}" \
--certificate_chain "${ca_cert}" \
"${model_path}"; then
echo "Could not verify signature created with pkcs11-certificate method."
exit 77
fi
fi

exit 0
22 changes: 21 additions & 1 deletion src/model_signing/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ def set_attribute(self, key, value):
help="PKCS #11 URI of the private key.",
)

# Decorator for paths to the PKCS #11 modules
_module_paths_option = click.option(
"--module-paths",
type=str,
metavar="PKCS11_MODULE_PATHS",
multiple=True,
help="PKCS #11 module paths.",
)

# Decorator for the commonly used option to pass a certificate chain to
# establish root of trust (when signing or verifying using certificates).
_certificate_root_of_trust_option = click.option(
Expand Down Expand Up @@ -507,13 +516,15 @@ def _sign_private_key(
@_allow_symlinks_option
@_write_signature_option
@_pkcs11_uri_option
@_module_paths_option
def _sign_pkcs11_key(
model_path: pathlib.Path,
ignore_paths: Iterable[pathlib.Path],
ignore_git_paths: bool,
allow_symlinks: bool,
signature: pathlib.Path,
pkcs11_uri: str,
module_paths: Iterable[str],
) -> None:
"""Sign using a private key using a PKCS #11 URI.

Expand All @@ -524,6 +535,9 @@ def _sign_pkcs11_key(
Traditionally, signing could be achieved by using a public/private key pair.
Pass the PKCS #11 URI of the signing key using `--pkcs11_uri`.

Paths in PKCS11_MODULE_PATHS provide access to PKCS #11 modules located
outside the default paths of /usr/lib/pkcs11 and /usr/lib64/pkcs11.

Note that this method does not provide a way to tie to the identity of the
signer, outside of pairing the keys. Also note that we don't offer key
management protocols.
Expand All @@ -533,7 +547,7 @@ def _sign_pkcs11_key(
model_path, list(ignore_paths) + [signature]
)
model_signing.signing.Config().use_pkcs11_signer(
pkcs11_uri=pkcs11_uri
pkcs11_uri=pkcs11_uri, module_paths=module_paths
).set_hashing_config(
model_signing.hashing.Config()
.set_ignored_paths(paths=ignored, ignore_git_paths=ignore_git_paths)
Expand Down Expand Up @@ -610,6 +624,7 @@ def _sign_certificate(
@_pkcs11_uri_option
@_signing_certificate_option
@_certificate_root_of_trust_option
@_module_paths_option
def _sign_pkcs11_certificate(
model_path: pathlib.Path,
ignore_paths: Iterable[pathlib.Path],
Expand All @@ -619,6 +634,7 @@ def _sign_pkcs11_certificate(
pkcs11_uri: str,
signing_certificate: pathlib.Path,
certificate_chain: Iterable[pathlib.Path],
module_paths: Iterable[str],
) -> None:
"""Sign using a certificate.

Expand All @@ -635,6 +651,9 @@ def _sign_pkcs11_certificate(
root of trust (this option can be repeated as needed, or all cerificates
could be placed in a single file).

Paths in PKCS11_MODULE_PATHS provide access to PKCS #11 modules located
outside the default paths of /usr/lib/pkcs11 and /usr/lib64/pkcs11.

Note that we don't offer certificate and key management protocols.
"""
try:
Expand All @@ -645,6 +664,7 @@ def _sign_pkcs11_certificate(
pkcs11_uri=pkcs11_uri,
signing_certificate=signing_certificate,
certificate_chain=certificate_chain,
module_paths=module_paths,
).set_hashing_config(
model_signing.hashing.Config()
.set_ignored_paths(paths=ignored, ignore_git_paths=ignore_git_paths)
Expand Down
2 changes: 1 addition & 1 deletion tests/_signing/pkcs11uri_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ class TestPkcs11SoftHSMSigning:
def run_softhsm_setup(self, cmd: str) -> tuple[bytes | None, int]:
curr_dir = os.path.dirname(os.path.realpath(__file__))
softhsm_setup = os.path.join(
curr_dir, "../../scripts/pkcs11-tests/softhsm_setup"
curr_dir, "../../scripts/tests/softhsm_setup"
)
result = subprocess.run([softhsm_setup, cmd], stdout=subprocess.PIPE)
return result.stdout, result.returncode
Expand Down
Loading