Skip to content

EffortlessMetrics/uselesskey

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

292 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

uselesskey

CI Crates.io docs.rs MSRV License: MIT OR Apache-2.0

Deterministic cryptographic test fixtures for Rust.

uselesskey is a test-fixture factory, not a crypto library. It generates key material, certificates, token-shaped fixtures, and negative artifacts at runtime so tests do not need committed PEM/DER/JWK blobs.

Why this exists

uselesskey is a test-fixture layer, not a runtime crypto service.

Use it when you need realistic cryptographic fixtures without committing PEM/DER/JWK files.

It exists to remove this test friction:

  • scanners inspect every commit in a PR, not just the final diff
  • fake-looking keys still trigger policy, push protection, and review friction

uselesskey replaces security exceptions + path ignores + fixture directories with one dev-dependency and runtime generation.

Do not use this crate for production key generation or certificate management. Deterministic mode is intentionally predictable by design. Random mode is for tests only.

What problem it solves

Without this layer, teams commonly end up with one of these:

Approach Problem
Commit PEM/DER files Triggers scanners and push protection
Generate keys ad hoc in tests Repeated boilerplate, slow RSA, no shared determinism
Use raw crypto crates directly You still have to assemble PEM/DER/JWK/X.509 shapes yourself
Use rcgen or other runtime crates directly Useful, but not centered on fixture ergonomics, determinism, or negative cases

uselesskey is built specifically for test artifacts.

What you get

Fixture families

  • RSA (2048, 3072, 4096)
  • ECDSA (P-256, P-384)
  • Ed25519
  • HMAC (HS256, HS384, HS512)
  • OpenPGP (RSA 2048/3072, Ed25519)
  • Token fixtures (API key, bearer, OAuth access-token / JWT shape)
  • X.509 self-signed certificates and certificate chains

Output shapes

  • PKCS#8 PEM/DER
  • SPKI PEM/DER
  • OpenPGP armored and binary keyblocks
  • JWK / JWKS
  • tempfiles for path-based APIs
  • X.509 leaves and chains, and negative variants

Negative artifacts

  • corrupt PEM
  • truncated DER
  • mismatched keypairs
  • expired / revoked / hostname-mismatch / unknown-CA certificates

Pick The Lane First

Start with the cheapest lane that preserves the test's semantics.

I need Recommended lane Why
entropy / scanner-shape only uselesskey-entropy or facade features = ["entropy"] deterministic bytes without key-generation baggage
JWT / bearer / API-token shapes only uselesskey-token or facade features = ["token"] token-shaped fixtures without RSA/X.509 pull-in
valid runtime crypto semantics leaf crates such as uselesskey-rsa, uselesskey-x509, uselesskey-ssh real PKCS#8/JWK/X.509/SSH fixture behavior
build-time materialized fixtures uselesskey-cli materialize + verify clean shape-only OUT_DIR / include_bytes! workflow, with RSA materialization as an explicit opt-in

Start with docs/how-to/choose-features.md for feature selection. Use docs/how-to/choose-lane.md when deciding between entropy, token, semantic, and materialized fixture workflows. See docs/reference/dependency-economics.md and docs/reference/audit-surface.md for the current local-cost and advisory receipts.

Choose the smallest feature set

The uselesskey facade has an empty default feature set. Enable only the fixture families you need.

Common starting points:

# Entropy-only fixtures
[dev-dependencies]
uselesskey = { version = "0.6.0", default-features = false, features = ["entropy"] }
# RSA fixtures
[dev-dependencies]
uselesskey = { version = "0.6.0", features = ["rsa"] }
# Token-only fixtures, no RSA/X.509 pull-in
[dev-dependencies]
uselesskey = { version = "0.6.0", default-features = false, features = ["token"] }
# RSA + JWK/JWKS
[dev-dependencies]
uselesskey = { version = "0.6.0", features = ["rsa", "jwk"] }
# X.509 fixtures
[dev-dependencies]
uselesskey = { version = "0.6.0", features = ["x509"] }
# Build-time materialization, shape-only common lane
cargo run -p uselesskey-cli -- materialize --manifest crates/materialize-shape-buildrs-example/uselesskey-fixtures.toml --out-dir target/tmp-fixtures
cargo run -p uselesskey-cli -- verify --manifest crates/materialize-shape-buildrs-example/uselesskey-fixtures.toml --out-dir target/tmp-fixtures

For build.rs consumers:

# Common shape-only build-time path
[build-dependencies]
uselesskey-cli = { version = "0.6.0", default-features = false }
# Specialized RSA PKCS#8 build-time path
[build-dependencies]
uselesskey-cli = { version = "0.6.0", default-features = false, features = ["rsa-materialize"] }

Use the facade for convenience. Depend on leaf crates only when compile-time minimization matters enough to justify the sharper API.

If you are unsure which flags to start with, start from docs/how-to/choose-features.md. For downstream bot/reviewer policy, use docs/how-to/downstream-fixture-policy.md. For a crate-by-crate support contract (stable/incubating/experimental, audience, and publish status), see docs/reference/support-matrix.md.

Quick start

use uselesskey::{Factory, RsaFactoryExt, RsaSpec};

// Random mode: different keys every run
let fx = Factory::random();

// Deterministic mode: stable output for a seed string
let fx = Factory::deterministic_from_str("my-test-seed");

// Or use env-var seed with random fallback
let fx = Factory::deterministic_from_env("USELESSKEY_SEED")
    .unwrap_or_else(|_| Factory::random());

let rsa = fx.rsa("issuer", RsaSpec::rs256());

let pkcs8_pem = rsa.private_key_pkcs8_pem();
let spki_der = rsa.public_key_spki_der();

The core shape is always:

(mode, domain, label, spec, variant) -> artifact

That keeps fixtures stable in deterministic mode and cacheable in both modes.

Feature reminders for common snippets

  • rsa for PEM/DER, tempfiles, and negative-key examples
  • rsa + jwk for public_jwk() / public_jwks()
  • x509 for certificate, rustls, and tonic examples
  • token for token-shaped fixtures only
  • pgp for armored/binary OpenPGP fixtures

Dependency Snippet Reminders

Dependency snippets:

  • Quick start (RSA)

    [dev-dependencies]
    uselesskey = { version = "0.6.0", features = ["rsa"] }
  • Token-only

    [dev-dependencies]
    uselesskey = { version = "0.6.0", default-features = false, features = ["token"] }
  • JWT/JWK

    [dev-dependencies]
    uselesskey = { version = "0.6.0", features = ["rsa", "jwk"] }
  • X.509 + rustls

    [dev-dependencies]
    uselesskey = { version = "0.6.0", features = ["x509"] }
    uselesskey-rustls = { version = "0.6.0", features = ["tls-config", "rustls-ring"] }
  • jsonwebtoken adapter

    [dev-dependencies]
    uselesskey = { version = "0.6.0", features = ["rsa", "ecdsa", "ed25519", "hmac"] }
    uselesskey-jsonwebtoken = { version = "0.6.0" }
  • JOSE/OpenID adapter

    [dev-dependencies]
    uselesskey = { version = "0.6.0", features = ["rsa", "ecdsa", "ed25519", "hmac"] }
    uselesskey-jose-openid = { version = "0.6.0" }
  • pgp-native adapter

    [dev-dependencies]
    uselesskey = { version = "0.6.0", features = ["pgp"] }
    uselesskey-pgp-native = { version = "0.6.0" }

JWK / JWKS

Requires features = ["rsa", "jwk"].

use uselesskey::{Factory, RsaSpec, RsaFactoryExt};

let fx = Factory::random();
let rsa = fx.rsa("issuer", RsaSpec::rs256());

let jwk = rsa.public_jwk();
let jwks = rsa.public_jwks();

Tempfiles

use uselesskey::{Factory, RsaSpec, RsaFactoryExt};

let fx = Factory::random();
let rsa = fx.rsa("server", RsaSpec::rs256());

let keyfile = rsa.write_private_key_pkcs8_pem().unwrap();
assert!(keyfile.path().exists());

X.509 Certificates

Requires features = ["x509"].

Self-signed certificates for simple TLS tests:

use uselesskey::{Factory, X509FactoryExt, X509Spec};

let fx = Factory::random();
let cert = fx.x509_self_signed("my-service", X509Spec::self_signed("test.example.com"));

let cert_pem = cert.cert_pem();
let key_pem = cert.private_key_pkcs8_pem();

Three-level chains (root intermediate leaf):

use uselesskey::{Factory, X509FactoryExt, ChainSpec};

let fx = Factory::random();
let chain = fx.x509_chain("my-service", ChainSpec::new("test.example.com"));

// Standard TLS server chain: leaf + intermediate, no root
let chain_pem = chain.chain_pem();

// Individual artifacts for custom setups
let root_pem = chain.root_cert_pem();
let leaf_key = chain.leaf_private_key_pkcs8_pem();

X.509 negative fixtures

These are for error-path tests, not validation logic.

use uselesskey::{Factory, X509FactoryExt, ChainSpec};

let fx = Factory::random();
let chain = fx.x509_chain("my-service", ChainSpec::new("test.example.com"));

// Expired leaf certificate
let expired = chain.expired_leaf();

// Hostname mismatch (SAN doesn't match expected hostname)
let wrong_host = chain.hostname_mismatch("wrong.example.com");

// Signed by an unknown CA (not in your trust store)
let unknown = chain.unknown_ca();

// Revoked leaf with CRL signed by the intermediate CA
let revoked = chain.revoked_leaf();
let crl_pem = revoked.crl_pem().expect("CRL present for revoked variant");

Negative fixtures (keys)

use uselesskey::{Factory, RsaSpec, RsaFactoryExt};
use uselesskey::negative::CorruptPem;

let fx = Factory::random();
let rsa = fx.rsa("issuer", RsaSpec::rs256());

let bad_pem = rsa.private_key_pkcs8_pem_corrupt(CorruptPem::BadBase64);
let truncated = rsa.private_key_pkcs8_der_truncated(32);
let mismatched_pub = rsa.mismatched_public_key_spki_der();

Token fixtures

Token fixtures are artifact shapes, not an auth framework. They exist so tests can use realistic-looking token values without committing blobs.

use uselesskey::{Factory, TokenFactoryExt, TokenSpec};

let fx = Factory::random();
let api_key = fx.token("billing", TokenSpec::api_key());
let bearer = fx.token("gateway", TokenSpec::bearer());
let oauth = fx.token("issuer", TokenSpec::oauth_access_token());

assert!(api_key.value().starts_with("uk_test_"));
assert!(bearer.authorization_header().starts_with("Bearer "));
assert_eq!(oauth.value().split('.').count(), 3);

Adapter crates

Adapter crates are separate packages, not facade features. That keeps integration versioning explicit and avoids coupling the facade to every downstream ecosystem type.

Use them when you want native third-party library types returned directly from fixture artifacts.

TLS config builders (uselesskey-rustls)

With the tls-config feature, build rustls configs in one step:

[dev-dependencies]
uselesskey = { version = "0.6.0", features = ["x509"] }
uselesskey-rustls = { version = "0.6.0", features = ["tls-config", "rustls-ring"] }
use uselesskey::{ChainSpec, Factory, X509FactoryExt};
use uselesskey_rustls::{RustlsServerConfigExt, RustlsClientConfigExt};

let fx = Factory::random();
let chain = fx.x509_chain("my-service", ChainSpec::new("test.example.com"));

let server_config = chain.server_config_rustls();
let client_config = chain.client_config_rustls();

ring signing keys (uselesskey-ring)

[dev-dependencies]
uselesskey = { version = "0.6.0", features = ["rsa"] }
uselesskey-ring = { version = "0.6.0", features = ["all"] }
use uselesskey::{Factory, RsaFactoryExt, RsaSpec};
use uselesskey_ring::RingRsaKeyPairExt;

let fx = Factory::random();
let rsa = fx.rsa("signer", RsaSpec::rs256());
let ring_kp = rsa.rsa_key_pair_ring();

RustCrypto types (uselesskey-rustcrypto)

[dev-dependencies]
uselesskey = { version = "0.6.0", features = ["rsa"] }
uselesskey-rustcrypto = { version = "0.6.0", features = ["all"] }
use uselesskey::{Factory, RsaFactoryExt, RsaSpec};
use uselesskey_rustcrypto::RustCryptoRsaExt;

let fx = Factory::random();
let rsa = fx.rsa("signer", RsaSpec::rs256());
let rsa_pk = rsa.rsa_private_key();

aws-lc-rs types (uselesskey-aws-lc-rs)

[dev-dependencies]
uselesskey = { version = "0.6.0", features = ["rsa"] }
uselesskey-aws-lc-rs = { version = "0.6.0", features = ["native", "all"] }
use uselesskey::{Factory, RsaFactoryExt, RsaSpec};
use uselesskey_aws_lc_rs::AwsLcRsRsaKeyPairExt;

let fx = Factory::random();
let rsa = fx.rsa("signer", RsaSpec::rs256());
let lc_kp = rsa.rsa_key_pair_aws_lc_rs();

gRPC TLS (uselesskey-tonic)

[dev-dependencies]
uselesskey = { version = "0.6.0", features = ["x509"] }
uselesskey-tonic = "0.6.0"
use uselesskey::{ChainSpec, Factory, X509FactoryExt};
use uselesskey_tonic::{TonicClientTlsExt, TonicServerTlsExt};

let fx = Factory::random();
let chain = fx.x509_chain("grpc", ChainSpec::new("test.example.com"));

let server_tls = chain.server_tls_config_tonic();
let client_tls = chain.client_tls_config_tonic("test.example.com");

Runnable Examples

The crates/uselesskey/examples/ directory contains standalone programs. Because the facade default feature set is empty, run them with cargo run -p uselesskey --example <name> --features "<flags>" using one working feature set below:

Example Feature(s) Description
adapter_jsonwebtoken rsa,ecdsa,ed25519,hmac Sign and verify JWTs using jsonwebtoken crate integration
adapter_rustls x509 Convert X.509 fixtures into rustls ServerConfig / ClientConfig
basic_ecdsa ecdsa,jwk Generate ECDSA keypairs for P-256 and P-384 in PEM, DER, JWK
basic_ed25519 ed25519,jwk Generate Ed25519 keypairs in PEM, DER, and JWK formats
basic_hmac hmac,jwk Generate HMAC secrets for HS256, HS384, and HS512
basic_rsa rsa,jwk Generate RSA keypairs in PEM, DER, and JWK formats
basic_token token Generate API key, bearer token, and OAuth access-token fixtures
basic_usage ecdsa,ed25519,rsa,jwk All-in-one: RSA, ECDSA, and Ed25519 fixture generation
deterministic rsa Reproducible fixtures from seeds - same seed always yields the same key
deterministic_mode rsa,ecdsa,ed25519 Order-independent deterministic derivation guarantees
jwk_generation ecdsa,ed25519,hmac,rsa,jwk Build JWKs and JWKS with JwksBuilder across key types
jwk_jwks ecdsa,ed25519,hmac,rsa,jwk JWK sets from multiple key types with metadata inspection
jwks rsa,ecdsa,jwk Build a JWKS from RSA and ECDSA public keys
jwks_server_mock rsa,ecdsa,ed25519,jwk Generate a JWKS response body for a mock /.well-known/jwks.json endpoint
jwt_rs256_jwks rsa,jwk RSA keypairs with JWK/JWKS extraction for JWT verification flows
jwt_signing rsa,jwk JWT signing with deterministic RSA, ECDSA, and HMAC keys (ECDSA/HMAC optional)
negative_fixtures x509 Intentionally invalid certificates and keys for error-path testing
tempfile_paths rsa,ed25519 Write key fixtures to temporary files for path-based APIs
tempfiles x509 Write X.509 cert, key, and identity PEM to temp files
tls_server x509 Certificate chain generation for TLS server testing
token_generation token Realistic API keys, bearer tokens, and OAuth tokens for tests
x509_certificates x509 Self-signed certs, cert chains, and negative X.509 fixtures

Workspace Crates

uselesskey is a facade crate that re-exports from focused implementation crates. Depend on the facade for convenience, or on individual crates to minimize compile time.

Implementation Crates

Crate Description
uselesskey Public facade — re-exports all key types and traits behind feature flags
uselesskey-core Factory, deterministic derivation, caching, and negative-fixture helpers
uselesskey-entropy Deterministic high-entropy byte fixtures for scanner-safe and placeholder tests
uselesskey-rsa RSA 2048/3072/4096 keypairs (PKCS#8, SPKI, PEM, DER)
uselesskey-ecdsa ECDSA P-256 / P-384 keypairs
uselesskey-ed25519 Ed25519 keypairs
uselesskey-hmac HMAC HS256/HS384/HS512 secrets
uselesskey-ssh Deterministic OpenSSH key and certificate fixtures
uselesskey-pgp OpenPGP key fixtures (armored + binary keyblocks)
uselesskey-token API key, bearer token, and OAuth access-token fixtures
uselesskey-webhook Deterministic webhook fixtures for GitHub, Stripe, and Slack signature tests
uselesskey-jwk Typed JWK/JWKS models and builders
uselesskey-x509 X.509 self-signed certificates and certificate chains
uselesskey-cli Command-line fixture generation, bundling, and export helpers
uselesskey-test-server Deterministic OIDC discovery and JWKS HTTP test server fixtures

Adapter Crates

Crate Description
uselesskey-axum axum auth-test helpers with deterministic JWKS/OIDC routes
uselesskey-jsonwebtoken jsonwebtoken EncodingKey / DecodingKey
uselesskey-jose-openid JOSE/OpenID-oriented native jsonwebtoken key conversions
uselesskey-pgp-native Native pgp SignedSecretKey / SignedPublicKey adapters
uselesskey-rustls rustls ServerConfig / ClientConfig builders
uselesskey-tonic tonic::transport TLS identity / config for gRPC
uselesskey-ring ring 0.17 native signing key types
uselesskey-rustcrypto RustCrypto native types (rsa::RsaPrivateKey, etc.)
uselesskey-aws-lc-rs aws-lc-rs native types

Feature Flags

The uselesskey facade defaults to no features.

Extension traits by feature:

  • rsa: RsaFactoryExt
  • ecdsa: EcdsaFactoryExt
  • ed25519: Ed25519FactoryExt
  • hmac: HmacFactoryExt
  • pgp: PgpFactoryExt
  • token: TokenFactoryExt
  • x509: X509FactoryExt

For output-family coverage and dependency implications, use the matrix below.

Feature matrix

Facade features (uselesskey crate)

Feature Extension Trait Algorithms / Outputs Implies
rsa RsaFactoryExt RSA 2048/3072/4096 — PKCS#8, SPKI, PEM, DER
ecdsa EcdsaFactoryExt P-256 (ES256), P-384 (ES384) — PKCS#8, SPKI
ed25519 Ed25519FactoryExt Ed25519 — PKCS#8, SPKI
hmac HmacFactoryExt HS256, HS384, HS512
pgp PgpFactoryExt OpenPGP RSA 2048/3072, Ed25519 — armored, binary
token TokenFactoryExt API key, bearer access token, and OAuth access token
x509 X509FactoryExt Self-signed certs, cert chains, negative certs rsa
jwk JWK/JWKS output for all enabled key types
all-keys (bundle) rsa ecdsa ed25519 hmac pgp
full (everything) all-keys token x509 jwk

Adapter crate key-type support

Each adapter crate has per-algorithm feature flags (rsa, ecdsa, ed25519, hmac) and an all convenience flag.

Adapter RSA ECDSA Ed25519 HMAC X.509 / TLS Extra features
uselesskey-jsonwebtoken
uselesskey-jose-openid
uselesskey-pgp-native
uselesskey-ring
uselesskey-rustcrypto
uselesskey-aws-lc-rs native (enables aws-lc-rs dep)
uselesskey-rustls tls-config, rustls-ring, rustls-aws-lc-rs
uselesskey-tonic

Why this crate

Order-independent determinism

Fixtures derive from stable identity components:

seed + (domain, label, spec, variant) -> derived seed -> artifact

Adding new fixtures doesn't perturb existing ones. Test order doesn't matter.

Cache-by-identity

RSA keygen is expensive. Per-factory caching by (domain, label, spec, variant) makes runtime generation cheap enough to replace committed fixtures.

Shape-first outputs

Ask for shapes first: PKCS#8, SPKI, PEM, DER, JWK, JWKS, or tempfiles. Consumers ask for artifact shapes; low-level crypto primitives are intentionally not the default output.

Negative artifacts as first-class

Corrupt PEM, truncated DER, mismatched keys, expired certs, revoked leaves with CRLs: these are exactly the artifacts teams otherwise handcraft and commit. uselesskey makes them deterministic, cheap, and disposable.

When not to use this crate

  • production key generation
  • runtime certificate authority behavior
  • certificate validation logic
  • HSM / TPM / hardware-backed keys
  • signing or verification APIs as the primary abstraction

For runtime certificate generation, reach for rcgen directly. For validation, use rustls, x509-parser, or the library actually responsible for verification.

Ecosystem

Use uselesskey when you need realistic test fixtures that should not live in git history.

Reach for:

  • rcgen when you need runtime certificate generation outside a fixture-centric workflow
  • rustls when you need TLS runtime integration and validation
  • x509-parser when you need parsing/inspection/validation work

Community

Stability and versioning

Derivation stability Artifacts for a given (seed, domain, label, spec, variant) tuple are stable within the same DerivationVersion. If derivation logic changes, a new derivation version is introduced instead of mutating the old one.

Semver Breaking API changes bump the minor version until 1.0, then the major version.

MSRV The minimum supported Rust version is 1.92 (edition 2024).

License

Licensed under either of:

at your option.

About

Deterministic cryptographic key and certificate fixtures for Rust tests. Never commit secrets again.

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors