# API Reference for Rust Crates
<style>
.content main { padding: 0 !important; max-width: none !important; }
</style>
<iframe
src="api/synta/index.html"
style="width:100%; height:calc(100vh - var(--menu-bar-height, 50px)); border:none; display:block;"
title="Synta Rust API Reference (rustdoc)">
</iframe>
> If the frame above is empty, build the documentation first:
> ```bash
> bash contrib/ci/local-ci.sh doc
> ```
---
## PKCS#11 URI Key Loading
The functions and types described below are part of `synta-certificate` and
allow loading private keys directly from PKCS#11 hardware or software tokens
without ever extracting key material into process memory.
### `BackendPrivateKey::from_pkcs11_uri`
```rust
pub fn from_pkcs11_uri(uri: &str) -> Result<Self, PrivateKeyError>
```
Loads a private key from a PKCS#11 token identified by the given RFC 7512
`pkcs11:` URI. The URI must begin with the `pkcs11:` scheme.
The key handle (or, for the NSS backend, the slot reference) is stored inside
the returned `BackendPrivateKey`. Key material is never extracted; all signing
operations are delegated to the token.
**When to use it:** Use `from_pkcs11_uri` instead of `from_pem` or
`from_pkcs8_der` whenever the private key resides in an HSM, smart card, or
software token and must not leave the token boundary.
**Backend-specific requirements:**
| OpenSSL (`openssl` feature) | None | `pkcs11-provider` configured via `OPENSSL_CONF` before the call |
| NSS (`nss` feature) | `token=` and `object=` | PKCS#11 module registered via `modutil` before the call |
See [deployment-guide.md](deployment-guide.md#hsm--pkcs11-key-storage) for
step-by-step setup instructions for each backend.
### `BackendPrivateKey::pkcs11_info`
```rust
pub fn pkcs11_info(&self) -> Option<&Pkcs11Uri>
```
Returns the parsed PKCS#11 URI for keys loaded via `from_pkcs11_uri`, or
`None` for software keys loaded from PEM/DER.
Useful for logging, diagnostics, or conditional logic that must behave
differently for HSM-backed keys.
### `Pkcs11Uri`
```rust
pub struct Pkcs11Uri {
pub raw: String, // verbatim URI string as supplied to from_pkcs11_uri
pub attrs: Pkcs11UriAttributes,
}
```
Parsed representation of an RFC 7512 `pkcs11:` URI. The `raw` field preserves
the original string exactly; `attrs` contains the pre-decoded path and query
attributes.
### `Pkcs11UriAttributes`
```rust
pub struct Pkcs11UriAttributes {
pub token: Option<String>, // token= path attribute (slot/token label)
pub object: Option<String>, // object= path attribute (key nickname/label)
pub id: Option<Vec<u8>>, // id= CKA_ID, percent-decoded raw bytes
pub pin_value: Option<String>, // ?pin-value= query attribute (token PIN)
}
```
Pre-decoded path and query attributes extracted from a `pkcs11:` URI.
### URI format example
```
pkcs11:token=MyHSM;object=cakey;type=private?pin-value=1234
```
- `token=MyHSM` — the token label (mandatory for NSS, optional for OpenSSL)
- `object=cakey` — the key label / nickname (mandatory for NSS, optional for OpenSSL)
- `type=private` — selects the private-key object class (recommended)
- `pin-value=1234` — PIN supplied inline; omit if the token is already authenticated
### Note on key material
`BackendPrivateKey::pkcs8_der` is always empty for HSM-backed keys. The token
handle is stored internally and used for every signing operation. Callers
should never attempt to serialise an HSM key via `pkcs8_der`.
---
## Python PKCS#11 Token Management
The `synta.pkcs11` submodule provides Python-level access to PKCS#11 token
management: listing slots, enumerating keys, generating key pairs on the token,
deleting keys, and querying whether a specific key exists. It is built on top
of the `TokenManager` trait in `synta-certificate` (feature `pkcs11-mgmt`).
For the underlying architecture see
[system-architecture.md](system-architecture.md#pkcs11-management-architecture).
### Availability
`synta.pkcs11` is present only when `synta-python` is compiled with the
`pkcs11-mgmt` feature, which is automatically enabled by the `openssl` and
`nss` features. If the feature is absent the module is `None`:
```python
import synta
pkcs11 = synta.pkcs11 # None when pkcs11-mgmt feature is absent
```
Tests that require the submodule should guard with:
```python
pkcs11 = pytest.importorskip("synta.pkcs11", reason="pkcs11-mgmt feature not compiled in")
```
### `synta.pkcs11.list_slots`
```text
list_slots(module: str | None = None) -> list[SlotInfo]
```
Returns all PKCS#11 slots reported by the loaded module. If `module` is
`None`, the module path is resolved in order: `PKCS11_MODULE_PATH` environment
variable, then candidate system paths (p11-kit-proxy.so on Fedora/RHEL and
Debian/Ubuntu variants). Pass an absolute path to `module` to bypass
auto-detection.
Raises `ValueError` if `module` is a relative path, if the resolved path does
not exist, or if the PKCS#11 module fails to initialize.
### `synta.pkcs11.SlotInfo`
Frozen pyclass returned by `list_slots()`.
| `slot_id` | `int` | `CK_SLOT_ID` — token slot identifier |
| `token_label` | `str` | Token label with trailing spaces stripped |
| `manufacturer_id` | `str` | Manufacturer identifier |
| `model` | `str` | Token model string |
| `serial_number` | `str` | Token serial number |
| `flags` | `int` | Subset of `CKF_*` bits: `0x0002` write-protected, `0x0004` login required, `0x0100` protected authentication path, `0x0400` token initialized |
### `synta.pkcs11.KeyInfo`
Frozen pyclass returned by `Pkcs11Token.list_keys()`.
| `label` | `str` | `CKA_LABEL` |
| `id` | `bytes` | `CKA_ID` raw bytes; may be empty |
| `key_type` | `str` | `"RSA"`, `"EC"`, `"Ed"`, `"ML-DSA"`, `"ML-KEM"`, or `"Unknown"` |
| `key_bits` | `int` | RSA modulus bits; `0` for non-RSA types |
### `synta.pkcs11.Pkcs11Token`
```text
Creates a token handle. `uri` must be a `pkcs11:` URI (RFC 7512); raises
`ValueError` if the URI scheme is wrong or the module cannot be loaded.
`module` follows the same resolution rules as `list_slots`.
Each call to a method on `Pkcs11Token` opens its own PKCS#11 session,
authenticates if `pin-value=` is present in the URI, performs the operation,
and closes the session. The `Pkcs11Token` object itself holds no open
session between calls.
`__repr__` always redacts the PIN: `pin-value=***`.
#### `Pkcs11Token.find_key`
```text
find_key(object_uri: str) -> bool
```
Returns `True` if a private-key object matching `object_uri` exists in the
token, `False` otherwise. `object_uri` must be a valid `pkcs11:` URI.
#### `Pkcs11Token.list_keys`
```text
list_keys() -> list[KeyInfo]
```
Returns all private keys visible to the authenticated session. The token's
`pin-value=` from the constructor URI is used to authenticate.
#### `Pkcs11Token.delete_key`
```text
delete_key(object_uri: str) -> None
```
Deletes the private key (and its matching public key object, if any) identified
by `object_uri`. Raises `ValueError` if the key is not found or deletion
fails.
#### `Pkcs11Token.generate_key_pair`
```text
generate_key_pair(
key_type: str,
param: int | str,
label: str,
extractable: bool = False,
) -> synta.PrivateKey
```
Generates a key pair on the token and returns a `synta.PrivateKey` handle
backed by the token object.
`key_type` and `param` combinations:
| `"rsa"` | `2048`, `3072`, or `4096` | Keys shorter than 2048 bits are rejected before opening a session |
| `"ec"` | `"P-256"`, `"P-384"`, or `"P-521"` | |
| `"ed25519"` | ignored | |
| `"ed448"` | ignored | |
| `"ml-dsa"` | `"ML-DSA-44"`, `"ML-DSA-65"`, or `"ML-DSA-87"` | Requires PKCS#11 v3.2 |
| `"ml-kem"` | `"ML-KEM-512"`, `"ML-KEM-768"`, or `"ML-KEM-1024"` | Requires PKCS#11 v3.2 |
`label` sets the `CKA_LABEL` attribute on both the private and public key
objects. `extractable` controls `CKA_EXTRACTABLE`; the default `False` keeps
the private key material confined to the token.
### Usage example
```python
import synta.pkcs11 as pkcs11
# Enumerate tokens visible through the system p11-kit proxy
slots = pkcs11.list_slots()
for s in slots:
print(s.slot_id, s.token_label, s.manufacturer_id)
# Manage a specific token identified by a PKCS#11 URI
token = pkcs11.Pkcs11Token("pkcs11:token=MySoftHSM2Token0?pin-value=1234")
# Generate an RSA-4096 key pair; key material stays on the token
key = token.generate_key_pair("rsa", 4096, "caKey") # -> synta.PrivateKey
# Generate an EC key pair on curve P-256
key = token.generate_key_pair("ec", "P-256", "ecKey") # -> synta.PrivateKey
# Check whether a specific key object exists
assert token.find_key("pkcs11:token=MySoftHSM2Token0;object=caKey")
# List all keys on the token
for k in token.list_keys():
print(k.label, k.key_type, k.key_bits)
# Delete a key object
token.delete_key("pkcs11:token=MySoftHSM2Token0;object=caKey")
# Use an explicit module path instead of auto-detection
token = pkcs11.Pkcs11Token(
"pkcs11:token=MySoftHSM2Token0?pin-value=1234",
module="/usr/lib64/pkcs11/libsofthsm2.so",
)
```
### Rust API (`synta_certificate` — `pkcs11-mgmt` feature)
The Python classes mirror the Rust types re-exported from the crate root:
```rust
// Enumerate slots without constructing a Pkcs11Manager
pub fn list_pkcs11_slots() -> Result<Vec<SlotInfo>, PrivateKeyError>
// Obtain a default manager (resolves module from env / system paths)
pub fn pkcs11_manager() -> Result<impl TokenManager, PrivateKeyError>
```
`Pkcs11Manager` can also be constructed explicitly:
```rust
// Load a specific module by absolute path
let mgr = Pkcs11Manager::new("/usr/lib64/pkcs11/libsofthsm2.so")?;
// Resolve module from URI's module-path attr, env var, or candidate paths
let uri = Pkcs11Uri::parse("pkcs11:token=MySoftHSM2Token0?pin-value=1234")?;
let mgr = Pkcs11Manager::from_uri(&uri)?;
// Resolve from env var / candidate paths only
let mgr = Pkcs11Manager::from_env()?;
```
`Pkcs11Manager` is `Send + Sync`. The `TokenManager` trait methods are:
```rust
pub trait TokenManager {
fn list_slots(&self) -> Result<Vec<SlotInfo>, PrivateKeyError>;
fn find_key(&self, uri: &Pkcs11Uri) -> Result<bool, PrivateKeyError>;
fn list_keys(&self, token_name: &str, pin: Option<&str>) -> Result<Vec<Pkcs11KeyInfo>, PrivateKeyError>;
fn delete_key(&self, uri: &Pkcs11Uri) -> Result<(), PrivateKeyError>;
fn generate_key_pair_in_token(
&self,
spec: &KeySpec,
uri: &Pkcs11Uri,
extractable: bool,
) -> Result<BackendPrivateKey, PrivateKeyError>;
}
```
Module path resolution order for `Pkcs11Manager::from_uri` and `from_env`:
1. `module-path` attribute in the PKCS#11 URI
2. `PKCS11_MODULE_PATH` environment variable
3. Candidate system paths probed in order (first existing path wins):
`/usr/lib64/pkcs11/p11-kit-proxy.so`,
`/usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-proxy.so`,
`/usr/lib/aarch64-linux-gnu/pkcs11/p11-kit-proxy.so`,
`/usr/lib/powerpc64le-linux-gnu/pkcs11/p11-kit-proxy.so`,
`/usr/lib/s390x-linux-gnu/pkcs11/p11-kit-proxy.so`,
`/usr/lib/pkcs11/p11-kit-proxy.so`
---
## RSA Key Transport
`BackendPublicKey` and `BackendPrivateKey` in `synta-certificate` expose
RSA encryption and decryption operations. These are used in CMS
`EnvelopedData` construction (key transport RecipientInfo) and in any other
protocol that requires wrapping a symmetric key with an RSA public key.
Both backends (OpenSSL and NSS) are supported. When both features are enabled,
OpenSSL takes priority.
### `BackendPublicKey::rsa_oaep_encrypt`
```rust
pub fn rsa_oaep_encrypt(
&self,
plaintext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, PrivateKeyError>
```
RSA-OAEP encryption using the recipient's public key.
`hash_alg` is the OAEP hash algorithm name: `"sha1"`, `"sha224"`,
`"sha256"`, `"sha384"`, or `"sha512"`.
Returns the ciphertext (same byte-length as the RSA modulus).
### `BackendPublicKey::rsa_pkcs1v15_encrypt`
```rust
pub fn rsa_pkcs1v15_encrypt(
&self,
plaintext: &[u8],
) -> Result<Vec<u8>, PrivateKeyError>
```
RSA PKCS#1 v1.5 encryption using the recipient's public key. For new
protocols prefer RSA-OAEP; PKCS#1 v1.5 is supported for compatibility with
legacy CMS content.
Returns the ciphertext (same byte-length as the RSA modulus).
### `BackendPrivateKey::rsa_oaep_decrypt`
```rust
pub fn rsa_oaep_decrypt(
&self,
ciphertext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, PrivateKeyError>
```
RSA-OAEP decryption using the recipient's private key.
`hash_alg` must match the algorithm used during encryption.
### `BackendPrivateKey::rsa_pkcs1v15_decrypt`
```rust
pub fn rsa_pkcs1v15_decrypt(
&self,
ciphertext: &[u8],
) -> Result<Vec<u8>, PrivateKeyError>
```
RSA PKCS#1 v1.5 decryption using the recipient's private key.
**Backend notes:**
| OpenSSL (`openssl` feature) | Uses `EVP_PKEY_CTX_set_rsa_padding` / `EVP_PKEY_decrypt` |
| NSS (`nss` feature, no `openssl`) | Imports the public key via `SECKEY_DecodeDERSubjectPublicKeyInfo` and the private key via `PK11_ImportDERPrivateKeyInfoAndReturnKey` into the NSS internal softokn slot, then calls `PK11_PubEncrypt` / `PK11_PrivDecrypt` |
---
## AES-GCM Authenticated Encryption
Both the Rust `BlockCipherProvider` trait and the Python `synta.crypto` module expose
AES-GCM (AEAD) encryption and decryption. Both backends (OpenSSL and NSS) are supported.
### `BlockCipherProvider::aes_gcm_encrypt` (Rust)
```rust
fn aes_gcm_encrypt(
&self,
key: &[u8],
nonce: &[u8],
plaintext: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, Self::Error>
```
Encrypts `plaintext` with AES-GCM. `key` must be 16, 24, or 32 bytes (AES-128,
AES-192, or AES-256). `nonce` must be exactly 12 bytes. `aad` is additional
authenticated data that is authenticated but not encrypted; pass `&[]` when not
needed.
Returns `ciphertext ‖ tag` where the authentication tag is 16 bytes.
### `BlockCipherProvider::aes_gcm_decrypt` (Rust)
```rust
fn aes_gcm_decrypt(
&self,
key: &[u8],
nonce: &[u8],
ciphertext_with_tag: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, Self::Error>
```
Decrypts and verifies `ciphertext_with_tag` (the output of `aes_gcm_encrypt`).
The last 16 bytes of `ciphertext_with_tag` are treated as the authentication tag.
Returns `Err` if the tag does not verify or the inputs are otherwise invalid.
Obtain the provider via `default_block_cipher_provider()` for backend-agnostic code.
### `synta.crypto.aes_gcm_encrypt` (Python)
```text
synta.crypto.aes_gcm_encrypt(
key: bytes,
nonce: bytes,
plaintext: bytes,
aad: bytes | None = None,
) -> bytes
```
Python wrapper around `BlockCipherProvider::aes_gcm_encrypt`. `key` is 16, 24, or
32 bytes; `nonce` is 12 bytes. `aad` defaults to `None` (no AAD). Returns
`ciphertext ‖ tag` (16-byte tag). Output is byte-for-byte compatible with
`cryptography.hazmat.primitives.ciphers.aead.AESGCM(key).encrypt(nonce, plaintext, aad)`.
Raises `ValueError` on invalid key length, nonce length, or backend error.
### `synta.crypto.aes_gcm_decrypt` (Python)
```text
synta.crypto.aes_gcm_decrypt(
key: bytes,
nonce: bytes,
ciphertext_with_tag: bytes,
aad: bytes | None = None,
) -> bytes
```
Decrypts and verifies the output of `aes_gcm_encrypt`. `nonce` and `aad` must
match those used during encryption. Raises `ValueError` if the authentication tag
does not verify.
---
## Importing Private Keys from Raw Components
Two `BackendPrivateKey` constructors allow importing key material that is already
available as raw big-endian byte slices. Both require the `openssl` or `nss` feature
and are available from the Rust API only; there is no Python-level equivalent.
### `BackendPrivateKey::from_ec_private_scalar`
```rust
pub fn from_ec_private_scalar(
d: &[u8],
x: &[u8],
y: &[u8],
curve: &str,
) -> Result<Self, PrivateKeyError>
```
Builds an EC private key from the private scalar `d` and the affine public-point
coordinates `x` and `y` (all big-endian). `curve` must be `"P-256"`, `"P-384"`,
or `"P-521"`.
Returns `Err(PrivateKeyError)` if `curve` is unrecognised or if the backend rejects
the key material (e.g. the point is not on the curve).
### `RsaPrivateComponents`
```rust
pub struct RsaPrivateComponents<'a> {
pub n: &'a [u8], // RSA modulus
pub e: &'a [u8], // public exponent
pub d: &'a [u8], // private exponent
pub p: &'a [u8], // first prime factor
pub q: &'a [u8], // second prime factor
pub dp: &'a [u8], // d mod (p - 1)
pub dq: &'a [u8], // d mod (q - 1)
pub qi: &'a [u8], // q^{-1} mod p
}
```
Helper struct that groups the eight CRT fields required to import an RSA private key.
All fields are big-endian unsigned integers, the same encoding used by JWK and PKCS#1.
### `BackendPrivateKey::from_rsa_private_components`
```rust
pub fn from_rsa_private_components(
components: &RsaPrivateComponents<'_>,
) -> Result<Self, PrivateKeyError>
```
Builds an RSA private key from the fields supplied in `components`. Returns
`Err(PrivateKeyError)` if the backend rejects the key material (e.g. inconsistent
CRT values).
---
## Merkle Tree Certificate Types (`synta-mtc`)
The types below are defined in `synta-mtc` and generated from `asn1/MTC.asn1`.
All types carry a lifetime parameter `'a` tied to the input buffer (zero-copy
borrowed fields).
### `ProofNode`
```rust
pub type ProofNode = OctetString;
```
A single hash value in an inclusion path (spec §4.3.2). `ProofNode` is a plain
`OCTET STRING` containing the sibling hash at one level of the tree. There are
no direction bits; direction is determined from the leaf index at verification
time.
### `InclusionProof`
```rust
pub struct InclusionProof {
pub log_entry_index: Integer,
pub tree_size: Integer,
pub subtree_start: Integer,
pub subtree_end: Integer,
pub inclusion_path: Vec<ProofNode>,
}
```
Per spec §6.1 MTCProof. `subtree_start` and `subtree_end` bound the subtree
attested by the accompanying cosignature (`[subtree_start, subtree_end)`).
`log_entry_index` is the absolute leaf position in the full log; this must equal
the `serialNumber` field of the enclosed `TBSCertificate` (see
`verify_serial_number` below).
### `TBSCertificateLogEntry`
```rust
pub struct TBSCertificateLogEntry<'a> {
pub version: Option<Integer>, // [0] EXPLICIT, DEFAULT 0
pub issuer: Name<'a>,
pub validity: Validity<'a>,
pub subject: Name<'a>,
pub subject_public_key_algorithm: AlgorithmIdentifier<'a>,
pub subject_public_key_info_hash: OctetString, // SHA-256 of SubjectPublicKeyInfo
pub issuer_unique_id: Option<BitStringRef<'a>>, // [1] EXPLICIT
pub subject_unique_id: Option<BitStringRef<'a>>, // [2] EXPLICIT
pub extensions: Option<RawDer<'a>>, // [3] EXPLICIT
}
```
Fields changed relative to earlier drafts:
- `subject_public_key_info_hash` (was `subject_public_key_hash`)
- `extensions` is at tag `[3]` (was `[2]`)
- `version` is a new optional field at tag `[0] EXPLICIT INTEGER DEFAULT 0`
### `CosignerID`
```rust
pub type CosignerID<'a> = TrustAnchorID<'a>;
pub struct TrustAnchorID<'a> {
pub hash_algorithm: AlgorithmIdentifier<'a>,
pub public_key: SubjectPublicKeyInfo<'a>,
}
```
`CosignerID` mirrors `LogID`: a cosigner is identified by a hash algorithm and
its public key. The previous structure (`{ issuer: Name, serialNumber:
INTEGER }`) was replaced by this `TrustAnchorID`-style identity.
### `verify_subtree_inclusion_proof`
```rust
pub fn verify_subtree_inclusion_proof(
algorithm: HashAlgorithm,
index: u64,
start: u64,
end: u64,
leaf_hash: &[u8],
proof_path: &[Vec<u8>],
expected_subtree_hash: &[u8],
) -> Result<()>
```
Verifies a compact proof path against a specific subtree root hash (spec
§4.3.3). Translates the absolute `index` to a subtree-relative position
(`index - start`) and delegates to `verify_inclusion_proof`. Use this instead
of `verify_subtree_consistency` when only a proof path (not all leaf hashes) is
available. Exported from `synta_mtc::crypto`.
### Serial number enforcement
Per spec §6.1, the `TBSCertificate.serialNumber` field of a Merkle Tree
Certificate must equal its `log_entry_index` and must be at least 1 (0 is
reserved for `null_entry`). The `CertificateValidator` checks this constraint
automatically as part of both `validate_standalone` and `validate_landmark`.