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