filesafe 1.1.0 → 2.0.1
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.
- data/README.txt +52 -19
- data/Rakefile +3 -2
- data/VERSION.txt +1 -1
- data/bin/filesafe +9 -9
- data/lib/filesafe.rb +104 -34
- data/test/bar +1 -0
- data/test/test_decrypt.rb +41 -0
- data/test/test_module.rb +3 -3
- metadata +6 -4
data/README.txt
CHANGED
@@ -46,12 +46,26 @@ encrypt F_KEY + F_IV to obtain:
|
|
46
46
|
C_F_KEY = ciphertext encrypted version of F_KEY, encrypted using M_KEY and M_IV
|
47
47
|
C_F_IV = ciphertext encrypted version of F_IV, encrypted using M_KEY and M_IV
|
48
48
|
|
49
|
-
A new file is opened, and SALT + C_F_KEY + C_F_IV are written.
|
50
|
-
|
51
|
-
|
49
|
+
A new file is opened, and SALT + C_F_KEY + C_F_IV are written. A number
|
50
|
+
of zero bytes are written to make space to store the HMAC that will be
|
51
|
+
calculated.
|
52
52
|
|
53
|
-
|
54
|
-
to the
|
53
|
+
Then the contents of the plaintext file are then encrypted using F_KEY and F_IV
|
54
|
+
and written to the new file following the file header described above.
|
55
|
+
|
56
|
+
A HMAC is calculated on SALT + C_F_KEY + C_F_IV + encrypted file text.
|
57
|
+
Then the PBKDF2 function is applied to PASS + HMAC using the same SALT
|
58
|
+
to provide a MAC of sorts.
|
59
|
+
|
60
|
+
The HMAC isn't used directly because it would be easier to attempt to
|
61
|
+
apply a dictionary attack against the passphrase, at least for smaller
|
62
|
+
encrypted files, without PBKDF2's multiple iterations which increase
|
63
|
+
computation time for each passphrase guess. This new HMAC/PBKDF2 hybrid
|
64
|
+
MAC is written over the top of the zero-bytes previously allocated, and
|
65
|
+
the file is closed.
|
66
|
+
|
67
|
+
Version 1.x of this library and utility stored the HMAC directly instead
|
68
|
+
utilizing this hybrid MAC scheme.
|
55
69
|
|
56
70
|
This new file is the encrypted file. The old plaintext file is overwritten and
|
57
71
|
removed.
|
@@ -59,16 +73,32 @@ removed.
|
|
59
73
|
The HMAC uses the same PASS passphrase and the same hash algorithm that
|
60
74
|
PBKDF2 uses (SHA-512 by default).
|
61
75
|
|
62
|
-
To recover the file, an HMAC is calculated on the encrypted file contents
|
63
|
-
excluding the
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
76
|
+
To recover the file, an HMAC is calculated on the encrypted file contents,
|
77
|
+
excluding the HMAC/PBKDF2 hybrid MAC data. The PBKDF2 function is applied
|
78
|
+
supplying PASS + HMAC as the passphrase and the SALT from the file to
|
79
|
+
calculate the hybrid HMAC/PBKDF2 MAC. This hybrid MAC is then compared to
|
80
|
+
the one from the encrypted file. If the calculated MAC doesn't match the
|
81
|
+
supplied MAC, then either the file has been corrupted, or the passphrase
|
82
|
+
is incorrect.
|
83
|
+
|
84
|
+
If the MACs match, the encrypted master key material C_F_KEY and C_F_IV
|
85
|
+
are read from the file. M_KEY and M_IV are generated using PBKDF2
|
86
|
+
and PASS with SALT. F_KEY and F_IV are decrypted using M_KEY and M_IV.
|
87
|
+
With the file encryption key and initialization vector, the file contents
|
88
|
+
are then decrypted, revealing the original file plaintext.
|
89
|
+
|
90
|
+
The encrypted file can then be safely removed. The provided utility has
|
91
|
+
the option of then creating a separate file containing a newly-generated
|
92
|
+
salt and a PBKDF2 generated sum using the new salt and the original
|
93
|
+
passphrase. This provides the user the ability to ask for a passphrase
|
94
|
+
and compare it (using PBKDF2 and the salt) to the original passphrase
|
95
|
+
without revealing the original passphrase.
|
96
|
+
|
97
|
+
The filesafe utility by default does exactly this, so that if a file
|
98
|
+
is re-encrypted (which will always use a freshly generated salt and
|
99
|
+
key), the user is asked for the original passphrase once again. Should
|
100
|
+
the user decide to use a new phrase, the temporary file may be safely
|
101
|
+
deleted.
|
72
102
|
|
73
103
|
|
74
104
|
LICENSE
|
@@ -102,8 +132,11 @@ Please report bugs by going to the author's web site and clicking on the
|
|
102
132
|
|
103
133
|
* http://www.aarongifford.com/leaveanote.html
|
104
134
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
135
|
+
I am debating as to whether I should replace the HMAC in the file header
|
136
|
+
with a PBKDF2 function, perhaps PBKDF2(passphrase, iterations, HMAC)
|
137
|
+
so as to make dictionary attacks against passwords much more difficult.
|
138
|
+
It would result in a slight file format change, so I'd have to bump up
|
139
|
+
the version, and perhaps provide a fallback to the old method if a
|
140
|
+
passphrase doesn't seem to match a ciphertext file's stored PBKDF2
|
141
|
+
result.
|
109
142
|
|
data/Rakefile
CHANGED
@@ -10,8 +10,9 @@ gemspec = Gem::Specification.new do |spec|
|
|
10
10
|
spec.homepage = 'http://www.aarongifford.com/computers/filesafe/'
|
11
11
|
spec.summary = 'Encrypt/decrypt files with a random 256-bit AES key secured by a passphrase derived master key using PBKDF2'
|
12
12
|
spec.description = 'A utility script for encrypting and decrypting files using a randomly generated 256-bit AES key and initialization vector secured using the PBKDF2 password/passphrase key derivation algorithm to secure the file key and IV.'
|
13
|
-
spec.has_rdoc =
|
13
|
+
spec.has_rdoc = true ## Very limited documentation
|
14
14
|
spec.extra_rdoc_files = [ 'README.txt' ]
|
15
|
+
spec.require_paths = [ 'lib' ]
|
15
16
|
spec.files = FileList[
|
16
17
|
'README.txt',
|
17
18
|
'VERSION.txt',
|
@@ -34,7 +35,7 @@ Rake::RDocTask.new do |rdoc|
|
|
34
35
|
rdoc.name = 'rdoc'
|
35
36
|
rdoc.main = 'README.txt'
|
36
37
|
rdoc.rdoc_dir = 'doc'
|
37
|
-
rdoc.rdoc_files.include('README.txt')
|
38
|
+
rdoc.rdoc_files.include('README.txt', 'lib/*')
|
38
39
|
end
|
39
40
|
|
40
41
|
Rake::TestTask.new do |t|
|
data/VERSION.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.1
|
data/bin/filesafe
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#
|
3
|
-
# FileSafe
|
4
|
-
# Version: 1.1.0
|
5
|
-
# From: http://www.aarongifford.com/computers/filesafe/index.html
|
3
|
+
# FileSafe - http://www.aarongifford.com/computers/filesafe/
|
6
4
|
#
|
7
5
|
# A simple file encryption/decryption script that uses 256-bit AES encryption,
|
8
6
|
# a secure random number/data source, and the password/passphrase (via PBKDF2)
|
9
7
|
# to encrypt/decrypt one or more files.
|
10
8
|
#
|
11
|
-
#
|
12
|
-
# Copyright (c) 2010 Aaron D. Gifford
|
9
|
+
# Author:: Aaron D. Gifford - http://www.aarongifford.com/
|
10
|
+
# Copyright:: Copyright (c) 2010 Aaron D. Gifford
|
11
|
+
# License:: This file may be used and distributed under the MIT license
|
13
12
|
#
|
14
13
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
15
14
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -39,14 +38,16 @@ Usage: #{$0} -e|-d [-n] [-p 'pass phrase'] [--] file [file [file...]]
|
|
39
38
|
-e Encrypt file(s)
|
40
39
|
-d Decrypt file(s)
|
41
40
|
-p 'pass phrase' Passphrase to use for operation
|
42
|
-
-n Do NOT
|
41
|
+
-n Do NOT use a temporary password HMAC/hash file:
|
42
|
+
* ignore such file if found during encryption
|
43
|
+
* do not create such file during decryption
|
43
44
|
-- End of options -- all subsequent arguments are files
|
44
45
|
|
45
46
|
EOM
|
46
47
|
exit
|
47
48
|
end
|
48
49
|
|
49
|
-
opt = {}
|
50
|
+
opt = { :notemp => false }
|
50
51
|
while ARGV.size > 0 && ARGV[0][0,1] == '-'
|
51
52
|
case ARGV[0]
|
52
53
|
when '-e'
|
@@ -63,7 +64,6 @@ while ARGV.size > 0 && ARGV[0][0,1] == '-'
|
|
63
64
|
opt[:phrase] = ARGV.shift
|
64
65
|
when '-n'
|
65
66
|
ARGV.shift
|
66
|
-
usage "This option only applies to decryption operations." unless opt[:op] == :decrypt
|
67
67
|
opt[:notemp] = true
|
68
68
|
when '--'
|
69
69
|
break
|
@@ -80,7 +80,7 @@ ARGV.each do |file|
|
|
80
80
|
FileSafe.decrypt(file, opt[:phrase], opt[:notemp])
|
81
81
|
puts "FILE DECRYPTED: #{file}"
|
82
82
|
else
|
83
|
-
FileSafe.encrypt(file, opt[:phrase])
|
83
|
+
FileSafe.encrypt(file, opt[:phrase], opt[:notemp])
|
84
84
|
puts "ENCRYPTED: #{file}"
|
85
85
|
end
|
86
86
|
rescue StandardError => e
|
data/lib/filesafe.rb
CHANGED
@@ -1,5 +1,35 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# FileSafe - http://www.aarongifford.com/computers/filesafe/
|
4
|
+
#
|
5
|
+
# A simple file encryption/decryption script that uses 256-bit AES encryption,
|
6
|
+
# a secure random number/data source, and the password/passphrase (via PBKDF2)
|
7
|
+
# to encrypt/decrypt one or more files.
|
8
|
+
#
|
9
|
+
# Author:: Aaron D. Gifford - http://www.aarongifford.com/
|
10
|
+
# Copyright:: Copyright (c) 2010 Aaron D. Gifford
|
11
|
+
# License:: This file may be used and distributed under the MIT license
|
12
|
+
#
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
14
|
+
# of this software and associated documentation files (the "Software"), to deal
|
15
|
+
# in the Software without restriction, including without limitation the rights
|
16
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
17
|
+
# copies of the Software, and to permit persons to whom the Software is
|
18
|
+
# furnished to do so, subject to the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be included in
|
21
|
+
# all copies or substantial portions of the Software.
|
22
|
+
#
|
23
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
24
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
25
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
26
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
27
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
28
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
29
|
+
# THE SOFTWARE.
|
2
30
|
|
31
|
+
# FileSafe module has four module methods, two for file encryption/decryption,
|
32
|
+
# one for passphrase hashing, and one for reading a passphrase from a terminal.
|
3
33
|
module FileSafe
|
4
34
|
require 'openssl' ## Encryption/HMAC/Hash algorithms
|
5
35
|
require 'securerandom' ## Cryptographically secure source of random data
|
@@ -7,20 +37,42 @@ module FileSafe
|
|
7
37
|
require 'highline' ## For reading a passphrase from a terminal
|
8
38
|
require 'tempfile' ## Temporary file creation
|
9
39
|
|
10
|
-
|
40
|
+
# Temporary passphrase hash file suffix:
|
11
41
|
PASSHASH_SUFFIX = '.pass'
|
42
|
+
|
43
|
+
# Default cipher (AES-256 in CBC mode):
|
12
44
|
CIPHER = 'aes-256-cbc'
|
45
|
+
|
13
46
|
cipher = OpenSSL::Cipher::Cipher.new(CIPHER)
|
47
|
+
|
48
|
+
# Cipher block length/size (128 bits/16 bytes for AES-256):
|
14
49
|
BLOCK_LEN = cipher.block_size
|
50
|
+
|
51
|
+
# Cipher key length (256 bits/32 bytes for AES-256):
|
15
52
|
KEY_LEN = cipher.key_len
|
53
|
+
|
54
|
+
# Cipher initialization vector (IV) length (128 bits/16 bytes for AES-256):
|
16
55
|
IV_LEN = cipher.iv_len
|
56
|
+
|
57
|
+
# Salt size/length (384 bits/48 bytes, KEY + IV size):
|
17
58
|
SALT_LEN = KEY_LEN + IV_LEN
|
59
|
+
|
60
|
+
# Default hash function to use for HMAC (SHA-512 by default):
|
18
61
|
HMAC_FUNC = 'sha512'
|
62
|
+
|
63
|
+
# Default HMAC size/length (512 bits/64 bytes for HMAC-SHA-512):
|
19
64
|
HMAC_LEN = OpenSSL::HMAC.new('', HMAC_FUNC).digest.bytesize
|
65
|
+
|
66
|
+
# Default ciphertext file header size (key + IV + salt + HMAC = 1280 bits/160 bytes by default)
|
20
67
|
HEADER_LEN = KEY_LEN + IV_LEN + SALT_LEN + HMAC_LEN
|
68
|
+
|
69
|
+
# Number of iterations to use in PBKDF2 (4096 by default):
|
21
70
|
ITERATIONS = 4096
|
71
|
+
|
72
|
+
# Number of bytes to read from plaintext/ciphertext files at a time (64KB by default):
|
22
73
|
FILE_CHUNK_LEN = 65536
|
23
74
|
|
75
|
+
# Read a passphrase from a terminal.
|
24
76
|
def self.getphrase(check=false)
|
25
77
|
begin
|
26
78
|
phrase = HighLine.new.ask('Passphrase: '){|q| q.echo = '*' ; q.overwrite = true }
|
@@ -32,25 +84,43 @@ module FileSafe
|
|
32
84
|
end while true
|
33
85
|
end
|
34
86
|
|
35
|
-
def self.
|
87
|
+
def self.passmatch?(passphrase, salt, hash)
|
88
|
+
pbkdf2(passphrase, salt, HMAC_LEN) == hash
|
89
|
+
end
|
90
|
+
|
91
|
+
# Encrypt a file with the supplied passphrase--or if none is supplied,
|
92
|
+
# read a passphrase from the terminal.
|
93
|
+
def self.encrypt(file, passphrase=nil, notemp=true)
|
36
94
|
raise "Cannot encrypt non-existent file: #{file.inspect}" unless File.exist?(file)
|
37
95
|
raise "Cannot encrypt unreadable file: #{file.inspect}" unless File.readable?(file)
|
38
96
|
raise "Cannot encrypt unwritable file: #{file.inspect}" unless File.writable?(file)
|
39
97
|
passhash = false
|
40
|
-
if File.exist?(file + PASSHASH_SUFFIX)
|
98
|
+
if !notemp && File.exist?(file + PASSHASH_SUFFIX)
|
41
99
|
raise "Cannot read password hash temporary file: #{(file + PASSHASH_SUFFIX).inspect}" unless File.readable?(file + PASSHASH_SUFFIX)
|
42
100
|
raise "Password hash temporary file length is invalid: #{(file + PASSHASH_SUFFIX).inspect}" unless File.size(file + PASSHASH_SUFFIX) == SALT_LEN + HMAC_LEN
|
43
|
-
|
44
|
-
salt = fp.read(SALT_LEN)
|
45
|
-
passcheck = fp.read(HMAC_LEN)
|
46
|
-
loop do
|
47
|
-
passphrase = getphrase if passphrase.nil?
|
48
|
-
phash = hashpass(passphrase, salt)
|
49
|
-
break if passcheck == phash[1]
|
50
|
-
puts "*** ERROR: Passphrase mismatch. Try again, abort, or delete temporary file: #{file + PASSHASH_SUFFIX}"
|
51
|
-
passphrase = nil
|
52
|
-
end
|
101
|
+
|
53
102
|
passhash = true
|
103
|
+
|
104
|
+
## Read temporary passphrase hash file:
|
105
|
+
psalt = pcheck = nil
|
106
|
+
File.open(file + PASSHASH_SUFFIX, File::RDONLY) do |fp|
|
107
|
+
psalt = fp.read(SALT_LEN)
|
108
|
+
pcheck = fp.read(HMAC_LEN)
|
109
|
+
end
|
110
|
+
|
111
|
+
## Was a passphrase supplied?
|
112
|
+
if passphrase.nil?
|
113
|
+
## No, so ask for one:
|
114
|
+
loop do
|
115
|
+
passphrase = getphrase
|
116
|
+
## Check the phrase against the stored hash:
|
117
|
+
break if passmatch?(passphrase, psalt, pcheck)
|
118
|
+
puts "*** ERROR: Passphrase mismatch. Try again, abort, or delete temporary file: #{file + PASSHASH_SUFFIX}"
|
119
|
+
end
|
120
|
+
else
|
121
|
+
## Yes, so check supplied phrase against the stored hash:
|
122
|
+
raise "Passphrase mismatch" unless passmatch?(passphrase, psalt, pcheck)
|
123
|
+
end
|
54
124
|
elsif passphrase.nil?
|
55
125
|
puts "*** ALERT: Enter your NEW passphrase twice. DO NOT FORGET IT, or you may lose your data!"
|
56
126
|
passphrase = getphrase(true)
|
@@ -62,13 +132,7 @@ module FileSafe
|
|
62
132
|
file_iv = SecureRandom.random_bytes(IV_LEN) ## And a random initialization vector
|
63
133
|
|
64
134
|
## Encrypt the file key and IV using password-derived keying material:
|
65
|
-
keymaterial =
|
66
|
-
p.hash_function = HMAC_FUNC
|
67
|
-
p.password = passphrase
|
68
|
-
p.salt = salt
|
69
|
-
p.iterations = ITERATIONS
|
70
|
-
p.key_length = KEY_LEN + IV_LEN
|
71
|
-
end.bin_string
|
135
|
+
keymaterial = pbkdf2(passphrase, salt, KEY_LEN + IV_LEN)
|
72
136
|
cipher = OpenSSL::Cipher::Cipher.new(CIPHER)
|
73
137
|
cipher.encrypt
|
74
138
|
## No padding required for this operation since the file key + IV is
|
@@ -87,7 +151,7 @@ module FileSafe
|
|
87
151
|
wfp = Tempfile.new(File.basename(rfp.path), File.dirname(rfp.path))
|
88
152
|
|
89
153
|
## Write the salt and encrypted file key + IV and
|
90
|
-
## temporarily fill the HMAC slot with zero-bytes:
|
154
|
+
## temporarily fill the PBKDF2-hashed HMAC slot with zero-bytes:
|
91
155
|
wfp.write(salt + encrypted_file_key + encrypted_file_iv + (0.chr * HMAC_LEN))
|
92
156
|
|
93
157
|
## Start the HMAC:
|
@@ -119,9 +183,11 @@ module FileSafe
|
|
119
183
|
hmac << data
|
120
184
|
end
|
121
185
|
|
122
|
-
##
|
186
|
+
## Instead of storing the HMAC directly, use PBKDF2 to store data generated
|
187
|
+
## from the HMAC in hopes that PBKDF2's multiple iterations will make
|
188
|
+
## brute force dictionary attacks against the passphrase much more cumbersome:
|
123
189
|
wfp.pos = SALT_LEN + KEY_LEN + IV_LEN
|
124
|
-
wfp.write(hmac.digest)
|
190
|
+
wfp.write(pbkdf2(passphrase + hmac.digest, salt, HMAC_LEN))
|
125
191
|
|
126
192
|
## Overwrite the original plaintext file with zero bytes.
|
127
193
|
## This adds a small measure of security against recovering
|
@@ -153,6 +219,10 @@ module FileSafe
|
|
153
219
|
File.delete(file + PASSHASH_SUFFIX) if passhash
|
154
220
|
end
|
155
221
|
|
222
|
+
# Decrypt a file with the supplied passphrase--or if none is supplied,
|
223
|
+
# read a passphrase from the terminal. Optionally create a temporary
|
224
|
+
# file of the same name with a suffix (by default ".pass") and store
|
225
|
+
# a passphrase hash in that file for future use.
|
156
226
|
def self.decrypt(file, passphrase=nil, notemp=true)
|
157
227
|
raise "Cannot decrypt non-existent file: #{file.inspect}" unless File.exist?(file)
|
158
228
|
raise "Cannot decrypt unreadable file: #{file.inspect}" unless File.readable?(file)
|
@@ -166,7 +236,7 @@ module FileSafe
|
|
166
236
|
salt = fp.read(SALT_LEN)
|
167
237
|
encrypted_file_key = fp.read(KEY_LEN)
|
168
238
|
encrypted_file_iv = fp.read(IV_LEN)
|
169
|
-
|
239
|
+
file_check = fp.read(HMAC_LEN)
|
170
240
|
test_hmac = OpenSSL::HMAC.new(passphrase, HMAC_FUNC)
|
171
241
|
test_hmac << salt
|
172
242
|
test_hmac << encrypted_file_key
|
@@ -176,7 +246,7 @@ module FileSafe
|
|
176
246
|
test_hmac << data unless data.bytesize == 0
|
177
247
|
end
|
178
248
|
fp.close
|
179
|
-
break if test_hmac.digest ==
|
249
|
+
break if pbkdf2(passphrase + test_hmac.digest, salt, HMAC_LEN) == file_check
|
180
250
|
puts "*** ERROR: Incorrect passphrase, or file is not encrypted. Try again or abort."
|
181
251
|
passphrase = nil
|
182
252
|
end
|
@@ -242,24 +312,24 @@ module FileSafe
|
|
242
312
|
|
243
313
|
unless notemp
|
244
314
|
## Write password hash temp. file using PBKDF2 as an iterated hash of sorts of HMAC_LEN bytes:
|
245
|
-
|
315
|
+
salt = SecureRandom.random_bytes(SALT_LEN) if salt.nil?
|
316
|
+
File.open(file + PASSHASH_SUFFIX, File::WRONLY|File::EXCL|File::CREAT) do |f|
|
317
|
+
f.write(salt + pbkdf2(passphrase, salt, HMAC_LEN))
|
318
|
+
end
|
246
319
|
end
|
247
320
|
end
|
248
321
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
def self.hashpass(passphrase, salt=nil)
|
253
|
-
## Grab a new chunk of secure random data if no salt was supplied:
|
254
|
-
salt = SecureRandom.random_bytes(SALT_LEN) if salt.nil?
|
322
|
+
# Execute PBKDF2 to generate the specified number of bytes of
|
323
|
+
# pseudo-random key material.
|
324
|
+
def self.pbkdf2(passphrase, salt, len)
|
255
325
|
hash = PBKDF2.new do |p|
|
256
326
|
p.hash_function = HMAC_FUNC
|
257
327
|
p.password = passphrase
|
258
328
|
p.salt = salt
|
259
329
|
p.iterations = ITERATIONS
|
260
|
-
p.key_length =
|
330
|
+
p.key_length = len
|
261
331
|
end.bin_string
|
262
|
-
[ salt, hash ]
|
263
332
|
end
|
264
333
|
|
265
334
|
end
|
335
|
+
|
data/test/bar
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
foo
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require_relative '../lib/filesafe.rb'
|
5
|
+
|
6
|
+
class FileSafeDecryptTest < Test::Unit::TestCase
|
7
|
+
CIPHERTEXT = [
|
8
|
+
"60e8aacea874e36f39fef5f51cf727252359c575230b7306b6379194" +
|
9
|
+
"12bc53e9106ddaecc0ded13503e6ef9d3ff9f0285bb133d3d88464c0" +
|
10
|
+
"eea4f728ae509942a4e8be070a70d49b8668ddbda2102412ca42c917" +
|
11
|
+
"bd74c824ae6b35bb697a0fccd8f0822e310f96bfc34546e289e6dbed" +
|
12
|
+
"f3dd30eca6585ad344593ca65f6aa323722a29c1c19257b135756340" +
|
13
|
+
"7a88de6a92a85dae5dd9ea0cb8a2ccaf3c45bace571cb0c791186837" +
|
14
|
+
"a3a6ff650545286afbd75087b42582da571521fbc74fa3499dc22ebc" +
|
15
|
+
"8482e13c4055313b38a0cf79"].pack('H*')
|
16
|
+
PLAINTEXT =
|
17
|
+
"This is a PLAINTEXT file for testing\n"
|
18
|
+
PASS =
|
19
|
+
"topsecretstuff"
|
20
|
+
|
21
|
+
def setup
|
22
|
+
## Create a temporary file:
|
23
|
+
@testfile = 'test.out'
|
24
|
+
File.open(@testfile,'w'){|f| f.print CIPHERTEXT }
|
25
|
+
end
|
26
|
+
|
27
|
+
def teardown
|
28
|
+
File.unlink(@testfile)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_decrypt
|
32
|
+
## Decrypt data:
|
33
|
+
FileSafe.decrypt(@testfile, PASS, true)
|
34
|
+
|
35
|
+
## Read decrypted file contents:
|
36
|
+
plaintext = File.open(@testfile,'r'){|f| f.read}
|
37
|
+
|
38
|
+
assert(PLAINTEXT == plaintext)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
data/test/test_module.rb
CHANGED
@@ -39,7 +39,7 @@ class FileSafeModuleTest < Test::Unit::TestCase
|
|
39
39
|
assert(File.size(@testfile) == @plaintext_size)
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
42
|
+
def test_pbkdf2
|
43
43
|
pass = "When in the course of human events..."
|
44
44
|
salt = "01caf8e2e844a37810280f231f3059aca54e631528c1c57eb643df2c" +
|
45
45
|
"8c6c74bc4a6136784ecff873dcd09a80059f6e80"
|
@@ -48,8 +48,8 @@ class FileSafeModuleTest < Test::Unit::TestCase
|
|
48
48
|
"9f42a359ef12536c"
|
49
49
|
salt = [salt].pack('H*')
|
50
50
|
goal = [goal].pack('H*')
|
51
|
-
hash = FileSafe.
|
52
|
-
assert(hash
|
51
|
+
hash = FileSafe.pbkdf2(pass, salt, FileSafe::HMAC_LEN)
|
52
|
+
assert(hash == goal)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
metadata
CHANGED
@@ -3,10 +3,10 @@ name: filesafe
|
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
|
-
-
|
7
|
-
- 1
|
6
|
+
- 2
|
8
7
|
- 0
|
9
|
-
|
8
|
+
- 1
|
9
|
+
version: 2.0.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Aaron D. Gifford
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-01-04 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -61,7 +61,9 @@ files:
|
|
61
61
|
- Rakefile
|
62
62
|
- bin/filesafe
|
63
63
|
- lib/filesafe.rb
|
64
|
+
- test/bar
|
64
65
|
- test/test_module.rb
|
66
|
+
- test/test_decrypt.rb
|
65
67
|
- test/test_cli.rb
|
66
68
|
has_rdoc: true
|
67
69
|
homepage: http://www.aarongifford.com/computers/filesafe/
|