def addsig(ctx, field, name, reason, location, certify, existing_only, timestamp_url, use_pades, with_validation_info, validation_context, trust_replace, trust, other_certs, style_name, stamp_url): ctx.obj[Ctx.EXISTING_ONLY] = existing_only or field is None ctx.obj[Ctx.TIMESTAMP_URL] = timestamp_url if use_pades: subfilter = fields.SigSeedSubFilter.PADES else: subfilter = fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED if with_validation_info: vc_kwargs = _build_vc_kwargs(ctx, validation_context, trust, trust_replace, other_certs, allow_fetching=True) vc = ValidationContext(**vc_kwargs) else: vc = None field_name, new_field_spec = parse_field_location_spec( field, require_full_spec=False) ctx.obj[Ctx.SIG_META] = signers.PdfSignatureMetadata( field_name=field_name, location=location, reason=reason, name=name, certify=certify, subfilter=subfilter, embed_validation_info=with_validation_info, validation_context=vc) ctx.obj[Ctx.NEW_FIELD_SPEC] = new_field_spec ctx.obj[Ctx.STAMP_STYLE] = _select_style(ctx, style_name, stamp_url) ctx.obj[Ctx.STAMP_URL] = stamp_url
def _check_certificate(self, certificate): _, _, certificate_bytes = pem.unarmor( certificate.encode(), multiple=False) certificate = x509.Certificate.load(certificate_bytes) trust_roots = [] with open(self.ca_crt, 'rb') as f: for _, _, der_bytes in pem.unarmor(f.read(), multiple=True): trust_roots.append(der_bytes) crls = [] with open(settings.CA_CRL, 'rb') as f: crls.append(f.read()) context = ValidationContext(crls=crls, trust_roots=trust_roots) try: validator = CertificateValidator( certificate, validation_context=context) result = validator.validate_usage( set(['digital_signature']) ) dev = True except errors.PathValidationError as e: logger.debug("SimpleCA: validate PathValidationError %r" % (e)) dev = False except errors.PathBuildingError as e: logger.debug("SimpleCA: validate PathBuildingError %r" % (e)) dev = False logger.info("SimpleCA: validate cert %r == %r" % (certificate.serial_number, dev)) return dev
def get_validation_context(self, name=None, as_dict=False): name = name or self.default_validation_context try: vc_config = self.validation_contexts[name] except KeyError: raise ConfigurationError( f"There is no validation context named '{name}'.") vc_kwargs = parse_trust_config(vc_config, self.time_tolerance) return vc_kwargs if as_dict else ValidationContext(**vc_kwargs)
def lta_update(ctx, infile, validation_context, trust, trust_replace, other_certs, timestamp_url): with pyhanko_exception_manager(): vc_kwargs = _build_vc_kwargs(ctx, validation_context, trust, trust_replace, other_certs) timestamper = HTTPTimeStamper(timestamp_url) r = PdfFileReader(infile) signers.PdfTimeStamper(timestamper).update_archival_timestamp_chain( r, ValidationContext(**vc_kwargs))
def test_crl_without_update_field(self): cert = self._load_cert_object('microsoft_armored.crt') root_certificates = self._load_trust_roots( os.path.join(fixtures_dir, 'root_certs')) moment = datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc) context = ValidationContext(trust_roots=root_certificates, moment=moment, allow_fetching=True) validator = CertificateValidator(cert, validation_context=context) validator.validate_usage(set(['digital_signature']), set(['code_signing']), False)
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')
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) path = validator.validate_tls('www.mozilla.org') self.assertEqual(3, len(path))
def test_basic_certificate_validator_tls(self): cert = self._load_cert_object('codex.crt') other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')] moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc) context = ValidationContext(moment=moment) validator = CertificateValidator(cert, other_certs, context) path = validator.validate_tls('codexns.io') self.assertEqual(3, len(path))
def test_basic_certificate_validator_tls_invalid_key_usage(self): cert = self._load_cert_object('codex.crt') other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')] moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc) context = ValidationContext(moment=moment) validator = CertificateValidator(cert, other_certs, context) with self.assertRaisesRegexp(PathValidationError, 'for the purpose'): validator.validate_usage(set(['crl_sign']))
def verify(self, certificate): """Verifies the certificate, and its chain. :param Certificate certificate: The certificate to verify :return: A valid certificate chain for this certificate. :rtype: Iterable[Certificate] :raises AuthenticodeVerificationError: When the certificate could not be verified. """ # we keep track of our asn1 objects to make sure we return Certificate objects when we're done to_check_asn1cert = certificate.to_asn1crypto all_certs = {to_check_asn1cert: certificate} # we need to get lists of our intermediates and trusted certificates intermediates, trust_roots = [], [] for store in self.stores: for cert in store: asn1cert = cert.to_asn1crypto # we short-circuit the check here to ensure we do not check too much possibilities (trust_roots if store.trusted else intermediates).append(asn1cert) all_certs[asn1cert] = cert # construct the context and validator for certvalidator timestamp = self.timestamp context = ValidationContext( trust_roots=list(trust_roots), moment=timestamp, weak_hash_algos=set() if self.allow_legacy else None, revocation_mode=self.revocation_mode, allow_fetching=self.allow_fetching, crl_fetch_params={'timeout': self.fetch_timeout}, ocsp_fetch_params={'timeout': self.fetch_timeout}, crls=self.crls, ocsps=self.ocsps) validator = CertificateValidator( end_entity_cert=to_check_asn1cert, intermediate_certs=list(intermediates), validation_context=context) # verify the chain try: chain = validator.validate_usage( key_usage=set(self.key_usages) if self.key_usages else set(), extended_key_usage=set(self.extended_key_usages) if self.extended_key_usages else set(), extended_optional=self.optional_eku) except Exception as e: raise VerificationError("Chain verification from %s failed: %s" % (certificate, e)) signify_chain = [all_certs[x] for x in chain] self.verify_trust(signify_chain[0]) return signify_chain
def test_basic_certificate_validator_tls_invalid_hostname(self): cert = self._load_cert_object('codex.crt') other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')] moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc) context = ValidationContext(moment=moment) validator = CertificateValidator(cert, other_certs, context) with self.assertRaisesRegexp(PathValidationError, 'not valid'): validator.validate_tls('google.com')
def validate_certificate(self, ca_certs: List[bytes]) -> bool: if not ca_certs: raise ValueError("ca_certs should be set.") if not self._cert: return False ctx = ValidationContext(trust_roots=ca_certs) try: validator = CertificateValidator(self._cert, self._intermediates, validation_context=ctx) validator.validate_usage(set(["digital_signature"]), extended_optional=True) except Exception as err: raise VerifyError("Failed to validate the certificate bound to the key.") from err return True
def __init__( self, expected_measurement=None, accept_debug=False, accept_configuration_needed=False, accept_group_out_of_date=False, ): self.expected_measurement = expected_measurement self.accept_debug = accept_debug self.accept_configuration_needed = accept_configuration_needed self.accept_group_out_of_date = accept_group_out_of_date trust_roots = [] for _, _, der_bytes in pem.unarmor(intel_sgx_root_ca, multiple=True): trust_roots.append(der_bytes) self.context = ValidationContext(trust_roots=trust_roots)
def test_basic_certificate_validator_tls_whitelist(self): cert = self._load_cert_object('codex.crt') other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')] context = ValidationContext(whitelisted_certs=[cert.sha1_fingerprint]) validator = CertificateValidator(cert, other_certs, context) # If whitelist does not work, this will raise exception for expiration validator.validate_tls('codexns.io') # 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']))
def test_crl_without_update_field_hard_fail(self): cert = self._load_cert_object('microsoft_armored.crt') root_certificates = self._load_trust_roots( os.path.join(fixtures_dir, 'root_certs')) moment = datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc) context = ValidationContext(trust_roots=root_certificates, moment=moment, allow_fetching=True, revocation_mode='hard-fail') validator = CertificateValidator(cert, validation_context=context) with self.assertRaisesRegexp( PathValidationError, 'nextUpdate field is expected to be present in CRL'): validator.validate_usage(set(['digital_signature']), set(['code_signing']), False)
def initialiser(self): # Charger le contexte de validation with open(self._contexte.configuration.mq_cafile, 'rb') as fichier: self.__cert_millegrille = fichier.read() self.__validation_context = ValidationContext( trust_roots=[self.__cert_millegrille]) self._charger_cle_privee() self._charger_certificat() self._enveloppe = EnveloppeCertificat( certificat_pem='\n'.join(self._chaine)) # Verifier que le certificat peut bien etre utilise pour signer des transactions # Valide aussi la chaine et les dates d'expiration self.valider_x509_enveloppe(self._enveloppe)
def verify(self, certificate): """Verifies the certificate, and its chain. :param Certificate certificate: The certificate to verify :return: A valid certificate chain for this certificate. :rtype: Iterable[Certificate] :raises AuthenticodeVerificationError: When the certificate could not be verified. """ # we keep track of our asn1 objects to make sure we return Certificate objects when we're done to_check_asn1cert = certificate.to_asn1crypto all_certs = {to_check_asn1cert: certificate} # we need to get lists of our intermediates and trusted certificates intermediates, trust_roots = [], [] for store in self.stores: for cert in store: asn1cert = cert.to_asn1crypto (trust_roots if store.trusted else intermediates).append(asn1cert) all_certs[asn1cert] = cert # construct the context and validator for certvalidator context = ValidationContext( trust_roots=list(trust_roots), moment=self.timestamp, weak_hash_algos=set() if self.allow_legacy else None) validator = CertificateValidator( end_entity_cert=to_check_asn1cert, intermediate_certs=list(intermediates), validation_context=context) # verify the chain try: chain = validator.validate_usage( key_usage=set(self.key_usages) if self.key_usages else set(), extended_key_usage=set(self.extended_key_usages) if self.extended_key_usages else set(), extended_optional=self.optional_eku) except Exception as e: raise VerificationError("Chain verification from %s failed: %s" % (certificate, e)) else: return [all_certs[x] for x in chain]
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']))
def verify_chain(certificate: Union[str, x509.Certificate], ca: str): if isinstance(certificate, x509.Certificate): pem = certificate.public_bytes(serialization.Encoding.PEM) else: if certificate.startswith("-----BEGIN CERTIFICATE-----"): pem = certificate.encode() else: with open(certificate, "rb") as my_file: pem = my_file.read() # cert = x509.load_pem_x509_certificate(pem, default_backend()) with open(ca, "rb") as my_file: pem_ca = my_file.read() # ca = x509.load_pem_x509_certificate(pem_ca, default_backend()) trust_roots = [pem_ca] context = ValidationContext(trust_roots=trust_roots) validator = CertificateValidator(pem, validation_context=context) return validator.validate_usage({"digital_signature"})
def _signature_status(ltv_profile, ltv_obsessive, pretty_print, vc_kwargs, executive_summary, embedded_sig): try: if ltv_profile is None: vc = ValidationContext(**vc_kwargs) status = validation.validate_pdf_signature( embedded_sig, signer_validation_context=vc) else: status = validation.validate_pdf_ltv_signature( embedded_sig, ltv_profile, force_revinfo=ltv_obsessive, validation_context_kwargs=vc_kwargs) if executive_summary and not pretty_print: return 'VALID' if status.bottom_line else 'INVALID' elif pretty_print: return status.pretty_print_details() else: return status.summary() except validation.ValidationInfoReadingError as e: msg = ('An error occurred while parsing the revocation information ' 'for this signature: ' + str(e)) logger.error(msg, exc_info=e) if pretty_print: return msg else: return 'REVINFO_FAILURE' except SignatureValidationError as e: msg = 'An error occurred while validating this signature: ' + str(e) logger.error(msg, exc_info=e) if pretty_print: return msg else: return 'INVALID' except Exception as e: msg = 'Generic processing error: ' + str(e) logger.error(msg, exc_info=e) if pretty_print: return msg else: return 'MALFORMED'
def _validate_chain_certvalidator(self, tlslite_connection): """Validate server certificate chain using 3rd party certvalidator library which uses oscrypt/libcrypto Note: oscrypt uses ctypes find_library() which does not work in certain distributions such as alpine. (e.g. see https://github.com/docker-library/python/issues/111) On such systems, users will have to rely on other server cert validation approaches such as using openssl or turning it off completely. """ try: from certvalidator import CertificateValidator from certvalidator import ValidationContext from asn1crypto import x509, pem # validate server certificate chain session = tlslite_connection.sock.session assert type(session.serverCertChain.x509List) == list # get the end-entity cert file_bytes = session.serverCertChain.x509List[0].bytes end_entity_cert = x509.Certificate.load(str(file_bytes)) def cert_files_exist(path, file_names): file_names = [os.path.join(path, f) for f in file_names] for f in file_names: if not os.path.isfile(f): return False return True def get_cert_bytes(cert_dir, file_names): file_names = [os.path.join(cert_dir, f) for f in file_names] result = [] for fname in file_names: arr = open(fname, "rb").read() cert_bytes = pem.unarmor(arr)[2] result.append(cert_bytes) return result intermediate_cert_names = [ "comodo_ca_intermediate.pem", "sectigo_ca_intermediate.pem", ] extra_trust_names = [ "scalyr_agent_ca_root.pem", "addtrust_external_ca_root.pem", ] # Determine the directory containing the certs. # First check the directory containing the _ca_file # but if we don't find the intermediate/extra certs there # then look in the relative `certs` directory. The latter # will typically be required if running directly from source all_cert_names = intermediate_cert_names + extra_trust_names cert_dir = os.path.dirname(self._ca_file) if not cert_files_exist(cert_dir, all_cert_names): path = os.path.dirname(os.path.abspath(__file__)) path = os.path.abspath(path + "../../certs") if cert_files_exist(path, all_cert_names): cert_dir = path trust_roots = None intermediate_certs = get_cert_bytes(cert_dir, intermediate_cert_names) extra_trust_roots = get_cert_bytes(cert_dir, extra_trust_names) if trust_roots: context = ValidationContext( trust_roots=trust_roots, extra_trust_roots=extra_trust_roots, other_certs=intermediate_certs, # whitelisted_certs=[end_entity_cert.sha1_fingerprint], ) else: context = ValidationContext( extra_trust_roots=extra_trust_roots, other_certs=intermediate_certs, # whitelisted_certs=[end_entity_cert.sha1_fingerprint], ) validator = CertificateValidator( end_entity_cert, validation_context=context ) validator.validate_tls(six.text_type(self._host)) log.info( "Scalyr server cert chain successfully validated via certvalidator library" ) except Exception as ce: log.exception("Error validating server certificate chain: %s" % ce) raise
def validate(self, root_ca: CaSecret, with_openssl: bool = False): ''' Validate that the cert and its certchain are anchored to the root cert. This function does not check certificate recovation or OCSP :param Secret root_ca: the self-signed root CA to validate against :param with_openssl: also use the openssl binary to validate the cert :returns: (none) :raises: ValueError if the certchain is invalid ''' pem_signed_cert = self.cert_as_pem() pem_cert_chain = [ x.public_bytes(serialization.Encoding.PEM) for x in self.cert_chain ] context = ValidationContext(trust_roots=[root_ca.cert_as_pem()]) validator = CertificateValidator(pem_signed_cert, pem_cert_chain, validation_context=context) try: validator.validate_usage(set()) except (ValidationError, PathBuildingError) as exc: raise ValueError(f'Certchain failed validation: {exc}') from exc if not with_openssl: return tmpdir = tempfile.TemporaryDirectory() rootfile = tmpdir.name + '/rootca.pem' with open(rootfile, 'w') as file_desc: file_desc.write(root_ca.cert_as_pem().decode('utf-8')) certfile = tmpdir.name + '/cert.pem' with open(certfile, 'w') as file_desc: file_desc.write(self.cert_as_pem().decode('utf-8')) cmd = [ 'openssl', 'verify', '-CAfile', rootfile, ] if self.cert_chain: chainfile = tmpdir.name + '/chain.pem' with open(chainfile, 'w') as file_desc: for cert in self.cert_chain: file_desc.write( cert.public_bytes( serialization.Encoding.PEM).decode('utf-8')) cmd.extend(['-untrusted', chainfile]) cmd.append(certfile) result = subprocess.run(cmd) if result.returncode != 0: raise ValueError( f'Certificate validation with command {" ".join(cmd)} ' f'failed: {result.returncode} for cert {certfile} and ' f'root CA {rootfile}') tmpdir.cleanup() _LOGGER.debug('Successfully validated certchain using OpenSSL for ' f'cert {certfile} and root CA {rootfile}')
def valider_x509_enveloppe(self, enveloppe: EnveloppeCertificat, date_reference: datetime.datetime = None): """ Valide une enveloppe :param enveloppe: :param date_reference: :param ignorer_date: Charger le certificat en utilisation date courante ou fin de periode de validite :return: Resultat de validation (toujours valide) :raises certvalidator.errors.PathBuildingError: Si le path est invalide """ cert_pem = enveloppe.certificat_pem.encode('utf-8') inter_list = list() # self._logger.debug("CERT PEM :\n%s" % enveloppe.certificat_pem) for pem in enveloppe.reste_chaine_pem: # self._logger.debug("Chaine PEM :\n%s" % pem.strip()) inter_list.append(pem.strip().encode('utf-8')) if date_reference is not None: # batir un contexte avec la date validation_context = ValidationContext( moment=date_reference, trust_roots=[self.__cert_millegrille]) else: validation_context = self.__validation_context # Verifier le certificat - noter qu'une exception est lancee en cas de probleme try: validator = CertificateValidator( cert_pem, intermediate_certs=inter_list, validation_context=validation_context) resultat = validator.validate_usage({'digital_signature'}) enveloppe.set_est_verifie(True) except PathValidationError as pve: msg = pve.args[0] if 'expired' in msg: self._logger.info( "Un des certificats est expire, verifier en fonction de la date de reference" ) # Le certificat est expire, on fait la validation pour la fin de la periode de validite date_reference = pytz.UTC.localize(enveloppe.not_valid_after) validation_context = ValidationContext( moment=date_reference, trust_roots=[self.__cert_millegrille]) validator = CertificateValidator( cert_pem, intermediate_certs=inter_list, validation_context=validation_context) try: resultat = validator.validate_usage({'digital_signature'}) enveloppe.set_est_verifie(True) raise CertificatExpire( ) # La chaine est valide pour une date anterieure except PathValidationError as pve: if self._logger.isEnabledFor(logging.DEBUG): self._logger.exception( "Erreur validation path certificat") else: self._logger.info( "Erreur validation path certificat : %s", str(pve)) else: if self._logger.isEnabledFor(logging.DEBUG): self._logger.exception("Erreur validation path certificat") else: self._logger.info("Erreur validation path certificat : %s", str(pve)) raise pve except PathBuildingError as pbe: # Verifier si on a une millegrille tierce dernier_cert_pem = inter_list[-1] dernier_cert = EnveloppeCertificat(certificat_pem=dernier_cert_pem) if dernier_cert.is_rootCA: idmg = dernier_cert.idmg # Verifier si le idmg est dans la liste des idmg autorises autorisation = self.__autorisations_idmg.get(idmg) if autorisation is None: # Pas autorise, lancer l'exception raise pbe elif autorisation.get('domaines_permis'): # Valider la chaine en fonction de la racine fournie if date_reference is not None: # batir un contexte avec la date validation_context = ValidationContext( moment=date_reference, trust_roots=[ self.__cert_millegrille, dernier_cert_pem ]) else: validation_context = ValidationContext(trust_roots=[ self.__cert_millegrille, dernier_cert_pem ]) validator = CertificateValidator( cert_pem, intermediate_certs=inter_list, validation_context=validation_context) validator.validate_usage({'digital_signature'}) # Valide, on lance une exception pour indiquer la condition de validite (business rule) raise AutorisationConditionnelleDomaine( autorisation['domaines_permis'], idmg, enveloppe) return resultat
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 certvalidator results for %s are different' % info[0], None, os_result, cv_result) return socket_errors == 0
def verify_bib(self, ctr, bib): addl_protected = b'' addl_unprotected = {} aad_scope = 0x7 for param in bib.payload.parameters: if param.type_code == 3: addl_protected = bytes(param.value) elif param.type_code == 4: addl_unprotected = dict(param.value) elif param.type_code == 5: aad_scope = int(param.value) addl_protected_map = cbor2.loads( addl_protected) if addl_protected else {} dupe_keys = set(addl_protected_map.keys()).intersection( set(addl_unprotected.keys())) if dupe_keys: LOGGER.warning('Duplicate keys in additional headers: %s', dupe_keys) return StatusReport.ReasonCode.FAILED_SEC addl_headers = dict(addl_protected_map) addl_headers.update(addl_unprotected) bundle_at = DtnTimeField.dtntime_to_datetime( ctr.bundle.primary.create_ts.getfieldval('dtntime')) val_ctx = ValidationContext( trust_roots=[ cert.public_bytes(serialization.Encoding.DER) for cert in self._ca_certs ], other_certs=[ cert.public_bytes(serialization.Encoding.DER) for cert in self._cert_chain ], moment=bundle_at, ) LOGGER.debug('Validating certificates at time %s', bundle_at) failure = None for (ix, blk_num) in enumerate(bib.payload.targets): target_blk = ctr.block_num(blk_num) for result in bib.payload.results[ix].results: msg_cls = CoseMessage._COSE_MSG_ID[result.type_code] # replace detached payload msg_enc = bytes(result.getfieldval('value')) msg_dec = cbor2.loads(msg_enc) LOGGER.debug('Received COSE message\n%s', encode_diagnostic(msg_dec)) msg_dec[2] = target_blk.getfieldval('btsd') msg_obj = msg_cls.from_cose_obj(msg_dec) msg_obj.external_aad = CoseContext.get_bpsec_cose_aad( ctr, target_blk, bib, aad_scope, addl_protected) # use additional headers as defaults for (key, val) in msg_cls._parse_header(addl_headers).items(): msg_obj.uhdr.setdefault(key, val) LOGGER.info('full uhdr %s', msg_obj.uhdr) x5t_item = msg_obj.get_attr(headers.X5t) x5t = X5T.decode(x5t_item) if x5t_item else None x5chain_item = msg_obj.get_attr(headers.X5chain) if isinstance(x5chain_item, bytes): x5chain = [x5chain_item] else: x5chain = x5chain_item LOGGER.info('Validating X5t %s and X5chain length %d', x5t.encode() if x5t else None, len(x5chain) if x5chain else 0) if x5t is None and x5chain: # Only one possible end-entity cert LOGGER.warning('No X5T in header, assuming single chain') found_chain = x5chain else: try: found_chain = x5chain if x5t.matches( x5chain[0]) else None if not found_chain: raise RuntimeError( 'No chain matcing end-entity cert for {}'. format(x5t.encode())) LOGGER.debug( 'Found chain matcing end-entity cert for %s', x5t.encode()) except Exception as err: LOGGER.error( 'Failed to find cert chain for block num %d: %s', blk_num, err) failure = StatusReport.ReasonCode.FAILED_SEC continue LOGGER.debug('Validating chain with %d certs against %d CAs', len(found_chain), len(self._ca_certs)) try: val = CertificateValidator( end_entity_cert=found_chain[0], intermediate_certs=found_chain[1:], validation_context=val_ctx) val.validate_usage( key_usage={'digital_signature'}, extended_key_usage={'1.3.6.1.5.5.7.3.35'}, extended_optional=True) except Exception as err: LOGGER.error('Failed to verify chain on block num %d: %s', blk_num, err) failure = StatusReport.ReasonCode.FAILED_SEC continue peer_nodeid = bib.payload.source end_cert = x509.load_der_x509_certificate( found_chain[0], default_backend()) authn_nodeid = tcpcl.session.match_id( peer_nodeid, end_cert, x509.UniformResourceIdentifier, LOGGER, 'NODE-ID') if not authn_nodeid: LOGGER.error( 'Failed to authenticate peer "%s" on block num %d', peer_nodeid, blk_num) failure = StatusReport.ReasonCode.FAILED_SEC # Continue on to verification try: msg_obj.key = self.extract_cose_key(end_cert.public_key()) msg_obj.verify_signature() LOGGER.info('Verified signature on block num %d', blk_num) except Exception as err: LOGGER.error( 'Failed to verify signature on block num %d: %s', blk_num, err) failure = StatusReport.ReasonCode.FAILED_SEC return failure