Security
This module is responsible for securing the transfer of result files contained in the docker images that represent PHT Trains. This is done by encrypting the resulting files using envelope encryption.
Additionally the progression of the train, the validity of the image and the results, as well as the identities of the participating stations are validated before a participant executes a train image.
TODO add link to paper
A more detailed description of the steps involved in the Protocol refer [link to paper].
The different stages of this protocol are used in the main DAG used by the pht station [link to station docu, repo] but can also be used independently i.e. to to offer decryption of result to authorized users at a station.
TODO add images showing how the security protocol works
The classes and functions implementing the security protocol are documented in the following sections.
Security Protocol
The following image shows the steps performed by the protocol, the pre-run protocol is performed before a train is executed and after decrypting the files checks the validity of the immutable files, if the hash of the result files corresponds to the hash sent by the previous station and the validity of the digital signature.
After a successful execution of the train container the post-run protocol calculates the hash of the results, signs the hash and the digital signature, encrypts the files using a new symmetric key. This key is encrypted using the public keys provided in the train configuration .json file and stored in the image. Before it is transferred to other stations.
- class train_lib.security.SecurityProtocol.SecurityProtocol(station_id: str, config: Union[str, dict], results_dir: Optional[str] = None, train_dir: Optional[str] = None, docker_client=None)[source]
Bases:
objectClass that performs the security protocol outlined in the security concept
- Parameters
station_id (str) – PID used to identify the station and to access the correct security values inside the train_config.json
config (Union[str, dict]) – either a string containing a path to the train_config.json or a dictionary containing the values parsed from said json file
results_dir – path to the directory containing the results
train_dir – path to the directory containing the immutable files defining a train
- post_run_protocol(img: Optional[str] = None, private_key_path: Optional[str] = None, mutable_dir: str = '/opt/pht_results')[source]
Updates the necessary values in the train_config.json and encrypts the updated files after a successful train execution.
- Parameters
img – identifier of the image <repository>:<tag>
private_key_path – path to the private key associated with the current station and with the corresponding public key registered in vault under the PID chosen by the station
mutable_dir – path to the directory in which the mutable files are stored
- Returns
- pre_run_protocol(img: Optional[str] = None, private_key_path: Optional[str] = None, immutable_dir: str = '/opt/pht_train', mutable_dir: str = '/opt/pht_results')[source]
Decrypts the files contained in the train. And performs the steps necessary to validate a train before it is being run
- Parameters
img – identifier of the image from which the security relevant files will be extracted
private_key_path –
immutable_dir –
mutable_dir –
- Returns
- sign_digital_signature(sk: cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey)[source]
Update the digital signature of the train after successful execution of the train. If there is no previous signature present creates a signature based on the session id, otherwise the signature of the previous station is loaded, signed using the current stations private key and appended to the list of signatures stored in the train.
- Parameters
sk – private key of the currently running station
- validate_immutable_files(train_dir: Optional[str] = None, files: Optional[list] = None, ordered_file_list: Optional[List[str]] = None, immutable_file_names: Optional[List[str]] = None)[source]
Checks if the hash of the immutable files is the same as the one stored at the creation of the train
- Raises
ValidationError – when the files to be executed do not match the agreed upon files
- Returns
- validate_previous_results(files: Optional[List[BinaryIO]] = None)[source]
Verify that the results from the execution of the previous station did not change, by hashing the stored results from the previous station and comparing it with the decrypted stored hash from the previous station
- verify_digital_signature()[source]
Verifies the digital signature of the train by iterating over the list of signatures and verifying each one using the correct public key stored in the train configuration json
- Raise
InvalidSignatureError if any of the signed values can not be validated using the provided public keys
Key Manager
- class train_lib.security.KeyManager.KeyManager(train_config: Union[str, dict])[source]
Bases:
objectClass that creates, stores and if necessary updates all relevant keys for symmetric and asymmetric encryption
- encrypt_symmetric_key(sym_key: bytes) dict[source]
Encrypt the symmetric key with all public keys provided in the train configuration file
- Parameters
sym_key – byte object containing the the symmetric key used to encrypt the mutable files
- Returns
dictionary containing the symmetric key encrypted with all available public keys, keys are the station
ids and values are the symmetric key encrypted with the RSA public key associated with the station id
- Return type
- generate_encrypted_keys(symmetric_key: bytes)[source]
Generates a dictionary containing the symmetric key used to encrypt files, encrypted with the public keys of all stations on the route :param symmetric_key: byte object containing the symmetric key used to encrypt the mutable files :return: Dictionary consisting of key = Station Id, value = Symmetric key encrypted with public key of station
- static generate_symmetric_key()[source]
Create a symmetric fernet key for encrypting sensitive files :return:
- get_security_param(param: str)[source]
Returns a parameter from the associated keyfile :param param: :return: value of the specified parameter
- get_sym_key(station_id: str, private_key_path: Optional[str] = None)[source]
Decrypts the symmetric key using a stored private key :arg station_id: station identifier used to load the correct public key :return: symmetric fernet key used to encrypt and decrypt files
- static load_private_key(env_key: Optional[str] = None, key_path: Optional[str] = None)[source]
Loads the private key from the path provided provided in the environment variables of the currently running image :param key_path: path to a file containing the private key :param env_key: environment variable containing a hex string representing the station private key :return: a private key object either rsa or ec
- static load_public_key(key: str)[source]
Loads a public key :param key: string representation of a public key :return: public key object for asymmetric encryption
Hashing
- train_lib.security.Hashing.hash_immutable_files(immutable_files: Union[List[str], List[BinaryIO]], user_id: str, session_id: bytes, binary_files=False, ordered_file_list: Optional[list] = None, immutable_file_names: Optional[List[str]] = None)[source]
Calculates the hash of all immutable files in the train, A, R, Q as well as the :param binary_files: boolean parameter indicating whether the files are binary files or file paths :param user_id: :param session_id: :param immutable_files: :return: byte object representing the hash of all files
Symmetric Encryption
- class train_lib.security.SymmetricEncryption.FileEncryptor(key: bytes)[source]
Bases:
objectPerforms symmetric encryption and decryption of sensitive files belonging to the train cargo
Docker Image Validation
- train_lib.docker_util.validate_master_image.validate_train_image(train_img: str, master_image: str)[source]
Validates a train image against an official master image :param train_img: identifier of the docker image defining a train :param master_image: identifier of the master docker image to validate against :return: