from __future__ import annotations
import sys
from pathlib import Path
from typing import Callable
from criterion_compat import measure as _criterion_measure
from criterion_compat import save_criterion_files as _criterion_save
try:
import synta
import synta.oids as _synta_oids
except ImportError:
print(
"ERROR: 'synta' Python module not found.\n"
"Build and install it with:\n"
" cd synta-python && maturin develop --release && cd ..",
file=sys.stderr,
)
sys.exit(1)
try:
import cryptography.x509 as _cx509
_HAS_CRYPTOGRAPHY = True
except ImportError:
_HAS_CRYPTOGRAPHY = False
print(
"INFO: 'cryptography' package not installed — skipping cryptography_x509 comparison.\n"
"Install with: pip install cryptography",
file=sys.stderr,
)
_SCRIPT_DIR = Path(__file__).resolve().parent
_REPO_ROOT = _SCRIPT_DIR.parent
_TEST_VECTORS_DIR = _REPO_ROOT / "tests" / "vectors"
_X509_DIR = (
_TEST_VECTORS_DIR
/ "cryptography"
/ "vectors"
/ "cryptography_vectors"
/ "x509"
)
_PKITS_DIR = _X509_DIR / "PKITS_data" / "certs"
_CRITERION_DIR: Path | None = None
if "--save-criterion" in sys.argv:
_idx = sys.argv.index("--save-criterion")
_next = sys.argv[_idx + 1] if _idx + 1 < len(sys.argv) else ""
if _next and not _next.startswith("--"):
_CRITERION_DIR = Path(_next)
else:
_CRITERION_DIR = _REPO_ROOT / "target" / "criterion"
WARMUP_TIME_S = 3.0
MEASUREMENT_TIME_S = 5.0
def _measure(fn: Callable[[], object]) -> tuple[float, int, list[float], list[float]]:
avg_us, iters, times_ns = _criterion_measure(fn, WARMUP_TIME_S, MEASUREMENT_TIME_S)
return avg_us, sum(int(x) for x in iters), iters, times_ns
def _run(
group: str,
function: str,
value: str,
fn: Callable[[], object],
) -> None:
avg_us, total_iters, iters, times_ns = _measure(fn)
print(f"{group}/{function}/{value} avg: {avg_us:.3f} µs ({total_iters} iterations)")
if _CRITERION_DIR is not None:
_criterion_save(_CRITERION_DIR, group, function, value, iters, times_ns)
_OID_STR = str(_synta_oids.CT_PRECERT_SCTS)
def bench_object_identifier_constructor() -> None:
print()
group = "object_identifier_constructor"
value = _OID_STR
_run(group, "synta", value, lambda: synta.ObjectIdentifier(_OID_STR))
if _HAS_CRYPTOGRAPHY:
_run(group, "cryptography_x509", value,
lambda: _cx509.ObjectIdentifier(_OID_STR))
def bench_load_der_certificate(cert_bytes: bytes, value: str) -> None:
print()
group = "load_der_certificate"
_run(group, "synta", value,
lambda: synta.Certificate.from_der(cert_bytes))
_run(group, "synta_full", value,
lambda: synta.Certificate.full_from_der(cert_bytes))
if _HAS_CRYPTOGRAPHY:
_run(group, "cryptography_x509", value,
lambda: _cx509.load_der_x509_certificate(cert_bytes))
def bench_load_pem_certificate(pem_bytes: bytes, value: str) -> None:
print()
group = "load_pem_certificate"
_run(group, "synta", value,
lambda: synta.Certificate.from_pem(pem_bytes))
if _HAS_CRYPTOGRAPHY:
_run(group, "cryptography_x509", value,
lambda: _cx509.load_pem_x509_certificate(pem_bytes))
def bench_encode_pem_certificate(cert_bytes: bytes, value: str) -> None:
print()
group = "encode_pem_certificate"
synta_cert = synta.Certificate.from_der(cert_bytes)
_run(group, "synta", value,
lambda: synta.Certificate.to_pem(synta_cert))
if _HAS_CRYPTOGRAPHY:
import cryptography.hazmat.primitives.serialization as _ser
cx509_cert = _cx509.load_der_x509_certificate(cert_bytes)
_run(group, "cryptography_x509", value,
lambda: cx509_cert.public_bytes(_ser.Encoding.PEM))
def main() -> None:
if not _TEST_VECTORS_DIR.exists():
print(
f"ERROR: test vectors directory not found:\n {_TEST_VECTORS_DIR}\n\n"
"Run the Criterion benchmarks first to clone certificate repositories:\n"
" cargo bench -p synta-bench --bench bindings --no-run",
file=sys.stderr,
)
sys.exit(1)
if _CRITERION_DIR is not None:
print(f"Criterion JSON output: {_CRITERION_DIR}", file=sys.stderr)
bench_object_identifier_constructor()
good_ca = _PKITS_DIR / "GoodCACert.crt"
if good_ca.exists():
good_ca_bytes = good_ca.read_bytes()
bench_load_der_certificate(good_ca_bytes, "GoodCACert")
bench_encode_pem_certificate(good_ca_bytes, "GoodCACert")
else:
print(
f"\nWARNING: {good_ca} not found — skipping load_der_certificate.\n"
"Run: cargo bench -p synta-bench --bench bindings --no-run",
file=sys.stderr,
)
cryptography_io = _X509_DIR / "cryptography.io.pem"
if cryptography_io.exists():
bench_load_pem_certificate(cryptography_io.read_bytes(), "cryptography.io")
else:
print(
f"\nWARNING: {cryptography_io} not found — skipping load_pem_certificate.",
file=sys.stderr,
)
print()
if __name__ == "__main__":
main()