# Extended Security Services (RFC 2634)
`synta` provides three fluent builder classes for RFC 2634 Extended Security
Services (ESS) structures. ESS defines optional CMS signed-attribute extensions
for S/MIME: signed receipts, security labels, and signing certificate binding.
All three builders are exported directly from the top-level `synta` module.
| `SigningCertificateBuilder` | `id-aa-signingCertificate` | §5.4 |
| `ReceiptRequestBuilder` | `id-aa-receiptRequest` | §2.7 |
| `ESSSecurityLabelBuilder` | `id-aa-securityLabel` | §3.2 |
## SigningCertificateBuilder
Fluent builder for a `SigningCertificate` DER SEQUENCE (RFC 2634 §5.4).
`SigningCertificate` is a CMS signed attribute that identifies the signer's
certificate by its SHA-1 hash, preventing an attacker from substituting a
different certificate with the same key. At least one certificate ID must be
added before calling `build()`.
```python
class SigningCertificateBuilder:
def __init__(self) -> None: ...
def add_cert_id(
self,
cert_hash: bytes,
issuer_serial_der: bytes | None,
) -> SigningCertificateBuilder: ...
# Add a certificate entry identified by its SHA-1 hash.
# cert_hash: 20-byte SHA-1 digest of the complete DER-encoded certificate.
# issuer_serial_der: optional pre-encoded IssuerSerial SEQUENCE DER TLV,
# or None to omit the issuer/serial field.
# Raises ValueError on decode error (deferred to build()).
def add_policy(self, policy_oid: list[int]) -> SigningCertificateBuilder: ...
# Add a certificate policy OID to the optional policies field.
# policy_oid: OID arc components as a list of ints.
# Raises ValueError if the OID is invalid (deferred to build()).
def build(self) -> bytes: ...
# Build the DER-encoded SigningCertificate SEQUENCE.
# Raises ValueError if no certificate IDs were added or DER encoding fails.
```
### Example
```python,ignore
import hashlib
import synta
# Read the signer's DER certificate
cert_der = open("signer.der", "rb").read()
cert_sha1 = hashlib.sha1(cert_der).digest() # 20-byte SHA-1 hash
# Minimal: hash only, no issuer/serial
sc_der = (
synta.SigningCertificateBuilder()
.add_cert_id(cert_sha1, None)
.build()
)
# With issuer/serial (provides stronger binding)
# issuer_serial_der is a pre-encoded IssuerSerial SEQUENCE DER TLV
sc_with_is_der = (
synta.SigningCertificateBuilder()
.add_cert_id(cert_sha1, issuer_serial_der)
.build()
)
# sc_der can then be added as a signed attribute to a CMS SignedData
```
---
## ReceiptRequestBuilder
Fluent builder for a `ReceiptRequest` DER SEQUENCE (RFC 2634 §2.7).
A `ReceiptRequest` signed attribute requests that recipients return a signed
receipt. Required fields: `signed_content_identifier` and exactly one
`receipts_from_*` call.
```python
class ReceiptRequestBuilder:
def __init__(self) -> None: ...
def signed_content_identifier(self, id: bytes) -> ReceiptRequestBuilder: ...
# Set the signedContentIdentifier (an OCTET STRING value).
def receipts_from_all(self) -> ReceiptRequestBuilder: ...
# Request receipts from all recipients (allReceipts [0]).
def receipts_from_first_tier(self) -> ReceiptRequestBuilder: ...
# Request receipts from first-tier recipients only
# (firstTierRecipients [0]).
def add_receipt_to_email(self, email: str) -> ReceiptRequestBuilder: ...
# Add an email address to the receiptsTo list.
# Wraps a single RFC 822 name in a GeneralNames SEQUENCE.
# Raises ValueError on invalid email (deferred to build()).
def add_receipt_to_raw(self, general_names_der: bytes) -> ReceiptRequestBuilder: ...
# Add a pre-encoded GeneralNames DER TLV to the receiptsTo list.
# Use this for non-email GeneralName types.
def build(self) -> bytes: ...
# Build the DER-encoded ReceiptRequest SEQUENCE.
# Raises ValueError if signed_content_identifier or receipts_from_*
# were not set, or if DER encoding fails.
```
### Example
```python,ignore
import os
import synta
# Content identifier — typically a hash or random value
content_id = os.urandom(16)
rr_der = (
synta.ReceiptRequestBuilder()
.signed_content_identifier(content_id)
.receipts_from_all()
.add_receipt_to_email("receipts@example.com")
.build()
)
# First-tier only, multiple receipt-to addresses
rr_ft_der = (
synta.ReceiptRequestBuilder()
.signed_content_identifier(content_id)
.receipts_from_first_tier()
.add_receipt_to_email("alice@example.com")
.add_receipt_to_email("bob@example.com")
.build()
)
```
---
## ESSSecurityLabelBuilder
Fluent builder for an `ESSSecurityLabel` DER SET (RFC 2634 §3.2).
An `ESSSecurityLabel` signed attribute carries an information security label on
a signed message. The `security_policy` OID is required; all other fields are
optional.
```python
class ESSSecurityLabelBuilder:
def __init__(self) -> None: ...
def security_policy(self, policy_oid: list[int]) -> ESSSecurityLabelBuilder: ...
# Set the securityPolicyIdentifier OID. Required.
# policy_oid: OID arc components as a list of ints.
# Raises ValueError if the OID is invalid (deferred to build()).
def classification(self, value: int) -> ESSSecurityLabelBuilder: ...
# Set the optional securityClassification value (0–32767).
# Standard values: 0=UNMARKED, 1=UNCLASSIFIED, 2=RESTRICTED,
# 3=CONFIDENTIAL, 4=SECRET, 5=TOP_SECRET.
def privacy_mark_utf8(self, mark: str) -> ESSSecurityLabelBuilder: ...
# Set the optional privacyMark as a UTF8String.
def privacy_mark_printable(self, mark: str) -> ESSSecurityLabelBuilder: ...
# Set the optional privacyMark as a PrintableString.
# Raises ValueError if mark contains non-PrintableString characters
# (deferred to build()).
def build(self) -> bytes: ...
# Build the DER-encoded ESSSecurityLabel SET.
# Raises ValueError if security_policy was not set or DER encoding fails.
```
### Security classification constants
RFC 2634 §3.2 defines these standard classification values:
| 0 | UNMARKED |
| 1 | UNCLASSIFIED |
| 2 | RESTRICTED |
| 3 | CONFIDENTIAL |
| 4 | SECRET |
| 5 | TOP_SECRET |
Values 6–32767 are reserved for national or local use.
### Example
```python,ignore
import synta
# Custom or well-known policy OID — here using a PKIX test OID
policy_oid = [1, 3, 6, 1, 5, 5, 7, 13, 1] # id-TEST-certPolicyOne
# Minimal: policy only
label_der = (
synta.ESSSecurityLabelBuilder()
.security_policy(policy_oid)
.build()
)
# With classification and privacy mark
label_classified = (
synta.ESSSecurityLabelBuilder()
.security_policy(policy_oid)
.classification(4) # SECRET
.privacy_mark_utf8("SECRET")
.build()
)
```
See also [Time-Stamp Protocol](tsp.md) for the RFC 3161 TSP builder, and
[CMS Signed Data](../cms/signed-data.md) for the CMS context in which ESS
attributes are used.