opensecret 0.0.960 → 0.0.962

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b95962b0946200185b87257ef12fbfab9e689ecd
4
- data.tar.gz: 40eb3bb6b0a401192e33a92b23053d35755f7c51
3
+ metadata.gz: 06aed72d992d1b8f9ae2533681c47e20d9974e05
4
+ data.tar.gz: 04b41b862706f7f98d95f78bdbc18405c0e5b80a
5
5
  SHA512:
6
- metadata.gz: 8e6c1c61a35e114c6e20e5d8192c3d1a2b996fb6bb65ebd58858f268d2839d5d629824883b2efc8ef8e50faf064f13efcf77e850986313baa168fb0ea06caf10
7
- data.tar.gz: 8f511f4f83d18d85a3891ef504bef7b60a8f8bcb59e427d86f24acf928315d765132be999a0aa63a2aec4cd818e7b96ab61df59346fc6753c94c30fa4bc14b66
6
+ metadata.gz: 993f8128c462a648fb3599326cac9ee378a17d8bbaea620aee11223a6b10584205df2c2c68b2856223befccb2a0e02207188ddbd80dfe3e0bd615c6f1d95a66f
7
+ data.tar.gz: 6684892e42493e09e13549591ef8b048ad95d0b586c4a8f1226e4d1f063fc42401241326a814c35c78699aa2a2d441fceb9f69ea9ce050c909eed090258168dd
@@ -19,7 +19,7 @@ class Trial
19
19
 
20
20
  end
21
21
 
22
- Trial.ciphername
22
+ ####### ======> Trial.ciphername
23
23
 
24
24
 
25
25
  def self.certify
data/lib/opensecret.rb CHANGED
@@ -65,21 +65,52 @@ class CliInterpreter < Thor
65
65
  end
66
66
 
67
67
 
68
- # Description of the open "wake-up" call.
69
- desc "open CONTEXT_PATH", "CONTEXT_PATH context path to the family of secrets to be created."
68
+ # Description of the open use case command.
69
+ desc "open OUTER_PATH", "OUTER_PATH to envelope of secrets to stuff and then lock."
70
70
 
71
71
  # Open up a conduit from which we can add, subtract, update and list secrets
72
72
  # before they are committed (and pushed) into permanent locked storage.
73
73
  #
74
- # @param context_path [String] the path to USB key for storing encrypted keys
75
- def open context_path
74
+ # @param outer_path [String] the path to USB key for storing encrypted keys
75
+ def open outer_path
76
76
 
77
77
  open_uc = OpenSecret::Open.new
78
- open_uc.context_path = context_path
78
+ open_uc.outer_path = outer_path
79
79
  open_uc.flow_of_events
80
80
 
81
81
  end
82
82
 
83
+ # Description of the unlock use case command.
84
+ desc "unlock OUTER_PATH", "OUTER_PATH to locked secrets to open for reading or stuffing."
85
+
86
+ # If confident that command history cannot be exploited to gain the human password
87
+ # or if the agent running opensecret is itself a script, the <tt>with</tt> option can
88
+ # be used to convey the password.
89
+ option :with
90
+
91
+ # Unlock a secrets envelope at the specified outer path so that we can read, put
92
+ # and discard secrets.
93
+ #
94
+ # This use case requires the human (agent) password unless the <tt>--no-human-password</tt>
95
+ # flag was posted along with the <tt>init</tt> command.
96
+ #
97
+ # There are two ways to provide the password (for the <b><em>my/gadgets</em></b> group)
98
+ #
99
+ # - <tt>opensecret unlock my/gadgets</tt> and respond to the password prompt (or)
100
+ # - <tt>opensecret unlock my/gadgets --with="hUM4n-0pen$3cr3t"</tt>
101
+ #
102
+ # If providing the password on the command line, one must be confident that the shell's
103
+ # command history cannot be exploited to capture it.
104
+ #
105
+ # @param outer_path [String] the path to the (previously) locked secrets in frozen storage.
106
+ def unlock outer_path
107
+
108
+ unlock_uc = OpenSecret::Unlock.new
109
+ unlock_uc.outer_path = outer_path
110
+ unlock_uc.master_p4ss = options[:with] if options[:with]
111
+ unlock_uc.flow_of_events
112
+
113
+ end
83
114
 
84
115
  # Description of the put secret command.
85
116
  desc "put <secret_id> <secret_value>", "put secret like login/username into opened context."
@@ -28,6 +28,50 @@ module OpenSecret
28
28
  # with a powerful symmetric encryption algorithm which could be any one of the
29
29
  # leading ciphers such as TwoFish or the Advanced Encryption Standard (AES).
30
30
  #
31
+ # == Ciphers at 3 Levels
32
+ #
33
+ # Ciphers are implemented at three distinct levels.
34
+ #
35
+ # <b>Low Level Ciphers</b>
36
+ #
37
+ # Low level ciphers are given text to encrypt and an instantiated dictionary
38
+ # in which to place the encryption parameters such as keys and initialization
39
+ # vectors (iv)s.
40
+ #
41
+ # Some more specific ciphers can handle authorization data for example the
42
+ # Galois Counter Mode (GCM) cipher.
43
+ #
44
+ # Low level ciphers know nothing about text IO nor reading and writing to
45
+ # persistence structures like files, queues and databases.
46
+ #
47
+ # <b>Mid Level Ciphers</b>
48
+ #
49
+ # Mid level ciphers talk to the low level ciphers and bring in input and output
50
+ # textual formats like OpenSecret's two-part block structures.
51
+ #
52
+ # Mid level ciphers still know nothing of persistence structures like files,
53
+ # queues and databases.
54
+ #
55
+ # <b>Use Case Level Ciphers</b>
56
+ #
57
+ # The ciphers operating at the use case level talk to mid level ciphers. They
58
+ # interact with the <b>opensecret store API</b> which brings persistence
59
+ # functions such as <b>read/write</b> as well as remoting functions such as
60
+ # <b>push/pull</b>.
61
+ #
62
+ # Use Case level ciphers interact with the latest crypt technologies due to
63
+ # interface separation. Also they talk classes implementing persistence stores
64
+ # allowing assets liek Git, S3, DropBox, simple files, SSH filesystems, Samba
65
+ # to hold locked key and material crypts.
66
+ #
67
+ # Databases stores will be introduced soon allowing opensecret to plug in and
68
+ # exploit database managers like Mongo, Hadoop, MySQL, Maria, and PostgreSQL.
69
+ #
70
+ # Plugging into DevOps orchestration platforms like Terraform, Ansible, Chef
71
+ # and Puppet will soon be available. Add this with integrations to other credential
72
+ # managers like HashiCorp's Vault, Credstash, Amazon KMS, Git Secrets, PGP,
73
+ # LastPass, KeePass and KeePassX.
74
+ #
31
75
  # == How to Implement a Cipher
32
76
  #
33
77
  # Extend this base class to inherit lots of +unexciting+ functionality
@@ -40,7 +84,6 @@ module OpenSecret
40
84
  # - handles +exceptions+ and +malicious input detection+ and incubation
41
85
  # - +_performs the asymmetric encryption_+ of the cipher's symmetrically encrypted output
42
86
  #
43
- #
44
87
  # == What Behaviour Must Ciphers Implement
45
88
  #
46
89
  # Ciphers bring the cryptographic mathematics and implementation algorithms
@@ -57,80 +100,10 @@ module OpenSecret
57
100
  # do the nitty gritty of file-handling plus managing stores and paths.
58
101
  class Cipher
59
102
 
60
- # Many ciphers (like Blowfish) constrains plain text lengths to multiples
61
- # of 8 (or 16) and a common +right pad with spaces+ strategy is employed
62
- # as a workaround. opensecret does it diferently.
63
- #
64
- # == Why isn't Space Padding Used?
65
- #
66
- # If opensecret padded plaintext (ending in one or more spaces) with
67
- # spaces, the decrypt phase (after right stripping spaces) would return
68
- # plain text string +shorter than the original+.
69
- #
70
- # == So How is Padding Done?
71
- #
72
- # Instead of single space padding - opensecret uses an unlikely 7 character
73
- # padder which is repeated until the multiple is reached.
74
- #
75
- # <tt><-|@|-></tt>
76
- #
77
- # == So How is Padding Done?
78
- #
79
- # The +padder length must be a prime number+ or infinite loops could occur.
80
- #
81
- # If the padder string is likely to occur in the plain text, another
82
- # padder (or strategy) should and could be employed.
83
- #
84
- TEXT_PADDER = "<-|@|->"
85
-
86
-
87
- # An unusual string that glues together an encryption dictionary and
88
- # a chunk of base64 encoded and encrypted ciphertext.
89
- # The string must be unusual enough to ensure it does not occur within
90
- # the dictionary metadata keys or values.
91
- INNER_GLUE_STRING = "\n<-|@| < || opensecret inner crypt material axis || > |@|->\n\n"
92
-
93
-
94
- # An unusual string that glues together the asymmetrically encrypted outer
95
- # encryption key with the outer crypted text.
96
- OUTER_GLUE_STRING = "\n<-|@| < || opensecret outer crypt material axis || > |@|->\n\n"
97
-
98
-
99
- # Text header for key-value pairs hash map that will be serialized.
100
- DICTIONARY = "dictionary"
101
-
102
- # Name for the class of cipher employed.
103
- DICT_CIPHER_NAME = "cipher.class"
104
-
105
- # Name for the {Base64} encoded symmetric (lock/unlock) crypt key.
106
- DICT_CRYPT_KEY = "encryption.key"
107
-
108
- # Name for the {Base64} encoded plain text digest.
109
- DICT_PLAINTEXT_DIGEST = "plaintext.digest"
110
-
111
- # Name for the {Base64} encoded crypt material digest.
112
- DICT_MATERIAL_DIGEST = "cipher.digest"
113
-
114
- # Name for the plain text initialization vector (iv).
115
- DICT_INIT_VECTOR = "crypt.init.vector"
116
-
117
- # Name for the {Base64} encoded crypted cipher text.
118
- DICT_CIPHER_TEXT = "cipher.text"
119
-
120
-
121
- # The cipher constructor instantiates the encryption dictionary which
122
- # will be collaboratively added to by the parent and child ciphers.
123
- def initialize
124
-
125
- @dictionary = {}
126
-
127
- end
128
-
129
-
130
- # Ciphers use +symmetric algorithms+ to encrypt the given text, which
131
- # is then wrapped up along with the encryption key and other +metadata+
103
+ # Ciphers use <b>symmetric algorithms</b> to encrypt the given text, which
104
+ # is then wrapped up along with the encryption key and other <b>metadata</b>
132
105
  # pertinent to the algorithm, they then encrypt this bundle with the
133
- # +public key+ provided and return the text that can safely be stored in
106
+ # <b>public key</b> provided and return the text that can safely be stored in
134
107
  # a text file.
135
108
  #
136
109
  # Ciphers should never interact with the filesystem which makes them
@@ -142,158 +115,84 @@ module OpenSecret
142
115
  # Every component in the pipeline bears the responsibility for nullifying
143
116
  # and rejecting malicious content.
144
117
  #
145
- # @param public_key_text [String] textual portion of an {OpenSSL::PKey::RSA}
146
- # public key. There is no need for an asymmetric encryption agent to know
147
- # the asymmetic private key.
118
+ # @param public_key [OpenSSL::PKey::RSA]
119
+ # an {OpenSSL::PKey::RSA} public key. The unique selling point of
120
+ # asymmetric encryption is it can be done without recourse to the heavily
121
+ # protected private key. Thus the encryption process can continue with
122
+ # just a public key as long as its authenticity is assured.
148
123
  #
149
- # @param payload_text [String] plaintext (or base64 encoded) text to encrypt
124
+ # @param payload_text [String]
125
+ # plaintext (or base64 encoded) text to encrypt
150
126
  #
151
127
  # @return [String] doubly (symmetric and asymmetric) encrypted cipher text
152
- def encrypt_it public_key_text, payload_text
153
-
154
- crypted_payload = do_symmetric_encryption payload_text
128
+ def self.encrypt_it public_key, payload_text
155
129
 
156
- ######### puts JSON.pretty_generate(@dictionary)
130
+ crypt_data = {}
131
+ crypted_payload = Base64.encode64( Aes256.do_encrypt( crypt_data, payload_text ) )
132
+ unified_material = CryptIO.inner_crypt_serialize crypt_data, crypted_payload
157
133
 
158
- unified_material = unify_hash_and_text crypted_payload
159
134
  outer_crypt_key = OpenSecret::Engineer.strong_key( 128 )
160
- crypted_cryptkey = do_asymmetric_encryption public_key_text, outer_crypt_key
161
- crypted_material = Base64.encode64(Blowfish.new.encryptor unified_material, outer_crypt_key)
162
- locked_ciphertxt = unify_text_and_text crypted_cryptkey, crypted_material
163
-
164
- return locked_ciphertxt
135
+ crypted_cryptkey = Base64.encode64( public_key.public_encrypt( outer_crypt_key ) )
165
136
 
166
- end
167
-
168
-
169
- # This method takes the textual public key lacking the private key portion
170
- # as it is not needed, and encrypts the secret text with it.
171
- # It returns a Base64 encoded version of they encrypted text.
172
- #
173
- # The public key text must be compatible with {OpenSSL::PKey::RSA} as the
174
- # class will be instantiated using the public key text.
175
- #
176
- # @param public_key_text [String] the textual portion of an RSA public key
177
- # @param secret_text [String] secret text to encrypt with the public key
178
- #
179
- # @return [String] a Base64 encoded version of the encrypted secret text
180
- def do_asymmetric_encryption public_key_text, secret_text
137
+ crypted_material = Base64.encode64(Blowfish.new.encryptor unified_material, outer_crypt_key)
181
138
 
182
- asymmetric_encrypt_key = OpenSSL::PKey::RSA.new public_key_text
183
- Base64.encode64( asymmetric_encrypt_key.public_encrypt( secret_text[0..1012] ) )
139
+ return CryptIO.outer_crypt_serialize( crypted_cryptkey, crypted_material )
184
140
 
185
141
  end
186
142
 
187
143
 
188
- # The decrypted cipher-text is actually a two part bundle consisting of
189
- # a dictionary and then more cipher-text which is then decrypted using
190
- # the symmetric key held within the dictionary.
144
+ # This method takes and <b><em>opensecret formatted</em></b> cipher-text block
145
+ # generated by {self.encrypt_it} and returns the original message that has effectively
146
+ # been doubly encrypted using a symmetric and asymmetric cipher. This type of
147
+ # encryption is standard best practice when serializing secrets.
191
148
  #
192
- # The private key revealed a dictionary holding the symmetric encryption
193
- # key and other details pertinent to the cipher's encrypt/decrypt process.
194
- # This data is used to work on the cipher text, eventually revealing the
195
- # original plain text (which half the time is actually a private key).
149
+ # opensecret cipher-text blocks <b><em>look like a two(2) part bundle</em></b>
150
+ # but they are <b><em>actually a three(3) part bundle</em></b> because the second
151
+ # part is in itself an amalgam of two distinct objects, serialized as text blocks.
196
152
  #
197
- # Ciphers should never interact with the filesystem which makes them
198
- # reusable in API and remote store scenarios.
153
+ # <b>The 3 OpenSecret Blocks</b>
199
154
  #
200
- # @param private_key [String] reveals the dictionary and more ciphertext
201
- # @param cipher_text [String] the crypted (base64 encoded) text bundle
155
+ # Even though the incoming text <b><em>appears to contain two (2) blocks</em></b>,
156
+ # it <b><em>actually contains three (3)</em></b>.
202
157
  #
203
- # @return [String] the plain or encoded text first pushed into the cipher
204
- def decrypt_it private_key, cipher_text
205
-
206
- paraphernalia = do_asymmetric_decryption private_key, cipher_text
207
- return do_symmetric_decryption get_dictionary(paraphernalia), get_crypt(paraphernalia)
208
-
209
- end
210
-
211
-
212
- # Serialize and then unite a hash map and a textual chunk using
213
- # a known but unusual separator string in a manner that protects
214
- # the content integrity during the serialize and extraction phases.
215
- #
216
- # == Why an Unusual Separator String
158
+ # - a massive symmetric encryption key (locked by an asymmetric keypair)
159
+ # - a dictionary denoting the algorithm and parameters used to encrypt the 3rd block
160
+ # - the true message whose encryption is parametized by the dictionary (in 2nd block)
217
161
  #
218
- # The separator string must be unusual to make it unlikely for it
219
- # to occur in any of the map's key value pairs nor indeed the chunk
220
- # of text being glued. Were this to happen, the separate and reconstitute
221
- # phase may not accurately return the same two entities we are employed
222
- # to unite.
162
+ # The second and third block are only revealed by asymmetrically decrypting
163
+ # the key in the first block and using it to symmetrically decrypt what appears
164
+ # to be a unified second block.
223
165
  #
224
- # == Hash (Map) Contents
166
+ # @param private_key [OpenSSL::PKey::RSA]
167
+ # the <b>asymmetric private key</b> whose corresponding public key was
168
+ # employed during the encryption of a super-strong 128 character symmetric
169
+ # key embalmed by the first ciphertext block.
225
170
  #
226
- # The map contents will effectively be serialized in a simplistic
227
- # manner therefore the keys should all be strings and the value
228
- # types restricted to
171
+ # @param os_block_text [String]
172
+ # the locked cipher text is the opensecret formatted block which comes
173
+ # in two main chunks. First is the <b>long strong</b> symmetric encryption
174
+ # key crypted with the public key portion of the private key in the first
175
+ # parameter.
229
176
  #
230
- # - strings
231
- # - integers and/or floating point numbers
232
- # - booleans
177
+ # The second chunk is the symmetrically crypted text that was locked with
178
+ # the encryption key revealed in the first chunk.
233
179
  #
234
- # Binary text is not allowed neither in the map or the text chunk.
235
- # Any binary should be base64 encoded before being passed to us.
236
- #
237
- # @param text_chunk [String] the text chunk to be glued at the bottom
238
- #
239
- # @return [String] serialized and glued together result of map plus text
240
- def unify_hash_and_text text_chunk
241
-
242
- nil_or_empty_hash = @dictionary.nil? || @dictionary.empty?
243
- raise ArgumentError, "Cannot unify nil or empty metadata." if nil_or_empty_hash
244
-
245
- ini_map = IniFile.new
246
- ini_map[ DICTIONARY ] = @dictionary
247
-
248
- return ini_map.to_s + INNER_GLUE_STRING + text_chunk
249
-
250
- end
251
-
252
-
253
-
254
- # Using an outer divider (glue) - attach the asymmetrically encrypted outer
255
- # encryption key with the outer encrypted text.
256
- #
257
- # @param crypt_material_x [String] asymmetrically encrypted (encoded) outer encryption key
258
- # @param crypt_material_y [String] symmetrically encrypted inner metadata and payload crypt
259
- #
260
- # @return [String] concatenated result of the two crypt materials and divider string
261
- def unify_text_and_text crypt_material_x, crypt_material_y
262
-
263
- return crypt_material_x + OUTER_GLUE_STRING + crypt_material_y
264
-
265
- end
266
-
267
-
268
-
269
- def encrypt_usecase public_key, secret_path, stores, plain_text
270
-
271
- key_pair = KeyPair.new
272
-
273
- secret_bundle_crypt = encrypt_it key_pair.public_key, plain_text
274
- stores[1].write_path( secret_path, secret_bundle_crypt )
275
-
276
- secret_key_crypt = encrypt_it public_key, key_pair.private_key
277
- stores[0].write_path( secret_path, secret_key_crypt )
278
-
279
- end
280
-
281
-
282
-
283
- def decrypt_usecase private_key, public_key, secret_path, stores
284
-
285
- inner_private_key = decrypt_it( private_key, stores[0].read_path(secret_path) )
286
- secret_text = decrypt_it( inner_private_key, stores[1].read_path(secret_path) )
287
-
288
- encrypt_usecase public_key, secret_path, stores, secret_text
289
- return secret_text
290
-
291
- end
180
+ # @return [String]
181
+ # the doubly encrypted plain text that is locked by a symmetric key and
182
+ # that symmetric key itself locked using the public key portion of the
183
+ # private key whose crypted form is presented in the first parameter.
184
+ def self.decrypt_it private_key, os_block_text
292
185
 
186
+ first_block = Base64.decode64( CryptIO.outer_crypt_deserialize os_block_text, true )
187
+ trail_block = Base64.decode64( CryptIO.outer_crypt_deserialize os_block_text, false )
293
188
 
189
+ decrypt_key = private_key.private_decrypt first_block
190
+ inner_block = Blowfish.new.decryptor( trail_block, decrypt_key )
294
191
 
295
- def rekey_usecase old_private_key, new_public_key, secret_path, store
192
+ crypt_props = Hash.new
193
+ cipher_text = CryptIO.inner_crypt_deserialize( crypt_props, inner_block )
296
194
 
195
+ return Aes256.do_decrypt( crypt_props, cipher_text )
297
196
 
298
197
  end
299
198
 
@@ -13,9 +13,9 @@ module OpenSecret
13
13
  # dictionary which will be stored along with the ciphertext itself.
14
14
  # The dictionary includes
15
15
  #
16
- # - <tt>symmetric.cipher</tt> - the algorithm used to encrypt and decrypt
17
- # - <tt>encryption.key</tt> - hex encoded key for encrypting and decrypting
18
- # - <tt>initialize.vector</tt> - the initialization vector known as a IV (four)
16
+ # - <b>symmetric.cipher</b> - the algorithm used to encrypt and decrypt
17
+ # - <b>encryption.key</b> - hex encoded key for encrypting and decrypting
18
+ # - <b>initialize.vector</b> - the initialization vector known as a IV (four)
19
19
  #
20
20
  # == Aes256 Implemented Methods
21
21
  #
@@ -25,137 +25,121 @@ module OpenSecret
25
25
  #
26
26
  # This class implements the below methods
27
27
  #
28
- # - <tt>do_symmetric_encryption(plain_text)</tt> - resulting in ciphertext
29
- # - <tt>do_symmetric_decryption(ciphertext, encryption_dictionary)</tt> &raquo; plaintext
28
+ # - <b>do_symmetric_encryption(plain_text)</b> - resulting in ciphertext
29
+ # - <b>do_symmetric_decryption(ciphertext, encryption_dictionary)</b> &raquo; plaintext
30
30
  #
31
- # and it also sets the <tt>@dictionary</tt> hash (map) of pertinent
31
+ # and it also sets the <b>@dictionary</b> hash (map) of pertinent
32
32
  # key/value pairs including the +encryption algorithm+ and +encryption key+.
33
33
  #
34
34
  # That's It. Cipher children can rely on the {OpenSecret::Cipher} parent to
35
35
  # do the nitty gritty of file-handling plus managing stores and paths.
36
36
 
37
- class Aes256 < OpenSecret::Cipher
38
-
39
- @@initialize_vector_keyname = "initialize.vector"
37
+ class Aes256
40
38
 
41
39
  # Use the AES 256 bit block cipher and a robust strong random key plus
42
40
  # initialization vector (IV) to symmetrically encrypt the plain text.
43
41
  #
44
- # Add these key/value pairs to @dictionary instance map.
42
+ # <b>Cryptographic Properties</b>
43
+ #
44
+ # This encrypt event populates key/value pairs to the hash (dictionary) instance
45
+ # given in the parameter.
46
+ #
47
+ # A crypt properties dictionary acts as <b>output from every encryption event</b>
48
+ # and <b>input to every decryption event</b>. The most common properties include
45
49
  #
46
- # - <tt>symmetric.cipher</tt> - the algorithm used to encrypt and decrypt
47
- # - <tt>encryption.key</tt> - hex encoded key for encrypting and decrypting
48
- # - <tt>initialize.vector</tt> - the initialization vector known as a IV (four)
50
+ # - the symmetric key used for the encryption and decryption
51
+ # - the iv (initialization vector) that adds another dimension of strength
52
+ # - authorization data that thwarts switch attacks by tying context to content
53
+ # - the cipher algorithm, its implementation and its encryption strength
54
+ # - the digest of the original message for validation purposes
55
+ #
56
+ # @param e_properties [Hash]
57
+ # instantiated hash map in which the encrryption properties will
58
+ # be stuffed.
49
59
  #
50
60
  # @param plain_text [String] the plain (or base64 encoded) text to encrypt
51
61
  # @return [String] the symmetrically encrypted cipher text
52
- def do_symmetric_encryption plain_text
62
+ def self.do_encrypt e_properties, plain_text
53
63
 
54
64
  crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
55
65
  crypt_cipher.encrypt
66
+ plain_text_digest = Digest::SHA256.digest plain_text
56
67
 
57
- @dictionary[DICT_CIPHER_NAME] = crypt_cipher.class.name
58
- @dictionary[DICT_CRYPT_KEY] = Base64.urlsafe_encode64 crypt_cipher.random_key
59
- @dictionary[DICT_INIT_VECTOR] = Base64.urlsafe_encode64 crypt_cipher.random_iv
60
- @dictionary[DICT_PLAINTEXT_DIGEST] = Base64.urlsafe_encode64(Digest::SHA256.digest(plain_text))
61
-
62
- cipher_text = crypt_cipher.update( plain_text ) + crypt_cipher.final
68
+ e_properties[CryptIO::DICT_CIPHER_NAME] = crypt_cipher.class.name
69
+ e_properties[CryptIO::DICT_CRYPT_KEY] = Base64.urlsafe_encode64 crypt_cipher.random_key
70
+ e_properties[CryptIO::DICT_CRYPT_IV] = Base64.urlsafe_encode64 crypt_cipher.random_iv
71
+ e_properties[CryptIO::DICT_TEXT_DIGEST] = Base64.urlsafe_encode64 plain_text_digest
63
72
 
64
- @dictionary[DICT_CIPHER_TEXT] = Base64.urlsafe_encode64( cipher_text )
65
- @dictionary[DICT_MATERIAL_DIGEST] = Base64.urlsafe_encode64(Digest::SHA256.digest(cipher_text))
66
-
67
- return cipher_text
73
+ return crypt_cipher.update( plain_text ) + crypt_cipher.final
68
74
 
69
75
  end
70
76
 
71
77
 
72
- # Use the AES 256 bit block cipher together with the encryption key
73
- # and initialization vector (iv) sitting in the encryption_dictionary,
74
- # to symmetrically decrypt the parameter cipher text.
78
+ # Use the AES 256 bit block cipher together with the encryption key,
79
+ # initialization vector (iv) and other data found within the decryption
80
+ # properties dictionary to symmetrically decrypt the cipher text.
81
+ #
82
+ # This encrypt event in {self.do_encrypt} populated the property dictionary
83
+ # that was presumably serialized, stored, retrieved then deserialized and
84
+ # (at last) presented in the first parameter.
85
+ #
86
+ # <b>Cryptographic Properties</b>
87
+ #
88
+ # A crypt properties dictionary is the <b>output from every encryption event</b>
89
+ # and <b>input to every decryption event</b>. The most common properties include
75
90
  #
76
- # == Pre-Condition | Encryption ...
91
+ # - the symmetric key used for the encryption and decryption
92
+ # - the iv (initialization vector) that adds another dimension of strength
93
+ # - authorization data that thwarts switch attacks by tying context to content
94
+ # - the cipher algorithm, its implementation and its encryption strength
95
+ # - the digest of the original message for validation purposes
77
96
  #
78
- # This method requires the <tt>@dictionary</tt> instance
79
- # variable to have been set and to contain (amongst others)
97
+ # @param d_properties [Hash]
98
+ # the crypt properties dictionary is the <b>output from every encryption event</b>
99
+ # and (as in this case) <b>input to every decryption event</b>.
80
100
  #
81
- # - the <tt>encryption.key</tt> - hex encoded key for encrypting and decrypting
82
- # - and <tt>initialize.vector</tt> - the initialization vector known as a IV (four)
101
+ # @param cipher_text [String]
102
+ # the (already decoded) cipher text for decryption by this method using the
103
+ # encryption properties setup during the past encrypt event.
83
104
  #
84
- # @param cipher_text [String] the base64 encoded cipher text to decrypt
85
- # @return [String] decrypted plain text from symmetric key and cipher text
86
- def do_symmetric_decryption cipher_text
105
+ # @return [String]
106
+ # the plain text message originally given to be encrypted. If the message digest
107
+ # is provided within the decryption properties dictionary a sanity check will
108
+ # occur.
109
+ #
110
+ # @raise [RuntimeError]
111
+ # if decryption fails or the recalculated message digest fails an equivalence test.
112
+ def self.do_decrypt d_properties, cipher_text
113
+
114
+ decode_cipher = OpenSSL::Cipher::AES256.new(:CBC)
115
+ decode_cipher.decrypt
116
+
117
+ decode_cipher.key = Base64.urlsafe_decode64( d_properties[CryptIO::DICT_CRYPT_KEY] )
118
+ decode_cipher.iv = Base64.urlsafe_decode64( d_properties[CryptIO::DICT_CRYPT_IV] )
87
119
 
88
- abort "Implement AES 256 decryption in aes-256"
120
+ plain_text = decode_cipher.update( cipher_text ) + decode_cipher.final
121
+ assert_digest_equivalence( d_properties[CryptIO::DICT_TEXT_DIGEST], plain_text )
122
+
123
+ return plain_text
89
124
 
90
125
  end
91
126
 
92
127
 
128
+ private
129
+
93
130
 
94
- =begin
95
- encode_cipher = OpenSSL::Cipher.new('aes-256-cbc')
96
- encode_cipher.encrypt # We are encrypting
97
- key = encode_cipher.random_key
98
- iv = encode_cipher.random_iv
99
- hex_key = key.unpack("H*").first
100
- hex_iv = iv.unpack("H*").first
101
-
102
- line1 = "1>> This is secret number one over here with at @ and squiggle~ and round brakets().\n"
103
- line2 = "2>> secret number two with colon and semi :; angular <> qmarks ??.\n"
104
- line3 = "3>> secret number 3 fwd slash / and backslash twice \\ and pipe || and excla !!\n"
105
- line4 = "4>> secret 4 with pound ££ dollar $$ percent %% hat ^^ ampr && stars **\n"
106
- line5 = "5>> secret 5 with hyphens - and underscore __ and plus ++ and equal == and sqBs [[]].\n"
107
- line6 = "6>> secret 6 with double quote \"from here to here\" and \' single quotes\'.\n"
108
- line7 = "7>> secret 7 with periods .... and hashes #####\n"
109
-
110
- crypt_text = ""
111
- crypt_text += encode_cipher.update line1
112
- crypt_text += encode_cipher.update line2
113
- crypt_text += encode_cipher.update line3
114
- crypt_text += encode_cipher.update line4
115
- crypt_text += encode_cipher.update line5
116
- crypt_text += encode_cipher.update line6
117
- crypt_text += encode_cipher.update line7
118
- crypt_text += encode_cipher.final
119
- coded_crypt_text = Base64.urlsafe_encode64(crypt_text)
120
-
121
- puts ""
122
- puts "The key is #{hex_key}"
123
- puts "The IV is #{hex_iv}"
124
- puts "========================"
125
- puts "The Cipher Text is Below"
126
- puts "========================"
127
- puts coded_crypt_text
128
- puts "========================"
129
- puts crypt_text
130
- puts "========================"
131
- puts "========================"
132
- puts "========================"
133
- puts line1 + line2 + line3 + line4 + line5 + line6 + line7
134
- puts "========================"
135
- puts "========================"
136
- puts "========================"
137
- puts ""
138
- puts ""
139
-
140
- unencoded_crypt_text = Base64.urlsafe_decode64(coded_crypt_text)
141
- decode_cipher = OpenSSL::Cipher.new('aes-256-cbc')
142
-
143
- decode_cipher.decrypt
144
- decode_cipher.key = [hex_key].pack("H*")
145
- decode_cipher.iv = [hex_iv].pack("H*")
146
- first_part = decode_cipher.update( Base64.urlsafe_decode64(coded_crypt_text) )
147
- second_part = ""
148
- second_part << decode_cipher.final
149
-
150
- puts "========================"
151
- puts "Decrypted Text is Below"
152
- puts "========================"
153
- puts first_part
154
- puts "========================"
155
- puts second_part
156
- puts "========================"
157
- puts ""
158
- =end
131
+ def self.assert_digest_equivalence( digest_b4_encryption, plain_text_message )
132
+
133
+ plain_text_digest = Base64.urlsafe_encode64( Digest::SHA256.digest( plain_text_message ) )
134
+ return if digest_b4_encryption.eql? plain_text_digest
135
+
136
+ msg1 = "\nEquivalence check of original and decrypted plain text digests failed.\n"
137
+ msg2 = "Digest before encryption => #{digest_b4_encryption}\n"
138
+ msg3 = "Digest after decryption => #{plain_text_digest}\n"
139
+ error_message = msg1 + msg2 + msg3
140
+ raise RuntimeError, error_message
141
+
142
+ end
159
143
 
160
144
 
161
145
  end