Package: keyringpy
keyringSimple encryption-at-rest with key rotation support for Python.N.B.:keyringisnotfor encrypting passwords--for that, you should use
something likebcrypt. It's meant for
encrypting sensitive data you will need to access in plain text (e.g. storing
OAuth token from users). Passwords do not fall in that category.This package is completely independent from any storage mechanisms; the goal is
providing a few functions that could be easily integrated with any ORM.InstallationAdd package to yourrequirements.txtor:pip install keyringUsageEncryptionBy default, AES-128-CBC is the algorithm used for encryption. This algorithm
uses 16 bytes keys, but you're required to use a key that's double the size
because half of that keys will be used to generate the HMAC. The first 16 bytes
will be used as the encryption key, and the last 16 bytes will be used to
generate the HMAC.Using random data base64-encoded is the recommended way. You can easily generate
keys by using the following command:$ddif=/dev/urandombs=32count=12>/dev/null|opensslbase64-AqUjOJFgZsZbTICsN0TMkKqUvSgObYxnkHDsazTqE5tM=Include the result of this command in thevaluesection of the key description
in the keyring. Half this key is used for encryption, and half for the HMAC.Key sizeThe key size depends on the algorithm being used. The key size should be double
the size as half of it is used for HMAC computation.aes-128-cbc: 16 bytes (encryption) + 16 bytes (HMAC).aes-192-cbc: 24 bytes (encryption) + 24 bytes (HMAC).aes-256-cbc: 32 bytes (encryption) + 32 bytes (HMAC).About the encrypted messageInitialization vectors (IV) should be unpredictable and unique; ideally, they
will be cryptographically random. They do not have to be secret: IVs are
typically just added to ciphertext messages unencrypted. It may sound
contradictory that something has to be unpredictable and unique, but does not
have to be secret; it is important to remember that an attacker must not be able
to predict ahead of time what a given IV will be.With that in mind,keyringusesbase64(hmac(unencrypted iv + encrypted message) + unencrypted iv + encrypted message)as the final message. If you're planning to migrate from other encryption
mechanisms or read encrypted values from the database without usingkeyring,
make sure you account for this. The HMAC is 32-bytes long and the IV is 16-bytes
long.KeyringKeys are managed through a keyring--a short python Dictionary describing your
encryption keys. The keyring must be a Dictionary object mapping numeric ids of the
keys to the key values. A keyring must have at least one key. For example:{"1":"uDiMcWVNTuz//naQ88sOcN+E40CyBRGzGTT7OkoBS6M=","2":"VN8UXRVMNbIh9FWEFVde0q7GUA1SGOie1+FgAKlNYHc="}Theidis used to track which key encrypted which piece of data; a key with a
larger id is assumed to be newer. The value is the actual bytes of the
encryption key.Key RotationWithkeyringyou can have multiple encryption keys at once and key rotation
is fairly straightforward: if you add a key to thekeyringwith a higher id
than any other key, that key will automatically be used for encryption when
objects are either created or updated. Any keys that are no longer in use can be
safely removed from thekeyring.It's extremely important that you save the keyring id returned byencrypt();
otherwise, you may not be able to decrypt values (you can always decrypt values
if you still possessallencryption keys).If you're usingkeyringto encrypt database columns, it's recommended to use
a separatedkeyringfor each table you're planning to encrypt: this allows an
easier key rotation in case you need (e.g. key leaking).N.B.: Keys are hardcoded on these examples, but you shouldn't do it on your code
base. You can retrievekeyringfrom environment variables if you're deploying
toHerokuand alike, or deploy a JSON file with your
configuration management software (e.g. Ansible, Puppet, Chef, etc).Basic usage of keyringfromkeyringimportKeyring;keys={'1':"uDiMcWVNTuz//naQ88sOcN+E40CyBRGzGTT7OkoBS6M="}encryptor=Keyring(keys,{"digest_salt":"salt-n-pepper"})# STEP 1: Encrypt message using latest encryption key.encrypted,keyringId,digest=encryptor.encrypt("super secret")print(f'🔒{encrypted}')print(f'🔑{keyringId}')print(f'🔎{digest}')#=> 🔒 Vco48O95YC4jqj44MheY8zFO2NLMPp/KILiUGbKxHvAwLd2/AN+zUG650CJzogttqnF1cGMFb//Idg4+bXoRMQ==#=> 🔑 1#=> 🔎 c39ec9729dbacd45cecd5ea9a60b15b50b0cc857# STEP 2: Decrypted message using encryption key defined by keyring id.decrypted=encryptor.decrypt(encrypted,keyringId)print(f'✉️{decrypted}')#=> ✉️ super secretChange encryption algorithmYou can choose betweenAES-128-CBC,AES-192-CBCandAES-256-CBC. By
default,AES-128-CBCwill be used.To specify the encryption algorithm, set theencryptionoption. The following
example usesAES-256-CBC.fromkeyringimportKeyringkeys={"1":"uDiMcWVNTuz//naQ88sOcN+E40CyBRGzGTT7OkoBS6M="}encryptor=Keyring(keys,{"encryption":"aes-256-cbc","digest_salt":"<custom salt>",})Exchange data with RubyIf you use Ruby, you may be interested inhttps://github.com/fnando/attr_keyring, which is able to read and write
messages using the same format.Exchange data with Node.jsIf you use Node.js, you may be interested inhttps://github.com/fnando/keyring-node, which is able to read and write
messages using the same format.DevelopmentAfter checking out the repo, runpip install -r requirements.dev.txtto install dependencies. Then,
runpytestto run the tests.ContributingBug reports and pull requests are welcome on GitHub athttps://github.com/dannluciano/keyring-python. This project is intended to be a safe,
welcoming space for collaboration, and contributors are expected to adhere to
theContributor Covenantcode of conduct.LicenseThe gem is available as open source under the terms of theMIT License.IconIcon made byIcongeek26fromFlaticonis licensed by Creative Commons BY 3.0.Code of ConductEveryone interacting in thekeyringproject’s codebases, issue trackers,
chat rooms and mailing lists is expected to follow thecode of conduct.AcknowledgmentsInspired:by@fnandoonkeyring-nodeandattr_keyringand by Corona VirusThanks toIFPIfor pay my salary!