forgiva 1.0.pre.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.md +180 -0
- data/README.md +242 -0
- data/bin/forgiva +70 -0
- data/forgiva.gemspec +21 -0
- data/lib/constants.rb +53 -0
- data/lib/forgiva.rb +287 -0
- data/lib/forgiva_commands.rb +191 -0
- data/lib/forgiva_test.rb +66 -0
- data/lib/testvectors.rb +234 -0
- metadata +89 -0
data/lib/forgiva.rb
ADDED
@@ -0,0 +1,287 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require 'openssl'
|
3
|
+
require 'highline/import'
|
4
|
+
require 'constants'
|
5
|
+
|
6
|
+
# Password generation from 4 inputs
|
7
|
+
class Forgiva
|
8
|
+
attr_accessor :hostname, :account, :renewal_date, :master_password, :complexity, :length
|
9
|
+
|
10
|
+
def initialize(hostname, account, renewal_date, master_password, complexity, length)
|
11
|
+
@hostname = hostname
|
12
|
+
@account = account
|
13
|
+
@renewal_date = renewal_date
|
14
|
+
@master_password = master_password
|
15
|
+
@complexity = complexity
|
16
|
+
@length = length
|
17
|
+
end
|
18
|
+
|
19
|
+
def passwords
|
20
|
+
@passwords ||= generate
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
def generate
|
26
|
+
passwords = {}
|
27
|
+
|
28
|
+
# Getting input data as encrypted as salt
|
29
|
+
salt = encrypted_inputs
|
30
|
+
|
31
|
+
puts "SALT: #{salt.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
32
|
+
|
33
|
+
# Getting master password as already hashed SHA512
|
34
|
+
key = master_password
|
35
|
+
|
36
|
+
puts "CLEAR KEY: #{key.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
37
|
+
|
38
|
+
# If we have complexity options, then we overrun key
|
39
|
+
# with the pbkdf2 hmac sha256 or sha512 algorithms
|
40
|
+
if (@complexity == 2 || @complexity == 3) then
|
41
|
+
key = Forgiva.pbkdf2_hmac_sha(key,salt,@complexity)
|
42
|
+
end
|
43
|
+
|
44
|
+
puts "ENC KEY: #{key.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
45
|
+
|
46
|
+
|
47
|
+
Constants::ANIMALS.each do |a|
|
48
|
+
# For every other animal we re-run pbkdf2 hmac with sha1 over key
|
49
|
+
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(key, salt, 10_000, 32)
|
50
|
+
|
51
|
+
puts "GEN_KEY: #{key.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
52
|
+
|
53
|
+
passwords[a] = Forgiva.hash_to_password(key,@complexity, @length)
|
54
|
+
end
|
55
|
+
|
56
|
+
passwords
|
57
|
+
end
|
58
|
+
|
59
|
+
def hashed_hostname
|
60
|
+
Forgiva.hash_twice(hostname)
|
61
|
+
end
|
62
|
+
|
63
|
+
def hashed_account
|
64
|
+
Forgiva.hash_twice(account)
|
65
|
+
end
|
66
|
+
|
67
|
+
def hashed_renewal_date
|
68
|
+
Forgiva.hash_twice(renewal_date)
|
69
|
+
end
|
70
|
+
|
71
|
+
def hashed_master_password
|
72
|
+
Forgiva.hash_twice(master_password)
|
73
|
+
end
|
74
|
+
|
75
|
+
def encrypted_inputs
|
76
|
+
|
77
|
+
|
78
|
+
puts "hashed_hostname: #{hashed_hostname.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
79
|
+
puts "hashed_account: #{hashed_account.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
80
|
+
|
81
|
+
# Encrypt iteratively hostname and account and master_password
|
82
|
+
encrypt01 = Forgiva.iterative_encrypt(Forgiva.iterative_encrypt(hashed_hostname, hashed_account),
|
83
|
+
hashed_master_password)
|
84
|
+
|
85
|
+
puts "encrypt01: #{encrypt01.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
86
|
+
|
87
|
+
puts "hashed_renewal_date: #{hashed_renewal_date.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
88
|
+
puts "hashed_master_password: #{hashed_master_password.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
89
|
+
|
90
|
+
# Encrypt iteratively renewal date and master key
|
91
|
+
encrypt02 = Forgiva.iterative_encrypt(hashed_renewal_date, hashed_master_password)
|
92
|
+
|
93
|
+
puts "encrypt02: #{encrypt02.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
94
|
+
|
95
|
+
# Encrypt iteratively prior generated values
|
96
|
+
ret = Forgiva.iterative_encrypt(
|
97
|
+
encrypt01,
|
98
|
+
encrypt02)
|
99
|
+
|
100
|
+
puts "forgiva_encrypted_inputs: #{ret.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
101
|
+
|
102
|
+
return ret
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
#################
|
108
|
+
# Class methods #
|
109
|
+
#################
|
110
|
+
def self.pbkdf2_hmac_sha(key,salt,type)
|
111
|
+
|
112
|
+
rkey = nil
|
113
|
+
|
114
|
+
if (type == Constants::FORGIVA_PG_SIMPLE) then
|
115
|
+
rkey = OpenSSL::PKCS5.pbkdf2_hmac_sha1(key, salt, 10_000, 32)
|
116
|
+
elsif (type == Constants::FORGIVA_PG_INTERMEDIATE) then
|
117
|
+
digest = OpenSSL::Digest::SHA256.new
|
118
|
+
rkey = OpenSSL::PKCS5.pbkdf2_hmac(key,salt,10_000 * 1000, 32,digest);
|
119
|
+
elsif (type == Constants::FORGIVA_PG_ADVANCED) then
|
120
|
+
digest = OpenSSL::Digest::SHA512.new
|
121
|
+
rkey = OpenSSL::PKCS5.pbkdf2_hmac(key,salt,10_000 * 10000, 32,digest);
|
122
|
+
end
|
123
|
+
|
124
|
+
return rkey
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.pad_with_zeroes(data,block_size)
|
128
|
+
|
129
|
+
if (data.length % block_size != 0) then
|
130
|
+
|
131
|
+
tot = block_size + data.length
|
132
|
+
|
133
|
+
toadd = (tot - (tot % block_size) - data.length)
|
134
|
+
|
135
|
+
for i in (1..toadd) do
|
136
|
+
data = data + "\x00"
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
data
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.shorten_if_long(data,block_size)
|
145
|
+
|
146
|
+
data = data[0..block_size] if data.length > block_size
|
147
|
+
|
148
|
+
data
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.encrypt_ex(alg, data, key,iv)
|
152
|
+
|
153
|
+
cipher = OpenSSL::Cipher::Cipher.new(alg)
|
154
|
+
cipher.encrypt
|
155
|
+
|
156
|
+
key_ = shorten_if_long(pad_with_zeroes(key,cipher.key_len),cipher.key_len)
|
157
|
+
iv_ = shorten_if_long(pad_with_zeroes(iv,cipher.iv_len),cipher.iv_len)
|
158
|
+
|
159
|
+
cipher.key = key_
|
160
|
+
cipher.iv = iv_
|
161
|
+
|
162
|
+
## Padding if length is not multiple of block size of cipher
|
163
|
+
data = pad_with_zeroes(data,cipher.block_size)
|
164
|
+
|
165
|
+
puts "self.encrypt: #{alg} - #{data.unpack('H*')} Key: #{cipher.key_len} #{key_.unpack('H*')} IV: #{cipher.iv_len} #{iv_.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
166
|
+
|
167
|
+
|
168
|
+
ret = cipher.update(data)
|
169
|
+
|
170
|
+
#+ cipher.final
|
171
|
+
|
172
|
+
puts "self.encrypt: (ret) #{ret.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
173
|
+
|
174
|
+
return ret
|
175
|
+
|
176
|
+
|
177
|
+
rescue OpenSSL::Cipher::CipherError => e
|
178
|
+
puts "Error: #{e.message} data_len #{data.length} key_len #{key.length} iv_len #{iv.length}"
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.encrypt(alg, data, extension)
|
182
|
+
|
183
|
+
return self.encrypt_ex(alg,
|
184
|
+
data,
|
185
|
+
self.pbkdf2_hmac_sha(extension,'forgiva', Constants::FORGIVA_PG_SIMPLE),
|
186
|
+
sha512ize(extension))
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.iterative_encrypt(val1, val2)
|
190
|
+
ret = val1
|
191
|
+
|
192
|
+
val1.each_byte do |c|
|
193
|
+
alg = Constants::ENC_ALGS[c % Constants::ENC_ALGS.length]
|
194
|
+
ret = encrypt(alg, ret, val2)
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
puts "#{ret.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
199
|
+
|
200
|
+
ret
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.hash(alg, val)
|
204
|
+
dig = OpenSSL::Digest.new(alg)
|
205
|
+
nval =val
|
206
|
+
puts "alg: #{alg}" if (val == nil)
|
207
|
+
|
208
|
+
|
209
|
+
ret = ""
|
210
|
+
puts "HASH IN: #{nval.unpack('H*')}:#{nval.length} alg: #{alg} dl: #{dig.digest_length}" if Constants::DEBUG_OUTPUT
|
211
|
+
|
212
|
+
if (dig.digest_length < val.length) then
|
213
|
+
|
214
|
+
st = 0
|
215
|
+
en = dig.digest_length
|
216
|
+
while (true) do
|
217
|
+
|
218
|
+
inblock = val[st..en-1]
|
219
|
+
puts "INBLOCK: #{inblock.unpack('H*')}:#{inblock.length}" if Constants::DEBUG_OUTPUT
|
220
|
+
outblock = OpenSSL::Digest.digest(alg,inblock)
|
221
|
+
puts "OUTBLOCK: #{outblock.unpack('H*')}:#{outblock.length}" if Constants::DEBUG_OUTPUT
|
222
|
+
ret = ret + outblock
|
223
|
+
puts "NRET: #{ret.unpack('H*')}:#{ret.length}" if Constants::DEBUG_OUTPUT
|
224
|
+
|
225
|
+
st = en
|
226
|
+
break if (st >= val.length)
|
227
|
+
|
228
|
+
en = st + dig.digest_length
|
229
|
+
en = val.length if (en > val.length)
|
230
|
+
end
|
231
|
+
|
232
|
+
ret = ret[0...val.length]
|
233
|
+
|
234
|
+
else
|
235
|
+
ret = dig.digest(nval)
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
puts "HASH OUT: #{ret.unpack('H*')}:#{ret.length}" if Constants::DEBUG_OUTPUT
|
240
|
+
return ret
|
241
|
+
end
|
242
|
+
|
243
|
+
def self.iterative_hash(val)
|
244
|
+
ret = val
|
245
|
+
|
246
|
+
val.each_byte do |c|
|
247
|
+
alg = Constants::HASH_ALGS[c % Constants::HASH_ALGS.length]
|
248
|
+
ret = hash(alg, ret)
|
249
|
+
end
|
250
|
+
|
251
|
+
ret
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.sha512ize(val)
|
255
|
+
hash('sha512', val)
|
256
|
+
end
|
257
|
+
|
258
|
+
def self.hash_twice(val)
|
259
|
+
iterative_hash(iterative_hash(val))
|
260
|
+
end
|
261
|
+
|
262
|
+
def self.hash_to_password(val,complexity,length)
|
263
|
+
ret = ''
|
264
|
+
|
265
|
+
# to be sure it is long enough
|
266
|
+
hashed = sha512ize(val)
|
267
|
+
|
268
|
+
|
269
|
+
pchars = (complexity == 2 ? Constants::PASSWORD_CHARS_INTERMEDIATE :
|
270
|
+
(complexity == 3 ? Constants::PASSWORD_CHARS_ADVANCED : Constants::PASSWORD_CHARS))
|
271
|
+
|
272
|
+
|
273
|
+
hashed.each_byte do |c|
|
274
|
+
ret += pchars[c % pchars.length]
|
275
|
+
break if ret.length >= length
|
276
|
+
end
|
277
|
+
|
278
|
+
ret = ret[0..length-1]
|
279
|
+
|
280
|
+
puts "HASH_TO_PASSWORD (IN): #{val.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
281
|
+
puts "HASH_TO_PASSWORD (HASHED): #{hashed.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
282
|
+
puts "HASH_TO_PASSWORD (OUT): #{ret.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
283
|
+
|
284
|
+
return ret
|
285
|
+
|
286
|
+
end
|
287
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'forgiva'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
# Command line access to Forgiva
|
5
|
+
class ForgivaCommands
|
6
|
+
attr_accessor :hash_args
|
7
|
+
|
8
|
+
def initialize(hash_args = {})
|
9
|
+
@hash_args = hash_args
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
|
14
|
+
single_generate_choose if single_by_choose?
|
15
|
+
single_generate if !single_by_choose?
|
16
|
+
end
|
17
|
+
|
18
|
+
def ask_for_master_password
|
19
|
+
|
20
|
+
master_password = 'a'
|
21
|
+
master_password_check = 'b'
|
22
|
+
|
23
|
+
while master_password != master_password_check
|
24
|
+
master_password = ask(Constants::COLOR_CYA + "Master password: " + Constants::COLOR_RST ) { |q| q.echo = false }
|
25
|
+
master_password_check = ask(Constants::COLOR_CYA + "Master password (again): " + Constants::COLOR_RST ) { |q| q.echo = false }
|
26
|
+
|
27
|
+
puts 'Master passwords do not match!' unless master_password == master_password_check
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
digest = OpenSSL::Digest.digest("sha512",master_password)
|
33
|
+
|
34
|
+
puts "digest: #{digest.unpack('H*')}" if Constants::DEBUG_OUTPUT
|
35
|
+
return digest
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def forgiva_r_path
|
40
|
+
File.join(Dir.home, '.forgivacr')
|
41
|
+
end
|
42
|
+
|
43
|
+
def record
|
44
|
+
line_to_add = "#{@hostname};#{@account};#{@renewal_date}"
|
45
|
+
|
46
|
+
File.open(forgiva_r_path, 'a') do |file|
|
47
|
+
file.puts line_to_add
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def saved_records
|
52
|
+
recs = []
|
53
|
+
i_a = 1
|
54
|
+
File.open(forgiva_r_path).each do |line|
|
55
|
+
|
56
|
+
begin
|
57
|
+
recs << line.rstrip
|
58
|
+
hostname, account, renewal_date = line.rstrip.split(';')
|
59
|
+
|
60
|
+
puts "#{Constants::COLOR_BRI}#{i_a} - #{Constants::COLOR_BLU}#{hostname} #{Constants::COLOR_CYA} #{account} : #{Constants::COLOR_RST} #{renewal_date}"
|
61
|
+
i_a += 1
|
62
|
+
rescue
|
63
|
+
puts "Invalid line in credentials file (#{forgiva_r_path}) - #{line} "
|
64
|
+
exit(1)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
recs
|
69
|
+
end
|
70
|
+
|
71
|
+
def single_generate_choose
|
72
|
+
recs = saved_records
|
73
|
+
|
74
|
+
puts('')
|
75
|
+
idx = ask("#{Constants::COLOR_GRN}Selection: #{Constants::COLOR_RST}")
|
76
|
+
|
77
|
+
r = recs[idx.to_i-1].split(';')
|
78
|
+
|
79
|
+
@hostname = r[0]
|
80
|
+
@account = r[1]
|
81
|
+
@renewal_date = r[2]
|
82
|
+
|
83
|
+
single_generate
|
84
|
+
end
|
85
|
+
|
86
|
+
def single_generate
|
87
|
+
|
88
|
+
init_hostname
|
89
|
+
init_account
|
90
|
+
init_renewal_date
|
91
|
+
init_length
|
92
|
+
init_master_password
|
93
|
+
init_complexity
|
94
|
+
|
95
|
+
puts Constants::COLOR_GRN + "Generating..." + Constants::COLOR_RST
|
96
|
+
puts ""
|
97
|
+
|
98
|
+
record if record?
|
99
|
+
|
100
|
+
passwords = make_passwords(@hostname, @account, @renewal_date, @master_password, @complexity, @length)
|
101
|
+
|
102
|
+
if animals.length > 1
|
103
|
+
Constants::ANIMALS.each { |a| puts "#{Constants::COLOR_YEL}#{a}#{Constants::COLOR_RST}\t#{Constants::COLOR_BRI}#{passwords[a]}#{Constants::COLOR_RST}" }
|
104
|
+
else
|
105
|
+
puts passwords[animals[0]]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def record?
|
110
|
+
hash_args.key? 's' || hash_args.key?('save-credentials')
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
def single_by_choose?
|
118
|
+
hash_args.key?('e') || hash_args.key?('select-credentials')
|
119
|
+
end
|
120
|
+
|
121
|
+
def init_length
|
122
|
+
@length = 16
|
123
|
+
@length = hash_args['l'].to_i if hash_args['l'] != nil
|
124
|
+
@length = hash_args['length'].to_i if @length == nil && hash_args['length'] != nil
|
125
|
+
return @length
|
126
|
+
end
|
127
|
+
|
128
|
+
def init_master_password
|
129
|
+
@master_password = ask_for_master_password
|
130
|
+
return @master_password
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def init_complexity
|
135
|
+
@complexity = Constants::FORGIVA_PG_SIMPLE
|
136
|
+
@complexity = hash_args['c'].to_i if hash_args['c'] != nil
|
137
|
+
@complexity = hash_args['complexity'].to_i if @complexity == nil && hash_args['complexity'] != nil
|
138
|
+
|
139
|
+
if (@complexity == Constants::FORGIVA_PG_INTERMEDIATE) then
|
140
|
+
puts Constants::COLOR_YEL + "\nINTERMEDIATE COMPLEXITY\n" + Constants::COLOR_RST
|
141
|
+
elsif (@complexity == Constants::FORGIVA_PG_ADVANCED) then
|
142
|
+
puts Constants::COLOR_RED + "\nADVANCED COMPLEXITY\n" + Constants::COLOR_RST
|
143
|
+
end
|
144
|
+
|
145
|
+
return @complexity
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def init_hostname
|
150
|
+
@hostname = hash_args['h'] if hash_args['h'] != nil
|
151
|
+
@hostname = hash_args['host'] if @hostname == nil && hash_args['host'] != nil
|
152
|
+
@hostname = ask(Constants::COLOR_GRN + "Hostname: " + Constants::COLOR_RST ) if @hostname == nil
|
153
|
+
return @hostname
|
154
|
+
end
|
155
|
+
|
156
|
+
def init_account
|
157
|
+
@account = hash_args['a'] if hash_args['a'] != nil
|
158
|
+
@account = hash_args['account'] if hash_args['account'] != nil
|
159
|
+
@account = ask(Constants::COLOR_GRN + "Account: " + Constants::COLOR_RST ) if @account == nil
|
160
|
+
return @account
|
161
|
+
end
|
162
|
+
|
163
|
+
def init_renewal_date
|
164
|
+
@renewal_date = "1970-01-01" #Time.now.strftime("%Y-%m-%d")
|
165
|
+
@renewal_date = hash_args['r'] if hash_args['r'] != nil
|
166
|
+
@renewal_date = hash_args['renewal-date'] if @hostname == nil && hash_args['renewal-date'] != nil
|
167
|
+
|
168
|
+
begin
|
169
|
+
Date.strptime(@renewal_date, '%Y-%m-%d')
|
170
|
+
rescue
|
171
|
+
puts "WARNING: Renewal date is not valid for YEAR-MONTH-DAY format but still accepted";
|
172
|
+
end
|
173
|
+
|
174
|
+
@renewal_date = @renewal_date.gsub(';','')
|
175
|
+
|
176
|
+
return @renewal_date
|
177
|
+
end
|
178
|
+
|
179
|
+
def animals
|
180
|
+
|
181
|
+
return Constants::ANIMALS
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
def make_passwords(hostname, account, renewal_date, master_password, complexity,length)
|
186
|
+
Forgiva.new(hostname, account, renewal_date, master_password,complexity,length).passwords
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
end
|
data/lib/forgiva_test.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'forgiva'
|
2
|
+
require 'testvectors'
|
3
|
+
require 'openssl'
|
4
|
+
require 'constants'
|
5
|
+
|
6
|
+
class ForgivaTest
|
7
|
+
|
8
|
+
|
9
|
+
def self.run_tests
|
10
|
+
|
11
|
+
|
12
|
+
TestVectors::FA_TESTS.each do |test_vec|
|
13
|
+
|
14
|
+
puts "#{Constants::COLOR_GRN} Testing algorithm #{test_vec[:algorithm_name]} ... #{Constants::COLOR_RST}"
|
15
|
+
|
16
|
+
plain_data = [test_vec[:data_hex]].pack('H*')
|
17
|
+
key = [test_vec[:key_hex]].pack('H*')
|
18
|
+
iv = [test_vec[:iv_hex]].pack('H*')
|
19
|
+
expected = [test_vec[:target_hex]].pack('H*')
|
20
|
+
|
21
|
+
if (test_vec[:is_encryption_algorithm]) then
|
22
|
+
result = Forgiva.encrypt_ex(test_vec[:algorithm_name], plain_data, key, iv)
|
23
|
+
|
24
|
+
else
|
25
|
+
result = Forgiva.hash(test_vec[:algorithm_name],plain_data)
|
26
|
+
end
|
27
|
+
|
28
|
+
if (result != expected) then
|
29
|
+
puts "#{Constants::COLOR_RED} FAILED: (Expected: #{test_vec[:target_hex]}) #{result.unpack('H*') if result != nil} #{Constants::COLOR_RST}"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
TestVectors::FG_TESTS.each do |test_vec|
|
36
|
+
|
37
|
+
puts "#{Constants::COLOR_GRN} Testing forgiva #{Constants::COLOR_BLU} #{test_vec[:host]} " \
|
38
|
+
<<"/ #{test_vec[:account]} / #{test_vec[:renewal_date]} / #{Constants::COLOR_MGN} #{test_vec[:animal_name]} #{Constants::COLOR_GRN} " \
|
39
|
+
<<" on complexity #{test_vec[:complexity]} #{Constants::COLOR_RST}"
|
40
|
+
|
41
|
+
p_hash = OpenSSL::Digest.digest("sha512",test_vec[:master_key])
|
42
|
+
|
43
|
+
passes = Forgiva.new(test_vec[:host],
|
44
|
+
test_vec[:account],
|
45
|
+
test_vec[:renewal_date],
|
46
|
+
p_hash,
|
47
|
+
test_vec[:complexity],
|
48
|
+
16).passwords
|
49
|
+
|
50
|
+
g_pass = passes[test_vec[:animal_name]].unpack('H*')[0]
|
51
|
+
|
52
|
+
|
53
|
+
if (g_pass.downcase != test_vec[:expected_password_hash]) then
|
54
|
+
puts "#{Constants::COLOR_RED} FAILED: (Expected: #{test_vec[:expected_password_hash]}) #{Constants::COLOR_RST} #{g_pass}"
|
55
|
+
else
|
56
|
+
puts "#{Constants::COLOR_GRN}! SUCCESS: (#{g_pass}) #{Constants::COLOR_RST}"
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|