2 unstable releases
| 0.4.0 | Feb 16, 2026 |
|---|---|
| 0.3.0 | Feb 13, 2026 |
#4 in #bip-32
48KB
1K
SLoC
hdpki
Hierarchical deterministic key derivation for OpenSSH and X.509. Derives ed25519 keys from a master seed using HMAC-SHA512 chain codes (BIP-32 style), producing both OpenSSH keys and X.509 certificates from the same deterministic key tree.
Features
- BIP-32-style HD derivation with HMAC-SHA512 chain codes for ed25519
- BIP-39 mnemonic generation and recovery
- OpenSSH key output (private key + authorized_keys format)
- X.509 certificate generation (self-signed or CA-signed)
- Canonical path validation:
m/ssh/<principal>/<domain>/<host>/<bucket>
CLI usage
The hdpki binary supports generating master seeds, deriving SSH keys, and
creating X.509 certificates.
# Generate a new master seed and write it to master.seed (base64 encoded)
hdpki master --output master.seed
# Create a BIP-39 mnemonic and print it alongside the seed
hdpki master --bip39
# Derive an SSH key for a canonical HD path
hdpki derive \
-m master.seed \
-p m/ssh/user:alice/example.com/bastion.example.com/1700000000 \
-o id_ed25519 \
-A id_ed25519.pub
# Generate a self-signed X.509 certificate
hdpki cert \
-m master.seed \
-p m/ssh/user:alice/example.com/bastion.example.com/1700000000 \
--cn "alice@example.com" \
--san-dns bastion.example.com \
-o cert.pem \
-k key.pem
# Generate a CA certificate
hdpki cert \
-m master.seed \
-p m/ssh/user:ca/example.com/ca.example.com/1 \
--cn "Example CA" --org "Example Corp" \
--ca --days 3650 \
-o ca.pem -k ca-key.pem
# Sign a leaf certificate with the CA
hdpki cert \
-m master.seed \
-p m/ssh/user:web/example.com/web.example.com/1 \
--cn "web.example.com" \
--san-dns web.example.com \
--issuer-path m/ssh/user:ca/example.com/ca.example.com/1 \
--issuer-cert ca.pem \
--days 90 \
-o leaf.pem -k leaf-key.pem
Library usage
use hdpki::{master, X509Params};
let seed = b"my-secret-master-seed-material";
let root = master(seed).unwrap();
// Derive a node and get SSH material
let node = root.derive("m/ssh/user:alice/example.com/bastion.example.com/1").unwrap();
let material = node.ed25519().unwrap();
// Generate a self-signed X.509 certificate
let params = X509Params {
common_name: "alice@example.com".to_string(),
san_dns_names: vec!["bastion.example.com".to_string()],
..Default::default()
};
let cert = node.x509_self_signed(¶ms).unwrap();
println!("{}", cert.cert_pem);
Path format
The canonical derivation path follows this structure:
m/ssh/<principal>/<domain>/<host>/<bucket>
- principal:
user:<name>orhost:<name> - domain: Fully qualified domain (e.g.,
example.com) - host: Must be within the domain (e.g.,
bastion.example.com) - bucket: Numeric value for key rotation (e.g., epoch timestamp)
Use --raw-path (or -r) to bypass strict validation.
License
Apache-2.0
Dependencies
~16–30MB
~525K SLoC