synta 0.2.1

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
#!/usr/bin/env python3
"""
Example: RFC 9810 / RFC 4210 Certificate Management Protocol (CMP).

Demonstrates:
  - Building a pkiConf CMPMessage with CMPMessageBuilder (sender_rfc822,
    recipient_directory_name, transaction_id)
  - Building an ir (Initialization Request) that wraps a CertReqMessages body
  - DER encoding and round-trip parsing via CMPMessage.from_der
  - Inspecting PKIHeader fields (pvno, body_type, sender_der, nonces)
  - OID constants exported by synta.cmp
"""

import os
import synta
import synta.cmp as cmp
import synta.crmf as crmf


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


def demo_pkiconf():
    section("1. Build pkiConf message")

    subject_der = (
        synta.NameBuilder()
        .common_name("host.example.com")
        .organization("Example Org")
        .build()
    )
    print(f"Subject DER ({len(subject_der)} bytes): {subject_der.hex()}")

    txid = os.urandom(16)
    snonce = os.urandom(16)

    conf_msg = (
        cmp.CMPMessageBuilder()
        .sender_rfc822("ca@example.com")
        .recipient_directory_name(subject_der)
        .transaction_id(txid)
        .sender_nonce(snonce)
        .body_pkiconf()
        .build()
    )
    print(f"pkiConf: {repr(conf_msg)}")
    print(f"pvno       : {conf_msg.pvno}")
    print(f"body_type  : {conf_msg.body_type}")
    print(f"body_der   : {conf_msg.body_der}")
    print(f"sender_der : {conf_msg.sender_der.hex()}")
    print(f"txid       : {conf_msg.transaction_id.hex()}")
    print(f"snonce     : {conf_msg.sender_nonce.hex()}")

    return conf_msg, txid, snonce, subject_der


def demo_pkiconf_roundtrip(conf_msg, txid, snonce):
    section("2. DER round-trip (pkiConf)")

    der_conf = conf_msg.to_der()
    print(f"DER length: {len(der_conf)} bytes")

    conf2 = cmp.CMPMessage.from_der(der_conf)
    assert conf2.pvno == 2
    assert conf2.body_type == "pkiconf"
    assert conf2.body_der is None
    assert conf2.transaction_id == txid
    assert conf2.sender_nonce == snonce
    print("pkiConf round-trip assertions passed.")


def demo_ir(subject_der, txid, snonce):
    section("3. Build ir (Initialization Request)")

    req = (
        crmf.CertReqMsgBuilder()
        .cert_req_id(0)
        .subject_name(subject_der)
        .popo_ra_verified()
        .build()
    )
    msgs_der = (
        crmf.CertReqMessagesBuilder()
        .add_request(req)
        .build()
        .to_der()
    )

    ir_msg = (
        cmp.CMPMessageBuilder()
        .sender_rfc822("ca@example.com")
        .recipient_directory_name(subject_der)
        .transaction_id(txid)
        .sender_nonce(snonce)
        .body_ir(msgs_der)
        .build()
    )
    print(f"ir: {repr(ir_msg)}")
    print(f"body_type  : {ir_msg.body_type}")
    body = ir_msg.body_der
    assert body is not None
    print(f"body_der   : {len(body)} bytes")

    inner_msgs = crmf.CertReqMessages.from_der(body)
    assert len(inner_msgs) == 1
    assert inner_msgs[0].cert_req_id == 0
    print("ir body decoded: CertReqMessages with 1 request, id=0")

    return ir_msg


def demo_ir_roundtrip(ir_msg, txid):
    section("4. DER round-trip (ir)")

    der_ir = ir_msg.to_der()
    print(f"DER length: {len(der_ir)} bytes")

    ir2 = cmp.CMPMessage.from_der(der_ir)
    assert ir2.pvno == 2
    assert ir2.body_type == "ir"
    assert ir2.body_der is not None
    assert ir2.transaction_id == txid
    print("ir round-trip assertions passed.")


def demo_directory_name_sender():
    section("5. directoryName sender and rfc822 recipient")

    ca_der = (
        synta.NameBuilder()
        .common_name("Example CA")
        .organization("Example Org")
        .build()
    )

    dir_msg = (
        cmp.CMPMessageBuilder()
        .sender_directory_name(ca_der)
        .recipient_rfc822("user@example.com")
        .body_pkiconf()
        .build()
    )
    print(f"dir_msg: {repr(dir_msg)}")
    assert dir_msg.body_type == "pkiconf"
    print("directoryName sender / rfc822 recipient: OK")


def demo_oid_constants():
    section("6. OID constants")

    oids = [
        ("ID_PASSWORD_BASED_MAC",          cmp.ID_PASSWORD_BASED_MAC),
        ("ID_DHBASED_MAC",                 cmp.ID_DHBASED_MAC),
        ("ID_KEM_BASED_MAC",               cmp.ID_KEM_BASED_MAC),
        ("ID_KP_CM_KGA",                   cmp.ID_KP_CM_KGA),
        ("ID_REG_CTRL_ALT_CERT_TEMPLATE",  cmp.ID_REG_CTRL_ALT_CERT_TEMPLATE),
        ("ID_REG_CTRL_ALG_ID",             cmp.ID_REG_CTRL_ALG_ID),
        ("ID_REG_CTRL_RSA_KEY_LEN",        cmp.ID_REG_CTRL_RSA_KEY_LEN),
    ]
    for name, oid in oids:
        print(f"  {name:40s} = {oid}")


def main():
    print("=" * 60)
    print("Example: CMP (RFC 9810 / RFC 4210)")
    print("=" * 60)
    conf_msg, txid, snonce, subject_der = demo_pkiconf()
    demo_pkiconf_roundtrip(conf_msg, txid, snonce)
    ir_msg = demo_ir(subject_der, txid, snonce)
    demo_ir_roundtrip(ir_msg, txid)
    demo_directory_name_sender()
    demo_oid_constants()
    print("\nDone.")


if __name__ == "__main__":
    main()