Signstar
This project provides tools and documentation for running a generic signing enclave with the help of a Hardware Security Module (HSM) as backend.
Raw cryptographic signatures and OpenPGP data signatures are supported.
The dedicated operating system Signstar OS makes use the tools developed in the Signstar project and provides central solution targeting commodity hardware.
Documentation
The project documentation for the Signstar project can be found at https://signstar.archlinux.page
Components
Signstar consists of several loosely coupled components, some of which are used in conjunction with one another.
- nethsm: A library and command-line interface to provide interaction with the Nitrokey NetHSM to applications
- signstar-common: Shared components and data types for Signstar tools and libraries
- signstar-config: Configuration file handling for Signstar hosts
- signstar-configure-build: A commandline interface for the configuration of Signstar system during build-time
- signstar-configure: An executable, that non-interactively configures a Signstar host, its user’s credentials and HSM backends
- signstar-crypto: Common types and functionality for cryptography in Signstar
- signstar-download-key-certificates: Library and executable for returning key certificates stored in backends of a Signstar host
- signstar-request-signature: An executable, run on a client host, that prepares data to be signed and retrieves a signature for it from a Signstar setup
- signstar-sign: An executable, that allows signing of messages with the help of an HSM, based on a configuration
- signstar-yubihsm2: Integration for YubiHSM2 devices as Signstar backend
Requirements
A Signstar setup requires a TPM-2.0-enabled host, allowing to run Signstar OS which provides a read-only root filesystem and an encrypted /var partition for its state.
This signing service host is connected to one or more HSM devices and exposes signstar-sign to clients of the signing service.
Clients use signstar-request-signature to connect to a Signstar setup and retrieve a signature for a provided payload.
Further details on the setup, as well as the threat model that the setup operates under can be found in the design documentation.
Packaging
The justfile contains recipes for generating integration useful for packaging:
just generate shell_completions nethsm-cligenerates shell completions for [nethsm-cli] to$CARGO_TARGET_DIR/output/shell_completions/(or to$PWD/output/shell_completions/if$CARGO_TARGET_DIRis unset)just generate manpages nethsm-cligenerates man pages for [nethsm-cli] to$CARGO_TARGET_DIR/output/manpages/(or to$PWD/output/manpages/if$CARGO_TARGET_DIRis unset)
The target directory is created automatically.
Releases
Releases of components are created by the developers of this project.
OpenPGP certificates with the following OpenPGP fingerprints can be used to verify signed tags:
991F6E3F0765CF6295888586139B09DA5BF0D338(David Runge <dvzrv@archlinux.org>)653909A2F0E37C106F5FAF546C8857E0D8E8F074(Wiktor Kwapisiewicz <wiktor@archlinux.org>)
Some of the above are part of archlinux-keyring and certified by at least three main signing keys of the distribution. All certificate are cross-signed and can be retrieved from OpenPGP keyservers.
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
NetHSM
A high-level library and command-line interface to interact with the API of a Nitrokey NetHSM.
The NetHSM is a hardware appliance, that serves as secure store for cryptographic keys. With the help of a REST API it is possible to communicate with the device (as well as the official nethsm container) for setup and various cryptographic actions.
The nethsm-sdk-rs library is auto-generated using openapi-generator. This leads to a broad API surface with sparse documentation, that this crate attempts to rectify with the help of a central struct used for authentication setup and communication.
As this crate is a wrapper around [nethsm_sdk_rs] it covers all available actions from provisioning, over key and user management to backup and restore.
The NetHSM provides dedicated user management based on a role system (see [UserRole]) which can be used to separate concerns.
Each user has exactly one role.
With the help of a namespace concept, it is possible to segregate users and their keys into secluded groups.
Notably, this introduces R-Administrators (system-wide users in the [Administrator][UserRole::Administrator] role), which have access to all system-wide actions, but can not modify users and keys in a namespace and N-Administrators (namespace users in the [Administrator][UserRole::Administrator] role), which have access only to actions towards users and keys in their own namespace.
Namespace users in the [Operator][UserRole::Operator] role only have access to keys in their own namespace, while system-wide users only have access to system-wide keys.
The cryptographic key material on the NetHSM can be assigned to one or several tags.
Users in the [Operator][UserRole::Operator] role can be assigned to the same tags to gain access to the respective keys.
Using the central [NetHsm] struct it is possible to establish a TLS connection for multiple users and all available operations.
TLS validation can be configured based on a variant of the [ConnectionSecurity] enum:
- [
ConnectionSecurity::Unsafe]: The host certificate is not validated. - [
ConnectionSecurity::RootCertificates]: The host certificate is validated based on configurable fingerprints. - [
ConnectionSecurity::Native]: The host certificate is validated using the native Operating System trust store.
Documentation
- https://signstar.archlinux.page/rustdoc/nethsm/ for development version of the crate
- https://docs.rs/nethsm/latest/nethsm/ for released versions of the crate
Apart from the crate specific documentation it is very recommended to read the canonical upstream documentation as well: https://docs.nitrokey.com/nethsm/
Testing
This library is integration tested against Nitrokey’s official nethsm container. To run these long running tests a podman installation is required. The tests handle the creation and teardown of containers as needed.
cargo test --all -- --ignored
Re-exports
This crate relies on a set of external crates in its own public API.
Re-exports ensure that the respective dependencies do not have to be relied upon directly by consumers:
- [
chrono::DateTime] - [
chrono::Utc] - [
nethsm_sdk_rs::models::DistinguishedName] - [
nethsm_sdk_rs::models::InfoData] - [
nethsm_sdk_rs::models::LoggingConfig] - [
nethsm_sdk_rs::models::NetworkConfigInput] - [
nethsm_sdk_rs::models::NetworkConfigOutput] - [
nethsm_sdk_rs::models::PublicKey] - [
nethsm_sdk_rs::models::SystemInfo] - [
nethsm_sdk_rs::models::SystemState] - [
nethsm_sdk_rs::models::SystemUpdateData] - [
nethsm_sdk_rs::models::UserData]
Examples
Library
Establish a connection with a Nitrokey NetHSM and manage credentials:
use nethsm::{Connection, ConnectionSecurity, Credentials, NetHsm, Passphrase};
fn main() -> testresult::TestResult {
// Create a new connection to a NetHSM at "https://example.org" using admin credentials
let nethsm = NetHsm::new(
Connection::new(
"https://example.org/api/v1".try_into()?,
ConnectionSecurity::Unsafe,
),
Some(Credentials::new("admin".parse()?, Some(Passphrase::new("passphrase".to_string())))),
None,
None,
)?;
// Connections can be initialized without any credentials and more than one can be provided later on
let nethsm = NetHsm::new(
Connection::new(
"https://example.org/api/v1".try_into()?,
ConnectionSecurity::Unsafe,
),
None,
None,
None,
)?;
nethsm.add_credentials(Credentials::new("admin".parse()?, Some(Passphrase::new("passphrase".to_string()))));
nethsm.add_credentials(Credentials::new("user1".parse()?, Some(Passphrase::new("other_passphrase".to_string()))));
// A set of credentials must be used before establishing a connection with the configured NetHSM
nethsm.use_credentials(&"user1".parse()?)?;
Ok(())
}
Backup
Backup features require enabling the backup feature of this crate.
Listing all fields in a backup file:
#![allow(unused)]
fn main() {
#[cfg(feature = "backup")] {
use std::collections::HashMap;
use nethsm::backup::Backup;
let backup = Backup::parse(std::fs::File::open("tests/nethsm.backup-file.bkp")?)?;
let decryptor = backup.decrypt(b"my-very-unsafe-backup-passphrase")?;
assert_eq!(decryptor.version()?, [0]);
for item in decryptor.items_iter() {
let (key, value) = item?;
println!("Found {key} with value: {value:X?}");
}
} testresult::ok(())
}
Dumping the value of one specified field (here /config/version):
#![allow(unused)]
fn main() {
#[cfg(feature = "backup")] {
use std::collections::HashMap;
use nethsm::backup::Backup;
let backup = Backup::parse(std::fs::File::open("tests/nethsm.backup-file.bkp")?)?;
let decryptor = backup.decrypt(b"my-very-unsafe-backup-passphrase")?;
assert_eq!(decryptor.version()?, [0]);
for (key, value) in decryptor
.items_iter()
.flat_map(|item| item.ok())
.filter(|(key, _)| key == "/config/version")
{
println!("Found {key} with value: {value:X?}");
}
} testresult::ok(())
}
CLI
Installation
The CLI can be installed using cargo:
cargo install nethsm --locked --features=cli
Afterwards the nethsm executable is available.
It is recommended to refer to the extensive --help output of the executable and its subcommands.
Usage
The following assumes a recent version of openssl and podman.
Start a test container
just run-nethsm-container
The tag of the upstream NetHSM container to use can be modified using the global nethsm_image_tag just variable.
To retrieve the value which represents the latest testing release:
just get-latest-nethsm-release-short-commit
Configuration file
The configuration file uses the TOML format.
By default an Operating System specific, well-defined configuration file location is chosen.
Using -c / --config / the NETHSM_CONFIG environment variable it is possible to provide a custom configuration file location.
# use a custom, temporary directory for all generated files
nethsm_tmpdir="$(mktemp --directory --suffix '.nethsm-test')"
# set a custom, temporary configuration file location
export NETHSM_CONFIG="$(mktemp --tmpdir="$nethsm_tmpdir" --suffix '-nethsm.toml' --dry-run)"
To be able to interact with a NetHSM (or the testing container), each device must be added to the configuration file.
# add the container using unsafe TLS connection handling for testing
nethsm env add device --label test https://localhost:8443/api/v1 Unsafe
If only one device environment is configured, it is used by default when issuing nethsm commands.
If more than one environment is configured, the target device must be selected using the global -l/ --label option.
The handling of credentials is flexible: Credentials can be stored in the configuration file with or without passphrases or not at all. If credentials are not configured, they are prompted for interactively.
# prepare a temporary passphrase file for the initial admin user passphrase
nethsm_admin_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.admin-passphrase.txt')"
export NETHSM_PASSPHRASE_FILE="$nethsm_admin_passphrase_file"
printf 'my-very-unsafe-admin-passphrase' > "$NETHSM_PASSPHRASE_FILE"
# add the default admin user credentials
nethsm env add credentials admin Administrator
Provisioning
Before using a device for the first time, it must be provisioned. This includes setting the passphrase for the initial “admin” user, the unlock passphrase and the system time of the device.
# prepare a temporary passphrase file for the initial unlock passphrase
export NETHSM_UNLOCK_PASSPHRASE_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.unlock-passphrase.txt')"
printf 'my-very-unsafe-unlock-passphrase' > "$NETHSM_UNLOCK_PASSPHRASE_FILE"
# reuse the initial admin passphrase
export NETHSM_ADMIN_PASSPHRASE_FILE="$nethsm_admin_passphrase_file"
nethsm provision
Users
Each user may be in exactly one role (“Administrator”, “Operator”, “Metrics” or “Backup”). Users either exist system-wide or in a “Namespace”. Users in a Namespace only have access to users and keys in their own Namespace and are not able to interact with system-wide facilities. System-wide users on the other hand are not able to access keys or manipulate users in a Namespace, but can interact with other system-wide facilities, as well as system-wide users and keys.
- “Administrator”: for adjusting system configuration, managing users and keys (may exist in a Namespace or system-wide)
- R-Administrator: a system-wide Administrator which is able to interact with all system-wide facilities, as well as managing system-wide users and keys
- N-Administrator: a namespace Administrator, which is only able to operate on users and keys in their own namespace
- “Operator”: for using cryptographic keys and getting random bytes (may exist in a Namespace or system-wide)
- “Metrics”: for retrieving metrics of a device (may only exist system-wide)
- “Backup”: for creating and downloading backups of a device (may only exist system-wide)
System-wide and namespace users are easily distinguishable: While system-wide user names consist only of characters in the set [a-z0-9] (e.g. admin1), namespace user names consist of characters in the set [a-z0-9~] and start with the namespace name (e.g. namespace1~admin1).
nethsm_admin1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.admin1-passphrase.txt')"
printf 'my-very-unsafe-admin1-passphrase' > "$nethsm_admin1_passphrase_file"
nethsm_operator1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.operator1-passphrase.txt')"
printf 'my-very-unsafe-operator1-passphrase' > "$nethsm_operator1_passphrase_file"
nethsm_backup1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.backup1-passphrase.txt')"
printf 'my-very-unsafe-backup1-passphrase' > "$nethsm_backup1_passphrase_file"
nethsm_metrics1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.metrics1-passphrase.txt')"
printf 'my-very-unsafe-metrics1-passphrase' > "$nethsm_metrics1_passphrase_file"
nethsm_namespace1_admin1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.namespace1-admin1-passphrase.txt')"
printf 'my-very-unsafe-namespace1-admin1-passphrase' > "$nethsm_namespace1_admin1_passphrase_file"
nethsm_namespace1_operator1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.namespace1-operator1-passphrase.txt')"
printf 'my-very-unsafe-namespace1-operator1-passphrase' > "$nethsm_namespace1_operator1_passphrase_file"
# we create a user in each role and add the credentials including passphrases to the configuration
# NOTE: this is for testing purposes! passphrases stored in configuration files can easily be retrieved!
export NETHSM_PASSPHRASE_FILE="$nethsm_admin1_passphrase_file"
nethsm user add "Some Admin1" Administrator admin1
nethsm env add credentials admin1 Administrator
export NETHSM_PASSPHRASE_FILE="$nethsm_operator1_passphrase_file"
nethsm user add "Some Operator1" Operator operator1
nethsm env add credentials operator1 Operator
export NETHSM_PASSPHRASE_FILE="$nethsm_backup1_passphrase_file"
nethsm user add "Some Backup1" Backup backup1
nethsm env add credentials backup1 Backup
export NETHSM_PASSPHRASE_FILE="$nethsm_metrics1_passphrase_file"
nethsm user add "Some Metrics1" Metrics metrics1
nethsm env add credentials metrics1 Metrics
# we also create an admin for a namespace "namespace1" and create that namespace
export NETHSM_PASSPHRASE_FILE="$nethsm_namespace1_admin1_passphrase_file"
nethsm user add "Namespace1 Admin1" Administrator namespace1~admin1
nethsm env add credentials namespace1~admin1 Administrator
nethsm --user admin namespace add namespace1
# now the N-Administrator can create further users in that namespace
export NETHSM_PASSPHRASE_FILE="$nethsm_operator1_passphrase_file"
nethsm --user namespace1~admin1 user add "Namespace1 Operator1" Operator namespace1~operator1
nethsm env add credentials namespace1~operator1 Operator
# NOTE: from now on we have to be *specific* about which Administrator and which Operator user to use for each action as we have multiple and `nethsm` opportunistically selects the first it finds!
# show the configured environments in the configuration file
nethsm env list
The user names and accompanying information can be queried:
# the R-Administrator can see all users
while read -r user; do
nethsm --user admin1 user get "$user"
done < <(nethsm --user admin1 user list)
# the N-Administrator can only see the users in its own namespace
while read -r user; do
nethsm --user namespace1~admin1 user get "$user"
done < <(nethsm --user namespace1~admin1 user list)
Tags for users can only be created once keys with those tags exists.
Keys
Keys on the device are managed using users in the “Administrator” role. Depending on restrictions (tags), the keys may then be used by users in the “Operator” role.
NOTE: Keys created by an N-Administrator are only visible within their namespace and only available to Operator users in that namespace.
Generating keys
Below, we are generating keys of all available types (Curve25519, EcP256, EcP384, EcP521, Generic and Rsa). When generating a key, the unique ID for it may be set manually (else it is auto-generated). Tags, which later on allow users access to the keys may also be set during key generation.
Note that some keys require to set the key bit length (i.e. Generic and Rsa).
# keys created by the R-Administrator are only available to system-wide Operator users!
nethsm --user admin1 key generate --key-id signing1 --tags tag1 Curve25519 EdDsaSignature
nethsm --user admin1 key generate --key-id signing3 --tags tag2 EcP256 EcdsaSignature
nethsm --user admin1 key generate --key-id signing4 --tags tag2 EcP384 EcdsaSignature
nethsm --user admin1 key generate --key-id signing5 --tags tag2 EcP521 EcdsaSignature
nethsm --user admin1 key generate --key-id encdec1 --tags tag3 --length 128 Generic AesDecryptionCbc AesEncryptionCbc
nethsm --user admin1 key generate --key-id dec1 --tags tag4 --length 2048 Rsa RsaDecryptionPkcs1
nethsm --user admin1 key generate --key-id signing6 --tags tag5 --length 2048 Rsa RsaSignaturePssSha512
nethsm --user admin1 key generate --key-id signing8 --tags tag6 --length 2048 Rsa RsaSignaturePkcs1
# keys created by the N-Administrator are only available to Operator users in the same namespace!
nethsm --user namespace1~admin1 key generate --key-id signing1 --tags tag1 Curve25519 EdDsaSignature
nethsm --user namespace1~admin1 key generate --key-id signing3 --tags tag2 EcP256 EcdsaSignature
nethsm --user namespace1~admin1 key generate --key-id signing4 --tags tag2 EcP384 EcdsaSignature
nethsm --user namespace1~admin1 key generate --key-id signing5 --tags tag2 EcP521 EcdsaSignature
nethsm --user namespace1~admin1 key generate --key-id encdec1 --length 128 --tags tag3 Generic AesDecryptionCbc AesEncryptionCbc
nethsm --user namespace1~admin1 key generate --key-id dec1 --length 2048 --tags tag4 Rsa RsaDecryptionPkcs1
nethsm --user namespace1~admin1 key generate --key-id signing6 --length 2048 --tags tag5 Rsa RsaSignaturePssSha512
nethsm --user namespace1~admin1 key generate --key-id signing8 --length 2048 --tags tag6 Rsa RsaSignaturePkcs1
All key IDs on the device and info about them can be listed:
# R-Administrators can only see system-wide keys
while read -r key; do
nethsm key get "$key"
done < <(nethsm --user admin1 key list)
# N-Administrators can only see keys in their own namespace
while read -r key; do
nethsm key get "$key"
done < <(nethsm --user namespace1~admin1 key list)
Importing keys
Keys can also be imported:
ed25519_cert_pem="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.ed25519_cert.pem')"
ed25519_cert_der="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.ed25519_cert.pkcs8.der')"
openssl genpkey -algorithm ed25519 -out "$ed25519_cert_pem"
openssl pkcs8 -topk8 -inform pem -in "$ed25519_cert_pem" -outform der -nocrypt -out "$ed25519_cert_der"
# import supports PKCS#8 private key in ASN.1 DER-encoded format, by default
nethsm --user admin1 key import --key-id signing7 Curve25519 "$ed25519_cert_der" EdDsaSignature
# however, importing a PKCS#8 private key in ASN.1 PEM-encoded format is supported, too
nethsm --user admin1 key import --format PEM --key-id signing9 Curve25519 "$ed25519_cert_pem" EdDsaSignature
# forgot to set a tag for key signing7 so that operator1 has access!
nethsm --user admin1 key tag signing7 tag1
# show information about the new key
nethsm --user operator1 key get signing7
# the same for namespace1
# import supports PKCS#8 private key in ASN.1 DER-encoded format, by default
nethsm --user namespace1~admin1 key import --key-id signing7 Curve25519 "$ed25519_cert_der" EdDsaSignature
# however, importing a PKCS#8 private key in ASN.1 PEM-encoded format is supported, too
nethsm --user namespace1~admin1 key import --format PEM --key-id signing9 Curve25519 "$ed25519_cert_pem" EdDsaSignature
# forgot to set a tag for key signing7 so that namespace1~operator1 has access!
nethsm --user namespace1~admin1 key tag signing7 tag1
# show information about the new key
nethsm --user namespace1~operator1 key get signing7
Access to keys
To provide access to keys for users, the users have to be tagged with the same tags as the keys.
# an R-Administrator can only modify system-wide users
nethsm --user admin1 user tag operator1 tag1
# an N-Administrator can only modify namespace users
nethsm --user namespace1~admin1 user tag namespace1~operator1 tag1
Signing messages
export NETHSM_KEY_SIGNATURE_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.readme.signature.sig')"
export NETHSM_KEY_PUBKEY_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.pubkey.pem')"
message_digest="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.message.dgst')"
# if we add the tag2 tag for operator1 it is able to use signing{2-5}
nethsm --user admin1 user tag operator1 tag2
# if we add the tag5 tag for operator1 it is able to use signing6
nethsm --user admin1 user tag operator1 tag5
nethsm --user admin1 user tag operator1 tag6
# we made the same tags available in namespace1, so the examples work similarly
# if we add the tag2 tag for namespace1~operator1 it is able to use signing{2-5}
nethsm --user namespace1~admin1 user tag namespace1~operator1 tag2
# if we add the tag5 tag for namespace1~operator1 it is able to use signing6
nethsm --user namespace1~admin1 user tag namespace1~operator1 tag5
nethsm --user namespace1~admin1 user tag namespace1~operator1 tag6
# create a signature with each key type
nethsm --user operator1 key sign --force signing1 EdDsa README.md
nethsm --user operator1 key public-key --force signing1
openssl pkeyutl -verify -in README.md -rawin -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm --user operator1 key sign --force signing3 EcdsaP256 README.md
nethsm --user operator1 key public-key --force signing3
openssl dgst -sha256 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm --user operator1 key sign --force signing4 EcdsaP384 README.md
nethsm --user operator1 key public-key --force signing4
openssl dgst -sha384 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm --user operator1 key sign --force signing5 EcdsaP521 README.md
nethsm --user operator1 key public-key --force signing5
openssl dgst -sha512 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm --user operator1 key sign --force signing6 PssSha512 README.md
nethsm --user operator1 key public-key --force signing6
openssl dgst -sha512 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha512 -pkeyopt rsa_pss_saltlen:-1
nethsm --user operator1 key sign --force signing7 EdDsa README.md
nethsm --user operator1 key public-key --force signing7
openssl pkeyutl -verify -in README.md -rawin -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
# the same of course also works in a namespace!
nethsm --user namespace1~operator1 key sign --force signing1 EdDsa README.md
nethsm --user namespace1~operator1 key public-key --force signing1
openssl pkeyutl -verify -in README.md -rawin -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm --user namespace1~operator1 key sign --force signing3 EcdsaP256 README.md
nethsm --user namespace1~operator1 key public-key --force signing3
openssl dgst -sha256 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm --user namespace1~operator1 key sign --force signing4 EcdsaP384 README.md
nethsm --user namespace1~operator1 key public-key --force signing4
openssl dgst -sha384 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm --user namespace1~operator1 key sign --force signing5 EcdsaP521 README.md
nethsm --user namespace1~operator1 key public-key --force signing5
openssl dgst -sha512 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm --user namespace1~operator1 key sign --force signing6 PssSha512 README.md
nethsm --user namespace1~operator1 key public-key --force signing6
openssl dgst -sha512 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha512 -pkeyopt rsa_pss_saltlen:-1
nethsm --user namespace1~operator1 key sign --force signing7 EdDsa README.md
nethsm --user namespace1~operator1 key public-key --force signing7
openssl pkeyutl -verify -in README.md -rawin -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
# if we remove the tag5 tag for operator1 it is no longer able to use signing6
nethsm --user admin1 user untag operator1 tag5
# analogous: if we remove the tag5 tag for namespace1~operator1 it is no longer able to use signing6
nethsm --user namespace1~admin1 user untag namespace1~operator1 tag5
OpenPGP
The CLI can also create OpenPGP certificates for keys stored in the HSM:
export GNUPGHOME="$(mktemp --directory --tmpdir="$nethsm_tmpdir" --suffix 'gnupghome')"
export NETHSM_KEY_CERT_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.openpgp-cert.pgp')"
nethsm --user admin1 --user operator1 openpgp add --can-sign signing1 "Test signing1 key <test@example.org>"
nethsm --user operator1 key cert get --force signing1
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing1 key"
nethsm --user admin1 --user operator1 openpgp add signing3 "Test signing3 key <test@example.org>"
nethsm --user operator1 key cert get --force signing3
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing3 key"
nethsm --user admin1 --user operator1 openpgp add signing4 "Test signing4 key <test@example.org>"
nethsm --user operator1 key cert get --force signing4
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing4 key"
nethsm --user admin1 --user operator1 openpgp add signing5 "Test signing5 key <test@example.org>"
nethsm --user operator1 key cert get --force signing5
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing5 key"
nethsm --user admin1 --user operator1 openpgp add signing8 "Test signing8 key <test@example.org>"
nethsm --user operator1 key cert get --force signing8
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing8 key"
# all of this works with our namespaced keys as well of course!
nethsm --user namespace1~admin1 --user namespace1~operator1 openpgp add --can-sign signing1 "Test signing1 key <test@example.org>"
nethsm --user namespace1~operator1 key cert get --force signing1
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing1 key"
nethsm --user namespace1~admin1 --user namespace1~operator1 openpgp add signing3 "Test signing3 key <test@example.org>"
nethsm --user namespace1~operator1 key cert get --force signing3
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing3 key"
nethsm --user namespace1~admin1 --user namespace1~operator1 openpgp add signing4 "Test signing4 key <test@example.org>"
nethsm --user namespace1~operator1 key cert get --force signing4
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing4 key"
nethsm --user namespace1~admin1 --user namespace1~operator1 openpgp add signing5 "Test signing5 key <test@example.org>"
nethsm --user namespace1~operator1 key cert get --force signing5
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing5 key"
nethsm --user namespace1~admin1 --user namespace1~operator1 openpgp add signing8 "Test signing8 key <test@example.org>"
nethsm --user namespace1~operator1 key cert get --force signing8
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing8 key"
Importing new keys:
export NETHSM_OPENPGP_TSK_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.openpgp-private-key.tsk')"
rsop generate-key --no-armor --signing-only "Test signing10 key <test@example.org>" > "$NETHSM_OPENPGP_TSK_FILE"
nethsm --user admin1 openpgp import --key-id signing10 --tags tag1
# openpgp import automatically stores the certificate so it can be fetched
nethsm --user operator1 key cert get --force signing10
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing10 key"
nethsm --user namespace1~admin1 openpgp import --key-id signing10 --tags tag1
# openpgp import automatically stores the certificate so it can be fetched
nethsm --user namespace1~operator1 key cert get --force signing10
gpg --import "$NETHSM_KEY_CERT_OUTPUT_FILE"
rpacket dump "$NETHSM_KEY_CERT_OUTPUT_FILE" | grep "Test signing10 key"
Signing messages:
export NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.openpgp-message.txt.sig')"
export NETHSM_OPENPGP_SIGNATURE_MESSAGE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.openpgp-message.txt')"
printf "I like strawberries\n" > "$NETHSM_OPENPGP_SIGNATURE_MESSAGE"
for key in signing1 signing3 signing4 signing5 signing8 signing10; do
nethsm --user operator1 openpgp sign --force "$key"
gpg --verify "$NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE" "$NETHSM_OPENPGP_SIGNATURE_MESSAGE"
nethsm --user operator1 key cert get --force "$key"
rsop verify "$NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE" "$NETHSM_KEY_CERT_OUTPUT_FILE" < "$NETHSM_OPENPGP_SIGNATURE_MESSAGE"
sqop verify "$NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE" "$NETHSM_KEY_CERT_OUTPUT_FILE" < "$NETHSM_OPENPGP_SIGNATURE_MESSAGE"
nethsm --user namespace1~operator1 openpgp sign --force "$key"
gpg --verify "$NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE" "$NETHSM_OPENPGP_SIGNATURE_MESSAGE"
nethsm --user namespace1~operator1 key cert get --force "$key"
rsop verify "$NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE" "$NETHSM_KEY_CERT_OUTPUT_FILE" < "$NETHSM_OPENPGP_SIGNATURE_MESSAGE"
done
signstar-request-signature prepare "$NETHSM_OPENPGP_SIGNATURE_MESSAGE" | tee "${NETHSM_OPENPGP_SIGNATURE_MESSAGE}.json"
nethsm openpgp sign-state --force "signing1" "${NETHSM_OPENPGP_SIGNATURE_MESSAGE}.json"
# the signature is always armored
grep -- "-----BEGIN PGP SIGNATURE-----" "$NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE"
gpg --verify "$NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE" "$NETHSM_OPENPGP_SIGNATURE_MESSAGE"
rpacket dump "$NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE"
sha512sum "$NETHSM_OPENPGP_SIGNATURE_MESSAGE"
jq < "${NETHSM_OPENPGP_SIGNATURE_MESSAGE}.json"
Encrypting messages
Messages can be encrypted using keys that offer the key mechanisms for this operation.
message="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.message.txt')"
printf "Hello World! This is a message!!" > "$message"
export NETHSM_KEY_ENCRYPT_IV="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.iv.txt')"
printf "This is unsafe!!" > "$NETHSM_KEY_ENCRYPT_IV"
export NETHSM_KEY_ENCRYPT_OUTPUT="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.symmetric-encrypted-message.txt.enc')"
# the initialization vector for decryption must be the same
export NETHSM_KEY_DECRYPT_IV="$NETHSM_KEY_ENCRYPT_IV"
export NETHSM_KEY_DECRYPT_OUTPUT="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.decrypted-message.txt')"
# we need to provide access to the key by tagging the user
nethsm --user admin1 user tag operator1 tag3
# let's use our symmetric encryption key to encrypt a message
nethsm --user operator1 key encrypt --force encdec1 "$message"
# now let's decrypt the encrypted message again
nethsm --user operator1 key decrypt --force encdec1 "$NETHSM_KEY_ENCRYPT_OUTPUT" AesCbc
cat "$NETHSM_KEY_DECRYPT_OUTPUT"
[[ "$(b2sum "$NETHSM_KEY_DECRYPT_OUTPUT" | cut -d ' ' -f1)" == "$(b2sum "$message" | cut -d ' ' -f1)" ]]
# this works analogously in a namespace
# we need to provide access to the key by tagging the user
nethsm --user namespace1~admin1 user tag namespace1~operator1 tag3
# let's use our symmetric encryption key to encrypt a message
nethsm --user namespace1~operator1 key encrypt --force encdec1 "$message"
# now let's decrypt the encrypted message again
nethsm --user namespace1~operator1 key decrypt --force encdec1 "$NETHSM_KEY_ENCRYPT_OUTPUT" AesCbc
cat "$NETHSM_KEY_DECRYPT_OUTPUT"
[[ "$(b2sum "$NETHSM_KEY_DECRYPT_OUTPUT" | cut -d ' ' -f1)" == "$(b2sum "$message" | cut -d ' ' -f1)" ]]
The same works for asymmetric keys as well:
# unset the initialization vectors as we do not need them for this
unset NETHSM_KEY_DECRYPT_IV NETHSM_KEY_ENCRYPT_IV
asymmetric_enc_message="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.asymmetric-encrypted-message.txt.enc')"
# we need to provide access to the key by tagging the user
nethsm --user admin1 user tag operator1 tag4
# retrieve the public key of the key to use (and overwrite any previously existing)
nethsm --user operator1 key public-key --force dec1
# encrypt the previous message
openssl pkeyutl -encrypt -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin -in "$message" -out "$asymmetric_enc_message"
# decrypt the asymmetrically encrypted message and replace any existing output
nethsm --user operator1 key decrypt --force dec1 "$asymmetric_enc_message" Pkcs1
cat "$NETHSM_KEY_DECRYPT_OUTPUT"
[[ "$(b2sum "$NETHSM_KEY_DECRYPT_OUTPUT" | cut -d ' ' -f1)" == "$(b2sum "$message" | cut -d ' ' -f1)" ]]
# this works analogously in a namespace
# we need to provide access to the key by tagging the user
nethsm --user namespace1~admin1 user tag namespace1~operator1 tag4
# retrieve the public key of the key to use (and overwrite any previously existing)
nethsm --user namespace1~operator1 key public-key --force dec1
# encrypt the previous message
openssl pkeyutl -encrypt -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin -in "$message" -out "$asymmetric_enc_message"
# decrypt the asymmetrically encrypted message and replace any existing output
nethsm --user namespace1~operator1 key decrypt --force dec1 "$asymmetric_enc_message" Pkcs1
cat "$NETHSM_KEY_DECRYPT_OUTPUT"
[[ "$(b2sum "$NETHSM_KEY_DECRYPT_OUTPUT" | cut -d ' ' -f1)" == "$(b2sum "$message" | cut -d ' ' -f1)" ]]
Public key
Administrators and operators can retrieve the public key of any key:
# when NETHSM_KEY_PUBKEY_OUTPUT_FILE is set, the public key is written to that file
# to print to stdout, we unset the environment variable
unset NETHSM_KEY_PUBKEY_OUTPUT_FILE
# keys of type "Generic" don't have a public key, so we do not request them
for key in signing1 signing{3..8} dec1; do
nethsm --user operator1 key public-key --force "$key"
# in our namespace1 we have keys of the same name, that we can get public keys for
nethsm --user namespace1~operator1 key public-key --force "$key"
done
Certificate Signing Requests for keys
Certificate Signing Requests for a particular target can be issued using the keys.
# get a CSR for example.com
nethsm --user operator1 key csr signing7 example.com
# also for the key of the same name in our namespace
nethsm --user namespace1~operator1 key csr signing7 example.com
Random bytes
The device can generate an arbitrary number of random bytes on demand. All users in the “Operator” role have access to this functionality!
export NETHSM_RANDOM_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.random.txt')"
nethsm random 200
[[ -f "$NETHSM_RANDOM_OUTPUT_FILE" ]]
Metrics
The metrics for a device can only be retrieved by a system-wide user in the “Metrics” role.
nethsm metrics
Device configuration
Several aspects of the device configuration can be retrieved and modified. The device configuration is only available to “R-Administrators” (system-wide users in the “Administrator” role).
Boot mode
The boot mode defines whether the system starts into “Locked” or “Operational” state (the former requiring to supply the unlock passphrase to get to “Operational” state).
nethsm --user admin1 config get boot-mode
# let's set it to unattended
nethsm --user admin1 config set boot-mode Unattended
nethsm --user admin1 config get boot-mode
Logging
Each device may send syslog to a remote host.
nethsm --user admin1 config get logging
Network
The devices have a unique and static network configuration.
nethsm --user admin1 config get network
System Time
The device’s system time can be queried and set.
nethsm --user admin1 config get time
nethsm --user admin1 config set time
nethsm --user admin1 config get time
TLS certificate
We can get and set the TLS certificate used for the device.
nethsm --user admin1 config get tls-certificate
# this generates a new RSA 4096bit certificate on the device
nethsm --user admin1 config set tls-generate Rsa 4096
nethsm --user admin1 config get tls-certificate
We can also receive only the public key for the TLS certificate:
nethsm --user admin1 config get tls-public-key
Or generate a Certificate Signing Request for the TLS certificate:
nethsm --user admin1 config get tls-csr example.com
Setting passphrases
The backup passphrase is used to decrypt a backup created for the device, when importing. By default it is the empty string ("").
nethsm_backup_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.backup-passphrase.txt')"
printf 'my-very-unsafe-backup-passphrase' > "$nethsm_backup_passphrase_file"
export NETHSM_NEW_PASSPHRASE_FILE="$nethsm_backup_passphrase_file"
nethsm_initial_backup_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.initial-backup-passphrase.txt')"
touch "$nethsm_initial_backup_passphrase_file"
export NETHSM_OLD_PASSPHRASE_FILE="$nethsm_initial_backup_passphrase_file"
nethsm --user admin1 config set backup-passphrase
The unlock passphrase is set during initial provisioning and is used to unlock the device when it is locked.
export NETHSM_OLD_PASSPHRASE_FILE="$NETHSM_UNLOCK_PASSPHRASE_FILE"
export NETHSM_NEW_PASSPHRASE_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.unlock-passphrase.txt')"
printf 'my-new-unsafe-unlock-passphrase' > "$NETHSM_NEW_PASSPHRASE_FILE"
nethsm --user admin1 config set unlock-passphrase
Locking
The device can be locked and unlocked, which puts it into state "Locked" and "Operational", respectively.
nethsm --user admin1 lock
nethsm --user admin1 health state
nethsm --user admin1 health alive
# as we have changed the unlock passphrase, we need to provide the new one
export NETHSM_UNLOCK_PASSPHRASE_FILE="$NETHSM_NEW_PASSPHRASE_FILE"
nethsm unlock
nethsm --user admin1 health state
nethsm --user admin1 health ready
System modifications
The devices offer various system level actions, e.g.:
# reset device to factory settings
nethsm --user admin1 system factory-reset
# reboot device
nethsm --user admin1 system reboot
# shut down device
nethsm --user admin1 system shutdown
# get system info about the device
nethsm --user admin1 system info
Backups
The device offers backing up of keys and user data. Backup retrieval is only available to system-wide users in the “Backup” role!
export NETHSM_BACKUP_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.backup-file.bkp')"
nethsm system backup
A backup can later on be used to restore a device, using an “R-Administrator”:
export NETHSM_BACKUP_PASSPHRASE_FILE="$nethsm_backup_passphrase_file"
nethsm --user admin1 system restore "$NETHSM_BACKUP_OUTPUT_FILE"
Backups can be validated offline:
export NETHSM_VALIDATE_BACKUP_PASSPHRASE_FILE="$nethsm_backup_passphrase_file"
nethsm system validate-backup "$NETHSM_BACKUP_OUTPUT_FILE"
Updates
Updates for the operating system/ firmware of the device are uploaded to the device and then applied or aborted.
nethsm --user admin1 system upload-update my-update-file.bin
# apply the update
nethsm --user admin1 system commit-update
# abort the update
nethsm --user admin1 system cancel-update
Features
_nethsm-integration-test: Integration tests that require a containerized NetHSM test environment. NOTE: Unless you are developing this crate, you will very likely not want to use this feature._test-helpers: Enables thenethsm::testmodule which provides utilities for test setups that may also be useful for other crates. NOTE: Unless you are developing this crate or write NetHSM related integration tests in your own crate, you will very likely not want to use this feature.backup- enables the use of thenethsm::backupmodule for reading, verifying and extracting NetHSM backup filescli: For thenethsmcommand-line interface
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.11.0] - 2026-09-28
Added
- Add
NamespacedUserIdfor describing namespacedUserId - (deps) [breaking] Update crate nethsm-sdk-rs to 4.0.0
- (deps) Update rustcrypto crates
- Implement
BackendCheckfor NetHSMConnection - [breaking] Extend
CryptographicKeyContext::OpenPgpwithnotations - Add support for ECC K-256 (Koblitz)
- Add back NIST P-224 support
- [breaking] Drop MD5 from supported algorithms and dependencies
- (cargo) Rename
test-helpersfeature to_test-helpers - Expose
key::ErrorasKeyError - Derive
OrdandPartialOrdforConnection - Derive
OrdandPartialOrdforUrl - Derive
OrdandPartialOrdforSystemWideUserId - Derive
OrdandPartialOrdforUserId - Derive
OrdandPartialOrdforNamespaceId - Derive
OrdandPartialOrdforConnectionSecurity - Derive
OrdandPartialOrdforHostCertificateFingerprints - Derive
OrdandPartialOrdforCertFingerprint - Derive
OrdandPartialOrdforKeyId - Move OpenPGP related logic out of
nethsmintosignstar-crypto - Add
SystemWideUserIdfrom signstar-config - Implement
UserWithPassphraseforFullCredentials - Add
TryFromBox<dyn UserWithPassphrase>forCredentials - [breaking] Rely on
signstar_cryptofor common cryptographic key types - Implement
From<Level>forLogLevel
Fixed
- [breaking] Diversify the logger setup for journald and terminal
- (deps) Remove unused crate
hex - [breaking] Correctly generate certificates with multiple User IDs
- Remove useless borrows in formatting
- Rewrite panicking branches to avoid clippy warnings
- (deps) Update Rust crate picky-asn1-x509 to v0.15.0
Other
- [breaking] Expose all signstar_crypto errors over top-level Error type
- (README) Refer to correct crate in
_test-helpersdescription - Remove the use of
rust_dotenvand rely on environment - Add missing documentation for
key::Errorvariants - (README) Improve information about available features
- (deps) Move
sha1crate to workspace dependencies - (deps) Move
serde_jsoncrate to workspace dependencies - (deps) Update Rust crate pgp to 0.19
- Expose
SignedSecretKeythroughnethsm - [breaking] Remove
SigningKeySetup - (deps) Update Rust crate pgp to 0.17
- [breaking] Use
nethsm_backup::validate_backup - [breaking] Use
signstar_crypto::passphrase::Passphrase - Move
base64ctto workspace dependency - Move
p256,p384andp521crates to workspace dependencies - Move
nethsm-sdk-rsto workspace dependencies - Move all top-level API documentation to README and include it
- Move
NetHsmimplementation to separate modules
[0.10.0] - 2025-08-19
Added
- Use one location for NetHSM image tag to make it consistent everywhere
- [breaking] Remove NIST P-224 as it has been removed upstream
Fixed
- Remove suppression for
missing_docsasrstestnow does not trigger this lint
[0.9.2] - 2025-07-10
Other
- update Cargo.toml dependencies
[0.9.1] - 2025-07-09
Added
- Add
Displayimpl forCryptographicKeyContext - Add
NetHsm::get_current_userto retrieve current auth user - Add
NetHsm::get_urlto retrieve the current URL - [breaking] Allow detecting when the certificate has not been uploaded
- Use new integration test feature for all crates instead of
#[ignore] - Add debug logs to all
NetHsmmethods - Expose default max idle connections and timeout as consts
- Derive strums
AsRefStrandDisplayforNamespaceSupport - Implement
AsRef<KeyFlags>forKeyUsageFlags - Implement
DisplayforCredentials - Implement
DisplayforPassphrase - Implement
DisplayforConnection - Implement
DisplayforConnectionSecurity - Implement
DisplayforHostCertificateFingerprints - Implement
DisplayforCertFingerprint - Derive
CopyforBootMode - Allow creating
CryptographicKeyContextfromSignedPublicKey - Create
KeyMechanismfromnethsm_sdk_rs::models::KeyMechanism - Automatically remove containers started by the test
- [breaking] Fold
nethsm-testsintonethsmas atestsubmodule
Fixed
- Do not remove creds before adding them in
NetHsm::add_credentials - Inline the variable to avoid the
clippy::uninlined_format_argslint warning - (deps) [breaking] upgrade rpgp to 0.16.0
Other
- Fix violations of MD022 and MD032 in changelogs
- (deps) Move
pgpto workspace dependencies - Reformat all TOML files with
taplo - Sort derives using
cargo sort-derives - Fix clippy lints regarding variables in
format! - Stop adding Certification flag to generated primary keys
[0.8.0] - 2025-04-22
Added
- Add
OpenPgpUserIdList::first - Add
From<nethsm_sdk_rs::models::UserRole>forUserRole - [breaking] Return a list of
KeyIds, notStrings fromNetHsm::get_keys - [breaking] Return
NamespaceIds notStrings fromNetHsm::get_namespaces - [breaking] Return a list of
UserIds notStrings fromNetHsm::get_users - [breaking] Rely on
NamespaceIdinstead ofStringfor robustness - Publicly re-export
chrono::DateTimeandchrono::Utc - [breaking] make
openpgp_sign_statealways return an armored OpenPGP signature - Require
Debugto be implemented in public items - Add
NetHsm::set_agent, allowing to modify the connection agent - [breaking] Initialize
NetHsmusing aConnection - Add
FullCredentialswhich requires passphrases for credentials - Add custom
serde::SerializeforPassphrase - Derive
serde::DeserializeforPassphrase - Derive
DefaultforPassphrase - Derive
CloneforCredentials - Derive
DebugforCredentials - Rely on
aws-lc-rsas default crypto provider forrustls
Fixed
- (deps) update rust crate pgp to 0.15
Other
- Move
nethsm::key::Errorto top of doc for easier discovery - (tests) Change equality asserts to use
assert_eq!for better error messages - Wrap inner errors in I/O other errors for error propagation
- (nethsm) Log inner errors in
nethsm::openpgpmodule - Add documentation for the
nethsmcrate and require it globally - Create
AgentforNetHsmin atls::create_agent - Adjust
NetHsm::newfor the use ofConnection - Move
nethsm_config::Connectiontonethsm::Connection - Move
Urlto aconnectionmodule and improve error handling - Switch to rustfmt style edition 2024
[0.7.3] - 2024-12-13
Added
- Add
NetHsm::openpgp_sign_state
[0.7.2] - 2024-12-08
Added
- Add
validate_backupto validate NetHSM backups - Add
Passphrase::expose_borrowed
Other
- (README) Add links to latest (un)released crate documentation
- (deps) Update dependencies to fix security issues
- (cargo) Consolidate dependencies with workspace dependencies
[0.7.1] - 2024-11-27
Other
- Update libc crate as the previously used version was yanked
[0.7.0] - 2024-11-26
Added
- Introduce
nethsm-testsfor easier integration testing - Derive
Copyfornethsm::UserRole - Implement
AsRef<str>forNamespaceIdto return string slice - Rely on
serde’sintoandtry_fromattributes forKeyId - Rely on
serde’sintoandtry_fromattributes forUserId - Add
SigningKeySetupstruct to track key setups for signing - Add
CryptographicKeyContextto track a key’s crypto context - [breaking] Provide version with
OpenPgpVersionwhen creating OpenPGP certificate - [breaking] Use
OpenPgpUserIdfor User ID when creating OpenPGP certificate - Add
OpenPgpVersionto track OpenPGP version - Add
OpenPgpUserIdandOpenPgpUserIdListfor OpenPGP User IDs - Add function to validate
SignatureTypeagainst other key data - Derive
DeserializeandSerializeforSignatureType - Derive
Hash,EqandPartialEqfor some types
Fixed
- (deps) update rust crate picky-asn1-x509 to 0.14.0
- Properly truncate digests for ECDSA signing schemes
- Make serde use
TryFrom<String>for deserialization - (deps) Update dependencies removing yanked crate
- (deps) Migrate to rpgp v0.14.0
- Print more details on errors
- (deps) Update
secrecyto version0.10.2 - (deps) update rust crate strum to 0.26.0
- Read real value of the RSA modulus instead of using a hardcoded one
Other
- Consolidate contributing and licensing information
- (deps) Update dependencies and fix license ID
- (cargo) Move same-crate, feature-incompatible crates to workspace
- (cargo) Move common dependencies to workspace dependencies
- (cargo) Move shared dependencies to workspace dependencies
- (cargo) Move package metadata to workspace
- Use correct link to upstream Error type in
nethsm_sdk::Message - Add docs for and spacing between
nethsm::key::Errorvariants - (deps) update rust crate rstest to 0.23.0
- Refactor error cases to use
Error::UnsupportedKeyFormat - (deps) update rust crate rustainers to 0.13.0
[0.6.0] - 2024-09-11
Added
- Ensure valid bit length when generating RSA TLS keys
- Ensure valid bit length for block cipher and RSA keys
Fixed
- Adjust broken links in
KeyIddocumentation
Other
- [breaking] Introduce
nethsm::KeyIdtype - Improve documentation for Error variant
Error::Key - [breaking] Remove unused Error variant
Error::KeyData - Provide function to check KeyType - KeyMechanism compatibility
- [breaking] Use
u32instead ofi32for ports and lengths
[0.5.0] - 2024-09-06
Fixed
- (deps) [breaking] update rust crate rustls-native-certs to 0.8.0
- Pad secret keys with zeros before sending them to NetHSM
- Use correct function for constructing MPIs
Other
- Make
SignatureTypea copy type - Import keys of all supported types
- (Cargo.toml) Remove duplicate rand development dependency
- Replace ed25519-compact with ed25519-dalek in all tests
[0.4.0] - 2024-08-30
Added
- Validate namespace access using
UserIdmethod - Add
UserIdandNamespaceIdtypes for handling User IDs - Add facilities for namespace administration
- Add facilities for OpenPGP certificate creation and signing
- Add support for PEM-encoded private keys in
key import
Fixed
- (Cargo.toml) Have cargo-machete ignore the md-5 dependency
- Do not require
Administratorrole when executingunlock - Adjust the test to remove credentials as they are not needed in
unlock - Adjust functions as
update_filedoes not need to be async - Reduce the number of direct dependencies
- (README.md) Remove license attribution as it is in reuse config
Other
- Remove warning from
NetHsm::restoremethod - Adapt existing documentation for the use of namespaces
- Adapt and extend tests for use of namespaces
- [breaking] Move
CredentialsandPassphraseto user module - Adjust information on output format on public key retrieval
- Adjust documentation on authentication for unlock call
- Split tests for retrieval of TLS public key
- Fix user role requirements for backup retrieval
- (deps) update rust crate rstest to 0.22.0
- Pin nethsm container image version to c16fe4ed
- (nethsm/tests/config.rs) Fix create_backup test
- Simplify license attribution setup for entire project
[0.3.0] - 2024-07-12
Added
- Add functions to return key type specific lists of key mechanisms
- Extend FromStr for ConnectionSecurity to cover case sensitivity
- Derive strum::IntoStaticStr for various types
- Derive strum::EnumIter for various types
- [breaking] Assemble connection configuration only when it is needed
- [breaking] Use secrecy for passphrase zeroing
- Publicly re-export all required nethsm-sdk-rs models
Fixed
- (nethsm/src/nethsm_sdk.rs) Fix spelling mistake in error message
Other
- Switch rustls’s crypto provider to ring
- Describe output data types for signing functionality
- (Cargo.toml) Remove strum_macros as it is unused
- Assemble user agent string from crate data
- Make container setup with fixtures more robust
- Use TestResult for all doc tests
[0.2.0] - 2024-05-10
Added
- [breaking] Use PrivateKeyImport for import of private key material
- Provide own LogLevel
- Provide own EncryptMode
- Provide own DecryptMode
- Provide own KeyMechanism
- Provide own TlsKeyType
- Provide own KeyType
- Provide own UserRole
- Derive Clone for SignatureType
- Derive strum::{Display,EnumString} for BootMode
- Add handling of /config/tls/public.pem endpoint
- Add handling of /system/info endpoint
- Add handling of /health/ready endpoint
- Add handling of /health/alive endpoint
Fixed
- Simplify use of strum macros for SignatureType
Other
- Rely on global use of serde for Message
- Extend KeyType to validate a list of KeyMechanisms
- Add dedicated tests for /health/state endpoint handling
[0.1.1] - 2024-05-04
Added
- Use custom url type to validate the connection to a NetHSM
- Implement Serialize/Deserialize for ConnectionSecurity
Other
- Use re-exported facilities instead of nethsm_sdk_rs directly
- (README.md) Adjust test setup for new podman requirements
- (container) Use rustainers instead of podman-api
[0.1.0] - 2024-03-22
Added
- Add library for controlling a NetHSM
Signstar common
Shared components and data types for Signstar tools and libraries
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_common/ for development version of the crate
- https://docs.rs/signstar_common/latest/signstar_common/ for released versions of the crate
Features
logging: Enables thesignstar_config::loggingmodule which provides utilities for logging.serde: Enablesserdeintegration for types which support it
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.2.0] - 2026-09-28
Added
- Implement
signstar-download-key-certificates - Add types for certificate request and response
- [breaking] Introduce a crate-level error type for signstar-common
- Add
BackendTypeto track information about supported HSM types - Add trait
BackendCheckto check backend functionality - (cargo) Use workspace lints
- Rely on
log::Levelinstead ofnethsm::LogLevelfor defaults
Fixed
- [breaking] Diversify the logger setup for journald and terminal
- Try to connect to journald socket instead of relying on
connected_to_journal
Other
- (deps) Move
simplelogcrate to workspace dependencies - (README) Improve information about available features
- Document all publicly visible items
[0.1.2] - 2025-08-19
Added
- Add systemd journal logging module to
signstar-common
[0.1.1] - 2025-07-10
Other
- Fix violations of MD022 and MD032 in changelogs
- Reformat all TOML files with
taplo
[0.1.0] - 2025-04-22
Added
- Add
signstar-commoncrate to provide common facilities
Signstar config
Configuration file handling for Signstar hosts.
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_config/ for development version of the crate
- https://docs.rs/signstar_config/latest/signstar_config/ for released versions of the crate
Examples
Administrative credentials
Administrative credentials on a Signstar host describe all required secrets to unlock, backup, restore and fully provision a NetHSM backend.
They can be used from plaintext and systemd-creds encrypted files.
Functions for interacting with configurations in default locations must be called by root.
NetHSM
#[cfg(feature = "nethsm")]
mod impl_nethsm {
use signstar_config::{admin_credentials::AdminCredentials, nethsm::NetHsmAdminCredentials};
use signstar_crypto::AdministrativeSecretHandling;
pub fn main() -> testresult::TestResult {
// Load from plaintext file in default location
let creds = NetHsmAdminCredentials::load(AdministrativeSecretHandling::Plaintext)?;
// Load from systemd-creds encrypted file in default location
let creds = NetHsmAdminCredentials::load(AdministrativeSecretHandling::SystemdCreds)?;
// Store in plaintext file in default location
creds.store(AdministrativeSecretHandling::Plaintext)?;
// Store in systemd-creds encrypted file in default location
creds.store(AdministrativeSecretHandling::SystemdCreds)?;
Ok(())
}
}
#[cfg(not(feature = "nethsm"))]
mod impl_none {
pub fn main() -> testresult::TestResult {
Ok(())
}
}
#[cfg(feature = "nethsm")]
use impl_nethsm::main;
#[cfg(not(feature = "nethsm"))]
use impl_none::main;
YubiHSM2
#[cfg(feature = "yubihsm2")]
mod impl_yubihsm2 {
use signstar_config::{
admin_credentials::AdminCredentials,
yubihsm2::admin_credentials::YubiHsm2AdminCredentials,
};
use signstar_crypto::AdministrativeSecretHandling;
pub fn main() -> testresult::TestResult {
// Load from plaintext file in default location
let creds = YubiHsm2AdminCredentials::load(AdministrativeSecretHandling::Plaintext)?;
// Load from systemd-creds encrypted file in default location
let creds = YubiHsm2AdminCredentials::load(AdministrativeSecretHandling::SystemdCreds)?;
// Store in plaintext file in default location
creds.store(AdministrativeSecretHandling::Plaintext)?;
// Store in systemd-creds encrypted file in default location
creds.store(AdministrativeSecretHandling::SystemdCreds)?;
Ok(())
}
}
#[cfg(not(feature = "yubihsm2"))]
mod impl_none {
pub fn main() -> testresult::TestResult {
Ok(())
}
}
#[cfg(feature = "yubihsm2")]
use impl_yubihsm2::main;
#[cfg(not(feature = "yubihsm2"))]
use impl_none::main;
Creating secrets for non-administrative credentials
Non-administrative credentials on a Signstar host provide access to non-administrative users on a backend.
They can be used in plaintext and systemd-creds encrypted files.
Assuming, that a Signstar configuration is present on the host, it is possible to create secrets for each backend user assigned to any of the configured system users. Functions for the creation of secrets must be called by root.
NOTE: For the creation of system users based on a Signstar config refer to signstar-configure-build.
#[cfg(any(feature = "nethsm", feature = "yubihsm2"))]
mod impl_any {
use signstar_config::{
config::{Config, UserBackendConnection, UserBackendConnectionFilter},
};
use signstar_crypto::AdministrativeSecretHandling;
pub fn main() -> testresult::TestResult {
// Load Signstar config from one of the default system locations.
let config = Config::from_system_path()?;
// Get the user backend connections for all non-administrative users.
let user_backend_connections = config.user_backend_connections(&[UserBackendConnectionFilter::NonAdmin]);
// Create secrets for each system user and their backend users.
for user_backend_connection in user_backend_connections.iter() {
user_backend_connection.create_non_admin_backend_user_secrets()?;
}
Ok(())
}
}
#[cfg(not(any(feature = "nethsm", feature = "yubihsm2")))]
mod impl_none {
pub fn main() -> testresult::TestResult {
Ok(())
}
}
#[cfg(any(feature = "nethsm", feature = "yubihsm2"))]
use impl_any::main;
#[cfg(not(any(feature = "nethsm", feature = "yubihsm2")))]
use impl_none::main;
Loading secrets for non-administrative users
Depending on user mapping in the Signstar config, a system user may have one or more NetHSM backend users assigned to it. The credentials for each NetHSM backend user can be loaded by each configured system user. Functions for the loading of secrets must be called by the system user that is assigned that particular secret.
#[cfg(any(feature = "nethsm", feature = "yubihsm2"))]
mod impl_any {
use signstar_config::config::{Config, NonAdminBackendUserIdFilter, NonAdminBackendUserIdKind};
pub fn main() -> testresult::TestResult {
// Load Signstar config from one of the default system locations.
let config = Config::from_system_path()?;
// Get the user backend connection for the Unix user named "test".
let Some(user_backend_connection) = config.user_backend_connection(&"test".parse()?) else {
panic!("No Unix user of that name is configured for any of the available backends.")
};
// Assuming the selected Unix user is supposed to be used for signing, get the credentials for its assigned user in the backend.
let credentials = user_backend_connection.load_non_admin_backend_user_secrets(NonAdminBackendUserIdFilter{ backend_user_id_kind: NonAdminBackendUserIdKind::Signing })?;
Ok(())
}
}
#[cfg(not(any(feature = "nethsm", feature = "yubihsm2")))]
mod impl_none {
pub fn main() -> testresult::TestResult {
Ok(())
}
}
#[cfg(any(feature = "nethsm", feature = "yubihsm2"))]
use impl_any::main;
#[cfg(not(any(feature = "nethsm", feature = "yubihsm2")))]
use impl_none::main;
Features
_containerized-integration-test: Integration tests that require a containerized test environment. NOTE: Unless you are developing this crate, you will very likely not want to use this feature._nethsm-integration-test: Integration tests that require a containerized NetHSM environment. NOTE: Unless you are developing this crate, you will very likely not want to use this feature._test-helpers: Enables thesignstar_config::testmodule which provides utilities for test setups that may also be useful for other crates. NOTE: Unless you are developing this crate, you will very likely not want to use this feature._yubihsm2-mockhsm: Test environment and integration using a virtual YubiHSM2. NOTE: Unless you are developing this crate, you will very likely not want to use this feature.nethsm: Enables support for the NetHSM backend.yubihsm2: Enables support for the YubiHSM2 backend.
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.3.0] - 2026-09-29
Added
- Implement
signstar-download-key-certificates - [breaking] Rename
SystemUserMappingvariant for WireGuard config downloads - [breaking] Remove
wrapping_key_idfromYubiHsm2UserMapping::Backup - Sort
SystemUserMappingand implementOrdandPartialOrd - Add
NetHsmUserMapping::CertificateRetrieval - Add
YubiHsm2UserMapping::CertificateRetrieval - [breaking] Introduce a crate-level error type for signstar-common
- (deps) [breaking] Update crate nethsm-sdk-rs to 4.0.0
- (deps) Update rustcrypto crates
- Implement creating
NetHsmAdminCredentialsfromConfig - Implement creating
YubiHsm2AdminCredentialsfromConfig - Emit a warning on missing (but configured) system users
- Derive,
EqandPartialEqforConfigandSystemConfig - Re-export
signstar_yubihsm2::Connectioninyubihsm2module - Add labels to generated YubiHSM objects
- Add permissions missing in the administrative key
- [breaking] Export types from
signstar-yubihsm2via theyubihsm2module - Export types from the
yubihsm2crate via theyubihsm2module - Export types from the
nethsmcrate via thenethsmmodule - [breaking] Support sets of filters with
Config::user_backend_connections - [breaking] Extend
CryptographicKeyContext::OpenPgpwithnotations - Add module implementing
StateDifffor YubiHSM2 - Add
YubiHsm2Backendto configure YubiHSM2s based on configs - Implement
DisplayforStateDiffReport - [breaking] Add
YubiHsm2AdminCredentials::default_administrator - Add
YubiHsm2AdminCredentials::administrators - Add
AuthTypetoYubiHsm2ConfigUserData - Add
YubiHsm2UserMapping::authentication_key_info - Add
YubiHsm2Config::WRAP_KEY_ID - [breaking] Change
YubiHsm2UserMapping::domainsto always returnDomains - Return
Result<Option<usize>, Error>for certificate size estimation - Add size validation of
YubiHsm2UserMappinginstances in aYubiHsm2Config - Always require
expectattributes instead ofallows - Add
YubiHsm2ConfigStateasYubiHsm2Configstate representation - Derive
OrdandPartialOrdforStateOrigin - Derive
EqandPartialEqforStateOrigin - [breaking] Move
backup_passphrasemethod toAdminCredentialstrait - [breaking] Move
iterationmethod toAdminCredentialstrait - Rely on stricter
PassphrasePolicyforYubiHsm2AdminCredentials - Rely on stricter
PassphrasePolicyinNetHsmAdminCredentials - Add
NetHsmDiffforNetHsmConfigStateandNetHsmBackendState - Implement
StateOriginInfoforNetHsmConfigState - Allow returning validated admin users from
NetHsmAdminCredentials - Implement
StateOriginInfoforNetHsmBackendState - Implement
DisplayforNetHsmConfigUserKeyData - Implement
DisplayforNetHsmConfigUserData - Derive
EqandPartialEqforNetHsmBackendState - Implement state comparison for system user data
- Add new state comparison mechanism based on common traits
- Add
NetHsmConfigStateasNetHsmConfigrepresentation - Add
SystemUserConfigStatetracking system user data inConfig - Add
SystemUserHostStatetracking system users on the host - Implement
MappingAuthorizedKeyEntryforUserBackendConnection - Allow writing authorized key file with
MappingAuthorizedKeyEntry - Implement
ConfigSystemUserDataforConfig - Implement creating
SystemUserDatafromYubiHsm2UserMapping - Implement creating
SystemUserDatafromNetHsmUserMapping - Implement creating
SystemUserDatafromSystemUserMapping - Add
SystemUserId::rootto return the root user - Add
ConfigSystemUserDatatrait returningSystemUserData - Add
SystemUserDatato track the different kinds of system users - Add
PartialEqbetweenKeyStateandNetHsmConfigUserKeyData - Add
PartialEqbetweenUserStateandNetHsmConfigUserData - Derive
Hash,Ord,PartialOrdforKeyState - Derive
Hash,Ord,PartialOrdforUserState - Derive
Hash,Ord,PartialOrdforKeyCertificateState - Add
YubiHsm2UserMapping::capability - Add
YubiHsm2Config::CAP_SIGNINGtracking signing capabilities - Add
YubiHsm2Config::CAP_HERMETIC_AUDIT_LOG - Add
YubiHsm2Config::CAP_BACKUPtracking backup capabilities - Add
YubiHsm2Config::CAP_AUDIT_LOGtracking audit log capabilities - Add
YubiHsm2UserMapping::CAP_ADMINto track admin capabilities - Add
YubiHsm2Config::AUDIT_COMMANDSto track commands to audit - Move Signstar config fixtures to central workspace location
- Remove
SignstarConfigin favor ofConfig - Port from
SignstarConfgtoConfigin NetHSM backend handling - Support creating
SignstarConfigNetHsmStatefromNetHsmConfig - Derive
PartialEqandEqforSignstarConfigNetHsmState - Port to new
Configformat - (cargo) Add
_yubihsm2-mockhsmfeature - Add
SystemUserId::from_current_unix_user - Switch from
YubiHsmConnectiontosignstar_yubihsm2::Connection - (cargo) Remove
serdefrom default features - [breaking] Drop MD5 from supported algorithms and dependencies
- (cargo) Rename
test-helpersfeature to_test-helpers - Add new YAML based config file format
- Derive
EqandPartialEqforNetHsmConfig - Derive
EqandPartialEqforYubiHsm2Config - Add
SystemUserMappingandSystemConfig - Add
YubiHsm2UserMappingandYubiHsm2Config - Add
NetHsmUserMappingandNetHsmConfig - Add traits used for retrieving data from generic user mappings
- Use
Idinsignstar-yubihsm2::Credentials - Add
TryFrom<nix::unistd::User>forSystemUserId - Implement
AsRef<Entry>forAuthorizedKeyEntry - Implement
OrdandPartialOrdforAuthorizedKeyEntry - Use
yubihsm::device::SerialNumberinYubiHsmConnection - Derive
OrdandPartialOrdforyubihsmconnection - Derive
OrdandPartialOrdforNetHsmMetricsUsers - Derive
OrdandPartialOrdforSystemUserId - Add metrics related variants for
UserMapping - Add
UserMapping::SystemYubiHsm2Backup - Add administrative credentials handling for YubiHSM2 backends
- Add
UserMapping::YubiHsmOnlyAdmin - Generically load creds with
ExtendedUserMapping::load_credentials - Add
UserMapping::backend_users - [breaking] Generically return non-administrative creds when writing them
- Add
UserMapping::backend_users_with_new_passphrase - Add
AsRef<UserId>forSystemWideUserId - Add
UserMappingFilterandBackendUserKind - [breaking] Use
UserWithPassphraseinstead ofFullCredentials - Add
UserMapping::SystemYubiHsmOperatorSigningfor YubiHSM2 - Add
BackendConnection::YubiHsm2for YubiHSM2 backend connections - Add the
yubihsm2module to handle YubiHSM2 backends - [breaking] Use
signstar_crypto::key::SigningKeySetupinUserMapping - [breaking] Rely on
signstar_cryptofor common cryptographic key types - Add
AdminCredentialstrait for generic admin creds handling - [breaking] Rename
AdminCredentialstoNetHsmAdminCredentials - [breaking] Support using different HSM backends in
SignstarConfig
Fixed
- Filter more robustly in
Config::user_backend_connections - Reset the backend, if default credentials are still usable
- [breaking] Diversify the logger setup for journald and terminal
- Fix capabilities required for signing data
- Remove filtering of internal YubiHSM2 objects
- Add missing
ExportWrappedpermission to the administrative set - (deps) Update Rust crate serde-saphyr to v1
- [breaking] Correctly generate certificates with multiple User IDs
- Remove useless borrows in formatting
- Allow only max one tag for user and key data in the NetHSM backend
- (deps) Update Rust crate garde to 0.23.0
- Only add NetHSM admins when both setup in config and admin creds
- (cargo) Move
num-enumto workspace dependencies - (deps) Update Rust crate toml to v1
- (deps) Update Rust crate nix to 0.31.0
- [breaking] Track the wrapping key id in
UserMapping::SystemYubiHsm2Backup - Rewrite panicking branches to avoid clippy warnings
- Replace returning errors to direct
panics to avoid clippy lints
Other
- Rely on coverage collection functionality in change-user-run
- Add
ConfigFileVariant::contains_backend, checking for backend - Remove warning for
_yubihsm2-mockhsmfeature debug requirement - Add man page for the Signstar configuration file
- (fixtures) Consolidate the naming of system users in configs
- Use
SystemPrepareConfigto prepare config/usermapping tests - Use
SystemPrepareConfigto prepare YubiHSM2 admin creds tests - Ignore (potentially variable) OpenPGPv4 certificate sizes
- Enforce binary ID for
_containerized-integration-testtests - [breaking] Move state handling for
YubiHsm2Configtostatemodule - Remove unnecessary feature gate from
yubihsm2::configmodule - [breaking] Replace signstar-yubihsm2’s
Idwithyubihsm::object::Id - [breaking] Use
signstar-yubihsm2types for theYubiHsm2UserMapping - [breaking] Expose unlock
Passphraseinstead of raw string reference - [breaking] Expose backup
Passphraseinstead of raw string reference - [breaking] Rename
NetHsmAdminCredentials::get_namespace_administrators - [breaking] Rename
NetHsmAdminCredentials::get_default_administrator - [breaking] Rename
NetHsmAdminCredentials::get_administrators - [breaking] Rename
NetHsmAdminCredentials::get_unlock_passphrase - [breaking] Rename
NetHsmAdminCredentials::get_backup_passphrase - [breaking] Rename
NetHsmAdminCredentials::get_iteration - [breaking] Move
IterationMismatchfromnethsmto crateError - (cargo) Move
instato workspace dependencies - (cargo) Move
gardeto workspace dependencies - (cargo) Move
tomlto workspace dependencies - (cargo) Move
pretty_assertionsto workspace dependencies - [breaking] Expose all signstar_crypto errors over top-level Error type
- [breaking] Return optional
DomainsfromYubiHsm2UserMapping::domains - [breaking] Remove
StateHandlingbased state comparison - Streamline the use of “NetHSM” in the
configmodule - [breaking] Track
NetHsmConfig, notConfiginNetHsmBackend - Move
NetHsmBackendstate representation tobackendmodule - Consolidate
config::filetest modules targeting no backend - [breaking] Rename
NetHsmStatetoNetHsmBackendState - Rename
NetHsmConfigStatetoNetHsmConfigStateLegacy - Add writing of authorized keys files to
SystemUserConfig::apply - [breaking] Remove unused
StateComparisonFailure - [breaking] Require
Anyfor theStateHandlingtrait - [breaking] Rename state structs for
NetHsmConfigdata - [breaking] Rely on
change-user-runto detect command availability - [breaking] Remove
ErrorExitCode - [breaking] Render the
errormodule private - [breaking] Use
AdminCredentialsviaadmin_credentialsmodule - [breaking] Only expose
KeyCertificateStatethroughconfigmodule - [breaking] Use
config::Erroronly viaconfigmodule - [breaking] Use
AuthorizedKeyEntry/SystemUserIdviaconfigmodule - [breaking] Only re-export used members of the
nethsm::statemodule - [breaking] Don’t re-export
nethsm::Erroron the crate-level - [breaking] Use
NetHsmBackendthrough thenethsmmodule - [breaking] Remove re-export of
nethsm::NetHsmMetricsUsers - [breaking] Remove re-export of
nethsm::FilterUserKeys - [breaking] Use
NetHsmAdminCredentialsfrom thenethsmmodule - [breaking] Rename
SignstarConfigNetHsmStatetoNetHsmConfigState - [breaking] Render
UserStatesandKeyStatesprivate structs - [breaking] Merge
config::state::nethsmwithnethsm::state - [breaking] Add all available config fixtures to
ConfigFileVariant - Use
pretty_assertionsfor improved output usingassert_eq - Add helpers to
testmodule for preparing systems with aConfig - Update fixtures to use localhost and cover all mapping variants
- Add fixtures for YubiHSM2 mockhsm
- Test roundtripping Signstar configs using YubiHSM2 mockhsm
- Ignore mockhsm fixtures when only testing with
yubihsm2feature - (README) Describe all current features in feature section
- (cargo) Remove
_integration-testfeature - Don’t require the
nethsmfeature for integration tests - Add integration tests for non-admin secrets creation/loading
- Use
Passphrasefromsignstar-cryptonotnethsm - Check creation and loading of backend user secrets
- Check use of Unix users in usermapping implementations
- Expose common functionality for containerized integration tests
- [breaking] Only expose selected items of the
nethsm::configmodule - (deps) Update Rust crate pgp to 0.19
- Make
AuthorizedKeyEntrya newtype forEntrynotString - (cargo) Move crate
tempfileto workspace dependencies - (cargo) Move
nixcrate to workspace dependencies - Lock the backend when comparing config and NetHSM state
- [breaking] More generically represent config and backend state
- [breaking] Change name to
UserMapping::YubiHsm2OnlyAdmin - [breaking] Change name to
UserMapping::SystemYubiHsm2OperatorSigning - Simplify
UserMappingmatches using.. - Split tests for
UserMappingmethods into modules per backend - Separate
nethsm::admin_credentialsintegration tests - Explain capabilities on
UserMapping::SystemYubiHsmOperatorSigning - [breaking] Remove
SystemWideUserIdand use thenethsmcrate instead - Consolidate the documentation on HSM backends
- Add info logging to
ExtendedUserMappingcredentials methods - [breaking] Use
String, notUserIdinCredentialsLoadingError - [breaking] Return
KeyIdfromUserMapping::get_nethsm_user_key_and_tag - [breaking] Use
signstar_crypto::passphrase::Passphrase - Rename
UserMapping::get_key_ids - Rename
UserMapping::get_tags - Rename
UserMapping::get_namespaces - Rename
UserMapping::has_system_and_nethsm_user - Move
NetHsmMetricsUsers,FilterUserKeystocrate::nethsm - Move
NetHsmAdminCredentialstosignstar_config::nethsm
[0.2.0] - 2025-08-19
Added
- Use
change-user-runcrate instead ofsignstar_config::test - Add
SignstarConfigfor the configuration on Signstar hosts
Fixed
- (deps) Update Rust crate toml to 0.9.0
Other
- Collect coverage produced by tests run (partially) as other user
- Remove the use of
confy - Initialize logger using
signstar_common::logging::setup_logging
[0.1.0] - 2025-07-10
Added
- Add
nethsmmodule to interact with NetHSM backends. - (test-helpers) Add helpers for NetHSM backend testing
- [breaking] Fold
signstar-testintosignstar-configas a separatetestmodule - Introduce
signstar-testfor common test utilities - Expose all binaries, not only examples in integration tests
- Add
AdminCredentials::get_default_administrator - Ensure data for
AdminCredentialsis valid - Replace the use of
CredentialswithFullCredentials - Replace
Userwithnethsm::FullCredentials - Use
nethsm_config::Passphrasefor all administrative passphrases - Add
signstar-configcrate to handle Signstar host configs
Fixed
- (deps) Update Rust crate which to v8
- (deps) update rust crate nix to 0.30.0
- Box the
confy::ConfyErrorso that the error size does not explode - Only fail to load as non-root in
AdminCredentials::load
Other
- (fixtures) Add more administrator users and diversify key contexts
- Move constants for configuration file contents to test modules
- Reformat all TOML files with
taplo - Sort derives using
cargo sort-derives - Fix clippy lints regarding variables in
format!
NAME
config.yaml - Configuration file format for Signstar hosts.
DESCRIPTION
A YAML configuration file that encodes the relevant system and HSM backend settings for a Signstar[1] host.
Each configuration file must be a regular file to be considered.
Directories and precedence
The handling of configuration files is based on the Configuration Files Specification[2].
Both system and real users read config.yaml from the system directories “/usr/share/signstar/” and “/usr/local/share/signstar/”, the volatile runtime directory “/run/signstar/” and the local administration directory “/etc/signstar/”. For real users, no other configuration file locations (e.g. those based on the XDG Base Directory Specification[3]) exist.
The order of the aforementioned directories describes the order of precedence in increasing priority. A file from a directory with higher priority fully replaces one from a directory with lower priority (e.g. “/etc/signstar/config.yaml” replaces “/usr/share/signstar/config.yaml”).
The config.yaml configuration file format neither supports drop-in configuration files, nor the masking of configuration files.
FILE FORMAT
The file must contain valid YAML data and all of the following described required fields and values must be present.
system
This top-level object describes settings relevant to the entire Signstar system.
iteration
This non-negative number describes the version of the data in the configuration file.
The number should be incremented, if data changes in a breaking manner:
- a new administrative user is added
- a new user or key is added
- a user or key is removed
- the means of encryption for administrative or non-administrative credentials are changed
admin_secret_handling
This describes the way administrative credentials are handled on the Signstar host and whether they are permanently persisted.
This must be one of the following two strings or the shamirs-secret-sharing object:
-
plaintext: The administrative credentials are stored in plaintext files in Signstar’s state directory.
NOTE: The Signstar host does not provide integration to retrieve plaintext administrative credentials!
-
systemd-creds: The administrative credentials are stored in files encrypted with systemd-creds(1) in Signstar’s state directory.
NOTE: The Signstar host does not provide integration to retrieve systemd-creds administrative credentials!
-
shamirs-secret-sharing: This object describes the encryption and decryption of administrative credentials using Shamir’s Secret Sharing[3]. With this setup, several individuals are provided with cryptographic shares of the (encrypted) administrative credentials, which are not stored permanently on the Signstar host. A subset of these individual’s cryptographic shares are required to decrypt the administrative credentials (see also the share_holder object).
- number_of_shares: The total number of shares used for Shamir’s Secret Sharing[3].
- threshold: The number of shares required for decrypting administrative credentials encrypted with Shamir’s Secret Sharing[3].
NOTE: It is strongly recommended to expose administrative credentials only on an encrypted storage device (see cryptsetup(8)) on the Signstar host and to rely on encrypted files (therefore shamirs-secret-sharing is recommended over systemd-creds and systemd-creds is recommended over plaintext). Currently, only plaintext and systemd-creds support is available.
non_admin_secret_handling
The way non-administrative credentials are handled on the system.
This must be one of the following two strings:
- plaintext: The credentials are stored in plaintext files in Signstar’s state directory.
- systemd-creds: The credentials are stored in files encrypted with systemd-creds(1) in Signstar’s state directory.
NOTE: It is strongly recommended to expose non-administrative credentials only on an encrypted storage device (see cryptsetup(8)) and to rely on encrypted files (therefore systemd-creds is recommended over plaintext).
mappings
A set of user mapping objects, that describe specific integration with the Signstar host, based on its system users. The following entries are understood.
wireguard_download
This object describes an optional system user that is used to download WireGuard configuration files used by the Signstar host.
- system_user: The unique Unix username of a user on the Signstar host. This user has access to the relevant WireGuard configuration.
- ssh_authorized_key: The unique SSH public key of the system_user.
share_holder
This object describes an optional system user that is used to upload and download shares of the administrative credentials, encrypted using Shamir’s Secret Sharing[3].
NOTE: The number of objects of this type must be the same as number_of_shares, if shamirs-secret-sharing is used for admin_secret_handling and otherwise always zero.
- system_user: The unique Unix username of a user on the Signstar host. This user has access to a dedicated, user-specific location for the upload and download of shares of the administrative credentials, encrypted using Shamirs Secret Sharing[3].
- ssh_authorized_key: The unique SSH public key of the system_user.
nethsm
This top-level object describes settings relevant to NetHSM[4] backends.
NOTE: This object can only be used, if NetHSM support is compiled in!
connections
A set of connections to a NetHSM[4] device. Each connection is described by an object with URL and the TLS security chosen for it.
-
url: The unique URL under which a NetHSM is available.
-
tls_security: The TLS security used for the url.
This must be one of the following two strings, or the root_certificates object:
-
unsafe: Always trust the TLS certificate associated with a host.
WARNING: This does not check the host key, which allows for trivial man-in-the-middle attacks!
-
native: Use the native CA trust store for TLS certificate verification.
-
root_certificates: A list of unique CA certificates in PEM format. These certificates are used to validate the host certificates of the connection.
-
mappings
A set of user mapping objects, that each describe the integration of specific NetHSM[4] users with the Signstar host. The following entries are understood.
admin
The unique username of an administrative user of the NetHSM[4]. Administrative users are used to
- create users
- namespaces
- create keys
- restore from backup
NOTE: The default administrative user is named “admin” and can be reused (a random passphrase is chosen for it during provisioning of the device).
backup
This object describes a system user and its SSH authorized key, as well as a backend user for retrieving the backup of the NetHSM[4].
- backend_user: The unique username of a backup user in the NetHSM[4].
- ssh_authorized_key: The unique SSH public key of the system_user.
- system_user: The unique Unix username of a user on the Signstar host.
certificate_retrieval
This object describes a system user and its SSH authorized key, as well as a set of backend users for retrieving the certificates associated with each signing key of the NetHSM[4].
- system_wide: The unique username of a system-wide operator user in the NetHSM[4]. This operator user provides access to the certificates of system-wide keys.
- namespaced: A set of unique, namespaced usernames of operator users in the NetHSM[4]. Each of these operator users provide access to the certificates in their respective namespace.
- ssh_authorized_key: The unique SSH public key of the system_user.
- system_user: The unique Unix username of a user on the Signstar host.
hermetic_metrics
This object describes a system user, as well as backend users for retrieving metrics from the NetHSM[4] on the Signstar host. This integration is useful for integration with other services, that are running on the Signstar host.
- backend_users: This object contains information about a metrics user and one or more operator users of the NetHSM[4].
- metrics_user: The unique username of a system-wide metrics user in the NetHSM[4].
- operator_users: A set of unique operator usernames in the NetHSM[4].
- system_user: The unique Unix username of a user on the Signstar host.
metrics
This object describes a system user and its SSH authorized key, as well as backend users for retrieving metrics of the NetHSM[4] from the Signstar host.
- backend_users: This object contains information about a metrics user and one or more operator users of the NetHSM[4].
- metrics_user: The unique username of a metrics user in the NetHSM[4].
- operator_users: A set of unique operator usernames in the NetHSM[4].
- ssh_authorized_key: The unique SSH public key of the system_user.
- system_user: The unique Unix username of a user on the Signstar host.
signing
This object describes a system user and its SSH authorized key, as well as a backend user and a specific key for retrieving digital signatures from the NetHSM[4] via the Signstar host.
-
backend_user: The unique username of an operator user in the NetHSM[4]. This operator user is assigned a tag, which is also assigned to the cryptographic key with the ID signing_key_id and thus is granted usage of this key.
-
signing_key_id: A unique key ID in the NetHSM[4].
NOTE: If the backend_user is associated with a namespace, this key ID is only available in that particular namespace.
-
key_setup: This object describes the cryptographic key and what type of digital signatures can be created with it.
NOTE: The combination of key_type, key_mechanism and signature_type must be valid.
-
key_type: The type of the cryptographic key.
This must be one of the following strings:
- Curve25519: for a Montgomery curve key over a prime field for the prime number 2^255-19
- EcBp256: for an elliptic (Brainpool) curve key over a prime field for a prime of size 256 bit
- EcBp384: for an elliptic (Brainpool) curve key over a prime field for a prime of size 384 bit
- EcBp512: for an elliptic (Brainpool) curve key over a prime field for a prime of size 512 bit
- EcK256: for an elliptic (Koblitz) curve key over a prime field for a prime of size 256 bit
- EcP224: for an elliptic-curve key over a prime field for a prime of size 224 bit
- EcP256: for an elliptic-curve key over a prime field for a prime of size 256 bit
- EcP384: for an elliptic-curve key over a prime field for a prime of size 384 bit
- EcP521: for an elliptic-curve key over a prime field for a prime of size 521 bit
- Generic: for a generic key used for block ciphers
- Rsa: for an RSA key
-
key_mechanisms: The list of mechanisms supported by the cryptographic key.
This must be one or more of the following strings:
- AesDecryptionCbc: Decryption using the Advanced Encryption Standard (AES) with Cipher Block Chaining (CBC)
- AesEncryptionCbc: Encryption using the Advanced Encryption Standard (AES) with Cipher Block Chaining (CBC)
- EcdsaSignature: Signing following the Elliptic Curve Digital Signature Algorithm (ECDSA)
- EdDsaSignature: Signing following the Edwards-curve Digital Signature Algorithm (EdDSA)
- RsaDecryptionOaepMd5: RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using an MD-5 hash
- RsaDecryptionOaepSha1: RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-1 hash
- RsaDecryptionOaepSha224: RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-224 hash
- RsaDecryptionOaepSha256: RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-256 hash
- RsaDecryptionOaepSha384: RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-384 hash
- RsaDecryptionOaepSha512: RSA decryption with Optimal Asymmetric Encryption Padding (OAEP) using a SHA-512 hash
- RsaDecryptionPkcs1: RSA decryption following the PKCS#1 standard
- RsaDecryptionRaw: Raw RSA decryption
- RsaSignaturePkcs1: RSA signing following the PKCS#1 standard
- RsaSignaturePssSha1: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-1 hash
- RsaSignaturePssSha224: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-224 hash
- RsaSignaturePssSha256: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-256 hash
- RsaSignaturePssSha384: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-384 hash
- RsaSignaturePssSha512: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-512 hash
-
signature_type: The signature type supported by the cryptographic key.
This must be one of the following strings:
- EcdsaK256: Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a (Koblitz) key over a prime field for a prime of size 256 bit
- EcdsaP224: Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a key over a prime field for a prime of size 224 bit
- EcdsaP256: Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a key over a prime field for a prime of size 256 bit,
- EcdsaP384: Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a key over a prime field for a prime of size 384 bit
- EcdsaP521: Elliptic Curve Digital Signature Algorithm (ECDSA) signing using a key over a prime field for a prime of size 521 bit
- EdDsa: Signing following the Edwards-curve Digital Signature Algorithm (EdDSA)
- Pkcs1: RSA signing following the PKCS#1 standard
- PssSha1: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-1 hash
- PssSha224: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-224 hash
- PssSha256: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-256 hash
- PssSha384: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-384 hash
- PssSha512: RSA signing following a “probabilistic signature scheme” (PSS) using a SHA-512 hash
-
key_context: This object or string describes the context in which the cryptographic key is used.
This must be one of the following two options:
- raw: If this string is used, the cryptographic key is used for raw cryptographic key operations.
- openpgp: If this object is used, an OpenPGP certificate[5] is created using the cryptographic key and and then associated with it.
This certificate is then stored in the NetHSM.
This object describes properties of the OpenPGP certificate[5], as well as any OpenPGP signature[5] issued using the cryptographic key.
-
notations: This optional hashmap consists of entries that each describe the key and value of an OpenPGP notation[6], that will be included in the signatures issued by this key.
-
user_ids: A list of unique strings, each describing a valid OpenPGP User ID[7].
-
version: The version of OpenPGP used for the certificate and each digital signature issued using the cryptographic key. This must be one of the following strings:
- 4: for RFC 4880[8]
- 6: for RFC 9580[9]
NOTE: Currently only 4 is supported!
-
-
-
ssh_authorized_key: The unique SSH public key of the system_user.
-
system_user: The unique Unix username of a user on the Signstar host.
-
tag: A unique tag assigned to both backend_user and signing_key_id. Tags are used as restrictions for users and keys. Only users with the same tag as a key can use that particular key.
yubihsm2
This top-level object describes settings relevant to YubiHSM2[10] backends.
NOTE: This object can only be used, if YubiHSM2 support is compiled in!
connections
A set of connections to a YubiHSM2[10].
This must be one or more of the following strings or objects:
-
usb: This object describes a YubiHSM2[10] device connected over USB.
- serial_number: A ten character long, number-only string, that encodes the unique serial number of a YubiHSM2[10] device.
-
mock: This string enables the use of an emulated, in-memory HSM for testing purposes.
WARNING: This option should only be used for testing purposes as it only provides transient data (per request) and it is only available, if YubiHSM2 mockhsm support is compiled in.
mappings
A set of user mapping objects, that describe the integration of specific YubiHSM2[10] authentication keys with the Signstar host. The following entries are understood.
admin
This object describes an administrative user of the YubiHSM2[10].
-
authentication_key_id: The unique authentication key ID of the user.
This must be a number larger than 0 and smaller than 65535.
NOTE: The default authentication key ID of an unprovisioned device is 1 and can be reused (a random passphrase is chosen for it during provisioning of the device).
Administrative users are used to
- create users
- create keys
- restore from backup
audit_log
This object describes a system user and its SSH authorized key, as well as a backend user for retrieving the audit log of the YubiHSM2[10] via the Signstar host.
-
authentication_key_id: The unique authentication key ID of the user.
This must be a number larger than 0 and smaller than 65535.
-
ssh_authorized_key: The unique SSH public key of the system_user.
-
system_user: The unique Unix username of a user on the Signstar host.
backup
This object describes a system user and its SSH authorized key, as well as a backend user for retrieving the backup of the YubiHSM2[10].
-
authentication_key_id: The unique authentication key ID of the user.
This must be a number larger than 0 and smaller than 65535.
-
ssh_authorized_key: The unique SSH public key of the system_user.
-
system_user: The unique Unix username of a user on the Signstar host.
certificate_retrieval
This object describes a system user and its SSH authorized key, as well as a backend user for retrieving the certificates associated with each signing key of the YubiHSM2[4].
-
authentication_key_id: The unique authentication key ID of the user.
This must be a number larger than 0 and smaller than 65535.
-
ssh_authorized_key: The unique SSH public key of the system_user.
-
system_user: The unique Unix username of a user on the Signstar host.
hermetic_audit_log
This object describes a system user, as well as a backend user for retrieving the audit log of the YubiHSM2[10] on the Signstar host. This integration is useful for integration with other services, that are running on the Signstar host.
-
authentication_key_id: The unique authentication key ID of the user.
This must be a number larger than 0 and smaller than 65535.
-
system_user: The unique Unix username of a user on the Signstar host.
signing
This object describes a system user and its SSH authorized key, as well as a backend user and a specific key for retrieving digital signatures from the YubiHSM2[10] via the Signstar host.
-
authentication_key_id: The unique authentication key ID of the user.
This must be a number larger than 0 and smaller than 65535.
-
domain: The unique domain, that the authentication_key_id and signing_key_id are part of. Domains partition the access capabilities of individual authentication keys to signing keys on the device.
This must be a number between 1 and 16.
-
key_setup: This object describes the cryptographic key and what type of digital signatures can be created with it.
NOTE: The combination of key_type, key_mechanism and signature_type must be valid.
-
key_type: The type of the cryptographic key.
This must be one of the following strings:
- Curve25519: for a Montgomery curve key over a prime field for the prime number 2^255-19
-
key_mechanisms: The list of mechanisms supported by the cryptographic key.
This must be one or more of the following strings:
- EdDsaSignature: Signing following the Edwards-curve Digital Signature Algorithm (EdDSA)
-
signature_type: The signature type supported by the cryptographic key.
This must be one of the following strings:
- EdDsa: Signing following the Edwards-curve Digital Signature Algorithm (EdDSA)
-
key_context: This object or string describes the context in which the cryptographic key is used.
This must be one of the following two options:
-
raw: If this string is used, the cryptographic key is used for raw cryptographic key operations.
-
openpgp: If this object is used, an OpenPGP certificate[5] is created using the cryptographic key and and then associated with it. This certificate is then stored in the NetHSM.
The openpgp object describes properties of the OpenPGP certificate[5], as well as any OpenPGP signature[5] issued using the cryptographic key.
-
notations: This optional hashmap consists of entries that each describe the key and value of an OpenPGP notation[6].
-
user_ids: A list of unique strings, each describing a valid OpenPGP User ID[7].
-
version: The version of OpenPGP used for the certificate and each digital signature issued using the cryptographic key. This must be one of the following strings:
- 4: for RFC 4880[8]
- 6: for RFC 9580[9]
NOTE: Currently only 4 is supported!
-
-
-
-
signing_key_id: A unique key ID in the YubiHSM2[10].
-
ssh_authorized_key: The unique SSH public key of the system_user.
-
system_user: The unique Unix username of a user on the Signstar host.
EXAMPLES
All backends with plaintext secret handling
system:
iteration: 1
admin_secret_handling: plaintext
non_admin_secret_handling: plaintext
mappings:
- wireguard_download:
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host
system_user: signstar-wireguard-download
nethsm:
connections:
- url: https://localhost:8080/
tls_security: Unsafe
- url: https://localhost:8081/
tls_security: Unsafe
mappings:
- admin: admin
- admin: ns1~admin
- backup:
backend_user: backup
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrIYA+bfMBThUP5lKbMFEHiytmcCPhpkGrB/85n0mAN user@host
system_user: nethsm-backup
- certificate_retrieval:
system_wide: certificateretrieval
namespaced:
- ns1~certificateretrieval
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDvhXirK3o+KPoMqhLlpfPe1UQOqznnAaTOeNtmZraUv user@host
system_user: nethsm-certificate-retrieval
- hermetic_metrics:
backend_users:
metrics_user: hermeticmetrics
operator_users:
- hermetickeymetrics
system_user: nethsm-hermetic-metrics
- metrics:
backend_users:
metrics_user: metrics
operator_users:
- keymetrics
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host
system_user: nethsm-metrics
- signing:
backend_user: signing
signing_key_id: signing1
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
notations:
test: value
user_ids:
- Foobar McFooface <foobar@mcfooface.org>
version: "4"
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host
system_user: nethsm-signing
tag: signing1
- signing:
backend_user: ns1~signing
signing_key_id: signing1
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
notations:
test: value
user_ids:
- Barfoo McBarface <barfoo@mcbarface.org>
version: "4"
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJr4wdlEv3ZKkDufEQTZSLOjDLO3DeNN2pqKmp00ufIu user@host
system_user: nethsm-ns1-signing
tag: signing1
yubihsm2:
connections:
- usb:
serial_number: 12345678
- usb:
serial_number: 87654321
mappings:
- admin:
authentication_key_id: 1
- audit_log:
authentication_key_id: 2
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxR0Oc+SWXkEvvZPitc6NvjvykgiKc9iauRI7tLYvcp user@host
system_user: yubihsm2-audit-log
- backup:
authentication_key_id: 3
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIETxhCqeZhfzFLfH0KFyw3u/w/dkRBUrft8tQm7DEVzY user@host
system_user: yubihsm2-backup
- certificate_retrieval:
authentication_key_id: 4
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDfCJD+futTp8vlKxx2Rd1hqY+vnMp9iXSRTeCLhcGmr user@host
system_user: yubihsm2-certificate-retrieval
- hermetic_audit_log:
authentication_key_id: 5
system_user: yubihsm2-hermetic-audit-log
- signing:
authentication_key_id: 6
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
notations:
test: value
user_ids:
- Foobar McFooface <foobar@mcfooface.org>
version: "4"
domain: 1
signing_key_id: 1
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIClIXZdx0aDOPcIQA+6Qx68cwSUgGTL3TWzDSX3qUEOQ user@host
system_user: yubihsm2-signing
Shamirs Secret Sharing and systemd creds
system:
iteration: 1
admin_secret_handling:
shamirs-secret-sharing:
number_of_shares: 3
threshold: 2
non_admin_secret_handling: systemd-creds
mappings:
- share_holder:
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAN54Gd1jMz+yNDjBRwX1SnOtWuUsVF64RJIeYJ8DI7b user@host
system_user: signstar-share-holder1
- share_holder:
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host
system_user: signstar-share-holder2
- share_holder:
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILWqWyMCk5BdSl1c3KYoLEokKr7qNVPbI1IbBhgEBQj5 user@host
system_user: signstar-share-holder3
- wireguard_download:
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host
system_user: signstar-wireguard-download
nethsm:
connections:
- url: https://localhost:8080/
tls_security: Unsafe
- url: https://localhost:8081/
tls_security: Unsafe
mappings:
- admin: admin
- admin: ns1~admin
- backup:
backend_user: backup
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrIYA+bfMBThUP5lKbMFEHiytmcCPhpkGrB/85n0mAN user@host
system_user: nethsm-backup
- certificate_retrieval:
system_wide: certificateretrieval
namespaced:
- ns1~certificateretrieval
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDvhXirK3o+KPoMqhLlpfPe1UQOqznnAaTOeNtmZraUv user@host
system_user: nethsm-certificate-retrieval
- hermetic_metrics:
backend_users:
metrics_user: hermeticmetrics
operator_users:
- hermetickeymetrics
system_user: nethsm-hermetic-metrics
- metrics:
backend_users:
metrics_user: metrics
operator_users:
- keymetrics
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host
system_user: nethsm-metrics
- signing:
backend_user: signing
signing_key_id: signing1
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
user_ids:
- Foobar McFooface <foobar@mcfooface.org>
version: "4"
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host
system_user: nethsm-signing
tag: signing1
- signing:
backend_user: ns1~signing
signing_key_id: signing1
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
user_ids:
- Barfoo McBarface <barfoo@mcbarface.org>
version: "4"
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJr4wdlEv3ZKkDufEQTZSLOjDLO3DeNN2pqKmp00ufIu user@host
system_user: nethsm-ns1-signing
tag: signing1
yubihsm2:
connections:
- usb:
serial_number: 12345678
- usb:
serial_number: 87654321
mappings:
- admin:
authentication_key_id: 1
- audit_log:
authentication_key_id: 2
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxR0Oc+SWXkEvvZPitc6NvjvykgiKc9iauRI7tLYvcp user@host
system_user: yubihsm2-audit-log
- backup:
authentication_key_id: 3
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIETxhCqeZhfzFLfH0KFyw3u/w/dkRBUrft8tQm7DEVzY user@host
system_user: yubihsm2-backup
- certificate_retrieval:
authentication_key_id: 4
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDfCJD+futTp8vlKxx2Rd1hqY+vnMp9iXSRTeCLhcGmr user@host
system_user: yubihsm2-certificate-retrieval
- hermetic_audit_log:
authentication_key_id: 5
system_user: yubihsm2-hermetic-audit-log
- signing:
authentication_key_id: 6
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
user_ids:
- Foobar McFooface <foobar@mcfooface.org>
version: "4"
domain: 1
signing_key_id: 1
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIClIXZdx0aDOPcIQA+6Qx68cwSUgGTL3TWzDSX3qUEOQ user@host
system_user: yubihsm2-signing
Systemd credentials
system:
iteration: 1
admin_secret_handling: plaintext
non_admin_secret_handling: systemd-creds
mappings:
- wireguard_download:
system_user: wireguard-downloader
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host
nethsm:
connections:
- url: https://localhost:8080/
tls_security: Unsafe
- url: https://localhost:8081/
tls_security: Unsafe
mappings:
- admin: admin
- admin: ns1~admin
- backup:
backend_user: backup
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrIYA+bfMBThUP5lKbMFEHiytmcCPhpkGrB/85n0mAN user@host
system_user: nethsm-backup
- certificate_retrieval:
system_wide: certificateretrieval
namespaced:
- ns1~certificateretrieval
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDvhXirK3o+KPoMqhLlpfPe1UQOqznnAaTOeNtmZraUv user@host
system_user: nethsm-certificate-retrieval
- hermetic_metrics:
backend_users:
metrics_user: hermeticmetrics
operator_users:
- hermetickeymetrics
system_user: nethsm-hermetic-audit-log
- metrics:
backend_users:
metrics_user: metrics
operator_users:
- keymetrics
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host
system_user: nethsm-metrics
- signing:
backend_user: signing
signing_key_id: signing1
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
user_ids:
- Foobar McFooface <foobar@mcfooface.org>
version: "4"
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host
system_user: nethsm-signing
tag: signing1
- signing:
backend_user: ns1~signing
signing_key_id: signing1
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
user_ids:
- Barfoo McBarface <barfoo@mcbarface.org>
version: "4"
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJr4wdlEv3ZKkDufEQTZSLOjDLO3DeNN2pqKmp00ufIu user@host
system_user: nethsm-ns1-signing
tag: signing1
yubihsm2:
connections:
- usb:
serial_number: 12345678
- usb:
serial_number: 87654321
mappings:
- admin:
authentication_key_id: 1
- audit_log:
authentication_key_id: 2
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxR0Oc+SWXkEvvZPitc6NvjvykgiKc9iauRI7tLYvcp user@host
system_user: yubihsm2-audit-log
- backup:
authentication_key_id: 3
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIETxhCqeZhfzFLfH0KFyw3u/w/dkRBUrft8tQm7DEVzY user@host
system_user: yubihsm2-backup
- certificate_retrieval:
authentication_key_id: 4
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDfCJD+futTp8vlKxx2Rd1hqY+vnMp9iXSRTeCLhcGmr user@host
system_user: yubihsm2-certificate-retrieval
- hermetic_audit_log:
authentication_key_id: 5
system_user: yubihsm2-hermetic-audit-log
- signing:
authentication_key_id: 6
key_setup:
key_type: Curve25519
key_mechanisms:
- EdDsaSignature
signature_type: EdDsa
key_context:
openpgp:
user_ids:
- Foobar McFooface <foobar@mcfooface.org>
version: "4"
domain: 1
signing_key_id: 1
ssh_authorized_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIClIXZdx0aDOPcIQA+6Qx68cwSUgGTL3TWzDSX3qUEOQ user@host
system_user: yubihsm2-signing
SEE ALSO
nethsm(1), signstar-sign(1), signstar-configure(1), signstar-configure-build(1), systemd-creds(1), cryptsetup(8)
NOTES
-
Signstar
-
Configuration Files Specification
https://uapi-group.org/specifications/specs/configuration_files_specification/
-
XDG Base Directory Specification
-
Shamir’s Secret Sharing
-
NetHSM
-
OpenPGP certificate
-
OpenPGP signature
-
OpenPGP notation
https://openpgp.dev/book/adv/signatures.html#notation-signature-subpackets
-
OpenPGP User ID
-
RFC 4880
-
RFC 9580
-
YubiHSM2
https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/
Signstar configure
Runtime configuration for Signstar hosts.
A Signstar host is a Linux host, that has several regular users configured, which allow access to specific credentials on an HSM backend.
This package provides a library and the binary signstar-configure that rely on the system’s Signstar configuration file to handle and optionally configure the administrative credentials which are then used to configure
- the available HSM backends (e.g. NetHSM and/or YubiHSM2)
- the specific backend credentials for real users on the host
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_configure/ for development version of the crate
- https://docs.rs/signstar_configure/latest/signstar_configure/ for released versions of the crate
Examples
Library
Synchronization of backends depends on the system’s Signstar configuration file, the available HSM backends and administrative credentials.
#[cfg(not(feature = "_yubihsm2-mockhsm"))]
mod default {
pub fn main() {}
}
#[cfg(feature = "_yubihsm2-mockhsm")]
mod yubihsm2_mockhsm {
use signstar_configure::HostConfiguration;
use signstar_config::config::Config;
pub fn main() -> testresult::TestResult {
// NOTE: You can rely on the `signstar_configure::load_config` helper function to load the system's Signstar config.
let config = Config::from_file_path("../fixtures/config/yubihsm2_mockhsm_backend/admin-plaintext-non-admin-plaintext.yaml")?;
let backend = HostConfiguration::new(&config);
backend.sync()?;;
Ok(())
}
}
#[cfg(feature = "_yubihsm2-mockhsm")]
use yubihsm2_mockhsm::main;
#[cfg(not(feature = "_yubihsm2-mockhsm"))]
use default::main;
CLI
signstar-configure
Features
_containerized-integration-test: Integration tests that require a containerized test environment. NOTE: Unless you are developing this crate, you will very likely not want to use this feature._yubihsm2-mockhsm: Test environment and integration using a virtual YubiHSM2 NOTE: Unless you are developing this crate, you will very likely not want to use this feature.nethsm: Enables support for the NetHSM backend (default).yubihsm2: Enables support for the YubiHSM2 backend (default)
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.1.1] - 2026-09-29
Added
- Make
climodule public
[0.1.0] - 2026-09-29
Added
- (systemd) Connect stdout and stderr to journal
- Add systemd units for
signstar-configure - Add the
signstar-configurecommand line interface - Add library support for backend configuration
- Initialize the
signstar-configurecrate
Fixed
- Persist new admin credentials before synchronization of backend
- (systemd) Fix use of
OnCalendarin the signstar-configure timer - [breaking] Diversify the logger setup for journald and terminal
Other
- Add architecture document
Architecture
This project tries to opportunistically configure all backends it is aware of in a way that is consistent with the Signstar configuration file on the Signstar host.
Functionality and integration
The executable must be run as root, and is meant to be run repeatedly on a timer after boot (e.g. using a systemd.timer unit). Running the application repeatedly allows to react to changes with regards to the administrative credentials or backends, apply backups, etc.
An initial run of the executable is meant to be used in Automatic Boot Assessment scenarios to establish whether the system consistently fails to boot and reverting to a previous state of the Signstar host should be attempted.
signstar-configure may fail for various reasons and exit with a non-zero status code.
Any possible failure is of one of the following two categories:
- unrecoverable: The Signstar system is irreparably broken and needs to be reset (potentially booted into a previous version).
- recoverable: The Signstar system is missing required input data or the backend is not available (the executable may succeed when rerunning it).
The signstar-configure executable is able to restore an HSM backend from backup.
Each backup file is tied to a very specific set of administrative credentials and a specific Signstar configuration file.
The specific set of administrative credentials allow to apply the backup and make use of all administrative accounts available in the backend.
The version match between backup file, Signstar configuration file and administrative credentials is referred to as an iteration.
Dependencies
The signstar-configure executable depends on correct system time and should therefore only run after the system clock has been synchronized successfully.
System configuration
When running signstar-configure, the execution flow passes through several stages.
Each stage may have a set of inputs, outputs and/or optional-outputs.
All outputs and optional-outputs can be made available to the following stages.
Each stage may abort based on unrecoverable or recoverable errors. Many stages may be skipped based on whether a condition can not be met.
Read configuration
A Signstar configuration file on the system is read from one of the default locations.
- input: Signstar configuration file.
- unrecoverable: There is no configuration file, or the configuration file can not be read or is invalid.
- output: The configuration object, which covers general configuration options, a set of connections and a set of user mappings.
Check connections
Each backend connection configured in the Signstar configuration file is checked. As a fallback, the hardcoded default connection - if applicable - of an unprovisioned HSM backend is checked.
First, each exact configured connection is probed for availability. If there is only one configured connection and it is not reachable at the configured address, the default connection is probed instead.
If there is more than one connection, with none of them reachable, the default connection is probed instead for the first unreachable connection.
If there is more than one connection, with at least one of them in locked or operational state and one or more of them not reachable, the default connection is probed instead for the first unreachable connection.
- input: Signstar configuration object.
- unrecoverable: One of the connections fails due to TLS issues (e.g. the backend changed and does not provide the same TLS certificate as before).
- recoverable: A backend is not reachable (neither at its configured address, nor ath the hardcoded default address).
- output: A set of available connections.
Read backup file
Any Signstar backup file present in the dedicated location is read and validated.
- input: Signstar backup file.
- condition: Signstar backup file exists.
- recoverable: More than one backup file has been uploaded and all are removed.
- recoverable: A backup file is not well-formed and is removed.
- output: A Signstar backup file.
Read administrative credentials
All administrative credentials present on the Signstar host are read and validated. Administrative credentials may exist as persistent plaintext files, systemd-creds encrypted files or as ephemeral shares of a shared secret using Shamir’s Secret Sharing (SSS).
- input: Signstar administrative credentials.
- condition: Signstar administrative credentials are present.
- recoverable: Administrative credentials are invalid and are removed.
- output: One or more sets of administrative credentials.
Validate backup file with administrative credentials
If a Signstar backup file is present, use administrative credentials with a matching iteration to validate the file.
- input: Signstar backup file.
- input: One or more sets of administrative credentials.
- condition: Signstar backup file exists.
- condition: Signstar administrative credentials are present.
- recoverable: Administrative credentials with a matching iteration can not be used for validating the Signstar backup file and the backup file is removed.
- output: Administrative credentials in particular iteration.
Create administrative credentials
If all configured connections are available and unprovisioned and no backup file is present, create initial administrative credentials. Type (plaintext, systemd-creds encrypted files or shares of a shared secret using SSS) and location depend on the relevant Signstar configuration item.
- input: Signstar configuration object.
- input: Signstar backup file.
- condition: All available connections are in unprovisioned state.
- condition: Signstar backup file does not exist.
- unrecoverable: Administrative credentials can not be created.
- output: Administrative credentials.
Ensure all shares of a shared secret are downloaded
If Shamir’s Secret Sharing is configured for the administrative credentials and administrative credentials are present in a runtime directory and all available connections are in unprovisioned state and no backup file is present check if all shares of the shared secret are downloaded.
To track the download state of each share in the login user’s respective runtime directory, signstar-configure relies on accompanying state files that indicate them being downloaded at least once.
- input: Signstar backup file.
- input: Administrative credentials.
- condition: All available connections are in unprovisioned state.
- condition: Administrative credentials are available.
- condition: Shamir’s Secret Sharing is used for administrative credentials.
- condition: Signstar backup file does not exist.
- recoverable: Not all shared secrets are downloaded.
Create non-administrative secrets
If administrative credentials are available, non-administrative secrets for backend users associated with each login user on the Signstar host are created in a persistent, per-user location.
- input: Administrative credentials.
- input: Signstar configuration object.
- condition: Administrative credentials are available.
- condition: Iteration of administrative credentials matches that of the Signstar configuration object.
- recoverable: Administrative credentials are not available.
- unrecoverable: Non-administrative secrets can not be created.
- output: Non-administrative secrets.
Provision first unprovisioned backend
If all backends are (available and) unprovisioned, no Signstar backup file exists and administrative credentials in an iteration matching the Signstar configuration object are present on the system, provision the first backend using the administrative credentials in the particular version.
- input: Administrative credentials.
- input: Non-administrative secrets.
- input: Signstar configuration object.
- condition: All backends are unprovisioned.
- condition: Signstar backup file does not exist.
- condition: Iteration of administrative credentials matches that of the Signstar configuration object.
- recoverable: An error occurs while provisioning the backend because of connectivity issues and the non-administrative secrets are removed.
Restore from backup file
If all backends are available, a Signstar backup file exists and administrative credentials in an iteration matching the Signstar configuration object are present on the system, restore all backends from it. First restore any backend from backup, that is unprovisioned, then - on a best effort basis - attempt to restore from backup any backend that has already been provisioned by iteratively trying the provided administrative credentials.
Afterwards, persist the newly created non-administrative secrets. Remove the administrative credentials if Shamir’s Secret Sharing is used and keep them with the matching iteration if Shamir’s Secret Sharing is not used. Finally, remove the uploaded backup file.
- input: Administrative credentials.
- input: Non-administrative secrets.
- input: Signstar configuration object.
- input: Signstar backup file.
- condition: Signstar backup file exists.
- condition: Iteration of administrative credentials matches that of the Signstar backup file and Signstar configuration object.
Synchronize backend
If all backends are available (either unprovisioned or provisioned), no Signstar backup file exists and administrative credentials in an iteration matching the Signstar configuration object are present on the system, synchronize the state of all backends with the administrative credentials and the Signstar configuration object. Remove ephemeral data.
- input: All backends are available (unprovisioned or provisioned).
- input: Administrative credentials.
- input: Signstar configuration object.
TODO: Update scenario, when iteration of Signstar configuration changes and we have administrative credentials in a previous iteration. TODO: Deal with multiple NetHSM backends in unprovisioned state (which will have the same IP address!).
Signstar configure build
A commandline tool to configure a Signstar system during build.
The scope of this project is to read a dedicated configuration file, derive system users and their integration from it and create them.
The signstar-configure-build executable must be run as root.
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_configure_build/ for development version of the crate
- https://docs.rs/signstar_configure_build/latest/signstar_configure_build/ for released versions of the crate
Configuration file
By default signstar-configure-build relies on the configuration file /usr/share/signstar/config.yaml and will fail if it is not found or not valid.
One of the following configuration files in increasing priority are used instead, if they exist:
/run/signstar/config.yaml/etc/signstar/config.yaml
Alternatively, signstar-configure-build can be provided with a custom configuration file location using the --config/ -c option.
System users
Based on configured user mappings in the configuration file, signstar-configure-build:
- creates unlocked system users
- without passphrase
- with a home directory below
/var/lib/signstar/home/(but without creating it)
- adds tmpfiles.d integration for each user, so that their home directory is created automatically
- adds a dedicated authorized_keys file and sshd_config drop-in configuration, which defines a ForceCommand option to enforce specific commands for each configured user with SSH access
Examples
Assuming a valid configuration file in one of the understood configuration file locations, the executable is called without any options:
signstar-configure-build
Features
_yubihsm2-mockhsm: Test environment using a virtual YubiHSM2 (currently this feature merely implies theyubihsm2feature). NOTE: Unless you are developing this crate, you will very likely not want to use this feature.yubihsm2: For YubiHSM2 support.
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.4.0] - 2026-09-29
Added
- [breaking] Rename
SystemUserMappingvariant for WireGuard config downloads - Add
NetHsmUserMapping::CertificateRetrieval - Add
YubiHsm2UserMapping::CertificateRetrieval - (deps) Update rustcrypto crates
- [breaking] Support sets of filters with
Config::user_backend_connections - Add created users to the
_yubihsm2group - Add logging to
signstar-configure-build - Port to new
Configformat - (cargo) Rename
mockhsmfeature to_yubihsm2-mockhsm - (cargo) Add a
mockhsmfeature, switching on theyubihsm2feature - Add metrics related variants for
UserMapping - Add
UserMapping::SystemYubiHsm2Backup - Add
UserMapping::YubiHsmOnlyAdmin - (cargo) Use workspace lints
- Add
UserMapping::SystemYubiHsmOperatorSigningfor YubiHSM2 - [breaking] Use
signstar_crypto::key::SigningKeySetupinUserMapping - [breaking] Support using different HSM backends in
SignstarConfig
Fixed
- Align the naming of
signstar-download-key-certificates - Fix
DisableForwardinginstruction - Disable all SSH forwarding features for Signstar host system users
- [breaking] Diversify the logger setup for journald and terminal
- Set a random passphrase for created users
- (deps) Update Rust crate sysinfo to v0.39.0
- (deps) Update Rust crate nix to 0.31.0
- (deps) Update Rust crate sysinfo to v0.38.0
Other
- Remove warning for
_yubihsm2-mockhsmfeature debug requirement - (fixtures) Consolidate the naming of system users in configs
- [breaking] Use
AuthorizedKeyEntry/SystemUserIdviaconfigmodule - (README) Improve information about available features
- Make
AuthorizedKeyEntrya newtype forEntrynotString - (cargo) Move
nixcrate to workspace dependencies - [breaking] Change name to
UserMapping::YubiHsm2OnlyAdmin - [breaking] Change name to
UserMapping::SystemYubiHsm2OperatorSigning - Make
Error::NoForceCommandForMappingmore generic - Use clap’s ‘-V’/
--versionintegration instead of a custom one - Document all publicly visible items
[0.3.0] - 2025-08-19
Added
- Replace
HermeticParallelConfigwithSignstarConfig
Fixed
- (deps) Update Rust crate sysinfo to v0.37.0
- (deps) Update Rust crate sysinfo to 0.36.0
Other
- Use
signstar-signwhen configuring accounts for signing
[0.2.0] - 2025-07-10
Added
- Use Display for printing errors
- Rely on
signstar-commoncrate for default locations
Fixed
- (deps) update rust crate nix to 0.30.0
- (deps) update rust crate sysinfo to 0.35.0
- (deps) update rust crate sysinfo to v0.34.0
Other
- Fix violations of MD007
- Fix violations of MD022 and MD032 in changelogs
- Reformat all TOML files with
taplo - Sort derives using
cargo sort-derives - Switch to rustfmt style edition 2024
- Add
non_admin_secret_handlingto example configuration - Add
admin_secret_handlingto example configuration
[0.1.2] - 2024-12-08
Fixed
- (deps) update rust crate sysinfo to 0.33.0
Other
- (README) Add links to latest (un)released crate documentation
[0.1.1] - 2024-11-27
Other
- Update libc crate as the previously used version was yanked
[0.1.0] - 2024-11-26
Added
- Add build-time configuration tool for signstar host
Other
- Consolidate contributing and licensing information
Signstar crypto
Common types and functionality for cryptography in Signstar.
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_crypto/ for development version of the crate
- https://docs.rs/signstar_crypto/latest/signstar_crypto/ for released versions of the crate
Features
_containerized-integration-test: Integration tests that require a containerized test environment. NOTE: Unless you are developing this crate, you will very likely not want to use this feature._test-helpers: Helpers used for integration test setups. NOTE: Unless you are developing this crate, you will very likely not want to use this feature.nethsm: Enable integration required for use with a NetHSM.yubihsm2: Enables support for the YubiHSM2 backend.
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.1.0] - 2026-09-28
Added
- Sort
CryptographicKeyContextand implementPartialOrdandOrd - [breaking] Introduce a crate-level error type for signstar-common
- (deps) [breaking] Update crate nethsm-sdk-rs to 4.0.0
- (deps) Update rustcrypto crates
- (deps) Upgrade crate
yubihsm2to 0.45.0 - Use better defaults for
OpenPgpKeyUsageFlags - [breaking] Extend
CryptographicKeyContext::OpenPgpwithnotations - Derive
CloneforOpenPgpKeyUsageFlags - (deps) Upgrade crate
yubihsm2to 0.44.0 - Return
Result<Option<usize>, Error>for certificate size estimation - Add size validation of
YubiHsm2UserMappinginstances in aYubiHsm2Config - Add
EmptyEd25519Signerfor easy certificate size estimation - Append Signer’s UID to issued signatures
- Implement
TryFrom<yubihsm::Algorithm>forKeyType - Add support for P-256, P-384 and (partially) P-512 Brainpool
- Add support for ECC K-256 (Koblitz)
- Add back NIST P-224 support
- Add
Passphrase::check_against_policy - Add
Passphrase::new_with_policy - Add
PassphrasePolicyto describePassphrasepolicies - Add
lenandis_emptymethods forPassphrase - Implement creating
Passphrasefrom a file path - [breaking] Drop MD5 from supported algorithms and dependencies
- Publicly expose default values for Shamir’s Secret Sharing (SSS)
- Derive
AsRefStrforNonAdministrativeSecretHandling - Add
AdministrativeSecretHandlingenum - Add reading and writing of non-administrative secrets
- Derive
OrdandPartialOrdforOpenPgpUserIdList - Derive
OrdandPartialOrdforOpenPgpUserId - Implement
OrdandPartialOrdforOpenPgpUserIdType - Implement
DisplayforOpenPgpUserIdType - Derive
OrdandPartialOrdforOpenPgpVersion - Derive
OrdandPartialOrdforCryptographicKeyContext - Skip serializing
SigningKeySetup::key_lengthif it isNone - Derive
OrdandPartialOrdforSigningKeySetup - Move OpenPGP related logic out of
nethsmintosignstar-crypto - Add
Passphrase::generateto generate new passphrases - Add the
UserWithPassphrasetrait - Add
SigningKeySetupto describe environments for signing keys - Make
PrivateKeyDatapublicly accessible - Add
Passphrasetype to handle passphrases - Add types for cryptographic key ingestion and import
- Add
keymodule providing various types for cryptographic keys - Add an
openpgpmodule for simple OpenPGP related types - Initialize bare
signstar-cryptocrate
Fixed
- [breaking] Diversify the logger setup for journald and terminal
- (deps) More strictly lock the version ranges for custom dependencies
- [breaking] Correctly generate certificates with multiple User IDs
- (deps) Update Rust crate pgp to 0.20
- remove unused import
- Replace returning errors to direct
panics to avoid clippy lints
Other
- Rely on coverage collection functionality in change-user-run
- Enforce
_containerized-integration-testfeature for test module - Enforce binary ID for
_containerized-integration-testtests - Improve
CryptographicKeyContext::openpgp_cert_sizedocumentation - [breaking] Move
Error::UnsupportedNetHsmKeyMechanismtokeymodule - [breaking] Expose all signstar_crypto errors over top-level Error type
- (README) Improve information about available features
- (deps) Update Rust crate pgp to 0.19
- Use serde
Deserialize/Serializetop-level inopenpgpmodule - Feature-guard unit tests that require the
nethsmfeature - Expose
SignedSecretKeythroughnethsm
Signstar Download Key Certificates
Offers a library and executable for returning key certificates stored in backends of a Signstar host.
Currently, only OpenPGP certificates are returned. However, the response format is designed with extensibility in mind and other technologies can be integrated in the future.
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_download_key_certificates/ for development version of the crate
- https://docs.rs/signstar_download_key_certificates/latest/signstar_download_key_certificates/ for released versions of the crate
signstar-download-key-certificates
The command does not take any flags (with the exception of the verbosity level) nor any input:
signstar-download-key-certificates | jq --raw-output '.certs[0]'
Features
_containerized-integration-test: Integration tests that require a containerized test environment. NOTE: Unless you are developing this crate, you will very likely not want to use this feature._yubihsm2-mockhsm: Test environment and integration using a virtual YubiHSM2 (implies theyubihsm2feature). NOTE: Unless you are developing this crate, you will very likely not want to use this feature.cli: Enables the command line interface (enabled by default)nethsm: For NetHSM support.yubihsm2: For YubiHSM2 support.
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.1.0] - 2026-09-29
Added
- Implement
signstar-download-key-certificates
Other
- Add documentation for NetHSM test routes
Signstar Request Signature
This crate offers a library and an executable for creating, reading and writing of signing requests for files.
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_request_signature/ for development version of the crate
- https://docs.rs/signstar_request_signature/ for released versions of the crate
- Design document and specifications for requests and responses
Preparing signing request
The tool can be used to prepare a raw JSON signing request:
signstar-request-signature prepare Cargo.toml
Sending the signing request over SSH
Additionally it’s possible to prepare the signing request and send it over SSH in one command.
send subcommand requires parameters related to SSH session:
signstar-request-signature send --config "$config" --user signstar-sign Cargo.toml | jq
Features
_containerized-integration-test: Integration tests that require a containerized test environment. NOTE: Unless you are developing this crate, you will very likely not want to use this feature.cli: Enables command line interface for creating and sending signing requests
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.2.1] - 2026-09-29
Added
- (cargo) Use the
clifeature by default
[0.2.0] - 2026-09-28
Added
- (deps) Update rustcrypto crates
- Always require the
userargument when runningsignstar-request-signature send - Always require
expectattributes instead ofallows - Use configuration file when sending signing requests
- Make executable and related dependencies optional in
signstar-request-signature
Fixed
- [breaking] Diversify the logger setup for journald and terminal
- (deps) More strictly lock the version ranges for custom dependencies
- (deps) Update Rust crate russh to v0.63.3
- Use
NonZeroU32for exit status code failures - (deps) Update Rust crate russh to v0.62.1
- (deps) Update Rust crate russh to 0.61.0
- (deps) Update Rust crate hmac to 0.13.0 and sha1 to 0.11.0
- (deps) Update Rust crate russh to 0.59.0
- Remove unnecessary call to
into - (deps) Update Rust crate russh to 0.58.0
- (deps) Update Rust crate russh to 0.57.0
- Update test vectors for sha2 hasher state
- (deps) Update Rust crate russh to v0.55.0
Other
- Enforce binary ID for
_containerized-integration-testtests - Convert
allows intoexpects - Remove redundant denies that are defined using workspace lints
- Format all JSON files using
biome - (deps) Update Rust crate ssh-agent-lib to v0.6.0
- Use
digest-iodependency instead of our own struct - (deps) Move
sha1crate to workspace dependencies - (deps) Move
serde_jsoncrate to workspace dependencies - Use base64ct instead of base64 in
signstar-request-signature - (deps) Update dependencies
- (cargo) Move crate
clap-verbosity-flagto workspace dependencies - (cargo) Use crate
tokiofrom workspace dependencies - (cargo) Move crate
tempfileto workspace dependencies
[0.1.3] - 2025-08-19
Added
- Add logging with verbosity to
signstar-request-signature
Fixed
- (deps) Update Rust crate russh to 0.54.0
[0.1.2] - 2025-07-10
Added
- Use
Digest::updateinstead ofcopyforRequest::for_file - Use Display for printing errors
Fixed
- Wrap
sha2::Sha512in an adapter providing thestd::io::Writeimplementation - (deps) Update Rust crate russh to v0.53.0
- Use shorter names for agent socket path in
ssh-roundtriptest - (deps) update rust crate russh to 0.52.0
Other
- Fix violations of MD007
- Fix violations of MD022 and MD032 in changelogs
- Reformat all TOML files with
taplo - Sort derives using
cargo sort-derives
[0.1.1] - 2025-04-22
Added
- Add
Response::v1for creating version 1 signing responses - Add signing specifications to mdbook
- Add
signstar-request-signature sendsubcommand for sending signing requests - Add API to programmatically send signing requests
Other
- (deps) make
loga workspace dependency - Add documentation and enable strict lints for
signstar-request-signature - Improve code documentation in signstar-request-signature
- Add documentation for Signing Responses
- Switch to rustfmt style edition 2024
[0.1.0] - 2024-12-13
Added
- Add minimal binary for producing hash states
Design
This document describes the design decisions and technical details of the signature request mechanism used by Signstar.
Design requirements
-
the signing request must be of small size, regardless of the size of the signed file. This makes it possible to minimize network traffic between client and the signing server
-
the signing request must convey enough information to identify the file being signed in a secure way
-
the signing request must allow optional extensions and evolution of the format. Backwards and forwards compatibility will allow mixing versions of clients and servers which will make the migration to new versions smoother
-
the format of the request must be easy to inspect by a human operator if such a need arises
Transmitting the state instead of the digest
---
title: High-level Signstar signing setup
---
sequenceDiagram
participant C as Client
participant S as Signstar
participant N as NetHSM
Note over C: hashes a file without finalizing the digest
C ->> S: send a signing request with the hasher state
S ->> S: recover signed file's digest from the hasher state
S ->> S: create OpenPGP packets and append them to the hasher
S ->> S: finalize the OpenPGP hasher stream
S ->> N: request a raw cryptographic signature
N ->> N: create a raw cryptographic signature
N ->> S: return a raw cryptographic signature
S ->> S: add protocol-specific framing to the raw cryptographic signature
S ->> C: return signature
The signing requests of most cryptographic systems are based on providing a digest to create a signature for. The Signstar project follows a different approach, outlined in the graph above. The internal hasher state is transmitted instead of the final digest, which has several advantages:
-
it decouples the act of hashing a file from the framing that is imposed by the target cryptographic system (e.g. OpenPGP).
-
transmitting the state makes it possible for the client to do just the file hashing with no cryptographic technology specific handling at all. All further cryptographic processing (e.g. appending OpenPGP packets) is done by the server.
-
the server may append any packets that it sees fit, for example timestamping notations, capturing the identity of the signer or any constraints on the signature (such as making the signatures expire).
-
since the state is just an array of bytes the signing server may additionally finalize it early to recover the digest of the file to be signed.
-
the client cannot fake data (such as OpenPGP notations), since it is the server appending them.
-
this design is easily expandable to other systems, that require only the file digest (such as SSH signatures).
The client sends a state and a malicious Signstar host could misuse the state to claim a signature over a different file. This creates a central and exposed position for the signing service host and requires it to be trustworthy and sufficiently monitored. In this design it is considered an acceptable trade-off, as the Signstar host is considered critical infrastructure, that has no direct access to private key material.
A discussion with the RustCrypto upstream raised the point that transmitting the state is “well beyond the typical analysis”, as hazmat functionality is made use of. Note that transmitting the hasher state is only needed in the context of OpenPGP signatures, as in the case of SSH and X.509 the file digest is used directly.
Comparison of signing methods
The following section discusses the details on what exactly is signed by the low-level cryptographic primitive. All reviewed cryptographic systems allow attaching additional metadata to signed files. Low-level primitives therefore make use of a different digest than that of the plain file itself.
OpenPGP
In OpenPGP the calculation of the digest-to-be-signed is described in RFC 9580: Notes on Signatures.
Currently, two types of signatures are in active use: Version 4 and version 6.
For version 4 signatures a hash of the file to be signed is created. Then the hasher is updated with OpenPGP specific metadata (signature subpackets), just as if they were appended at the end of the file.
This can be imagined as follows:
flowchart TD
F[file] --> CAT
M[metadata] --> CAT
CAT(concatenate bytes)
CAT --> DIGEST
DIGEST(compute digest) --> DTS
DTS[data to be signed]
RFC 9580, which is a revision of the widely implemented RFC 4880 additionally specifies Version 6 signatures which prepend the file with a randomly generated “salt” value:
flowchart TD
S[salt] --> CAT
F[file] --> CAT
M[metadata] --> CAT
CAT(concatenate bytes)
CAT --> DIGEST
DIGEST(compute digest) --> DTS
DTS[data to be signed]
The server creates pgp-metadata and continues the hashing, which produces the final digest-to-be-signed.
With V4 signatures the server is able to recover the digest of the file, which may be embedded in the OpenPGP metadata as a signature notation.
Version 6 signatures prepend a randomly generated “salt” value, which precludes calculation of the original file digest by the server and addition of this value as a signature notation. However, this still allows to issue a signature over the combination of salt and file.
SSH file signatures
SSH calculates the file digest directly, then wraps that digest’s value in a different structure, which includes metadata.
The documentation provides a rationale to why the file digest is computed first:
This is done to limit the amount of data presented to the signature operation, which may be of concern if the signing key is held in limited or slow hardware or on a remote ssh-agent.
Conceptually the operation looks like this:
flowchart TD
P[SSH signature prefix] --> CAT
F[file] --> DIGEST
DIGEST(compute digest) --> CAT
CAT(concatenate bytes) --> DIGEST2
DIGEST2(compute digest) --> DTS
DTS[data to be signed]
X.509 signatures
X.509 signatures are similar to SSH file signatures: the digest of the file is computed first, then it is embedded in the SignedData structure through an intermediate SignerInfo structure. The entire SignedData structure is digested and signed. The signature is inserted as a field of the SignerInfo.
flowchart TD
F[file] --> DIGEST
DIGEST(compute digest) --> WRAP
WRAP(wrap in a signerInfo) --> WRAP2
WRAP2(wrap in a signedData) --> DIGEST2
DIGEST2(compute digest) --> DTS
DTS[data to be signed] --> SIG
SIG(update signerInfo)
Signstar Create Signing Request
A signing request format for Signstar systems.
Glossary
- Client: The application (here:
signstar-request-signature) which computes a file digest and creates a signing request - Server: The application (currently:
nethsm-cli) which receives a signing request, processes it and returns a signature - Signing request: Machine and human-readable description of the data that will be signed. The exact format for the signing request is described below.
- Signature: Raw cryptographic signature in a technology-specific framing (e.g. in “packets” for OpenPGP).
Format specification
The signing request is a custom JSON encoded data format. It is used to represent required information on data input (the hasher state type and the hasher state) and output (the requested signature type). Additionally, arbitrary optional data can be provided.
The below sample signing request is fully expanded for illustrative purposes:
{
"version": "1.0.0",
"required": {
"input": {
"type": "sha2-0.11-SHA512-state",
"content": [8,201,188,243,103,230,9,106,59,167,202,132,133,174,103,187,43,248,148,254,114,243,110,60,241,54,29,95,58,245,79,165,209,130,230,173,127,82,14,81,31,108,62,43,140,104,5,155,107,189,65,251,171,217,131,31,121,33,126,19,25,205,224,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,20,73,32,108,105,107,101,32,115,116,114,97,119,98,101,114,114,105,101,115,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
},
"output": {
"type": "OpenPGPv4"
}
},
"optional": {
"request-time": 1728913277,
"XHy1dHj": "https://gitlab.archlinux.org/archlinux/signstar/-/merge_requests/43"
}
}
The fields are as follows:
-
version- Semantic Versioning-compatible version string. Incompatible changes to the format will result in a major version bump. -
required- a dictionary of fields that the client considers critical enough that the signing server should reject a signing request if any of these fields are not understood. This serves the same purpose as the “critical bit” in OpenPGP and X.509.
optional- a dictionary of fields that are optional for the server to understand. Their presence may cause the signing server to act differently. If any field in this category is not understood by the server it must ignore it.
Required category
This specification defines two fields in the Required category:
-
input- the input to the signing process. It is an object with two fields:type- type of the input content. The following values are defined:sha2-0.11-SHA512-state: Represents a hasher state, as expected by thesha2crate. The format is stable across minor versions. Note that due to internal OpenPGP hashing mechanics this is not a digest of the data (e.g. a file) being signed.
content- the actual bytes of the input content.
-
output- the output of the signing process as expected by the client. This is an object with the following fields:type- the type of the signature expected by the client. The following values are defined:OpenPGPv4: OpenPGP v4 signatures
Optional category
There are no defined fields in this category.
Specification evolution
The specification can be extended by adding new fields or allowed values in minor version changes. If fields are removed, this constitutes a major version change to the specification. Old values are supported indefinitely (with the exception of security related changes). This allows older clients to keep using the same API and only upgrade when they want to take advantage of additional features.
If the client sends a request that is not spec-compliant (e.g. a required field has not been provided) the server MUST reject the request.
The following is a non-exhaustive list of non-compliant behavior:
- providing more or other fields than
version,requiredandoptional, - providing more fields than
inputandoutputinrequireddictionary, - providing other values than the ones listed in the Required section.
Any changes in the optional category are allowed, since this is explicitly an open-ended section.
Deprecation of values is handled in the same way: the old value is supported and the accepting party (i.e. the server) must transparently translate old, deprecated values to the new format.
Each change in the protocol must be documented in this specification.
Refer to the design document for an in-depth discussion of the technical details.
Signing Response specification
A signing response format for Signstar systems.
Glossary
- Client: The application (here:
signstar-request-signature) which computes a file digest and creates a signing request, - Server: The application (currently:
nethsm-cli) which receives a signing request, processes it and returns a signature, - Signing response: Machine and human-readable description of the data signature. The exact format for the signing response is described below.
- Signature: Raw cryptographic signature in a technology-specific framing (e.g. in “packets” for OpenPGP).
Format specification
The signing response is a custom JSON encoded data format.
The below sample signing response is fully expanded for illustrative purposes:
{
"version": "1.0.0",
"signature": "-----BEGIN PGP SIGNATURE-----\n\nwnUEABYKAB0WIQRfsO5nGa+i+6sgdPRTD9XY/kzu2QUCZ8WtVgAKCRBTD9XY/kzu\n2XCMAQCjsYYJ00u9wUE0O1CO8OOEi/4sXq1cDol6jhYep4awgwD8DtXJ/nCnSmRE\npSgPblgptDYtLdzJvvxd7G9kCSNS4AM=\n=XHyn\n-----END PGP SIGNATURE-----"
}
The fields are as follows:
-
version- Semantic Versioning-compatible version string. Incompatible changes to the format will result in a major version bump. -
signature- raw signature represented as a string. The signature must be ASCII-armored.
Specification evolution
The specification can be extended by adding new fields or allowed values in minor version changes. If fields are removed, this constitutes a major version change to the specification. Old values are supported indefinitely (with the exception of security related changes). This allows older clients to keep using the same API and only upgrade when they want to take advantage of additional features.
If the server sends a response that is not spec-compliant (e.g. a version field has not been provided) the client MUST reject the response.
Deprecation of values is handled in the same way: the old value is supported and the accepting party (i.e. the server) must transparently translate old, deprecated values to the new format.
Each change in the protocol must be documented in this specification.
Refer to the design document for an in-depth discussion of the technical details.
Signstar Sign
Offers an executable for processing Signstar signing requests.
Requests are created using signstar-request-signature and specify everything that is needed for creating an artifact signature.
The signstar-sign executable provided by this crate returns a Signstar signing response which contains a signature in protocol-specific framing.
Currently, only OpenPGP signatures are supported. However, both request and response format are designed with extensibility in mind and other technologies can be integrated in the future.
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_sign/ for development version of the crate
- https://docs.rs/signstar_sign/latest/signstar_sign/ for released versions of the crate
signstar-sign
The following command takes a signing request, encoded in JSON, and produces a JSON response.
The JSON response contains a signature field, which is an armored OpenPGP signature.
signstar-sign < ../signstar-request-signature/tests/sample-request.json | jq --raw-output .signature | rsop dearmor | rpacket dump
Features
_containerized-integration-test: Integration tests that require a containerized test environment. NOTE: Unless you are developing this crate, you will very likely not want to use this feature._yubihsm2-mockhsm: Test environment and integration using a virtual YubiHSM2 (implies theyubihsm2feature). NOTE: Unless you are developing this crate, you will very likely not want to use this feature.yubihsm2: For YubiHSM2 support.
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.2.0] - 2026-09-29
Added
- Add
NetHsmUserMapping::CertificateRetrieval - Add
YubiHsm2UserMapping::CertificateRetrieval - [breaking] Support sets of filters with
Config::user_backend_connections - [breaking] Extend
CryptographicKeyContext::OpenPgpwithnotations - Append Signer’s UID to issued signatures
- Port to new
Configformat - (cargo) Rename
mockhsmfeature to_yubihsm2-mockhsm - (cargo) Rename
test-helpersfeature to_test-helpers - Use
yubihsm::device::SerialNumberinYubiHsmConnection - Add support for YubiHSM signing to
signstar-sign - Move OpenPGP related logic out of
nethsmintosignstar-crypto - [breaking] Use
UserWithPassphraseinstead ofFullCredentials - Add
BackendConnection::YubiHsm2for YubiHSM2 backend connections - [breaking] Use
signstar_crypto::key::SigningKeySetupinUserMapping - [breaking] Support using different HSM backends in
SignstarConfig
Fixed
- [breaking] Diversify the logger setup for journald and terminal
- Update test vectors for sha2 hasher state
- Use debug builds with all features for testing
signstar-yubihsm2
Other
- Add documentation for NetHSM test routes
- Rely on coverage collection functionality in change-user-run
- Remove warning for
_yubihsm2-mockhsmfeature debug requirement - Enforce binary ID for
_containerized-integration-testtests - [breaking] Expose all signstar_crypto errors over top-level Error type
- Add writing of authorized keys files to
SystemUserConfig::apply - [breaking] Use
AuthorizedKeyEntry/SystemUserIdviaconfigmodule - [breaking] Add all available config fixtures to
ConfigFileVariant - Imply
yubihsm2withmockhsmfeature and guard tests - (README) Improve information about available features
- Use base64ct instead of base64 in
signstar-sign - (cargo) Move crate
clap-verbosity-flagto workspace dependencies - (cargo) Move crate
tempfileto workspace dependencies - [breaking] Change name to
UserMapping::SystemYubiHsm2OperatorSigning
[0.1.1] - 2025-08-19
Added
- Use
change-user-runcrate instead ofsignstar_config::test - Add
SignstarConfigfor the configuration on Signstar hosts - Enable generation of shell completions and man pages for
signstar-sign - Add logging with verbosity to
signstar-sign
Other
- Collect coverage produced by tests run (partially) as other user
[0.1.0] - 2025-07-10
Added
- Log errors if the command returned with a non-zero status
- [breaking] Fold
signstar-testintosignstar-configas a separatetestmodule - Add
signstar-sign
Fixed
- Update
rcgencode for new API - (deps) Update Rust crate which to v8
Other
- (deps) Update Rust crate rcgen to 0.14.0
- Reformat all TOML files with
taplo - Remove use of deprecated
tempfile::TempDir::into_path
Signstar YubiHSM2
Integration for YubiHSM2 devices as Signstar backend.
This library contains a small, safe subset of primitives used by Signstar. For example: YubiHSM2 allows exporting raw secret keys while in Signstar we would always export them under wrap (encrypted). Additionally, this library provides strong types for expressing object capabilities and identity.
To ease deployment and provisioning of a YubiHSM2 backend a command-line interface to execute deployment scenarios is included.
Documentation
- https://signstar.archlinux.page/rustdoc/signstar_yubihsm2/ for development version of the crate
- https://docs.rs/signstar_yubihsm2/latest/signstar_yubihsm2/ for released versions of the crate
Examples
CLI
Running scenarios
Reset the device to factory settings (erasing all key material) and get the HSM info:
echo $PWD
signstar-yubihsm scenario run "tests/scenarios/reset.json" | jq --compact-output
Adding new authentication key:
signstar-yubihsm scenario run "tests/scenarios/add-auth.json" | jq --compact-output
Generating key:
signstar-yubihsm scenario run "tests/scenarios/gen-key.json" | jq --compact-output
Signing using ed25519 keys:
signstar-yubihsm scenario run "tests/scenarios/raw-sign.json" | jq --compact-output
Import and export opaque data:
signstar-yubihsm scenario run "tests/scenarios/opaque-data.json" | jq --compact-output
Exporting key under wrap:
signstar-yubihsm scenario run "tests/scenarios/wrapping/export-wrapped.json" | jq --compact-output
Import previously wrapped key and using it for signing:
signstar-yubihsm scenario run "tests/scenarios/wrapping/import-wrapped.json" | jq --compact-output
Enable forced auditing of signing and retrieving log:
signstar-yubihsm scenario run "tests/scenarios/audit.json" | jq --compact-output
Backup
Decrypt and print details about several types of objects:
signstar-yubihsm backup dump "tests/backup/auth.yhw" "$wrap_key"
signstar-yubihsm backup dump "tests/backup/opaque.yhw" "$wrap_key"
signstar-yubihsm backup dump "tests/backup/private-ed25519.yhw" "$wrap_key"
signstar-yubihsm backup dump "tests/backup/private-ed25519-seed.yhw" "$wrap_key"
Wrapping ed25519 keys
Wrap an ed25519 private key file while setting its capabilities, domains, label and object identifier for a target device:
signstar-yubihsm backup wrap-ed25519 --capabilities sign-eddsa,export-wrapped --domains 1,2 --label test --id 3 "$private_key" "$wrap_key" --output "$wrapped_key"
Features
_yubihsm2-mockhsm: Test environment and integration using a virtual YubiHSM2. NOTE: Unless you are developing this crate, you will very likely not want to use this feature.cli: Enables command line interface for executing scenario filesserde: Serialization and deserialization of objects usingserde.
Contributing
Please refer to the contributing guidelines to learn how to contribute to this project.
License
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.1.0] - 2026-09-28
Added
- (deps) Update rustcrypto crates
- (deps) Upgrade crate
yubihsm2to 0.45.0 - Add labels to generated YubiHSM objects
- Add
Label::from_truncated_strto createLabelfromstr - Implement creating
ConnectorfromConnection - Implement
BackendCheckfor YubiHSM2Connection - [breaking] Extend
CryptographicKeyContext::OpenPgpwithnotations - Derive
strum::DisplayforObjectAlgorithm - Serialize
AsymmetricAlgorithmusingkebab-case - Derive
strum::DisplayforWrapKeyKind - Derive
strum::DisplayforOpaqueDataAlgorithm - Add
YubiHsm2SigningKey::close_session - Add
YubiHsm2SigningKey::new - Publicly re-export
yubihsm::Client - Implement
DisplayforObjectId - Add support for changing the currently used authentication key
- (deps) Upgrade crate
yubihsm2to 0.44.0 - Implement
Labelfromyubihsm::object::Label - Implement comparison, ordering and hashing for
Label - Add
ObjectAlgorithmto describe YubiHSM2 object algorithms - Add
AsymmetricAlgorithmto describe asymmetric key algorithms - Implement
WrapKeyKindfromyubihsm::wrap::Algorithm - Derive
HashforWrapKeyKind - Derive
PartialOrdandOrdforWrapKeyKind - Derive serde
DeserializeandSerializeforWrapAlgorithm - Derive
PartialOrdandOrdforWrapAlgorithm - Implement
Vec<Vec<CommandReturnValue>>fromScenarioReturnValue - Add support for retrieving opaque data from a YubiHSM2 to
Command - Add support for putting opaque data into the YubiHSM2 to
Command - Implement creating
yubihsm::object::LabelfromLabel - Add serde
Deserialize/Serializesupport forLabel - Fail on
\0characters inLabel::from_str - Publicly re-export
yubihsm::object::Info - Publicly re-export
yubihsm::Connector - Implement
AsRef<BTreeSet<Domain>>forDomains - Implement
AsRef<BTreeSet<Capability>>forCapabilities - Add
Credentials::idto return theId - Add constructor for
Scenario - Implement the listing of YubiHSM2 objects based on filter sets
- [breaking] Hide all file-backed types behind the “cli” feature
- Expose
CommandNamepublicly - Add
ScenarioReturnValue::chainsreturning return values - Add
YubiHsmWrapKeyFromWrapKeyto createyubihsm::wrap::Key - Add
WrapKeyFromPassphrasecreatingWrapKeyfromPassphrase - Add
WrapKeyas strong-typed abstraction foryubihsm::wrap::Key - Add
WrapKeyKindmapping toyubihsm::wrap::Algorithm - Derive
CloneandCopyforObjectId - Derive
CloneforKeyInfo - [breaking] Use file-backed authentication only for the CLI
- Add
AuthenticationKey, wrappingyubihsm::authentication::Key - Add
FileBackedCredentialsfor credentials based on file contents - Implement creating
Capabilitiesfromyubihsm::Capability - Implement
DisplayforCapabilities - Implement
DisplayforDomains - Derive strum’s
AsRefStrandDisplayforDomain - Add all (upstream) available variants for the
Capabilityenum - Derive
strum::AsRefStrforCapabilityand serialize correctly - Derive
HashforDomains - Derive
PartialOrdandOrdforDomains - Derive
HashforCapabilities - Derive
PartialOrdandOrdforCapabilities - Derive
PartialOrdandOrdforCapability - Add
wrap-ed25519subcommand tosignstar-yubihsm2 backup - Add support for clap for
Domainobjects - Rely on
Domains::bitsinDomains::to_be_bytes - Add
Domains::bitsto return the underlying bits value - Re-export
yubihsm::capability::Capability - Re-export
yubihsm::command::Code - Add
backup dumpsubcommand tosignstar-yubihsm2 - Add
backupmodule tosignstar-yubihsm2 - [breaking] Allow specifying multiple domains per key object
- Add
Connectionto describe the connection to a YubiHSM2 - (cargo) Remove
serdefrom default features - (cargo) Rename
mockhsmfeature to_yubihsm2-mockhsm - Use
Idinsignstar-yubihsm2::Credentials - Add
Idas a low-level YubiHSM2 Object ID representation - Implement
DisplayforDomain - Derive
HashforDomain - Derive
PartialOrdandOrdforDomain - Derive
EqforDomain - Add YubiHSM provisioning CLI
- Use
yubihsm::device::SerialNumberinYubiHsmConnection - Re-export
yubihsm::Domainandyubihsm::device::SerialNumber - Add
signstar_yubihsm2::signerto sign with YubiHSM2 keys - Add optional
serdesupport forCredentials - Add
Credentialsfor handling YubiHSM2 authentication - Add bare
signstar-yubihsm2crate
Fixed
- [breaking] Diversify the logger setup for journald and terminal
- (deps) Update Rust crate ccm to 0.6.0
- (deps) Update Rust crate argon2 to 0.6.0
- [breaking] Correctly generate certificates with multiple User IDs
- Use
WrapKeyfor wrapping keys, notAuthenticationKey - Don’t require mutability in
Fromimplementations forDomains - Fix clippy lints reported by Rust stable
- (cargo) Only require
clap-verbosity-flagforclifeature - Properly separate
mockhsmandserdefeatures - Move time import to not cause issues with
mockhsmdisabled - Use debug builds with all features for testing
signstar-yubihsm2
Other
- Remove warning for
_yubihsm2-mockhsmfeature debug requirement - [breaking] Remove unused types
LogDigestandLOG_DIGEST_SIZE - [breaking] Replace signstar-yubihsm2’s
Idwithyubihsm::object::Id - [breaking] Switch from yubihsm.rs to yubihsm2
- Convert
allows intoexpects - Improve
ScenarioReturnValue::persist_file_backed_scenario - Improve
ScenarioReturnValue::compare_with_file_backed_scenario - Expand the docs for
FileBackedScenarioReturnValueMismatch - [breaking] Separate writing to output file from default library functions
- [breaking] Rename
Commandvariants to matchyubishmfunctions - [breaking] Make serialization of command output optional
- (cargo) Move crate
yubihsmto workspace dependencies - Use
AuthenticationKeyinstead of custom function - [breaking] Replace
AuthwithFileBackedCredentials - [breaking] Restructure
Scenarioas chain of authenticated commands - Format all JSON files using
biome - Use top-level use statements for external types
- [breaking] Expose all signstar_crypto errors over top-level Error type
- [breaking] Standardize on snake_case for scenario object representation
- Move module files to mod.rs files
- Sort
From/TryFromimplementations forDomains - [breaking] Adhere to upstream naming convention in
Capabilityvariants - Use
BTreeSetinstead ofHashSetinCapabilities - Use
BTreeSetinstead ofHashSetforDomains - Derive
EqandPartialEqfor object identifiers - [breaking] Introduce
Labeltype and use that inInnerFormat - [breaking] Use dedicated
Capabilitiestype inInnerFormat - Add documentation describing YubiHSM Wrap (YHW) format
- (README) Improve information about available features
- [breaking] Rely on
IdinAuthandCommandfor increased robustness - [breaking] Use
IdinKeyInfoto increase robustness - [breaking] Rely on
IdinObjectIdfor increased robustness - (deps) Move
serde_jsoncrate to workspace dependencies - (deps) Move
clap-verbosity-flagto workspace dependencies - (deps) Update Rust crate pgp to 0.19
Design documentation
This document outlines the design of the Signstar project in more detail.
NOTE: Until a test system (%6) is deployed, this document will likely undergo further revisions.
See previous setup for details on the setup upon which Signstar improves.
In evaluated setups all high-level setups which have been evaluated are listed. Only one of them (option C) is considered for implementation.
Setup
Signstar as a signing service is meant to be run as a set of middleware applications on a physical host, fronting a Nitrokey NetHSM.
The signing service allows for assigned users to request signatures, metrics and backups. For system modification related tasks, dedicated credentials are used to provide shares of a shared secret for the (re)configuration of the HSM and backups files to restore from.
Hardware Security Module
The NetHSM offers users of different roles in segregated namespaces:
- Administrator (R-Administrator for system-wide and N-Administrator for per-namespace Administrator): for creating, removing and modifying unprivileged users (e.g. Operator, Backup, Metrics), creating, removing and modifying of keys and all other system-level privileged actions
- Operator: for cryptographic operations using accessible keys (may exist system-wide or in a namespace)
- Metrics: for pulling metrics of the HSM (may only exist system-wide)
- Backup: for downloading encrypted backups of the HSM, containing device settings, user setups and keys (may only exist system-wide)
The Administrator credentials are needed for initial setup of the appliance, as well as any ongoing maintenance tasks that involve adding, removing or modifying users or keys. Each Operator account is assigned exactly one private signing key of the HSM (using the tags system of the NetHSM).
The user setup is explained in more detail in the configuration section.
Signing service
The signing service host is a physical machine running the custom image-based operating system Signstar OS (based on Arch Linux), running from a read-only root filesystem (with verity signing support) and an encrypted /var partition for state, such as SSH host keys and per-user configuration.
Its central configuration and user setup is provided through OS updates.
Per-user configuration files for the integration with the NetHSM are created and changed in a persistent location by a dedicated tool, once administrative credentials have been passed to the host. Administrative credentials for the HSM are provided by designated individuals as shares of a secret divided using Shamir’s Secret Sharing (SSS) algorithm.
Authentication towards the signing service host is provided over SSH for credentials mapped to user roles on the HSM, that allow for unprivileged operations (i.e. Operator, Metrics and Backup users), for special system users that allow providing specific system-altering configuration (e.g. WireGuard configuration items, backup files, or shares of the administrative credentials) or for downloading purposes (e.g. key certificates, or shares of administrative credentials).
Dedicated software components provide access for authenticated users to these different functionalities:
signstar-download-signature: for signing messages using HSM Operator credentialssignstar-download-backup: for receiving backups of the HSM using HSM Backup credentialssignstar-download-key-certificates: for downloading the certificates (e.g. OpenPGP certificates) of all keyssignstar-download-metrics: for retrieving metrics of the device using HSM Metrics credentialssignstar-download-secret-share: for downloading (new) individual shares of a secret (containing administrative credentials) divided using SSSsignstar-download-wireguard: for downloading the public key of the WireGuard setup used for diverting logs and metrics to a dedicated hostsignstar-upload-backup: for uploading a backup file, to be used in a system restore actionsignstar-upload-secret-share: for providing HSM administrative credentials as shares of a secret divided using SSSsignstar-upload-update: for uploading NetHSM firmware updates
Additionally, the signstar-configure executable is used to (re)configure the HSM and per-user configuration files based on central configuration and provided administrative credentials.
Signing service clients
All signing service clients should rely on tamper-proof hardware (e.g. TPM2, or hardware token) to guard dedicated credentials for connecting to the signing service.
Networking
The Signstar setup is run on a dedicated host, that on one hand is exposed to the network of its clients, and on the other to the network of the NethHSM.
It is advisable to not expose the NetHSM to the network beyond that of the host running Signstar OS.
---
title: Simplified network setup
---
sequenceDiagram
participant C as Client
participant S as Signstar
participant N as NetHSM
Note over S: Public network range
Note over N: Private network range
C ->> S: accepted
S ->> N: accepted
C --x N: denied
Authentication
Clients authenticate against the signing service host as individual Unix users over SSH and each user is assigned an executable as dedicated component for their use-case. Credentials to the HSM are never directly exposed to the clients of the signing service host.
---
title: User relation of Signstar and HSM credentials
---
sequenceDiagram
participant C as Client
participant S as Signstar
participant N as NetHSM
Note over S: pair of Signstar credentials
Note over N: pair of NetHSM credentials
C ->> S: User "A" requests operation
S ->> S: Middleware user "A" is mapped to HSM Operator user "X"
S ->> N: Operation is requested using Operator user "X"
N ->> S: Result of operation is returned
S ->> C: Result of operation for user "A" is returned
The following executables rely on a per-user configuration, that provides a one-to-one mapping of signing service host credentials to HSM Operator, Backup and Metrics credentials.
Match user signer
ForceCommand /usr/bin/signstar-download-signature
Match user backup
ForceCommand /usr/bin/signstar-download-backup
Match user metrics
ForceCommand /usr/bin/signstar-download-metrics
Match user certificates
ForceCommand /usr/bin/signstar-download-key-certificates
The following executables do not rely on a NetHSM user mapping, as they are only used to provide data to the system or download data generated by the system, but not to interact with the HSM.
Match user backup-upload
ForceCommand /usr/bin/signstar-upload-backup
Match user secret-share-download
ForceCommand /usr/bin/signstar-download-secret-share
Match user secret-share-upload
ForceCommand /usr/bin/signstar-upload-secret-share
Match user update-upload
ForceCommand /usr/bin/signstar-upload-update
Match user wireguard-download
ForceCommand /usr/bin/signstar-download-wireguard
Logs and metrics
Syslogs of NetHSM devices are collected on the signing service host. Both the HSM syslogs and the host’s own systemd-journald are consumed by Grafana Alloy and sent to a dedicated Grafana Loki logging host over a WireGuard tunnel.
---
title: Simplified syslog setup
---
sequenceDiagram
participant N as NetHSM
participant S as Signstar OS
participant L as Loki
loop logs
loop NetHSM syslog
N ->> S: syslog to syslog-ng
S ->> S: syslog-ng to Grafana Alloy
end
loop Signstar OS journal
S ->> S: journal to Grafana Alloy
end
S -->> L: Grafana Alloy send<br>(WireGuard)
end
Metrics for the NetHSM are collected using a custom Prometheus metrics exporter which relies on Metrics user credentials of the HSM for device metrics and a set of dedicated Operator user credentials for per-key metrics. Metrics for the signing service host itself are collected separately. All metrics are scraped using Prometheus over a WireGuard tunnel.
---
title: Simplified metrics setup
---
sequenceDiagram
participant N as NetHSM
participant S as Signstar OS
participant M as Prometheus
loop metrics
loop NetHSM metrics exporter
S ->> N: request
N ->> S: return
end
loop Signstar OS metrics
S ->> S: Export host metrics
end
S -->> M: scrape metrics<br>(WireGuard)
end
Configuration
The configuration of the signing service host is changed by upgrading its read-only operating system Signstar OS. The basic configuration of the NetHSM (i.e. its available users and keys) is set and altered using a central, versioned configuration file on the signing service host, which does not include any passphrases.
Passphrases for common (unprivileged) operations towards the NetHSM are kept in per-user configuration files and can only be created when (re)configuring the HSM using administrative credentials.
The administrative credentials for the NetHSM (for (re)configuration actions) are provided to the signing service host by designated shareholders of a shared secret, divided using Shamir’s Secret Sharing.
The shared secret contains the unlock and backup passphrases and all Administrator credentials of the NetHSM. If one of these is changed or a new one is added, the shared secret is recreated and shareholders must download their shares anew.
Configuration actions towards the NetHSM relate to users in the Administrator role: System-wide (R-Administrator - R) or per namespaces (N-Administrator - N).
NOTE: To segregate groups of users and their access to keys, namespaces for each purpose are created (e.g. “release artifact signing”, “repository sync database signing”, “package signing”).
- provision (R)
- configure TLS setup (R)
- reboot system (R)
- shut down system (R)
- factory reset system (R)
- restore from backup (R)
- set network configuration (R)
- set logging configuration (R)
- set boot mode (R)
- set backup passphrase (R)
- set unlock passphrase (R)
- create Administrator user (R)
- remove Administrator user (R)
- create Backup user (R)
- remove Backup user (R)
- create Metrics user (R)
- remove Metrics user (R)
- create Administrator user in namespace (R)
- create namespace (R)
- delete namespace (R)
- create Operator user in namespace (N)
- remove Operator user in namespace (N)
- set passphrase for Operator user in namespace (N)
- generate key in namespace (N)
- remove key in namespace (N)
- tag key in namespace (N)
- untag key in namespace (N)
- tag user in namespace (N)
- untag user in namespace (N)
When it comes to configuration, one can distinguish between privileges required for initial and those required for ongoing administrative operations.
Initial
An initial configuration encompasses device, user and key configuration.
- provision (R)
- configure TLS setup (R)
- reboot system (R)
- shut down system (R)
- factory reset system (R)
- restore from backup (R)
- set network configuration (R)
- set logging configuration (R)
- set boot mode (R)
- set backup passphrase (R)
- set unlock passphrase (R)
- create N-Administrator user in namespace (R)
- create namespace (R)
- create Operator user in namespace (N)
- generate key in namespace (N)
- tag key in namespace (N)
- tag user in namespace (N)
Ongoing system maintenance
When the device-level configuration changes, the system-wide Administrator credentials are required.
- configure TLS setup (R)
- reboot system (R)
- shut down system (R)
- factory reset system (R)
- restore from backup (R)
- set network configuration (R)
- set logging configuration (R)
- set boot mode (R)
- set backup passphrase (R)
- create system-wide Operator user (R)
- create system-wide Backup user (R)
- create system-wide Metrics user (R)
- create N-Administrator user in namespace (R)
- create namespace (R)
- delete namespace (R)
Ongoing key and user maintenance
Ongoing key and user configuration entails the addition, removal and modification of existing users and keys.
- create Operator user in namespace (N)
- remove Operator user in namespace (N)
- set passphrase for Operator user in namespace (N)
- generate key in namespace (N)
- remove key in namespace (N)
- tag key in namespace (N)
- untag key in namespace (N)
- tag user in namespace (N)
- untag user in namespace (N)
Deployment
The signing service host is instantiated by deploying an installation image of Signstar OS onto a host with UEFI support in “setup mode”.
During first boot, relevant partitions and encrypted credentials are created.
After first boot dedicated system credentials are used to download the public key for a WireGuard tunnel to a host used for logging and metrics aggregation.
If the attached NetHSM is unprovisioned, administrative credentials are generated by the signing service host and made available as shares of a shared secret, divided using Shamir’s Secret Sharing. After successful download of all shares, the system provisions the NetHSM, as well as dedicated system user configurations and removes the administrative credentials.
After successful initial configuration, dedicated credentials are used to download an encrypted backup of the NetHSM.
---
title: Initial deployment
---
sequenceDiagram
actor C as Admin
actor H as n shareholders
participant S as Signing service host
participant N as NetHSM
participant L as Logging server
Note over S: Signstar OS
C ->> S: Signstar OS installation image
S --> S: Install and first boot
S ->> C: Download WireGuard public key
C --> L: Configure WireGuard tunnel
S ->> L: WireGuard tunnel
S ->> N: Unprovisioned?
N ->> S: Yes!
S --> S: NetHSM administrative credentials (SSS)
S ->> H: Share of administrative credentials
S ->> N: Configure
S --> S: Delete administrative credentials
Upgrading
The signing service host is upgraded by providing relevant update artifacts on a specific webserver. Signstar OS upgrades autonomously with the help of systemd-sysupdate.
Upgrades to the NetHSM are uploaded to the signing service host using designated credentials, which make use of signstar-upload-update. Once sufficient shares of the shared administrative credentials are uploaded after that, the NetHSM is updated using signstar-configure.
Restoring
The signing service host is bound to its particular hardware. If the hardware fails, a new physical host must be connected, which has to undergo initial deployment.
To restore a NetHSM from backup, a backup file is uploaded using designated credentials, which make use of signstar-upload-backup. Once sufficient shares of the shared administrative credentials used for creating the backup are uploaded after that, the NetHSM is restored using signstar-configure.
Signing
The central feature of the signing service is to allow (otherwise unprivileged) users on that host to request cryptographic signatures backed by an HSM.
---
title: High-level Signstar signing setup
---
sequenceDiagram
participant C as Client
participant S as Signstar
participant N as NetHSM
Note over S: pair of Signstar credentials
Note over N: pair of NetHSM credentials
C ->> S: request a signature
S ->> N: request a raw cryptographic signature
N ->> N: create a raw cryptographic signature
N ->> S: return a raw cryptographic signature
S ->> S: add protocol-specific framing to the raw cryptographic signature
S ->> C: return signature
Signature request payload
Clients may request a signature for very large artifacts, which leads to congestion on the signing service host (as observed in other signing solutions such as sigul). To prevent this from happening signstar will employ a custom machine-readable payload format (tracked in #41), that contains required metadata and pre-hashed data for a signature.
Making use of this custom payload comes at the downside, that the signing service never directly interacts with the specific artifact to sign and solely relies on provided metadata (which may be fraudulent).
When transmitting actual artifacts to sign, the signing service would be able to evaluate and/ or validate an artifact. However, this comes at the downside of having to employ custom parsers and validation tools, which may slow down the signing process significantly and also increase the attack surface of the setup.
Attestation log
An attestation log is a tamper-resistent and append-only data structure to which the resulting raw cryptographic signatures from signing operations are appended to. Additional metadata in an entry should provide insight into caller of the signing operation, the alleged payload and the certificate or fingerprint of the issuer.
Writing to an attestion log happens exclusively on the signing service host in an environment only exposing signing capabilities to authenticated users.
Work on attestation log integration is tracked in #43.
Threshold signatures
To further manifest and strengthen the Arch Linux reproducible builds effort, it is possible to build a system in the future in which only reproducible artifacts are eligible for a signature.
In such a system, m build machines create the artifact in question and each request a signature from the Signstar system.
Only if a threshold of n (where n <= m) identical artifacts is met, a signature is created.
OpenPGP
Private keys stored on the HSM may be used for OpenPGP signatures.
For this a dedicated certificate with a User ID specific to a single host is created (e.g. Build Host 1 <buildhost1@archlinux.org>).
When used in a packaging context, an OpenPGP certificate should not be created with an expiration time, as its validity is determined by a PGPKI (aka. Web of Trust, as provided by archlinux-keyring).
Single component key certificates are preferred in a packaging context, because a signing key is not used for authentication, encryption or third-party certification.
Moreover, certificates with multiple component keys also require combining and using multiple private keys on the HSM, which is currently not yet supported (see #50).
When creating OpenPGP signatures, the signing service may append the transaction metadata (e.g. file metadata, requesting client) as notations in the hashed area of the signature. In the future the position of the signature in an attestation log, may additionally be appended in the unhashed area of the signature.
OpenPGP signatures consist of OpenPGP packets (see packet structure of data signatures), that contain raw cryptographic signatures (e.g. those created by an HSM). As such, Signstar is responsible for issuing cryptographic signatures in the correct “framing”.
---
title: OpenPGP signature creation with attestation log
---
sequenceDiagram
participant C as n clients
participant S as Signstar
participant A as attestation log
participant N as NetHSM
Note over C: one set of signstar credentials each
Note over S: n NetHSM Operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings
critical authentication
critical
option
C ->> C: create checksum, gather metadata
C ->>+ S: authenticate,<br/>transmit hash and metadata
option
C ->> C: gather metadata
C ->>+ S: authenticate,<br/>transmit file and metadata
end
S -->> S: log user access and request to syslog
option login failure
S -->> S: log user login failure to syslog
S -x C: failure
option login successful
S -->> S: log user login success to syslog
critical user mapping
S ->> S: map client user to NetHSM user and certificate ID
option mapping not found
S -->> S: log user mapping failure to syslog
S -x C: failure
option mapping found
S -->> S: log user mapping success to syslog
end
end
critical data preparation
S ->> S: check request
option request is file
S ->> S: create checksum for file
end
critical raw cryptographic signing
S ->>+ N: authenticate for client,<br/>transmit checksum and cert ID
option authentication fails/ raw cryptographic signature not created
S -->> S: log signature failure to syslog
S -x C: failure
option authentication succeeds/ raw cryptographic signature created
N ->>- S: receive raw cryptographic signature
S -->> S: log signature success to syslog
critical appending to attestation log
S ->> A: send raw cryptographic signature and metadata
option authentication fails/ attestation log not appended to
S -->> S: log attestation log append failure to syslog
option authentication succeeds/ attestation log appended to
A ->> S: receive slot number
S -->> S: log attestation log append success to syslog
end
critical OpenPGP signature creation
S ->> S: create OpenPGP signature with notation for attestation log slot in unhashed area
option creation of OpenPGP signature fails
S -->> S: log failure to create OpenPGP signature to syslog
option creation of OpenPGP signature succeeds
S -->> S: log successful creation of OpenPGP signature to syslog
end
S -->> S: log return of OpenPGP signature to syslog
S -->>- C: receive OpenPGP signature
end
Packages and repository sync databases
By unifying the surface for how clients interact with the signing service, it is possible to improve over the previous setup for packages and repository sync databases by requesting signatures transparently from centrally managed build and repository server hosts.
Here the signature issuing takes place as outlined in the section on OpenPGP signing.
---
title: Signing service signing files or hashes
---
sequenceDiagram
participant B as build server
participant S as Signstar
participant R as repo server
Note over B: one set of Signstar credentials
Note over R: one set of Signstar credentials
critical
B ->>+ B: get sources,<br/>build package(s)
loop get signature for each package
B ->>+ S: request signature
S ->>- B: receive OpenPGP signature
end
B ->>- B: move package file and OpenPGP signature to publicly accessible storage
end
critical repository update
R ->>+ R: order to add package(s) from build server to repo
B ->> R: download package(s) and OpenPGP signature(s) to pool
R ->> R: generate temporary sync databases
loop get signature for each sync database
R ->>+ S: request signature
S ->>- R: receive OpenPGP signature
end
R ->>- R: update repository
end
Secure Boot Shim
A signed shim can be created by first (reproducibly) building the shim package and afterwards signing the resulting EFI binary. This requires X.509 support, which is tracked in #51.
---
title: Signing service signing shim
---
sequenceDiagram
participant B as client
participant S as Signstar
participant N as NetHSM
Note over B: one set of Signstar credentials
Note over S: n NetHSM Operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings
critical
B ->> B: install shim package
critical
B ->> B: gather metadata
B ->> S: authenticate,<br/>send EFI file and metadata
S ->> S: create checksum for file
end
S ->> N: authenticate for client,<br/>transmit digest and key ID
N -->> S: raw cryptographic signature
S ->> S: create signature
S -->> B: receive signature
B ->> B: move signed shim to output dir
end
Threat model
This documents orients itself around the threat modeling manifesto and threat modeling capabilities. It is designed as a living document, which is meant to be extended in the future.
While threat models can be designed using threat dragon, the authors chose to rely on a more basic approach based on threat categories and response and mitigation categories.
Description
An Arch Linux image-based operating system, running on a dedicated host, that is connected to an HSM device. The system issues signatures for incoming signing requests based on a dedicated protocol by forwarding the raw cryptographic signature task to the HSM. All authentication to the host is done using SSH. Updates to the image-based OS are built using a dedicated key for secure boot/verity signing and the resulting payloads are signed using a dedicated set of OpenPGP keys.
Assumptions
The following assumptions about the setup of the system provide a baseline for its operation:
- The physical security of the host is guaranteed by a non-malicious hoster.
- The hoster is provided with an HSM that they connect physically to the specific host.
- The HSM vendor is not malicious.
- Secure Boot on the host is available and the firmware provides it reliably.
- The host offers a TPM-2.0.
- Access to the firmware is guaranteed by KVM console.
Potential threats
- Threat (Elevation of Privileges): Attacker boots a malicious payload on the host.
Action (Mitigate): A passphrase is set for the host’s firmware and the boot is locked to a pre-defined payload.
Validation: Attempt to boot the machine into a default recovery system. - Threat (Information disclosure): Attacker has physical access to machine and creates a copy of the hard drive contents.
Action (Mitigate): All data used long-term that deserves protection is placed on a partition, encrypted using the TPM-2.0 of the host.
If an attacker copies the contents of the harddrive, they are not able to decrypt the partition without the TPM-2.0 of the host.
Validation: Boot the machine into a recovery system, copy the contents of the harddrive to an image and attempt to decrypt the encrypted data partition. - Threat (Tampering): Attacker provides a malicious upgrade to the image-based OS.
Action (Eliminate): Upgrades to the image-based OS are cryptographically signed. The operating system only considers an upgrade, if a valid signature is found alongside the upgrade payload in a pre-configured location.
Validation: Add an unsigned upgrade to the image-based OS in the pre-configured location and ensure that the upgrade is not considered. - Threat (Elevation of privileges): Attacker abuses a known vulnerability in a component used in the image-based operating system.
Action (Mitigate): The vulnerable component is fixed/upgraded and a new version of the image-based OS is released. Components unnecessary to the prime function of the system are removed from each image of the dedicated operating system to reduce the attack surface.
Validation: A rigirous CI setup ensures that no known vulnerabilities are present in the components developed in the context of the project and that those are buildable on a daily basis. A separate CI ensures that updates to the image-based OS can be built on a daily basis. - Threat (Tampering): Attacker manipulates the root filesystem of the host.
Action (Eliminate): The filesystems of the host are verified usingdm-verity(see e.g. veritysetup).
Validation: Boot into a recovery system, manipulate a file on the root filesystem and attempt to boot. Ensure, that the system fails to boot. - Threat (Tampering): Attacker manipulates the filesystems of a running system.
Action (Eliminate): All partitions, except the data partition, are mounted read-only. All partitions except the data partition usedm-verity.
Validation: A dedicated test application attempts to write to the read-only partitions during boot and leads the OS to fail its boot. - Threat (Repudiation): Attacker manipulates logs or metrics to cover their tracks.
Action (Mitigate): Logs and metrics are directly sent/forwarded to a central logging/metrics server. The system does not offer login shells.
Validation: A dedicated test application shuts down the logging service on the host, which can be observed on the loggine server. - Threat (Spoofing): Attacker impersonates a client to the system.
Action (Transfer): Clients are expected to guard their SSH private keys using a TPM-2.0.
Action (Mitigate): If an impersonation is detected, a new version of the image-based operating system is released, that disables access for the affected SSH public key.
Validation: Ensure that after an update disabling an SSH public key, that the client can no longer log in. - Threat (Denial of service): An attacker issues many failed attempts to authenticate and blocks other, legitimate clients from requesting a signature.
Action (Eliminate): The system relies entirely on SSH public key authentication and does not support password authentication or brute-force-prevention mechanisms such as fail2ban, which could lead to legitimate clients to the system getting blocked.
Validation: Launch many failed attempts to authenticate as a known client and ensure that the legitimate client can still authenticate using their SSH private key. - Threat (Denial of service): Attacker physically destroys the machine and HSM.
Action (Eliminate): The host runs on commodity hardware and encrypted backups are created for the HSM.
Validation: Wipe the harddrive of a system (in a multi-system setup) and set it up from scratch. Afterwards provide the encrypted backup to the system to restore the HSM from. - Threat (Information disclosure): Attacker copies the backup file for the HSM.
Action (Mitigate): The backup file for the HSM is always encrypted. The passphrase for decryption is distributed to several individuals using Shamir’s Secret Sharing.
Validation: Ensure that the encrypted backup works by restoring an existing system from it. - Threat (Information disclosure): Attacker exfiltrates operator-level authentication tokens for the HSM from the running system.
Action (Mitigate): The operator-level credentials for the HSM are kept in the encrypted partition and can only be used with the specific connected hardware device. Additionally, they are encrypted per system-user, using the TPM-2.0 of the host.
Validation: n/a - Threat (Information disclosure): Attacker exfiltrates administrator-level authentication tokens for the HSM from the running system.
Action (Accept): Shares of the administrator-level credentials for the HSM are briefly kept in a volatile filesystem during installation of the system or when restoring from backup and could be extracted during that timeframe.
In such a situation, the administrative credentials must be rotated and new shares of the shared secret be created using Shamir’s Secret Sharing.
Validation: n/a - Threat (Information disclosure): Attacker copies administrator-level authentication tokens for the HSM.
Action (Mitigate): The administrator-level credentials for the HSM are split using Shamir’s Secret Sharing and distributed among trusted individuals of the distribution. No individual has access to the complete administrator-level credentials.
Validation: Attempt to extract the administrator-level credentials from an insufficient amount of shares. - Threat (Elevation of privileges): Attacker uses the HSM to create a signature.
Action (Mitigate): A dedicated host with an image-based OS is connected to the HSM. It holds the operator-level credentials for the HSM and only briefly has access to the administrator-level credentials. Each HSM is configured with unique operator-level credentials. An HSM requires the specific host it was configured with to function.
Validation: Ensure that authentication against an HSM is not possible, after using it on another machine. - Threat (Information disclosure): Attacker exfiltrates administrator-level and/or operator-level authentication tokens for the HSM from the running system and steals the HSM.
Action (Accept): Shares of the administrator-level credentials for the HSM are briefly kept in a volatile filesystem during installation of the system and the operator-level credentials are kept in the encrypted data partition. If they are exfiltrated and the attacker also steals that specific HSM, the HSM can be used to issue signatures.
In such a situation, all affected private keys must be revoked, a public statement be made and afterwards an entirely new set of private keys be created.
Validation: n/a - Threat (Tampering): Attacker manipulates the firmware bootloader.
Action (Transfer): The firmware of the system host is locked by a passphrase, known to a dedicated set of people. Access to the firmware can only be granted by first requesting a KVM console from the hoster after passing two-factor authentication.
Validation: Ensure that ordering a KVM console is logged by the hoster. - Threat (Repudiation): Attacker manipulates logs or metrics on the wire.
Action (Mitigate): Both logs and metrics are transferred to a central logging server encrypted, through a VPN.
Validation: Ensure logs and metrics are not transmitted in the clear (e.g. with the help of wireshark). - Threat (Tampering): Attacker uses a compromised client to create a signature for a malicious payload.
Action (Accept): Requesting signatures for malicious payloads is possible. However, after the fact, the list of signatures requested since the client compromise can be correlated from the logs on the central logging server. The affected payloads need to be inspected and potentially re-created.
Validation: n/a - Threat (Spoofing): Attacker forges a signature.
Action (Transfer): The signing keys used by the HSM rely on strong cryptographic algorithms and only exist in the HSM or in encrypted backups. The certificate of each OpenPGP key is certified with at least three third-party certifications using PGPKI, through which a signature created using the key can be authenticated. Recipients of payload and signature need to verify the signature.
Validation: A foreign OpenPGP signature for a payload is attempted to be validated against a PKI and fails. - Threat (Elevation of Privilege): Attacker compromises credentials of a specific system user and uses it to perform an action only valid for another user.
Action (Mitigate): Each system user on the host is strongly tied to a specific role. An operator user can only ever created signatures using a specific key in the HSM, a metrics user can only ever create metrics for the system or a set of keys and a backup user can only ever download an encrypted backup.
Validation: Using the credentials of a backup system user, attempt to request a signature and ensure that the call fails. - Threat (Elevation of Privilege): Attacker gains full control over the operating system of the host and issues signatures.
Action (Accept): Shares of the administrator-level credentials for the HSM are briefly kept in a volatile filesystem during installation of the system or when restoring from backup and could be made use of during that timeframe. Operator-level credentials are available continuously in the encrypted data partition and could always be used. If an attacker gains full control over the system, they can create signatures using all private keys used in the HSM. In this case, the system should be shut down immediately and a new one needs to be setup from scratch. If administrator-level credentials have been exposed, this involves creating new keys and revoking all affected ones.
Validation: n/a - Threat (Elevation of Privilege): Attacker exports all keys from the HSM.
Action (Mitigate): Keys can only be exported from the HSM using administrative credentials, which are passed in to the host as a set of shares of a shared secret. The shares are maintained by a set of trusted individuals with dedicated authentication to the host. Beyond the brief time windows in which a sufficient amount of shares are available on the host in a volatile filesystem location, the operating system does not have access to the needed administrative credentials for exporting keys.
Validation: A dedicated test attempts to export keys from the HSM during boot and fails. - Threat (Tampering): Attacker uses a compromised client to encrypt data instead of signing it.
Action (Eliminate): Keys are bound to a specific scope that they are used in. This restriction is enforced in the HSM and on a protocol level (e.g. OpenPGP key usage flags). Additionally, the protocol for requesting a signature does not offer any other usage.
Validation: n/a - Threat (Elevation of Privilege): Attacker compromises a sufficient amount of shareholders by exfiltrating their shares and SSH authentication and exfiltrates an encrypted backup file or steals one of the HSMs.
Action (Accept): With a sufficient amount of shares, it is possible to decrypt the encrypted backup or to connect to the HSM and subsequently export all private key material.
In such a situation, all affected private keys must be revoked, a public statement be made and afterwards an entirely new set of private keys be created.
Validation: n/a - Threat (Elevation of Privilege): Attacker exfiltrates the key for secure boot/verity signing and steals the host and HSM.
Action (Accept): Each copy of the secure boot/verity signing key is supposed to be imported into a dedicated hardware token, so that it cannot be exfiltrated directly. With it, an attacker could build a valid image to boot on the host and create signatures, given operator-level credentials are present. If the key for secure boot/ verity signing operations is compromised, all affected hosts need to be shut down and reprovisioned using images that use a new key.
Validation: n/a - Threat (Elevation of Privilege): Attacker provides a malicious HSM by impersonating the manufacturer, or is a malicious manufacturer.
Action (Accept): Dedicated tooling interacts with the HSM in a reproducible way during normal operations. However, the HSM itself may act as a malicious entity, e.g. by turning into keyboard, or sending all created private keys to a remote entity without our knowledge.
Validation: n/a - Threat (Tamper): Attacker uses a known (or unknown) vulnerability in the firmware of the host to e.g. circumvent Secure Boot.
Action (Mitigate): With KVM console access to the host, its firmware can be upgraded.
Validation: n/a
Evaluated setups
This document contains evaluated, but not considered setup concepts for signstar.
Scenarios
In the below subsection all evaluated scenarios are listed. The following list provides a more high-level overview of the evaluated features.
| Feature | A | B | C | D | E | F |
|---|---|---|---|---|---|---|
| attestation log | ❌️ | ✅️ | ✅️ | ❌️ | ✅️ | ✅️ |
| central signing | ❌️ | ✅️ | ✅️ | ✅️ | ✅️ | ✅️ |
| low complexity | ✅️ | ✅️ | ✅️ | ✅️ | ❌️ | ❌️ |
| client crypto backend unaware 1 | ❌️ | ✅️ | ✅️ | ✅️ | ✅️ | ✅️ |
| no direct client access to hardware appliance | ❌️ | ✅️ | ✅️ | ✅️ | ✅️ | ✅️ |
| no direct build server access to repo server | ❌️ | ❌️ | ✅️ | ❌️ | ✅️ | ✅️ |
| no direct signing service access to repo server | ✅️ | ✅️ | ✅️ | ✅️ | ❌️ | ❌️ |
| no transmission of files from build server | ✅️ | ✅️ | ✔️ | ❌️ | ✔️ | ❌️ |
| no custom wire format | ✅️ | ❌️ | ❌️ | ❌️ | ❌️ | ❌️ |
| no workflow complexity offloaded to signing service | ✅️ | ✅️ | ✅️ | ✅️ | ❌️ | ❌️ |
HSM directly
The clients directly interact with the hardware appliance (there is no signing service).
---
title: HSM directly
---
sequenceDiagram
participant B as build server
participant N as NetHSM
participant R as repo server
participant L as logging server
participant M as metrics server
Note over B: 1 NetHSM operator credential,<br/>1 certificate ID,<br/>1 repo server credential
Note over B: PKCS11 based tooling for signing
Note over R: 1 NetHSM operator credential,<br/>1 certificate ID
Note over R: PKCS11 based tooling for signing
loop package build
B ->> B: get sources,<br/>build package(s),<br/>get signature,<br/>send to repo server
end
B-->>N: authenticate,<br/>transmit checksum and cert ID
N->>B: raw cryptographic signature
loop repository update
R ->> R: receive package(s),<br/>generate sync databases,<br/>get signature,<br/>update repository
end
B ->> R: package(s) and OpenPGP signature(s)
R -->> N: authenticate,<br/>transmit checksum and cert ID
N ->> R: raw cryptographic signature
loop metrics collection
N --> M: read
B --> M: read
R --> M: read
end
loop log aggregation
N -->> L: send via syslog
end
Signing service signing hashes
In this setup a microservice takes care of taking authenticated client requests and issuing signatures for the request via a PKCS#11 backend.
---
title: Signing service signing hashes
---
sequenceDiagram
participant B as build server
participant S as signing server
participant N as NetHSM
participant R as repo server
participant L as logging server
participant M as metrics server
Note over B: 1 signing server credential,<br/>1 repo server credential
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings
Note over S: PKCS11 based tooling for signing
Note over R: 1 signing server credential
loop package build
B ->> B: get sources,<br/>build package(s),<br/>create OpenPGP hash,<br/>get signature,<br/>send to repo server
end
B -->> S: authenticate,<br/>transmit hash and file metadata
S -->> N: authenticate for client,<br/>transmit hash and cert ID
N ->> S: raw cryptographic signature
S ->> B: OpenPGP signature
loop repository update
R ->> R: receive package(s),<br/>generate sync databases,<br/>create OpenPGP hash,<br/>get signature,<br/>update repository
end
B ->> R: package(s) and OpenPGP signature(s)
R -->> S: authenticate,<br/>transmit hash
S -->> N: authenticate for client,<br/>transmit hash and cert ID
N ->> S: raw cryptographic signature
S ->> R: OpenPGP signature
loop metrics collection
B --> M: read
S --> M: read
N --> M: read
R --> M: read
end
loop log aggregation
N -->> L: send via syslog
end
The signing process in more detail may look as follows:
---
title: Signing process in "Signing service signing hashes" scenario
---
sequenceDiagram
participant C as n clients
participant S as signing server
participant N as NetHSM
Note over C: one signing server credential each,<br/>if build server: one repo server credential each
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings
critical authentication
C ->> C: create OpenPGP hash
C -->>+ S: authenticate,<br/>transmit hash and file metadata
S ->> S: log user access and request
option login failure
S -x C: failure
S ->> S: log user login failure
option login successful
S ->> S: log user login success
critical user mapping
S ->> S: map client user to NetHSM user and certificate ID
option mapping not found
S ->> S: log user mapping failure
S -x C: failure
option mapping found
S ->> S: log user mapping success
end
end
critical data preparation
S ->> S: check request
option request is file
S ->> S: create checksum for file
end
critical signing
S -->>+ N: authenticate for client,<br/>transmit checksum and cert ID
option authentication fails/ signature not created
S ->> S: log signature failure
S -x C: failure
S ->> S: log signature return failure
option authentication succeeds/ signature created
N ->>- S: raw cryptographic signature
S ->> S: log signature success
S ->>- C: OpenPGP signature
S ->> S: log signature return success
end
Here, the n clients may be build servers or the repository server, as they are functionally equal in behavior.
Signing service signing files or hashes
In this setup a microservice takes care of taking authenticated client requests and issuing signatures for the request via a PKCS#11 backend. Clients may send checksums or entire files using a custom wire format.
On a build server the signed packages are exposed via a static webserver location.
---
title: Signing service signing files or hashes
---
sequenceDiagram
participant B as build server
participant S as signing server
participant N as NetHSM
participant R as repo server
participant L as logging server
participant M as metrics server
Note over B: 1 signing server credential
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings
Note over S: PKCS11 based tooling for signing
Note over R: 1 signing server credential
critical
B ->> B: get sources,<br/>build package(s)
loop get signature for each package
critical
option
B ->> B: create checksum,<br/>gather metadata
B ->> S: authenticate,<br/>send digest and metadata
option
B ->> B: gather metadata
B ->> S: authenticate,<br/>send file and metadata
S ->> S: create checksum for file
end
S ->> S: combine checksum and metadata to OpenPGP digest
S ->> N: authenticate for client,<br/>transmit OpenPGP digest and cert ID
N -->> S: raw cryptographic signature
S ->> S: create OpenPGP signature
S -->> B: receive OpenPGP signature
B ->> B: move package file and OpenPGP signature<br/>to publicly accessible storage
end
end
critical repository update
R ->> R: order to add package(s) from build server to repo
B ->> R: download package(s) and OpenPGP signature(s) to pool
R ->> R: generate temporary sync databases
loop get signature for each sync database
critical
option
R ->> R: create checksum,<br/>gather metadata
R ->> S: authenticate,<br/>send digest and metadata
option
R ->> R: gather metadata
R ->> S: authenticate,<br/>send file and metadata
S ->> S: create checksum for file
end
S ->> S: combine checksum and metadata to OpenPGP digest
S ->> N: authenticate for client,<br/>transmit OpenPGP digest and cert ID
N -->> S: raw cryptographic signature
S ->> S: create OpenPGP signature
S -->> R: receive OpenPGP signature
end
R ->> R: update repository
end
loop metrics collection
B --> M: read
S --> M: read
N --> M: read
R --> M: read
end
loop log aggregation
N -->> L: send via syslog
end
The signing process in more detail may look as follows:
---
title: Signing process in "Signing service signing files or hashes" scenario
---
sequenceDiagram
participant C as n clients
participant S as signing server
participant A as attestation log
participant N as NetHSM
Note over C: one signing server credential each
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings
critical authentication
critical
option
C ->> C: create checksum, gather metadata
C ->>+ S: authenticate,<br/>transmit hash and metadata
option
C ->> C: gather metadata
C ->>+ S: authenticate,<br/>transmit file and metadata
end
S ->> A: log user access and request
option login failure
S -x C: failure
S ->> A: log user login failure
option login successful
S ->> A: log user login success
critical user mapping
S ->> S: map client user to NetHSM user and certificate ID
option mapping not found
S ->> A: log user mapping failure
S -x C: failure
option mapping found
S ->> A: log user mapping success
end
end
critical data preparation
S ->> S: check request
option request is file
S ->> S: create checksum for file
end
critical signing
S ->>+ N: authenticate for client,<br/>transmit checksum and cert ID
option authentication fails/ signature not created
S ->> A: log signature failure
S -x C: failure
S ->> A: log signature return failure
option authentication succeeds/ signature created
N -->>- S: receive raw cryptographic signature
S ->> A: log signature success
S ->> S: create OpenPGP signature
S -->>- C: receive OpenPGP signature
end
Here, the n clients may be build servers or the repository server, as they are functionally equal in behavior.
Signing service signing files
In this setup a microservice takes care of taking authenticated client requests and issuing signatures for the request via a PKCS#11 backend. The client sends entire files to the service.
---
title: Signing service signing files
---
sequenceDiagram
participant B as build server
participant S as signing server
participant N as NetHSM
participant R as repo server
participant L as logging server
participant M as metrics server
Note over B: 1 signing server credential,<br/>1 repo server credential
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings
Note over S: PKCS11 based tooling for signing
Note over R: 1 signing server credential
loop package build
B ->> B: get sources,<br/>build package(s),<br/>get signature,<br/>send to repo server
end
B -->> S: authenticate,<br/>transmit file
S -->> N: authenticate for client,<br/>transmit checksum and cert ID
N ->> S: raw cryptographic signature
S ->> B: OpenPGP signature
loop repository update
R ->> R: receive package(s),<br/>generate sync databases,<br/>get signature,<br/>update repository
end
B ->> R: package(s) and OpenPGP signature(s)
R -->> S: authenticate,<br/>transmit file
S -->> N: authenticate for client,<br/>transmit checksum and cert ID
N ->> S: raw cryptographic signature
S ->> R: OpenPGP signature
loop metrics collection
B --> M: read
S --> M: read
N --> M: read
R --> M: read
end
loop log aggregation
N -->> L: send via syslog
end
The signing process in more detail may look as follows:
---
title: Signing process in "Signing service signing files" scenario
---
sequenceDiagram
participant C as n clients
participant S as signing server
participant N as NetHSM
Note over C: one signing server credential each,<br/>if build server: one repo server credential each
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings
critical authentication
C -->>+ S: authenticate,<br/>transmit file
S ->> S: log user access and request
option login failure
S -x C: failure
S ->> S: log user login failure
option login successful
S ->> S: log user login success
critical user mapping
S ->> S: map client user to NetHSM user and certificate ID
option mapping not found
S ->> S: log user mapping failure
S -x C: failure
option mapping found
S ->> S: log user mapping success
end
end
critical data preparation
S ->> S: create checksum for file
end
critical signing
S -->>+ N: authenticate for client,<br/>transmit checksum and cert ID
option authentication fails/ signature not created
S ->> S: log signature failure
S -x C: failure
S ->> S: log signature return failure
option authentication succeeds/ signature created
N ->>- S: raw cryptographic signature
S ->> S: log signature success
S ->>- C: OpenPGP signature
S ->> S: log signature return success
end
Here, the n clients may be build servers or the repository server, as they are functionally equal in behavior.
Signing service signing hashes and files as proxy
---
title: Signing service signing hashes and files as proxy
---
sequenceDiagram
participant B as build server
participant S as signing server
participant N as NetHSM
participant R as repo server
participant L as logging server
participant M as metrics server
Note over B: 1 signing server credential
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings,<br/>1 repo server credential
Note over S: PKCS11 based tooling for signing
Note over R: 1 signing server credential
loop package build
B ->> B: get sources,<br/>build package(s),<br/>send to signing server
end
B -->> S: authenticate,<br/>transmit file
S -->> N: authenticate for client,<br/>transmit checksum and cert ID
N ->> S: raw cryptographic signature
loop repository update
R ->> R: receive package(s),<br/>generate sync databases,<br/>get signature,<br/>update repository
end
S ->> R: package(s) and OpenPGP signature(s)
R -->> S: authenticate,<br/>transmit checksum or file
S -->> N: authenticate for client,<br/>transmit checksum and cert ID
N ->> S: raw cryptographic signature
S ->> R: OpenPGP signature
loop metrics collection
B --> M: read
S --> M: read
N --> M: read
R --> M: read
end
loop log aggregation
N -->> L: send via syslog
end
The signing process in more detail may look as follows:
---
title: Signing process in "Signing service signing hashes and files as proxy" scenario
---
sequenceDiagram
participant B as n build clients
participant R as one repo client
participant S as signing server
participant N as NetHSM
Note over B: 1 signing server credential each
Note over R: 1 signing server credential
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings,<br/>repo server credential
critical data aggregation
B ->> B: aggregate packages and metadata (e.g. target repo) in single file
end
critical authentication
B -->>+ S: authenticate,<br/>transmit package(s) and target repo
S ->> S: log user access and request
option login failure
break login failure
S ->> S: log user login failure
S -x B: failure
end
option login success
S ->> S: log user login success
critical user mapping
S ->> S: map client user to NetHSM user and certificate ID
option mapping not found
break user mapping not found
S ->> S: log user mapping failure
S -x B: failure
end
option mapping found
S ->> S: log user mapping success
S ->> B: success
end
end
loop data preparation
S ->> S: create checksum for package
end
loop get signature(s)
critical signing
S ->>+ N: authenticate for client,<br/>transmit checksum and cert ID
option authentication fails/ signature not created
S ->> S: log signature failure
option authentication succeeds/ signature created
N ->>- S: raw cryptographic signature
S ->> S: log signature success
end
end
critical send to repo server
S ->>- R: authenticate,<br/>transmit package(s), OpenPGP signature(s) and target repo
option authentication failure
S ->> S: log failure of transmitting package(s), OpenPGP signature(s) and target repo
option authentication success
S ->> S: log success of transmitting package(s), OpenPGP signature(s) and target repo
end
loop payload preparation
R ->> R: create OpenPGP hash for database and collect file metadata
end
critical authentication
R -->>+ S: authenticate,<br/>transmit OpenPGP hash and file metadata
S ->> S: log user access and request
option login failure
break login failure
S ->> S: log user login failure
S -x R: failure
end
option login successful
S ->> S: log user login success
critical user mapping
S ->> S: map client user to NetHSM user and certificate ID
option mapping not found
break user mapping not found
S ->> S: log user mapping failure
S -x R: failure
end
option mapping found
S ->> S: log user mapping success
end
end
loop get signature(s)
critical signing
S ->>+ N: authenticate for client,<br/>transmit checksum and cert ID
option authentication fails/ signature not created
break signature failure
S ->> S: log signature failure
S -x R: failure
end
option authentication succeeds/ signature created
N ->>- S: raw cryptographic signature
S ->> S: log signature success
end
end
critical return of signature(s)
S ->>- R: OpenPGP signature(s)
option signature(s) not returned
S ->> S: log failed return of OpenPGP signature(s)
option signature(s) returned
S ->> S: log successful return of OpenPGP signature(s)
end
Signing service signing files as proxy
---
title: Signing service signing files as proxy
---
sequenceDiagram
participant B as build server
participant S as signing server
participant N as NetHSM
participant R as repo server
participant L as logging server
participant M as metrics server
Note over B: 1 signing server credential
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings,<br/>1 repo server credential
Note over S: PKCS11 based tooling for signing
Note over R: 1 signing server credential
loop package build
B ->> B: get sources,<br/>build package(s),<br/>send to signing server
end
B -->> S: authenticate,<br/>transmit file
S -->> N: authenticate for client,<br/>transmit checksum and cert ID
N ->> S: raw cryptographic signature
loop repository update
R ->> R: receive package(s),<br/>generate sync databases,<br/>get signature,<br/>update repository
end
S ->> R: package(s) and OpenPGP signature(s)
R -->> S: authenticate,<br/>transmit checksum or file
S -->> N: authenticate for client,<br/>transmit checksum and cert ID
N ->> S: raw cryptographic signature
S ->> R: OpenPGP signature
loop metrics collection
B --> M: read
S --> M: read
N --> M: read
R --> M: read
end
loop log aggregation
N -->> L: send via syslog
end
The signing process in more detail may look as follows:
---
title: Signing process in "Signing service signing files as proxy" scenario
---
sequenceDiagram
participant B as n build clients
participant R as one repo client
participant S as signing server
participant N as NetHSM
Note over B: 1 signing server credential each
Note over R: 1 signing server credential
Note over S: n NetHSM operator credentials,<br/>n NetHSM certificate IDs,<br/>n client to NetHSM mappings,<br/>repo server credential
critical data aggregation
B ->> B: aggregate packages and metadata (e.g. target repo) in single file
end
critical authentication
B -->>+ S: authenticate,<br/>transmit package(s) and target repo
S ->> S: log user access and request
option login failure
break login failure
S ->> S: log user login failure
S -x B: failure
end
option login success
S ->> S: log user login success
critical user mapping
S ->> S: map client user to NetHSM user and certificate ID
option mapping not found
break user mapping not found
S ->> S: log user mapping failure
S -x B: failure
end
option mapping found
S ->> S: log user mapping success
S ->> B: success
end
end
loop data preparation
S ->> S: create checksum for package
end
loop get signature(s)
critical signing
S ->>+ N: authenticate for client,<br/>transmit checksum and cert ID
option authentication fails/ signature not created
S ->> S: log signature failure
option authentication succeeds/ signature created
N ->>- S: raw cryptographic signature
S ->> S: log signature success
end
end
critical send to repo server
S ->>- R: authenticate,<br/>transmit package(s), OpenPGP signature(s) and target repo
option authentication failure
S ->> S: log failure of transmitting package(s), OpenPGP signature(s) and target repo
option authentication success
S ->> S: log success of transmitting package(s), OpenPGP signature(s) and target repo
end
critical authentication
R -->>+ S: authenticate,<br/>transmit database(s)
S ->> S: log user access and request
option login failure
break login failure
S ->> S: log user login failure
S -x R: failure
end
option login successful
S ->> S: log user login success
critical user mapping
S ->> S: map client user to NetHSM user and certificate ID
option mapping not found
break user mapping not found
S ->> S: log user mapping failure
S -x R: failure
end
option mapping found
S ->> S: log user mapping success
end
end
loop data preparation
S ->> S: create checksum for database
end
loop get signature(s)
critical signing
S ->>+ N: authenticate for client,<br/>transmit checksum and cert ID
option authentication fails/ signature not created
break signature failure
S ->> S: log signature failure
S -x R: failure
end
option authentication succeeds/ signature created
N ->>- S: raw cryptographic signature
S ->> S: log signature success
end
end
critical return of signature(s)
S ->>- R: OpenPGP signature(s)
option signature(s) not returned
S ->> S: log failed return of OpenPGP signature(s)
option signature(s) returned
S ->> S: log successful return of OpenPGP signature(s)
end
Previous setup
This document provides an overview of the workflows and contexts for package creation and other artifacts on Arch Linux as it has been until at least 2024.
Packages
The packaging infrastructure involves creating packages on n machines that m package maintainers have access to.
In many cases, the same machine is also used for cryptographically signing the resulting package file(s). There is no overview over whether package maintainers use hardware tokens for this to prevent key exfiltration and no way to enforce it either.
From n machines that m package maintainers have access to, package and detached signature files are copied to a central package repository server.
---
title: Per package maintainer access
---
sequenceDiagram
actor P as package maintainer
participant B as n build machines
participant R as repo server
Note over B: 1 pair of build machine credentials
Note over R: 1 pair of repo server credentials
P ->> B: build and sign package(s)
P ->> R: push built package and signature file(s)
critical
B ->> B: get sources,<br/>build package(s)
B ->> B: sign package(s)
end
Repository sync databases
The central repository server is responsible for creating the repository sync database files, which define the state of each binary package repository.
Repository sync databases are not signed as that would involve either forwarding gpg-agent to the host from n machines that m package maintainers have access to (security and blocking issue), or to add a software key to the host (which may be exfiltrated easily).
Release artifacts
Other artifacts such as installation media and virtual machine images are built semi-automatically or manually and are usually cryptographically signed.
Signing happens either with a software key in CI (in the case of virtual machine images) and is prone to exfiltration attacks, or manual on a single person’s machine.
---
title: Building and signing of virtual machine images
---
sequenceDiagram
participant C as Continuous Integration Pipeline
participant R as repo server
Note over R: 1 pair of repo server credentials
C ->> R: push built installation media and signature file(s)
critical
C ->> C: get sources,<br/>build installation media
C ->> C: sign installation media
end
---
title: Building and signing of installation media
---
sequenceDiagram
actor P as release manager
participant B as n build machines
participant R as repo server
Note over B: 1 pair of build machine credentials
Note over R: 1 pair of repo server credentials
P ->> B: build and sign installation media
P ->> R: push built installation media and signature file(s)
critical
B ->> B: get sources,<br/>build installation media
B ->> B: sign installation media
end
Secure Boot Shim
There is so far no signed shim for Secure Boot, as the location and safe-keeping of a signing key as well as its use for signature creation in packaging is so far unsolved.
API documentation
- nethsm
- signstar-common
- signstar-config
- signstar-configure
- signstar-configure-build
- signstar-download-key-certificates
- signstar-request-signature
- signstar-sign
- signstar-yubihsm2
Local Dependency Graph
The following figure shows the dependency relationships between the crates in the Signstar workspace.
Contributing
These are the contributing guidelines for the signstar project.
Development takes place in the signstar repository.
Writing code
This project is written in Rust and formatted using the nightly rustfmt version.
All contributions are linted using clippy and spell checked using codespell.
The dependencies are linted with cargo-deny.
License identifiers and copyright statements are checked using reuse.
Various just targets are used to run checks and tests.
To aide in development, it is encouraged to install the relevant git pre-commit and git pre-push hooks:
just add-hooks
Writing specifications
Specifications for technology of this project are written in markdown documents in the context of a component, that serves as its reference implementation.
The specifications are located in the component’s resources/specification/ directory.
Specification versioning
A new specification version must be created, if fields of an existing specification are altered (e.g. a field is removed, added or otherwise changed semantically).
By default, given an example specification named topic and given only one version of topic exists, there would only be a document named topic.md.
If the need for version two of topic arises, the document is renamed to topicv1.md, a new file named topicv2.md is used for the new version and a symlink from the generic specification name to the most recent version (here topic.md -> topicv2.md) is created.
Versioned specifications additionally must clearly state the specification version number they are addressing in the NAME and DESCRIPTION section of the document.
New (versions of) specifications must be accompanied by examples and code testing those examples.
The examples and code testing those examples must be kept around for legacy and deprecated specifications to guarantee backwards compatibility.
Writing commit messages
To ensure compatibility and automatic creation of semantic versioning compatible releases the commit message style follows conventional commits. Additionally, commits must be signed off and should be signed cryptographically.
Developer Certificate of Origin
All commits for this project must be signed off using git commit --signoff.
When running just add-hooks in this repository, git is configured to do this automatically going forward.
The semantics of the sign off line are explained in the Developer’s Certificate of Origin.
To check if your commits are correctly signed off locally:
just check-commits
To sign off your last commit, if it is not yet signed off:
git commit --amend --signoff
If you want to fix multiple commits on a feature branch:
git rebase --signoff main
Creating releases
Releases are created by the developers of this project using release-plz by running (per package in the workspace):
just prepare-release <package>
Changed files are added in a pull request towards the default branch.
Once the changes are merged to the default branch a tag is created and pushed for the respective package:
just release <package>
The crate is afterwards automatically published on crates.io using a pipeline job.
License
All code contributions fall under the terms of the Apache-2.0 and MIT.
Configuration file contributions fall under the terms of the CC0-1.0.
Documentation contributions fall under the terms of the CC-BY-SA-4.0.
Specific license assignments and attribution are handled using REUSE.toml.
Individual contributors are all summarized as “Signstar Contributors”.
For a full list of individual contributors, refer to git log --format="%an <%aE>" | sort -u.
Testing
Signstar contains several forms of tests, which behave differently depending on the scope of the test.
Test types
Unit tests
They are regular Rust tests, either marked with the standard #[test] attribute or with a parametrization-capable #[rstest] using the rstest crate.
For convenience just test-all runs them all using cargo nextest for maximum parallelism (in all relevant feature permutations).
Doc tests
Our documentation is tested to validate if the examples are using latest API.
Due to a nextest limitation these need to be run using the standard cargo tooling.
For convenience just test-docs runs them all.
README tests
These tests extract shell fragments from README.md documents in our projects and run them.
This makes sure that the examples using Signstar binaries are also up to date.
Some of these tests use a virtual NetHSM in a container or run in a dedicated Arch Linux container.
For convenience just test-readmes runs them all.
NetHSM integration tests
Several Rust tests require a running NetHSM container.
NetHSM tests, marked with a _nethsm-integration-test feature fall into this category.
For convenience just nethsm-integration-tests runs all NetHSM tests.
Containerized integration tests
Some tests need to be run in a container, as they depend on global system state.
These are marked with a _containerized-integration-test feature and reside in a integration test module named containerized.
A custom runner script (.cargo/runner.sh) ensures that matching tests are run in a dedicated Arch Linux container each.
For convenience just containerized-integration-tests-all runs all containerized tests (in all relevant feature permutations).
Code coverage
All tests (with the exception of doc tests due to a llvm-cov limitation) gather code coverage data if the COVERAGE_REPORT environment variable is set to true.
This is done automatically in CI and the coverage results are displayed inline in merge request diffs.
The numeric values and changes with regards to the main branch are additionally displayed in the MR view.
Media
Videos
2026
Signing distribution artifacts with Signstar
2024
Boring Infrastructure: Building a secure signing environment
Community
Come chat with us on Delta Chat. To be added to our group, contact the dedicated inviter profile:
Constructive feedback, contributions, as well as user support are welcome.
