Encrypted Streams - mikahama/mikatools GitHub Wiki
This page is for those who want to know more about the encrypted file streams in mikatools. Just remember that all file operations on mikatools accept a password and a salt, so for a majority of people, the information on this page might be a bit too detailed.
See how to
mikatools.crypto
The mikatools.crypto file provides two classes that provide the cryptographic stream functionalities. CryptoReadStream and CryptoWriteStream.
About the encryption used
Mikatools uses Fernet encryption for symmetric encryption. All mikatools file handlers take in a password and a salt. The key used in the cryptographic process is derived from the two in the following way:
import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt.encode(),
iterations=100000,
backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(password))
f = Fernet(key)
For practical reasons, every time write(text) is called in CryptoWriteStream, the text is split by a new line (\n) and each split is encrypted separately. The line breaks are retained in the encrypted text. This is done so that readline() functionality in CryptoReadStream and can easily read the file line by line. To enhance security, you can call write(text, line_breaks=False), but this will make readline() read the entire file regardless of the line breaks in the plain text.
Every time write(text) is called, the output is encoded with base64 and a block separating character ! is placed at the end of the written stream. This is again done because of practical reasons to make write(text) behave exactly like a regular file stream. To enhance security, it is advisable to call write(text) only once storing everything in the file at once. This way, there is no telling how long different parts of the encrypted text are.
Keys and asymmetric encryption
All mikatools file handlers (open_read, open_wrte, json_load, pickle_load...) accept two parameters for an asymmetric encryption: key and key_password. The key parameter can be a key object (from generate_keys or load_key) or it can be a file path or the RSA key as a string.
Any of the following is possible:
from mikatools import crypto
from mikatools import *
private, public = crypto.generate_keys()
json_dump(secret_data, "secret.json", key=public)
json_dump(secret_data, "secret.json", key="/path/to/my/id_rsa.pub")
json_dump(secret_data, "secret.json", key="-----BEGIN RSA PUBLIC KEY-----...The rest of the key")
Generating keys
from mikatools import crypto
private, public = crypto.generate_keys()
This generates a new private-public key pair.
Loading a key
It is possible to load key from a file as well. If the key is encrypted, key_password parameter can be specified.
from mikatools import crypto
public = crypto.load_key("id_rsa.pub")
private = crypto.load_key("id_rsa", key_password="my_secret_password")
If you happen to have the key as a string in memory, you can load that key as well. NB the key must start with -----BEGIN. If the key is encrypted, key_password parameter can be specified.
from mikatools import crypto
private = crypto.load_key("-----BEGIN RSA PRIVATE KEY-----...The rest of the key")
Saving a key
You can use save_key(key, path, key_password=None) to save your private and public key. The optional parameter key_password will set a password for encrypting the RSA key.
from mikatools import crypto
private, public = crypto.generate_keys()
crypto.save_key(private, "id_rsa", key_password="your_optional_password")
crypto.save_key(public, "id_rsa.pub")