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
@@ -0,0 +1,262 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
|
7
|
+
# PBKDF2 is a powerful leading <b>Key Derivation Function (KDF)</b> that exists to
|
8
|
+
# convert <b>low entropy</b> human created passwords into a high entropy key that
|
9
|
+
# is computationally infeasible to acquire through brute force.
|
10
|
+
#
|
11
|
+
# As human generated passwords have a relatively small key space, key derivation
|
12
|
+
# functions must be slow to compute with any implementation.
|
13
|
+
#
|
14
|
+
# PBKDF2 offers an <b>iteration count</b> that configures the number of iterations
|
15
|
+
# performed to create the key.
|
16
|
+
#
|
17
|
+
# <b>One million (1,000,000) should be the iteration count's lower bound.</b>
|
18
|
+
#
|
19
|
+
# == Upgrading the OpenSSL <em>pbkdf2_hmac</em> Behaviour
|
20
|
+
#
|
21
|
+
# As soon as the new Ruby and OpenSSL libraries become commonplace this class should
|
22
|
+
# be upgraded to use the <b>new and improved {OpenSSL::KDF.pbkdf2_hmac} behaviour</b>
|
23
|
+
# rather than {OpenSSL::PKCS5.pbkdf2_hmac}.
|
24
|
+
#
|
25
|
+
# The difficulty is in detecting the operating system's C libraries that are directly
|
26
|
+
# accessed for OpenSSL functionality. If the distinction can be made accurately, those
|
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.
|
54
|
+
class KeyPbkdf2
|
55
|
+
|
56
|
+
|
57
|
+
# <b>One million iterations</b> is necessary due to the
|
58
|
+
# growth of <b>GPU driven cloud based computing</b> power
|
59
|
+
# that is curently being honed by mining BitCoin and training
|
60
|
+
# neural networks.
|
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
|
86
|
+
|
87
|
+
|
88
|
+
# Documentation for this algorithm says this about the key length.
|
89
|
+
#
|
90
|
+
# Make the key length <b>larger than or equal to the output length</b>
|
91
|
+
# of the <b>underlying digest function</b>, otherwise an attacker could
|
92
|
+
# simply try to brute-force the key.
|
93
|
+
#
|
94
|
+
# According to PKCS#5, security is limited by the output length of
|
95
|
+
# the underlying digest function, i.e. security is not improved if a
|
96
|
+
# key length strictly larger than the digest output length is chosen.
|
97
|
+
#
|
98
|
+
# Therefore, when using PKCS5 for password storage, it suffices to
|
99
|
+
# store values equal to the digest output length, nothing is gained
|
100
|
+
# by storing larger values.
|
101
|
+
PBKDF2_EXPORT_KEY_LENGTH = OpenSSL::Digest::SHA384.new.digest_length
|
102
|
+
|
103
|
+
|
104
|
+
# For a 384 bit digest the key length is 48 bytes and the bit length
|
105
|
+
# is 384 bits.
|
106
|
+
PBKDF2_EXPORT_BIT_LENGTH = PBKDF2_EXPORT_KEY_LENGTH * 8
|
107
|
+
|
108
|
+
|
109
|
+
# The documented recommended salt length in bytes for the PBKDF2
|
110
|
+
# algorithm is between <b>16 and 24 bytes</b>. The setting here is
|
111
|
+
# at the upper bound of that range.
|
112
|
+
PBKDF2_SALT_LENGTH_BYTES = 24
|
113
|
+
|
114
|
+
|
115
|
+
# Return a random cryptographic salt generated from twenty-four
|
116
|
+
# random bytes produced by a secure random number generator. The
|
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.
|
128
|
+
#
|
129
|
+
# 42+12345678abcdefgh12345678ABCDEFGH ~> 4,200,000 iterations
|
130
|
+
# 9+12345678abcdefgh12345678ABCDEFGH ~> 900,000 iterations
|
131
|
+
# 100+12345678abcdefgh12345678ABCDEFGH ~> 10,000,000 iterations
|
132
|
+
#
|
133
|
+
# Note that the generate key method will convert the trailing 32
|
134
|
+
# base64 characters back into a <b>24 byte binary</b> string.
|
135
|
+
#
|
136
|
+
# @return [String]
|
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.
|
144
|
+
def self.generate_pbkdf2_salt
|
145
|
+
|
146
|
+
pbkdf2_salt = Key64.from_bits( Key.to_random_bits( PBKDF2_SALT_LENGTH_BYTES ) )
|
147
|
+
return "#{PBKDF2_ITERATION_MULTIPLIER}+#{pbkdf2_salt}"
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
# Generate a 128 bit binary key from the PBKDF2 password derivation
|
153
|
+
# function. The most important input to this function is the human
|
154
|
+
# generated key. The best responsibly sourced key with at least 95%
|
155
|
+
# entropy will contain about 40 characters spread randomly over the
|
156
|
+
# set of 95 typable characters.
|
157
|
+
#
|
158
|
+
# Aside from the human password the other inputs are
|
159
|
+
#
|
160
|
+
# - a base64 encoded randomly generated salt of 16 to 24 bytes
|
161
|
+
# - an iteration count of at least 1 million (due to GPU advances)
|
162
|
+
# - an output key length that is at least 16 bytes (128 bits)
|
163
|
+
# - a digest algorithm implementation (we use SHA512K)
|
164
|
+
#
|
165
|
+
# The {Key} returned by this method encapsulates the derived
|
166
|
+
# key of the byte (bit) length specified.
|
167
|
+
#
|
168
|
+
# <b>PBKDF2 Output Key Length Note</b>
|
169
|
+
#
|
170
|
+
# Documentation for this algorithm says this about the key length.
|
171
|
+
#
|
172
|
+
# Typically, the key length should be larger than or equal to the
|
173
|
+
# output length of the underlying digest function, otherwise an
|
174
|
+
# attacker could simply try to brute-force the key. According to
|
175
|
+
# PKCS#5, security is limited by the output length of the underlying
|
176
|
+
# digest function, i.e. security is not improved if a key length
|
177
|
+
# strictly larger than the digest output length is chosen.
|
178
|
+
#
|
179
|
+
# Therefore, when using PKCS5 for password storage, it suffices to
|
180
|
+
# store values equal to the digest output length, nothing is gained
|
181
|
+
# by storing larger values.
|
182
|
+
#
|
183
|
+
# <b>Upgrading the OpenSSL <em>pbkdf2_hmac</em> Behaviour</b>
|
184
|
+
#
|
185
|
+
# As soon as the new Ruby and OpenSSL libraries become commonplace this class should
|
186
|
+
# be upgraded to use the <b>new and improved {OpenSSL::KDF.pbkdf2_hmac} behaviour</b>
|
187
|
+
# rather than {OpenSSL::PKCS5.pbkdf2_hmac}.
|
188
|
+
#
|
189
|
+
# The difficulty is in detecting the operating system's C libraries that are directly
|
190
|
+
# accessed for OpenSSL functionality. If the distinction can be made accurately, those
|
191
|
+
# with newer libraries can reap the benefits immediately.
|
192
|
+
#
|
193
|
+
# @param human_secret [String]
|
194
|
+
# a robust human generated password with as much entropy as can
|
195
|
+
# be mustered. Remember that 40 characters spread randomly over
|
196
|
+
# the key space of about 95 characters and not relating to any
|
197
|
+
# dictionary word or name is the way to generate a powerful key
|
198
|
+
# that has embedded a near 100% entropy rating.
|
199
|
+
#
|
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
|
+
#
|
206
|
+
# The salt string presented here must have either been recently
|
207
|
+
# generated by {generate_pbkdf2salt} or read from a persistence
|
208
|
+
# store and resubmitted here in order to regenerate the same key.
|
209
|
+
#
|
210
|
+
# @return [Key]
|
211
|
+
# a key holder containing the key which can then be accessed via
|
212
|
+
# many different formats. The {Key} returned by this method
|
213
|
+
# encapsulates the derived key with the specified byte count.
|
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
|
223
|
+
|
224
|
+
binary_salt = Key.to_binary_from_bit_string( Key64.to_bits( pbkdf2_salt ) )
|
225
|
+
err_msg = "Expected salt of #{PBKDF2_SALT_LENGTH_BYTES} bytes not #{binary_salt.length}."
|
226
|
+
raise ArgumentError, err_msg unless binary_salt.length == PBKDF2_SALT_LENGTH_BYTES
|
227
|
+
|
228
|
+
pbkdf2_key = OpenSSL::PKCS5.pbkdf2_hmac(
|
229
|
+
human_secret,
|
230
|
+
binary_salt,
|
231
|
+
iteration_count,
|
232
|
+
PBKDF2_EXPORT_KEY_LENGTH,
|
233
|
+
OpenSSL::Digest::SHA384.new
|
234
|
+
)
|
235
|
+
|
236
|
+
return Key.from_binary( pbkdf2_key )
|
237
|
+
|
238
|
+
end
|
239
|
+
|
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
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
|
7
|
+
# SCrypt is a <b>Key Derivation Function (KDF)</b> with a reliable OpenSSL
|
8
|
+
# implementation that converts <b>low entropy</b> password-like text to a
|
9
|
+
# high entropy key that is computationally infeasible to acquire through brute
|
10
|
+
# force.
|
11
|
+
#
|
12
|
+
# SCrypt is incredibly resistant to attacks using dedicated hardware with
|
13
|
+
# massive memory to boot.
|
14
|
+
#
|
15
|
+
class KdfSCrypt
|
16
|
+
|
17
|
+
# SCrypt salts are recommended to contain 16 and 32 bytes
|
18
|
+
# inclusive. Here we opt for 24 bytes which unrolls out to
|
19
|
+
# 192 bits which serializes into 32 base64 characters.
|
20
|
+
SCRYPT_SALT_BYTE_LENGTH = 24
|
21
|
+
|
22
|
+
# The iteration count is determined using the powers of
|
23
|
+
# two so if the iteration integer is 12 there will be two
|
24
|
+
# to the power of 12 ( 2^12 ) giving 4096 iterations.
|
25
|
+
# The minimum number is 4 (16 iterations) and the max is 31.
|
26
|
+
# @example
|
27
|
+
# Configuring 16 into this directive results in
|
28
|
+
# 2^16 = 65,536 iterations
|
29
|
+
#
|
30
|
+
# This is a safe default and will slow the derivation time
|
31
|
+
# to about a second on a powerful 2020 laptop.
|
32
|
+
SCRYPT_ITERATION_INTEGER = 16
|
33
|
+
|
34
|
+
# The scrypt algorithm produces a key that is 181 bits in
|
35
|
+
# length. The algorithm then converts the binary 181 bits
|
36
|
+
# into a (6-bit) Radix64 character.
|
37
|
+
#
|
38
|
+
# 181 / 6 = 30 remainder 1 (so 31 characters are needed).
|
39
|
+
SCRYPT_KEY_LENGTH = 31
|
40
|
+
|
41
|
+
|
42
|
+
# When the key is transported using a 64 character set where
|
43
|
+
# each character is represented by 6 bits - the Scrypt key
|
44
|
+
# expands to 186 bits rather than the original 181 bits.
|
45
|
+
#
|
46
|
+
# This expansion is because of the remainder.
|
47
|
+
#
|
48
|
+
# 181 bits divided by 6 is 30 characters plus 1 character
|
49
|
+
# for the extra bit.
|
50
|
+
#
|
51
|
+
# The 31 transported characters then appear as
|
52
|
+
# 31 times 6 which equals 186 bits.
|
53
|
+
SCRYPT_KEY_TRANSPORT_LENGTH = 186
|
54
|
+
|
55
|
+
# The scrypt algorithm salt string should be 22 characters
|
56
|
+
# and may include forward slashes and periods.
|
57
|
+
SCRYPT_SALT_LENGTH = 22
|
58
|
+
|
59
|
+
# Scrypt outputs a single line of text that holds the prefix
|
60
|
+
# then the Radix64 encoded salt and finally the Radix64
|
61
|
+
# encoded hash key.
|
62
|
+
#
|
63
|
+
# The prefix consists of <b>two sections</b> sandwiched within
|
64
|
+
# two dollar <b>$</b> signs at the extremeties and a third dollar
|
65
|
+
# separating them.
|
66
|
+
#
|
67
|
+
# The two sections are the
|
68
|
+
# - Scrypt algorithm <b>version number</b> (2a or 2b) and
|
69
|
+
# - a power of 2 integer defining the no. of interations
|
70
|
+
SCRYPT_OUTPUT_TEXT_PREFIX = "$2a$#{SCRYPT_ITERATION_INTEGER}$"
|
71
|
+
|
72
|
+
|
73
|
+
# Generate a secure random and unpredictable salt suitable for
|
74
|
+
# the SCrypt algorithm. SCrypt salts are recommended to contain
|
75
|
+
# 16 and 32 bytes inclusive. Here we opt for 24 bytes which
|
76
|
+
# unrolls to 192 bits which in turn is 32 base64 characters.
|
77
|
+
#
|
78
|
+
# The {SafeDb::KdfSCrypt::SCRYPT_SALT_BYTE_LENGTH} constant
|
79
|
+
# defines the <b>number of random bytes</b> required for a robust
|
80
|
+
# SCrypt salt.
|
81
|
+
#
|
82
|
+
# The salt can be persisted and then resubmitted in order to
|
83
|
+
# regenerate the same key in the future.
|
84
|
+
#
|
85
|
+
# @return [String]
|
86
|
+
# the salt in a bit string format which can be converted to
|
87
|
+
# in order to feed the derivation function or indeed converted
|
88
|
+
# to base64 in order to persist it.
|
89
|
+
def self.generate_scrypt_salt
|
90
|
+
return Key.to_random_bits( SCRYPT_SALT_BYTE_LENGTH )
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
# Key generators should first use the {generate_salt} method to create
|
96
|
+
# a Scrypt salt string and then submit it to this method together with
|
97
|
+
# a human generated password in order to derive a key.
|
98
|
+
#
|
99
|
+
# The salt can be persisted and then resubmitted again to this method
|
100
|
+
# in order to regenerate the same key at any time in the future.
|
101
|
+
#
|
102
|
+
# Generate a binary key from the scrypt password derivation function.
|
103
|
+
#
|
104
|
+
# This differs from a server side password to hash usage in that we
|
105
|
+
# are interested in the 186bit key that scrypt produces. This method
|
106
|
+
# returns this reproducible key for use during symmetric encryption and
|
107
|
+
# decryption.
|
108
|
+
#
|
109
|
+
# @param secret_text [String]
|
110
|
+
# a robust human generated password with as much entropy as can
|
111
|
+
# be mustered. Remember that 40 characters spread randomly over
|
112
|
+
# the key space of about 90 characters and not relating to any
|
113
|
+
# dictionary word or name is the way to generate a powerful key
|
114
|
+
# that has embedded a near 100% entropy rating.
|
115
|
+
#
|
116
|
+
# @param scrypt_salt [String]
|
117
|
+
# the salt string that has either been recently generated via the
|
118
|
+
# {generate_salt} method or read from a persistence store and
|
119
|
+
# resubmitted here (in the future) to regenerate the same key.
|
120
|
+
#
|
121
|
+
# @return [Key]
|
122
|
+
# a key holder containing the key which can then be accessed via
|
123
|
+
# many different formats.
|
124
|
+
def self.generate_key secret_text, scrypt_salt
|
125
|
+
|
126
|
+
binary_salt = Key.to_binary_from_bit_string( scrypt_salt )
|
127
|
+
|
128
|
+
require "openssl"
|
129
|
+
|
130
|
+
puts ""
|
131
|
+
puts $LOADED_FEATURES.grep(/openssl/)
|
132
|
+
puts ""
|
133
|
+
|
134
|
+
scrypt_key = OpenSSL::KDF.scrypt(secret_text, salt: binary_salt, N: 2**SCRYPT_ITERATION_INTEGER, r: 8, p: 1, length: 33)
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
=begin
|
139
|
+
hashed_secret = Scrypt::Engine.hash_secret( secret_text, to_scrypt_salt(scrypt_salt) )
|
140
|
+
encoded64_key = Scrypt::Password.new( hashed_secret ).to_s
|
141
|
+
key_begin_index = SCRYPT_OUTPUT_TEXT_PREFIX.length + SCRYPT_SALT_LENGTH
|
142
|
+
radix64_key_str = encoded64_key[ key_begin_index .. -1 ]
|
143
|
+
key_length_mesg = "The scrypt key length should have #{SCRYPT_KEY_LENGTH} characters."
|
144
|
+
raise RuntimeError, key_length_mesg unless radix64_key_str.length == SCRYPT_KEY_LENGTH
|
145
|
+
|
146
|
+
return Key.new(radix64_key_str)
|
147
|
+
=end
|
148
|
+
return scrypt_key
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
|
156
|
+
def self.scrypt_test_method
|
157
|
+
|
158
|
+
puts ""
|
159
|
+
puts "##############################################################################"
|
160
|
+
|
161
|
+
key_count = 20
|
162
|
+
for n in 0 .. key_count
|
163
|
+
scrypt_saltbits = SafeDb::KdfSCrypt.generate_scrypt_salt
|
164
|
+
scrypt_key = SafeDb::KdfSCrypt.generate_key( "abonekanoby", scrypt_saltbits )
|
165
|
+
scrypt_saltchar = SafeDb::Key64.from_bits( scrypt_saltbits )
|
166
|
+
puts "#{n} Salt => #{scrypt_saltchar} (#{scrypt_saltchar.length}) => Key => #{scrypt_key} (#{scrypt_key.length})"
|
167
|
+
end
|
168
|
+
|
169
|
+
puts "##############################################################################"
|
170
|
+
puts ""
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
def self.to_scrypt_salt the_salt
|
177
|
+
return SCRYPT_OUTPUT_TEXT_PREFIX + the_salt
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.assert_scrypt_salt the_salt
|
181
|
+
raise RuntimeError, "scrypt salt not expected to be nil." if the_salt.nil?
|
182
|
+
salt_length_msg = "A scrypt salt is expected to contain #{SCRYPT_SALT_LENGTH} characters."
|
183
|
+
raise RuntimeError, salt_length_msg unless the_salt.length == SCRYPT_SALT_LENGTH
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
end
|