synta 0.1.6

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# 1. `example_pem_helpers.py` — PEM/DER conversion

[← Example index](index.md) · [example_pem_helpers.py on Codeberg](https://codeberg.org/abbra/synta/src/branch/main/examples/example_pem_helpers.py)

Bindings: `pem_to_der`, `der_to_pem`, `Certificate.from_pem`, `Certificate.to_pem`,
`CertificationRequest.from_pem`, `CertificationRequest.to_pem`,
`CertificateList.from_pem`, `CertificateList.to_pem`.

- Round-trip a single PEM certificate block through `pem_to_der` / `der_to_pem` (`pem_to_der` always returns `list[bytes]`).
- Show `pem_to_der` with a multi-block PEM chain.
- Use `Certificate.from_pem` on a single block and on a two-certificate chain.
- Use `Certificate.to_pem` on a single cert and a list of certs.

## Source

```python
#!/usr/bin/env python3
"""
Example 1: PEM <-> DER conversion helpers.

Demonstrates: pem_to_der, der_to_pem, Certificate.from_pem, Certificate.to_pem,
CertificationRequest.from_pem/to_pem, CertificateList.from_pem/to_pem,
OCSPResponse.from_pem/to_pem.
"""

import pathlib
import synta

VECTORS = pathlib.Path(__file__).parent.parent / "tests" / "vectors"

# Minimal embedded DER for a CSR (RSA 2048, CN=test.example.com)
_CSR_DER = bytes.fromhex(
    "3082028130820169020100303c3119301706035504030c10746573742e657861"
    "6d706c652e636f6d31123010060355040a0c095465737420436f7270310b3009"
    "06035504061302555330820122300d06092a864886f70d01010105000382010f"
    "003082010a0282010100ab58e2a00789193f4f9bf38287609d7cbe41abe69809"
    "2c9cab84867392a941c22599fbae06edfde4c2e347c303ff3766c29c07cbdc7"
    "54dfe963d7bda8518c81102f8e783913787ade8e2e4084e152f7895bf84ab9a"
    "494b2ebd682ae5ee4ffea728d4a03ba55e4c98d93c155f547c78387fcd1576e"
    "f3d4cac56304af2d8e1ddb64a512ce5e48cf4af16b223475abeb2ea37ed30aa"
    "4c7e5cde8df2a2e3a02fda1fa3f921c9925f52e9f7545b61152f0409f57666f"
    "2b48442d5fe172f241b838e4d5372935e4bf612d7152fea25a4e1a8d2cd1281"
    "b80ab20dd28405d7e29079c13d0b26ef1a5fe6d4266447a27a569a4803de0e1"
    "0ed799975b5eb68e670379bba230203010001a000300d06092a864886f70d01"
    "010b050003820101008644c965a495f06031835ceab2edb4de43dc52a2bef6b"
    "2d8d21acaf8f05704006b1608cf46c54582f113ba22a2e343e62bdcff600a97"
    "6b27dc68d44e8060fd66f23e87504f824f4cc9345778609dd73d140461f72ed"
    "85150a30704b9a32e1e7a858055adb88dcb54eca8993dcb34b6f681fbb8a6c7"
    "b9429bac8f433bdb777f7893b0f67d8ed83f5cf65a02fe840fe8999d4540b88"
    "8d9e1df0f4b1adc2535f8a298c2961138aa7d161ad39f0452b1bb76cf6fe4d7"
    "3a3a1108ecac1939addad1e83322b2c0ebf5b98acd20fae9b6bf6a7a844f5d5"
    "f679e3d3059c3e465050ae8860532b8e1aaa87a68a93410e44db25297e9638d"
    "8f06eb86c5ccfb00b27a010785"
)

# Minimal embedded DER for a CRL (RSA, CN=Test CA, no revoked entries)
_CRL_DER = bytes.fromhex(
    "3082015a3044020101300d06092a864886f70d01010b050030123110300e0603"
    "5504030c0754657374204341170d3236303130313030303030305a170d323630"
    "3730313030303030305a300d06092a864886f70d01010b050003820101004195"
    "618e7d6d6a6440919e872408156017646fd8cb0d617c6952755bfeb5b5f6391d"
    "62b7257c7f94f461851f48ef9f9ade7c05e96679c25f8cb70b619bbc34b16a9"
    "4ceaa072d66d2985407bcd6376925ba7d89fa6861cb7811c8571c9e6bb5d86a5"
    "aad7cf3b62b63e7269b90d11d0e5a5cd54fa50edc6747e997f61738866b6f00"
    "6c9bace2daba18b5bcedaa4bc25cf9add69b3680e6b170b3f57d82e74451d1a"
    "2a1968db64c7d189c8869bd47a16bf91a0c6940cda54cdcbe355176792dc787"
    "2f2a162a2482df9d7d1c6af568808962b23479bbd8229fac19cab7798d9a83f0"
    "0ea4d83a6c61a4e720966e4ee4d9c3bbc3bdb94afde24fc70c8adb265ae15104"
)

# Minimal OCSP successful response
_OCSP_DER = bytes.fromhex(
    "308201c40a0100a08201bd308201b906092b0601050507300101048201aa3082"
    "01a630818fa216041451faf8a55a16074dc0703456a9a831e6f1f8d1b8180f32"
    "303236303331303136303033345a30643062303a300906052b0e03021a050004"
    "14bf7052c8b9c0f760c89123e099815eb2c0394226041451faf8a55a16074dc0"
    "703456a9a831e6f1f8d1b802012a8000180f32303236303130313030303030305a"
    "30110a0f32303236303130313030303030305a300d06092a864886f70d01010b"
    "050003820101004de5a70defb08fe67d2ae1c4ab1ce22db43707db4ca3b65537"
    "a99cf799acbda8194697f4aaf51ab13d7c36ff45abbff19ebd1071d329b30058"
    "025185837125cba733ae42413ab56899d408b934121b16d8325331392a343772"
    "083daa11da186476ea1fb4201c7bc1ac71d380357ff78071b18ecdbfdd6b1c0c"
    "0ab996dae5475d849d27b2c22780b4dc76371eaaaa487d11ed13d48bfd121c9f"
    "847f82ff70bf0143977d06f3f59e70cd8332976058e8dcf23bc1cd2b520ddc36"
    "5a61cc81d6e2e881719056d4db925eb4d86ab4f116f6fc376365c85912dea5b3"
    "4983cea9efe51cfbcbbabef08263aa39bc5f1742e9ecf0986b09025b56ed15a2"
    "a2f8a012fbaad5"
)


def section(title):
    print(f"\n{'─' * 60}\n{title}\n{'─' * 60}")


def demo_pem_to_der_single():
    section("pem_to_der — single PEM block → list[bytes]")
    path = VECTORS / "test_certificate.pem"
    if not path.exists():
        print(f"  Skipped: {path} not found")
        return
    pem_data = path.read_bytes()
    ders = synta.pem_to_der(pem_data)
    assert isinstance(ders, list) and len(ders) == 1
    der = ders[0]
    cert = synta.Certificate.from_der(der)
    print(f"  subject:    {cert.subject}")
    print(f"  DER length: {len(der)} bytes")


def demo_pem_to_der_multi():
    section("pem_to_der — two PEM blocks → list[bytes]")
    path1 = VECTORS / "test_certificate.pem"
    path2 = VECTORS / "test_certificate2.pem"
    if not path1.exists() or not path2.exists():
        print(f"  Skipped: test vector files not found under {VECTORS}")
        return
    pem1 = path1.read_bytes()
    pem2 = path2.read_bytes()
    combined = pem1 + pem2
    ders = synta.pem_to_der(combined)
    assert isinstance(ders, list)
    print(f"  block count: {len(ders)}")
    for i, d in enumerate(ders):
        c = synta.Certificate.from_der(d)
        print(f"  block {i}: {c.subject}  ({len(d)} bytes)")


def demo_der_to_pem():
    section("der_to_pem — raw DER → PEM bytes")
    path = VECTORS / "test_certificate.der"
    if not path.exists():
        print(f"  Skipped: {path} not found")
        return
    der = path.read_bytes()
    pem = synta.der_to_pem(der, "CERTIFICATE")
    assert pem.startswith(b"-----BEGIN CERTIFICATE-----")
    assert pem.rstrip().endswith(b"-----END CERTIFICATE-----")
    print(f"  PEM header: {pem[:27].decode()}")
    print(f"  PEM length: {len(pem)} bytes")

    # Round-trip: DER → PEM → DER
    der2 = synta.pem_to_der(pem)[0]
    assert der2 == der
    print("  Round-trip DER → PEM → DER: OK")


def demo_certificate_from_pem():
    section("Certificate.from_pem — single and multi-block")
    path1 = VECTORS / "test_certificate.pem"
    path2 = VECTORS / "test_certificate2.pem"
    if not path1.exists() or not path2.exists():
        print(f"  Skipped: test vector files not found under {VECTORS}")
        return
    pem_data = path1.read_bytes()
    cert = synta.Certificate.from_pem(pem_data)
    assert isinstance(cert, synta.Certificate)
    print(f"  single block → Certificate: {cert.subject}")

    # Combine two certs
    pem1 = path1.read_bytes()
    pem2 = path2.read_bytes()
    certs = synta.Certificate.from_pem(pem1 + pem2)
    assert isinstance(certs, list)
    print(f"  two blocks  → list of {len(certs)} certificates")


def demo_certificate_to_pem():
    section("Certificate.to_pem — single cert and list of certs")
    path1 = VECTORS / "test_certificate.pem"
    path2 = VECTORS / "test_certificate2.pem"
    if not path1.exists() or not path2.exists():
        print(f"  Skipped: test vector files not found under {VECTORS}")
        return
    pem1 = path1.read_bytes()
    pem2 = path2.read_bytes()
    cert1 = synta.Certificate.from_pem(pem1)
    cert2 = synta.Certificate.from_pem(pem2)

    # Single certificate
    pem_out = synta.Certificate.to_pem(cert1)
    assert pem_out.startswith(b"-----BEGIN CERTIFICATE-----")
    print(f"  to_pem(cert): {len(pem_out)} bytes")

    # List of certificates → concatenated PEM
    chain_pem = synta.Certificate.to_pem([cert1, cert2])
    assert chain_pem.count(b"-----BEGIN CERTIFICATE-----") == 2
    print(f"  to_pem([c1, c2]): {len(chain_pem)} bytes, "
          f"{chain_pem.count(b'BEGIN CERTIFICATE')} blocks")


def demo_csr_pem_roundtrip():
    section("CertificationRequest.from_pem / to_pem — round-trip")
    pem = synta.der_to_pem(_CSR_DER, "CERTIFICATE REQUEST")
    csr = synta.CertificationRequest.from_pem(pem)
    assert isinstance(csr, synta.CertificationRequest)
    print(f"  CSR subject: {csr.subject}")

    pem_out = synta.CertificationRequest.to_pem(csr)
    assert pem_out.startswith(b"-----BEGIN CERTIFICATE REQUEST-----")
    csr2 = synta.CertificationRequest.from_pem(pem_out)
    assert csr2.subject == csr.subject
    print("  CSR PEM round-trip: OK")


def demo_crl_pem_roundtrip():
    section("CertificateList.from_pem / to_pem — round-trip")
    pem = synta.der_to_pem(_CRL_DER, "X509 CRL")
    crl = synta.CertificateList.from_pem(pem)
    assert isinstance(crl, synta.CertificateList)
    print(f"  CRL issuer: {crl.issuer}")

    pem_out = synta.CertificateList.to_pem(crl)
    assert pem_out.startswith(b"-----BEGIN X509 CRL-----")
    crl2 = synta.CertificateList.from_pem(pem_out)
    assert crl2.issuer == crl.issuer
    print("  CRL PEM round-trip: OK")


def demo_ocsp_pem_roundtrip():
    section("OCSPResponse.from_pem / to_pem — round-trip")
    pem = synta.der_to_pem(_OCSP_DER, "OCSP RESPONSE")
    resp = synta.OCSPResponse.from_pem(pem)
    assert isinstance(resp, synta.OCSPResponse)
    print(f"  OCSP status: {resp.status}")

    pem_out = synta.OCSPResponse.to_pem(resp)
    assert pem_out.startswith(b"-----BEGIN OCSP RESPONSE-----")
    resp2 = synta.OCSPResponse.from_pem(pem_out)
    assert resp2.status == resp.status
    print("  OCSP PEM round-trip: OK")


def demo_pem_to_der_error():
    section("pem_to_der — ValueError for non-PEM input")
    try:
        synta.pem_to_der(b"not PEM at all")
        assert False, "expected ValueError"
    except ValueError as e:
        print(f"  ValueError: {e}")


def main():
    print("=" * 60)
    print("Example 1: PEM <-> DER conversion helpers")
    print("=" * 60)
    demo_pem_to_der_single()
    demo_pem_to_der_multi()
    demo_der_to_pem()
    demo_certificate_from_pem()
    demo_certificate_to_pem()
    demo_csr_pem_roundtrip()
    demo_crl_pem_roundtrip()
    demo_ocsp_pem_roundtrip()
    demo_pem_to_der_error()
    print("\nAll PEM helper examples completed.")


if __name__ == "__main__":
    main()
```