def test_certvalidator_with_params(self):

        cert = self._load_nist_cert('ValidPolicyMappingTest12EE.crt')
        ca_certs = [self._load_nist_cert('TrustAnchorRootCertificate.crt')]
        other_certs = [self._load_nist_cert('P12Mapping1to3CACert.crt')]

        context = ValidationContext(trust_roots=ca_certs,
                                    other_certs=other_certs,
                                    revocation_mode="soft-fail",
                                    weak_hash_algos={'md2', 'md5'})

        validator = CertificateValidator(
            cert,
            validation_context=context,
            pkix_params=PKIXValidationParams(user_initial_policy_set=frozenset(
                ['2.16.840.1.101.3.2.1.48.1'])))
        path = validator.validate_usage(key_usage={'digital_signature'})

        # check if we got the right policy processing
        # (i.e. if our params got through)
        qps = path.qualified_policies()

        qp, = qps
        self.assertEqual(1, len(qp.qualifiers))
        qual_obj, = qp.qualifiers
        self.assertEqual(qual_obj['policy_qualifier_id'].native, 'user_notice')
        self.assertEqual(
            qual_obj['qualifier']['explicit_text'].native,
            'q7:  This is the user notice from qualifier 7 associated with '
            'NIST-test-policy-3.  This user notice should be displayed '
            'when  NIST-test-policy-1 is in the user-constrained-policy-set')
Exemple #2
0
def test_cert_constraint_composite(requests_mock):
    vc = live_testing_vc(requests_mock)
    signer_validation_path = CertificateValidator(
        FROM_CA.signing_cert, FROM_CA.cert_registry, validation_context=vc
    ).validate_usage(set())
    tsa_validation_path = CertificateValidator(
        DUMMY_TS.tsa_cert, FROM_CA.cert_registry, validation_context=vc
    ).validate_usage(set())

    from asn1crypto import x509
    scc = fields.SigCertConstraints(
        flags=fields.SigCertConstraintFlags.ISSUER | fields.SigCertConstraintFlags.SUBJECT_DN,
        issuers=[INTERM_CERT],
        subject_dn=x509.Name.build(
            {'common_name': 'Alice', 'country_name': 'BE'}
        )
    )
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)

    from asn1crypto import x509
    scc = fields.SigCertConstraints(
        flags=fields.SigCertConstraintFlags.ISSUER | fields.SigCertConstraintFlags.SUBJECT_DN,
        issuers=[INTERM_CERT],
        subject_dn=x509.Name.build(
            {'common_name': 'Alice & Bob', 'country_name': 'BE'}
        )
    )
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    def test_basic_certificate_validator_tls(self):
        cert = self._load_cert_object('mozilla.org.crt')
        other_certs = [
            self._load_cert_object('digicert-sha2-secure-server-ca.crt')
        ]

        moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        validator.validate_tls('www.mozilla.org')
 def test_self_signed_with_policy(self):
     # tests whether a corner case in the policy validation logic when the
     # path length is zero is handled gracefully
     cert = self._load_cert_object('self-signed-with-policy.crt')
     context = ValidationContext(trust_roots=[cert], allow_fetching=False)
     validator = CertificateValidator(cert, validation_context=context)
     path = validator.validate_usage({'digital_signature'})
     qp, = path.qualified_policies()
     # Note: the cert declares a concrete policy, but for the purposes
     # of PKIX validation, any policy is valid, since we're validating
     # a self-signed certificate (so everything breaks down anyway)
     self.assertEqual(qp.user_domain_policy_id, 'any_policy')
     self.assertEqual(qp.issuer_domain_policy_id, 'any_policy')
    def test_basic_certificate_validator_tls_invalid_key_usage(self):
        cert = self._load_cert_object('mozilla.org.crt')
        other_certs = [
            self._load_cert_object('digicert-sha2-secure-server-ca.crt')
        ]

        moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        with self.assertRaisesRegex(PathValidationError, 'for the purpose'):
            validator.validate_usage(set(['crl_sign']))
    def test_basic_certificate_validator_tls_invalid_hostname(self):
        cert = self._load_cert_object('mozilla.org.crt')
        other_certs = [
            self._load_cert_object('digicert-sha2-secure-server-ca.crt')
        ]

        moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        with self.assertRaisesRegex(PathValidationError, 'not valid'):
            validator.validate_tls('google.com')
Exemple #7
0
    def validate_cert_usage(cls,
                            validator: CertificateValidator,
                            key_usage_settings: KeyUsageConstraints = None):
        key_usage_settings = key_usage_settings or KeyUsageConstraints()
        key_usage_settings = KeyUsageConstraints(
            key_usage=(cls.key_usage if key_usage_settings.key_usage is None
                       else key_usage_settings.key_usage),
            extd_key_usage=(cls.extd_key_usage
                            if key_usage_settings.extd_key_usage is None else
                            key_usage_settings.extd_key_usage))
        cert: x509.Certificate = validator._certificate

        revoked = trusted = False
        path = None

        try:
            # validate usage without going through pyhanko_certvalidator
            key_usage_settings.validate(cert)
            path = validator.validate_usage(key_usage=set())
            trusted = True
        except InvalidCertificateError as e:
            # TODO accumulate these somewhere
            logger.warning(e)
        except RevokedError:
            revoked = True
        except (PathValidationError, PathBuildingError) as e:
            logger.warning(e)
        if not trusted:
            subj = cert.subject.human_friendly
            logger.warning(f"Chain of trust validation for {subj} failed.")
        return trusted, revoked, path
def test_validate(requests_mock, setup):
    setup.illusionist.register(requests_mock)
    signer_cert = setup.arch.get_cert(CertLabel('signer1'))
    root = setup.arch.get_cert(CertLabel('root'))
    interm = setup.arch.get_cert(CertLabel('interm'))
    vc = ValidationContext(trust_roots=[root],
                           allow_fetching=True,
                           revocation_mode='hard-fail',
                           other_certs=[interm])

    validator = CertificateValidator(signer_cert,
                                     intermediate_certs=[],
                                     validation_context=vc)
    validator.validate_usage({'digital_signature'})

    assert len(vc.ocsps)
    assert len(vc.crls)
Exemple #9
0
    def validation_paths(self, validation_context):
        """
        Produce validation paths for the certificates gathered by this
        :class:`.TimeStamper`.

        This is internal API.

        :param validation_context:
            The validation context to apply.
        :return:
            A generator producing validation paths.
        """
        for cert in self._certs.values():
            validator = CertificateValidator(
                cert,
                intermediate_certs=self.cert_registry,
                validation_context=validation_context)
            yield validator.validate_usage(set(), {"time_stamping"})
async def test_meta_tsa_verify():
    # check if my testing setup works
    vc = ValidationContext(
        trust_roots=TRUST_ROOTS, allow_fetching=False, crls=[],
        ocsps=[FIXED_OCSP], revocation_mode='hard-fail'
    )
    with pytest.raises(PathValidationError):
        cv = CertificateValidator(TSA_CERT, validation_context=vc)
        await cv.async_validate_usage({'time_stamping'})
Exemple #11
0
    async def _validate_signed_data(signed_data):
        cert_info = extract_certificate_info(signed_data)
        cert = cert_info.signer_cert
        other_certs = cert_info.other_certs

        validator = CertificateValidator(
            cert, intermediate_certs=other_certs,
            validation_context=validation_context
        )
        path = await validator.async_validate_usage(key_usage=set())
        paths.append(path)
Exemple #12
0
def test_cert_constraint_issuer(requests_mock):
    vc = live_testing_vc(requests_mock)
    signer_validation_path = CertificateValidator(
        FROM_CA.signing_cert, FROM_CA.cert_registry, validation_context=vc
    ).validate_usage(set())
    tsa_validation_path = CertificateValidator(
        DUMMY_TS.tsa_cert, FROM_CA.cert_registry, validation_context=vc
    ).validate_usage(set())

    scc = fields.SigCertConstraints(
        flags=fields.SigCertConstraintFlags.ISSUER,
        issuers=[ROOT_CERT]
    )
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(FROM_CA.signing_cert, None)

    scc = fields.SigCertConstraints(
        flags=fields.SigCertConstraintFlags.ISSUER,
        issuers=[INTERM_CERT]
    )
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)

    scc = fields.SigCertConstraints(
        flags=fields.SigCertConstraintFlags.ISSUER,
        issuers=[INTERM_CERT, SELF_SIGN.signing_cert]
    )
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)

    scc = fields.SigCertConstraints(issuers=[INTERM_CERT])
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)
    def test_basic_certificate_validator_tls_whitelist(self):
        cert = self._load_cert_object('mozilla.org.crt')
        other_certs = [
            self._load_cert_object('digicert-sha2-secure-server-ca.crt')
        ]

        moment = datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(whitelisted_certs=[cert.sha1_fingerprint],
                                    moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        # If whitelist does not work, this will raise exception for expiration
        validator.validate_tls('www.mozilla.org')

        # If whitelist does not work, this will raise exception for hostname
        validator.validate_tls('google.com')

        # If whitelist does not work, this will raise exception for key usage
        validator.validate_usage(set(['crl_sign']))
Exemple #14
0
    async def validation_paths(self, validation_context):
        """
        Produce validation paths for the certificates gathered by this
        :class:`.TimeStamper`.

        This is internal API.

        :param validation_context:
            The validation context to apply.
        :return:
            An asynchronous generator of validation paths.
        """
        await self._ensure_dummy()
        for cert in self._certs.values():
            validator = CertificateValidator(
                cert,
                intermediate_certs=self.cert_registry,
                validation_context=validation_context)
            yield await validator.async_validate_usage(set(),
                                                       {"time_stamping"})
async def cms_basic_validation(signed_data: cms.SignedData,
                               status_cls: Type[StatusType] = SignatureStatus,
                               raw_digest: bytes = None,
                               validation_context: ValidationContext = None,
                               status_kwargs: dict = None,
                               key_usage_settings: KeyUsageConstraints = None,
                               encap_data_invalid=False):
    """
    Perform basic validation of CMS and PKCS#7 signatures in isolation
    (i.e. integrity and trust checks).

    Internal API.
    """
    signer_info = extract_signer_info(signed_data)
    cert_info = extract_certs_for_validation(signed_data)
    cert = cert_info.signer_cert
    other_certs = cert_info.other_certs

    weak_hash_algos = None
    if validation_context is not None:
        weak_hash_algos = validation_context.weak_hash_algos
    if weak_hash_algos is None:
        weak_hash_algos = DEFAULT_WEAK_HASH_ALGORITHMS

    signature_algorithm: cms.SignedDigestAlgorithm = \
        signer_info['signature_algorithm']
    mechanism = signature_algorithm['algorithm'].native
    md_algorithm = signer_info['digest_algorithm']['algorithm'].native
    eci = signed_data['encap_content_info']
    expected_content_type = eci['content_type'].native
    if raw_digest is None:
        # this means that there should be encapsulated data
        raw = bytes(eci['content'])
        md_spec = get_pyca_cryptography_hash(md_algorithm)
        md = hashes.Hash(md_spec)
        md.update(raw)
        raw_digest = md.finalize()

    # first, do the cryptographic identity checks
    intact, valid = validate_sig_integrity(
        signer_info,
        cert,
        expected_content_type=expected_content_type,
        actual_digest=raw_digest,
        weak_hash_algorithms=weak_hash_algos)

    # if the data being encapsulated by the signature is itself invalid,
    #  this flag is set
    intact &= not encap_data_invalid
    valid &= intact

    # next, validate trust
    ades_status = path = None
    if valid:
        validator = CertificateValidator(cert,
                                         intermediate_certs=other_certs,
                                         validation_context=validation_context)
        ades_status, path = await status_cls.validate_cert_usage(
            validator, key_usage_settings=key_usage_settings)

    status_kwargs = status_kwargs or {}
    status_kwargs.update(intact=intact,
                         valid=valid,
                         signing_cert=cert,
                         md_algorithm=md_algorithm,
                         pkcs7_signature_mechanism=mechanism,
                         trust_problem_indic=ades_status,
                         validation_path=path)
    return status_kwargs
def run():
    """
    Runs through TLS hosts in the Alexa top 1000 to test TLS functionality

    :return:
        A bool - if the test succeeded without any socket errors
    """

    task_start = time.time()
    success = 0
    tls_errors = 0
    socket_errors = 0
    mismatch_info = []

    context = ValidationContext(allow_fetching=True)

    with open(os.path.join(fixtures_dir, 'alexa_top_1000.csv'), 'rb') as f:
        for line in f:
            domain = line.decode('utf-8').rstrip()
            os_result = None
            cv_result = None
            os_message = None
            cv_message = None

            try:
                os_start = time.time()
                con = tls.TLSSocket(domain, 443, timeout=3)
                con.close()
                success += 1
                os_result = 'OK'
                os_message = 'Success'
                _color('green', 'OK', domain, os_start)
            except (TLSVerificationError) as e:
                tls_errors += 1
                os_result = 'TLS'
                os_message = str_cls(e)
                _color('yellow', 'TLS', domain, os_start, str_cls(e))
            except (socket.error) as e:
                socket_errors += 1
                os_result = 'SOCK'
                os_message = str_cls(e)
                _color('red', 'SOCK', domain, os_start, str_cls(e))

            try:
                cv_start = time.time()
                session = tls.TLSSession(manual_validation=True)
                con = tls.TLSSocket(domain, 443, timeout=3, session=session)
                validator = CertificateValidator(con.certificate, con.intermediates, context)
                validator.validate_tls(domain)
                con.close()
                success += 1
                cv_result = 'OK'
                cv_message = 'Success'
                _color('green', 'OK', domain, cv_start)
            except (PathValidationError, PathBuildingError) as e:
                tls_errors += 1
                cv_result = 'TLS'
                cv_message = str_cls(e)
                _color('yellow', 'TLS', domain, cv_start, str_cls(e))
            except (socket.error) as e:
                socket_errors += 1
                cv_result = 'SOCK'
                cv_message = str_cls(e)
                _color('red', 'SOCK', domain, cv_start, str_cls(e))

            if os_result != cv_result:
                mismatch_info.append([
                    domain,
                    os_result,
                    os_message,
                    cv_result,
                    cv_message
                ])

    total_time = time.time() - task_start
    total_domains = success + tls_errors + socket_errors

    stats = []
    if success > 0:
        stats.append('%d [%sOK%s]' % (success, Fore.GREEN, Fore.RESET))
    if tls_errors > 0:
        stats.append('%d [%sTLS%s]' % (tls_errors, Fore.YELLOW, Fore.RESET))
    if socket_errors > 0:
        stats.append('%d [%sSOCK%s]' % (socket_errors, Fore.RED, Fore.RESET))
    print('')
    print('Checked %d domains in %.3f seconds - %s' % (total_domains, total_time, ' '.join(stats)))

    if mismatch_info:
        print('')
        for info in mismatch_info:
            os_result = '[%s] %s' % (info[1], info[2])
            cv_result = '[%s] %s' % (info[3], info[4])
            _color(
                'red',
                'DIFF',
                'oscrypto and pyhanko_certvalidator results for %s are different' % info[0],
                None,
                os_result,
                cv_result
            )

    return socket_errors == 0
Exemple #17
0
 def _validation_job(cert):
     validator = CertificateValidator(
         cert,
         intermediate_certs=self.cert_registry,
         validation_context=validation_context)
     return validator.async_validate_usage(set(), {"time_stamping"})