# Project Structure
The Python package is built from three Rust cdylib crates and one shared rlib:
```
synta-python/ # Primary extension crate (cdylib → _synta)
├── Cargo.toml # crate-type = ["cdylib"]; built by maturin
└── src/
├── lib.rs # #[pymodule] entry point, Encoding enum, pem_to_der/der_to_pem
├── types.rs # ASN.1 primitive type wrappers (Integer, OID, BitString, …)
├── decoder.rs # PyDecoder wrapper
├── encoder.rs # PyEncoder wrapper
├── error.rs # SyntaError Python exception class
├── certificate/
│ ├── mod.rs # re-exports; PKI types live in synta-certificate
│ ├── cert.rs # PyCertificate
│ ├── cert_builder.rs # CertificateBuilder
│ ├── csr_builder.rs # CsrBuilder
│ ├── name_builder.rs # NameBuilder
│ ├── pkix.rs # CertificateList, OCSPResponse
│ └── cms/ # synta.cms submodule (RFC 5652 / RFC 9629)
│ ├── mod.rs # register_cms_submodule, encode_element_opt helper
│ ├── container.rs # ContentInfo, IssuerAndSerialNumber
│ ├── signed.rs # SignedData, SignerInfo
│ ├── enveloped.rs # EnvelopedData, EncryptedData
│ ├── digest.rs # DigestedData, AuthenticatedData
│ ├── kem.rs # KEMRecipientInfo, CMSORIforKEMOtherInfo
│ └── builder.rs # EnvelopedDataBuilder
├── ext_builders.rs # synta.ext submodule: basic_constraints, key_usage, SKI/AKI, SAN/AIA/EKU builders
├── crypto.rs # synta symmetric-crypto primitives (HMAC, PBKDF2, AES, Fernet, OTP)
└── crypto_keys.rs # PublicKey, PrivateKey (RSA, EC, EdDSA)
synta-python-krb5/ # Kerberos / SPNEGO extension crate (cdylib → _krb5)
├── Cargo.toml # crate-type = ["cdylib"]; built with cargo
└── src/
├── lib.rs # #[pymodule] entry point; OnceLock for ObjectIdentifier class
├── krb5.rs # register_krb5_module: Krb5PrincipalName, NT_*/ETYPE_* constants
├── krb5_core.rs # core Kerberos types
├── krb5_fast.rs # FAST armoring types
├── pkinit.rs # PKINIT classes (EncryptionKey, PKAuthenticator, AuthPack, …)
└── spnego.rs # register_spnego_module: NegTokenInit, NegTokenResp, NegotiationToken
synta-python-mtc/ # Merkle Tree Certificates extension crate (cdylib → _mtc)
├── Cargo.toml # crate-type = ["cdylib"]; built with cargo
└── src/
├── lib.rs # #[pymodule] entry point
└── mtc.rs # register_mtc_module: all MTC types
synta-python-common/ # Shared infrastructure rlib (no Python module)
├── Cargo.toml # crate-type = ["rlib"]; statically linked into each cdylib
└── src/
├── lib.rs # re-exports SyntaErr, install_submodule, opt_py_list
├── error.rs # SyntaErr: orphan-rule bridge synta::Error → PyErr
├── submodule.rs # install_submodule: registers PyModule in sys.modules
└── utils.rs # opt_py_list: Option<Vec<Py<T>>> → Option<PyList>
python/ # Python package source (installed by maturin)
├── bench_certificate.py # Certificate parsing benchmark (synta vs cryptography)
├── bench_x509.py # Port of cryptography's test_x509.py benchmarks
├── bench_pkcs.py # PKCS#7 / PKCS#12 extraction benchmark (synta vs cryptography)
├── criterion_compat.py # Criterion-compatible sampling + JSON output (importable)
└── synta/
├── __init__.py # Package exports; imports _synta first, then _krb5 and _mtc
└── py.typed # PEP 561 marker
pyproject.toml # Maturin configuration (manifest-path → synta-python)
```
## Key source file roles
| `synta-python/src/lib.rs` | Module registration, `Encoding` enum, `pem_to_der`, `der_to_pem`, top-level exports |
| `synta-python/src/decoder.rs` | `Decoder` class wrapping `synta::Decoder`; includes `decode_any_str()` |
| `synta-python/src/encoder.rs` | `Encoder` class wrapping `synta::Encoder` |
| `synta-python/src/types.rs` | Python wrappers for all ASN.1 primitive types |
| `synta-python/src/error.rs` | `SyntaError` Python exception (defined only in `_synta`) |
| `synta-python/src/certificate/mod.rs` | PKI types: `ObjectIdentifier`, PKCS loaders, `synta.oids` / `synta.oids.attr` submodule registration |
| `synta-python/src/certificate/cert.rs` | `Certificate` pyclass (lazy decode via OnceLock) |
| `synta-python/src/certificate/pkix.rs` | `CertificationRequest`, `CertificateList`, `OCSPResponse` pyclasses |
| `synta-python-krb5/src/lib.rs` | `_krb5` module init; caches `ObjectIdentifier` from `_synta` via `OnceLock` |
| `synta-python-krb5/src/pkinit.rs` | PKINIT classes: `EncryptionKey`, `Checksum`, `KDFAlgorithmId`, `PKAuthenticator`, `AuthPack`, `PaPkAsReq/Rep`, etc. |
| `synta-python-krb5/src/krb5.rs` | `synta.krb5` submodule: `Krb5PrincipalName`, PKINIT classes, `NT_*` constants |
| `synta-python-krb5/src/spnego.rs` | `synta.spnego` submodule: `NegTokenInit`, `NegTokenResp`, `NegotiationToken` |
| `synta-python-mtc/src/mtc.rs` | `synta.mtc` submodule: all Merkle Tree Certificate types |
| `synta-python-common/src/error.rs` | `SyntaErr` newtype: maps `synta::Error` variants to standard Python exceptions |
| `synta-python-common/src/submodule.rs` | `install_submodule`: sets `__name__`, `__package__`, `__doc__`, adds to `sys.modules` |
## Why the split
Kerberos and Merkle Tree Certificate types are not needed in most deployments.
Moving them into separate cdylibs means users who do not import `synta.krb5` or
`synta.mtc` never pay for loading those types at `import synta` time.
The `synta-python-common` rlib avoids duplicating the error-mapping and
submodule-registration boilerplate across the three cdylibs.
The `_synta.abi3.so` PyO3 bindings live in `synta-python`. The `synta-certificate`
and `synta-krb5` Rust library crates have no PyO3 dependency; their types are
wrapped in `synta-python/src/certificate/` and `synta-python-krb5/src/` respectively.
See also [Cargo Features](cargo-features.md) and [Development](development.md).