opensecret 0.0.988 → 0.0.9925
Sign up to get free protection for your applications and to get access to all the features.
- 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."
|