opensecret 0.0.9925 → 0.0.9949
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 +4 -4
- data/README.md +656 -40
- data/lib/configs/README.md +58 -0
- data/lib/extension/file.rb +67 -0
- data/lib/extension/string.rb +10 -0
- data/lib/factbase/facts.opensecret.io.ini +1 -0
- data/lib/interprete.rb +334 -61
- data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
- data/lib/keytools/kdf.api.rb +9 -15
- data/lib/keytools/kdf.bcrypt.rb +69 -19
- data/lib/keytools/kdf.pbkdf2.rb +112 -23
- data/lib/keytools/key.api.rb +146 -36
- data/lib/keytools/key.db.rb +94 -29
- data/lib/keytools/key.id.rb +1 -1
- data/lib/keytools/key.ident.rb +243 -0
- data/lib/keytools/key.local.rb +62 -68
- data/lib/keytools/key.pass.rb +2 -2
- data/lib/keytools/key.rb +2 -28
- data/lib/modules/{cryptology.md → README.md} +0 -0
- data/lib/session/fact.finder.rb +65 -428
- data/lib/session/time.stamp.rb +1 -28
- data/lib/usecase/cmd.rb +127 -54
- 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 +1 -1
- data/lib/usecase/import.rb +13 -30
- data/lib/usecase/init.rb +2 -17
- data/lib/usecase/jenkins/README.md +146 -0
- data/lib/usecase/jenkins/crazy_ruby_post_attempt.OLD +234 -0
- data/lib/usecase/jenkins/jenkins.rb +208 -0
- data/lib/usecase/login.rb +6 -5
- data/lib/usecase/logout.rb +1 -3
- data/lib/usecase/open.rb +11 -66
- data/lib/usecase/print.rb +40 -0
- data/lib/usecase/put.rb +34 -156
- data/lib/usecase/set.rb +2 -4
- 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 +4 -80
- data/lib/usecase/update/README.md +55 -0
- data/lib/usecase/update/rename.rb +180 -0
- data/lib/usecase/use.rb +1 -3
- 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 +1 -1
- data/opensecret.gemspec +3 -4
- metadata +34 -35
- data/.travis.yml +0 -5
- data/CODE_OF_CONDUCT.md +0 -74
- data/LICENSE.txt +0 -21
- data/bin/ops +0 -20
- data/lib/keytools/binary.map.rb +0 -294
- data/lib/keytools/doc.conversion.to.ones.and.zeroes.ruby +0 -179
- data/lib/keytools/doc.rsa.radix.binary-mapping.ruby +0 -190
- data/lib/keytools/doc.star.schema.strategy.txt +0 -77
- data/lib/keytools/doc.using.pbkdf2.kdf.ruby +0 -95
- data/lib/keytools/doc.using.pbkdf2.pkcs.ruby +0 -266
- data/lib/keytools/key.mach.rb +0 -248
- data/lib/keytools/keydebug.txt +0 -295
- data/lib/modules/cryptology/open.bcrypt.rb +0 -170
- data/lib/usecase/read.rb +0 -89
- data/lib/usecase/safe.rb +0 -92
Binary file
|
data/lib/keytools/kdf.api.rb
CHANGED
@@ -28,9 +28,8 @@ module OpenKey
|
|
28
28
|
# == How to Create the Encryption Key
|
29
29
|
#
|
30
30
|
# To create a high entropy encryption key this method takes the first
|
31
|
-
# 168 bits from the 186 bit BCrypt key
|
32
|
-
#
|
33
|
-
# {KeyPbkdf2} class and amalgamates them to produce a 264 bit key.
|
31
|
+
# 168 bits from the 186 bit BCrypt key and the first 96 bits from the
|
32
|
+
# 132 bit PBKDF2 key and amalgamates them to produce a 264 bit key.
|
34
33
|
#
|
35
34
|
# The 264 bit key is then digested to produce a 256bit encryption key.
|
36
35
|
class KdfApi
|
@@ -51,7 +50,7 @@ module OpenKey
|
|
51
50
|
|
52
51
|
|
53
52
|
# To create a high entropy encryption key we use the full 180 bits
|
54
|
-
# from the returned 180 bit BCrypt key
|
53
|
+
# from the returned 180 bit BCrypt key.
|
55
54
|
#
|
56
55
|
# When amalgamated with the <b>332 bits from the PBKDF2 Key</b> we
|
57
56
|
# achieve a powerful <b>union key length</b> of 512 bits.
|
@@ -67,8 +66,8 @@ module OpenKey
|
|
67
66
|
|
68
67
|
|
69
68
|
# To create a high entropy encryption key we use the full 180 bits
|
70
|
-
# from the returned 180 bit BCrypt key
|
71
|
-
# the
|
69
|
+
# from the returned 180 bit BCrypt key and the first 332 bits from
|
70
|
+
# the 384 bit PBKDF2 key.
|
72
71
|
#
|
73
72
|
# On amalgamation, the outcome is a quality <b>union key length</b>
|
74
73
|
# of <b>512 bits</b>.
|
@@ -107,19 +106,14 @@ module OpenKey
|
|
107
106
|
# that exists to convert <b>low entropy</b> human generated passwords into a high
|
108
107
|
# entropy key that is computationally infeasible to acquire through brute force.
|
109
108
|
#
|
110
|
-
# To create a high entropy encryption key we use the full 180 bits
|
111
|
-
# from the returned 180 bit BCrypt key produced by {KdfBCrypt} and
|
112
|
-
# the first 332 bits from the 384 bit key returned by PBKDF2.
|
113
|
-
#
|
114
109
|
# On amalgamation, the outcome is a quality <b>union key length</b>
|
115
110
|
# of <b>512 bits</b>.
|
116
111
|
#
|
117
112
|
# == Creating a High Entropy Encryption Key
|
118
113
|
#
|
119
114
|
# To create a high entropy encryption key this method takes the first
|
120
|
-
# 168 bits from the 186 bit BCrypt
|
121
|
-
#
|
122
|
-
# {KeyPbkdf2} class and amalgamates them to produce a 264 bit key.
|
115
|
+
# 168 bits from the 186 bit BCrypt and the first 96 bits from the 132
|
116
|
+
# bit PBKDF2 key and amalgamates them to produce a 264 bit key.
|
123
117
|
#
|
124
118
|
# Note that all four of the above numbers are divisable by six (6), for
|
125
119
|
# representation with a 64 character set, and eight (8), for transport
|
@@ -206,8 +200,8 @@ module OpenKey
|
|
206
200
|
|
207
201
|
def self.derive_and_amalgamate( human_secret, bcrypt_salt, pbkdf2_salt )
|
208
202
|
|
209
|
-
bcrypt_key =
|
210
|
-
pbkdf2_key =
|
203
|
+
bcrypt_key = KdfBCrypt.generate_key( human_secret, bcrypt_salt )
|
204
|
+
pbkdf2_key = KeyPbkdf2.generate_key( human_secret.reverse, pbkdf2_salt )
|
211
205
|
|
212
206
|
assert_bcrypt_key_bit_length bcrypt_key
|
213
207
|
assert_pbkdf2_key_bit_length pbkdf2_key
|
data/lib/keytools/kdf.bcrypt.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
|
4
4
|
module OpenKey
|
5
5
|
|
6
|
-
|
7
6
|
# BCrypt is a <b>Blowfish based Key Derivation Function (KDF)</b> that exists to
|
8
7
|
# convert <b>low entropy</b> human created passwords into a high entropy key that
|
9
8
|
# is computationally infeasible to acquire through brute force.
|
@@ -24,6 +23,25 @@ module OpenKey
|
|
24
23
|
# <b>A cost of 16 will result in 2^16 = 65,536 iterations</b> and will slow the
|
25
24
|
# derivation time to about a second on a powerful 2020 laptop.
|
26
25
|
#
|
26
|
+
# == BCrypt Cost Iteration Timings on an Intel i-5 Laptop
|
27
|
+
#
|
28
|
+
# The benchmark timings were incredibly consistent and
|
29
|
+
# took almost exactly twice as long for every step.
|
30
|
+
#
|
31
|
+
# An IBM ThinkPad was used to generate the timings.
|
32
|
+
#
|
33
|
+
# Memory RAM ~> 15GiB
|
34
|
+
# Processors ~> Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
|
35
|
+
#
|
36
|
+
# The timing results (for 2 steps) multiplied by four (4).
|
37
|
+
#
|
38
|
+
# 3.84 seconds for 2^16 (65,536) iterations
|
39
|
+
# 0.96 seconds for 2^14 (16,384) iterations
|
40
|
+
# 0.24 seconds for 2^12 ( 4,096) iterations
|
41
|
+
# 0.06 seconds for 2^10 ( 1,024) iterations
|
42
|
+
#
|
43
|
+
# A double digit iteration cost must be provided to avoid
|
44
|
+
# an in-built failure trap. The default cost is now 10.
|
27
45
|
class KdfBCrypt
|
28
46
|
|
29
47
|
require "bcrypt"
|
@@ -32,13 +50,31 @@ module OpenKey
|
|
32
50
|
# two so if the iteration integer is 12 there will be two
|
33
51
|
# to the power of 12 ( 2^12 ) giving 4096 iterations.
|
34
52
|
# The minimum number is 4 (16 iterations) and the max is 31.
|
53
|
+
#
|
35
54
|
# @example
|
36
55
|
# Configuring 16 into this directive results in
|
37
56
|
# 2^16 = 65,536 iterations
|
38
57
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
|
58
|
+
# == BCrypt Cost Iteration Timings on an Intel i-5 Laptop
|
59
|
+
#
|
60
|
+
# The benchmark timings were incredibly consistent and
|
61
|
+
# took almost exactly twice as long for every step.
|
62
|
+
#
|
63
|
+
# An IBM ThinkPad was used to generate the timings.
|
64
|
+
#
|
65
|
+
# Memory RAM ~> 15GiB
|
66
|
+
# Processors ~> Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
|
67
|
+
#
|
68
|
+
# The timing results (for 2 steps) multiplied by four (4).
|
69
|
+
#
|
70
|
+
# 3.84 seconds for 2^16 (65,536) iterations
|
71
|
+
# 0.96 seconds for 2^14 (16,384) iterations
|
72
|
+
# 0.24 seconds for 2^12 ( 4,096) iterations
|
73
|
+
# 0.06 seconds for 2^10 ( 1,024) iterations
|
74
|
+
#
|
75
|
+
# A double digit iteration cost must be provided to avoid
|
76
|
+
# an in-built failure trap. The default cost is now 10.
|
77
|
+
BCRYPT_ITERATION_INTEGER = 10
|
42
78
|
|
43
79
|
# The bcrypt algorithm produces a key that is 181 bits in
|
44
80
|
# length. The algorithm then converts the binary 181 bits
|
@@ -46,13 +82,11 @@ module OpenKey
|
|
46
82
|
#
|
47
83
|
# 181 / 6 = 30 remainder 1 (so 31 characters are needed).
|
48
84
|
BCRYPT_KEY_LENGTH = 31
|
49
|
-
|
50
85
|
|
51
86
|
# BCrypt key derivation (from text) implementations truncate
|
52
87
|
# the first 55 characters of the incoming text.
|
53
88
|
BCRYPT_MAX_IN_TEXT_LENGTH = 55
|
54
89
|
|
55
|
-
|
56
90
|
# The BCrypt algorithm produces 181 raw binary bits which is just
|
57
91
|
# one bit more than a 30 character base64 string. Hence the algorithm
|
58
92
|
# puts out 31 characters.
|
@@ -60,7 +94,6 @@ module OpenKey
|
|
60
94
|
# We discard the 31st character because 5 of its 6 bits are 100%
|
61
95
|
# predictable. Thus the returned key will contribute 180 bits.
|
62
96
|
BCRYPT_KEY_EXPORT_BIT_LENGTH = 180
|
63
|
-
|
64
97
|
|
65
98
|
# The BCrypt algorithm salt string should be 22 characters
|
66
99
|
# and may include forward slashes and periods.
|
@@ -134,15 +167,15 @@ module OpenKey
|
|
134
167
|
# same form to the {generate_key} method.
|
135
168
|
def self.generate_bcrypt_salt
|
136
169
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
170
|
+
full_bcrypt_salt = BCrypt::Engine.generate_salt( BCRYPT_ITERATION_INTEGER )
|
171
|
+
main_bcrypt_salt = full_bcrypt_salt[ BCRYPT_OUTPUT_TEXT_PREFIX.length .. -1 ]
|
172
|
+
keep_bcrypt_salt = "#{BCRYPT_ITERATION_INTEGER}#{main_bcrypt_salt}"
|
173
|
+
assert_bcrypt_salt( keep_bcrypt_salt )
|
174
|
+
return keep_bcrypt_salt
|
141
175
|
|
142
176
|
end
|
143
177
|
|
144
178
|
|
145
|
-
|
146
179
|
# Key generators should first use the {generate_salt} method to create
|
147
180
|
# a BCrypt salt string and then submit it to this method together with
|
148
181
|
# a human generated password in order to derive a key.
|
@@ -181,16 +214,18 @@ module OpenKey
|
|
181
214
|
# predictable. Thus the returned key will contribute 180 bits.
|
182
215
|
def self.generate_key human_secret, bcrypt_salt
|
183
216
|
|
217
|
+
iteration_int = bcrypt_salt[ 0 .. 1 ]
|
218
|
+
bcrypt_prefix = "$2x$#{iteration_int}$"
|
219
|
+
full_salt_str = bcrypt_prefix + bcrypt_salt[ 2 .. -1 ]
|
220
|
+
|
184
221
|
assert_bcrypt_salt( bcrypt_salt )
|
185
|
-
full_bcrypt_salt = BCRYPT_OUTPUT_TEXT_PREFIX + bcrypt_salt
|
186
|
-
hashed_secret = BCrypt::Engine.hash_secret( human_secret, full_bcrypt_salt )
|
187
|
-
encoded64_key = BCrypt::Password.new( hashed_secret ).to_s
|
188
222
|
|
223
|
+
hashed_secret = BCrypt::Engine.hash_secret( human_secret, full_salt_str )
|
224
|
+
encoded64_key = BCrypt::Password.new( hashed_secret ).to_s
|
189
225
|
key_begin_index = BCRYPT_OUTPUT_TEXT_PREFIX.length + BCRYPT_SALT_LENGTH
|
190
226
|
radix64_key_str = encoded64_key[ key_begin_index .. -1 ]
|
191
227
|
key_length_mesg = "The BCrypt key length should have #{BCRYPT_KEY_LENGTH} characters."
|
192
228
|
raise RuntimeError, key_length_mesg unless radix64_key_str.length == BCRYPT_KEY_LENGTH
|
193
|
-
|
194
229
|
chopped_radix64_key = radix64_key_str.chop()
|
195
230
|
|
196
231
|
return Key.from_radix64( chopped_radix64_key )
|
@@ -198,14 +233,29 @@ module OpenKey
|
|
198
233
|
end
|
199
234
|
|
200
235
|
|
201
|
-
|
202
236
|
private
|
203
237
|
|
204
238
|
|
239
|
+
# ---
|
240
|
+
# --- Timings Code
|
241
|
+
# ---
|
242
|
+
# --- chopped_radix64_key = NIL
|
243
|
+
# --- require 'benchmark'
|
244
|
+
# --- timings = Benchmark.measure {
|
245
|
+
# ---
|
246
|
+
# --- -- wrapped up code block
|
247
|
+
# ---
|
248
|
+
# --- }
|
249
|
+
# ---
|
250
|
+
# --- log.info(x) { "BCrypt key generation timings ~> #{timings}" }
|
251
|
+
# ---
|
252
|
+
|
253
|
+
|
205
254
|
def self.assert_bcrypt_salt the_salt
|
206
255
|
raise RuntimeError, "bcrypt salt not expected to be nil." if the_salt.nil?
|
207
|
-
|
208
|
-
|
256
|
+
bcrypt_total_length = 2 + BCRYPT_SALT_LENGTH
|
257
|
+
salt_length_msg = "BCrypt salt #{the_salt} is #{the_salt.length} and not #{bcrypt_total_length} characters."
|
258
|
+
raise RuntimeError, salt_length_msg unless the_salt.length == bcrypt_total_length
|
209
259
|
end
|
210
260
|
|
211
261
|
|
data/lib/keytools/kdf.pbkdf2.rb
CHANGED
@@ -25,6 +25,32 @@ module OpenKey
|
|
25
25
|
# The difficulty is in detecting the operating system's C libraries that are directly
|
26
26
|
# accessed for OpenSSL functionality. If the distinction can be made accurately, those
|
27
27
|
# with newer libraries can reap the benefits immediately.
|
28
|
+
#
|
29
|
+
# == PBKDF2 Cost Iteration Timings on an Intel i-5 Laptop
|
30
|
+
#
|
31
|
+
# An IBM ThinkPad was used to generate the timings.
|
32
|
+
#
|
33
|
+
# Memory RAM ~> 15GiB
|
34
|
+
# Processors ~> Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
|
35
|
+
#
|
36
|
+
# The timing results show that a prudent value is somewhere
|
37
|
+
# between one hundred thousand and ten million iterations.
|
38
|
+
#
|
39
|
+
# 9.6 seconds for 10,000,000 ten million iterations
|
40
|
+
# 0.96 seconds for 1,000,000 one million iterations
|
41
|
+
# 0.096 seconds for 100,000 one hundred thousand iterations
|
42
|
+
#
|
43
|
+
# Open key sets iteration counts for PBKDF2 in hexadecimal and
|
44
|
+
# a valid range starts at 1 and counts up in chunks of a hundred
|
45
|
+
# thousand (100,000).
|
46
|
+
#
|
47
|
+
# 1 ~> 100,000
|
48
|
+
# 5 ~> 500,000
|
49
|
+
# 10 ~> 1,000,000
|
50
|
+
# 16 ~> 16,000,000
|
51
|
+
# 256 ~> 256,000,000
|
52
|
+
#
|
53
|
+
# The maximum iteration multiplier allowed is 16,384.
|
28
54
|
class KeyPbkdf2
|
29
55
|
|
30
56
|
|
@@ -32,7 +58,31 @@ module OpenKey
|
|
32
58
|
# growth of <b>GPU driven cloud based computing</b> power
|
33
59
|
# that is curently being honed by mining BitCoin and training
|
34
60
|
# neural networks.
|
35
|
-
|
61
|
+
#
|
62
|
+
# == PBKDF2 Cost Iteration Timings on an Intel i-5 Laptop
|
63
|
+
#
|
64
|
+
# An IBM ThinkPad was used to generate the timings.
|
65
|
+
#
|
66
|
+
# Memory RAM ~> 15GiB
|
67
|
+
# Processors ~> Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
|
68
|
+
#
|
69
|
+
# The timing results show that a prudent value is somewhere
|
70
|
+
# between one hundred thousand and ten million iterations.
|
71
|
+
#
|
72
|
+
# Open key sets iteration counts for PBKDF2 in hexadecimal and
|
73
|
+
# a valid range starts at 1 and counts up in chunks of a hundred
|
74
|
+
# thousand (100,000).
|
75
|
+
#
|
76
|
+
# 1 ~> 100,000
|
77
|
+
# 5 ~> 500,000
|
78
|
+
# 10 ~> 1,000,000
|
79
|
+
# 16 ~> 16,000,000
|
80
|
+
# 256 ~> 256,000,000
|
81
|
+
PBKDF2_ITERATION_MULTIPLIER = 1
|
82
|
+
|
83
|
+
# The quantity used to multiply the iteration multiplier by to
|
84
|
+
# gain the iteration count.
|
85
|
+
ONE_HUNDRED_THOUSAND = 100000
|
36
86
|
|
37
87
|
|
38
88
|
# Documentation for this algorithm says this about the key length.
|
@@ -56,7 +106,6 @@ module OpenKey
|
|
56
106
|
PBKDF2_EXPORT_BIT_LENGTH = PBKDF2_EXPORT_KEY_LENGTH * 8
|
57
107
|
|
58
108
|
|
59
|
-
|
60
109
|
# The documented recommended salt length in bytes for the PBKDF2
|
61
110
|
# algorithm is between <b>16 and 24 bytes</b>. The setting here is
|
62
111
|
# at the upper bound of that range.
|
@@ -65,26 +114,38 @@ module OpenKey
|
|
65
114
|
|
66
115
|
# Return a random cryptographic salt generated from twenty-four
|
67
116
|
# random bytes produced by a secure random number generator. The
|
68
|
-
# returned salt is a Base64 encoded string that can be
|
69
|
-
# and
|
117
|
+
# returned salt is primarily a Base64 encoded string that can be
|
118
|
+
# stored and then passed to the {KeyPbkdf2.generate_key} method.
|
119
|
+
#
|
120
|
+
# + ------------ + -------- + ------------ + ------------- +
|
121
|
+
# | | Bits | Bytes | Base64 |
|
122
|
+
# | ------------ | -------- | ------------ | ------------- |
|
123
|
+
# | PBKDF2 Salt | 192 Bits | 24 bytes | 32 characters |
|
124
|
+
# + ------------ + -------- + ------------ + ------------- +
|
125
|
+
#
|
126
|
+
# The leading part of the character sequence indicates the length
|
127
|
+
# of the salt in chunks of 100,000 and is plus sign separated.
|
70
128
|
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# | PBKDF2 Salt | 192 Bits | 24 bytes | 32 characters |
|
75
|
-
# + ------------ + -------- + ------------ + ------------------- +
|
129
|
+
# 42+12345678abcdefgh12345678ABCDEFGH ~> 4,200,000 iterations
|
130
|
+
# 9+12345678abcdefgh12345678ABCDEFGH ~> 900,000 iterations
|
131
|
+
# 100+12345678abcdefgh12345678ABCDEFGH ~> 10,000,000 iterations
|
76
132
|
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
# key method will convert the 32 base64 characters back into a
|
80
|
-
# <b>24 byte binary</b> formatted string.
|
133
|
+
# Note that the generate key method will convert the trailing 32
|
134
|
+
# base64 characters back into a <b>24 byte binary</b> string.
|
81
135
|
#
|
82
136
|
# @return [String]
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
137
|
+
# a relatively small iteration count multiplier separated from the
|
138
|
+
# main salt characters by a plus sign. The salt characters will
|
139
|
+
# consist of 32 base64 characters which can be stored and fed into
|
140
|
+
# the {generate_key}.
|
141
|
+
#
|
142
|
+
# These 32 characters are a representation of the twenty-four (24)
|
143
|
+
# randomly and securely generated bytes.
|
86
144
|
def self.generate_pbkdf2_salt
|
87
|
-
|
145
|
+
|
146
|
+
pbkdf2_salt = Key64.from_bits( Key.to_random_bits( PBKDF2_SALT_LENGTH_BYTES ) )
|
147
|
+
return "#{PBKDF2_ITERATION_MULTIPLIER}+#{pbkdf2_salt}"
|
148
|
+
|
88
149
|
end
|
89
150
|
|
90
151
|
|
@@ -136,9 +197,12 @@ module OpenKey
|
|
136
197
|
# dictionary word or name is the way to generate a powerful key
|
137
198
|
# that has embedded a near 100% entropy rating.
|
138
199
|
#
|
139
|
-
# @param
|
140
|
-
# this
|
141
|
-
#
|
200
|
+
# @param pbkdf2_string [String]
|
201
|
+
# this is a relatively small iteration count multiplier separated
|
202
|
+
# from the main salt characters by a plus sign. The salt characters
|
203
|
+
# will consist of 32 base64 characters which can be stored and fed
|
204
|
+
# into the {generate_key}.
|
205
|
+
#
|
142
206
|
# The salt string presented here must have either been recently
|
143
207
|
# generated by {generate_pbkdf2salt} or read from a persistence
|
144
208
|
# store and resubmitted here in order to regenerate the same key.
|
@@ -147,9 +211,16 @@ module OpenKey
|
|
147
211
|
# a key holder containing the key which can then be accessed via
|
148
212
|
# many different formats. The {Key} returned by this method
|
149
213
|
# encapsulates the derived key with the specified byte count.
|
150
|
-
def self.generate_key human_secret,
|
214
|
+
def self.generate_key human_secret, pbkdf2_string
|
215
|
+
|
216
|
+
KeyError.not_new pbkdf2_string, "PBKDF2 Algorithm Salt"
|
217
|
+
multiplier = pbkdf2_string.split("+")[0].to_i
|
218
|
+
pbkdf2_salt = pbkdf2_string.split("+")[1]
|
219
|
+
|
220
|
+
mult_msg = "Iteration multiplier is an integer from 1 to 16,384 not [#{multiplier}]."
|
221
|
+
raise ArgumentError, mult_msg_msg unless( multiplier > 0 && multiplier < 16385 )
|
222
|
+
iteration_count = multiplier * ONE_HUNDRED_THOUSAND
|
151
223
|
|
152
|
-
KeyError.not_new pbkdf2_salt, "PBKDF2 Algorithm Salt"
|
153
224
|
binary_salt = Key.to_binary_from_bit_string( Key64.to_bits( pbkdf2_salt ) )
|
154
225
|
err_msg = "Expected salt of #{PBKDF2_SALT_LENGTH_BYTES} bytes not #{binary_salt.length}."
|
155
226
|
raise ArgumentError, err_msg unless binary_salt.length == PBKDF2_SALT_LENGTH_BYTES
|
@@ -157,7 +228,7 @@ module OpenKey
|
|
157
228
|
pbkdf2_key = OpenSSL::PKCS5.pbkdf2_hmac(
|
158
229
|
human_secret,
|
159
230
|
binary_salt,
|
160
|
-
|
231
|
+
iteration_count,
|
161
232
|
PBKDF2_EXPORT_KEY_LENGTH,
|
162
233
|
OpenSSL::Digest::SHA384.new
|
163
234
|
)
|
@@ -167,6 +238,24 @@ module OpenKey
|
|
167
238
|
end
|
168
239
|
|
169
240
|
|
241
|
+
private
|
242
|
+
|
243
|
+
|
244
|
+
# ---
|
245
|
+
# --- Timings Code
|
246
|
+
# ---
|
247
|
+
# --- chopped_radix64_key = NIL
|
248
|
+
# --- require 'benchmark'
|
249
|
+
# --- timings = Benchmark.measure {
|
250
|
+
# ---
|
251
|
+
# --- -- wrapped up code block
|
252
|
+
# ---
|
253
|
+
# --- }
|
254
|
+
# ---
|
255
|
+
# --- log.info(x) { "PBKDF2 key generation timings ~> #{timings}" }
|
256
|
+
# ---
|
257
|
+
|
258
|
+
|
170
259
|
end
|
171
260
|
|
172
261
|
|
data/lib/keytools/key.api.rb
CHANGED
@@ -2,6 +2,22 @@
|
|
2
2
|
|
3
3
|
module OpenKey
|
4
4
|
|
5
|
+
# Use RubyMine to understand the correlations and dependencies on
|
6
|
+
# this now monolithic class that must be broken up before meaningful
|
7
|
+
# effective and efficient progress can be made.
|
8
|
+
#
|
9
|
+
# ---
|
10
|
+
#
|
11
|
+
# == REFACTOR KEY API TO DRAW OUT POSSIBLY THESE FIVE CONCEPTS.
|
12
|
+
#
|
13
|
+
# - [1] the safe tty token
|
14
|
+
# - [2] the machine configurations in ~/.config/openkey/openkey.app.config.ini
|
15
|
+
# - [3] the login / logout session crumbs database
|
16
|
+
# - [4] the master content database holding local config, chapters and verses
|
17
|
+
# - [5] the safe databases that unmarshal into either JSON or file content
|
18
|
+
#
|
19
|
+
# ---
|
20
|
+
#
|
5
21
|
# Use the key applications programming interface to transition the
|
6
22
|
# state of three (3) core keys in accordance with the needs of the
|
7
23
|
# executing use case.
|
@@ -93,7 +109,7 @@ module OpenKey
|
|
93
109
|
#
|
94
110
|
# [srn1-apzd]
|
95
111
|
# app.instance.id = crnl-d3my
|
96
|
-
# keystore.url.id = /home/
|
112
|
+
# keystore.url.id = /home/joe/credentials/repo
|
97
113
|
# initialize.time = Fri May 25 11:59:46 2018 ( 18145.1159.462 )
|
98
114
|
#
|
99
115
|
# @param domain_name [String]
|
@@ -540,11 +556,11 @@ module OpenKey
|
|
540
556
|
# are logging out of from the shell on this machine.
|
541
557
|
def self.do_logout( domain_name )
|
542
558
|
|
543
|
-
# --> @todo - user should ONLY type in
|
544
|
-
# --> @todo - user should ONLY type in
|
545
|
-
# --> @todo - user should ONLY type in
|
546
|
-
# --> @todo - user should ONLY type in
|
547
|
-
# --> @todo - user should ONLY type in
|
559
|
+
# --> @todo - user should ONLY type in logout | without domain name
|
560
|
+
# --> @todo - user should ONLY type in logout | without domain name
|
561
|
+
# --> @todo - user should ONLY type in logout | without domain name
|
562
|
+
# --> @todo - user should ONLY type in logout | without domain name
|
563
|
+
# --> @todo - user should ONLY type in logout | without domain name
|
548
564
|
|
549
565
|
|
550
566
|
# --> ######################
|
@@ -615,16 +631,97 @@ module OpenKey
|
|
615
631
|
end
|
616
632
|
|
617
633
|
|
634
|
+
# Return a date/time string detailing when the master database was first created.
|
635
|
+
#
|
636
|
+
# @param the_master_db [Hash]
|
637
|
+
# the master database to inspect = REFACTOR convert methods into a class instance
|
638
|
+
#
|
639
|
+
# @return [String]
|
640
|
+
# return a date/time string representation denoting when the master database
|
641
|
+
# was first created.
|
642
|
+
def self.to_db_create_date( the_master_db )
|
643
|
+
return the_master_db[ DB_CREATE_DATE ]
|
644
|
+
end
|
645
|
+
|
646
|
+
|
647
|
+
# Return the domain name of the master database.
|
648
|
+
#
|
649
|
+
# @param the_master_db [Hash]
|
650
|
+
# the master database to inspect = REFACTOR convert methods into a class instance
|
651
|
+
#
|
652
|
+
# @return [String]
|
653
|
+
# return the domain name of the master database.
|
654
|
+
def self.to_db_domain_name( the_master_db )
|
655
|
+
return the_master_db[ DB_DOMAIN_NAME ]
|
656
|
+
end
|
657
|
+
|
658
|
+
|
659
|
+
# Return the domain ID of the master database.
|
660
|
+
#
|
661
|
+
# @param the_master_db [Hash]
|
662
|
+
# the master database to inspect = REFACTOR convert methods into a class instance
|
663
|
+
#
|
664
|
+
# @return [String]
|
665
|
+
# return the domain ID of the master database.
|
666
|
+
def self.to_db_domain_id( the_master_db )
|
667
|
+
return the_master_db[ DB_DOMAIN_ID ]
|
668
|
+
end
|
669
|
+
|
670
|
+
|
671
|
+
# Return a dictionary containing a string key and the corresponding master database
|
672
|
+
# value whenever the master database key starts with the parameter string.
|
673
|
+
#
|
674
|
+
# For example if the master database contains a dictionary like this.
|
675
|
+
#
|
676
|
+
# envelope@earth => { radius => 24034km, sun_distance_light_minutes => 8 }
|
677
|
+
# textfile@kepler => { filepath => $HOME/keplers_laws.txt, filekey => Nsf8F34dhDT34jLKsLf52 }
|
678
|
+
# envelope@jupiter => { radius => 852837km, sun_distance_light_minutes => 6 }
|
679
|
+
# envelope@pluto => { radius => 2601km, sun_distance_light_minutes => 52 }
|
680
|
+
# textfile@newton => { filepath => $HOME/newtons_laws.txt, filekey => sdDFRTTYu4567fghFG5Jl }
|
681
|
+
#
|
682
|
+
# with "envelope@" as the start string to match.
|
683
|
+
# The returned dictionary would have 3 elements whose keys are the unique portion of the string.
|
684
|
+
#
|
685
|
+
# earth => { radius => 24034km, sun_distance_light_minutes => 8 }
|
686
|
+
# jupiter => { radius => 852837km, sun_distance_light_minutes => 6 }
|
687
|
+
# pluto => { radius => 2601km, sun_distance_light_minutes => 52 }
|
688
|
+
#
|
689
|
+
# If no matches are found an empty dictionary is returned.
|
690
|
+
#
|
691
|
+
# @param the_master_db [Hash]
|
692
|
+
# the master database to inspect = REFACTOR convert methods into a class instance
|
693
|
+
#
|
694
|
+
# @param start_string [String]
|
695
|
+
# the start string to match. Every key in the master database that
|
696
|
+
# starts with this string is considered a match. The corresponding value
|
697
|
+
# of each matching key is appended onto the end of an array.
|
698
|
+
#
|
699
|
+
# @return [Hash]
|
700
|
+
# a dictionary whose keys are the unique (2nd) portion of the string with corresponding
|
701
|
+
# values and in no particular order.
|
702
|
+
def self.to_matching_dictionary( the_master_db, start_string )
|
703
|
+
|
704
|
+
matching_dictionary = {}
|
705
|
+
the_master_db.each_key do | db_key |
|
706
|
+
next unless db_key.start_with?( start_string )
|
707
|
+
dictionary_key = db_key.gsub( start_string, "" )
|
708
|
+
matching_dictionary.store( dictionary_key, the_master_db[db_key] )
|
709
|
+
end
|
710
|
+
return matching_dictionary
|
711
|
+
|
712
|
+
end
|
713
|
+
|
714
|
+
|
618
715
|
# To read the content we first find the appropriate shell key and the
|
619
|
-
# appropriate database ciphertext, one decrypts the other to produce the
|
620
|
-
# database
|
621
|
-
#
|
716
|
+
# appropriate database ciphertext, one decrypts the other to produce the master
|
717
|
+
# database decryption key which in turn reveals the JSON representation of the
|
718
|
+
# master database.
|
622
719
|
#
|
623
|
-
# The
|
720
|
+
# The master database JSON is deserialized as a {Hash} and returned.
|
624
721
|
#
|
625
|
-
# <b>Steps Taken To Read the
|
722
|
+
# <b>Steps Taken To Read the Master Database</b>
|
626
723
|
#
|
627
|
-
# Reading the
|
724
|
+
# Reading the master database requires a rostra of actions namely
|
628
725
|
#
|
629
726
|
# - reading the path to the <b>keystore breadcrumbs file</b>
|
630
727
|
# - using the session token to derive the (unique to the) shell key
|
@@ -632,10 +729,19 @@ module OpenKey
|
|
632
729
|
# - reading the encrypted and encoded content, decoding and decrypting it
|
633
730
|
# - employing index key, ciphertext and random iv to reveal the content
|
634
731
|
#
|
732
|
+
# @param use_grandparent_pid [Boolean]
|
733
|
+
#
|
734
|
+
# Optional boolean parameter. If set to true the PID (process ID) used
|
735
|
+
# as part of an obfuscator key and normally acquired from the parent
|
736
|
+
# process should now be acquired from the grandparent's process.
|
737
|
+
#
|
738
|
+
# Set to true when accessing the safe's credentials from a sub process
|
739
|
+
# rather than directly through the logged in shell.
|
740
|
+
#
|
635
741
|
# @return [String]
|
636
742
|
# decode, decrypt and hen return the plain text content that was written
|
637
743
|
# to a file by the {write_content} method.
|
638
|
-
def self.
|
744
|
+
def self.read_master_db( use_grandparent_pid = false )
|
639
745
|
|
640
746
|
# --
|
641
747
|
# -- Get the filepath to the breadcrumbs file using the trail in
|
@@ -652,7 +758,7 @@ module OpenKey
|
|
652
758
|
# --
|
653
759
|
# -- Regenerate intra-session key from the session token.
|
654
760
|
# --
|
655
|
-
intra_key = KeyLocal.regenerate_shell_key( to_token() )
|
761
|
+
intra_key = KeyLocal.regenerate_shell_key( to_token(), use_grandparent_pid )
|
656
762
|
|
657
763
|
# --
|
658
764
|
# -- Decrypt and acquire the content enryption key that was created
|
@@ -674,10 +780,10 @@ module OpenKey
|
|
674
780
|
# --
|
675
781
|
# -- Get the full ciphertext file (warts and all) and then top and
|
676
782
|
# -- tail until just the valuable ciphertext is at hand. Decode then
|
677
|
-
# -- decrypt the ciphertext and instantiate a key database from
|
783
|
+
# -- decrypt the ciphertext and instantiate a key database from the
|
784
|
+
# -- resulting JSON string.
|
678
785
|
# --
|
679
786
|
crypt_txt = binary_from_read( crypt_filepath )
|
680
|
-
|
681
787
|
json_content = power_key.do_decrypt_text( random_iv, crypt_txt )
|
682
788
|
|
683
789
|
return KeyDb.from_json( json_content )
|
@@ -709,8 +815,8 @@ module OpenKey
|
|
709
815
|
# method and the resulting content will be encrypted and written to
|
710
816
|
# the file at path {content_ciphertxt_file_from_session_token}.
|
711
817
|
#
|
712
|
-
# This method's mirror is {
|
713
|
-
def self.
|
818
|
+
# This method's mirror is {read_master_db}.
|
819
|
+
def self.write_master_db( content_header, app_database )
|
714
820
|
|
715
821
|
# --
|
716
822
|
# -- Get the filepath to the breadcrumbs file using the trail in
|
@@ -918,13 +1024,9 @@ module OpenKey
|
|
918
1024
|
crypt_txt = binary_from_read( content_path )
|
919
1025
|
random_iv = KeyIV.in_binary( crumbs_map[ CONTENT_RANDOM_IV ] )
|
920
1026
|
crypt_key = Key.from_char64( crumbs_map[ CONTENT_ENCRYPT_KEY ] )
|
921
|
-
|
1027
|
+
text_data = crypt_key.do_decrypt_text( random_iv, crypt_txt )
|
922
1028
|
|
923
|
-
|
924
|
-
# -- The decrypted content is expected to be a tree data structure
|
925
|
-
# -- streamed into JSON before encryption.
|
926
|
-
# --
|
927
|
-
return KeyDb.from_json( json_text )
|
1029
|
+
return text_data
|
928
1030
|
|
929
1031
|
end
|
930
1032
|
|
@@ -967,7 +1069,7 @@ module OpenKey
|
|
967
1069
|
# and if it exists we assert that the content filepath should also
|
968
1070
|
# be present.
|
969
1071
|
#
|
970
|
-
def self.
|
1072
|
+
def self.db_envelope_exists?( crumbs_map )
|
971
1073
|
|
972
1074
|
return false if crumbs_map.nil?
|
973
1075
|
return false unless crumbs_map.has_key?( CONTENT_EXTERNAL_ID )
|
@@ -1028,13 +1130,17 @@ module OpenKey
|
|
1028
1130
|
private
|
1029
1131
|
|
1030
1132
|
|
1133
|
+
# --------------------------------------------------------
|
1134
|
+
# In order to separate keys into a new gem we must
|
1135
|
+
# break knowledge of this variable name and have it
|
1136
|
+
# instead passed in by clients.
|
1137
|
+
TOKEN_VARIABLE_NAME = "SAFE_TTY_TOKEN"
|
1138
|
+
TOKEN_VARIABLE_SIZE = 152
|
1139
|
+
# --------------------------------------------------------
|
1031
1140
|
|
1032
|
-
MACHINE_CONFIG_FILE = File.join( Dir.home, ".config/openkey/openkey.app.config.ini" )
|
1033
1141
|
|
1142
|
+
MACHINE_CONFIG_FILE = File.join( Dir.home, ".config/openkey/openkey.app.config.ini" )
|
1034
1143
|
SESSION_APP_DOMAINS = "session.app.domains"
|
1035
|
-
TOKEN_VARIABLE_NAME = "OPEN_SESSION_TOKEN"
|
1036
|
-
TOKEN_VARIABLE_SIZE = 150
|
1037
|
-
|
1038
1144
|
SESSION_IDENTIFIER_KEY = "session.identifiers"
|
1039
1145
|
KEYSTORE_IDENTIFIER_KEY = "keystore.url.id"
|
1040
1146
|
APP_INSTANCE_ID_KEY = "app.instance.id"
|
@@ -1072,7 +1178,11 @@ module OpenKey
|
|
1072
1178
|
CONTENT_FILE_PREFIX = "tree.db"
|
1073
1179
|
CONTENT_EXTERNAL_ID = "content.xid"
|
1074
1180
|
CONTENT_ENCRYPT_KEY = "content.key"
|
1075
|
-
CONTENT_RANDOM_IV
|
1181
|
+
CONTENT_RANDOM_IV = "content.iv"
|
1182
|
+
|
1183
|
+
DB_CREATE_DATE = "db.create.date"
|
1184
|
+
DB_DOMAIN_NAME = "db.domain.name"
|
1185
|
+
DB_DOMAIN_ID = "db.domain.id"
|
1076
1186
|
|
1077
1187
|
|
1078
1188
|
def self.binary_to_write( to_filepath, content_header, binary_ciphertext )
|
@@ -1121,9 +1231,9 @@ module OpenKey
|
|
1121
1231
|
app_id = KeyId.derive_app_instance_identifier( domain_name )
|
1122
1232
|
|
1123
1233
|
initial_db = KeyDb.new()
|
1124
|
-
initial_db.store(
|
1125
|
-
initial_db.store(
|
1126
|
-
initial_db.store(
|
1234
|
+
initial_db.store( DB_CREATE_DATE, KeyNow.fetch() )
|
1235
|
+
initial_db.store( DB_DOMAIN_NAME, domain_name )
|
1236
|
+
initial_db.store( DB_DOMAIN_ID, app_id )
|
1127
1237
|
return initial_db.to_json
|
1128
1238
|
|
1129
1239
|
end
|
@@ -1259,15 +1369,15 @@ module OpenKey
|
|
1259
1369
|
end
|
1260
1370
|
|
1261
1371
|
|
1262
|
-
def raise_token_error env_var_name, message
|
1372
|
+
def self.raise_token_error env_var_name, message
|
1263
1373
|
|
1264
1374
|
puts ""
|
1265
1375
|
puts "#{TOKEN_VARIABLE_NAME} environment variable #{message}."
|
1266
1376
|
puts "To instantiate it you can use the below command."
|
1267
1377
|
puts ""
|
1268
|
-
puts "$ export
|
1378
|
+
puts "$ export #{TOKEN_VARIABLE_NAME}=`safe token`"
|
1269
1379
|
puts ""
|
1270
|
-
puts "ps => those are backticks around `
|
1380
|
+
puts "ps => those are backticks around `safe token` (not apostrophes)."
|
1271
1381
|
puts ""
|
1272
1382
|
|
1273
1383
|
raise RuntimeError, "#{TOKEN_VARIABLE_NAME} environment variable #{message}."
|