synta 0.1.10

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# API Reference for Rust Crates

<style>
  /* Remove content padding and width cap so the iframe fills edge-to-edge */
  .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:**

| Backend | Mandatory URI attributes | Prerequisites |
|---------|--------------------------|--------------|
| 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`.

---

## 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:**

| Backend | Implementation |
|---------|----------------|
| 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).