safedb 0.01.0001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +793 -0
  7. data/Rakefile +16 -0
  8. data/bin/safe +5 -0
  9. data/lib/configs/README.md +58 -0
  10. data/lib/extension/array.rb +162 -0
  11. data/lib/extension/dir.rb +35 -0
  12. data/lib/extension/file.rb +123 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +572 -0
  15. data/lib/factbase/facts.safedb.net.ini +38 -0
  16. data/lib/interprete.rb +462 -0
  17. data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
  18. data/lib/keytools/kdf.api.rb +243 -0
  19. data/lib/keytools/kdf.bcrypt.rb +265 -0
  20. data/lib/keytools/kdf.pbkdf2.rb +262 -0
  21. data/lib/keytools/kdf.scrypt.rb +190 -0
  22. data/lib/keytools/key.64.rb +326 -0
  23. data/lib/keytools/key.algo.rb +109 -0
  24. data/lib/keytools/key.api.rb +1391 -0
  25. data/lib/keytools/key.db.rb +330 -0
  26. data/lib/keytools/key.docs.rb +195 -0
  27. data/lib/keytools/key.error.rb +110 -0
  28. data/lib/keytools/key.id.rb +271 -0
  29. data/lib/keytools/key.ident.rb +243 -0
  30. data/lib/keytools/key.iv.rb +107 -0
  31. data/lib/keytools/key.local.rb +259 -0
  32. data/lib/keytools/key.now.rb +402 -0
  33. data/lib/keytools/key.pair.rb +259 -0
  34. data/lib/keytools/key.pass.rb +120 -0
  35. data/lib/keytools/key.rb +585 -0
  36. data/lib/logging/gem.logging.rb +132 -0
  37. data/lib/modules/README.md +43 -0
  38. data/lib/modules/cryptology/aes-256.rb +154 -0
  39. data/lib/modules/cryptology/amalgam.rb +70 -0
  40. data/lib/modules/cryptology/blowfish.rb +130 -0
  41. data/lib/modules/cryptology/cipher.rb +207 -0
  42. data/lib/modules/cryptology/collect.rb +138 -0
  43. data/lib/modules/cryptology/crypt.io.rb +225 -0
  44. data/lib/modules/cryptology/engineer.rb +99 -0
  45. data/lib/modules/mappers/dictionary.rb +288 -0
  46. data/lib/modules/storage/coldstore.rb +186 -0
  47. data/lib/modules/storage/git.store.rb +399 -0
  48. data/lib/session/fact.finder.rb +334 -0
  49. data/lib/session/require.gem.rb +112 -0
  50. data/lib/session/time.stamp.rb +340 -0
  51. data/lib/session/user.home.rb +49 -0
  52. data/lib/usecase/cmd.rb +487 -0
  53. data/lib/usecase/config/README.md +57 -0
  54. data/lib/usecase/docker/README.md +146 -0
  55. data/lib/usecase/docker/docker.rb +49 -0
  56. data/lib/usecase/edit/README.md +43 -0
  57. data/lib/usecase/edit/delete.rb +46 -0
  58. data/lib/usecase/export.rb +40 -0
  59. data/lib/usecase/files/README.md +37 -0
  60. data/lib/usecase/files/eject.rb +56 -0
  61. data/lib/usecase/files/file_me.rb +78 -0
  62. data/lib/usecase/files/read.rb +169 -0
  63. data/lib/usecase/files/write.rb +89 -0
  64. data/lib/usecase/goto.rb +57 -0
  65. data/lib/usecase/id.rb +36 -0
  66. data/lib/usecase/import.rb +157 -0
  67. data/lib/usecase/init.rb +63 -0
  68. data/lib/usecase/jenkins/README.md +146 -0
  69. data/lib/usecase/jenkins/jenkins.rb +208 -0
  70. data/lib/usecase/login.rb +71 -0
  71. data/lib/usecase/logout.rb +28 -0
  72. data/lib/usecase/open.rb +71 -0
  73. data/lib/usecase/print.rb +40 -0
  74. data/lib/usecase/put.rb +81 -0
  75. data/lib/usecase/set.rb +44 -0
  76. data/lib/usecase/show.rb +138 -0
  77. data/lib/usecase/terraform/README.md +91 -0
  78. data/lib/usecase/terraform/terraform.rb +121 -0
  79. data/lib/usecase/token.rb +35 -0
  80. data/lib/usecase/update/README.md +55 -0
  81. data/lib/usecase/update/rename.rb +180 -0
  82. data/lib/usecase/use.rb +41 -0
  83. data/lib/usecase/verse.rb +20 -0
  84. data/lib/usecase/view.rb +71 -0
  85. data/lib/usecase/vpn/README.md +150 -0
  86. data/lib/usecase/vpn/vpn.ini +31 -0
  87. data/lib/usecase/vpn/vpn.rb +54 -0
  88. data/lib/version.rb +3 -0
  89. data/safedb.gemspec +34 -0
  90. metadata +193 -0
@@ -0,0 +1,585 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+ # First use the class methods to source keys, then use a key's instance
6
+ # methods to access its properties and in concert with other symmetrical
7
+ # information, you can use the keys to lock (encrypt) or unlock (decrypt)
8
+ # other keys and objecs.
9
+ #
10
+ # == Sourcing and Deriving Keys
11
+ #
12
+ # Keys can be
13
+ #
14
+ # - sourced from a secure random byte generating function
15
+ # - sourced from ciphertext and another (decryption) key
16
+ # - generated by passing a secret through key derivation functions
17
+ # - regenerated from a secret and previously stored salts
18
+ # - sourced from the current unique workstation shell environment
19
+ # - sourced from an environment variable containing ciphertext
20
+ #
21
+ #
22
+ # Keys need to be viewed (represented) in multiple ways and the essence
23
+ # of the key viewer is to input keys {as_bits}, {as_bytes} and {as_base64}
24
+ # and then output the same key (in as far as is possible) - as bits, as
25
+ # bytes and as base64.
26
+ #
27
+ # == Key | To and From Behaviour
28
+ #
29
+ # Use the <b>From</b> methods to create Keys from a variety of resources
30
+ # such as
31
+ #
32
+ # - a base64 encoded string
33
+ # - a binary byte string
34
+ # - a string of one and zero bits
35
+ # - a hexadecimal representation
36
+ #
37
+ # Once you have instantiated the key, you will then be able to convert it
38
+ # (within reason due to bit, byte and base64 lengths) to any of the above
39
+ # key representations.
40
+ #
41
+ # == Key | Bits Bytes and Base64
42
+ #
43
+ # The shoe doesn't always fit when its on the other foot and this is best
44
+ # illustratd with a table that maps bits to 8 bit bytes and 6 bit Base64
45
+ # characters.
46
+ #
47
+ # | --------- | -------- | ------------ | ------------------------------- |
48
+ # | Fit? | Bits | Bytes | (and) Base64 |
49
+ # | --------- | -------- | ------------ | ------------------------------- |
50
+ # | Perfect | 168 Bits | is 21 bytes | 28 Chars - bcrypt chops to this |
51
+ # | Perfect | 216 Bits | is 27 bytes | 36 Chars - |
52
+ # | Perfect | 264 Bits | is 33 bytes | 44 Chars - holder 4 256bit keys |
53
+ # | Perfect | 384 Bits | is 48 bytes | 64 Chars - 216 + 168 equals 384 |
54
+ # | --------- | -------- | ------------ | ------------------------------- |
55
+ # | Imperfect | 128 Bits | 16 precisely | 22 Chars - 21 + 2 remain bits |
56
+ # | Imperfect | 186 Bits | 23 remain 2 | 31 Characers precisely |
57
+ # | Imperfect | 256 Bits | 32 precisely | 43 Chars - 42 + 4 remain bits |
58
+ # | --------- | -------- | ------------ | ------------------------------- |
59
+ #
60
+ # Yes, the shoe doesn't always fit when it's on the other foot.
61
+ #
62
+ # == Schoolboy Error
63
+ #
64
+ # <b>The strategy is so simple, we call it a schoolboy error.</b>
65
+ #
66
+ # If we want to use a key with n bits and either n % 6 or n % 8 (or both)
67
+ # are not zero - <b>we instantiate a Key</b> with the lowest common
68
+ # denominator of 6 and 8 that exceeds n.
69
+ #
70
+ # So when we request a byte, or base64 representation the viewer will
71
+ # truncate (not round down) to the desired length.
72
+ #
73
+ # == Mapping Each Character to 6 Binary Bits
74
+ #
75
+ # We need 6 binary bits to represent a base64 character (and 4
76
+ # bits for hexadecimal). Here is an example mapping between
77
+ # a base 64 character, an integer and the six bit binary.
78
+ #
79
+ # Character Integer Binary (6 Bit)
80
+ #
81
+ # a 0 000000
82
+ # b 1 000001
83
+ # c 2 000010
84
+ #
85
+ # y 25 011001
86
+ # z 26 011010
87
+ # A 27 011011
88
+ # B 28 011100
89
+ #
90
+ # 8 60 111100
91
+ # 9 61 111101
92
+ # / 62 111110
93
+ # + 63 111111
94
+ #
95
+ class Key
96
+
97
+ # Initialize a key object from a bit string of ones and zeroes provided
98
+ # in the parameter string.
99
+ #
100
+ # For example a string of 384 bits (ones and zeroes) can be thought of
101
+ # as a 48 byte key which can also be represented with 64 more compact
102
+ # base64 characters.
103
+ #
104
+ # | -------- | ------------ | -------------------------------- |
105
+ # | Bits | Bytes | Base64 |
106
+ # | -------- | ------------ | -------------------------------- |
107
+ # | 384 Bits | is 48 bytes | and 64 characters |
108
+ # | -------- | ------------ | -------------------------------- |
109
+ #
110
+ # @param the_bit_string [String]
111
+ # the bit string of ones and zeroes that represents the bits that
112
+ # represent this key
113
+ def initialize the_bit_string
114
+ @bit_string = the_bit_string
115
+ end
116
+
117
+
118
+ # Return a (secure) randomly generated super high entropy 384 bit key
119
+ # that can be stored with <b>64 base64 characters</b> and used to
120
+ # <b><em>source digest functions</em></b> that can unreversibly convert
121
+ # the key to a <b>256 bit symmetric encryption key</b>.
122
+ #
123
+ # | -------- | ------------ | -------------------------------- |
124
+ # | Bits | Bytes | Base64 |
125
+ # | -------- | ------------ | -------------------------------- |
126
+ # | 384 Bits | is 48 bytes | and 64 characters |
127
+ # | -------- | ------------ | -------------------------------- |
128
+ #
129
+ # This key easily translates to a base64 and/or byte array format because
130
+ # the 384 bit count is a <b>multiple of both 6 and 8</b>.
131
+ #
132
+ # @return [SafeDb::Key]
133
+ # return a key containing 384 random bits (or a random array of 48 bytes)
134
+ # which can if necessary be serialized into 64 base64 characters.
135
+ #
136
+ # @raise [ArgumentError]
137
+ # If a nil or zero length byte array is received.
138
+ # Or if the number of bytes <b>multiplied by 8</b>
139
+ # is <b>not a multiple of 6</b>.
140
+ def self.from_random
141
+ return Key.new( to_random_bits( RANDOM_KEY_BYTE_LENGTH ) )
142
+ end
143
+
144
+
145
+ def self.to_random_bits the_byte_length
146
+ random_bit_string = ""
147
+ for n in 1 .. the_byte_length
148
+ random_integer = SecureRandom.random_number( EIGHT_BIT_INTEGER_SIZE )
149
+ random_bit_string += "%08d" % [ random_integer.to_s(2) ]
150
+ end
151
+ return random_bit_string
152
+ end
153
+
154
+
155
+ # Return the key represented by the parameter sequence of base64
156
+ # characters.
157
+ #
158
+ # @param char64_string [String]
159
+ #
160
+ # The base64 character sequence which the returned key is
161
+ # instantiated from. Naturally this character sequencee cannot
162
+ # be nil, nor can it contain any characters that are not
163
+ # present in {Key64::YACHT64_CHARACTER_SET}.
164
+ #
165
+ # Ideally the number of parameter characters multiplied by 6
166
+ # <b>should be a multiple of eight (8)</b> otherwise the new
167
+ # key's bit string will require padding and extension.
168
+ #
169
+ # @return [SafeDb::Key]
170
+ # return a key from the parameter sequence of base64 characters.
171
+ #
172
+ # @raise [ArgumentError]
173
+ # If a nil or zero length byte array is received.
174
+ # Or if the number of bytes <b>multiplied by 8</b>
175
+ # is <b>not a multiple of 6</b>.
176
+ def self.from_char64 char64_string
177
+ return Key.new( Key64.to_bits( char64_string ) )
178
+ end
179
+
180
+
181
+ # Return a key represented by the parameter binary string.
182
+ #
183
+ # @param binary_text [String]
184
+ # The binary string that the returned key will be
185
+ # instantiated from.
186
+ #
187
+ # @return [SafeDb::Key]
188
+ # return a key from the binary byte string parameter
189
+ def self.from_binary binary_text
190
+ ones_and_zeroes = binary_text.unpack("B*")[0]
191
+ return Key.new( ones_and_zeroes )
192
+ end
193
+
194
+
195
+ # Convert a string of Radix64 characters into a key.
196
+ #
197
+ # This method converts the base64 string into the internal YACHT64 format
198
+ # and then converts that into a bit string so that a key can be instantiated.
199
+ #
200
+ # @param radix64_string [String]
201
+ # the radix64 string to convert into akey. This string will be a subset
202
+ # of the usual 62 character suspects together with period and forward
203
+ # slash characters.
204
+ #
205
+ # This parameter should not contain newlines nor carriage returns.
206
+ #
207
+ # @return [SafeDb::Key]
208
+ # return a key from the parameter sequence of base64 characters.
209
+ #
210
+ # @raise [ArgumentError]
211
+ # If a nil or zero length parameter array is received.
212
+ def self.from_radix64 radix64_string
213
+ return Key.new( Key64.from_radix64_to_bits( radix64_string ) )
214
+ end
215
+
216
+
217
+ # When a key is initialized, it is internally represented as a
218
+ # string of ones and zeroes primarily for simplicity and can be
219
+ # visualized as bits that are either off or on.
220
+ #
221
+ # Once internalized a key can also be represented as
222
+ #
223
+ # - a sequence of base64 (or radix64) characters (1 per 6 bits)
224
+ # - a binary string suitable for encryption (1 byte per 8 bits)
225
+ # - a 256bit encryption key from Digest(ing) the binary form
226
+ #
227
+ # @return [String]
228
+ # a string of literally ones and zeroes that represent the
229
+ # sequence of bits making up this key.
230
+ def to_s
231
+
232
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
233
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
234
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
235
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
236
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
237
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
238
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
239
+
240
+ ## ---------------------------------------------
241
+ ## +++++++++ WARNING ++++++++
242
+ ## ---------------------------------------------
243
+ ##
244
+ ## to_s does not need 2b called
245
+ ## So both the below print the same.
246
+ ##
247
+ ## So YOU MUST KEEP the to_s method until a proper test suite is in place.
248
+ ## So YOU MUST KEEP the to_s method until a proper test suite is in place.
249
+ ##
250
+ ## puts "#{the_key}"
251
+ ## puts "#{the_key.to_s}"
252
+ ##
253
+ ## So YOU MUST KEEP the to_s method until a proper test suite is in place.
254
+ ## So YOU MUST KEEP the to_s method until a proper test suite is in place.
255
+ ##
256
+ ## ---------------------------------------------
257
+
258
+ return @bit_string
259
+ end
260
+
261
+
262
+ # Convert this keys bit value into a printable character set
263
+ # that is suitable for storing in multiple places such as
264
+ # <b>environment variables</b> and <b>INI files</b>.
265
+ #
266
+ # @return [String]
267
+ # printable characters from a set of 62 alpha-numerics
268
+ # plus an @ symbol and a percent % sign.
269
+ #
270
+ # @raise ArgumentError
271
+ # If the bit value string for this key is nil.
272
+ # Or if the bit string length is not a multiple of six.
273
+ # Or if it contains any character that is not a 1 or 0.
274
+ def to_char64
275
+ assert_non_nil_bits
276
+ return Key64.from_bits( @bit_string )
277
+ end
278
+
279
+
280
+ # Return the <b>un-printable <em>binary</em> bytes</b> representation
281
+ # of this key. If you store 128 bits it will produce 22 characters
282
+ # because 128 divide by 6 is 21 characters and a remainder of two (2)
283
+ # bits.
284
+ #
285
+ # The re-conversion of the 22 characters will now produce 132 bits which
286
+ # is different from the original 128 bits.
287
+ #
288
+ # @return [Byte]
289
+ # a non-printable binary string of eight (8) bit bytes which can be
290
+ # used as input to both digest and symmetric cipher functions.
291
+ def to_binary
292
+ return [ to_s ].pack("B*")
293
+ end
294
+
295
+
296
+ # Return the <b>un-printable <em>binary</em> bytes</b> representation
297
+ # of this key. If you store 128 bits it will produce 22 characters
298
+ # because 128 divide by 6 is 21 characters and a remainder of two (2)
299
+ # bits.
300
+ #
301
+ # The re-conversion of the 22 characters will now produce 132 bits which
302
+ # is different from the original 128 bits.
303
+ #
304
+ # @return [Byte]
305
+ # a non-printable binary string of eight (8) bit bytes which can be
306
+ # used as input to both digest and symmetric cipher functions.
307
+ def self.to_binary_from_bit_string bit_string_to_convert
308
+ return [ bit_string_to_convert ].pack("B*")
309
+ end
310
+
311
+
312
+ # This method uses digests to convert the key's binary representation
313
+ # (which is either 48 bytes for purely random keys or 64 bytes for keys
314
+ # derived from human sourced secrets) into a key whose size is ideal for
315
+ # plying the ubiquitous <b>AES256 symmetric encryption algorithm</b>.
316
+ #
317
+ # This method should only ever be called when this key has been derived
318
+ # from either a (huge) <b>48 byte random source</b> or from a key derivation
319
+ # function (KDF) such as BCrypt, SCrypt, PBKDF2 or a union from which the
320
+ # 512 bit (64 byte) key can be reduced to 256 bits.
321
+ #
322
+ # @return [String]
323
+ # a binary string of thirty-two (32) eight (8) bit bytes which
324
+ # if appropriate can be used as a symmetric encryption key especially
325
+ # to the powerful AES256 cipher.
326
+ def to_aes_key
327
+ return Digest::SHA256.digest( to_binary() )
328
+ end
329
+
330
+
331
+ # This method uses the SHA384 digest to convert this key's binary
332
+ # representation into another (newly instantiated) key whose size
333
+ # is <b>precisely 384 bits</b>.
334
+ #
335
+ # If you take the returned key and call
336
+ #
337
+ # - {to_char64} you get a 64 character base64 string
338
+ # - {to_s} you get a string of 384 ones and zeroes
339
+ # - {to_binary} you get a 48 byte binary string
340
+ #
341
+ # @return [SafeDb::Key]
342
+ # a key with a bit length (ones and zeroes) of <b>precisely 384</b>.
343
+ def to_384_bit_key
344
+
345
+ a_384_bit_key = Key.from_binary( Digest::SHA384.digest( to_binary() ) )
346
+
347
+ has_384_chars = a_384_bit_key.to_s.length == 384
348
+ err_msg = "Digested key length is #{a_384_bit_key.to_s.length} instead of 384."
349
+ raise RuntimeError, err_msg unless has_384_chars
350
+
351
+ return a_384_bit_key
352
+
353
+ end
354
+
355
+
356
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
357
+ # 256bit representation of this key to encrypt the parameter key.
358
+ #
359
+ # Store the ciphertext provided by this method. To re-acquire (reconstitute)
360
+ # the parameter key use the {do_decrypt_key} decryption method with
361
+ # the ciphertext produced here.
362
+ #
363
+ # <b>Only Encrypt Strong Keys</b>
364
+ #
365
+ # Never encrypt a potentially weak key, like one derived from a human password
366
+ # (even though it is put through key derivation functions).
367
+ #
368
+ # Once generated (or regenerated) a potentially weak key should live only as
369
+ # long as it takes for it to encrypt a strong key. The strong key can then
370
+ # be used to encrypt valuable assets.
371
+ #
372
+ # <b>Enforcing Strong Key Size</b>
373
+ #
374
+ # If one key is potentially weaker than the other, the weaker key must be this
375
+ # object and the strong key is the parameter key.
376
+ #
377
+ # This method thus enforces the size of the strong key. A strong key has
378
+ # 384 bits of entropy, and is represented by 64 base64 characters.
379
+ #
380
+ # @param key_to_encrypt [SafeDb::Key]
381
+ # this is the key that will first be serialized into base64 and then locked
382
+ # down using the 256 bit binary string from this host object as the symmetric
383
+ # encryption key.
384
+ #
385
+ # This method is sensitive to the size of the parameter key and expects to
386
+ # encrypt <b>exactly 64 base64 characters</b> within the parameter key.
387
+ #
388
+ # @return [String]
389
+ # The returned ciphertext should be stored. Its breakdown is as follows.
390
+ # 96 bytes are returned which equates to 128 base64 characters.
391
+ # The random initialization vector (iv) accounts for the first 16 bytes.
392
+ # The actual crypt ciphertext then accounts for the final 80 bytes.
393
+ #
394
+ # @raise [ArgumentError]
395
+ # the size of the parameter (strong) key is enforced to ensure that it has
396
+ # exactly 384 bits of entropy which are represented by 64 base64 characters.
397
+ def do_encrypt_key key_to_encrypt
398
+
399
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
400
+
401
+ crypt_cipher.encrypt()
402
+ random_iv = crypt_cipher.random_iv()
403
+ crypt_cipher.key = to_aes_key()
404
+
405
+ calling_module = File.basename caller_locations(1,1).first.absolute_path, ".rb"
406
+ calling_method = caller_locations(1,1).first.base_label
407
+ calling_lineno = caller_locations(1,1).first.lineno
408
+ caller_details = "#{calling_module} | #{calling_method} | (line #{calling_lineno})"
409
+
410
+ cipher_text = crypt_cipher.update( key_to_encrypt.to_char64 ) + crypt_cipher.final
411
+
412
+ binary_text = random_iv + cipher_text
413
+ ones_zeroes = binary_text.unpack("B*")[0]
414
+ ciphertxt64 = Key64.from_bits( ones_zeroes )
415
+
416
+ size_msg = "Expected bit count is #{EXPECTED_CIPHER_BIT_LENGTH} not #{ones_zeroes.length}."
417
+ raise RuntimeError, size_msg unless ones_zeroes.length == EXPECTED_CIPHER_BIT_LENGTH
418
+
419
+ return ciphertxt64
420
+
421
+ end
422
+
423
+
424
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
425
+ # 256bit representation of this key to decrypt the parameter ciphertext and
426
+ # return the previously encrypted key.
427
+ #
428
+ # To re-acquire (reconstitute) the original key call this method with the
429
+ # stored ciphertext that was returned by the {do_encrypt_key}.
430
+ #
431
+ # <b>Only Encrypt Strong Keys</b>
432
+ #
433
+ # Never encrypt a potentially weak key, like one derived from a human password
434
+ # (even though it is put through key derivation functions).
435
+ #
436
+ # Once generated (or regenerated) a potentially weak key should live only as
437
+ # long as it takes for it to encrypt a strong key. The strong key can then
438
+ # be used to encrypt valuable assets.
439
+ #
440
+ # <b>Enforcing Strong Key Size</b>
441
+ #
442
+ # If one key is potentially weaker than the other, the weaker key must be this
443
+ # object and the strong key is reconstituted and returned by this method.
444
+ #
445
+ # @param ciphertext_to_decrypt [String]
446
+ # Provide the ciphertext produced by our sister key encryption method.
447
+ # The ciphertext should hold 96 bytes which equates to 128 base64 characters.
448
+ # The random initialization vector (iv) accounts for the first 16 bytes.
449
+ # The actual crypt ciphertext then accounts for the final 80 bytes.
450
+ #
451
+ # @return [Key]
452
+ # return the key that was serialized into base64 and then encrypted (locked down)
453
+ # with the 256 bit binary symmetric encryption key from this host object.
454
+ #
455
+ # @raise [ArgumentError]
456
+ # the size of the parameter ciphertext must be 128 base 64 characters.
457
+ def do_decrypt_key ciphertext_to_decrypt
458
+
459
+ bit_text = Key64.to_bits(ciphertext_to_decrypt)
460
+ size_msg = "Expected bit count is #{EXPECTED_CIPHER_BIT_LENGTH} not #{bit_text.length}."
461
+ raise RuntimeError, size_msg unless bit_text.length == EXPECTED_CIPHER_BIT_LENGTH
462
+
463
+ cipher_x = OpenSSL::Cipher::AES256.new(:CBC)
464
+ cipher_x.decrypt()
465
+
466
+ rawbytes = [ bit_text ].pack("B*")
467
+
468
+ cipher_x.key = to_aes_key()
469
+ cipher_x.iv = rawbytes[ 0 .. ( RANDOM_IV_BYTE_COUNT - 1 ) ]
470
+ key_chars_64 = cipher_x.update( rawbytes[ RANDOM_IV_BYTE_COUNT .. -1 ] ) + cipher_x.final
471
+
472
+ return Key.from_char64( key_chars_64 )
473
+
474
+ end
475
+
476
+
477
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
478
+ # 256bit representation of this key to encrypt the parameter plaintext using
479
+ # the parameter random initialization vector.
480
+ #
481
+ # Store the ciphertext provided by this method. To re-acquire (reconstitute)
482
+ # the plaintext use the {do_decrypt_text} decryption method, giving
483
+ # it the same initialization vector and the ciphertext produced here.
484
+ #
485
+ # <b>Only Encrypt Once</b>
486
+ #
487
+ # Despite the initialization vector protecting against switch attacks you
488
+ # should <b>only use this or any other key once</b> to encrypt an object.
489
+ # While it is okay to encrypt small targets using two different keys, it
490
+ # pays not to do the same when the target is large.
491
+ #
492
+ # @param random_iv [String]
493
+ # a randomly generated 16 byte binary string that is to be used as the
494
+ # initialization vector (IV) - this is a requirement for AES encryption
495
+ # in CBC mode - this IV does not need to be treated as a secret
496
+ #
497
+ # @param plain_text [String]
498
+ # the plaintext or binary string to be encrypted. To re-acquire this string
499
+ # use the {do_decrypt_text} decryption method, giving it the same
500
+ # initialization vector (provided in the first parameter) and the ciphertext
501
+ # returned from this method.
502
+ #
503
+ # @return [String]
504
+ # The returned binary ciphertext should be encoded and persisted until such
505
+ # a time as its re-acquisition by authorized parties becomes necessary.
506
+ def do_encrypt_text random_iv, plain_text
507
+
508
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
509
+
510
+ crypt_cipher.encrypt()
511
+ crypt_cipher.iv = random_iv
512
+ crypt_cipher.key = to_aes_key()
513
+
514
+ return crypt_cipher.update( plain_text ) + crypt_cipher.final
515
+
516
+ end
517
+
518
+
519
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
520
+ # 256bit representation of this key to decrypt the parameter ciphertext using
521
+ # the parameter random initialization vector.
522
+ #
523
+ # Use this method to re-acquire (reconstitute) the plaintext that was
524
+ # converted to ciphertext by the {do_encrypt_text} encryption method,
525
+ # naturally using the same initialization vector for both calls.
526
+ #
527
+ # <b>Only Decrypt Once</b>
528
+ #
529
+ # Consider <b>a key spent</b> as soon as it decrypts the one object it was
530
+ # created to decrypt. Like a bee dying after a sting, a key should die after
531
+ # it decrypts an object. Should re-decryption be necessary - another key
532
+ # should be derived or generated.
533
+ #
534
+ # @param random_iv [String]
535
+ # a randomly generated 16 byte binary string that is to be used as the
536
+ # initialization vector (IV) - this is a requirement for AES decryption
537
+ # in CBC mode - this IV does not need to be treated as a secret
538
+ #
539
+ # @param cipher_text [String]
540
+ # the ciphertext or binary string to be decrypted in order to re-acquire
541
+ # (reconstitute) the plaintext that was converted to ciphertext by the
542
+ # {do_encrypt_text} encryption method.
543
+ #
544
+ # @return [String]
545
+ # if the plaintext (or binary string) returned here still needs to be
546
+ # kept on the low, derive or generate another key to protect it.
547
+ def do_decrypt_text random_iv, cipher_text
548
+
549
+ raise ArgumentError, "Incoming cipher text cannot be nil." if cipher_text.nil?
550
+
551
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
552
+
553
+ crypt_cipher.decrypt()
554
+ crypt_cipher.iv = random_iv
555
+ crypt_cipher.key = to_aes_key()
556
+
557
+ return crypt_cipher.update( cipher_text ) + crypt_cipher.final
558
+
559
+ end
560
+
561
+
562
+ private
563
+
564
+
565
+ RANDOM_KEY_BYTE_LENGTH = 48
566
+
567
+ EIGHT_BIT_INTEGER_SIZE = 256
568
+
569
+ RANDOM_IV_BYTE_COUNT = 16
570
+
571
+ CIPHERTEXT_BYTE_COUNT = 80
572
+
573
+ EXPECTED_CIPHER_BIT_LENGTH = ( CIPHERTEXT_BYTE_COUNT + RANDOM_IV_BYTE_COUNT ) * 8
574
+
575
+
576
+ def assert_non_nil_bits
577
+ nil_err_msg = "The bit string for this key is nil."
578
+ raise RuntimeError, nil_err_msg if @bit_string.nil?
579
+ end
580
+
581
+
582
+ end
583
+
584
+
585
+ end