Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions resources/cmputils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
certextractutils,
certutils,
convertutils,
copyasn1utils,
cryptoutils,
envdatautils,
keyutils,
Expand Down Expand Up @@ -562,12 +563,6 @@ def prepare_old_cert_id_control(
if inc_serial_number:
ser_num += 1

if cert is not None:
cert_issuer = utils.get_openssl_name_notation(cert["tbsCertificate"]["issuer"]) # type: ignore
cert_issuer: Optional[str]
else:
cert_issuer = None

if bad_issuer and cert is None:
raise ValueError("A valid certificate must be provided when `bad_issuer` is True.")

Expand All @@ -577,15 +572,19 @@ def prepare_old_cert_id_control(
issuer=True,
)

if issuer is None and cert_issuer is None:
if not issuer and cert is None:
raise ValueError("Either a valid certificate or an issuer must be provided.")

_issuer_name = issuer or cert_issuer # type: ignore
_issuer_name: str
issuer_name = prepareutils.prepare_general_name(
name_type="directoryName",
name_str=_issuer_name,
)
if issuer:
issuer_name = prepareutils.prepare_general_name(
name_type="directoryName",
name_str=issuer,
)
else:
# Copy the `Name` object. A round trip through text reverses the RDN order (RFC 4514 Section 2.1),
# so the control would no longer identify the certificate.
cert_issuer = copyasn1utils.copy_name(target=rfc9480.Name(), filled_name=cert["tbsCertificate"]["issuer"]) # type: ignore
issuer_name = prepareutils.prepare_general_name_from_name(cert_issuer)

old_cert_id = rfc4211.OldCertId()
old_cert_id["issuer"] = issuer_name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from pyasn1.codec.der.decoder import decode
from pyasn1.codec.der.encoder import encode
from pyasn1_alt_modules import rfc4211, rfc5280
from resources.certbuildutils import generate_certificate
from resources.cmputils import prepare_controls_structure
from resources.certbuildutils import build_certificate, generate_certificate
from resources.cmputils import prepare_controls_structure, prepare_old_cert_id_control
from resources.keyutils import generate_key
from resources.utils import get_openssl_name_notation

Expand Down Expand Up @@ -51,6 +51,31 @@ def test_prepare_structure_controls(self):
decoded_controls, rest = decode(encoded_data, asn1Spec=rfc4211.Controls())
self.assertEqual(rest, b"")

def test_old_cert_id_keeps_multi_rdn_issuer(self):
"""
GIVEN a certificate whose issuer has several RDNs
WHEN prepare_old_cert_id_control is called with the certificate,
THEN the issuer of the `OldCertId` must be DER-identical to the issuer of the certificate.
"""
cert, _ = build_certificate(common_name="CN=Test CA,O=Test Org,C=DE")
control = prepare_old_cert_id_control(cert=cert)
self.assertEqual(
encode(control["value"]["issuer"]["directoryName"]["rdnSequence"]),
encode(cert["tbsCertificate"]["issuer"]["rdnSequence"]),
)

def test_old_cert_id_does_not_share_issuer_with_cert(self):
"""
GIVEN a certificate
WHEN prepare_old_cert_id_control is called with the certificate and the issuer of the control is changed,
THEN the issuer of the certificate must stay unchanged.
"""
cert, _ = build_certificate(common_name="CN=Test CA,O=Test Org,C=DE")
issuer_der = encode(cert["tbsCertificate"]["issuer"]["rdnSequence"])
control = prepare_old_cert_id_control(cert=cert)
control["value"]["issuer"]["directoryName"]["rdnSequence"].clear()
self.assertEqual(encode(cert["tbsCertificate"]["issuer"]["rdnSequence"]), issuer_der)


if __name__ == "__main__":
unittest.main()