synta 0.2.3

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# Time-Stamp Protocol (RFC 3161)

`synta.TimeStampReqBuilder` constructs DER-encoded `TimeStampReq` structures
(RFC 3161 §2.4.1) for submission to a Time-Stamp Authority (TSA).  The builder
is exported directly from the top-level `synta` module.

`synta.TimeStampResp` decodes DER-encoded `TimeStampResp` structures
(RFC 3161 §2.4.2) returned by a TSA.  It is a frozen, read-only type.

## TimeStampReqBuilder

Fluent builder for an RFC 3161 `TimeStampReq` SEQUENCE.

The only required field is `message_imprint`.  All other fields are optional
per the RFC.

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

    def message_imprint(
        self,
        hash_alg_oid: list[int],
        hashed_message: bytes,
    ) -> TimeStampReqBuilder: ...
    # Set the messageImprint from a hash algorithm OID (as a list of integer
    # arc components) and raw hash bytes.
    # A NULL parameters field is added automatically (standard for SHA-2).
    # hash_alg_oid: e.g. list(synta.oids.SHA256.components())
    # hashed_message: raw hash bytes, no OCTET STRING wrapper.

    def message_imprint_with_alg_der(
        self,
        alg_der: bytes,
        hashed_message: bytes,
    ) -> TimeStampReqBuilder: ...
    # Set the messageImprint from a pre-encoded AlgorithmIdentifier DER TLV
    # and raw hash bytes.
    # alg_der must be a complete AlgorithmIdentifier SEQUENCE TLV.

    def req_policy(self, oid_components: list[int]) -> TimeStampReqBuilder: ...
    # Set the optional reqPolicy OID by arc components.
    # Requests that the TSA apply a specific timestamp policy.

    def nonce(self, nonce_bytes: bytes) -> TimeStampReqBuilder: ...
    # Set the optional nonce value.
    # nonce_bytes is the big-endian two's-complement INTEGER representation.

    def cert_req(self, val: bool) -> TimeStampReqBuilder: ...
    # Set the certReq flag.
    # When True, requests that the TSA include its signing certificate
    # (and chain) in the time-stamp response.

    def build(self) -> bytes: ...
    # Build the DER-encoded TimeStampReq SEQUENCE.
    # Raises ValueError if message_imprint was not set, if any OID is
    # invalid, or if DER encoding fails.
```

## TimeStampResp

Read-only representation of an RFC 3161 `TimeStampResp` SEQUENCE (§2.4.2).

A `TimeStampResp` carries a `PKIStatusInfo` (indicating whether the request was
granted or rejected) and an optional `timeStampToken` (a CMS `ContentInfo`
wrapping a `SignedData` whose `eContent` is a DER-encoded `TSTInfo`).

The object is **frozen**: no mutation is possible after construction.  Field
decoding is deferred — the DER bytes are validated on construction (the SEQUENCE
tag and length are checked eagerly), and each property decodes its value from the
original bytes on first access and caches the result.

```python
class TimeStampResp:
    @staticmethod
    def from_der(data: bytes) -> TimeStampResp: ...
    # Parse a DER-encoded TimeStampResp SEQUENCE.
    # Raises ValueError if the bytes cannot be decoded.

    def to_der(self) -> bytes: ...
    # Return the original DER bytes passed to from_der().

    @property
    def status(self) -> int: ...
    # PKIStatus integer value (RFC 3161 §2.4.2 / RFC 3280 §3.2.3):
    #   0 — granted              (request fully granted)
    #   1 — grantedWithMods      (granted with modifications)
    #   2 — rejection            (request rejected)
    #   3 — waiting              (request deferred)
    #   4 — revocationWarning    (impending revocation of signer cert)
    #   5 — revocationNotification (signer cert has been revoked)
    # Raises ValueError if decoding fails.

    @property
    def time_stamp_token(self) -> bytes | None: ...
    # Raw DER bytes of the timeStampToken ContentInfo, or None if absent.
    # The token is absent for rejected or deferred responses.
    # The eContentType OID inside is id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4).
    # Raises ValueError if re-encoding the token fails.

    def __repr__(self) -> str: ...
    # Returns e.g. "TimeStampResp(status=0)".
```

### Usage

```python,ignore
import synta

# Load a TSA response from a .tsr file
with open("response.tsr", "rb") as f:
    resp = synta.TimeStampResp.from_der(f.read())

# Check whether the request was granted
if resp.status == 0:
    token_der = resp.time_stamp_token   # bytes — the CMS ContentInfo
    print(f"Token received: {len(token_der)} bytes")
elif resp.status == 2:
    print("Request rejected by TSA")
else:
    print(f"Unexpected status: {resp.status}")

# Round-trip: recover original DER bytes
assert resp.to_der() == open("response.tsr", "rb").read()
```

**Status code reference:**

| Value | Named constant | Meaning |
|-------|---------------|---------|
| `0` | `granted` | The request was fully granted. |
| `1` | `grantedWithMods` | Granted, but the TSA modified some fields. |
| `2` | `rejection` | The request was rejected. |
| `3` | `waiting` | The request is pending; resubmit later. |
| `4` | `revocationWarning` | The signer certificate is about to be revoked. |
| `5` | `revocationNotification` | The signer certificate has been revoked. |

A `timeStampToken` is present only when `status` is `0` or `1`. For all other
status values `time_stamp_token` returns `None`.

## Usage

```python,ignore
import hashlib
import synta
import synta.oids as oids

# Compute SHA-256 digest of the content to timestamp
data = b"content to be time-stamped"
digest = hashlib.sha256(data).digest()
sha256_comps = list(oids.SHA256.components())

# Minimal request — only message_imprint required
req_der = (
    synta.TimeStampReqBuilder()
    .message_imprint(sha256_comps, digest)
    .build()
)

# Full request — nonce prevents replay; certReq asks for the TSA cert chain
import os
nonce = os.urandom(8)

req_der = (
    synta.TimeStampReqBuilder()
    .message_imprint(sha256_comps, digest)
    .nonce(nonce)
    .cert_req(True)
    .build()
)

# Send req_der to TSA endpoint (e.g. via HTTP POST)
# Response (TimeStampResp DER) contains a TimeStampToken if successful
```

### Requesting a specific TSA policy

```python,ignore
import synta
import hashlib

digest = hashlib.sha256(b"data").digest()

# Use a test policy OID — 1.3.6.1.5.5.7.13.1 (id-TEST-certPolicyOne)
# See also synta_certificate::pkix_test_cert_policies_types
test_policy = [1, 3, 6, 1, 5, 5, 7, 13, 1]

req_der = (
    synta.TimeStampReqBuilder()
    .message_imprint(list(synta.oids.SHA256.components()), digest)
    .req_policy(test_policy)
    .build()
)
```

## RFC 3161 type reference

The following types are defined in `synta_certificate::tsp_types`.  Types marked
**Python** are accessible directly from the `synta` Python module.

| Type | RFC 3161 § | Description | Python |
|------|-----------|-------------|--------|
| `TimeStampReq` | §2.4.1 | Time-stamp request message | `synta.TimeStampReqBuilder` |
| `MessageImprint` | §2.4.1 | Hash algorithm + hashed data | (built by `TimeStampReqBuilder`) |
| `TimeStampResp` | §2.4.2 | TSA response (status + optional token) | `synta.TimeStampResp` |
| `PKIStatusInfo` | §2.4.2 | Status code, free text, failure info | (via `TimeStampResp.status`) |
| `TSTInfo` | §2.4.2 | TSTInfo content embedded in the SignedData response | Rust parse only |
| `Accuracy` | §2.4.2 | Time-stamp accuracy (seconds, milliseconds, microseconds) | Rust parse only |
The following types are defined in `synta_certificate::tsp_types` and are
available for parse-only workloads via the Rust API.

| Type | RFC 3161 § | Description |
|------|-----------|-------------|
| `TimeStampReq` | §2.4.1 | Time-stamp request message |
| `MessageImprint` | §2.4.1 | Hash algorithm + hashed data |
| `TimeStampResp` | §2.4.2 | TSA response (status + optional token) |
| `PKIStatusInfo` | §2.4.2 | Status code, free text, failure info |
| `TSTInfo` | §2.4.2 | TSTInfo content embedded in the SignedData response |
| `Accuracy` | §2.4.2 | Time-stamp accuracy (seconds, milliseconds, microseconds) |

The `timeStampToken` field in `TimeStampResp` and the `TimeStampToken` type
alias are both `ContentInfo` structures from CMS (RFC 5652).  The
`eContentType` OID is `id-ct-TSTInfo` (`1.2.840.113549.1.9.16.1.4`), and
the `eContent` is a DER-encoded `TSTInfo` wrapped in a `SignedData`.

See also [CMP Messages](cmp.md) for another PKI protocol builder, and
[Extended Security Services](ess.md) for CMS signed-attribute helpers.