import datetime
import traceback
import pytest
import synta
import synta.ext as ext
import synta.oids as oids
def test_name_builder_common_name():
name_der = synta.NameBuilder().common_name("Test CA").build()
assert isinstance(name_der, bytes)
assert len(name_der) > 0
assert name_der[0] == 0x30
def test_name_builder_chaining():
name_der = (
synta.NameBuilder()
.country("US")
.organization("Example Corp")
.organizational_unit("Engineering")
.common_name("example.com")
.build()
)
assert isinstance(name_der, bytes)
assert len(name_der) > 0
single = synta.NameBuilder().common_name("example.com").build()
assert len(name_der) > len(single)
def test_name_builder_empty():
name_der = synta.NameBuilder().build()
assert name_der == bytes([0x30, 0x00])
def test_name_builder_add_attr():
name_der = synta.NameBuilder().add_attr("2.5.4.3", "via add_attr").build()
assert isinstance(name_der, bytes)
assert len(name_der) > 0
def test_name_builder_add_attr_invalid_oid():
with pytest.raises(ValueError, match="invalid OID"):
synta.NameBuilder().add_attr("not.an.oid", "value")
def test_name_builder_roundtrip():
name_der = synta.NameBuilder().common_name("Round-trip Test").build()
key = synta.PrivateKey.generate_ec("P-256")
now = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
later = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
bc_der = ext.basic_constraints(ca=True)
cert = (
synta.CertificateBuilder()
.issuer_name(name_der)
.subject_name(name_der)
.public_key(key.public_key)
.serial_number(1)
.not_valid_before_utc(now)
.not_valid_after_utc(later)
.add_extension(str(oids.BASIC_CONSTRAINTS), True, bc_der)
.sign(key, "sha256")
)
from synta import parse_name_attrs
attrs = parse_name_attrs(cert.issuer_raw_der)
assert any(v == "Round-trip Test" for _, v in attrs)
def test_sign_unsigned_signature_algorithm():
key = synta.PrivateKey.generate_ec("P-256")
name_der = synta.NameBuilder().common_name("Unsigned Test Root").build()
now = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
later = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
bc_der = ext.basic_constraints(ca=True)
ku_der = ext.key_usage(ext.KU_KEY_CERT_SIGN | ext.KU_CRL_SIGN)
cert = (
synta.CertificateBuilder()
.issuer_name(name_der)
.subject_name(name_der)
.public_key(key.public_key)
.serial_number(1)
.not_valid_before_utc(now)
.not_valid_after_utc(later)
.add_extension(str(oids.BASIC_CONSTRAINTS), True, bc_der)
.add_extension(str(oids.KEY_USAGE), True, ku_der)
.sign_unsigned()
)
assert cert.signature_algorithm_oid == oids.ALG_UNSIGNED
def test_sign_unsigned_signature_value_empty():
key = synta.PrivateKey.generate_ec("P-256")
name_der = synta.NameBuilder().common_name("Empty Sig Test").build()
now = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
later = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
cert = (
synta.CertificateBuilder()
.issuer_name(name_der)
.subject_name(name_der)
.public_key(key.public_key)
.serial_number(2)
.not_valid_before_utc(now)
.not_valid_after_utc(later)
.sign_unsigned()
)
assert cert.signature_value == b""
def test_sign_unsigned_missing_field():
name_der = synta.NameBuilder().common_name("Incomplete").build()
now = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
later = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
with pytest.raises(ValueError):
(
synta.CertificateBuilder()
.issuer_name(name_der)
.subject_name(name_der)
.serial_number(1)
.not_valid_before_utc(now)
.not_valid_after_utc(later)
.sign_unsigned()
)
def test_sign_ec_sha256():
key = synta.PrivateKey.generate_ec("P-256")
name_der = synta.NameBuilder().common_name("EC Signed Leaf").build()
issuer_der = synta.NameBuilder().common_name("EC Test CA").build()
now = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
later = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
ku_der = ext.key_usage(ext.KU_DIGITAL_SIGNATURE)
cert = (
synta.CertificateBuilder()
.issuer_name(issuer_der)
.subject_name(name_der)
.public_key(key.public_key)
.serial_number(42)
.not_valid_before_utc(now)
.not_valid_after_utc(later)
.add_extension(str(oids.KEY_USAGE), True, ku_der)
.sign(key, "sha256")
)
assert cert.signature_algorithm_oid == oids.ECDSA_WITH_SHA256
def test_extension_roundtrip_basic_constraints():
key = synta.PrivateKey.generate_ec("P-256")
name_der = synta.NameBuilder().common_name("BC Round-trip CA").build()
now = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
later = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
bc_der = ext.basic_constraints(ca=True, path_length=0)
cert = (
synta.CertificateBuilder()
.issuer_name(name_der)
.subject_name(name_der)
.public_key(key.public_key)
.serial_number(1)
.not_valid_before_utc(now)
.not_valid_after_utc(later)
.add_extension(str(oids.BASIC_CONSTRAINTS), True, bc_der)
.sign_unsigned()
)
recovered = cert.get_extension_value_der(str(oids.BASIC_CONSTRAINTS))
assert recovered == bc_der
def test_extension_roundtrip_key_usage():
key = synta.PrivateKey.generate_ec("P-256")
name_der = synta.NameBuilder().common_name("KU Round-trip").build()
now = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
later = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
ku_der = ext.key_usage(ext.KU_KEY_CERT_SIGN | ext.KU_CRL_SIGN)
cert = (
synta.CertificateBuilder()
.issuer_name(name_der)
.subject_name(name_der)
.public_key(key.public_key)
.serial_number(1)
.not_valid_before_utc(now)
.not_valid_after_utc(later)
.add_extension(str(oids.KEY_USAGE), True, ku_der)
.sign_unsigned()
)
recovered = cert.get_extension_value_der(str(oids.KEY_USAGE))
assert recovered == ku_der
def test_subject_key_identifier_roundtrip():
key = synta.PrivateKey.generate_ec("P-256")
spki_der = key.public_key.to_der()
name_der = synta.NameBuilder().common_name("SKI Test").build()
now = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
later = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
ski_der = ext.subject_key_identifier(spki_der, ext.KEYID_RFC5280)
assert isinstance(ski_der, bytes)
assert len(ski_der) > 0
cert = (
synta.CertificateBuilder()
.issuer_name(name_der)
.subject_name(name_der)
.public_key(key.public_key)
.serial_number(1)
.not_valid_before_utc(now)
.not_valid_after_utc(later)
.add_extension(str(oids.SUBJECT_KEY_IDENTIFIER), False, ski_der)
.sign_unsigned()
)
recovered = cert.get_extension_value_der(str(oids.SUBJECT_KEY_IDENTIFIER))
assert recovered == ski_der
def main():
tests = [
test_name_builder_common_name,
test_name_builder_chaining,
test_name_builder_empty,
test_name_builder_add_attr,
test_name_builder_add_attr_invalid_oid,
test_name_builder_roundtrip,
test_sign_unsigned_signature_algorithm,
test_sign_unsigned_signature_value_empty,
test_sign_unsigned_missing_field,
test_sign_ec_sha256,
test_extension_roundtrip_basic_constraints,
test_extension_roundtrip_key_usage,
test_subject_key_identifier_roundtrip,
]
passed = 0
failed = 0
for test in tests:
try:
test()
print(f" ok {test.__name__}")
passed += 1
except Exception as e:
print(f" FAIL {test.__name__}: {e}")
traceback.print_exc()
failed += 1
print()
print(f"{passed} passed, {failed} failed")
if failed:
raise SystemExit(1)
if __name__ == "__main__":
main()