synta 0.1.12

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# OCSP Types

This page covers the OCSP parsing types: `OCSPResponse` (parsing incoming
responses) and `OCSPRequest` / `CertID` (parsing incoming requests).

For building OCSP structures from scratch, see
[CRL and OCSP Builders](crl-ocsp-builders.md).

---

## OCSPResponse

`OCSPResponse` represents an RFC 6960 OCSP Response (outer envelope only).

### Construction

```python
OCSPResponse.from_der(data: bytes) -> OCSPResponse
OCSPResponse.from_pem(data: bytes) -> OCSPResponse | list[OCSPResponse]
OCSPResponse.to_pem(resp_or_list) -> bytes
```

### Properties

| Property | Type | Description |
|---|---|---|
| `status` | `str` | Response status: `"successful"`, `"malformedRequest"`, `"internalError"`, `"tryLater"`, `"sigRequired"`, `"unauthorized"` |
| `response_type_oid` | `ObjectIdentifier \| None` | OID of the responseBytes contentType, or `None` for non-successful responses |
| `response_bytes` | `bytes \| None` | Raw content of the responseBytes OCTET STRING, or `None` |

### Methods

| Method | Signature | Returns | Description |
|---|---|---|---|
| `to_der()` | `()` | `bytes` | Original DER bytes |
| `verify_signature` | `(responder: Certificate)` | `None` | Verify the `BasicOCSPResponse` signature using the responder's public key. Raises `ValueError` if no `responseBytes` is present, the response type is not `id-pkix-ocsp-basic`, or the signature is invalid. |

### Full class stub

```python
class OCSPResponse:
    @staticmethod
    def from_der(data: bytes) -> OCSPResponse: ...
    @staticmethod
    def from_pem(data: bytes) -> OCSPResponse | list[OCSPResponse]: ...
    @staticmethod
    def to_pem(resp_or_list) -> bytes: ...

    status: str
    response_type_oid: ObjectIdentifier | None
    response_bytes: bytes | None

    def to_der(self) -> bytes: ...
    def verify_signature(self, responder: Certificate) -> None: ...
```

### Usage

```python
import synta

# Parse a DER-encoded OCSP response
with open("ocsp.der", "rb") as f:
    resp = synta.OCSPResponse.from_der(f.read())

# Check the outer status
print(resp.status)              # e.g. "successful"
print(resp.response_type_oid)   # OID, typically id-pkix-ocsp-basic

# Access the raw inner bytes for further decoding
if resp.status == "successful" and resp.response_bytes:
    # resp.response_bytes is the content of the responseBytes OCTET STRING
    # Decode it as a BasicOCSPResponse using the Decoder
    dec = synta.Decoder(resp.response_bytes, synta.Encoding.DER)
    inner = dec.decode_sequence()
    # ... (decode tbsResponseData, signatureAlgorithm, signature, etc.)

# Verify the response signature
responder_cert = synta.Certificate.from_der(open("responder.der", "rb").read())
try:
    resp.verify_signature(responder_cert)
    print("OCSP signature valid")
except ValueError as e:
    print(f"OCSP verification failed: {e}")
```

---

## CertID

`CertID` identifies one certificate inside an OCSP request by its issuer
hashes and serial number.  Instances are returned by `OCSPRequest.request_list`
and are read-only.

### Properties

| Property | Type | Description |
|---|---|---|
| `hash_algorithm_oid` | `ObjectIdentifier` | Dotted-decimal OID of the hash algorithm used to compute the issuer hashes |
| `issuer_name_hash` | `bytes` | Raw bytes of the issuer DN hash |
| `issuer_key_hash` | `bytes` | Raw bytes of the issuer public key hash |
| `serial_number` | `int` | Certificate serial number as a Python `int` |

### Full class stub

```python
class CertID:
    hash_algorithm_oid: ObjectIdentifier
    issuer_name_hash: bytes
    issuer_key_hash: bytes
    serial_number: int
```

---

## OCSPRequest

`OCSPRequest` represents an RFC 6960 / RFC 9654 OCSP Request as received by
an OCSP responder.  Use `request_list` to iterate the queried certificates.

### Construction

```python
OCSPRequest.from_der(data: bytes) -> OCSPRequest
OCSPRequest.from_pem(data: bytes) -> OCSPRequest | list[OCSPRequest]
```

### Properties

| Property | Type | Description |
|---|---|---|
| `request_list` | `list[CertID]` | The certificates whose status is being queried |
| `requestor_name` | `bytes \| None` | Re-encoded DER of the requestor `GeneralName`, or `None` if absent |
| `request_extensions` | `bytes \| None` | DER bytes of the request extensions `SEQUENCE OF Extension`, or `None` |

### Methods

| Method | Signature | Returns | Description |
|---|---|---|---|
| `to_der()` | `()` | `bytes` | Original DER bytes |

### Full class stub

```python
class OCSPRequest:
    @staticmethod
    def from_der(data: bytes) -> OCSPRequest: ...
    @staticmethod
    def from_pem(data: bytes) -> OCSPRequest | list[OCSPRequest]: ...

    request_list: list[CertID]
    requestor_name: bytes | None
    request_extensions: bytes | None

    def to_der(self) -> bytes: ...
```

### Usage

```python
import synta

# Parse a DER-encoded OCSP request (e.g. received from an HTTP server)
with open("request.der", "rb") as f:
    req = synta.OCSPRequest.from_der(f.read())

# Inspect each queried certificate
for cert_id in req.request_list:
    print(f"hash algorithm: {cert_id.hash_algorithm_oid}")
    print(f"serial:         {cert_id.serial_number}")
    print(f"name hash:      {cert_id.issuer_name_hash.hex()}")
    print(f"key hash:       {cert_id.issuer_key_hash.hex()}")

# Check for an optional requestorName (re-encoded as DER GeneralName TLV)
if req.requestor_name is not None:
    print(f"requestorName DER: {req.requestor_name.hex()}")

# Round-trip the request
assert req.to_der() == open("request.der", "rb").read()
```

### Round-trip example: build, encode, then parse back

```python,ignore
import synta, hashlib

# AlgorithmIdentifier for SHA-1 (id-sha1, no parameters)
SHA1_ALG = bytes.fromhex("300906052b0e03021a0500")

name_der  = synta.NameBuilder().common_name("Example CA").build()
name_hash = hashlib.sha1(name_der).digest()
key_hash  = bytes(20)   # 20-byte placeholder; use real issuer SPKI hash in production
serial    = bytes([0x01])

# 1. Build an unsigned OCSPRequest
spec = synta.OCSPCertIDSpec(
    hash_algorithm_der=SHA1_ALG,
    issuer_name_hash=name_hash,
    issuer_key_hash=key_hash,
    serial=serial,
)
ocsp_der = synta.OCSPRequestBuilder().add_request(spec).build_tbs()

# 2. Parse it back
req = synta.OCSPRequest.from_der(ocsp_der)
assert len(req.request_list) == 1

cert_id = req.request_list[0]
assert cert_id.issuer_name_hash == name_hash
assert cert_id.issuer_key_hash  == key_hash
assert cert_id.serial_number    == 1
print(f"hash_algorithm_oid: {cert_id.hash_algorithm_oid}")
```

See also [Certificate](certificate.md), [CRL](crl.md), and
[CRL and OCSP Builders](crl-ocsp-builders.md).