lenc 1.1.3 → 1.2.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/README.md +40 -10
- data/bin/encr +5 -0
- data/lib/lenc.rb +1 -0
- data/lib/lenc/aes.rb +127 -52
- data/lib/lenc/encr.rb +64 -0
- data/lib/lenc/lencrypt.rb +13 -21
- data/lib/lenc/repo.rb +340 -160
- data/lib/lenc/tools.rb +22 -0
- data/test/test.rb +100 -32
- metadata +24 -5
data/lib/lenc/repo.rb
CHANGED
@@ -14,21 +14,15 @@ module RepoInternal
|
|
14
14
|
def initialize
|
15
15
|
@dirOnly, @negated, @pathMode, @rexp, @dbPattern = nil
|
16
16
|
end
|
17
|
-
|
18
|
-
def inspect
|
19
|
-
to_s
|
20
|
-
end
|
21
|
-
def to_s
|
22
|
-
s = "Ign<"
|
23
|
-
s << df(@dirOnly,"dirOnly") << "expr: " << @rexp.to_s << ">"
|
24
|
-
s
|
25
|
-
end
|
26
17
|
end
|
27
18
|
end
|
28
19
|
|
29
20
|
|
30
21
|
module LEnc
|
31
22
|
|
23
|
+
KEY_LEN_MIN = 8
|
24
|
+
KEY_LEN_MAX = 56
|
25
|
+
|
32
26
|
class DecryptionError < Exception
|
33
27
|
end
|
34
28
|
|
@@ -113,19 +107,51 @@ module LEnc
|
|
113
107
|
end
|
114
108
|
end
|
115
109
|
|
116
|
-
|
110
|
+
# If a password hasn't been defined, ask user for one.
|
111
|
+
# Also, pad password out to some minimum size
|
112
|
+
#
|
113
|
+
def define_password(pwd)
|
114
|
+
if !pwd
|
115
|
+
|
116
|
+
if true
|
117
|
+
|
118
|
+
# Use the 'highline' gem to allow typing password without echo to screen
|
119
|
+
require 'rubygems'
|
120
|
+
require 'highline/import'
|
121
|
+
pwd = ask("Password: ") {|q| q.echo = false}
|
122
|
+
|
123
|
+
else
|
124
|
+
printf("Password: ")
|
125
|
+
pwd = gets
|
126
|
+
end
|
127
|
+
|
128
|
+
if pwd
|
129
|
+
pwd.strip!
|
130
|
+
pwd = nil if pwd.size == 0
|
131
|
+
end
|
132
|
+
if !pwd
|
133
|
+
raise DecryptionError, "No password given"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
while pwd.size < KEY_LEN_MIN
|
138
|
+
pwd *= 2
|
139
|
+
end
|
140
|
+
pwd
|
141
|
+
end
|
142
|
+
|
143
|
+
|
117
144
|
# Create a new encryption repository, and open it.
|
118
145
|
#
|
119
|
-
# @param repo_dir
|
120
|
-
# @param key encryption key, a string from
|
121
|
-
# @param enc_dir
|
122
|
-
#
|
123
|
-
#
|
124
|
-
# @param
|
125
|
-
# user must supply the key every time the repository is updated
|
146
|
+
# @param repo_dir directory of new repository (nil for current directory)
|
147
|
+
# @param key encryption key, a string from KEY_LEN_MIN to KEY_LEN_MAX characters in length
|
148
|
+
# @param enc_dir if not nil, directory to store encrypted files; must not yet exist, and must
|
149
|
+
# not represent a directory lying within the repo_dir tree;
|
150
|
+
# if nil, repository will be encrypted in-place
|
151
|
+
# @param original_names if true, the filenames are not encrypted, only the file contents
|
126
152
|
# @raise ArgumentError if appropriate
|
127
153
|
#
|
128
|
-
def create(repo_dir, key, enc_dir, original_names=false
|
154
|
+
def create(repo_dir, key, enc_dir, original_names=false)
|
129
155
|
raise IllegalStateException if @state != STATE_CLOSED
|
130
156
|
|
131
157
|
db = warndb 0
|
@@ -149,28 +175,34 @@ module LEnc
|
|
149
175
|
|
150
176
|
@orignames = original_names
|
151
177
|
|
152
|
-
edir = File.absolute_path(enc_dir)
|
153
178
|
@confFile.set('orignames', @orignames)
|
154
179
|
|
155
|
-
if @verbosity >=
|
180
|
+
if @verbosity >= 1
|
156
181
|
pr("Creating encryption repository %s\n", @confFile.path)
|
157
182
|
end
|
158
183
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
184
|
+
edir = nil
|
185
|
+
if enc_dir
|
186
|
+
edir = File.absolute_path(enc_dir)
|
187
|
+
pp = verifyDirsDistinct([repo_dir, edir])
|
188
|
+
|
189
|
+
if pp
|
190
|
+
raise ArgumentError, "Directory " + pp[0] + \
|
191
|
+
" is a subdirectory of " + pp[1]
|
192
|
+
end
|
165
193
|
|
166
|
-
|
194
|
+
if File.exists?(edir)
|
195
|
+
raise ArgumentError, \
|
196
|
+
"Encryption directory or file already exists: '#{edir}'"
|
197
|
+
end
|
198
|
+
@confFile.set('enc_dir', edir)
|
199
|
+
end
|
200
|
+
|
201
|
+
key = define_password(key)
|
202
|
+
if (key.size < KEY_LEN_MIN || key.size > KEY_LEN_MAX)
|
167
203
|
raise ArgumentError, "Password length " + key.size.to_s \
|
168
204
|
+ " is illegal"
|
169
205
|
end
|
170
|
-
|
171
|
-
if store_key
|
172
|
-
@confFile.set('key', key)
|
173
|
-
end
|
174
206
|
|
175
207
|
# Construct a string that verifies the password is correct
|
176
208
|
en = MyAES.new(true, key )
|
@@ -181,17 +213,10 @@ module LEnc
|
|
181
213
|
vs2 = Base64.urlsafe_encode64(verifier_string)
|
182
214
|
@confFile.set('key_verifier', vs2)
|
183
215
|
|
184
|
-
|
185
|
-
# Create encryption directory
|
186
|
-
if File.exists?(edir)
|
187
|
-
raise ArgumentError, \
|
188
|
-
"Encryption directory or file already exists: '#{edir}'"
|
189
|
-
end
|
190
|
-
|
191
|
-
@confFile.set('enc_dir', edir)
|
192
|
-
|
193
216
|
if not @dryrun
|
194
|
-
|
217
|
+
if edir
|
218
|
+
Dir.mkdir(edir)
|
219
|
+
end
|
195
220
|
@confFile.write()
|
196
221
|
end
|
197
222
|
|
@@ -208,9 +233,9 @@ module LEnc
|
|
208
233
|
# @raise IllegalStateException if repository is already open
|
209
234
|
# @raise ArgumentError if directory doesn't exist, or does not lie in a repository
|
210
235
|
#
|
211
|
-
def open(startDirectory=nil, password = nil)
|
236
|
+
def open(startDirectory = nil, password = nil)
|
212
237
|
db = warndb 0
|
213
|
-
!db || pr("Repo.open startDir=%s\n",d(startDirectory))
|
238
|
+
!db || pr("Repo.open startDir=%s, password=%s\n",d(startDirectory),d(password))
|
214
239
|
|
215
240
|
raise IllegalStateException if @state != STATE_CLOSED
|
216
241
|
|
@@ -242,18 +267,7 @@ module LEnc
|
|
242
267
|
|
243
268
|
# Read values from configuration to instance vars
|
244
269
|
@encrDir = @confFile.val('enc_dir')
|
245
|
-
pwd =
|
246
|
-
if !pwd
|
247
|
-
printf("Password: ")
|
248
|
-
pwd = gets
|
249
|
-
if pwd
|
250
|
-
pwd.strip!
|
251
|
-
pwd = nil if pwd.size == 0
|
252
|
-
end
|
253
|
-
if !pwd
|
254
|
-
raise DecryptionError, "No password given"
|
255
|
-
end
|
256
|
-
end
|
270
|
+
pwd = define_password(password)
|
257
271
|
|
258
272
|
@orignames = @confFile.val('orignames')
|
259
273
|
@encrKey = pwd
|
@@ -277,29 +291,57 @@ module LEnc
|
|
277
291
|
reset_state()
|
278
292
|
end
|
279
293
|
|
280
|
-
#
|
294
|
+
# Encrypt repository's files.
|
295
|
+
# If repo is dual, finds files that need to be re-encrypted and does so.
|
296
|
+
# If singular, encrypts all those files that are not yet encrypted.
|
297
|
+
#
|
281
298
|
# Repository must be open.
|
282
|
-
#
|
283
|
-
# @param verifyEncryption for debug purposes; if true, each file that is encrypted is tested to confirm that
|
284
|
-
# it decrypts correctly.
|
285
299
|
#
|
286
300
|
# @raise IllegalStateException if repository isn't open.
|
287
301
|
#
|
288
|
-
def
|
302
|
+
def perform_encrypt()
|
289
303
|
raise IllegalStateException if @state != STATE_OPEN
|
290
304
|
|
291
|
-
|
305
|
+
enc_dir = @encrDir
|
306
|
+
if in_place?
|
307
|
+
enc_dir = @repoBaseDir
|
308
|
+
end
|
309
|
+
|
310
|
+
setInputOutputDirs(@startDir,enc_dir)
|
292
311
|
|
293
|
-
|
312
|
+
# If encrypting singular repository, ignore all .lencignore files
|
313
|
+
if in_place?
|
314
|
+
pushIgnoreList('', Repo.parseIgnoreList(".lencignore"))
|
315
|
+
end
|
294
316
|
|
295
317
|
puts("Encrypting...") if @verbosity >= 1
|
296
318
|
|
297
319
|
begin
|
298
|
-
|
320
|
+
ecrypt_directory_contents(@repoBaseDir, enc_dir)
|
299
321
|
puts("...done.") if @verbosity >= 1
|
300
322
|
end
|
301
323
|
end
|
302
324
|
|
325
|
+
# Decrypt files within singular repository.
|
326
|
+
# Repository must be open.
|
327
|
+
#
|
328
|
+
# @raise IllegalStateException if repository isn't open.
|
329
|
+
#
|
330
|
+
def perform_decrypt()
|
331
|
+
raise IllegalStateException if (@state != STATE_OPEN || !in_place?)
|
332
|
+
|
333
|
+
enc_dir = @repoBaseDir
|
334
|
+
|
335
|
+
setInputOutputDirs(enc_dir,enc_dir)
|
336
|
+
|
337
|
+
puts("Decrypting...") if @verbosity >= 1
|
338
|
+
|
339
|
+
begin
|
340
|
+
decrypt_directory_contents(enc_dir)
|
341
|
+
puts("...done.") if @verbosity >= 1
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
303
345
|
|
304
346
|
# Recover files from a repository's encryption folder.
|
305
347
|
#
|
@@ -316,6 +358,7 @@ module LEnc
|
|
316
358
|
|
317
359
|
ret = nil
|
318
360
|
|
361
|
+
key = define_password(key)
|
319
362
|
@encrKey = key
|
320
363
|
|
321
364
|
rd = File.absolute_path(rDir)
|
@@ -526,6 +569,10 @@ module LEnc
|
|
526
569
|
initIgnoreList()
|
527
570
|
end
|
528
571
|
|
572
|
+
def in_place?
|
573
|
+
!@encrDir
|
574
|
+
end
|
575
|
+
|
529
576
|
# Construct the initial ignore list.
|
530
577
|
#
|
531
578
|
# We maintain a stack of these lists, and subsequent operations can push and pop
|
@@ -580,7 +627,9 @@ module LEnc
|
|
580
627
|
#
|
581
628
|
# Encrypted filenames are also given a prefix to distinguish them
|
582
629
|
# from files not created by this program (or filenames that have not been encrypted)
|
583
|
-
#
|
630
|
+
# If the given filename already has this prefix, it is assumed that the filename has
|
631
|
+
# already been encrypted.
|
632
|
+
#
|
584
633
|
# If filenames are not encrypted in this repository, returns filename unchanged.
|
585
634
|
#
|
586
635
|
def encryptFilename(s)
|
@@ -590,6 +639,8 @@ module LEnc
|
|
590
639
|
|
591
640
|
return s if @orignames
|
592
641
|
|
642
|
+
return s if s.start_with?(ENCRFILENAMEPREFIX)
|
643
|
+
|
593
644
|
nonce = OpenSSL::Digest::SHA1.new(s).digest
|
594
645
|
!db || pr(" SHA1 applied, nonce=%s\n",dt(nonce))
|
595
646
|
!db || hex_dump(nonce,"SHA1 nonce")
|
@@ -645,7 +696,7 @@ module LEnc
|
|
645
696
|
!db || pr("verify_encrypt_pwd key %s, verifier %s\n",d(key),hex_dump_to_string(key_verifier))
|
646
697
|
|
647
698
|
if !MyAES.is_string_encrypted(key, key_verifier)
|
648
|
-
raise(DecryptionError, "
|
699
|
+
raise(DecryptionError, "incorrect password")
|
649
700
|
end
|
650
701
|
end
|
651
702
|
|
@@ -674,14 +725,23 @@ module LEnc
|
|
674
725
|
|
675
726
|
|
676
727
|
# Update a single source file if necessary (not a directory)
|
728
|
+
# @param sourceFile absolute path of source file
|
729
|
+
# @param encryptFile absolute path of encrypted file
|
730
|
+
#
|
677
731
|
def encrypt_file(sourceFile, encryptFile)
|
678
732
|
|
679
|
-
|
733
|
+
db = warndb 0
|
734
|
+
!db || pr("encrypt_file\n source=#{sourceFile}\n encrypt=#{encryptFile}\n")
|
735
|
+
|
736
|
+
# If encrypted file is a directory, delete it. This can only occur if it's a dual repository.
|
680
737
|
if File.directory?(encryptFile)
|
738
|
+
raise IllegalStateError if in_place?
|
739
|
+
|
681
740
|
pth = rel_path(encryptFile, @outputDir)
|
682
741
|
|
683
742
|
if @verbosity >= 1
|
684
|
-
msg = "Encrypting file " + rel_path(sourceFile, @inputDir)
|
743
|
+
msg = "Encrypting file " + rel_path(sourceFile, @inputDir) \
|
744
|
+
+ " is overwriting existing directory: " + pth
|
685
745
|
end
|
686
746
|
|
687
747
|
if not @dryrun
|
@@ -690,9 +750,9 @@ module LEnc
|
|
690
750
|
end
|
691
751
|
|
692
752
|
# Determine if existing encrypted version exists
|
693
|
-
# and is up to date
|
694
|
-
mustUpdate = (not File.file?(encryptFile)) \
|
695
|
-
or (File.mtime(encryptFile) < File.mtime(sourceFile))
|
753
|
+
# and is up to date; only if not in-place
|
754
|
+
mustUpdate = in_place? || ((not File.file?(encryptFile)) \
|
755
|
+
or (File.mtime(encryptFile) < File.mtime(sourceFile)))
|
696
756
|
|
697
757
|
if mustUpdate
|
698
758
|
showProgress = (@verbosity >= 0)
|
@@ -702,38 +762,36 @@ module LEnc
|
|
702
762
|
pr("%s", srcDisp)
|
703
763
|
end
|
704
764
|
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
if @verifyEncryption
|
710
|
-
# Verify that the original and decoded files are identical
|
711
|
-
decoded_file = convertFile(encryptFile, false, showProgress, true)
|
712
|
-
files_match = FileUtils.compare_file(sourceFile, decoded_file.path)
|
713
|
-
decoded_file.unlink
|
714
|
-
|
715
|
-
if !files_match
|
716
|
-
delete_file_or_dir(encryptFile)
|
717
|
-
raise EncryptionVerificationException, \
|
718
|
-
"File '#{srcDisp}' did not encrypt/decrypt correctly"
|
719
|
-
|
720
|
-
end
|
721
|
-
if @verbosity >= 0
|
722
|
-
pr(" (file #{srcDisp} encrypted correctly)\n")
|
723
|
-
end
|
765
|
+
temp_enc_path = convertFile(sourceFile, true, showProgress)
|
766
|
+
if not @dryrun
|
767
|
+
FileUtils.mv(temp_enc_path, encryptFile)
|
724
768
|
end
|
769
|
+
|
725
770
|
end
|
726
771
|
end
|
727
772
|
|
728
773
|
# Determine if a file matches one of the expressions in the ignore stack.
|
729
774
|
# Searches the stack from top to bottom (i.e., the outermost elements are examined last)
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
775
|
+
# @param name_only filename, without path
|
776
|
+
# @param full_path full path of file
|
777
|
+
#
|
778
|
+
def should_file_be_ignored(name_only, full_path)
|
779
|
+
db = warndb 0
|
780
|
+
!db || pr("should_file_be_ignored? #{name_only}\n")
|
781
|
+
|
782
|
+
# Let f2 be the filename including the directories corresponding
|
783
|
+
# to the ignore stack
|
784
|
+
|
785
|
+
f2 = name_only
|
786
|
+
|
734
787
|
@ignoreStack.reverse.each do |dir,ients|
|
735
788
|
ients.each do |ient|
|
736
|
-
|
789
|
+
|
790
|
+
if ient.dirOnly && !File.directory?(full_path)
|
791
|
+
next
|
792
|
+
end
|
793
|
+
|
794
|
+
fArg = ient.pathMode ? f2 : name_only
|
737
795
|
|
738
796
|
matches = ient.rexp.match(fArg)
|
739
797
|
!db || pr(" ent path=#{ient.pathMode} rexp=#{ient.rexp} neg=#{ient.negated} matches=#{matches}\n")
|
@@ -742,7 +800,7 @@ module LEnc
|
|
742
800
|
return !ient.negated
|
743
801
|
end
|
744
802
|
end
|
745
|
-
f2 = dir
|
803
|
+
f2 = File.join(dir,f2)
|
746
804
|
end
|
747
805
|
|
748
806
|
return false
|
@@ -760,8 +818,14 @@ module LEnc
|
|
760
818
|
|
761
819
|
# Examine all files in repository; reencrypt those that have changed
|
762
820
|
# (by comparing their time stamps with the time stamps of the encyrypted versions)
|
763
|
-
|
764
|
-
|
821
|
+
#
|
822
|
+
# @param sourceDir absolute path of source directory
|
823
|
+
# @param encryptDir absolute path of encryption directory
|
824
|
+
#
|
825
|
+
def ecrypt_directory_contents(sourceDir, encryptDir)
|
826
|
+
|
827
|
+
db = (warndb 0)
|
828
|
+
|
765
829
|
!db || pr("\n\nencryptDir\n %s =>\n %s\n",d(sourceDir),d(encryptDir))
|
766
830
|
|
767
831
|
# Add contents of .lencignore to stack. If none exists, treat as if empty
|
@@ -776,10 +840,14 @@ module LEnc
|
|
776
840
|
# Create set of encrypted filenames that belong to this directory, so
|
777
841
|
# we can delete encrypted versions of files that are no longer in the source
|
778
842
|
# directory.
|
779
|
-
|
843
|
+
# Don't do this if repo is in-place.
|
844
|
+
encFilenameSet = nil
|
845
|
+
if !in_place?
|
846
|
+
in_place? || encFilenameSet = Set.new
|
847
|
+
end
|
780
848
|
|
781
849
|
# If no encrypted directory exists, create one
|
782
|
-
if
|
850
|
+
if !in_place? && !File.directory?(encryptDir)
|
783
851
|
if @verbosity >= 1
|
784
852
|
puts("Creating encrypted directory: " + d(rel_path(encryptDir, @outputDir)))
|
785
853
|
end
|
@@ -805,9 +873,6 @@ module LEnc
|
|
805
873
|
# Convert string to ASCII-8BIT encoding.
|
806
874
|
f = to_ascii8(f2)
|
807
875
|
|
808
|
-
!db || pr(" testing if file should be ignored: #{f}\n")
|
809
|
-
ignore = shouldFileBeIgnored(f)
|
810
|
-
|
811
876
|
filePath = File.join(sourceDir,f)
|
812
877
|
|
813
878
|
if File.symlink?(filePath)
|
@@ -817,81 +882,198 @@ module LEnc
|
|
817
882
|
next
|
818
883
|
end
|
819
884
|
|
820
|
-
if
|
885
|
+
!db || pr(" testing if file should be ignored: #{f}\n")
|
886
|
+
if should_file_be_ignored(f, filePath)
|
821
887
|
!db || pr("(ignoring %s)\n", rel_path(filePath, @inputDir)) if @verbosity >= 1
|
822
888
|
next
|
823
889
|
end
|
824
890
|
|
825
|
-
|
826
|
-
|
827
|
-
|
891
|
+
# If we're doing in-place encryption, file is not a directory, and it's already encrypted, ignore
|
892
|
+
if in_place?
|
893
|
+
next if (!File.directory?(filePath) && f.start_with?(ENCRFILENAMEPREFIX))
|
894
|
+
next if (@orignames && MyAES.is_file_encrypted(@encrKey,filePath))
|
895
|
+
else
|
896
|
+
if f.start_with?(ENCRFILENAMEPREFIX)
|
897
|
+
if @verbosity >= 0
|
898
|
+
pr("(Omitting source file / dir with name that looks encrypted: #{d(f)})\n")
|
899
|
+
end
|
900
|
+
next
|
828
901
|
end
|
829
902
|
end
|
830
903
|
|
831
|
-
encrName = encryptFilename(f)
|
832
|
-
!db || pr(" encrypted filename %s => %s\n",d(f),d(encrName))
|
833
904
|
|
834
|
-
if
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
905
|
+
if !in_place?
|
906
|
+
encrName = encryptFilename(f)
|
907
|
+
!db || pr(" encrypted filename %s => %s\n",d(f),d(encrName))
|
908
|
+
encFilenameSet.add(encrName)
|
909
|
+
encrPath = File.join(encryptDir, encrName)
|
910
|
+
|
911
|
+
if File.directory?(filePath)
|
912
|
+
ecrypt_directory_contents(filePath, encrPath)
|
913
|
+
else
|
914
|
+
!db || pr("...attempting to encrypt file #{filePath} to #{encrPath}...\n")
|
915
|
+
encrypt_file(filePath, encrPath)
|
843
916
|
end
|
844
|
-
pr(" (filename #{f} encrypted correctly)\n") if @verbosity >= 0
|
845
|
-
end
|
846
|
-
|
847
|
-
encFilenameSet.add(encrName)
|
848
|
-
encrPath = File.join(encryptDir, encrName)
|
849
917
|
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
918
|
+
else
|
919
|
+
|
920
|
+
encrPath = filePath
|
921
|
+
if !@origNames
|
922
|
+
encrName = encryptFilename(f)
|
923
|
+
encrPath = File.join(sourceDir, encrName)
|
924
|
+
end
|
925
|
+
|
926
|
+
if File.directory?(filePath)
|
927
|
+
ecrypt_directory_contents(filePath, filePath)
|
928
|
+
# Rename the directory to its encrypted form, if necessary
|
929
|
+
if filePath != encrPath
|
930
|
+
!db || pr(" renaming now-encrypted file from\n #{filePath}\n to\n #{encrPath}\n")
|
931
|
+
FileUtils.mv(filePath,encrPath)
|
932
|
+
end
|
933
|
+
else
|
934
|
+
encrypt_file(filePath, encrPath)
|
935
|
+
# Delete unencrypted file, if not using original names
|
936
|
+
if !@orignames && !@dryrun
|
937
|
+
FileUtils.rm(filePath)
|
938
|
+
end
|
939
|
+
end
|
855
940
|
end
|
941
|
+
|
856
942
|
end
|
857
943
|
|
858
944
|
# Truncate global ignore list to original length
|
859
945
|
popIgnoreList()
|
860
946
|
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
947
|
+
if !in_place?
|
948
|
+
# Examine every file in encrypted directory; delete those that don't correspond to source dir
|
949
|
+
|
950
|
+
# (if doing dry run, encrypt dir may not exist)
|
951
|
+
|
952
|
+
!db || pr("examining files in encrypted dir #{encryptDir} to delete ones that don't belong\n")
|
953
|
+
if File.directory?(encryptDir)
|
954
|
+
dire = dir_entries(encryptDir)
|
955
|
+
else
|
956
|
+
dire = []
|
957
|
+
end
|
958
|
+
|
959
|
+
dire.each do |f|
|
960
|
+
next if not f.start_with?(ENCRFILENAMEPREFIX)
|
961
|
+
|
962
|
+
if not encFilenameSet.member? f
|
963
|
+
begin
|
964
|
+
orphanOrigName = decryptFilename(f)
|
965
|
+
next if !orphanOrigName
|
966
|
+
orphanPath = File.join(encryptDir, f)
|
967
|
+
if @verbosity >= 1
|
968
|
+
printf("Removing encrypted version of missing (or ignored) file " \
|
969
|
+
+ rel_path(File.join(sourceDir, orphanOrigName), @inputDir) + ": " + orphanPath)
|
970
|
+
end
|
971
|
+
if !@dryrun
|
972
|
+
remove_file_or_dir(orphanPath)
|
973
|
+
end
|
974
|
+
rescue DecryptionError
|
975
|
+
# ignore...
|
976
|
+
end
|
977
|
+
end
|
978
|
+
end
|
870
979
|
end
|
980
|
+
end
|
981
|
+
|
982
|
+
# Decrypt all files recursively within singular repository subdirectory
|
983
|
+
#
|
984
|
+
# @param encr_dir_path absolute path of encrypted directory
|
985
|
+
# @param decr_dir_name absolute path of directory after decrypting (used for display purposes only);
|
986
|
+
# if nil, uses encrypted directory name
|
987
|
+
#
|
988
|
+
def decrypt_directory_contents(encr_dir_path,decr_dir_name = nil)
|
989
|
+
|
990
|
+
decr_dir_name ||= encr_dir_path
|
871
991
|
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
992
|
+
db = warndb 0
|
993
|
+
|
994
|
+
!db || pr("\n\ndecrypt_directory_contents: %s (orignames=#{@orignames})\n",d(encr_dir_path))
|
995
|
+
|
996
|
+
# Examine each file in source dir
|
997
|
+
dirc = dir_entries(encr_dir_path)
|
998
|
+
|
999
|
+
dirc.each do |f2|
|
1000
|
+
# Convert string to ASCII-8BIT encoding.
|
1001
|
+
f = to_ascii8(f2)
|
1002
|
+
|
1003
|
+
filePath = File.join(encr_dir_path,f)
|
1004
|
+
if File.symlink?(filePath)
|
1005
|
+
if @verbosity >= 0
|
1006
|
+
pr("Omitting symlink file '#{rel_path(filePath,@inputDir)}'\n")
|
1007
|
+
end
|
1008
|
+
next
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
!db || pr(" filePath=#{filePath}\n")
|
1012
|
+
decrPath = filePath
|
1013
|
+
decrName = f
|
1014
|
+
if @orignames
|
1015
|
+
if File.file?(filePath)
|
1016
|
+
if !MyAES.is_file_encrypted(@encrKey,filePath)
|
1017
|
+
!db || pr(" file is not encrypted, skipping\n")
|
1018
|
+
next
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
else
|
1022
|
+
next if !f.start_with?(ENCRFILENAMEPREFIX)
|
876
1023
|
begin
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
1024
|
+
decrName = decryptFilename(f)
|
1025
|
+
decrPath = File.join(encr_dir_path, decrName)
|
1026
|
+
rescue DecryptionError => e
|
1027
|
+
puts "Unable to decrypt filename #{f}"
|
1028
|
+
next
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
if File.directory?(filePath)
|
1033
|
+
decrypt_directory_contents(filePath, File.join(decr_dir_name, decrName))
|
1034
|
+
# Rename the directory to its decrypted form, if filenames are to be encrypted
|
1035
|
+
if !@orignames
|
1036
|
+
raise ArgumentError,"decrypted already exists: #{decrPath}" \
|
1037
|
+
if File.exist?(decrPath)
|
1038
|
+
|
1039
|
+
!db || pr(" renaming now-decrypted directory from\n #{filePath}\n to\n #{decrPath}\n")
|
1040
|
+
FileUtils.mv(filePath,decrPath)
|
1041
|
+
end
|
1042
|
+
else
|
1043
|
+
decrPathDisp = File.join(decr_dir_name,decrName)
|
1044
|
+
pth = rel_path(decrPathDisp, @repoBaseDir)
|
1045
|
+
showProgress = @verbosity >= 0
|
1046
|
+
if showProgress
|
1047
|
+
pr("%s", pth)
|
1048
|
+
end
|
1049
|
+
begin
|
1050
|
+
tmp_file = convertFile(filePath, false, showProgress)
|
1051
|
+
if !@orignames
|
1052
|
+
raise ArgumentError,"decrypted file or directory already exists: #{decrPath}" \
|
1053
|
+
if File.exist?(decrPath)
|
883
1054
|
end
|
884
|
-
|
885
|
-
|
1055
|
+
|
1056
|
+
if not @dryrun
|
1057
|
+
remove_file_or_dir(decrPath)
|
1058
|
+
FileUtils.mv(tmp_file.path, decrPath)
|
1059
|
+
# Remove the encrypted version, if we aren't using original names
|
1060
|
+
if !@orignames
|
1061
|
+
FileUtils.rm(filePath)
|
1062
|
+
end
|
1063
|
+
else
|
1064
|
+
tmp_file.unlink
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
rescue DecryptionError => e
|
1068
|
+
if @verbosity >= 0
|
1069
|
+
msg = "Unable to decrypt file: #{pth} (cause: #{e.message})"
|
1070
|
+
pr("\n%s\n", msg)
|
886
1071
|
end
|
887
|
-
rescue DecryptionError
|
888
|
-
# ignore...
|
889
1072
|
end
|
890
1073
|
end
|
891
1074
|
end
|
892
1075
|
end
|
893
|
-
|
894
|
-
|
1076
|
+
|
895
1077
|
# Decrypt all files (and, recursively, folders) within a directory to _recover folder
|
896
1078
|
def recover(encryptDir, recoverDir)
|
897
1079
|
db = warndb 0
|
@@ -916,7 +1098,10 @@ module LEnc
|
|
916
1098
|
dirc.each do |f|
|
917
1099
|
|
918
1100
|
!db || pr("...file=%s\n",d(f))
|
919
|
-
|
1101
|
+
|
1102
|
+
encrFullPath = File.join(encryptDir, f)
|
1103
|
+
|
1104
|
+
if should_file_be_ignored(f, encrFullPath)
|
920
1105
|
if @verbosity >= 1
|
921
1106
|
pr("(ignoring %s)\n", rel_path(f, @inputDir))
|
922
1107
|
end
|
@@ -943,7 +1128,6 @@ module LEnc
|
|
943
1128
|
origName = decrName
|
944
1129
|
end
|
945
1130
|
origPath = File.join(recoverDir, origName)
|
946
|
-
encrFullPath = File.join(encryptDir, f)
|
947
1131
|
|
948
1132
|
|
949
1133
|
# If decrypted version already exists, and is more recent than the
|
@@ -999,7 +1183,7 @@ module LEnc
|
|
999
1183
|
# @param encrypt true if encrypting
|
1000
1184
|
# @return temporary file containing modified file
|
1001
1185
|
#
|
1002
|
-
def convertFile(srcPath,encrypt, showProgress=false
|
1186
|
+
def convertFile(srcPath,encrypt, showProgress=false)
|
1003
1187
|
|
1004
1188
|
db = warndb 0
|
1005
1189
|
!db||pr("\n\n\n\nconvertFile\n %s\n",d(srcPath))
|
@@ -1023,10 +1207,6 @@ module LEnc
|
|
1023
1207
|
showDots = showProgress && chunksRemaining >= dotSize
|
1024
1208
|
!db || (showDots = false)
|
1025
1209
|
|
1026
|
-
if showDots and verifying
|
1027
|
-
pr(" verifying:")
|
1028
|
-
end
|
1029
|
-
|
1030
1210
|
!db || pr("\n encrKey=%s\n",dt(@encrKey))
|
1031
1211
|
|
1032
1212
|
bf = MyAES.new(encrypt, @encrKey)
|
@@ -1072,7 +1252,7 @@ module LEnc
|
|
1072
1252
|
|
1073
1253
|
end
|
1074
1254
|
|
1075
|
-
if showProgress
|
1255
|
+
if showProgress
|
1076
1256
|
pr("\n")
|
1077
1257
|
end
|
1078
1258
|
|