synta 0.2.0

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# PKCS#5 v2.1 — Password-Based Cryptography (RFC 8018)

`synta.pkcs5` provides DER-encoding helpers for PKCS#5 v2.1 (RFC 8018)
parameter structures used in password-based key derivation and encryption.
Two fluent builder classes are exported from the `synta.pkcs5` submodule, and
are also available directly from the top-level `synta` module.

```python
import synta.pkcs5 as pkcs5
```

## Pbkdf2ParamsBuilder

Fluent builder for `PBKDF2-params` (RFC 8018 §5.2).

Required fields: `salt` and `iteration_count`.  When `prf` is absent the
default HMAC-SHA-1 PRF is used per RFC 8018 §5.2; modern deployments should
always set `prf` explicitly to a SHA-2 HMAC.

```python
class Pbkdf2ParamsBuilder:
    def __init__(self) -> None: ...

    def salt(self, salt: bytes) -> Pbkdf2ParamsBuilder: ...
    # Set the salt OCTET STRING.
    # RFC 8018 recommends at least 8 bytes of random salt.

    def iteration_count(self, count: int) -> Pbkdf2ParamsBuilder: ...
    # Set the iterationCount.
    # RFC 8018 minimum is 1000; modern deployments use at least 10 000.
    # Raises ValueError if count is 0 (deferred to build()).

    def key_length(self, len: int) -> Pbkdf2ParamsBuilder: ...
    # Set the optional keyLength (output key length in bytes).

    def prf(self, prf_oid: list[int]) -> Pbkdf2ParamsBuilder: ...
    # Set the optional prf (pseudo-random function) algorithm OID.
    # Use an OID constant from synta.pkcs5, e.g. list(pkcs5.ID_HMAC_WITH_SHA256.components()).
    # When absent, defaults to HMAC-SHA-1 per RFC 8018.
    # Raises ValueError if the OID is invalid (deferred to build()).

    def build(self) -> bytes: ...
    # Build the DER-encoded PBKDF2-params SEQUENCE.
    # Raises ValueError if salt or iteration_count are missing, or if DER
    # encoding fails.

    def build_as_algorithm_identifier(self, alg_oid: list[int]) -> bytes: ...
    # Build PBKDF2-params and wrap it as an AlgorithmIdentifier.
    # Produces AlgorithmIdentifier { id-PBKDF2, PBKDF2-params } DER for use
    # as the keyDerivationFunc field in Pbes2ParamsBuilder.
    # Pass list(pkcs5.ID_PBKDF2.components()) as alg_oid.
    # Raises ValueError if any required field is missing or encoding fails.
```

## Pbes2ParamsBuilder

Fluent builder for `PBES2-params` (RFC 8018 §6.2).

`PBES2-params` contains two `AlgorithmIdentifier` fields: the key derivation
function (typically PBKDF2) and the encryption scheme (typically AES-CBC-PAD).
Both fields are required.

```python
class Pbes2ParamsBuilder:
    def __init__(self) -> None: ...

    def key_derivation_func(self, alg_id_der: bytes) -> Pbes2ParamsBuilder: ...
    # Set the keyDerivationFunc from a pre-encoded AlgorithmIdentifier DER blob.
    # Use Pbkdf2ParamsBuilder.build_as_algorithm_identifier() to produce this.

    def encryption_scheme(self, alg_id_der: bytes) -> Pbes2ParamsBuilder: ...
    # Set the encryptionScheme from a pre-encoded AlgorithmIdentifier DER blob.

    def encryption_scheme_from_oid(
        self,
        enc_oid: list[int],
        iv: bytes,
    ) -> Pbes2ParamsBuilder: ...
    # Set the encryptionScheme from an OID and an IV OCTET STRING value.
    # Builds AlgorithmIdentifier { oid, OCTET STRING iv } for AES-CBC-PAD.
    # enc_oid: e.g. list(pkcs5.AES256_CBC_PAD.components())
    # iv: 16-byte initialization vector.
    # Raises ValueError if the OID is invalid (deferred to build()).

    def build(self) -> bytes: ...
    # Build the DER-encoded PBES2-params SEQUENCE.
    # Raises ValueError if either field was not set or DER encoding fails.
```

## OID constants (synta.pkcs5)

| Constant | OID | Description |
|----------|-----|-------------|
| `ID_PBKDF2` | `1.2.840.113549.1.5.12` | PBKDF2 key derivation |
| `ID_PBES2` | `1.2.840.113549.1.5.13` | PBES2 encryption scheme |
| `ID_HMAC_WITH_SHA1` | `1.2.840.113549.2.7` | HMAC-SHA-1 PRF (RFC 8018 default) |
| `ID_HMAC_WITH_SHA224` | `1.2.840.113549.2.8` | HMAC-SHA-224 PRF |
| `ID_HMAC_WITH_SHA256` | `1.2.840.113549.2.9` | HMAC-SHA-256 PRF |
| `ID_HMAC_WITH_SHA384` | `1.2.840.113549.2.10` | HMAC-SHA-384 PRF |
| `ID_HMAC_WITH_SHA512` | `1.2.840.113549.2.11` | HMAC-SHA-512 PRF |
| `AES128_CBC_PAD` | `2.16.840.1.101.3.4.1.2` | AES-128-CBC-PAD |
| `AES192_CBC_PAD` | `2.16.840.1.101.3.4.1.22` | AES-192-CBC-PAD |
| `AES256_CBC_PAD` | `2.16.840.1.101.3.4.1.42` | AES-256-CBC-PAD |

## Usage

### Building PBKDF2 parameters

```python,ignore
import os
import synta
import synta.pkcs5 as pkcs5

salt = os.urandom(16)

params_der = (
    synta.Pbkdf2ParamsBuilder()
    .salt(salt)
    .iteration_count(600_000)
    .key_length(32)
    .prf(list(pkcs5.ID_HMAC_WITH_SHA256.components()))
    .build()
)
```

### Building PBES2 parameters (PBKDF2 + AES-256-CBC)

```python,ignore
import os
import synta
import synta.pkcs5 as pkcs5

salt = os.urandom(16)
iv   = os.urandom(16)

# Step 1: build the key derivation AlgorithmIdentifier
kdf_der = (
    synta.Pbkdf2ParamsBuilder()
    .salt(salt)
    .iteration_count(600_000)
    .prf(list(pkcs5.ID_HMAC_WITH_SHA256.components()))
    .build_as_algorithm_identifier(list(pkcs5.ID_PBKDF2.components()))
)

# Step 2: wrap in PBES2-params with AES-256-CBC-PAD
pbes2_der = (
    synta.Pbes2ParamsBuilder()
    .key_derivation_func(kdf_der)
    .encryption_scheme_from_oid(list(pkcs5.AES256_CBC_PAD.components()), iv)
    .build()
)

# pbes2_der is ready for use as the parameters field of the
# PBES2 AlgorithmIdentifier (OID = pkcs5.ID_PBES2)
```

See also [PKCS#8 Private Key Envelope](../protocols/pkcs8.md) where PBES2
parameters appear when encrypting a `OneAsymmetricKey`, and
[CMS EncryptedData](../cms/encrypted-data.md) for symmetric encryption of CMS
content.