opensecret 0.0.962 → 0.0.988
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -10
- data/bin/opensecret +3 -4
- data/bin/ops +5 -0
- data/lib/extension/string.rb +114 -0
- data/lib/factbase/facts.opensecret.io.ini +9 -21
- data/lib/interprete/begin.rb +232 -0
- data/lib/interprete/cmd.rb +621 -0
- data/lib/{plugins/usecases/unlock.rb → interprete/export.rb} +25 -70
- data/lib/interprete/init.rb +205 -0
- data/lib/interprete/key.rb +119 -0
- data/lib/interprete/open.rb +148 -0
- data/lib/{plugins/usecases → interprete}/put.rb +19 -6
- data/lib/{plugins/usecases → interprete}/safe.rb +2 -1
- data/lib/{plugins/usecases/lock.rb → interprete/seal.rb} +24 -34
- data/lib/interprete/set.rb +46 -0
- data/lib/interprete/use.rb +43 -0
- data/lib/interpreter.rb +165 -0
- data/lib/keytools/binary.map.rb +245 -0
- data/lib/keytools/digester.rb +245 -0
- data/lib/keytools/doc.conversion.to.ones.and.zeroes.ruby +179 -0
- data/lib/keytools/doc.rsa.radix.binary-mapping.ruby +190 -0
- data/lib/keytools/doc.star.schema.strategy.txt +77 -0
- data/lib/keytools/doc.using.pbkdf2.kdf.ruby +95 -0
- data/lib/keytools/doc.using.pbkdf2.pkcs.ruby +266 -0
- data/lib/keytools/kdf.bcrypt.rb +180 -0
- data/lib/keytools/kdf.pbkdf2.rb +164 -0
- data/lib/keytools/key.data.rb +227 -0
- data/lib/keytools/key.derivation.rb +341 -0
- data/lib/keytools/key.module.rb +140 -0
- data/lib/keytools/key.rb +481 -0
- data/lib/logging/gem.logging.rb +1 -2
- data/lib/modules/cryptology.md +43 -0
- data/lib/{plugins/ciphers → modules/cryptology}/aes-256.rb +6 -0
- data/lib/{crypto → modules/cryptology}/amalgam.rb +6 -0
- data/lib/modules/cryptology/blowfish.rb +130 -0
- data/lib/modules/cryptology/cipher.rb +207 -0
- data/lib/modules/cryptology/collect.rb +118 -0
- data/lib/{plugins → modules/cryptology}/crypt.io.rb +5 -0
- data/lib/{crypto → modules/cryptology}/engineer.rb +7 -1
- data/lib/{crypto → modules/cryptology}/open.bcrypt.rb +0 -0
- data/lib/modules/mappers/collateral.rb +282 -0
- data/lib/modules/mappers/dictionary.rb +288 -0
- data/lib/modules/mappers/envelope.rb +127 -0
- data/lib/modules/mappers/settings.rb +170 -0
- data/lib/modules/storage/coldstore.rb +186 -0
- data/lib/{opensecret/plugins.io/git/git.flow.rb → modules/storage/git.store.rb} +11 -0
- data/lib/notepad/scratch.pad.rb +17 -0
- data/lib/session/fact.finder.rb +13 -0
- data/lib/session/require.gem.rb +5 -0
- data/lib/store-commands.txt +180 -0
- data/lib/version.rb +1 -1
- data/opensecret.gemspec +5 -6
- metadata +74 -29
- data/lib/crypto/blowfish.rb +0 -85
- data/lib/crypto/collect.rb +0 -140
- data/lib/crypto/verify.rb +0 -33
- data/lib/opensecret.rb +0 -236
- data/lib/plugins/cipher.rb +0 -203
- data/lib/plugins/ciphers/blowfish.rb +0 -126
- data/lib/plugins/coldstore.rb +0 -181
- data/lib/plugins/envelope.rb +0 -116
- data/lib/plugins/secrets.uc.rb +0 -94
- data/lib/plugins/usecase.rb +0 -239
- data/lib/plugins/usecases/init.rb +0 -145
- data/lib/plugins/usecases/open.rb +0 -108
- data/lib/session/attributes.rb +0 -279
- data/lib/session/dictionary.rb +0 -191
- data/lib/session/file.path.rb +0 -53
- data/lib/session/session.rb +0 -80
@@ -0,0 +1,245 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
module OpenKey
|
4
|
+
|
5
|
+
class BinaryMap
|
6
|
+
|
7
|
+
|
8
|
+
require 'openssl'
|
9
|
+
require "base64"
|
10
|
+
require "bcrypt"
|
11
|
+
|
12
|
+
|
13
|
+
CHARACTERS_64 = [
|
14
|
+
"a", "9", "W", "B", "f", "K", "O", "z",
|
15
|
+
"3", "s", "1", "5", "c", "n", "E", "J",
|
16
|
+
"L", "A", "l", "6", "I", "w", "o", "g",
|
17
|
+
"k", "N", "t", "Y", "S", "/", "T", "b",
|
18
|
+
"V", "R", "H", "0", "@", "Z", "8", "F",
|
19
|
+
"G", "j", "u", "m", "M", "h", "4", "p",
|
20
|
+
"q", "d", "7", "v", "e", "2", "U", "X",
|
21
|
+
"r", "C", "y", "Q", "D", "x", "P", "i"
|
22
|
+
]
|
23
|
+
|
24
|
+
def self.tmp_holder
|
25
|
+
|
26
|
+
|
27
|
+
bcrypt_packed_key = [ bcrypt_key.to_s ].pack("B*")
|
28
|
+
pbkdf2_packed_key = [ pbkdf2_key.to_s ].pack("B*")
|
29
|
+
|
30
|
+
bcrypt_un_pack_ed = bcrypt_packed_key.unpack("B*")[0]
|
31
|
+
pbkdf2_un_pack_ed = pbkdf2_packed_key.unpack("B*")[0]
|
32
|
+
|
33
|
+
bcrypt_they_match = bcrypt_key.to_s.eql? bcrypt_un_pack_ed
|
34
|
+
pbkdf2_they_match = pbkdf2_key.to_s.eql? pbkdf2_un_pack_ed
|
35
|
+
|
36
|
+
puts ""
|
37
|
+
puts "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
38
|
+
puts "The actual binary thing => [ #{bcrypt_key.to_s} ]"
|
39
|
+
puts "Theactual unpacked ting => [ #{bcrypt_un_pack_ed} ]"
|
40
|
+
puts "Lengthof packed version => [ #{bcrypt_packed_key.length} ]"
|
41
|
+
puts "Byte size Length of Pck => [ #{bcrypt_packed_key.bytesize} ]"
|
42
|
+
puts "The packed keys version => [ #{bcrypt_packed_key} ]"
|
43
|
+
puts ""
|
44
|
+
puts "Length unpacked version => [ #{bcrypt_un_pack_ed.length} ]"
|
45
|
+
puts "Bytesize of Unpacked Vn => [ #{bcrypt_un_pack_ed.bytesize} ]"
|
46
|
+
puts ""
|
47
|
+
puts "Is full circle correct? => [ #{bcrypt_they_match} ]"
|
48
|
+
puts ""
|
49
|
+
puts bcrypt_key.to_s
|
50
|
+
puts bcrypt_un_pack_ed
|
51
|
+
puts ""
|
52
|
+
puts "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
53
|
+
puts ""
|
54
|
+
puts "The actual binary thing => [ #{pbkdf2_key.to_s} ]"
|
55
|
+
puts "Theactual unpacked ting => [ #{pbkdf2_un_pack_ed} ]"
|
56
|
+
puts "Lengthof packed version => [ #{pbkdf2_packed_key.length} ]"
|
57
|
+
puts "Byte size Length of Pck => [ #{pbkdf2_packed_key.bytesize} ]"
|
58
|
+
puts "The packed keys version => [ #{pbkdf2_packed_key} ]"
|
59
|
+
puts ""
|
60
|
+
puts "Length unpacked version => [ #{pbkdf2_un_pack_ed.length} ]"
|
61
|
+
puts "Bytesize of Unpacked Vn => [ #{pbkdf2_un_pack_ed.bytesize} ]"
|
62
|
+
puts ""
|
63
|
+
puts "Is full circle correct? => [ #{pbkdf2_they_match} ]"
|
64
|
+
puts ""
|
65
|
+
puts pbkdf2_key.to_s
|
66
|
+
puts pbkdf2_un_pack_ed
|
67
|
+
puts ""
|
68
|
+
|
69
|
+
char_index = 1
|
70
|
+
zero_count = 0
|
71
|
+
one_count = 0
|
72
|
+
|
73
|
+
pbkdf2_key.to_s.each_char do |string_bit|
|
74
|
+
puts "Bit number [ #{char_index} ] is a [ #{string_bit} ]" if char_index % 4 == 0
|
75
|
+
char_index += 1
|
76
|
+
zero_count += 1 if string_bit.eql? "0"
|
77
|
+
one_count += 1 if string_bit.eql? "1"
|
78
|
+
end
|
79
|
+
|
80
|
+
puts "There are [ #{zero_count} ] zeroes."
|
81
|
+
puts "There are [ #{one_count} ] ones."
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
def self.bcrypter
|
91
|
+
|
92
|
+
generated_salt = BCrypt::Engine.generate_salt( 12 )
|
93
|
+
|
94
|
+
|
95
|
+
my_password = BCrypt::Password.new( BCrypt::Engine.hash_secret( "my password text", generated_salt ) )
|
96
|
+
|
97
|
+
|
98
|
+
## my_password = BCrypt::Password.create( "my password", :cost => 12 )
|
99
|
+
|
100
|
+
salt_begin_index = my_password.to_s.rindex( "$" ) + 1
|
101
|
+
salt_hash_length = my_password.to_s.length - salt_begin_index
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
#=> "$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa"
|
106
|
+
|
107
|
+
# Salt is 22 characters
|
108
|
+
# Key is 31 characters
|
109
|
+
# total is 53 characters
|
110
|
+
|
111
|
+
good_password = my_password == "my password text"
|
112
|
+
bad_password = my_password == "my password text"
|
113
|
+
|
114
|
+
puts ""
|
115
|
+
puts "Bcrypt password is #{my_password}"
|
116
|
+
puts "Bcrypt COST is => #{BCrypt::Engine.cost}"
|
117
|
+
puts "Length of salt plus hash is #{salt_hash_length}"
|
118
|
+
puts "Salt begin index is #{salt_begin_index}"
|
119
|
+
puts "Generated Salt is #{generated_salt}"
|
120
|
+
puts "The Good Password? #{good_password}"
|
121
|
+
puts "The BadBoy Password? #{bad_password}"
|
122
|
+
puts ""
|
123
|
+
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
## Bcrypt password is $2a$12$nkyYKCwljFRtcif6FCXn3ey29P8mGMeY6YGNEaoBYmkScgyYrbusC
|
129
|
+
## Bcrypt COST is => 10
|
130
|
+
## Length of salt plus hash is 53
|
131
|
+
## Salt begin index is 7
|
132
|
+
## Generated Salt is $2a$12$nkyYKCwljFRtcif6FCXn3e
|
133
|
+
## The Good Password? true
|
134
|
+
## The BadBoy Password? false
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
def self.salter
|
139
|
+
|
140
|
+
original_pass = "abcdefghijklmnopqrstuvwxyz@0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+"
|
141
|
+
|
142
|
+
for n in 0 .. 63
|
143
|
+
the_pass = original_pass + original_pass[0..n]
|
144
|
+
puts "==========================================="
|
145
|
+
puts "Pass #{n} => #{the_pass}"
|
146
|
+
puts "Password LENGTH => #{the_pass.length}"
|
147
|
+
puts "==========================================="
|
148
|
+
password_cruncher the_pass
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.password_cruncher the_password
|
154
|
+
|
155
|
+
|
156
|
+
raise RuntimeError, "Only 72 characters change the resultant key. Strings with 73, 74, 75 (etc) characters produce the same key as the truncated 72 character input." if the_password.length > 72
|
157
|
+
|
158
|
+
preamble = "$2a$12$"
|
159
|
+
salt_chars = "nkyYKCwljFRtcif6FCXn3e"
|
160
|
+
|
161
|
+
cost_length = "12".length
|
162
|
+
expected_pre_len = 5 + cost_length
|
163
|
+
non_key_length = expected_pre_len + 22
|
164
|
+
raise RuntimeError, "Preamble length not expected" unless preamble.length == expected_pre_len
|
165
|
+
raise RuntimeError, "Salt length not expected" unless salt_chars.length == 22
|
166
|
+
|
167
|
+
generated_salt = preamble + salt_chars
|
168
|
+
|
169
|
+
my_password = BCrypt::Password.new( BCrypt::Engine.hash_secret( the_password, generated_salt ) ).to_s
|
170
|
+
|
171
|
+
salt_begin_index = my_password.rindex( "$" ) + 1
|
172
|
+
salt_hash_length = my_password.length - salt_begin_index
|
173
|
+
|
174
|
+
actual_key = my_password.gsub( generated_salt, "" )
|
175
|
+
raise RuntimeError, "Key length not expected" unless actual_key.length == 31
|
176
|
+
|
177
|
+
also_key = my_password[ non_key_length .. -1 ]
|
178
|
+
raise RuntimeError, "Key strings not matching" unless actual_key.eql? also_key
|
179
|
+
|
180
|
+
|
181
|
+
puts "Bcrypt password is #{my_password}"
|
182
|
+
puts "Bcrypt COST is => #{BCrypt::Engine.cost}"
|
183
|
+
puts "Length of salt plus hash is #{salt_hash_length}"
|
184
|
+
puts "Salt begin index is #{salt_begin_index}"
|
185
|
+
puts "Generated Salt is #{generated_salt}"
|
186
|
+
puts "Actual Key 1 => #{actual_key}"
|
187
|
+
puts "Actual Key 2 => #{also_key}"
|
188
|
+
puts "KEY Length is => [ #{also_key.length} ]"
|
189
|
+
puts ""
|
190
|
+
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
|
196
|
+
def self.crypter
|
197
|
+
|
198
|
+
cipher = OpenSSL::Cipher.new 'AES-128-CBC'
|
199
|
+
cipher.encrypt
|
200
|
+
iv = cipher.random_iv
|
201
|
+
|
202
|
+
pwd = 'some hopefully not to easily guessable password'
|
203
|
+
salt = OpenSSL::Random.random_bytes 16
|
204
|
+
iter = 20000
|
205
|
+
key_len = cipher.key_len
|
206
|
+
digest = OpenSSL::Digest::SHA512.new
|
207
|
+
|
208
|
+
key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, 1000000, 16, digest)
|
209
|
+
|
210
|
+
puts ""
|
211
|
+
puts "The generated salt has a length of [#{salt.length}]"
|
212
|
+
puts "The salt generated is => #{salt}"
|
213
|
+
puts "The generated key has a length of [#{key.length}]"
|
214
|
+
puts "The Key generated is => #{key}"
|
215
|
+
puts "The Key in Base64 is => #{Base64.encode64(key)}"
|
216
|
+
puts ""
|
217
|
+
|
218
|
+
cipher.key = key
|
219
|
+
|
220
|
+
##### Now encrypt the data:
|
221
|
+
|
222
|
+
### encrypted = cipher.update document
|
223
|
+
### encrypted << cipher.final
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
def self.binary
|
229
|
+
|
230
|
+
for n in 0 .. 128
|
231
|
+
six_bit_binary = "%06d" % [ n.to_s(2) ]
|
232
|
+
puts "#{n} in binary is => #{six_bit_binary}"
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
## BinaryMap.binary
|
238
|
+
## BinaryMap.crypter
|
239
|
+
## BinaryMap.bcrypter
|
240
|
+
## BinaryMap.salter
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
module OpenKey
|
4
|
+
|
5
|
+
# If you have a possibly weak human password that you need to either store
|
6
|
+
# or derive a symmetric encryption key from - you use this digester.
|
7
|
+
#
|
8
|
+
# You can safely store the resulting key confident that no known method exists
|
9
|
+
# of deriving the original password from the key. The digester uses the
|
10
|
+
# most recent SHA512 bit hashing algorithm - twice - to mince the key into
|
11
|
+
# something worthless to everyone, but the password originator.
|
12
|
+
#
|
13
|
+
# This key digester knows how to apply one-way functions that dessimate a
|
14
|
+
# (possibly weak) human generated (password) string.
|
15
|
+
#
|
16
|
+
# It should be initialized with a nonce like the count of nanoseconds
|
17
|
+
# since the current second (for low throughput systems).
|
18
|
+
#
|
19
|
+
# ==How to Use the Digester
|
20
|
+
#
|
21
|
+
# <b><em>Generating the Hashed Key</em></b>
|
22
|
+
#
|
23
|
+
# To use the digester you instantiate it with a nonce and then call
|
24
|
+
# the generate method with a possibly weak human key that is required
|
25
|
+
# to contain a minimum of 6 characters.
|
26
|
+
#
|
27
|
+
# The 6 characters excludes any leading or trailing whitespace.
|
28
|
+
# keydigester = Digester.new "nonce"
|
29
|
+
# the_key = keydigester.generate "human_k3y"
|
30
|
+
# output_iv = keydigester.encrypted_iv
|
31
|
+
#
|
32
|
+
# You then read off the encrypted iv (initialization vector).
|
33
|
+
#
|
34
|
+
# <b><em>Re-generating the Hashed Key</em></b>
|
35
|
+
#
|
36
|
+
# To retrieve the same key (perhaps many moons later), you instantiate
|
37
|
+
# another Digester providing the same nonce you gave to the generate
|
38
|
+
# method. You then supply the encrypted iv that you pulled out during
|
39
|
+
# the generate step.
|
40
|
+
# keydigester = Digester.new "nonce"
|
41
|
+
# keydigester.encrypted_iv = "axbxcxdx1x2x3x4x"
|
42
|
+
# the_key = keydigester.regenerate "human_k3y"
|
43
|
+
#
|
44
|
+
# Now when you regenerate the key - you have the original.
|
45
|
+
#
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# keydigester = Digester.new "nonce"
|
49
|
+
# the_key = keydigester.generate "human_k3y"
|
50
|
+
# output_iv = keydigester.encrypted_iv
|
51
|
+
#
|
52
|
+
# keydigester = Digester.new "nonce"
|
53
|
+
# keydigester.encrypted_iv = "axbxcxdx1x2x3x4x"
|
54
|
+
# the_key = keydigester.regenerate "human_k3y"
|
55
|
+
class Digester
|
56
|
+
|
57
|
+
attr_accessor :encrypted_iv
|
58
|
+
|
59
|
+
# The golden ratio is the relationship between the human password and the
|
60
|
+
# strengthening amalgam. Two is perhaps too small, three or four is perfect
|
61
|
+
# to strengthen human generated keys whose median tends to gravitate at
|
62
|
+
# around 12 characters.
|
63
|
+
AMALGAM_GOLDEN_RATIO = 3
|
64
|
+
|
65
|
+
# The inner nonce provides a salt of sorts to help with randomizing the
|
66
|
+
# keys and protecting them from pre-configured rainbow tables.
|
67
|
+
INNER_NONCE = "9]w/t$=ON@mUf(+_SoY"
|
68
|
+
|
69
|
+
# The minimum length tolerated for the assumed human provided password.
|
70
|
+
# This number excludes any leading or trailing whitespace which should
|
71
|
+
# be removed before length examination is performed.
|
72
|
+
MIN_HUMAN_KEY_LENGTH = 6
|
73
|
+
|
74
|
+
|
75
|
+
# Initialize this key digester with a nonce like the count of nanoseconds
|
76
|
+
# since the current second (for low throughput systems).
|
77
|
+
#
|
78
|
+
# Note that the same nonce must be provided in order to produce the same
|
79
|
+
# key from calling either {Digester.generate} or {Digester.regenerate}.
|
80
|
+
#
|
81
|
+
# @param the_nonce [String]
|
82
|
+
# the nonce can be the count of nanoseconds since the current second
|
83
|
+
# (for low throughput systems).
|
84
|
+
#
|
85
|
+
# @raise [ArgumentError]
|
86
|
+
# raise an error if the nonce is empty, or consists only of whitespace
|
87
|
+
# or is too short.
|
88
|
+
def initialize the_nonce
|
89
|
+
|
90
|
+
@nonce = the_nonce.strip.reverse
|
91
|
+
|
92
|
+
raise ArgumentError, "The nonce contains only whitespace." if @nonce.empty?
|
93
|
+
raise ArgumentError, "The nonce is too short." if @nonce.length < MIN_HUMAN_KEY_LENGTH
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Generate a viciously unretrievable nor reversable string from a possible
|
99
|
+
# weak key provided by a human being.
|
100
|
+
#
|
101
|
+
# Due to the fickle nature of human beings we use an amalgam key to frustrate
|
102
|
+
# attempts to derive the original through rainbow tables and replay attacks.
|
103
|
+
#
|
104
|
+
# <b>Getting the Initialization Vector</b>
|
105
|
+
#
|
106
|
+
# After calling this method the encrypted initialization vector generated
|
107
|
+
# must be stored and re-provided to the {Digester.regenerate} method as
|
108
|
+
# and when required.
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# keydigester = Digester.new "nonce"
|
112
|
+
# the_key = keydigester.generate "human_k3y"
|
113
|
+
# output_iv = keydigester.encrypted_iv
|
114
|
+
#
|
115
|
+
# @param human_p4ss [String]
|
116
|
+
#
|
117
|
+
# the key provided by a human being which cannot be less than six
|
118
|
+
# characters in length. Between 24 and 32 apparently random characters
|
119
|
+
# involving upper case letters, digits and punctuators.
|
120
|
+
#
|
121
|
+
# Leading and trailing whitespace is removed from the string if found.
|
122
|
+
#
|
123
|
+
# @raise [ArgumentError] if the (possibly) human sourced password is too short
|
124
|
+
def generate human_p4ss
|
125
|
+
|
126
|
+
human_key = human_p4ss.strip.reverse
|
127
|
+
raise ArgumentError, "The password contains only whitespace." if human_key.empty?
|
128
|
+
raise ArgumentError, "The password is too short." if human_key.length < MIN_HUMAN_KEY_LENGTH
|
129
|
+
|
130
|
+
machine_key = OpenSecret::ToolBelt::Engineer.machine_key human_key.length, AMALGAM_GOLDEN_RATIO
|
131
|
+
snippet = human_key[ human_key.length - MIN_HUMAN_KEY_LENGTH .. -1 ]
|
132
|
+
|
133
|
+
iv_key = [ INNER_NONCE, snippet, @nonce ].join
|
134
|
+
@encrypted_iv = machine_key.encrypt_url_encode( iv_key )
|
135
|
+
|
136
|
+
return hashed_outcome human_key, machine_key
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# Regenerate the viciously unretrievable nor reversable string that has been
|
142
|
+
# generated from a possible weak key provided by a human being.
|
143
|
+
#
|
144
|
+
# The difference between this and the {Digester.generate} method is that
|
145
|
+
# the encrypted initialization vector is provided here. This must first be
|
146
|
+
# decrypted before being fed back into the numerical mincer.
|
147
|
+
#
|
148
|
+
# The same nonce must also be provided in the constructor.
|
149
|
+
#
|
150
|
+
# <b>Setting the Initialization Vector</b>
|
151
|
+
#
|
152
|
+
# Before calling this method the encrypted initialization vector generated
|
153
|
+
# by the generate method needs to be faithfully returned.
|
154
|
+
#
|
155
|
+
# @example
|
156
|
+
#
|
157
|
+
# keydigester = Digester.new "nonce"
|
158
|
+
# keydigester.encrypted_iv = "axbxcxdx1x2x3x4x"
|
159
|
+
# the_key = keydigester.regenerate "human_k3y"
|
160
|
+
#
|
161
|
+
# @param human_p4ss [String]
|
162
|
+
#
|
163
|
+
# the key provided by a human being which cannot be less than six
|
164
|
+
# characters in length. Between 24 and 32 apparently random characters
|
165
|
+
# involving upper case letters, digits and punctuators.
|
166
|
+
#
|
167
|
+
# Leading and trailing whitespace is removed from the string if found.
|
168
|
+
#
|
169
|
+
# @raise [ArgumentError]
|
170
|
+
# If the human sourced password is too short.
|
171
|
+
# Or if it consists solely of whitespace.
|
172
|
+
def regenerate human_p4ss
|
173
|
+
|
174
|
+
human_key = human_p4ss.strip.reverse
|
175
|
+
raise ArgumentError, "The password contains only whitespace." if human_key.empty?
|
176
|
+
raise ArgumentError, "The password is too short." if human_key.length < MIN_HUMAN_KEY_LENGTH
|
177
|
+
|
178
|
+
snippet = human_key[ human_key.length - MIN_HUMAN_KEY_LENGTH .. -1 ]
|
179
|
+
iv_key = [ INNER_NONCE, snippet, @nonce ].join
|
180
|
+
machine_key = @encrypted_iv.url_decode_decrypt( iv_key )
|
181
|
+
|
182
|
+
return hashed_outcome human_key, machine_key
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
# This digester alphanumeric hasher employs the SHA512 digest algorithm
|
188
|
+
# to gnash the parameter key and then Base64 encode the result.
|
189
|
+
#
|
190
|
+
# This method removes any non alpha-numeric characeters after a url safe
|
191
|
+
# base 64 encoding and thus guarantees that the resultant string will
|
192
|
+
# not contain
|
193
|
+
#
|
194
|
+
# - any equals signs
|
195
|
+
# - any hyphens
|
196
|
+
# - any underscores
|
197
|
+
#
|
198
|
+
# It is safe to use this method when a result must be stored in environment
|
199
|
+
# variables or other forms (like INI) where an equals sign is frowned upon.
|
200
|
+
#
|
201
|
+
# That said, this method should not be used if one wants to return to the
|
202
|
+
# pre base64 encoded character sequence. By mashing away the non-alphanums
|
203
|
+
# it mashes away any realistic hope of a determinant return to the raw
|
204
|
+
# hashed value.
|
205
|
+
#
|
206
|
+
# @param hash_me [String]
|
207
|
+
# use a one-way function to mush me into a form that is repeatable but
|
208
|
+
# non-returnable to the original input.
|
209
|
+
#
|
210
|
+
# @return [String]
|
211
|
+
# The SHA512 mashed version of the parameter key which is Base64 encoded
|
212
|
+
# (using the url safe variant) and the result stripped of any non alpha
|
213
|
+
# numeric characters.
|
214
|
+
#
|
215
|
+
def self.alphanum_hash hash_me
|
216
|
+
return self.alphanum_encoder( OpenSSL::Digest::SHA512.digest( hash_me ) )
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
def self.mash hash_me, max_length
|
221
|
+
raise ArgumentError, "Max length #{max_length} is unacceptable." if max_length < 32
|
222
|
+
return self.alphanum_encoder( OpenSSL::Digest::SHA512.digest( hash_me ) )[0 .. (max_length-1)]
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
|
229
|
+
def self.alphanum_encoder encode_this
|
230
|
+
return Base64.urlsafe_encode64( encode_this ).delete("=\\-_")
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
def hashed_outcome the_human_key, the_machine_key
|
235
|
+
amalgam_key = OpenSecret::ToolBelt::Amalgam.passwords the_human_key, the_machine_key, AMALGAM_GOLDEN_RATIO
|
236
|
+
level1_hash = Digester.alphanum_encoder( OpenSSL::Digest::SHA512.digest( amalgam_key ) )
|
237
|
+
level2_hash = Digester.alphanum_encoder( OpenSSL::Digest::SHA512.digest( @nonce + level1_hash ) )
|
238
|
+
return level2_hash
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
end
|