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.
- checksums.yaml +5 -5
- data/README.md +56 -159
- data/bin/opensecret +2 -2
- data/bin/ops +17 -2
- data/lib/extension/string.rb +14 -16
- data/lib/{interpreter.rb → interprete.rb} +53 -29
- data/lib/keytools/binary.map.rb +49 -0
- data/lib/keytools/kdf.api.rb +249 -0
- data/lib/keytools/kdf.bcrypt.rb +64 -29
- data/lib/keytools/kdf.pbkdf2.rb +92 -83
- 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 +1281 -0
- data/lib/keytools/key.db.rb +265 -0
- data/lib/keytools/{key.module.rb → key.docs.rb} +55 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +265 -0
- data/lib/keytools/key.mach.rb +248 -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 +428 -298
- data/lib/keytools/keydebug.txt +295 -0
- data/lib/logging/gem.logging.rb +3 -3
- data/lib/modules/cryptology/collect.rb +20 -0
- data/lib/session/require.gem.rb +1 -1
- data/lib/usecase/cmd.rb +417 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +174 -0
- data/lib/usecase/init.rb +78 -0
- data/lib/usecase/login.rb +70 -0
- data/lib/usecase/logout.rb +30 -0
- data/lib/usecase/open.rb +126 -0
- data/lib/{interprete → usecase}/put.rb +100 -47
- data/lib/usecase/read.rb +89 -0
- data/lib/{interprete → usecase}/safe.rb +0 -0
- data/lib/{interprete → usecase}/set.rb +0 -0
- data/lib/usecase/token.rb +111 -0
- data/lib/{interprete → usecase}/use.rb +0 -0
- data/lib/version.rb +1 -1
- data/opensecret.gemspec +4 -3
- metadata +39 -33
- data/lib/exception/cli.error.rb +0 -53
- data/lib/exception/errors/cli.errors.rb +0 -31
- data/lib/interprete/begin.rb +0 -232
- data/lib/interprete/cmd.rb +0 -621
- data/lib/interprete/export.rb +0 -163
- data/lib/interprete/init.rb +0 -205
- data/lib/interprete/key.rb +0 -119
- data/lib/interprete/open.rb +0 -148
- data/lib/interprete/seal.rb +0 -129
- data/lib/keytools/digester.rb +0 -245
- data/lib/keytools/key.data.rb +0 -227
- data/lib/keytools/key.derivation.rb +0 -341
- data/lib/modules/mappers/collateral.rb +0 -282
- data/lib/modules/mappers/envelope.rb +0 -127
- data/lib/modules/mappers/settings.rb +0 -170
- data/lib/notepad/scratch.pad.rb +0 -224
- data/lib/store-commands.txt +0 -180
data/lib/keytools/binary.map.rb
CHANGED
@@ -234,6 +234,55 @@ cipher.key = key
|
|
234
234
|
|
235
235
|
end
|
236
236
|
|
237
|
+
|
238
|
+
def self.binbytes
|
239
|
+
|
240
|
+
for n in 0 .. 256
|
241
|
+
eight_bit_binary = "%08d" % [ n.to_s(2) ]
|
242
|
+
puts "#{eight_bit_binary} => #{n}"
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
def self.shovel
|
249
|
+
|
250
|
+
first_name = "joe"
|
251
|
+
last_name = "bloggs"
|
252
|
+
|
253
|
+
user_name = first_name
|
254
|
+
user_name << last_name
|
255
|
+
|
256
|
+
puts "Username => #{user_name}"
|
257
|
+
puts "First name => #{first_name}"
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
def self.print_randoms
|
263
|
+
require 'securerandom'
|
264
|
+
for n in 0 .. 99
|
265
|
+
puts "#{from_rand}"
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
|
271
|
+
def self.from_rand
|
272
|
+
random_bit_string = ""
|
273
|
+
the_ints = Array.new
|
274
|
+
for n in 1 .. 48
|
275
|
+
random_integer = SecureRandom.random_number( 256 )
|
276
|
+
the_ints.push random_integer
|
277
|
+
random_bit_string += "%08d" % [ random_integer.to_s(2) ]
|
278
|
+
end
|
279
|
+
puts "Integers => #{the_ints.to_s}"
|
280
|
+
return random_bit_string
|
281
|
+
end
|
282
|
+
|
283
|
+
## BinaryMap.print_randoms
|
284
|
+
## BinaryMap.shovel
|
285
|
+
## BinaryMap.binbytes
|
237
286
|
## BinaryMap.binary
|
238
287
|
## BinaryMap.crypter
|
239
288
|
## BinaryMap.bcrypter
|
@@ -0,0 +1,249 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module OpenKey
|
5
|
+
|
6
|
+
# The OpenKey underlying security strategy is to lock a master index file
|
7
|
+
# with a <b>symmetric encryption key</b> that is based on two randomly generated
|
8
|
+
# and amalgamated <b>55 and 45 character keys</b> and then to lock that key
|
9
|
+
# <b>(and only that key)</b> with a 256 bit symmetric encryption key derived from
|
10
|
+
# a human password and generated by at least two cryptographic workhorses known
|
11
|
+
# as <b>key derivation functions</b>.
|
12
|
+
#
|
13
|
+
# Random powerful keys are derived are seeded with 55 random bytes and
|
14
|
+
# then fed through the master key generator and its two key derivation
|
15
|
+
# functions (BCrypt and PBKDF2).
|
16
|
+
#
|
17
|
+
# == What Does the Master Encryption Key Generator Do?
|
18
|
+
#
|
19
|
+
# This class sits at the core of implementing that strategy and works to produce
|
20
|
+
# 256 bit encryption key derived from a human password which is then minced by
|
21
|
+
# two best of breed key derivation functions (BCrypt and PBKDF2).
|
22
|
+
#
|
23
|
+
# BCrypt (Blowfish) and PBKDF2 are the leading <b>key derivation functions</b>
|
24
|
+
# whose modus operandi is to convert <b>low entropy</b> human generated passwords
|
25
|
+
# into a high entropy key that is computationally infeasible to acquire via brute
|
26
|
+
# force.
|
27
|
+
#
|
28
|
+
# == How to Create the Encryption Key
|
29
|
+
#
|
30
|
+
# To create a high entropy encryption key this method takes the first
|
31
|
+
# 168 bits from the 186 bit BCrypt key produced by {KdfBCrypt} and
|
32
|
+
# the first 96 bits from the 132 bit PBKDF2 key produced inside the
|
33
|
+
# {KeyPbkdf2} class and amalgamates them to produce a 264 bit key.
|
34
|
+
#
|
35
|
+
# The 264 bit key is then digested to produce a 256bit encryption key.
|
36
|
+
class KdfApi
|
37
|
+
|
38
|
+
|
39
|
+
# BCrypt (Blowfish) and PBKDF2 are the leading <b>key derivation functions</b>
|
40
|
+
# whose modus operandi is to convert <b>low entropy</b> human generated passwords
|
41
|
+
# into a high entropy key that is computationally infeasible to acquire via brute
|
42
|
+
# force.
|
43
|
+
BCRYPT_SALT_KEY_NAME = "bcrypt.salt"
|
44
|
+
|
45
|
+
|
46
|
+
# BCrypt (Blowfish) and PBKDF2 are the leading <b>key derivation functions</b>
|
47
|
+
# whose modus operandi is to convert <b>low entropy</b> human generated passwords
|
48
|
+
# into a high entropy key that is computationally infeasible to acquire via brute
|
49
|
+
# force.
|
50
|
+
PBKDF2_SALT_KEY_NAME = "pbkdf2.salt"
|
51
|
+
|
52
|
+
|
53
|
+
# To create a high entropy encryption key we use the full 180 bits
|
54
|
+
# from the returned 180 bit BCrypt key produced by {KdfBCrypt}.
|
55
|
+
#
|
56
|
+
# When amalgamated with the <b>332 bits from the PBKDF2 Key</b> we
|
57
|
+
# achieve a powerful <b>union key length</b> of 512 bits.
|
58
|
+
BCRYPT_KEY_CONTRIBUTION_SIZE = 180
|
59
|
+
|
60
|
+
|
61
|
+
# The first 332 bits are used from the 384 bit key returned by the
|
62
|
+
# PBKDF2 algorithm.
|
63
|
+
#
|
64
|
+
# When amalgamated with the <b>180 bits from the BCrypt Key</b> we
|
65
|
+
# achieve a powerful <b>union key length</b> of 512 bits.
|
66
|
+
PBKDF2_KEY_CONTRIBUTION_SIZE = 332
|
67
|
+
|
68
|
+
|
69
|
+
# To create a high entropy encryption key we use the full 180 bits
|
70
|
+
# from the returned 180 bit BCrypt key produced by {KdfBCrypt} and
|
71
|
+
# the first 332 bits from the 384 bit key returned by PBKDF2.
|
72
|
+
#
|
73
|
+
# On amalgamation, the outcome is a quality <b>union key length</b>
|
74
|
+
# of <b>512 bits</b>.
|
75
|
+
AMALGAM_KEY_RAW_BIT_SIZE = BCRYPT_KEY_CONTRIBUTION_SIZE + PBKDF2_KEY_CONTRIBUTION_SIZE
|
76
|
+
|
77
|
+
|
78
|
+
# This method generates a 256 bit symmetric encryption key by passing a
|
79
|
+
# textual human sourced secret into two <b>key derivation functions</b>,
|
80
|
+
# namely <b>BCrypt and PBKDF2</b>. BCrypt, PBKDF2 and SCrypt are today's
|
81
|
+
# <b>in form best of breed</b> cryptographic workhorses for producing a
|
82
|
+
# high entropy key from possibly weak human sourced secret text.
|
83
|
+
#
|
84
|
+
# <b>Example | Derive Key from Password</b>
|
85
|
+
#
|
86
|
+
# key_store = KeyPair.new( "/path/to/kdf-salt-data.ini" )
|
87
|
+
# key_store.use( "peter-pan" )
|
88
|
+
# human_key = KdfApi.generate_from_password( "my_s3cr3t", key_store )
|
89
|
+
#
|
90
|
+
# strong_key = Key.from_random()
|
91
|
+
# human_key.encrypt_key( strong_key, key_store )
|
92
|
+
#
|
93
|
+
# strong_key.encrypt_file "/path/to/file-to-encrypt.pdf"
|
94
|
+
# strong_key.encrypt_text "I am the text to encrypt."
|
95
|
+
#
|
96
|
+
# ---
|
97
|
+
#
|
98
|
+
# <b>Do not use the key derived from a human secret</b> to encrypt anything
|
99
|
+
# other than a <b>high entropy key</b> randomly sourced from 48 bytes.
|
100
|
+
#
|
101
|
+
# Every time the user logs in, generate (recycle), another human key and
|
102
|
+
# another strong key and discard the previously outputted cipher texts.
|
103
|
+
#
|
104
|
+
# == BCrypt and the PBKDF2 Cryptographic Algorithms
|
105
|
+
#
|
106
|
+
# BCrypt (Blowfish) and PBKDF2 are the leading <b>key derivation functions</b>
|
107
|
+
# that exists to convert <b>low entropy</b> human generated passwords into a high
|
108
|
+
# entropy key that is computationally infeasible to acquire through brute force.
|
109
|
+
#
|
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
|
+
# On amalgamation, the outcome is a quality <b>union key length</b>
|
115
|
+
# of <b>512 bits</b>.
|
116
|
+
#
|
117
|
+
# == Creating a High Entropy Encryption Key
|
118
|
+
#
|
119
|
+
# To create a high entropy encryption key this method takes the first
|
120
|
+
# 168 bits from the 186 bit BCrypt key produced by {KdfBCrypt} and
|
121
|
+
# the first 96 bits from the 132 bit PBKDF2 key produced inside the
|
122
|
+
# {KeyPbkdf2} class and amalgamates them to produce a 264 bit key.
|
123
|
+
#
|
124
|
+
# Note that all four of the above numbers are divisable by six (6), for
|
125
|
+
# representation with a 64 character set, and eight (8), for transport
|
126
|
+
# via the byte (8 bit) protocols.
|
127
|
+
#
|
128
|
+
# <b>Size of BCrypt and PBKDF2 Derived Keys</b>
|
129
|
+
#
|
130
|
+
# + --------- - --------- +
|
131
|
+
# + --------- | --------- +
|
132
|
+
# | Algorithm | Bit Count |
|
133
|
+
# ----------- | --------- |
|
134
|
+
# | BCrypt | 180 Bits |
|
135
|
+
# | Pbkdf2 | 332 Bits |
|
136
|
+
# ----------- | --------- |
|
137
|
+
# | Total | 512 Bits |
|
138
|
+
# + --------- | --------- +
|
139
|
+
# + --------- - --------- +
|
140
|
+
#
|
141
|
+
# <b>256 Bit Encryption Key | Remove 8 Bits</b>
|
142
|
+
#
|
143
|
+
# The manufactured encryption key, an amalgam of the above now has
|
144
|
+
# 264 bits carried by 44 Base64 characters.
|
145
|
+
#
|
146
|
+
# Just before it is used to encrypt vital keys, eight (8) bits are
|
147
|
+
# removed from the end of the key. The key is then converted into a
|
148
|
+
# powerful 32 byte (256 bit) encryption agent and is hashed by the
|
149
|
+
# SHA256 digest and delivered.
|
150
|
+
#
|
151
|
+
# @param human_secret [String]
|
152
|
+
# a robust human generated password with as much entropy as can
|
153
|
+
# be mustered. Remember that 40 characters spread randomly over
|
154
|
+
# the key space of about 90 characters and not relating to any
|
155
|
+
# dictionary word or name is the way to generate a powerful key
|
156
|
+
# that has embedded a near 100% entropy rating.
|
157
|
+
#
|
158
|
+
# @param key_map [KeyPair]
|
159
|
+
# The KeyPair storage service must have been initialized and a
|
160
|
+
# section specified using {KeyPair.use} thus allowing this method
|
161
|
+
# to <b>write key-value pairs</b> representing the BCrypt and
|
162
|
+
# PBKDF2 salts through the {KeyPair.set} behaviour.
|
163
|
+
#
|
164
|
+
# @return [Key]
|
165
|
+
# the 256 bit symmetric encryption key derived from a human password
|
166
|
+
# and passed through two cryptographic workhorses.
|
167
|
+
def self.generate_from_password human_secret, key_map
|
168
|
+
|
169
|
+
bcrypt_salt = KdfBCrypt.generate_bcrypt_salt
|
170
|
+
pbkdf2_salt = KeyPbkdf2.generate_pbkdf2_salt
|
171
|
+
|
172
|
+
key_map.set( BCRYPT_SALT_KEY_NAME, bcrypt_salt )
|
173
|
+
key_map.set( PBKDF2_SALT_KEY_NAME, pbkdf2_salt )
|
174
|
+
|
175
|
+
return derive_and_amalgamate( human_secret, bcrypt_salt, pbkdf2_salt )
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
# Regenerate the viciously unretrievable nor reversable key that was
|
181
|
+
# generated in the past and with the same salts that were used during
|
182
|
+
# the original key derivation process.
|
183
|
+
#
|
184
|
+
# @param key_map [Hash]
|
185
|
+
# an instantiated and populated hash object containing the salts
|
186
|
+
# which were created in the past during the generation. These are
|
187
|
+
# now vital for a successful regeneration.
|
188
|
+
#
|
189
|
+
# @return [Key]
|
190
|
+
# the 256 bit symmetric encryption key that was previously generated
|
191
|
+
# from the secret and the cryptographic salts within the key_map.
|
192
|
+
def self.regenerate_from_salts human_secret, key_map
|
193
|
+
|
194
|
+
bcrypt_salt = key_map.get( BCRYPT_SALT_KEY_NAME )
|
195
|
+
pbkdf2_salt = key_map.get( PBKDF2_SALT_KEY_NAME )
|
196
|
+
|
197
|
+
return derive_and_amalgamate( human_secret, bcrypt_salt, pbkdf2_salt )
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
def self.derive_and_amalgamate( human_secret, bcrypt_salt, pbkdf2_salt )
|
208
|
+
|
209
|
+
bcrypt_key = OpenKey::KdfBCrypt.generate_key( human_secret, bcrypt_salt )
|
210
|
+
pbkdf2_key = OpenKey::KeyPbkdf2.generate_key( human_secret.reverse, pbkdf2_salt )
|
211
|
+
|
212
|
+
assert_bcrypt_key_bit_length bcrypt_key
|
213
|
+
assert_pbkdf2_key_bit_length pbkdf2_key
|
214
|
+
|
215
|
+
amalgam_key = Key.new ( bcrypt_key.to_s[ 0 .. (BCRYPT_KEY_CONTRIBUTION_SIZE-1) ] + pbkdf2_key.to_s[ 0 .. (PBKDF2_KEY_CONTRIBUTION_SIZE-1) ] )
|
216
|
+
|
217
|
+
assert_amalgam_key_bit_length amalgam_key
|
218
|
+
|
219
|
+
return amalgam_key
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
def self.assert_bcrypt_key_bit_length bcrypt_key
|
225
|
+
bcrypt_key_bit_length = bcrypt_key.to_s.bytesize
|
226
|
+
bcrypt_keysize_msg = "Expecting #{KdfBCrypt::BCRYPT_KEY_EXPORT_BIT_LENGTH} not #{bcrypt_key_bit_length} bits in bcrypt key."
|
227
|
+
raise RuntimeError, bcrypt_keysize_msg unless bcrypt_key_bit_length == KdfBCrypt::BCRYPT_KEY_EXPORT_BIT_LENGTH
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
def self.assert_pbkdf2_key_bit_length pbkdf2_key
|
232
|
+
pbkdf2_key_bit_length = pbkdf2_key.to_s.bytesize
|
233
|
+
pbkdf2_keysize_msg = "Expecting #{KeyPbkdf2::PBKDF2_EXPORT_BIT_LENGTH} not #{pbkdf2_key_bit_length} bits in pbkdf2 key."
|
234
|
+
raise RuntimeError, pbkdf2_keysize_msg unless pbkdf2_key_bit_length == KeyPbkdf2::PBKDF2_EXPORT_BIT_LENGTH
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
def self.assert_amalgam_key_bit_length amalgam_key
|
239
|
+
|
240
|
+
amalgam_key_bit_length = amalgam_key.to_s.bytesize
|
241
|
+
amalgam_keysize_msg = "Expecting #{AMALGAM_KEY_RAW_BIT_SIZE} not #{amalgam_key_bit_length} bits in amalgam key."
|
242
|
+
raise RuntimeError, amalgam_keysize_msg unless amalgam_key_bit_length == AMALGAM_KEY_RAW_BIT_SIZE
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
end
|
data/lib/keytools/kdf.bcrypt.rb
CHANGED
@@ -21,12 +21,10 @@ module OpenKey
|
|
21
21
|
#
|
22
22
|
# The <b>minimum cost</b> is 4 (16 iterations) and the maximum is 31.
|
23
23
|
#
|
24
|
-
# <b>A cost of 16 will result in 2^16 = 65,536 iterations</b
|
24
|
+
# <b>A cost of 16 will result in 2^16 = 65,536 iterations</b> and will slow the
|
25
|
+
# derivation time to about a second on a powerful 2020 laptop.
|
25
26
|
#
|
26
|
-
|
27
|
-
# to about a second on a powerful 2020 laptop.
|
28
|
-
#
|
29
|
-
class BCryptKeyGen
|
27
|
+
class KdfBCrypt
|
30
28
|
|
31
29
|
require "bcrypt"
|
32
30
|
|
@@ -50,20 +48,21 @@ module OpenKey
|
|
50
48
|
BCRYPT_KEY_LENGTH = 31
|
51
49
|
|
52
50
|
|
53
|
-
#
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
51
|
+
# BCrypt key derivation (from text) implementations truncate
|
52
|
+
# the first 55 characters of the incoming text.
|
53
|
+
BCRYPT_MAX_IN_TEXT_LENGTH = 55
|
54
|
+
|
55
|
+
|
56
|
+
# The BCrypt algorithm produces 181 raw binary bits which is just
|
57
|
+
# one bit more than a 30 character base64 string. Hence the algorithm
|
58
|
+
# puts out 31 characters.
|
61
59
|
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
|
60
|
+
# We discard the 31st character because 5 of its 6 bits are 100%
|
61
|
+
# predictable. Thus the returned key will contribute 180 bits.
|
62
|
+
BCRYPT_KEY_EXPORT_BIT_LENGTH = 180
|
63
|
+
|
65
64
|
|
66
|
-
# The
|
65
|
+
# The BCrypt algorithm salt string should be 22 characters
|
67
66
|
# and may include forward slashes and periods.
|
68
67
|
BCRYPT_SALT_LENGTH = 22
|
69
68
|
|
@@ -78,7 +77,7 @@ module OpenKey
|
|
78
77
|
# The two sections are the
|
79
78
|
# - BCrypt algorithm <b>version number</b> (2a or 2b) and
|
80
79
|
# - a power of 2 integer defining the no. of interations
|
81
|
-
BCRYPT_OUTPUT_TEXT_PREFIX = "$
|
80
|
+
BCRYPT_OUTPUT_TEXT_PREFIX = "$2x$#{BCRYPT_ITERATION_INTEGER}$"
|
82
81
|
|
83
82
|
|
84
83
|
# Key generators should use this method to create a BCrypt salt
|
@@ -101,15 +100,45 @@ module OpenKey
|
|
101
100
|
# This method removes the $2a$16$ preamble string and stores only
|
102
101
|
# the actual salt string whose length should be 22 characters.
|
103
102
|
#
|
103
|
+
# <b>Why do BCrypt salts always end with zero, e, u or period</b>?
|
104
|
+
#
|
105
|
+
# Two <b>(2) leftover bits</b> is the short answer.
|
106
|
+
#
|
107
|
+
# This is because the salts are a random 16 bytes and must be
|
108
|
+
# stored in base64. The 16 bytes equals 128bits which when converted
|
109
|
+
# to base64 (6bits per character) results in 21 characters and only
|
110
|
+
# two leftover bits.
|
111
|
+
#
|
112
|
+
# BCrypt Salt => t4bDqoJlHbb/k7bkt4/1Ku (22 characters)
|
113
|
+
# BCrypt Salt => 9BjuJU67IG9Lz5tYUhOqeO (22 characters)
|
114
|
+
# BCrypt Salt => grz.QREI35585Y3AaCoCTe (22 characters)
|
115
|
+
# BCrypt Salt => zsxrVW2RGIltSu.AoS4E7e (22 characters)
|
116
|
+
# BCrypt Salt => dTlRJZ6ijDDVk2cFoCQHPO (22 characters)
|
117
|
+
# BCrypt Salt => S9B1azH7oD8L3.CQfxxzJO (22 characters)
|
118
|
+
# BCrypt Salt => LoZh.q3NdnTIuOmR6gHJF. (22 characters)
|
119
|
+
# BCrypt Salt => y6DKk23SmgNR863pTZ8nYe (22 characters)
|
120
|
+
# BCrypt Salt => rokdUF6tg6wHV6F0ymKFme (22 characters)
|
121
|
+
# BCrypt Salt => jrDpNgh.0OEIYaxsR7E7d. (22 characters)
|
122
|
+
#
|
123
|
+
# Don't forget BCrypt uses Radix64 (from OpenBSD). So the two (2)
|
124
|
+
# leftover bits result in 4 possible values which effectively is
|
125
|
+
#
|
126
|
+
# a period (.)
|
127
|
+
# a zero (0)
|
128
|
+
# an e (e)
|
129
|
+
# or a u (u)
|
130
|
+
#
|
104
131
|
# @return [String]
|
105
132
|
# the salt in a printable format like base64, hex or a string
|
106
133
|
# of ones and zeroes. This salt should be submitted in the exact
|
107
134
|
# same form to the {generate_key} method.
|
108
|
-
def self.
|
135
|
+
def self.generate_bcrypt_salt
|
136
|
+
|
109
137
|
the_salt_str = BCrypt::Engine.generate_salt( BCRYPT_ITERATION_INTEGER )
|
110
138
|
bcrypt_salt = the_salt_str[ BCRYPT_OUTPUT_TEXT_PREFIX.length .. -1 ]
|
111
139
|
assert_bcrypt_salt bcrypt_salt
|
112
140
|
return bcrypt_salt
|
141
|
+
|
113
142
|
end
|
114
143
|
|
115
144
|
|
@@ -141,19 +170,30 @@ module OpenKey
|
|
141
170
|
# resubmitted here (in the future) to regenerate the same key.
|
142
171
|
#
|
143
172
|
# @return [Key]
|
144
|
-
#
|
145
|
-
#
|
173
|
+
# an {OpenKey::Key} that has been initialized from the 30 RADIX64
|
174
|
+
# character output from the BCrypt algorithm.
|
175
|
+
#
|
176
|
+
# The BCrypt algorithm produces 181 raw binary bits which is just
|
177
|
+
# one bit more than a 30 character base64 string. Hence the algorithm
|
178
|
+
# puts out 31 characters.
|
179
|
+
#
|
180
|
+
# We discard the 31st character because 5 of its 6 bits are 100%
|
181
|
+
# predictable. Thus the returned key will contribute 180 bits.
|
146
182
|
def self.generate_key human_secret, bcrypt_salt
|
147
183
|
|
148
|
-
|
184
|
+
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 )
|
149
187
|
encoded64_key = BCrypt::Password.new( hashed_secret ).to_s
|
150
188
|
|
151
189
|
key_begin_index = BCRYPT_OUTPUT_TEXT_PREFIX.length + BCRYPT_SALT_LENGTH
|
152
190
|
radix64_key_str = encoded64_key[ key_begin_index .. -1 ]
|
153
|
-
key_length_mesg = "The
|
191
|
+
key_length_mesg = "The BCrypt key length should have #{BCRYPT_KEY_LENGTH} characters."
|
154
192
|
raise RuntimeError, key_length_mesg unless radix64_key_str.length == BCRYPT_KEY_LENGTH
|
155
193
|
|
156
|
-
|
194
|
+
chopped_radix64_key = radix64_key_str.chop()
|
195
|
+
|
196
|
+
return Key.from_radix64( chopped_radix64_key )
|
157
197
|
|
158
198
|
end
|
159
199
|
|
@@ -162,11 +202,6 @@ module OpenKey
|
|
162
202
|
private
|
163
203
|
|
164
204
|
|
165
|
-
|
166
|
-
def self.to_bcrypt_salt the_salt
|
167
|
-
return BCRYPT_OUTPUT_TEXT_PREFIX + the_salt
|
168
|
-
end
|
169
|
-
|
170
205
|
def self.assert_bcrypt_salt the_salt
|
171
206
|
raise RuntimeError, "bcrypt salt not expected to be nil." if the_salt.nil?
|
172
207
|
salt_length_msg = "A bcrypt salt is expected to contain #{BCRYPT_SALT_LENGTH} characters."
|