opensecret 0.0.988 → 0.0.9925

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +56 -159
  3. data/bin/opensecret +2 -2
  4. data/bin/ops +17 -2
  5. data/lib/extension/string.rb +14 -16
  6. data/lib/{interpreter.rb → interprete.rb} +53 -29
  7. data/lib/keytools/binary.map.rb +49 -0
  8. data/lib/keytools/kdf.api.rb +249 -0
  9. data/lib/keytools/kdf.bcrypt.rb +64 -29
  10. data/lib/keytools/kdf.pbkdf2.rb +92 -83
  11. data/lib/keytools/kdf.scrypt.rb +190 -0
  12. data/lib/keytools/key.64.rb +326 -0
  13. data/lib/keytools/key.algo.rb +109 -0
  14. data/lib/keytools/key.api.rb +1281 -0
  15. data/lib/keytools/key.db.rb +265 -0
  16. data/lib/keytools/{key.module.rb → key.docs.rb} +55 -0
  17. data/lib/keytools/key.error.rb +110 -0
  18. data/lib/keytools/key.id.rb +271 -0
  19. data/lib/keytools/key.iv.rb +107 -0
  20. data/lib/keytools/key.local.rb +265 -0
  21. data/lib/keytools/key.mach.rb +248 -0
  22. data/lib/keytools/key.now.rb +402 -0
  23. data/lib/keytools/key.pair.rb +259 -0
  24. data/lib/keytools/key.pass.rb +120 -0
  25. data/lib/keytools/key.rb +428 -298
  26. data/lib/keytools/keydebug.txt +295 -0
  27. data/lib/logging/gem.logging.rb +3 -3
  28. data/lib/modules/cryptology/collect.rb +20 -0
  29. data/lib/session/require.gem.rb +1 -1
  30. data/lib/usecase/cmd.rb +417 -0
  31. data/lib/usecase/id.rb +36 -0
  32. data/lib/usecase/import.rb +174 -0
  33. data/lib/usecase/init.rb +78 -0
  34. data/lib/usecase/login.rb +70 -0
  35. data/lib/usecase/logout.rb +30 -0
  36. data/lib/usecase/open.rb +126 -0
  37. data/lib/{interprete → usecase}/put.rb +100 -47
  38. data/lib/usecase/read.rb +89 -0
  39. data/lib/{interprete → usecase}/safe.rb +0 -0
  40. data/lib/{interprete → usecase}/set.rb +0 -0
  41. data/lib/usecase/token.rb +111 -0
  42. data/lib/{interprete → usecase}/use.rb +0 -0
  43. data/lib/version.rb +1 -1
  44. data/opensecret.gemspec +4 -3
  45. metadata +39 -33
  46. data/lib/exception/cli.error.rb +0 -53
  47. data/lib/exception/errors/cli.errors.rb +0 -31
  48. data/lib/interprete/begin.rb +0 -232
  49. data/lib/interprete/cmd.rb +0 -621
  50. data/lib/interprete/export.rb +0 -163
  51. data/lib/interprete/init.rb +0 -205
  52. data/lib/interprete/key.rb +0 -119
  53. data/lib/interprete/open.rb +0 -148
  54. data/lib/interprete/seal.rb +0 -129
  55. data/lib/keytools/digester.rb +0 -245
  56. data/lib/keytools/key.data.rb +0 -227
  57. data/lib/keytools/key.derivation.rb +0 -341
  58. data/lib/modules/mappers/collateral.rb +0 -282
  59. data/lib/modules/mappers/envelope.rb +0 -127
  60. data/lib/modules/mappers/settings.rb +0 -170
  61. data/lib/notepad/scratch.pad.rb +0 -224
  62. data/lib/store-commands.txt +0 -180
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ module OpenKey
5
+
6
+ class KeyPass
7
+
8
+
9
+ # <tt>Collect something sensitive from the command line</tt> with a
10
+ # minimum length specified in the first parameter. This method can't
11
+ # know whether the information is a password, a pin number or whatever
12
+ # so it takes the integer minimum size at its word.
13
+ #
14
+ # <b>Question 5 to App Config | What is the Secret?</b>
15
+ #
16
+ # The client may need to acquire the secret if the answer to question 4 indicates the need
17
+ # to instantiate the keys and encrypt the application's plaintext database. The application
18
+ # should facilitate communication of the secret via
19
+ #
20
+ # - an environment variable
21
+ # - the system clipboard (cleared after reading)
22
+ # - a file whose path is a command parameter
23
+ # - a file in a pre-agreed location
24
+ # - a file in the present directory (with a pre-agreed name)
25
+ # - a URL from a parameter or pre-agreed
26
+ # - the shell's secure password reader
27
+ # - the DConf / GConf or GSettings configuration stores
28
+ # - a REST API
29
+ # - password managers like LastPass, KeePassX or 1Pass
30
+ # - the Amazon KMS (Key Management Store)
31
+ # - vaults from Ansible, Terraform and Kubernetes
32
+ # - credential managers like GitSecrets and Credstash
33
+ #
34
+ # @param prompt_twice [Boolean] indicate whether the user should be
35
+ # prompted twice. If true the prompt_2 text must be provided and
36
+ # converse is also true. A true value asserts that both times the
37
+ # user enters the same (case sensitive) string.
38
+ #
39
+ # @return [String] the collected string text ( watch out for non-ascii chars)
40
+ # @raise [ArgumentError] if the minimum size is less than one
41
+ def self.password_from_shell prompt_twice
42
+
43
+ assert_min_size MINIMUM_PASSWORD_SIZE
44
+
45
+ sleep(1)
46
+ puts "\nEnter a Password : "
47
+ first_secret = STDIN.noecho(&:gets).chomp
48
+
49
+ assert_input_text_size first_secret.length, MINIMUM_PASSWORD_SIZE
50
+ return first_secret unless prompt_twice
51
+
52
+ sleep(1)
53
+ puts "\nRe-enter the password : "
54
+ check_secret = STDIN.noecho(&:gets).chomp
55
+
56
+ assert_same_size_text first_secret, check_secret
57
+
58
+ return first_secret
59
+
60
+ end
61
+
62
+
63
+ # --
64
+ # -- Raise an exception if asked to collect text that is less
65
+ # -- than 3 characters in length.
66
+ # --
67
+ def self.assert_min_size min_size
68
+
69
+ min_length_msg = "\n\nCrypts with 2 (or less) characters open up exploitable holes.\n\n"
70
+ raise ArgumentError.new min_length_msg if min_size < 3
71
+
72
+ end
73
+
74
+
75
+ # --
76
+ # -- Output an error message and then exit if the entered input
77
+ # -- text size does not meet the minimum requirements.
78
+ # --
79
+ def self.assert_input_text_size input_size, min_size
80
+
81
+ if( input_size < min_size )
82
+
83
+ puts
84
+ puts "Input is too short. Please enter at least #{min_size} characters."
85
+ puts
86
+
87
+ exit
88
+
89
+ end
90
+
91
+ end
92
+
93
+
94
+ # --
95
+ # -- Assert that the text entered the second time is exactly (case sensitive)
96
+ # -- the same as the text entered the first time.
97
+ # --
98
+ def self.assert_same_size_text first_text, second_text
99
+
100
+ unless( first_text.eql? second_text )
101
+
102
+ puts
103
+ puts "Those two bits of text are not the same (in my book)!"
104
+ puts
105
+
106
+ exit
107
+
108
+ end
109
+
110
+ end
111
+
112
+ private
113
+
114
+ MINIMUM_PASSWORD_SIZE = 4
115
+
116
+
117
+ end
118
+
119
+
120
+ end
@@ -2,6 +2,23 @@
2
2
 
3
3
  module OpenKey
4
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
+ #
5
22
  # Keys need to be viewed (represented) in multiple ways and the essence
6
23
  # of the key viewer is to input keys {as_bits}, {as_bytes} and {as_base64}
7
24
  # and then output the same key (in as far as is possible) - as bits, as
@@ -53,54 +70,6 @@ module OpenKey
53
70
  # So when we request a byte, or base64 representation the viewer will
54
71
  # truncate (not round down) to the desired length.
55
72
  #
56
- #
57
- # == YACHT 64 | Yet Another Character Table
58
- #
59
- # This binary key class is a dab hand at converting base64 strings
60
- # into their 6-bit binary string equivalents.
61
- #
62
- # It can convert non-alphanumeric characters within either Base64 or
63
- # Radix64 into the OpenKey YACHT64 standard which has a forward slash
64
- # but neither a plus sign nor a period character.
65
- #
66
- # == Character Order | Base64 | UrlSafe64 | Radix64 | YACHT64
67
- #
68
- # The character sets for each of he four 64 fomats are as follows.
69
- #
70
- # - Base-64 is <b>A to Z</b> then <b>a to z</b> then <b>0 to 9</b> then <b>+</b> then <b>/</b>
71
- # - Radix64 is <b>.</b> then <b>/</b> then <b>0 to 9</b> then <b>A to Z</b> then <b>a to z</b>
72
- # - UrlSafeBase64 is Base64 but chars 63/64 are an <b>underscore (_)</b> and <b>hyphen (-)</b>
73
- # - UrlSafeBase64 <b>does not have line breaks and carriage returns</b> (unlike Base64)
74
- # - <b>OpenKey 64 (YACHT64)</b> uses the same 62 characters plus an @ sign and a forward slash
75
- # - The 64 <b>OpenKey 64</b> characters are <b>obfuscated into a random order</b>
76
- #
77
- # <b>Why Order Doesn't Matter</b>
78
- #
79
- # Order doesn't matter if string of bits is used in key creation
80
- # as long as this class is employed to do all the necessary
81
- # conversions.
82
- #
83
- # Base64 and Radix64 (outputted by the OpenBSD inspired bcrypt)
84
- # differ in both the order of characters and their choice of the
85
- # two non-alphanumeric characters.
86
- #
87
- # == 4 Non-AlphaNumerics | Base64 | Radix64 | YACHT64
88
- #
89
- # The behaviour here is happy to convert base64 strings produced by either
90
- # Radix64 or Base64 or UrlSafe Base64. Howeverr it aware of the
91
- # <b>non alpha-numeric characters</b> and converts them before processing
92
- # with the modus operandi that says
93
- #
94
- # - ignore the forward slash in <b>YACHT64, Base64 and Radix64</b>
95
- # - convert the <b>plus (+)</b> in Base64 to the <b>@ symbol</b> in YACHT64
96
- # - convert the <b>period (.)</b> in <b>Radix64</b> to the @ symbol in YACHT64
97
- # - convert <b>hyphen (-)</b> in <b>Url Safe Base64</b> into a fwd slash
98
- # - convert <b>underscore (_)</b> in <b>Url Safe Base64</b> to an @ sign
99
- # - <b>delete the (=) equals</b> padding character used by Base64
100
- #
101
- # Neither the OpenBSD backed Radix64 nor the OpenKey (YACHT64) entertain the
102
- # concept of padding.
103
- #
104
73
  # == Mapping Each Character to 6 Binary Bits
105
74
  #
106
75
  # We need 6 binary bits to represent a base64 character (and 4
@@ -125,353 +94,514 @@ module OpenKey
125
94
  #
126
95
  class Key
127
96
 
128
- # The internal YACHT64 OpenKey character set that can handle
129
- # conversion from either Radix64 or Base64. The 64 character
130
- # sets are all similar in that they hold 64 characters and
131
- # they define two non alphanumeric characters because the
132
- # 26 lowercase, 26 uppercase and 10 digits only adds up to
133
- # an agonising close 62 characters.
134
- YACHT64_CHARACTER_SET = [
135
- "a", "9", "W", "B", "f", "K", "O", "z",
136
- "3", "s", "1", "5", "c", "n", "E", "J",
137
- "L", "A", "l", "6", "I", "w", "o", "g",
138
- "k", "N", "t", "Y", "S", "%", "T", "b",
139
- "V", "R", "H", "0", "@", "Z", "8", "F",
140
- "G", "j", "u", "m", "M", "h", "4", "p",
141
- "q", "d", "7", "v", "e", "2", "U", "X",
142
- "r", "C", "y", "Q", "D", "x", "P", "i"
143
- ]
144
-
145
-
146
- # Initialize an n bit binary key (of literally ones and zeroes)
147
- # from the base64 represented string in the parameter.
148
- #
149
- # This method can convert either UNIX <b>Radix 64</b> or
150
- # <b>Base 64</b> into a <b>string of ones and zeroes</b>.
151
- #
152
- # The difference between Radix64 and Base64 is the ordering
153
- # of the characters and the choice of the two non alpha-numeric
154
- # (63rd and 64th or 1st and 2nd) characters.
155
- #
156
- # @param base64_string [String]
157
- # the either Base64 or (unix) Radix 64 encoded string
97
+ # Initialize a key object from a bit string of ones and zeroes provided
98
+ # in the parameter string.
158
99
  #
159
- # @raise [ArgumentError]
160
- # if the parameter string contains characters that are not
161
- # recognized as belonging to either the Base64 or the Radix64
162
- # character sets. See {OpenKey::Key::YACHT64_CHARACTER_SET} constant.
163
- #
164
- # @raise [RuntimeError]
165
- # if the conversion does not result in 6 bits for every character
166
- # in the parameter string.
167
- def self.from_base64 the_base64_string
168
-
169
- new_key = Key.new
170
- new_key.instantiate_from_base64 the_base64_string
171
- return new_key
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
172
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 [OpenKey::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 ) )
173
142
  end
174
143
 
175
144
 
176
- def instantiate_from_base64 base64_string
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
177
153
 
178
- @original64_string = replace_yacht64( base64_string )
179
154
 
180
- @binary_bit_string = ""
181
- @original64_string.each_char do |the_char|
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 [OpenKey::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
182
179
 
183
- yacht64_index = YACHT64_CHARACTER_SET.index(the_char)
184
180
 
185
- nil_msg = "Character [ #{the_char} ] is not in the YACHT64 table."
186
- raise ArgumentError, nil_msg if yacht64_index.nil?
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 [OpenKey::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
187
193
 
188
- index_msg = "yacht64 index should run between 0 and 63 inclusive."
189
- all_good = ( yacht64_index >= 0 ) && ( yacht64_index <= 63 )
190
- raise ArgumentError, index_msg unless all_good
191
194
 
192
- @binary_bit_string += "%06d" % [ yacht64_index.to_s(2) ]
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 [OpenKey::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
193
215
 
194
- end
195
216
 
196
- assert_in_out_lengths @original64_string, @binary_bit_string
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
197
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
198
259
  end
199
260
 
200
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
201
278
 
202
- def self.from_byte_array the_byte_array
203
-
204
- new_key = Key.new
205
- new_key.instantiate_from_base64( Base64.urlsafe_encode64( the_byte_array ) )
206
- return new_key
207
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*")
208
293
  end
209
294
 
210
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
+
211
311
 
212
- # Retrieve a cryptographically strong key that is 64 encoded
213
- # with a bit (not byte) count specified by the parameter.
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>.
214
316
  #
215
- # <b>Bit Count must be Multiple of 24</b>
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 {KdfApi}
320
+ # which delivers 512 bit (64 byte) key for reduction to 256 bits.
216
321
  #
217
- # The bit count parameter must be divisible by both 3 and 4
218
- # (thus 24) so that it can be represented by a whole number
219
- # of 8-bit bytes and a whole number of 6-bit base64 encoding
220
- # characters.
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>.
221
334
  #
222
- # If this were not the case the leftover bits could not strictly
223
- # be called "random" and could change in value when converted
224
- # from one representation to the other.
335
+ # If you take the returned key and call
225
336
  #
226
- # @param bit_count [Fixnum]
227
- # the number of bits divisable by 24 (both 3 and 4) so
228
- # that it can be represented by a whole number of 8-bit
229
- # bytes and a whole number of 6-bit base64 characters.
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
230
340
  #
231
- # @raise [ArgumentError]
232
- # if bit count parameter is not divisible by both 3 and 4
233
- def self.to_random_yacht64 bit_count
234
-
235
- mod24_msg = "The bit count of #{bit_count} is not divisable by 24."
236
- raise ArgumentError, mod24_msg unless bit_count % 24 == 0
341
+ # @return [OpenKey::Key]
342
+ # a key with a bit length (ones and zeroes) of <b>precisely 384</b>.
343
+ def to_384_bit_key
237
344
 
238
- num_6bit_blocks = bit_count / 6
239
- random64_string = SecureRandom.base64( num_6bit_blocks + 4 )
240
- perfect_rand_64 = random64_string[ 0 .. num_6bit_blocks ]
241
- length_64string = perfect_rand_64.length
345
+ a_384_bit_key = Key.from_binary( Digest::SHA384.digest( to_binary() ) )
242
346
 
243
- length_msg = "Expected [#{num_6bit_blocks}] characters not #{length_64string}."
244
- raise RuntimeError, length_msg unless length_64string == num_6bit_blocks
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
245
350
 
246
- ##################### return to_yacht64( perfect_rand_64 )
351
+ return a_384_bit_key
247
352
 
248
353
  end
249
354
 
250
355
 
251
- # Return a representation of this key in YACHT64 format which is
252
- # simply <b>Yet Another Coding Hieroglyphics-like Table (YACHT).
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.
253
358
  #
254
- # This new format can suck in RADIX64 as well as Base64 and is
255
- # safe for transportation with carriers such as
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.
256
362
  #
257
- # - URLs
258
- # - <b>one line text</b> (without newlines and carriage returns)
259
- # - <b>environment variables</b>
260
- # - INI, JSON, YAML and XML files
363
+ # <b>Only Encrypt Strong Keys</b>
261
364
  #
262
- # <b>Character Order | Base64 | UrlSafe64 | Radix64 | YACHT64</b>
365
+ # Never encrypt a potentially weak key, like one derived from a human password
366
+ # (even though it is put through key derivation functions).
263
367
  #
264
- # The character sets for each of he four 64 fomats are as follows.
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.
265
371
  #
266
- # - Base-64 is <b>A to Z</b> then <b>a to z</b> then <b>0 to 9</b> then <b>+</b> then <b>/</b>
267
- # - Radix64 is <b>.</b> then <b>/</b> then <b>0 to 9</b> then <b>A to Z</b> then <b>a to z</b>
268
- # - UrlSafeBase64 is Base64 but chars 63/64 are an <b>underscore (_)</b> and <b>hyphen (-)</b>
269
- # - UrlSafeBase64 <b>does not have line breaks and carriage returns</b> (unlike Base64)
270
- # - <b>OpenKey 64 (YACHT64)</b> uses the same 62 characters plus an @ sign and a forward slash
271
- # - The 64 <b>OpenKey 64</b> characters are <b>obfuscated into a random order</b>
372
+ # <b>Enforcing Strong Key Size</b>
272
373
  #
273
- # <b>Why Order Doesn't Matter</b>
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.
274
376
  #
275
- # Order doesn't matter if string of bits is used in key creation
276
- # as long as this class is employed to do all the necessary
277
- # conversions.
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.
278
379
  #
279
- # Base64 and Radix64 (outputted by the OpenBSD inspired bcrypt)
280
- # differ in both the order of characters and their choice of the
281
- # two non-alphanumeric characters.
380
+ # @param key_to_encrypt [OpenKey::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.
282
387
  #
283
388
  # @return [String]
284
- # Return the YACHT64 formatted textual string in a single line. This
285
- # string will consist of the 62 alpha-numeric characters and perhaps
286
- # an @ sign and a forward slash (/).
287
- #
288
- # @raise [RuntimeError]
289
- # raise an error if this class was initialized with a character that
290
- # was not recognised. Not withstanding the difference in non-whitespace
291
- # characters like carriage returns and newlines, an error is thrown
292
- # if the length of the original base64 and the output YACHT64 string are
293
- # not exactly the same length.
294
- def to_yacht64
295
-
296
- almost_base64 = replace_yacht64( @original64_string )
297
- the_yacht64_encoding = ""
298
-
299
- almost_base64.each_char do |the_char|
300
- yacht64_index = YACHT64_CHARACTER_SET.index(the_char)
301
- assert_yacht64_index yacht64_index
302
- the_yacht64_encoding += YACHT64_CHARACTER_SET[ yacht64_index ]
303
- end
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
304
398
 
305
- length_msg = "The base64 and yacht64 string lengths are not equal."
306
- goodlength = the_yacht64_encoding.length == @original64_string.length
307
- raise RuntimeError, length_msg unless goodlength
399
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
308
400
 
309
- return the_yacht64_encoding
401
+ crypt_cipher.encrypt()
402
+ random_iv = crypt_cipher.random_iv()
403
+ crypt_cipher.key = to_aes_key()
310
404
 
311
- end
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})"
312
409
 
410
+ log.info(x) { "### #####################################################################" }
411
+ log.info(x) { "### Caller Details =>> =>> #{caller_details}" }
412
+ log.info(x) { "### #####################################################################" }
413
+ log.info(x) { "The BitStr Char representation of this key => #{to_s()}" }
414
+ log.info(x) { "256bit Digest (Urlsafe Base64) of this key => #{Base64.urlsafe_encode64(to_aes_key())}" }
415
+ log.info(x) { "The IncomingKey Base64 Char representation => #{key_to_encrypt.to_char64()}" }
416
+ log.info(x) { "Random IV Used for the AESs Key Encryption => #{Base64.urlsafe_encode64(random_iv)}" }
313
417
 
314
- def to_s
315
- return @binary_bit_string
316
- end
418
+ cipher_text = crypt_cipher.update( key_to_encrypt.to_char64 ) + crypt_cipher.final
419
+ log.info(x) { "Cipher Text Produced after this Encryption => #{Base64.urlsafe_encode64(cipher_text)}" }
420
+
421
+ binary_text = random_iv + cipher_text
422
+ ones_zeroes = binary_text.unpack("B*")[0]
423
+ ciphertxt64 = Key64.from_bits( ones_zeroes )
317
424
 
425
+ log.info(x) { "Amalgam of Binary Random IV and Ciphertext => #{ciphertxt64}" }
426
+ log.info(x) { "------------------------------------------------------------------------- >>>>>>" }
318
427
 
319
- def to_264_bit_key
428
+ size_msg = "Expected bit count is #{EXPECTED_CIPHER_BIT_LENGTH} not #{ones_zeroes.length}."
429
+ raise RuntimeError, size_msg unless ones_zeroes.length == EXPECTED_CIPHER_BIT_LENGTH
320
430
 
321
- return [ to_s[ 0 .. (264-1) ] ].pack("B*")
431
+ return ciphertxt64
322
432
 
323
433
  end
324
434
 
325
435
 
326
- # This method chops away anything after the first 256 bits and returns
327
- # the result. Note that if the key was 24 bits long it will return
328
- # the 24 bits without question.
436
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
437
+ # 256bit representation of this key to decrypt the parameter ciphertext and
438
+ # return the previously encrypted key.
329
439
  #
330
- # <b>Size of BCrypt and PBKDF2 Derived Keys</b>
440
+ # To re-acquire (reconstitute) the original key call this method with the
441
+ # stored ciphertext that was returned by the {do_encrypt_key}.
331
442
  #
332
- # ----------- | --------- | ----------------- | ----------- |
333
- # ----------- | --------- | ----------------- | ----------- |
334
- # | Algorithm | Bit Count | Base64 Chars | 8 Bit Bytes |
335
- # ----------- | --------- | ----------------- | ----------- |
336
- # | BCrypt | 168 Bits | 28 characters | 21 bytes |
337
- # | Pbkdf2 | 96 Bits | 16 characters | 12 bytes |
338
- # ----------- | --------- | ----------------- | ----------- |
339
- # | Total | 264 Bits | 44 characters | 33 bytes |
340
- # ----------- | --------- | ----------------- | ----------- |
443
+ # <b>Only Encrypt Strong Keys</b>
341
444
  #
342
- # == 256 Bit Encryption Key | Remove 8 Bits
445
+ # Never encrypt a potentially weak key, like one derived from a human password
446
+ # (even though it is put through key derivation functions).
343
447
  #
344
- # The manufactured encryption key, an amalgam of the above now has
345
- # 264 bits carried by 44 Base64 characters.
448
+ # Once generated (or regenerated) a potentially weak key should live only as
449
+ # long as it takes for it to encrypt a strong key. The strong key can then
450
+ # be used to encrypt valuable assets.
346
451
  #
347
- # Just before it is used to encrypt vital keys, eight (8) bits are
348
- # removed from the end of the key. The key is then converted into a
349
- # powerful 32 byte (256 bit) encryption agent and is hashed by the
350
- # SHA256 digest and delivered.
452
+ # <b>Enforcing Strong Key Size</b>
351
453
  #
352
- # @return [Byte]
353
- # a binary string of thirty-two (32) eight (8) bit bytes which
354
- # if appropriate can be used as a symmetric encryption key especially
355
- # to the powerful AES256 cipher.
356
- def to_256_bit_key
357
- return [ to_s[ 0 .. (256-1) ] ].pack("B*")
358
- end
359
-
360
-
361
- # Return the <b>un-printable <em>binary</em> bytes</b> representation
362
- # of this key. If you store 128 bits it will produce 22 characters
363
- # because 128 divide by 6 is 21 characters and a remainder of two (2)
364
- # bits.
454
+ # If one key is potentially weaker than the other, the weaker key must be this
455
+ # object and the strong key is reconstituted and returned by this method.
365
456
  #
366
- # The re-conversion of the 22 characters will now produce 132 bits which
367
- # is different from the original 128 bits.
368
- #
369
- # @return [Byte]
370
- # a non-printable binary string of eight (8) bit bytes which can be
371
- # used as input to both digest and symmetric cipher functions.
372
- def to_binary_bytes
373
- return [ to_s ].pack("B*")
374
- end
375
-
376
-
377
- # Return a key that is stoked with 48 random bytes which translates to
378
- # a length of either <b>384 bits</b> or <b>64 base64 characters</b>.
379
- #
380
- # <b>The 48 Bytes map to 64 Base64 Characters</b>
381
- #
382
- # | -------- | ------------ | -------------------------------- |
383
- # | Bits | Bytes | Base64 |
384
- # | -------- | ------------ | -------------------------------- |
385
- # | 384 Bits | is 48 bytes | and 64 characters |
386
- # | -------- | ------------ | -------------------------------- |
387
- #
388
- # This key easily translates to a base64 and/or byte array format because
389
- # the 384 bit count is a <b>multiple of both 6 and 8</b>.
457
+ # @param ciphertext_to_decrypt [String]
458
+ # Provide the ciphertext produced by our sister key encryption method.
459
+ # The ciphertext should hold 96 bytes which equates to 128 base64 characters.
460
+ # The random initialization vector (iv) accounts for the first 16 bytes.
461
+ # The actual crypt ciphertext then accounts for the final 80 bytes.
390
462
  #
391
463
  # @return [Key]
392
- # return a key containing 384 random bits (or a random array of 48 bytes)
393
- # which can if necessary be serialized into 64 base64 characters.
394
- def self.from_random_bytes
464
+ # return the key that was serialized into base64 and then encrypted (locked down)
465
+ # with the 256 bit binary symmetric encryption key from this host object.
466
+ #
467
+ # @raise [ArgumentError]
468
+ # the size of the parameter ciphertext must be 128 base 64 characters.
469
+ def do_decrypt_key ciphertext_to_decrypt
470
+
471
+ calling_module = File.basename caller_locations(1,1).first.absolute_path, ".rb"
472
+ calling_method = caller_locations(1,1).first.base_label
473
+ calling_lineno = caller_locations(1,1).first.lineno
474
+ caller_details = "#{calling_module} | #{calling_method} | (line #{calling_lineno})"
395
475
 
396
- NUMBER_OF_BYTES_REQUIRED = 48
397
- NUMBER_OF_BITS_EXPECTED = 384
398
- BASE64_CHARACTER_COUNT = 64
476
+ log.info(x) { "### #####################################################################" }
477
+ log.info(x) { "### Caller Details =>> =>> #{caller_details}" }
478
+ log.info(x) { "### #####################################################################" }
479
+ log.info(x) { "Amalgam of Binary Random IV and Ciphertext => #{ciphertext_to_decrypt}" }
480
+ log.info(x) { "The Base64 Char representation of this key => #{to_s()}" }
481
+ log.info(x) { "256bit Digest (Urlsafe Base64) of this key => #{Base64.urlsafe_encode64(to_aes_key())}" }
399
482
 
400
- raw_bytes = SecureRandom.random_bytes( 64 )
483
+ bit_text = Key64.to_bits(ciphertext_to_decrypt)
484
+ size_msg = "Expected bit count is #{EXPECTED_CIPHER_BIT_LENGTH} not #{bit_text.length}."
485
+ raise RuntimeError, size_msg unless bit_text.length == EXPECTED_CIPHER_BIT_LENGTH
401
486
 
402
- too_short_msg = "Not enough (only [#{raw_bytes.length}]) random bytes generated."
403
- raise RuntimeError, too_short_msg unless raw_bytes.length >= NUMBER_OF_BYTES_REQUIRED
487
+ cipher_x = OpenSSL::Cipher::AES256.new(:CBC)
488
+ cipher_x.decrypt()
404
489
 
405
- random_key = Key.from_byte_array( raw_bytes[ 0 .. ( NUMBER_OF_BYTES_REQUIRED - 1 ) ] )
490
+ rawbytes = [ bit_text ].pack("B*")
406
491
 
407
- bit_length = random_key.to_s.length
408
- bit_lenth_msg = "Key with #{NUMBER_OF_BITS_EXPECTED} bits expected - not #{bit_length}."
409
- raise RuntimeError, bit_length_msg unless bit_length == NUMBER_OF_BITS_EXPECTED
492
+ cipher_x.key = to_aes_key()
493
+ cipher_x.iv = rawbytes[ 0 .. ( RANDOM_IV_BYTE_COUNT - 1 ) ]
494
+ key_chars_64 = cipher_x.update( rawbytes[ RANDOM_IV_BYTE_COUNT .. -1 ] ) + cipher_x.final
410
495
 
411
- base64_length = random_key.to_yacht64.length
412
- base64_lenth_msg = "Expected #{BASE64_CHARACTER_COUNT} base64 chars not #{base64_length}."
413
- raise RuntimeError, base64_length_msg unless base64_length == BASE64_CHARACTER_COUNT
496
+ log.info(x) { "The OutgoingKey Base64 Char representation => #{key_chars_64}" }
414
497
 
415
- return random_key
498
+ return Key.from_char64( key_chars_64 )
416
499
 
417
500
  end
418
501
 
419
502
 
503
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
504
+ # 256bit representation of this key to encrypt the parameter plaintext using
505
+ # the parameter random initialization vector.
506
+ #
507
+ # Store the ciphertext provided by this method. To re-acquire (reconstitute)
508
+ # the plaintext use the {do_decrypt_text} decryption method, giving
509
+ # it the same initialization vector and the ciphertext produced here.
510
+ #
511
+ # <b>Only Encrypt Once</b>
512
+ #
513
+ # Despite the initialization vector protecting against switch attacks you
514
+ # should <b>only use this or any other key once</b> to encrypt an object.
515
+ # While it is okay to encrypt small targets using two different keys, it
516
+ # pays not to do the same when the target is large.
517
+ #
518
+ # @param random_iv [String]
519
+ # a randomly generated 16 byte binary string that is to be used as the
520
+ # initialization vector (IV) - this is a requirement for AES encryption
521
+ # in CBC mode - this IV does not need to be treated as a secret
522
+ #
523
+ # @param plain_text [String]
524
+ # the plaintext or binary string to be encrypted. To re-acquire this string
525
+ # use the {do_decrypt_text} decryption method, giving it the same
526
+ # initialization vector (provided in the first parameter) and the ciphertext
527
+ # returned from this method.
528
+ #
529
+ # @return [String]
530
+ # The returned binary ciphertext should be encoded and persisted until such
531
+ # a time as its re-acquisition by authorized parties becomes necessary.
532
+ def do_encrypt_text random_iv, plain_text
533
+
534
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
535
+
536
+ crypt_cipher.encrypt()
537
+ crypt_cipher.iv = random_iv
538
+ crypt_cipher.key = to_aes_key()
420
539
 
540
+ return crypt_cipher.update( plain_text ) + crypt_cipher.final
541
+
542
+ end
421
543
 
422
544
 
423
- # Convert non-alphanumeric characters within either Base64 or Radix64 into
424
- # the OpenKey YACHT64 standard which has a forward slash but neither a plus sign
425
- # nor a period character.
545
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
546
+ # 256bit representation of this key to decrypt the parameter ciphertext using
547
+ # the parameter random initialization vector.
426
548
  #
427
- # <b>Converting the Non-Alphanumeric Characters</b>
549
+ # Use this method to re-acquire (reconstitute) the plaintext that was
550
+ # converted to ciphertext by the {do_encrypt_text} encryption method,
551
+ # naturally using the same initialization vector for both calls.
428
552
  #
429
- # The behaviour here is happy to convert base64 strings produced by either
430
- # Radix64 or Base64 or UrlSafe Base64. However, it is aware of the
431
- # <b>non alpha-numeric characters</b> and converts them before processing
432
- # with the modus operandi that says
553
+ # <b>Only Decrypt Once</b>
433
554
  #
434
- # - ignore the forward slash in <b>YACHT64, Base64 and Radix64</b>
435
- # - convert the <b>plus (+)</b> in Base64 to the <b>@ symbol</b> in YACHT64
436
- # - convert the <b>period (.)</b> in <b>Radix64</b> to the @ symbol in YACHT64
437
- # - convert <b>hyphen (-)</b> in <b>Url Safe Base64</b> into a fwd slash
438
- # - convert <b>underscore (_)</b> in <b>Url Safe Base64</b> to an @ sign
439
- # - <b>delete the (=) equals</b> padding character used by Base64
555
+ # Consider <b>a key spent</b> as soon as it decrypts the one object it was
556
+ # created to decrypt. Like a bee dying after a sting, a key should die after
557
+ # it decrypts an object. Should re-decryption be necessary - another key
558
+ # should be derived or generated.
440
559
  #
441
- # Neither the OpenBSD backed Radix64 nor the OpenKey (YACHT64) entertain the
442
- # concept of padding.
560
+ # @param random_iv [String]
561
+ # a randomly generated 16 byte binary string that is to be used as the
562
+ # initialization vector (IV) - this is a requirement for AES decryption
563
+ # in CBC mode - this IV does not need to be treated as a secret
443
564
  #
444
- # @param char64_string [String]
445
- # string produced in either the Radix64 or the Base64 format
565
+ # @param cipher_text [String]
566
+ # the ciphertext or binary string to be decrypted in order to re-acquire
567
+ # (reconstitute) the plaintext that was converted to ciphertext by the
568
+ # {do_encrypt_text} encryption method.
446
569
  #
447
570
  # @return [String]
448
- # an OpenKey YACHT64 standard string that does have a forward slash but neither
449
- # contains a plus sign, nor a period character - and are replaced by the @ symbol.
450
- # No equals padding character will be returned.
451
- def replace_yacht64 char64_string
452
- return char64_string.gsub(".", "@").gsub("/", "%").gsub("+", "@").gsub("-", "%").gsub("_", "@").delete("=")
453
- end
571
+ # if the plaintext (or binary string) returned here still needs to be
572
+ # kept on the low, derive or generate another key to protect it.
573
+ def do_decrypt_text random_iv, cipher_text
454
574
 
575
+ raise ArgumentError, "Incoming cipher text cannot be nil." if cipher_text.nil?
455
576
 
577
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
456
578
 
457
- private
579
+ crypt_cipher.decrypt()
580
+ crypt_cipher.iv = random_iv
581
+ crypt_cipher.key = to_aes_key()
458
582
 
583
+ return crypt_cipher.update( cipher_text ) + crypt_cipher.final
459
584
 
460
- def assert_yacht64_index yacht64_index
461
- index_msg = "yacht64 index should run between 0 and 63 inclusive."
462
- all_good = yacht64_index >= 0 && yacht64_index <= 63
463
- raise ArgumentError, index_msg unless all_good
464
585
  end
465
586
 
466
587
 
467
- def assert_in_out_lengths( in_string, out_string )
588
+ private
589
+
590
+
591
+ RANDOM_KEY_BYTE_LENGTH = 48
592
+
593
+ EIGHT_BIT_INTEGER_SIZE = 256
594
+
595
+ RANDOM_IV_BYTE_COUNT = 16
596
+
597
+ CIPHERTEXT_BYTE_COUNT = 80
598
+
599
+ EXPECTED_CIPHER_BIT_LENGTH = ( CIPHERTEXT_BYTE_COUNT + RANDOM_IV_BYTE_COUNT ) * 8
468
600
 
469
- in_length = in_string.length
470
- out_length = out_string.length
471
- good_ratio = out_length == in_length * 6
472
- size_msg = "Out string length [#{out_length}] not 6 times bigger than [#{in_length}]."
473
- raise RuntimeError, size_msg unless good_ratio
474
601
 
602
+ def assert_non_nil_bits
603
+ nil_err_msg = "The bit string for this key is nil."
604
+ raise RuntimeError, nil_err_msg if @bit_string.nil?
475
605
  end
476
606
 
477
607