forgiva 1.0.pre.0
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/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
|