class CheckVmc: def __init__(self, vmc_file, user_agent, is_file=False): self.STORAGE_CERT_DIR = Config.STORAGE_CERT_DIR self.vmc_file = vmc_file self.Utils = Utils() self.vmc_response = { "status": False, "errors": [], "vmc_link": vmc_file } self.is_file = is_file self.user_agent = user_agent def download_pem_path(self, url): print('Beginning file download certificate with urllib') self.Utils.check_dir_folder(self.STORAGE_CERT_DIR) file_name_hash = str(uuid.uuid4()) req = Request(url, headers={'User-Agent': self.user_agent}) with urlopen(req) as response, open( self.STORAGE_CERT_DIR + file_name_hash + ".pem", 'wb') as out_file: data = response.read() out_file.write(data) return self.STORAGE_CERT_DIR + file_name_hash + ".pem" def validate_vmc(self): try: end_entity_cert = None intermediates = [] with open(self.vmc_file, 'rb') as f: for type_name, headers, der_bytes in pem.unarmor( f.read(), multiple=True): if end_entity_cert is None: end_entity_cert = der_bytes else: intermediates.append(der_bytes) validator = CertificateValidator(end_entity_cert, intermediates) validated = validator.validate_usage(set(['digital_signature'])) # print(intermediates) except errors.PathValidationError as PathValidationError: self.vmc_response["errors"].append("Error: " + str(PathValidationError)) print(PathValidationError) except errors.RevokedError as RevokedError: self.vmc_response["errors"].append( "Error: Certificate Revoked.\n" + str(RevokedError)) print(RevokedError) except errors.InvalidCertificateError as InvalidCertificateError: self.vmc_response["errors"].append( "Error: Certificate Is Invalid.\n" + str(InvalidCertificateError)) print(InvalidCertificateError) except errors.PathBuildingError as PathBuildingError: self.vmc_response["errors"].append("Error: Cannot Build Path.\n" + str(PathBuildingError)) print(PathBuildingError) except Exception as e: self.vmc_response["errors"].append( "Error: Validation Exception.\n" + str(e)) print(e) # Check VMC extension def is_vmc_extension(self): if self.is_file: if self.vmc_file != None: return True else: print(self.vmc_file, "Upload Vmc certificates has an Invalid Extension") return False else: if self.vmc_file.endswith('.pem') or self.vmc_file.endswith( '.PEM'): return True else: self.vmc_response["errors"].append( "Invalid file extension use. Only .pem files allowed") return False # Check vmc certificate def check_vmc(self): if self.vmc_file != "": if not self.is_file: self.vmc_file = self.download_pem_path(self.vmc_file) if self.is_vmc_extension(): self.validate_vmc() if len(self.vmc_response['errors']) > 0: self.vmc_response['status'] = False else: self.vmc_response['status'] = True else: # Currently vmc certificate is optional self.vmc_response['status'] = "Option" return self.vmc_response
class CheckSvg: def __init__(self, svg_file, user_agent, is_file=False): self.RNG_SCHEMA_FILE = Config.RNG_SCHEMA_FILE self.STORAGE_SVG_DIR = Config.STORAGE_SVG_DIR self.svg_file = svg_file self.Utils = Utils() self.svg_image_path = None self.svg_response = { "status": False, "errors": [], "svg_link": svg_file } self.is_file = is_file self.user_agent = user_agent # Donwnload SVG def download_svg_path(self, url): print('Beginning SVG file download with requests') try: self.Utils.check_dir_folder(self.STORAGE_SVG_DIR) file_name_hash = str(uuid.uuid4()) session = requests.Session() session.max_redirects = 3 response = session.get(url, headers={'User-Agent': self.user_agent}) if response: self.svg_image_path = self.STORAGE_SVG_DIR + file_name_hash + ".svg" with open(self.STORAGE_SVG_DIR + file_name_hash + ".svg", 'wb+') as out_file: out_file.write(response.content) print("Generated file: " + self.STORAGE_SVG_DIR + file_name_hash + ".svg") return self.STORAGE_SVG_DIR + file_name_hash + ".svg" else: response.raise_for_status() return False except HTTPError as http_err: self.svg_response['errors'].append({ "short_error": "Http Error", "error_details": "An error occurred while fetching the BIMI SVG Image. " + str(http_err) + "." }) print(f'HTTP error : {http_err}, occurred while fetching image') return False except requests.exceptions.TooManyRedirects as red_err: self.svg_response['errors'].append({ "short_error": "Too many redirects", "error_details": "The svg URL redirected too many times, please remove the redirections, or atleast reduce them to 3 or less." }) print( f'HTTP error : More than 3 redirects while fetching the svg image' ) return False except Exception as e: print(e) self.svg_response["errors"].append({ "short_error": "Something went wrong while downloading the SVG Image", "error_details": "The SVG file referenced by the URL in the BIMI record is unreachable by this validator." }) return False # CHECK SVG Extension def is_svg_extension(self): print('Checking Svg extension') if self.is_file: if self.svg_file != None: return True else: print(self.svg_file, "Upload SVG Image has an Invalid Extension") return False else: if self.svg_file.endswith('.svg') or self.svg_file.endswith( '.SVG'): return True else: return False # Check if xml file has an SVG tag def is_svg_xml(self): print('Checking If this is an XML file') tag = None with open(self.svg_file, "r") as f: try: for event, el in et.iterparse(f, ('start', )): tag = el.tag break except et.ParseError: pass return tag == '{http://www.w3.org/2000/svg}svg' # SVG check function def check_svg(self): if self.svg_file != "": if self.is_svg_extension(): if not self.is_file: self.svg_file = self.download_svg_path(self.svg_file) if not self.svg_file: self.svg_response['status'] = False return self.svg_response if self.svg_file: if self.is_svg_xml(): self.check_svg_schema() self.svg_image_path = self.svg_file if len(self.svg_response['errors']) > 0: self.svg_response['status'] = False else: self.svg_response['status'] = True else: self.svg_response['errors'].append({ "short_error": "Invalid SVG", "error_details": "The SVG image in your BIMI record has no SVG tag" }) else: self.svg_response['errors'].append({ "short_error": "File extraction error", "error_details": "There was an issue with downloading the SVG. Either the SVG image file doesn't exist or the link is unreachable / blocked." }) else: self.svg_response['errors'].append({ "short_error": "Extension Error", "error_details": "Invalid File extension" }) else: self.svg_response['errors'].append({ "short_error": "No SVG Image Found", "error_details": "We have found a blank/empty SVG file Or no SVG link in you BIMI record. Please check your BIMI record for this." }) return self.svg_response # SVG check according to Relax Ng, rng file schema def check_svg_schema(self): try: result = subprocess.run( ['pyjing', "-c", self.RNG_SCHEMA_FILE, self.svg_file], stdout=subprocess.PIPE) # print(result.stdout) error_string = result.stdout.decode() if error_string: if error_string.find("error:") != -1: err = error_string.split("\n") for i in range(len(err)): # print(err[i]) if err[i]: clear_error_string = self.Utils.clear_response_single_string( err[i].split('.svg:')[1]) error_str = self.Utils.strip_svg_plugin_errors( self.STORAGE_SVG_DIR, clear_error_string, ", Check Line ") self.svg_response['errors'].append({ "short_error": error_str, "error_details": clear_error_string }) self.svg_response['status'] = False else: self.svg_response['status'] = True except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] print(exc_type, fname, exc_tb.tb_lineno) print(str(e))
import logging as logger from Config import Config from datetime import date from utils.Utils import Utils log_level = Config.LOGGING_LEVEL log_file_path = Config.LOG_FILE_PATH + str( date.today()) + "_" + Config.LOG_FILE_NAME Utils = Utils() Utils.check_dir_folder(Config.LOG_FILE_PATH) if log_level == "INFO": log_level = logger.INFO elif log_level == "DEBUG": log_level = logger.DEBUG elif log_level == "WARNING": log_level = logger.WARN else: log_level = logger.INFO logger.basicConfig( filename=log_file_path, filemode='a', format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', level=log_level)
class CheckSvg: def __init__(self, svg_file, user_agent, is_file=False): self.RNG_SCHEMA_FILE = Config.RNG_SCHEMA_FILE self.STORAGE_SVG_DIR = Config.STORAGE_SVG_DIR self.svg_file = svg_file self.Utils = Utils() self.svg_response = { "status": False, "errors": [], "svg_link": svg_file } self.is_file = is_file self.user_agent = user_agent # Donwnload SVG def download_svg_path(self, url): print('Beginning file download with urllib2') try: self.Utils.check_dir_folder(self.STORAGE_SVG_DIR) file_name_hash = str(uuid.uuid4()) req = Request(url, headers={'User-Agent': self.user_agent}) with urlopen(req) as response, open( self.STORAGE_SVG_DIR + file_name_hash + ".svg", 'wb') as out_file: data = response.read() out_file.write(data) return self.STORAGE_SVG_DIR + file_name_hash + ".svg" except Exception as e: print(e) self.svg_response['errors'].append({ "short_error": str(e), "error_details": clear_error_string }) return # CHECK SVG Extension def is_svg_extension(self): if self.is_file: if self.svg_file != None: return True else: print(self.svg_file, "Upload SVG Image has an Invalid Extension") return False else: if self.svg_file.endswith('.svg') or self.svg_file.endswith( '.SVG'): return True else: return False # Check if xml file has an SVG tag def is_svg_xml(self): tag = None with open(self.svg_file, "r") as f: try: for event, el in et.iterparse(f, ('start', )): tag = el.tag break except et.ParseError: pass return tag == '{http://www.w3.org/2000/svg}svg' # SVG check function def check_svg(self): if self.svg_file != "": if self.is_svg_extension(): if not self.is_file: self.svg_file = self.download_svg_path(self.svg_file) if self.is_svg_xml(): self.check_svg_schema() if len(self.svg_response['errors']) > 0: self.svg_response['status'] = False else: self.svg_response['status'] = True else: self.svg_response['errors'].append({ "short_error": "Invalid SVG", "error_details": "The SVG image in your BIMI record has no SVG tag" }) else: self.svg_response['errors'].append({ "short_error": "Extension Error", "error_details": "Invalid File extension" }) else: self.svg_response['errors'].append({ "short_error": "No SVG Image Found", "error_details": "We have found a blank/empty SVG file Or no SVG link in you BIMI record. Please check your BIMI record for this." }) return self.svg_response # SVG check according to Relax Ng, rng file schema def check_svg_schema(self): try: result = subprocess.run( ['pyjing', "-c", self.RNG_SCHEMA_FILE, self.svg_file], stdout=subprocess.PIPE) # print(result.stdout) error_string = result.stdout.decode() if error_string: if error_string.find("error:") != -1: err = error_string.split("error:") for i in range(1, len(err) - 1): clear_error_string = self.Utils.clear_response_single_string( err[i]) error_str = self.Utils.strip_svg_plugin_errors( self.STORAGE_SVG_DIR, clear_error_string, ", Check Line ") self.svg_response['errors'].append({ "short_error": error_str, "error_details": clear_error_string }) self.svg_response['status'] = False else: self.svg_response['status'] = True except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] print(exc_type, fname, exc_tb.tb_lineno) print(str(e))
class CheckVmc: def __init__(self, vmc_file, user_agent, svg_link='', is_file=False): self.STORAGE_CERT_DIR = Config.STORAGE_CERT_DIR self.vmc_file = vmc_file self.Utils = Utils() self.vmc_response = { "status": False, "errors": [], "vmc_link": vmc_file } self.is_file = is_file self.user_agent = user_agent self.parsed_vmc = None self.svg_link = svg_link self.pem_file_path = None def download_pem_path(self, url): try: print('Beginning VMC file download certificate') self.Utils.check_dir_folder(self.STORAGE_CERT_DIR) file_name_hash = str(uuid.uuid4()) session = requests.Session() session.max_redirects = 3 response = session.get(url, headers={'User-Agent': self.user_agent}) if response: self.pem_file_path = self.STORAGE_CERT_DIR + file_name_hash + ".pem" with open(self.STORAGE_CERT_DIR + file_name_hash + ".pem", 'wb+') as out_file: out_file.write(response.content) return self.STORAGE_CERT_DIR + file_name_hash + ".pem" else: response.raise_for_status() return False except HTTPError as http_err: self.vmc_response['errors'].append( "An error occurred while fetching the Certificate from the provided Url. " + str(http_err) + ".") print( f'HTTP error : {http_err} occurred while fetching the Certificate' ) return False except requests.exceptions.TooManyRedirects as red_err: self.vmc_response['errors'].append( "The cetificate URL redirected too many times, please remove the redirections, or atleast reduce them to 3 or less." ) print( f'HTTP error : {red_err}. More than 3 redirects while fetching the svg image' ) return False except Exception as e: print(e) return False def validate_vmc(self): try: end_entity_cert = None intermediates = [] with open(self.vmc_file, 'rb') as f: readfile = f.read() # Create parsed data for expiry and embedded svg check self.parsed_vmc = self.parse_vmc_cert(readfile) for type_name, headers, der_bytes in pem.unarmor( readfile, multiple=True): if end_entity_cert is None: end_entity_cert = der_bytes else: intermediates.append(der_bytes) validator = CertificateValidator(end_entity_cert, intermediates) validated = validator.validate_usage( set(['digital_signature']) # ,extended_key_usage=set(["server_auth", "client_auth"]) ) if validated: print("Certificate Validated") except errors.PathValidationError as PathValidationError: self.vmc_response["errors"].append("Warning: " + str(PathValidationError)) print(PathValidationError) except errors.RevokedError as RevokedError: self.vmc_response["errors"].append( "Warning: Certificate Revoked.\n" + str(RevokedError)) print(RevokedError) except errors.InvalidCertificateError as InvalidCertificateError: self.vmc_response["errors"].append( "Warning: Certificate Is Invalid.\n" + str(InvalidCertificateError)) print(InvalidCertificateError) except errors.PathBuildingError as PathBuildingError: # self.vmc_response["errors"].append("Warning: Cannot Build Path.\n"+str(PathBuildingError)) print(PathBuildingError) except Exception as e: self.vmc_response["errors"].append( "Warning: Validation Exception.\n" + str(e)) print(e) # Extract SVG image from VMC cert file def get_svg_from_cert(self): cert_svg = None for i in self.parsed_vmc.extensions: if hasattr(i.value, 'value') and 'svg' in str(i.value.value): data = (base64.b64decode( str(i.value.value).split("data:image/svg+xml;base64,")[1])) svg = (gzip.decompress(data).decode()) cert_svg = svg.strip() cert_svg = "".join(svg.split()) return cert_svg # naive comapsion SVG in PEM with BIMI SVG def compare_pem_svg(self): cert_svg = self.get_svg_from_cert() if cert_svg: with open(self.svg_link, encoding='utf-8') as svg_file: # Remove indentation / tabs / newline svg = "".join((svg_file.read()).split()) if svg.lower() == cert_svg.lower(): pass else: self.vmc_response["errors"].append( "The SVG image provided in the BIMI record doesn't match the SVG image provided in the pem file. Please make sure that they are the same.\n" ) else: self.vmc_response["errors"].append( "There's no embedded SVG Logotype Image as defined in svg logotype RFC 3709. \n" ) # Check VMC extension def is_vmc_extension(self): if self.is_file: if self.vmc_file != None: return True else: print(self.vmc_file, "Uploaded Vmc certificates has an Invalid Extension") return False else: if self.vmc_file.endswith('.pem') or self.vmc_file.endswith( '.PEM'): return True else: self.vmc_response["errors"].append( "Invalid file extension used. Only .pem files allowed") return False # Parse VMC certificate def parse_vmc_cert(self, pem_data): return x509.load_pem_x509_certificate(pem_data, default_backend()) # Check certificate expiry def cert_validity(self): current_date_time = datetime.utcnow() print(current_date_time) print(self.parsed_vmc.not_valid_before) print(self.parsed_vmc.not_valid_after) if self.parsed_vmc.not_valid_before > current_date_time: print("VMC certificate not yet active") self.vmc_response["errors"].append( "The VMC certificate used is not yet active, and being used prior to the active date: " + self.parsed_vmc.not_valid_before.strftime("%m/%d/%Y, %H:%M:%S") ) return False elif self.parsed_vmc.not_valid_after < current_date_time: print("VMC certificate expired") self.vmc_response["errors"].append( "The VMC certificate used is expired by date: " + self.parsed_vmc.not_valid_after.strftime("%m/%d/%Y, %H:%M:%S")) return False return True # Check VMC certificate def check_vmc(self): if self.vmc_file != "": if not self.is_file: self.vmc_file = self.download_pem_path(self.vmc_file) if not self.vmc_file: self.vmc_response['status'] = False return self.vmc_response else: self.pem_file_path = self.vmc_file if self.is_vmc_extension(): # Read stored file and validate with certvalidator self.validate_vmc() # Check certificate expiry self.cert_validity() # Compare SVG in BIMI and SVG in PEM cert self.compare_pem_svg() if len(self.vmc_response['errors']) > 0: self.vmc_response['status'] = False else: self.vmc_response['status'] = True else: # Currently vmc certificate is optional self.vmc_response['status'] = "Option" return self.vmc_response