def _decode_certificate_policies(backend, cp): cp = backend._ffi.cast("Cryptography_STACK_OF_POLICYINFO *", cp) cp = backend._ffi.gc(cp, backend._lib.CERTIFICATEPOLICIES_free) num = backend._lib.sk_POLICYINFO_num(cp) certificate_policies = [] for i in range(num): qualifiers = None pi = backend._lib.sk_POLICYINFO_value(cp, i) oid = x509.ObjectIdentifier(_obj2txt(backend, pi.policyid)) if pi.qualifiers != backend._ffi.NULL: qnum = backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers) qualifiers = [] for j in range(qnum): pqi = backend._lib.sk_POLICYQUALINFO_value(pi.qualifiers, j) pqualid = x509.ObjectIdentifier(_obj2txt(backend, pqi.pqualid)) if pqualid == CertificatePoliciesOID.CPS_QUALIFIER: cpsuri = backend._ffi.buffer( pqi.d.cpsuri.data, pqi.d.cpsuri.length)[:].decode("ascii") qualifiers.append(cpsuri) else: assert pqualid == CertificatePoliciesOID.CPS_USER_NOTICE user_notice = _decode_user_notice(backend, pqi.d.usernotice) qualifiers.append(user_notice) certificate_policies.append(x509.PolicyInformation(oid, qualifiers)) return x509.CertificatePolicies(certificate_policies)
def _build_certificate_policies(self, ext): cp = self._backend._ffi.cast("Cryptography_STACK_OF_POLICYINFO *", self._backend._lib.X509V3_EXT_d2i(ext)) assert cp != self._backend._ffi.NULL cp = self._backend._ffi.gc(cp, self._backend._lib.sk_POLICYINFO_free) num = self._backend._lib.sk_POLICYINFO_num(cp) certificate_policies = [] for i in range(num): qualifiers = None pi = self._backend._lib.sk_POLICYINFO_value(cp, i) oid = x509.ObjectIdentifier(_obj2txt(self._backend, pi.policyid)) if pi.qualifiers != self._backend._ffi.NULL: qnum = self._backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers) qualifiers = [] for j in range(qnum): pqi = self._backend._lib.sk_POLICYQUALINFO_value( pi.qualifiers, j) pqualid = x509.ObjectIdentifier( _obj2txt(self._backend, pqi.pqualid)) if pqualid == x509.OID_CPS_QUALIFIER: cpsuri = self._backend._ffi.buffer( pqi.d.cpsuri.data, pqi.d.cpsuri.length)[:].decode('ascii') qualifiers.append(cpsuri) elif pqualid == x509.OID_CPS_USER_NOTICE: user_notice = self._build_user_notice(pqi.d.usernotice) qualifiers.append(user_notice) certificate_policies.append(x509.PolicyInformation( oid, qualifiers)) return x509.CertificatePolicies(certificate_policies)
def _extensions(key: rsa.RSAPrivateKey, cert_opts: Dict) -> List[Tuple[bool, x509.Extension]]: # noqa sector = cert_opts['sector'] # certificate policies policies = [ x509.PolicyInformation(x509.ObjectIdentifier('1.3.76.16'), [x509.UserNotice(None, 'AgIDroot')]), x509.PolicyInformation(x509.ObjectIdentifier('1.3.76.16.6'), [x509.UserNotice(None, 'agIDcert')]), ] if sector == 'private': policies.append( x509.PolicyInformation(x509.ObjectIdentifier('1.3.76.16.4.3.1'), [x509.UserNotice(None, 'cert_SP_Priv')])) elif sector == 'public': policies.append( x509.PolicyInformation(x509.ObjectIdentifier('1.3.76.16.4.2.1'), [x509.UserNotice(None, 'cert_SP_Pub')])) else: emsg = 'Invalid value for sector (%s)' % sector raise Exception(emsg) # extensions list return [ # basicCinstraints (False, x509.BasicConstraints(ca=False, path_length=None)), # keyUsage (True, x509.KeyUsage( digital_signature=True, content_commitment=True, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False, )), # certifcatePolicies (False, x509.CertificatePolicies(policies)), # subjectKeyIdentifier (False, x509.SubjectKeyIdentifier.from_public_key(key.public_key())), ]
def create(): """ Create :return: """ parent_id = request.args.get("parent") parent_cert = Certificate.objects( id=parent_id).first() if parent_id else None form_default_data = {} # @todo how to list meta fields # @todo rewrite with a intersect field_names = [ f.name for f in CreateCertificateForm() if hasattr(x509.NameOID, f.name) ] for field_name in field_names: # get default value from environment variables form_default_data[field_name] = os.environ.get(f"DEFAULT_{field_name}") # get default value from parent certificate if any if parent_cert: for item in parent_cert.cert.subject: if item.oid == getattr(x509.NameOID, field_name): form_default_data[field_name] = item.value if parent_cert: form = CreateCertificateForm( data={ **form_default_data, "duration": int(current_app.config.get("DEFAULT_DURATION")), "parent": parent_cert.cert.serial_number, "mode": { "is_server_auth": True, "is_client_auth": True, }, "policy": { "oid": current_app.config.get('DEFAULT_POLICY_OID'), "url": current_app.config.get('DEFAULT_POLICY_URL'), }, "crl": { "url": current_app.config.get("DEFAULT_CA_ISSUER_URL") + url_for("repository.download", id=parent_cert.id, file_format="crl"), }, "aia": { "enabled": True, 'ca_issuers': current_app.config.get("DEFAULT_CA_ISSUER_URL") + url_for( "repository.download", id=parent_id, file_format="crt"), 'ocsp': current_app.config.get("DEFAULT_OCSP_URL") } }) else: form = CreateCertificateForm( data={ **form_default_data, "duration": 10 * int(current_app.config.get("DEFAULT_DURATION")), "parent": 0, "mode": { "is_ca": True, }, "policy": { "oid": current_app.config.get('DEFAULT_POLICY_OID'), "url": current_app.config.get('DEFAULT_POLICY_URL'), }, "aia": { "enabled": False, 'ca_issuers': current_app.config.get( "DEFAULT_CA_ISSUER_URL"), 'ocsp': current_app.config.get("DEFAULT_OCSP_URL") } }) if form.validate_on_submit(): # key key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) # serial number serial_number = x509.random_serial_number() # subject names = [] for field_name in field_names: if getattr(form, field_name).data: names.append( x509.NameAttribute(getattr(x509.NameOID, field_name), getattr(form, field_name).data)) subject = x509.Name(names) # issuer and signing key issuer = parent_cert.cert.subject if parent_cert else subject signing_key = parent_cert.key if parent_cert else key cert = x509.CertificateBuilder( ).subject_name(subject).issuer_name(issuer).public_key( key.public_key()).serial_number(serial_number).not_valid_before( datetime.utcnow()).not_valid_after( datetime.utcnow() + timedelta(days=int(form.duration.data))).add_extension( x509.SubjectKeyIdentifier.from_public_key( key.public_key()), critical=False).add_extension( x509.AuthorityKeyIdentifier.from_issuer_public_key( signing_key.public_key()), critical=False) # basic constraints cert = cert.add_extension(x509.BasicConstraints( form.mode.data.get('is_ca'), None), critical=False) # ocsp if form.aia.data.get("enabled"): cert = cert.add_extension( x509.AuthorityInformationAccess([ # openssl x509 -in 95285781730451486911577519787958288522332983584.crt -ocsp_uri -noout x509.AccessDescription( x509.OID_OCSP, x509.UniformResourceIdentifier( form.aia.data.get('ocsp'))), # @todo validate issuers x509.AccessDescription( x509.OID_CA_ISSUERS, x509.UniformResourceIdentifier( form.aia.data.get('ca_issuers'))) ]), critical=False) # key_usage if form.mode.data.get('is_ca'): cert = cert.add_extension( x509.KeyUsage( # 数字签名 digital_signature=True, # 认可签名 content_commitment=False, # 秘钥加密 key_encipherment=False, # 数据加密 data_encipherment=False, # 秘钥协商 key_agreement=False, # 证书签名 key_cert_sign=True, # CRL 签名 crl_sign=True, # 仅加密 encipher_only=False, # 仅解密 decipher_only=False, ), critical=True) else: cert = cert.add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=True, data_encipherment=True, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=True) # extended_key_usage # @todo render all extended key usage extended_key_usage = [] if form.mode.data.get('is_server_auth'): extended_key_usage.append(x509.oid.ExtendedKeyUsageOID.SERVER_AUTH) if form.mode.data.get('is_client_auth'): extended_key_usage.append(x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH) if len(extended_key_usage): cert = cert.add_extension( x509.ExtendedKeyUsage(extended_key_usage), critical=False) # subject alternative name san = [] for item in form.san.data.split("\n"): if item: try: ipaddress = IPv4Address(item) san.append(x509.IPAddress(ipaddress)) except Exception as e: logger.info(e) san.append(x509.DNSName(item)) if len(san): cert = cert.add_extension(x509.SubjectAlternativeName(san), critical=False) # certificate policies if form.policy.data.get('url') and form.policy.data.get('oid'): cert = cert.add_extension(x509.CertificatePolicies([ x509.PolicyInformation( x509.ObjectIdentifier(form.policy.data.get('oid')), [form.policy.data.get('url')], ) ]), critical=False) # crl distribution points if form.crl.data.get('url'): cert = cert.add_extension(x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[ x509.UniformResourceIdentifier( form.crl.data.get('url')) ], relative_name=None, reasons=None, crl_issuer=None, ) ]), critical=False) # sign cert = cert.sign(signing_key, hashes.SHA256(), default_backend()) # save c = Certificate(key=key, cert=cert, serial_number=str(serial_number), pid=parent_cert.id if parent_cert else None) c.save() flash(f"Certificate create successful") return redirect(url_for(".home")) return render_template("create.html", form=form)
def create_smartcard_cert(self, issuer_private_key, private_key, outpath=None): subject_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "My Company"), (NameOID.COMMON_NAME, "*****@*****.**"), ] issuer_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "My Company"), (NameOID.COMMON_NAME, "My Company Name"), ] not_before = datetime.datetime.fromisoformat("2016-08-08 15:15:15") not_after = datetime.datetime.fromisoformat("2025-08-08 15:15:15") serial_number = x509.random_serial_number() aia_descriptions = [ x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier( "http://sub.mycompany.com/sica.crt")), ] eku_usages = [ x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH, x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION, x509.ObjectIdentifier( "1.3.6.1.4.1.311.20.2.2" ), # smartcardLogon (Microsoft enhanced key usage) ] # basic constraints basic_constraints_ca = False basic_constraints_pathlen = None certificate_policies = [ x509.PolicyInformation( x509.oid.CertificatePoliciesOID.CPS_QUALIFIER, ["http://www.mycompany.com/cp/"]), ] # key usage: Signing only key_usages = [True] + 8 * [False] # subject alternative names subject_alternative_names = [ x509.DNSName("*****@*****.**"), ] crl_distribution_points = [ x509.DistributionPoint([ x509.UniformResourceIdentifier( "http://crl.mycompany.com/sica.crl") ], None, None, None) ] extensions = [ (x509.AuthorityInformationAccess(descriptions=aia_descriptions), False), (x509.SubjectKeyIdentifier.from_public_key( private_key.public_key()), False), (x509.BasicConstraints(basic_constraints_ca, basic_constraints_pathlen), True), (x509.AuthorityKeyIdentifier.from_issuer_public_key( issuer_private_key.public_key()), False), (x509.CertificatePolicies(policies=certificate_policies), False), (x509.CRLDistributionPoints(crl_distribution_points), False), (x509.KeyUsage(*key_usages), True), (x509.ExtendedKeyUsage(usages=eku_usages), False), (x509.SubjectAlternativeName(subject_alternative_names), False), ] cert = self.create_cert( not_before, not_after, serial_number, issuer_private_key, # use issuer private key to sign => do not self sign private_key, subject_entries=subject_entries, issuer_entries=issuer_entries, extensions=extensions) self.save_to_pem(cert, outpath) return cert
def create_sica_cert(self, issuer_private_key, private_key, outpath=None, ocsp_signing_eku=True): subject_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "My Company"), (NameOID.COMMON_NAME, "My Company Name"), ] issuer_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "Intermediate CA"), (NameOID.COMMON_NAME, "Intermediate CA Name"), ] not_before = datetime.datetime.fromisoformat("2016-02-02 10:10:10") not_after = datetime.datetime.fromisoformat("2022-02-02 10:10:10") serial_number = x509.random_serial_number() aia_descriptions = [ x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier( "http://sub.rootca.com/ica.crt")), x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier("http://ica.ocsp.rootca.com")), ] eku_usages = [ x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH, x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION, x509.ObjectIdentifier( "1.3.6.1.4.1.311.20.2.2" ), # smartcardLogon (Microsoft enhanced key usage) ] if ocsp_signing_eku: eku_usages.append(x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING) # basic constraints basic_constraints_ca = True basic_constraints_pathlen = 0 certificate_policies = [ x509.PolicyInformation( x509.oid.CertificatePoliciesOID.CPS_QUALIFIER, ["http://www.rootca.com/repo"]), x509.PolicyInformation( x509.oid.CertificatePoliciesOID.CPS_QUALIFIER, ["http://www.mycompany.com/cp/"]), ] crl_distribution_points = [ x509.DistributionPoint([ x509.UniformResourceIdentifier("http://crl.rootca.com/ica.crl") ], None, None, None) ] # key usage: Certificate Sign and CRL Sign set to True, rest set to False key_usages = 5 * [False] + 2 * [True] + 2 * [False] extensions = [ (x509.AuthorityInformationAccess(descriptions=aia_descriptions), False), (x509.SubjectKeyIdentifier.from_public_key( private_key.public_key()), False), (x509.BasicConstraints(basic_constraints_ca, basic_constraints_pathlen), True), (x509.AuthorityKeyIdentifier.from_issuer_public_key( issuer_private_key.public_key()), False), (x509.CertificatePolicies(policies=certificate_policies), False), (x509.CRLDistributionPoints(crl_distribution_points), False), (x509.KeyUsage(*key_usages), True), (x509.ExtendedKeyUsage(usages=eku_usages), False), ] cert = self.create_cert( not_before, not_after, serial_number, issuer_private_key, # use issuer private key to sign => do not self sign private_key, subject_entries=subject_entries, issuer_entries=issuer_entries, extensions=extensions) self.save_to_pem(cert, outpath) return cert
def create_ica_cert(self, issuer_private_key, private_key, outpath=None): subject_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "Intermediate CA"), (NameOID.COMMON_NAME, "Intermediate CA Name"), ] issuer_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "Root CA"), (NameOID.COMMON_NAME, "Root CA Name"), ] not_before = datetime.datetime.fromisoformat("2016-01-01 11:11:11") not_after = datetime.datetime.fromisoformat("2031-01-01 11:11:11") serial_number = x509.random_serial_number() aia_descriptions = [ x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier("http://ocsp.rootca.com")), x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier( "http://sub.rootca.com/rca.crt")), ] # basic constraints basic_constraints_ca = True basic_constraints_pathlen = None certificate_policies = [ x509.PolicyInformation(x509.oid.CertificatePoliciesOID.ANY_POLICY, ["http://www.rootca.com/repo"]), ] crl_distribution_points = [ x509.DistributionPoint([ x509.UniformResourceIdentifier("http://crl.rootca.com/rca.crl") ], None, None, None) ] # key usage: Certificate Sign and CRL Sign set to True, rest set to False key_usages = 5 * [False] + 2 * [True] + 2 * [False] extensions = [ (x509.BasicConstraints(basic_constraints_ca, basic_constraints_pathlen), True), (x509.CertificatePolicies(policies=certificate_policies), False), (x509.AuthorityInformationAccess(descriptions=aia_descriptions), False), (x509.KeyUsage(*key_usages), True), (x509.AuthorityKeyIdentifier.from_issuer_public_key( issuer_private_key.public_key()), False), (x509.CRLDistributionPoints(crl_distribution_points), False), (x509.SubjectKeyIdentifier.from_public_key( private_key.public_key()), False), ] cert = self.create_cert(not_before, not_after, serial_number, issuer_private_key, private_key, subject_entries=subject_entries, issuer_entries=issuer_entries, extensions=extensions) self.save_to_pem(cert, outpath) return cert
class PolicyInformationTestCase(TestCaseMixin, TestCase): """Test PolicyInformation class.""" oid = "2.5.29.32.0" # various qualifiers q1 = "text1" q2 = x509.UserNotice(explicit_text="text2", notice_reference=None) q3 = x509.UserNotice(explicit_text=None, notice_reference=x509.NoticeReference( organization="text3", notice_numbers=[1])) q4 = "text4" q5 = x509.UserNotice( explicit_text="text5", notice_reference=x509.NoticeReference(organization="text6", notice_numbers=[1, 2, 3]), ) x1 = x509.PolicyInformation(policy_identifier=ObjectIdentifier(oid), policy_qualifiers=[q1]) x2 = x509.PolicyInformation( policy_identifier=ObjectIdentifier(oid), policy_qualifiers=[q2], ) x3 = x509.PolicyInformation( policy_identifier=ObjectIdentifier(oid), policy_qualifiers=[q3], ) x4 = x509.PolicyInformation( policy_identifier=ObjectIdentifier(oid), policy_qualifiers=[q4, q5], ) s1: ParsablePolicyInformation = { "policy_identifier": oid, "policy_qualifiers": ["text1"], } s2: ParsablePolicyInformation = { "policy_identifier": oid, "policy_qualifiers": [{ "explicit_text": "text2", }], } s3: ParsablePolicyInformation = { "policy_identifier": oid, "policy_qualifiers": [{ "notice_reference": { "organization": "text3", "notice_numbers": [ 1, ], } }], } s4: ParsablePolicyInformation = { "policy_identifier": oid, "policy_qualifiers": [ "text4", { "explicit_text": "text5", "notice_reference": { "organization": "text6", "notice_numbers": [1, 2, 3], }, }, ], } s5: ParsablePolicyInformation = { "policy_identifier": oid, "policy_qualifiers": [{ "explicit_text": "text5", "notice_reference": { "notice_numbers": [1, 2, 3], }, }], } def setUp(self) -> None: super().setUp() self.pi1 = PolicyInformation(self.s1) self.pi2 = PolicyInformation(self.s2) self.pi3 = PolicyInformation(self.s3) self.pi4 = PolicyInformation(self.s4) self.pi5 = PolicyInformation(self.s5) self.pi_empty = PolicyInformation() def test_append(self) -> None: """Test PolicyInformation.append().""" self.pi1.append(self.q2) self.pi1.append(self.s3["policy_qualifiers"][0]) self.assertEqual( self.pi1, PolicyInformation({ "policy_identifier": self.oid, "policy_qualifiers": [self.q1, self.q2, self.q3], }), ) self.pi_empty.policy_identifier = self.oid self.pi_empty.append(self.q3) self.assertEqual(self.pi3, self.pi_empty) def test_as_text(self) -> None: """Test as_text().""" self.assertEqual( self.pi1.as_text(), "Policy Identifier: 2.5.29.32.0\n" "Policy Qualifiers:\n* text1") self.assertEqual( self.pi2.as_text(), "Policy Identifier: 2.5.29.32.0\n" "Policy Qualifiers:\n" "* UserNotice:\n" " * Explicit text: text2", ) self.assertEqual( self.pi3.as_text(), "Policy Identifier: 2.5.29.32.0\n" "Policy Qualifiers:\n" "* UserNotice:\n" " * Reference:\n" " * Organiziation: text3\n" " * Notice Numbers: [1]", ) self.assertEqual( self.pi4.as_text(), "Policy Identifier: 2.5.29.32.0\n" "Policy Qualifiers:\n" "* text4\n" "* UserNotice:\n" " * Explicit text: text5\n" " * Reference:\n" " * Organiziation: text6\n" " * Notice Numbers: [1, 2, 3]", ) self.assertEqual(self.pi_empty.as_text(), "Policy Identifier: None\nNo Policy Qualifiers") self.load_named_cas("__all__") self.load_named_certs("__all__") for name, cert in list(self.new_cas.items()) + list( self.new_certs.items()): # type: ignore[arg-type] try: ext = cert.pub.loaded.extensions.get_extension_for_class( x509.CertificatePolicies).value except x509.ExtensionNotFound: continue for index, policy in enumerate(ext): pinfo = PolicyInformation(policy) self.assertEqual(pinfo.as_text(), certs[name]["policy_texts"][index]) def test_certs(self) -> None: """Test for all known certs.""" self.load_named_cas("__all__") self.load_named_certs("__all__") for cert in list(self.new_cas.values()) + list( self.new_certs.values()): # type: ignore[arg-type] try: val = cert.pub.loaded.extensions.get_extension_for_class( x509.CertificatePolicies).value except x509.ExtensionNotFound: continue for policy in val: pi1 = PolicyInformation(policy) self.assertEqual(pi1.for_extension_type, policy) # pass the serialized value to the constructor and see if it's still the same pi2 = PolicyInformation( typing.cast(ParsablePolicyInformation, pi1.serialize())) self.assertEqual(pi1, pi2) self.assertEqual(pi1.serialize(), pi2.serialize()) self.assertEqual(pi2.for_extension_type, policy) def test_clear(self) -> None: """Test PolicyInformation.clear().""" self.pi1.clear() self.assertIsNone(self.pi1.policy_qualifiers) def test_constructor(self) -> None: """Test some constructors that are otherwise not called.""" pinfo = PolicyInformation() self.assertIsNone(pinfo.policy_identifier) self.assertIsNone(pinfo.policy_qualifiers) pinfo = PolicyInformation({ "policy_identifier": "1.2.3", "policy_qualifiers": [ x509.UserNotice(notice_reference=None, explicit_text="foobar"), ], }) self.assertEqual(len(pinfo), 1) pinfo = PolicyInformation({ "policy_identifier": "1.2.3", "policy_qualifiers": [{ "notice_reference": x509.NoticeReference(organization="foobar", notice_numbers=[1]), }], }) self.assertEqual(len(pinfo), 1) def test_constructor_errors(self) -> None: """Test various invalid values for the constructor.""" # type ignores are because we're testing exactly that here with self.assertRaisesRegex( ValueError, r"^PolicyInformation data must be either x509.PolicyInformation or dict$" ): PolicyInformation(True) # type: ignore[arg-type] with self.assertRaisesRegex( ValueError, r"^PolicyQualifier must be string, dict or x509.UserNotice$"): PolicyInformation({ "policy_identifier": "1.2.3", "policy_qualifiers": [True] } # type: ignore[list-item] ) with self.assertRaisesRegex( ValueError, r"^NoticeReference must be either None, a dict or an x509.NoticeReference$" ): PolicyInformation({ "policy_identifier": "1.2.3", "policy_qualifiers": [{ "notice_reference": True, # type: ignore[typeddict-item] }], }) def test_contains(self) -> None: """Test PolicyInformation.contains().""" self.assertIn(self.q1, self.pi1) self.assertIn(self.q2, self.pi2) self.assertIn(self.q3, self.pi3) self.assertIn(self.q4, self.pi4) self.assertIn(self.q5, self.pi4) self.assertIn(self.s1["policy_qualifiers"][0], self.pi1) self.assertIn(self.s2["policy_qualifiers"][0], self.pi2) self.assertIn(self.s3["policy_qualifiers"][0], self.pi3) self.assertIn(self.s4["policy_qualifiers"][0], self.pi4) self.assertIn(self.s4["policy_qualifiers"][1], self.pi4) self.assertNotIn(self.q2, self.pi1) self.assertNotIn(self.q1, self.pi_empty) self.assertNotIn(self.s1["policy_qualifiers"][0], self.pi2) self.assertNotIn(self.s2["policy_qualifiers"][0], self.pi1) self.assertNotIn(self.s2["policy_qualifiers"][0], self.pi_empty) # Invalid values are always false: self.assertNotIn(True, self.pi1) def test_count(self) -> None: """Test PolicyInformation.count().""" self.assertEqual(self.pi1.count(self.s1["policy_qualifiers"][0]), 1) self.assertEqual(self.pi1.count(self.q1), 1) self.assertEqual(self.pi1.count(self.s2), 0) # type: ignore[arg-type] # full pi is wrong self.assertEqual(self.pi1.count(self.q2), 0) self.assertEqual(self.pi_empty.count(self.q2), 0) self.assertEqual( self.pi1.count(True), 0) # type: ignore[arg-type] # what we're testing here! def test_delitem(self) -> None: """Test item deletion (e.g. ``del pi[0]``).""" del self.pi1[0] self.pi_empty.policy_identifier = self.oid self.assertEqual(self.pi1, self.pi_empty) self.assertEqual(len(self.pi4), 2) del self.pi4[0] self.assertEqual(len(self.pi4), 1) with self.assertRaisesRegex(IndexError, r"^list assignment index out of range$"): del self.pi1[0] def test_extend(self) -> None: """Test PolicyInformation.extend().""" self.pi1.extend([self.q2, self.q4]) self.assertEqual( self.pi1, PolicyInformation({ "policy_identifier": self.oid, "policy_qualifiers": [self.q1, self.q2, self.q4], }), ) self.pi2.extend([self.s1["policy_qualifiers"][0]]) self.assertEqual( self.pi2, PolicyInformation({ "policy_identifier": self.oid, "policy_qualifiers": [self.q2, self.q1], }), ) # extend an empty list self.pi_empty.extend([self.s1["policy_qualifiers"][0]]) self.assertEqual( self.pi_empty, PolicyInformation({ "policy_identifier": None, "policy_qualifiers": [self.q1], }), ) def test_getitem(self) -> None: """Test item getter (e.g. ``x = ext[0]``).""" self.assertEqual(self.pi1[0], self.s1["policy_qualifiers"][0]) self.assertEqual(self.pi4[0], self.s4["policy_qualifiers"][0]) self.assertEqual(self.pi4[1], self.s4["policy_qualifiers"][1]) self.assertEqual(self.pi4[1:], [self.s4["policy_qualifiers"][1]]) with self.assertRaisesRegex(IndexError, r"^list index out of range$"): self.pi_empty[0] # pylint: disable=pointless-statement with self.assertRaisesRegex(IndexError, r"^list index out of range$"): self.pi_empty[2:] # pylint: disable=pointless-statement def test_hash(self) -> None: """Test hash().""" self.assertEqual(hash(self.pi1), hash(self.pi1)) self.assertEqual(hash(self.pi2), hash(self.pi2)) self.assertEqual(hash(self.pi3), hash(self.pi3)) self.assertEqual(hash(self.pi4), hash(self.pi4)) self.assertEqual(hash(self.pi5), hash(self.pi5)) self.assertEqual(hash(self.pi_empty), hash(self.pi_empty)) self.assertEqual(hash(self.pi1), hash(PolicyInformation(self.s1))) self.assertEqual(hash(self.pi2), hash(PolicyInformation(self.s2))) self.assertEqual(hash(self.pi3), hash(PolicyInformation(self.s3))) self.assertEqual(hash(self.pi4), hash(PolicyInformation(self.s4))) self.assertEqual(hash(self.pi5), hash(PolicyInformation(self.s5))) self.assertEqual(hash(self.pi_empty), hash(PolicyInformation())) self.assertNotEqual(hash(self.pi1), hash(self.pi2)) self.assertNotEqual(hash(self.pi1), hash(self.pi3)) self.assertNotEqual(hash(self.pi1), hash(self.pi4)) self.assertNotEqual(hash(self.pi2), hash(self.pi3)) self.assertNotEqual(hash(self.pi2), hash(self.pi4)) self.assertNotEqual(hash(self.pi3), hash(self.pi4)) def test_insert(self) -> None: """Test PolicyInformation.insert().""" self.pi1.insert(0, self.q2) self.assertEqual( self.pi1, PolicyInformation({ "policy_identifier": self.oid, "policy_qualifiers": [self.q2, self.q1], }), ) self.pi1.insert(1, self.s3["policy_qualifiers"][0]) self.assertEqual( self.pi1, PolicyInformation({ "policy_identifier": self.oid, "policy_qualifiers": [self.q2, self.q3, self.q1], }), ) self.pi_empty.insert(1, self.q2) self.pi_empty.policy_identifier = self.oid self.assertEqual(self.pi2, self.pi_empty) def test_iter(self) -> None: """Test iter(pi).""" self.assertEqual(list(self.pi1), [self.q1]) self.assertEqual(list(self.pi2), [self.q2]) self.assertEqual(list(self.pi4), [self.q4, self.q5]) self.assertEqual(list(self.pi_empty), []) def test_len(self) -> None: """Test len(ext).""" self.assertEqual(len(self.pi1), 1) self.assertEqual(len(self.pi2), 1) self.assertEqual(len(self.pi3), 1) self.assertEqual(len(self.pi4), 2) self.assertEqual(len(self.pi5), 1) self.assertEqual(len(self.pi_empty), 0) def test_policy_identifier_setter(self) -> None: """Test setting a policy identifier.""" value = "1.2.3" expected = ObjectIdentifier(value) pinfo = PolicyInformation({ "policy_identifier": value, "policy_qualifiers": [] }) pinfo.policy_identifier = value self.assertEqual(pinfo.policy_identifier, expected) pinfo = PolicyInformation({"policy_identifier": expected}) self.assertEqual(pinfo.policy_identifier, expected) new_value = "2.3.4" new_expected = ObjectIdentifier(new_value) pinfo.policy_identifier = new_value self.assertEqual(pinfo.policy_identifier, new_expected) def test_pop(self) -> None: """Test PolicyInformation.pop().""" self.pi_empty.policy_identifier = self.oid self.assertEqual(self.pi1.pop(), self.s1["policy_qualifiers"][0]) self.assertEqual(self.pi1, self.pi_empty) self.assertEqual(self.pi4.pop(1), self.s4["policy_qualifiers"][1]) self.assertEqual( self.pi4, PolicyInformation({ "policy_identifier": self.oid, "policy_qualifiers": [self.q4], }), ) self.assertEqual(self.pi4.pop(), self.s4["policy_qualifiers"][0]) self.assertEqual(self.pi4, self.pi_empty) with self.assertRaisesRegex(IndexError, r"^pop from empty list$"): self.pi_empty.pop() def test_remove(self) -> None: """Test PolicyInformation.remove().""" self.pi_empty.policy_identifier = self.oid self.pi1.remove(self.q1) self.assertEqual(self.pi1, self.pi_empty) self.pi2.remove(self.s2["policy_qualifiers"][0]) self.assertEqual(self.pi1, self.pi_empty) self.pi4.remove(self.q4) self.assertEqual( self.pi4, PolicyInformation({ "policy_identifier": self.oid, "policy_qualifiers": [self.q5], }), ) with self.assertRaisesRegex(ValueError, r"^.*: not in list\.$"): self.pi_empty.remove(self.s3["policy_qualifiers"][0]) def _test_repr(self, func: typing.Callable[[typing.Any], str]) -> None: self.assertEqual( func(self.pi1), "<PolicyInformation(oid=2.5.29.32.0, qualifiers=['text1'])>") self.assertEqual( func(self.pi2), "<PolicyInformation(oid=2.5.29.32.0, qualifiers=[{'explicit_text': 'text2'}])>" ) self.assertEqual(func(self.pi_empty), "<PolicyInformation(oid=None, qualifiers=None)>") # NOTE: order of dict is different here, so we do not test output, just make sure there's no exception func(self.pi3) func(self.pi4) def test_repr(self) -> None: """Test repr().""" self._test_repr(repr) def test_serialize(self) -> None: """Test serialization.""" self.assertEqual(self.pi1.serialize(), { "policy_identifier": "2.5.29.32.0", "policy_qualifiers": ["text1"] }) self.assertEqual( self.pi2.serialize(), { "policy_identifier": "2.5.29.32.0", "policy_qualifiers": [{ "explicit_text": "text2" }] }, ) self.assertEqual( self.pi3.serialize(), { "policy_identifier": "2.5.29.32.0", "policy_qualifiers": [{ "notice_reference": { "notice_numbers": [1], "organization": "text3" } }], }, ) self.assertEqual( self.pi4.serialize(), { "policy_identifier": "2.5.29.32.0", "policy_qualifiers": [ "text4", { "explicit_text": "text5", "notice_reference": { "notice_numbers": [1, 2, 3], "organization": "text6" }, }, ], }, ) self.assertEqual( self.pi5.serialize(), { "policy_identifier": "2.5.29.32.0", "policy_qualifiers": [{ "explicit_text": "text5", "notice_reference": { "notice_numbers": [1, 2, 3] } }], }, ) def test_setitem(self) -> None: """Test __setitem__().""" # pylint: disable=invalid-name; let's just use pi here pi = PolicyInformation(self.s1) self.assertEqual(pi, self.pi1) pi[0] = self.q2 self.assertEqual(pi, self.pi2) pi = PolicyInformation(self.s1) pi[0:1] = [self.q2] self.assertEqual(pi, self.pi2) # list()[0:1] = "x" also works pi = PolicyInformation({"policy_identifier": "2.5.29.32.0"}) pi[0:1] = [self.q2] self.assertEqual(pi, self.pi2) pi = PolicyInformation() with self.assertRaisesRegex(ValueError, r"^Index out of range$"): pi[0] = self.q1 self.assertEqual(len(pi), 0) pi = PolicyInformation(self.s1) with self.assertRaisesRegex( ValueError, r"^PolicyQualifier must be string, dict or x509.UserNotice$"): pi[0] = True # type: ignore[assignment] self.assertEqual(pi, self.pi1) pi = PolicyInformation(self.s1) with self.assertRaisesRegex( TypeError, r"^bar/%s: Invalid key/value type$" % self.q1): pi["bar"] = self.q1 # type: ignore[index] self.assertEqual(pi, self.pi1) def test_str(self) -> None: """Test str().""" self._test_repr(str)
def for_extension_type(self) -> x509.PolicyInformation: """Convert instance to a suitable cryptography class.""" return x509.PolicyInformation(policy_identifier=self.policy_identifier, policy_qualifiers=self.policy_qualifiers)
class PolicyInformationTestCase(DjangoCATestCase): """Test PolicyInformation class.""" oid = '2.5.29.32.0' # various qualifiers q1 = 'text1' q2 = x509.UserNotice(explicit_text='text2', notice_reference=None) q3 = x509.UserNotice(explicit_text=None, notice_reference=x509.NoticeReference( organization='text3', notice_numbers=[1])) q4 = 'text4' q5 = x509.UserNotice(explicit_text='text5', notice_reference=x509.NoticeReference( organization='text6', notice_numbers=[1, 2, 3])) x1 = x509.PolicyInformation(policy_identifier=ObjectIdentifier(oid), policy_qualifiers=[q1]) x2 = x509.PolicyInformation( policy_identifier=ObjectIdentifier(oid), policy_qualifiers=[q2], ) x3 = x509.PolicyInformation( policy_identifier=ObjectIdentifier(oid), policy_qualifiers=[q3], ) x4 = x509.PolicyInformation( policy_identifier=ObjectIdentifier(oid), policy_qualifiers=[q4, q5], ) s1 = { 'policy_identifier': oid, 'policy_qualifiers': ['text1'], } s2 = { 'policy_identifier': oid, 'policy_qualifiers': [{ 'explicit_text': 'text2', }], } s3 = { 'policy_identifier': oid, 'policy_qualifiers': [{ 'notice_reference': { 'organization': 'text3', 'notice_numbers': [ 1, ], } }], } s4 = { 'policy_identifier': oid, 'policy_qualifiers': [ 'text4', { 'explicit_text': 'text5', 'notice_reference': { 'organization': 'text6', 'notice_numbers': [1, 2, 3], } } ], } def setUp(self): super().setUp() self.pi1 = PolicyInformation(self.s1) self.pi2 = PolicyInformation(self.s2) self.pi3 = PolicyInformation(self.s3) self.pi4 = PolicyInformation(self.s4) self.pi_empty = PolicyInformation() def test_append(self): """Test PolicyInformation.append().""" self.pi1.append(self.q2) self.pi1.append(self.s3['policy_qualifiers'][0]) self.assertEqual( self.pi1, PolicyInformation({ 'policy_identifier': self.oid, 'policy_qualifiers': [self.q1, self.q2, self.q3], })) self.pi_empty.policy_identifier = self.oid self.pi_empty.append(self.q3) self.assertEqual(self.pi3, self.pi_empty) def test_as_text(self): """Test as_text().""" self.assertEqual( self.pi1.as_text(), 'Policy Identifier: 2.5.29.32.0\n' 'Policy Qualifiers:\n* text1') self.assertEqual( self.pi2.as_text(), 'Policy Identifier: 2.5.29.32.0\n' 'Policy Qualifiers:\n' '* UserNotice:\n' ' * Explicit text: text2') self.assertEqual( self.pi3.as_text(), 'Policy Identifier: 2.5.29.32.0\n' 'Policy Qualifiers:\n' '* UserNotice:\n' ' * Reference:\n' ' * Organiziation: text3\n' ' * Notice Numbers: [1]') self.assertEqual( self.pi4.as_text(), 'Policy Identifier: 2.5.29.32.0\n' 'Policy Qualifiers:\n' '* text4\n' '* UserNotice:\n' ' * Explicit text: text5\n' ' * Reference:\n' ' * Organiziation: text6\n' ' * Notice Numbers: [1, 2, 3]') self.assertEqual(self.pi_empty.as_text(), 'Policy Identifier: None\nNo Policy Qualifiers') self.load_all_cas() self.load_all_certs() for name, cert in list(self.cas.items()) + list(self.certs.items()): try: ext = cert.x509.extensions.get_extension_for_oid( ExtensionOID.CERTIFICATE_POLICIES).value except x509.ExtensionNotFound: continue for index, policy in enumerate(ext): pinfo = PolicyInformation(policy) self.assertEqual(pinfo.as_text(), certs[name]['policy_texts'][index]) def test_certs(self): """Test for all known certs.""" self.load_all_cas() self.load_all_certs() for _name, cert in list(self.cas.items()) + list(self.certs.items()): try: val = cert.x509.extensions.get_extension_for_oid( ExtensionOID.CERTIFICATE_POLICIES).value except x509.ExtensionNotFound: continue for policy in val: pi1 = PolicyInformation(policy) self.assertEqual(pi1.for_extension_type, policy) # pass the serialized value to the constructor and see if it's still the same pi2 = PolicyInformation(pi1.serialize()) self.assertEqual(pi1, pi2) self.assertEqual(pi1.serialize(), pi2.serialize()) self.assertEqual(pi2.for_extension_type, policy) def test_clear(self): """Test PolicyInformation.clear().""" self.pi1.clear() self.assertIsNone(self.pi1.policy_qualifiers) def test_constructor(self): """Test some constructors that are otherwise not called.""" pinfo = PolicyInformation() self.assertIsNone(pinfo.policy_identifier) self.assertIsNone(pinfo.policy_qualifiers) pinfo = PolicyInformation({ 'policy_identifier': '1.2.3', 'policy_qualifiers': [ x509.UserNotice(notice_reference=None, explicit_text='foobar'), ], }) self.assertEqual(len(pinfo), 1) pinfo = PolicyInformation({ 'policy_identifier': '1.2.3', 'policy_qualifiers': [{ 'notice_reference': x509.NoticeReference(organization='foobar', notice_numbers=[1]), }], }) self.assertEqual(len(pinfo), 1) def test_constructor_errors(self): """Test various invalid values for the constructor.""" with self.assertRaisesRegex( ValueError, r'^PolicyInformation data must be either x509.PolicyInformation or dict$' ): PolicyInformation(True) with self.assertRaisesRegex( ValueError, r'^PolicyQualifier must be string, dict or x509.UserNotice$'): PolicyInformation({ 'policy_identifier': '1.2.3', 'policy_qualifiers': [True] }) with self.assertRaisesRegex( ValueError, r'^NoticeReference must be either None, a dict or an x509.NoticeReference$' ): PolicyInformation({ 'policy_identifier': '1.2.3', 'policy_qualifiers': [{ 'notice_reference': True, }] }) def test_contains(self): """Test PolicyInformation.contains().""" self.assertIn(self.q1, self.pi1) self.assertIn(self.q2, self.pi2) self.assertIn(self.q3, self.pi3) self.assertIn(self.q4, self.pi4) self.assertIn(self.q5, self.pi4) self.assertIn(self.s1['policy_qualifiers'][0], self.pi1) self.assertIn(self.s2['policy_qualifiers'][0], self.pi2) self.assertIn(self.s3['policy_qualifiers'][0], self.pi3) self.assertIn(self.s4['policy_qualifiers'][0], self.pi4) self.assertIn(self.s4['policy_qualifiers'][1], self.pi4) self.assertNotIn(self.q2, self.pi1) self.assertNotIn(self.q1, self.pi_empty) self.assertNotIn(self.s1['policy_qualifiers'][0], self.pi2) self.assertNotIn(self.s2['policy_qualifiers'][0], self.pi1) self.assertNotIn(self.s2['policy_qualifiers'][0], self.pi_empty) def test_count(self): """Test PolicyInformation.count().""" self.assertEqual(self.pi1.count(self.s1['policy_qualifiers'][0]), 1) self.assertEqual(self.pi1.count(self.q1), 1) self.assertEqual(self.pi1.count(self.s2), 0) self.assertEqual(self.pi1.count(self.q2), 0) self.assertEqual(self.pi_empty.count(self.q2), 0) self.assertEqual(self.pi1.count(True), 0) # pass an unparseable value def test_delitem(self): """Test item deletion (e.g. ``del pi[0]``).""" del self.pi1[0] self.pi_empty.policy_identifier = self.oid self.assertEqual(self.pi1, self.pi_empty) self.assertEqual(len(self.pi4), 2) del self.pi4[0] self.assertEqual(len(self.pi4), 1) with self.assertRaisesRegex(IndexError, r'^list assignment index out of range$'): del self.pi1[0] def test_extend(self): """Test PolicyInformation.extend().""" self.pi1.extend([self.q2, self.q4]) self.assertEqual( self.pi1, PolicyInformation({ 'policy_identifier': self.oid, 'policy_qualifiers': [self.q1, self.q2, self.q4], })) self.pi2.extend([self.s1['policy_qualifiers'][0]]) self.assertEqual( self.pi2, PolicyInformation({ 'policy_identifier': self.oid, 'policy_qualifiers': [self.q2, self.q1], })) # extend an empty list self.pi_empty.extend([self.s1['policy_qualifiers'][0]]) self.assertEqual( self.pi_empty, PolicyInformation({ 'policy_identifier': None, 'policy_qualifiers': [self.q1], })) def test_getitem(self): """Test item getter (e.g. ``x = ext[0]``).""" self.assertEqual(self.pi1[0], self.s1['policy_qualifiers'][0]) self.assertEqual(self.pi4[0], self.s4['policy_qualifiers'][0]) self.assertEqual(self.pi4[1], self.s4['policy_qualifiers'][1]) self.assertEqual(self.pi4[1:], [self.s4['policy_qualifiers'][1]]) with self.assertRaisesRegex(IndexError, r'^list index out of range$'): self.pi_empty[0] # pylint: disable=pointless-statement with self.assertRaisesRegex(IndexError, r'^list index out of range$'): self.pi_empty[2:] # pylint: disable=pointless-statement def test_hash(self): """Test hash().""" self.assertEqual(hash(self.pi1), hash(self.pi1)) self.assertEqual(hash(self.pi2), hash(self.pi2)) self.assertEqual(hash(self.pi3), hash(self.pi3)) self.assertEqual(hash(self.pi4), hash(self.pi4)) self.assertEqual(hash(self.pi_empty), hash(self.pi_empty)) self.assertEqual(hash(self.pi1), hash(PolicyInformation(self.s1))) self.assertEqual(hash(self.pi2), hash(PolicyInformation(self.s2))) self.assertEqual(hash(self.pi3), hash(PolicyInformation(self.s3))) self.assertEqual(hash(self.pi4), hash(PolicyInformation(self.s4))) self.assertEqual(hash(self.pi_empty), hash(PolicyInformation())) self.assertNotEqual(hash(self.pi1), hash(self.pi2)) self.assertNotEqual(hash(self.pi1), hash(self.pi3)) self.assertNotEqual(hash(self.pi1), hash(self.pi4)) self.assertNotEqual(hash(self.pi2), hash(self.pi3)) self.assertNotEqual(hash(self.pi2), hash(self.pi4)) self.assertNotEqual(hash(self.pi3), hash(self.pi4)) def test_insert(self): """Test PolicyInformation.insert().""" self.pi1.insert(0, self.q2) self.assertEqual( self.pi1, PolicyInformation({ 'policy_identifier': self.oid, 'policy_qualifiers': [self.q2, self.q1], })) self.pi1.insert(1, self.s3['policy_qualifiers'][0]) self.assertEqual( self.pi1, PolicyInformation({ 'policy_identifier': self.oid, 'policy_qualifiers': [self.q2, self.q3, self.q1], })) self.pi_empty.insert(1, self.q2) self.pi_empty.policy_identifier = self.oid self.assertEqual(self.pi2, self.pi_empty) def test_len(self): """Test len(ext).""" self.assertEqual(len(self.pi1), 1) self.assertEqual(len(self.pi2), 1) self.assertEqual(len(self.pi3), 1) self.assertEqual(len(self.pi4), 2) self.assertEqual(len(self.pi_empty), 0) def test_policy_identifier_setter(self): """Test setting a policy identifier.""" value = '1.2.3' expected = ObjectIdentifier(value) pinfo = PolicyInformation({'policy_identifier': value}) pinfo.policy_identifier = value self.assertEqual(pinfo.policy_identifier, expected) pinfo = PolicyInformation({'policy_identifier': expected}) self.assertEqual(pinfo.policy_identifier, expected) new_value = '2.3.4' new_expected = ObjectIdentifier(new_value) pinfo.policy_identifier = new_value self.assertEqual(pinfo.policy_identifier, new_expected) def test_pop(self): """Test PolicyInformation.pop().""" self.pi_empty.policy_identifier = self.oid self.assertEqual(self.pi1.pop(), self.s1['policy_qualifiers'][0]) self.assertEqual(self.pi1, self.pi_empty) self.assertEqual(self.pi4.pop(1), self.s4['policy_qualifiers'][1]) self.assertEqual( self.pi4, PolicyInformation({ 'policy_identifier': self.oid, 'policy_qualifiers': [self.q4], })) self.assertEqual(self.pi4.pop(), self.s4['policy_qualifiers'][0]) self.assertEqual(self.pi4, self.pi_empty) with self.assertRaisesRegex(IndexError, r'^pop from empty list$'): self.pi_empty.pop() def test_remove(self): """Test PolicyInformation.remove().""" self.pi_empty.policy_identifier = self.oid self.pi1.remove(self.q1) self.assertEqual(self.pi1, self.pi_empty) self.pi2.remove(self.s2['policy_qualifiers'][0]) self.assertEqual(self.pi1, self.pi_empty) self.pi4.remove(self.q4) self.assertEqual( self.pi4, PolicyInformation({ 'policy_identifier': self.oid, 'policy_qualifiers': [self.q5], })) with self.assertRaisesRegex(ValueError, r'^.*: not in list\.$'): self.pi_empty.remove(self.s3['policy_qualifiers'][0]) def _test_repr(self, func): self.assertEqual( func(self.pi1), "<PolicyInformation(oid=2.5.29.32.0, qualifiers=['text1'])>") self.assertEqual( func(self.pi2), "<PolicyInformation(oid=2.5.29.32.0, qualifiers=[{'explicit_text': 'text2'}])>" ) self.assertEqual(func(self.pi_empty), "<PolicyInformation(oid=None, qualifiers=None)>") # NOTE: order of dict is different here, so we do not test output, just make sure there's no exception func(self.pi3) func(self.pi4) def test_repr(self): """Test repr().""" self._test_repr(repr) def test_str(self): """Test str().""" self._test_repr(str)