lenc 1.0.0 → 1.1.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 +4 -4
- data/CHANGELOG.txt +6 -1
- data/README.txt +38 -28
- data/lib/lenc/aes.rb +8 -8
- data/lib/lenc/lencrypt.rb +18 -5
- data/lib/lenc/repo.rb +64 -18
- data/lib/lenc/tools.rb +4 -3
- data/test/test.rb +3 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbdf59fe864cde5c6b5d6ab0029ed7b3086b6abb
|
4
|
+
data.tar.gz: 935307daa431368ca0f0d86a8b9eca068d1d1b58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1377690289e75b202bf0a6ee0cffc28c469688ccde9a8dcba36051eed8433b2f32010f5db40b6d648a4d214e0a74ead2c2f4555c2caef5aa5875d21fd17cb90e
|
7
|
+
data.tar.gz: 46b0cc17fc7254f5a87abe27099d0f3ee21e92fc133bd87de2e20c1df052bc385def1bcda8a1e64f7fd280656a8e46c6664143c1020a91bfc989fb694cbc71a0
|
data/CHANGELOG.txt
CHANGED
@@ -1,2 +1,7 @@
|
|
1
|
-
2013-03-21
|
1
|
+
2013-03-21
|
2
2
|
* Version 1.0.0 released
|
3
|
+
|
4
|
+
2013-03-22
|
5
|
+
* For added safety, unless the --storekey option is given when the repository is
|
6
|
+
initialized, the key is no longer stored in repository configuration file, and
|
7
|
+
the user is prompted at every update to enter the password.
|
data/README.txt
CHANGED
@@ -1,29 +1,35 @@
|
|
1
|
-
|
1
|
+
# @markup markdown
|
2
|
+
|
3
|
+
Lenc
|
4
|
+
===========
|
5
|
+
|
6
|
+
A Ruby Gem that maintains encrypted repositories of files, enabling secure, encrypted
|
2
7
|
backups to free cloud services such as Dropbox, Google Drive, and Microsoft SkyDrive.
|
3
8
|
|
4
9
|
Written and (c) by Jeff Sember, March 2013.
|
5
|
-
================================================================================
|
6
|
-
|
7
10
|
|
11
|
+
Directory structure
|
12
|
+
--------
|
8
13
|
The program manipulates three distinct directories:
|
9
14
|
|
10
|
-
|
15
|
+
* A \<source\> directory, which holds all the
|
11
16
|
files to be encrypted. The only file that the program modifies within this
|
12
17
|
directory tree is a hidden configuration file ".lenc" (on Windows, this file
|
13
18
|
is named "__lenc_repo__.txt").
|
14
19
|
|
15
|
-
|
20
|
+
* An \<encrypted\> directory, where the program stores the encrypted
|
16
21
|
versions of all the files found in the <source> directory. This directory
|
17
22
|
is usually mapped to a cloud service (e.g., Dropbox), or perhaps to a thumb drive.
|
18
|
-
|
19
|
-
|
23
|
+
|
24
|
+
NOTE: THE <encrypted> DIRECTORY IS MANAGED BY THE PROGRAM!
|
25
|
+
ANY FILES WRITTEN TO THIS DIRECTORY BY THE USER MAY BE DELETED.
|
20
26
|
|
21
|
-
|
27
|
+
* A \<recover\> directory. The program can recover a set of encrypted files here.
|
22
28
|
For safety, the this directory must not lie within an existing repository.
|
23
29
|
|
24
30
|
|
25
31
|
Running the program
|
26
|
-
|
32
|
+
----
|
27
33
|
|
28
34
|
The program can be asked to perform one of the following tasks:
|
29
35
|
|
@@ -33,17 +39,17 @@ directory, and make it the current directory. Type:
|
|
33
39
|
lencrypt -i KEY ENCDIR
|
34
40
|
|
35
41
|
with KEY a set of characters (8 to 56 letters) to be used as the encryption key,
|
36
|
-
and ENCDIR the name of the
|
42
|
+
and ENCDIR the name of the \<encrypted\> directory (it must not already exist, and
|
37
43
|
it cannot lie within the current directory's tree).
|
38
44
|
|
39
45
|
|
40
|
-
2) Updating a repository. From within a
|
46
|
+
2) Updating a repository. From within a \<source\> directory tree, type:
|
41
47
|
|
42
48
|
lencrypt
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
into the
|
50
|
+
You will be prompted for the encryption key, and then the program will examine
|
51
|
+
which files within the \<source\> directory have been changed (since the repository
|
52
|
+
was created or last updated), and re-encrypt these into the \<encrypted\> directory.
|
47
53
|
|
48
54
|
3) Recovering encrypted files. Type:
|
49
55
|
|
@@ -52,12 +58,16 @@ into the <encrypted> directory.
|
|
52
58
|
with KEY the key associated with a repository whose encrypted files are stored in ENCDIR.
|
53
59
|
The recovered files will be stored in RECDIR.
|
54
60
|
|
61
|
+
|
62
|
+
Additional options can be found by typing:
|
63
|
+
|
64
|
+
lencrypt -h
|
55
65
|
|
56
66
|
|
57
67
|
Ignore files
|
58
|
-
|
68
|
+
----------------
|
59
69
|
If desired, you can avoid storing selected files in the encryption repository.
|
60
|
-
Within the
|
70
|
+
Within the \<source\> directory (or any of its subdirectories), place a text
|
61
71
|
file '.lencignore' with a list of file or directory names (or patterns) to be
|
62
72
|
ignored. Example:
|
63
73
|
|
@@ -74,17 +84,17 @@ Some files are automatically ignored, e.g. ".DS_Store".
|
|
74
84
|
|
75
85
|
The format of ignore files is similar to that of .gitignore files. Details:
|
76
86
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
87
|
+
* Each line should contain a single pattern representing files or directories to be ignored.
|
88
|
+
* A line will be ignored (treated as a comment) if it is blank, or if it starts with '#'.
|
89
|
+
* The path separator should be '/' (Mac, Unix) or '\' (Windows).
|
90
|
+
* If a pattern starts with '#', you can precede it with '\' to avoid it being ignored.
|
91
|
+
* Precede a pattern with '!' to specifically include files/directories, overriding any previous
|
92
|
+
matching pattern in a parent directory.
|
93
|
+
* If a pattern ends with the path separator, it will be removed, and the pattern will
|
94
|
+
match only directories, not files.
|
95
|
+
* The wildcard '*' matches for any sequence of zero or more characters.
|
96
|
+
* The wildcard '?' matches any single character.
|
97
|
+
* If the pattern contains any path separators, then the wildcards '*', '?' will not
|
98
|
+
match the path separator.
|
89
99
|
|
90
100
|
|
data/lib/lenc/aes.rb
CHANGED
@@ -430,10 +430,10 @@ module RepoInternal
|
|
430
430
|
#
|
431
431
|
# Returns true iff the start of the string seems to decrypt correctly
|
432
432
|
# for the given password
|
433
|
-
def is_string_encrypted(key, test_str)
|
434
|
-
db =
|
433
|
+
def self.is_string_encrypted(key, test_str)
|
434
|
+
db = warndb 0
|
435
435
|
|
436
|
-
!db ||
|
436
|
+
!db || hex_dump(test_str, "areBytesEncrypted?")
|
437
437
|
|
438
438
|
simple_str(test_str)
|
439
439
|
|
@@ -445,10 +445,10 @@ module RepoInternal
|
|
445
445
|
end
|
446
446
|
|
447
447
|
begin
|
448
|
-
de = MyAES(
|
449
|
-
de.finish(test_str[
|
448
|
+
de = MyAES.new(false, key)
|
449
|
+
de.finish(test_str[0...AES_BLOCK_SIZE + NONCE_SIZE_SMALL])
|
450
450
|
decr = de.flush()
|
451
|
-
!db ||
|
451
|
+
!db || hex_dump(decr,"decrypted successfully")
|
452
452
|
rescue LEnc::DecryptionError
|
453
453
|
!db || pr(" (caught DecryptionError)\n")
|
454
454
|
return false
|
@@ -460,9 +460,9 @@ module RepoInternal
|
|
460
460
|
# Determines if a file is an encrypted file
|
461
461
|
# @param key password to use (string, or array of bytes)
|
462
462
|
# @param path path to file
|
463
|
-
#
|
463
|
+
# @return true iff the start of the file seems to decrypt correctly
|
464
464
|
# for the given password, and the file is of the expected length.
|
465
|
-
def is_file_encrypted(key, path)
|
465
|
+
def self.is_file_encrypted(key, path)
|
466
466
|
|
467
467
|
# key = str_to_bytes(key)
|
468
468
|
|
data/lib/lenc/lencrypt.rb
CHANGED
@@ -10,7 +10,9 @@ class LEncApp
|
|
10
10
|
req 'trollop'
|
11
11
|
p = Trollop::Parser.new do
|
12
12
|
opt :init, "create new encryption repository: KEY ENCDIR ", :type => :strings
|
13
|
-
opt :orignames, "leave filenames unencrypted"
|
13
|
+
opt :orignames, "(with --init) leave filenames unencrypted"
|
14
|
+
opt :storekey, "(with --init) store the key within the repository configuration file so it" \
|
15
|
+
" need not be entered with every update"
|
14
16
|
opt :update, "update encrypted repository (default operation)"
|
15
17
|
opt :recover, "recover files from an encrypted repository: KEY ENCDIR RECDIR", :type => :strings
|
16
18
|
opt :where, "specify source directory (default = current directory)", :type => :string
|
@@ -42,7 +44,7 @@ class LEncApp
|
|
42
44
|
if (a = options[:init])
|
43
45
|
Trollop::die("Expecting: KEY ENCDIR") if a.size != 2
|
44
46
|
pwd,encDir = a
|
45
|
-
r.create(options[:where], pwd, encDir, options[:orignames])
|
47
|
+
r.create(options[:where], pwd, encDir, options[:orignames], options[:storekey])
|
46
48
|
elsif (a = options[:recover])
|
47
49
|
Trollop::die("Expecting: KEY ENCDIR RECDIR") if a.size != 3
|
48
50
|
r.perform_recovery(a[0],a[1],a[2])
|
@@ -62,9 +64,20 @@ end
|
|
62
64
|
if __FILE__ == $0
|
63
65
|
args = ARGV
|
64
66
|
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
67
|
+
# if true && Dir.home.end_with?("/jeff")
|
68
|
+
# warn("trying special")
|
69
|
+
# bs = File.join(Dir.home,"Desktop/_testdirs_")
|
70
|
+
#
|
71
|
+
# if !File.file? "#{bs}/src/.lenc"
|
72
|
+
# s = "-w #{bs}/src -i onefishtwofishredfishbluefish #{bs}/encr"
|
73
|
+
# else
|
74
|
+
# s = "-w #{bs}/src"
|
75
|
+
# end
|
76
|
+
## s = "-h"
|
77
|
+
# args = s.split
|
78
|
+
# # args = "-h".split
|
79
|
+
# end
|
80
|
+
|
68
81
|
LEncApp.new().run(args)
|
69
82
|
end
|
70
83
|
|
data/lib/lenc/repo.rb
CHANGED
@@ -104,7 +104,7 @@ module LEnc
|
|
104
104
|
|
105
105
|
# During recovery, we will use the first file we encounter to
|
106
106
|
# verify if the supplied password is correct, and abort if not.
|
107
|
-
@
|
107
|
+
@recovery_pwd_verified = false
|
108
108
|
|
109
109
|
if options.size > 0
|
110
110
|
raise ArgumentError, "Unrecognized options: " + d2(options)
|
@@ -119,10 +119,11 @@ module LEnc
|
|
119
119
|
# @param enc_dir directory to store encrypted files; must not yet exist, and must
|
120
120
|
# not represent a directory lying within the repo_dir tree
|
121
121
|
# @param original_names if true, the filenames are not encrypted, only the file contents
|
122
|
-
#
|
122
|
+
# @param store_key if true, the key is written to the repository configuration file; otherwise,
|
123
|
+
# user must supply the key every time the repository is updated
|
123
124
|
# @raise ArgumentError if appropriate
|
124
125
|
#
|
125
|
-
def create(repo_dir, key, enc_dir, original_names=false)
|
126
|
+
def create(repo_dir, key, enc_dir, original_names=false, store_key=true)
|
126
127
|
raise IllegalStateException if @state != STATE_CLOSED
|
127
128
|
|
128
129
|
db = warndb 0
|
@@ -165,7 +166,19 @@ module LEnc
|
|
165
166
|
+ " is illegal"
|
166
167
|
end
|
167
168
|
|
168
|
-
|
169
|
+
if store_key
|
170
|
+
@confFile.set('key', key)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Construct a string that verifies the password is correct
|
174
|
+
en = MyAES.new(true, key )
|
175
|
+
en.finish("!!!")
|
176
|
+
verifier_string = en.flush
|
177
|
+
|
178
|
+
# Store key verifier as an array of bytes, to avoid nonprintable problems
|
179
|
+
vs2 = Base64.urlsafe_encode64(verifier_string)
|
180
|
+
@confFile.set('key_verifier', vs2)
|
181
|
+
|
169
182
|
|
170
183
|
# Create encryption directory
|
171
184
|
if File.exists?(edir)
|
@@ -173,12 +186,13 @@ module LEnc
|
|
173
186
|
"Encryption directory or file already exists: '#{edir}'"
|
174
187
|
end
|
175
188
|
|
176
|
-
@confFile.set('
|
189
|
+
@confFile.set('enc_dir', edir)
|
177
190
|
|
178
191
|
if not @dryrun
|
179
192
|
Dir.mkdir(edir)
|
180
193
|
@confFile.write()
|
181
194
|
end
|
195
|
+
|
182
196
|
end
|
183
197
|
|
184
198
|
|
@@ -186,11 +200,13 @@ module LEnc
|
|
186
200
|
#
|
187
201
|
# @param startDirectory directory lying within repository tree; if nil, uses
|
188
202
|
# current directory
|
189
|
-
#
|
203
|
+
# @param password repository password; if a password was stored with the repository
|
204
|
+
# when it was created, this parameter is ignored; if this parameter is null, and
|
205
|
+
# no password was stored with the repository, the user will be prompted for one
|
190
206
|
# @raise IllegalStateException if repository is already open
|
191
207
|
# @raise ArgumentError if directory doesn't exist, or does not lie in a repository
|
192
208
|
#
|
193
|
-
def open(startDirectory=nil)
|
209
|
+
def open(startDirectory=nil, password = nil)
|
194
210
|
db = warndb 0
|
195
211
|
!db || pr("Repo.open startDir=%s\n",d(startDirectory))
|
196
212
|
|
@@ -223,12 +239,30 @@ module LEnc
|
|
223
239
|
end
|
224
240
|
|
225
241
|
# Read values from configuration to instance vars
|
226
|
-
@encrDir = @confFile.val('
|
227
|
-
|
242
|
+
@encrDir = @confFile.val('enc_dir')
|
243
|
+
pwd = @confFile.val('key') || password
|
244
|
+
if !pwd
|
245
|
+
printf("Password: ")
|
246
|
+
pwd = gets
|
247
|
+
if pwd
|
248
|
+
pwd.strip!
|
249
|
+
pwd = nil if pwd.size == 0
|
250
|
+
end
|
251
|
+
if !pwd
|
252
|
+
raise DecryptionError, "No password given"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
228
256
|
@orignames = @confFile.val('orignames')
|
257
|
+
@encrKey = pwd
|
229
258
|
|
230
259
|
prepareKeys()
|
231
|
-
|
260
|
+
|
261
|
+
key_verifier = @confFile.val('key_verifier')
|
262
|
+
key_verifier = Base64.urlsafe_decode64(key_verifier)
|
263
|
+
|
264
|
+
verify_encrypt_pwd(pwd, key_verifier)
|
265
|
+
|
232
266
|
@state = STATE_OPEN
|
233
267
|
end
|
234
268
|
|
@@ -494,7 +528,7 @@ module LEnc
|
|
494
528
|
@confFile = nil
|
495
529
|
@repoBaseDir = nil
|
496
530
|
@encrDir = nil
|
497
|
-
@version =
|
531
|
+
@version = 2.0
|
498
532
|
@orignames = false
|
499
533
|
initIgnoreList()
|
500
534
|
end
|
@@ -612,14 +646,26 @@ module LEnc
|
|
612
646
|
raise DecryptionError(e)
|
613
647
|
end
|
614
648
|
|
615
|
-
|
649
|
+
set_recovery_pwd_verified()
|
616
650
|
|
617
651
|
return s
|
618
652
|
end
|
619
653
|
|
620
|
-
|
621
|
-
|
622
|
-
|
654
|
+
# Verify that a key is the correct password for this repository.
|
655
|
+
# @param key key to verify
|
656
|
+
# @param key_verifier an encrypted string that will decrypt correctly if the key is correct
|
657
|
+
def verify_encrypt_pwd(key, key_verifier)
|
658
|
+
db = warndb 0
|
659
|
+
!db || pr("verify_encrypt_pwd key %s, verifier %s\n",d(key),hex_dump_to_string(key_verifier))
|
660
|
+
|
661
|
+
if !MyAES.is_string_encrypted(key, key_verifier)
|
662
|
+
raise(DecryptionError, "#{key} is not the correct password for this repository")
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
def set_recovery_pwd_verified
|
667
|
+
if !@recovery_pwd_verified
|
668
|
+
@recovery_pwd_verified = true
|
623
669
|
end
|
624
670
|
end
|
625
671
|
|
@@ -881,7 +927,7 @@ module LEnc
|
|
881
927
|
rescue DecryptionError
|
882
928
|
encPath = File.absolute_path(File.join(encryptDir,f))
|
883
929
|
pth = rel_path(encPath, @inputDir)
|
884
|
-
if !@
|
930
|
+
if !@recovery_pwd_verified
|
885
931
|
raise(DecryptionError,"Wrong password (cannot decrypt filename #{pth})")
|
886
932
|
end
|
887
933
|
|
@@ -917,7 +963,7 @@ module LEnc
|
|
917
963
|
end
|
918
964
|
begin
|
919
965
|
convertFile(encrFullPath, TEMPFILENAME, false, showProgress)
|
920
|
-
|
966
|
+
set_recovery_pwd_verified()
|
921
967
|
if not @dryrun
|
922
968
|
if File.file?(origPath)
|
923
969
|
remove_file_or_dir(origPath)
|
@@ -926,7 +972,7 @@ module LEnc
|
|
926
972
|
end
|
927
973
|
rescue DecryptionError => e
|
928
974
|
|
929
|
-
if !@
|
975
|
+
if !@recovery_pwd_verified
|
930
976
|
raise(DecryptionError,"Wrong password (cannot decrypt file #{pth})")
|
931
977
|
end
|
932
978
|
|
data/lib/lenc/tools.rb
CHANGED
@@ -530,7 +530,8 @@ end
|
|
530
530
|
|
531
531
|
if defined? Test::Unit
|
532
532
|
|
533
|
-
#
|
533
|
+
# A simple extension to Ruby's Test::Unit class that provides
|
534
|
+
# suite-level setup/teardown methods.
|
534
535
|
#
|
535
536
|
# If test suite functionality is desired within a script,
|
536
537
|
# then require 'test/unit' before requiring 'tools.rb'.
|
@@ -568,8 +569,8 @@ if defined? Test::Unit
|
|
568
569
|
# 2) The base class implementations of method_/suite_xxx do nothing.
|
569
570
|
#
|
570
571
|
# 3) The number of test cases reported may be higher than you expect, since
|
571
|
-
# there are test methods defined by the TestSuite class to
|
572
|
-
# functionality.
|
572
|
+
# there are additional test methods defined by the TestSuite class to
|
573
|
+
# implement the suite setup / teardown functionality.
|
573
574
|
#
|
574
575
|
# 4) Avoid naming test methods that fall outside of test_01 ... test_zz.
|
575
576
|
#
|
data/test/test.rb
CHANGED
@@ -227,7 +227,7 @@ end
|
|
227
227
|
def create_repo
|
228
228
|
clean
|
229
229
|
rp = build_repo_obj
|
230
|
-
rp.create(@@sourceDir,@@key,@@encryptDir)
|
230
|
+
rp.create(@@sourceDir,@@key,@@encryptDir, true)
|
231
231
|
rp.close
|
232
232
|
rp
|
233
233
|
end
|
@@ -306,13 +306,13 @@ end
|
|
306
306
|
# Create repo using lencrypt program
|
307
307
|
def test_101_create_repo
|
308
308
|
clean
|
309
|
-
a = "-i #{@@key} #{@@encryptDir}"
|
309
|
+
a = "-i #{@@key} #{@@encryptDir} --storekey"
|
310
310
|
ex(a)
|
311
311
|
end
|
312
312
|
|
313
313
|
def test_101_update_repo_with_verify
|
314
314
|
clean
|
315
|
-
ex("-i #{@@key} #{@@encryptDir}")
|
315
|
+
ex("-i #{@@key} #{@@encryptDir} --storekey")
|
316
316
|
rp = build_repo_obj
|
317
317
|
rp.open(@@sourceDir)
|
318
318
|
rp.perform_update(true)
|