opensecret 0.0.988 → 0.0.9925

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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