def from_file(cls, file_path, password=None, encoding='utf-8'): """ Load the signing key from the specified file. If *password* is specified, the file is assumed to have been encrypted using OpenSSL with ``aes-256-cbc`` as the cipher and ``sha256`` as the message digest. This uses :py:func:`.openssl_decrypt_data` internally for decrypting the data. :param str file_path: The path to the file to load. :param str password: An optional password to use for decrypting the file. :param str encoding: The encoding of the data. :return: A tuple of the key's ID, and the new :py:class:`.SigningKey` instance. :rtype: tuple """ with open(file_path, 'rb') as file_h: file_data = file_h.read() if password: file_data = openssl_decrypt_data(file_data, password, encoding=encoding) file_data = file_data.decode(encoding) file_data = serializers.JSON.loads(file_data) utilities.validate_json_schema(file_data, 'king-phisher.security.key') return cls.from_dict(file_data['signing-key'], encoding=file_data.pop('encoding', 'base64'), id=file_data['id'])
def _load_key_store(self, file_name): file_path = find.data_file(file_name) if not file_path: return 0 with open(file_path, 'r') as file_h: key_store = serializers.JSON.load(file_h) utilities.validate_json_schema(key_store, 'king-phisher.security') key_store = key_store['keys'] loaded = 0 for key_idx, key in enumerate(key_store, 1): identifier = key['id'] if identifier in self.keys: self.logger.warning( "skipping loading {0}:{1} due to a duplicate id".format( file_name, key_idx)) continue verifying_key = key['verifying-key'] key['verifying-key'] = VerifyingKey.from_dict( verifying_key, encoding=verifying_key.pop('encoding', 'base64')) self.keys[identifier] = key self.logger.debug("loaded key id: {0} from: {1}".format( identifier, file_path)) loaded += 1 return loaded
def __init__(self, data, keys=None): """ :param dict data: The formatted catalog data. :param keys: The keys to use for verifying remote data. :type keys: :py:class:`~king_phisher.security_keys.SecurityKeys` """ utilities.validate_json_schema(data, 'king-phisher.catalog') self.security_keys = keys or security_keys.SecurityKeys() """The :py:class:`~king_phisher.security_keys.SecurityKeys` used for verifying remote data.""" self.created = dateutil.parser.parse(data['created']) """The timestamp of when the remote data was generated.""" self.maintainers = tuple(maintainer['id'] for maintainer in data['maintainers']) """ A tuple containing the maintainers of the catalog and repositories. These are also the key identities that should be present for verifying the remote data. """ self.repositories = tuple( Repository(repo, keys=self.security_keys) for repo in data['repositories']) """A tuple of the :py:class:`.Repository` objects included in this catalog.""" self.logger.info("initialized catalog with {0:,} repositories".format( len(self.repositories)))
def test_schema(self): file_path = find.data_file('security.json') self.assertIsNotNone(file_path, msg='failed to find the security.json file') with open(file_path, 'r') as file_h: key_store = serializers.JSON.load(file_h) try: utilities.validate_json_schema(key_store, 'king-phisher.security') except jsonschema.ValidationError: self.fail('the security.json file failed validation')
def __init__(self, data, keys=None): """ :param dict data: The formatted repository data. :param keys: The keys to use for verifying remote data. :type keys: :py:class:`~king_phisher.security_keys.SecurityKeys` """ self.security_keys = keys or security_keys.SecurityKeys() """The :py:class:`~king_phisher.security_keys.SecurityKeys` used for verifying remote data.""" self._req_sess = requests.Session() self._req_sess.mount('file://', requests_file.FileAdapter()) self.description = data.get('description') self.homepage = data.get('homepage') """The URL of the homepage for this repository if it was specified.""" self.id = data['id'] """The unique identifier of this repository.""" self.title = data['title'] """The title string of this repository.""" self.url_base = data['url-base'] """The base URL string of files included in this repository.""" self.collections = utilities.FreezableDict() """The dictionary of the different collection types included in this repository.""" if 'collections-include' in data: # include-files is reversed so the dictionary can get .update()'ed and the first seen will be the value kept for include in reversed(data['collections-include']): include_data = self._fetch_json(include) utilities.validate_json_schema( include_data, 'king-phisher.catalog.collections') include_data = include_data['collections'] for collection_type in include.get('types', COLLECTION_TYPES): collection = include_data.get(collection_type) if collection is None: continue self._add_collection_data(collection_type, collection) if 'collections' in data: for collection_type in COLLECTION_TYPES: collection = data['collections'].get(collection_type) if collection is None: continue self._add_collection_data(collection_type, collection) item_count = sum( len(collection) for collection in self.collections.values()) self.logger.debug( "initialized catalog repository with {0} collection types and {1} total items" .format(len(self.collections), item_count)) for collection_type, collection in self.collections.items(): collection.freeze() self.collections[collection_type] = Collection( self, collection_type, collection) self.collections.freeze()
def _load_metadata(path): file_path, serializer = _find_metadata(path) if file_path is None: logger.info('found no metadata file for path: ' + path) return with open(file_path, 'r') as file_h: metadata = serializer.load(file_h) # manually set the version to a string so the input format is more forgiving if isinstance(metadata.get('version'), (float, int)): metadata['version'] = str(metadata['version']) try: utilities.validate_json_schema(metadata, 'king-phisher.template.site.metadata') except jsonschema.exceptions.ValidationError: logger.error("template metadata file: {0} failed to pass schema validation".format(file_path), exc_info=True) return None return metadata
def _load_metadata(path): file_path, loader = _find_metadata(path) if file_path is None: logger.info('found no metadata file for path: ' + path) return with open(file_path, 'r') as file_h: metadata = loader(file_h) # manually set the version to a string so the input format is more forgiving if isinstance(metadata.get('version'), (float, int)): metadata['version'] = str(metadata['version']) try: utilities.validate_json_schema(metadata, 'king-phisher.template.site.metadata') except jsonschema.exceptions.ValidationError: logger.error("template metadata file: {0} failed to pass schema validation".format(file_path), exc_info=True) return None metadata['pages'] = [utilities.make_webrelpath(path) for path in metadata['pages']] return metadata
def __init__(self, data, keys=None): """ :param dict data: The formatted repository data. :param keys: The keys to use for verifying remote data. :type keys: :py:class:`~king_phisher.security_keys.SecurityKeys` """ self.security_keys = keys or security_keys.SecurityKeys() """The :py:class:`~king_phisher.security_keys.SecurityKeys` used for verifying remote data.""" self._req_sess = requests.Session() self._req_sess.mount('file://', requests_file.FileAdapter()) self.description = data.get('description') self.homepage = data.get('homepage') """The URL of the homepage for this repository if it was specified.""" self.id = data['id'] """The unique identifier of this repository.""" self.title = data['title'] """The title string of this repository.""" self.url_base = data['url-base'] """The base URL string of files included in this repository.""" self.collections = utilities.FreezableDict() """The dictionary of the different collection types included in this repository.""" if 'collections-include' in data: # include-files is reversed so the dictionary can get .update()'ed and the first seen will be the value kept for include in reversed(data['collections-include']): include_data = self._fetch_json(include) utilities.validate_json_schema(include_data, 'king-phisher.catalog.collections') include_data = include_data['collections'] for collection_type in include.get('types', COLLECTION_TYPES): collection = include_data.get(collection_type) if collection is None: continue self._add_collection_data(collection_type, collection) if 'collections' in data: for collection_type in COLLECTION_TYPES: collection = data['collections'].get(collection_type) if collection is None: continue self._add_collection_data(collection_type, collection) item_count = sum(len(collection) for collection in self.collections.values()) self.logger.debug("initialized catalog repository with {0} collection types and {1} total items".format(len(self.collections), item_count)) for collection_type, collection in self.collections.items(): collection.freeze() self.collections[collection_type] = Collection(self, collection_type, collection) self.collections.freeze()
def _load_key_store(self, file_name): file_path = find.data_file(file_name) if not file_path: return 0 with open(file_path, 'r') as file_h: key_store = serializers.JSON.load(file_h) utilities.validate_json_schema(key_store, 'king-phisher.security') key_store = key_store['keys'] loaded = 0 for key_idx, key in enumerate(key_store, 1): identifier = key['id'] if identifier in self.keys: self.logger.warning("skipping loading {0}:{1} due to a duplicate id".format(file_name, key_idx)) continue verifying_key = key['verifying-key'] key['verifying-key'] = VerifyingKey.from_dict(verifying_key, encoding=verifying_key.pop('encoding', 'base64')) self.keys[identifier] = key self.logger.debug("loaded key id: {0} from: {1}".format(identifier, file_path)) loaded += 1 return loaded
def __init__(self, data, keys=None): """ :param dict data: The formatted catalog data. :param keys: The keys to use for verifying remote data. :type keys: :py:class:`~king_phisher.security_keys.SecurityKeys` """ utilities.validate_json_schema(data, 'king-phisher.catalog') self.security_keys = keys or security_keys.SecurityKeys() """The :py:class:`~king_phisher.security_keys.SecurityKeys` used for verifying remote data.""" self.created = dateutil.parser.parse(data['created']) """The timestamp of when the remote data was generated.""" self.maintainers = tuple(maintainer['id'] for maintainer in data['maintainers']) """ A tuple containing the maintainers of the catalog and repositories. These are also the key identities that should be present for verifying the remote data. """ self.repositories = tuple(Repository(repo, keys=self.security_keys) for repo in data['repositories']) """A tuple of the :py:class:`.Repository` objects included in this catalog.""" self.logger.info("initialized catalog with {0:,} repositories".format(len(self.repositories)))
def from_url(cls, url, keys=None, encoding='utf-8'): """ Initialize a new :py:class:`.Catalog` object from a resource at the specified URL. The resulting data is validated against a schema file with :py:func:`~king_phisher.utilities.validate_json_schema` before being passed to :py:meth:`~.__init__`. :param str url: The URL to the catalog data to load. :param keys: The keys to use for verifying remote data. :type keys: :py:class:`~king_phisher.security_keys.SecurityKeys` :param str encoding: The encoding of the catalog data. :return: The new catalog instance. :rtype: :py:class:`.Catalog` """ keys = keys or security_keys.SecurityKeys() req_sess = requests.Session() req_sess.mount('file://', requests_file.FileAdapter()) cls.logger.debug('fetching catalog from: ' + url) resp = req_sess.get(url) data = resp.content.decode(encoding) data = serializers.JSON.loads(data) utilities.validate_json_schema(data, 'king-phisher.catalog') keys.verify_dict(data, signature_encoding='base64') return cls(data, keys=keys)