Class: SafeDb::ToolBelt::Cipher
- Inherits:
-
Object
- Object
- SafeDb::ToolBelt::Cipher
- Defined in:
- lib/utils/ciphers/cipher.rb
Overview
Cipher is a base class that enables cipher varieties to be plugged and played with minimal effort. This Cipher implements much of the use case functionality - all extension classes need to do, is to subclass and implement only the core behaviour that define its identity.
Double Encryption | Cipher Parent vs Cipher Child
Double encryption first with a symmetric and then an asymmetric one fulfills the safe
promise of making the stored ciphertext utterly worthless.
The child ciphers implement the inner symmetric encyption whilst the parent implements the outer asymmetric encryption algorithm.
The process is done twice resulting in two stores that are mirrored in structure. The front end store holds doubly encrypted keys whist the backend store holds the doubly encrypted secrets.
Attackers wouldn’t be able to distinguish one from the other. Even if they theoretically cracked the asymmetric encryption - they would then be faced with a powerful symmetric encryption algorithm which could be any one of the leading ciphers such as TwoFish or the Advanced Encryption Standard (AES).
Ciphers at 3 Levels
Ciphers are implemented at three distinct levels.
Low Level Ciphers
Low level ciphers are given text to encrypt and an instantiated dictionary in which to place the encryption parameters such as keys and initialization vectors (iv)s.
Some more specific ciphers can handle authorization data for example the Galois Counter Mode (GCM) cipher.
Low level ciphers know nothing about text IO nor reading and writing to persistence structures like files, queues and databases.
Mid Level Ciphers
Mid level ciphers talk to the low level ciphers and bring in input and output textual formats like SafeDb’s two-part block structures.
Mid level ciphers still know nothing of persistence structures like files, queues and databases.
Use Case Level Ciphers
The ciphers operating at the use case level talk to mid level ciphers. They interact with the safe store API which brings persistence functions such as read/write as well as remoting functions such as push/pull.
Use Case level ciphers interact with the latest crypt technologies due to interface separation. Also they talk classes implementing persistence stores allowing assets liek Git, S3, DropBox, simple files, SSH filesystems, Samba to hold locked key and material crypts.
Databases stores will be introduced soon allowing safe to plug in and exploit database managers like Mongo, Hadoop, MySQL, Maria, and PostgreSQL.
Plugging into DevOps orchestration platforms like Terraform, Ansible, Chef and Puppet will soon be available. Add this with integrations to other credential managers like HashiCorp’s Vault, Credstash, Amazon KMS, Git Secrets, PGP, LastPass, KeePass and KeePassX.
How to Implement a Cipher
Extend this base class to inherit lots of unexciting
functionality that essentially
-
manages the main encryption and decryption use case flow
-
concatenates
the symmetric encryption meta data with ciphertext after encryption -
splits and objectifies the key/value metadata plus ciphertext before decryption
-
handles file read/writes in conjunction with the store plugins
-
handles
exceptions
and malicious input detection and incubation -
_performs the asymmetric encryption_ of the cipher’s symmetrically encrypted output
What Behaviour Must Ciphers Implement
Ciphers bring the cryptographic mathematics and implementation algorithms to the table. So when at home they must implement
-
do_symmetric_encryption(plain_text)
- resulting in ciphertext -
do_symmetric_decryption(ciphertext, encryption_dictionary)
» plaintext
and also set the @dictionary
hash (map) of pertinent key/value pairs including the encryption algorithm, the encryption key and the ciphertext signature to thwart any at-rest tampering.
That’s It. Cipher children can rely on the Cipher parent to do the nitty gritty of file-handling plus managing stores and paths.
Class Method Summary collapse
-
.decrypt_it(private_key, os_block_text) ⇒ String
This method takes and safe formatted cipher-text block generated by selfself.encrypt_it and returns the original message that has effectively been doubly encrypted using a symmetric and asymmetric cipher.
-
.encrypt_it(public_key, payload_text) ⇒ String
Ciphers use symmetric algorithms to encrypt the given text, which is then wrapped up along with the encryption key and other metadata pertinent to the algorithm, they then encrypt this bundle with the public key provided and return the text that can safely be stored in a text file.
Class Method Details
.decrypt_it(private_key, os_block_text) ⇒ String
This method takes and safe formatted cipher-text block generated by selfself.encrypt_it and returns the original message that has effectively been doubly encrypted using a symmetric and asymmetric cipher. This type of encryption is standard best practice when serializing secrets.
safe cipher-text blocks look like a two(2) part bundle but they are actually a three(3) part bundle because the second part is in itself an amalgam of two distinct objects, serialized as text blocks.
The 3 SafeDb Blocks
Even though the incoming text appears to contain two (2) blocks, it actually contains three (3).
-
a massive symmetric encryption key (locked by an asymmetric keypair)
-
a dictionary denoting the algorithm and parameters used to encrypt the 3rd block
-
the true message whose encryption is parametized by the dictionary (in 2nd block)
The second and third block are only revealed by asymmetrically decrypting the key in the first block and using it to symmetrically decrypt what appears to be a unified second block.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/utils/ciphers/cipher.rb', line 185 def self.decrypt_it private_key, os_block_text first_block = Base64.decode64( CryptIO.outer_crypt_deserialize os_block_text, true ) trail_block = Base64.decode64( CryptIO.outer_crypt_deserialize os_block_text, false ) decrypt_key = private_key.private_decrypt first_block inner_block = Blowfish.decryptor( trail_block, decrypt_key ) crypt_props = Hash.new cipher_text = CryptIO.inner_crypt_deserialize( crypt_props, inner_block ) return Aes256.do_decrypt( crypt_props, cipher_text ) end |
.encrypt_it(public_key, payload_text) ⇒ String
Ciphers use symmetric algorithms to encrypt the given text, which is then wrapped up along with the encryption key and other metadata pertinent to the algorithm, they then encrypt this bundle with the public key provided and return the text that can safely be stored in a text file.
Ciphers should never interact with the filesystem which makes them reusable in API and remote store scenarios.
Binary files should be converted into the base64 format before being presented to ciphers.
Every component in the pipeline bears the responsibility for nullifying and rejecting malicious content.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/utils/ciphers/cipher.rb', line 129 def self.encrypt_it public_key, payload_text crypt_data = {} crypted_payload = Base64.encode64( Aes256.do_encrypt( crypt_data, payload_text ) ) unified_material = CryptIO.inner_crypt_serialize crypt_data, crypted_payload outer_crypt_key = Engineer.strong_key( 128 ) crypted_cryptkey = Base64.encode64( public_key.public_encrypt( outer_crypt_key ) ) crypted_material = Base64.encode64(Blowfish.encryptor unified_material, outer_crypt_key) return CryptIO.outer_crypt_serialize( crypted_cryptkey, crypted_material ) end |