-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvirtual_file_system.go
403 lines (371 loc) · 14.7 KB
/
virtual_file_system.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
package dosktop
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"github.com/nwaples/rardecode"
"github.com/supercom32/dosktop/constants"
"github.com/supercom32/dosktop/internal/memory"
"github.com/yeka/zip"
"image"
"image/jpeg"
"image/png"
"io"
"io/ioutil"
"os"
"strings"
)
var virtualFileSystemArchive string
var virtualFileSystemPassword string
var virtualFileSystemArchiveType int
var virtualFileSystemEncryptionKey string
/*
GetScrambledPassword allows you to scramble a password with a simple
XOR algorithm. This allows a user to provide a password for a virtual file
system without having to store it in their own program as plaintext. To use
this feature, simply pass in your desired password and scramble key to
obtain your encoded password. This password can then be used to mount a virtual
file system provided you use the same scramble key to decode it. In addition,
the following information should be noted:
- This method is not designed for cryptographic security. It is simply
a method of transposing a password so that it is not viewable in
plaintext. While this can prevent casual users from extracting a password
hardcoded into a binary executable, it will not prevent a determined user
from obtaining it.
- The length and randomness of your 'password' will directly influence the
usefulness of your chosen 'scrambleKey'. Consider using a long and
random password if you want maximum effectiveness.
*/
func GetScrambledPassword(password string, scrambleKey string) string {
scrambledPassword := xorString(password, scrambleKey)
scrambledPassword = base64.StdEncoding.EncodeToString([]byte(scrambledPassword))
return scrambledPassword
}
/*
getUnscrambledPassword allows you to obtain an unscrambled password that was
created using the 'GetScrambledPassword' method. This is used by the virtual
file system to decode a password that was previously scrambled by the user in
order to avoid storing passwords in plaintext. In addition, the following
information should be noted:
- Password scrambling is not designed for cryptographic security. It is simply
a method of transposing a password so that it is not viewable in
plaintext. While this can prevent casual users from extracting a password
hardcoded into a binary executable, it will not prevent a determined user
from obtaining it.
*/
func getUnscrambledPassword(password string, scrambleKey string) string {
decodedString, _ := base64.StdEncoding.DecodeString(password)
unscrambledPassword := xorString(string(decodedString), scrambleKey)
return unscrambledPassword
}
/*
xorString allows you to perform an xor over a given string using a given
'scrambleKey'. This method is useful for when you don't want to store
something in plaintext. In addition, the following information should be
noted:
- String scrambling is not designed for cryptographic security. It is simply
a method of transposing data so that it is not viewable in plaintext. While
this can prevent casual users from extracting data hardcoded into a binary
executable, it will not prevent a determined user from obtaining it.
- While this method will properly XOR your string, it will not guarantee
that the obtained result is screen printable. Consider converting the
result to base64 if you require a value which can be correctly printed
and copied.
- The 'scrambleKey' will be MD5 hashed before being used. This ensures that
if any part of the scramble key has been modified, a totally different
XOR pattern will be generated. This precaution prevents partial decoding of
string data when some of the scramble key happens to be correct.
*/
func xorString(stringToXor string, scrambleKey string) string {
var xoredString string
hashedScrambleKey := getMD5Hash(scrambleKey)
for i := 0; i < len(stringToXor); i++ {
xoredString += string(stringToXor[i] ^ hashedScrambleKey[i % len(hashedScrambleKey)])
}
return xoredString
}
/*
getMD5Hash allows you to obtain an MD5 hash from a provided text string.
*/
func getMD5Hash(text string) string {
hash := md5.Sum([]byte(text))
return hex.EncodeToString(hash[:])
}
/*
mountVirtualFileSystem allows you to specify a virtual file system to mount.
A virtual file system is a ZIP or RAR archive that contains all files in which
you wish to access. This is useful since instead of distributing multiple
files and folders with your application, you can simply package everything
inside a virtual file system and just include that instead. Since files on
your local file system are accessed the same way as on a virtual file
system, you can use external resources for testing with and package them up
into your virtual file system when your ready for distribution. In addition,
the following information should be noted:
- If the archive you wish to use as your virtual file system is password
protected, you must provide it here at mount time. If the archive is not
password protected, then you can simply set this parameter as "" since it
will be ignored.
- If password protection is used, your password should not be considered
secure. Password protection is designed for basic resource integrity
purposes only. It will prevent casual modifications of your resources,
but nothing more.
- If you do not wish to store virtual file system passwords in plaintext,
you can provide a scrambled password instead. Simply pass in a
'scrambleKey' and your password will be decoded before use. If your
password does not require unscrambling, you can simply leave this parameter
as "". For more information on how to obtain a scrambled password, please
see the method 'GetScrambledPassword' for details.
- If for some reason the virtual file system was unable to be mounted, an
error will be returned so that your application can handle this case
appropriately.
*/
func mountVirtualFileSystem(archivePath string, password string, scrambleKey string) error {
err := isArchiveFormatZip(archivePath)
if err == nil {
virtualFileSystemArchiveType = constants.VirtualFileSystemZip
virtualFileSystemArchive = archivePath
virtualFileSystemPassword = password
virtualFileSystemEncryptionKey = scrambleKey
return err
}
err = isArchiveFormatRar(archivePath, password)
if err == nil {
virtualFileSystemArchiveType = constants.VirtualFileSystemRar
virtualFileSystemArchive = archivePath
virtualFileSystemPassword = password
virtualFileSystemEncryptionKey = scrambleKey
return err
}
err = errors.New(fmt.Sprintf("Failed to open or decode '%s'.", archivePath))
return err
}
/*
UnmountVirtualFileSystem allows you to reset the virtual file system to an
unmounted state. This is useful for when you want to access the physical
file system directly.
*/
func UnmountVirtualFileSystem() {
virtualFileSystemArchiveType = 0
virtualFileSystemArchive = ""
virtualFileSystemPassword = ""
virtualFileSystemEncryptionKey = ""
}
/*
isArchiveFormatZip allows you to detect if the provided archive is in ZIP
format or not.
*/
func isArchiveFormatZip(archivePath string) error {
readCloser, err := zip.OpenReader(archivePath)
if err == nil {
_ = readCloser.Close()
}
return err
}
/*
isArchiveFormatRar allows you to detect if the provided archive is in RAR
format or not.
*/
func isArchiveFormatRar(archivePath string, password string) error {
readCloser, err := rardecode.OpenReader(archivePath, password)
if err == nil {
_ = readCloser.Close()
}
return err
}
/*
getImageEntryFromFileSystem allows you to obtain an image entry from the
default file system. If you have a virtual file system mounted, then the
image file will be retrieved from it instead of your local file system.
In addition, the following information should be noted:
- If for some reason the requested image could not be obtained, an
error will be returned so that your application can handle this case
appropriately.
*/
func getImageEntryFromFileSystem(imageFile string) (memory.ImageEntryType, error){
imageData, err := getImageFromFileSystem(imageFile)
if err != nil {
return memory.NewImageEntry(), err
}
imageEntry := memory.NewImageEntry()
imageEntry.ImageData = imageData
return imageEntry, err
}
/*
getImageFromFileSystem allows you to obtain image data from a file from
the default file system. In addition, the following information should
be noted:
- If for some reason the requested image could not be obtained, an
error will be returned so that your application can handle this case
appropriately.
*/
func getImageFromFileSystem(imageFile string) (image.Image, error) {
var imageData image.Image
fileData, err := getFileDataFromFileSystem(imageFile)
if err != nil {
err = errors.New(fmt.Sprintf("Could not get image data from '%s': %s", imageFile, err.Error()))
return nil, err
}
if strings.HasSuffix(strings.ToLower(imageFile), ".jpg") || strings.HasSuffix(strings.ToLower(imageFile), ".jpeg"){
imageData, err = jpeg.Decode(bytes.NewReader(fileData))
}
if strings.HasSuffix(strings.ToLower(imageFile), ".png") {
imageData, err = png.Decode(bytes.NewReader(fileData))
}
if err != nil {
err = errors.New(fmt.Sprintf("Could not decode the image '%s': %s", imageFile, err.Error()))
return nil, err
}
return imageData, err
}
/*
getTextFromFileSystem allows you to obtain text data from a file from
the default file system. In addition, the following information should
be noted:
- If for some reason the requested text data could not be obtained, an
error will be returned so that your application can handle this case
appropriately.
*/
func getTextFromFileSystem(textFile string) (string, error) {
fileData, err := getFileDataFromFileSystem(textFile)
dataAsString := string(fileData)
return dataAsString, err
}
/*
getFileDataFromFileSystem allows you to get the contents of a file
from the default file system. If you have a virtual file system mounted, then
the file will be retrieved from it instead of your local file system. In
addition, the following information should be noted:
- If a file is being accessed from a password protected virtual file
system, then the password provided at mount time will be used to decrypt
the file automatically.
*/
func getFileDataFromFileSystem(fileName string) ([]byte, error) {
var fileData []byte
var err error
if virtualFileSystemArchiveType == constants.VirtualFileSystemZip {
fileData, err = getFileDataFromZipArchive(fileName)
return fileData, err
} else if virtualFileSystemArchiveType == constants.VirtualFileSystemRar {
fileData, err = getFileDataFromRarArchive(fileName)
return fileData, err
}
fileData, err = getFileDataFromLocalFileSystem(fileName)
if err != nil {
err = errors.New(fmt.Sprintf("Could not open the file '%s': %s", fileName, err.Error()))
}
return fileData, err
}
/*
getFileDataFromFileSystem allows you to get the contents of a file
from the local file system. If the contents of the file cannot be
retrieved, then an error is returned instead.
*/
func getFileDataFromLocalFileSystem(fileName string) ([]byte, error) {
var fileData []byte
fileReadCloser, err := os.Open(fileName)
if err != nil {
err = errors.New(fmt.Sprintf("Could not open the file '%s': %s", fileName, err.Error()))
return fileData, err
}
defer fileReadCloser.Close()
fileData, err = ioutil.ReadAll(fileReadCloser)
if err != nil {
err = errors.New(fmt.Sprintf("Could not read data from the file '%s': %s", fileName, err.Error()))
return fileData, err
}
return fileData, err
}
/*
getFileDataFromFileSystem allows you to get the contents of a file from a ZIP
archive. If the contents of the file cannot be retrieved, then an error is
returned instead. In addition, the following information should be noted:
- If a file is being accessed from a password protected virtual file
system, then the password provided at mount time will be used to decrypt
the file automatically.
*/
func getFileDataFromZipArchive(fileName string) ([]byte, error) {
var err error
var fileReadCloser io.ReadCloser
var fileData []byte
archivePassword := virtualFileSystemPassword
if virtualFileSystemEncryptionKey != "" {
archivePassword = getUnscrambledPassword(archivePassword, virtualFileSystemEncryptionKey)
}
archiveReadCloser, err := zip.OpenReader(virtualFileSystemArchive)
if err != nil {
err = errors.New(fmt.Sprintf("Could not open '%s': %s", virtualFileSystemArchive, err.Error()))
return fileData, err
}
defer archiveReadCloser.Close()
for _, currentFile := range archiveReadCloser.File {
if currentFile.Name == fileName {
if currentFile.IsEncrypted() {
currentFile.SetPassword(archivePassword)
}
fileReadCloser, err = currentFile.Open()
if err != nil {
err = errors.New(fmt.Sprintf("Could not open the file '%s' from the virtual file system: %s", fileName, err.Error()))
return fileData, err
}
fileData, err = ioutil.ReadAll(fileReadCloser)
if err != nil {
err = errors.New(fmt.Sprintf("Could not read data from the file '%s': %s", fileName, err.Error()))
return fileData, err
}
_ = fileReadCloser.Close()
}
}
if fileData == nil {
err = errors.New(fmt.Sprintf("Could not find the file '%s' from the virtual file system.", fileName))
return fileData, err
}
return fileData, err
}
/*
getFileDataFromFileSystem allows you to get the contents of a file from a RAR
archive. If the contents of the file cannot be retrieved, then an error is
returned instead. In addition, the following information should be noted:
- If a file is being accessed from a password protected virtual file
system, then the password provided at mount time will be used to decrypt
the file automatically. If a scramble key was provided at mount time, it will
be used to unscramble the password before being used.
*/
func getFileDataFromRarArchive(fileName string) ([]byte, error) {
var fileData []byte
archivePassword := virtualFileSystemPassword
if virtualFileSystemEncryptionKey != "" {
archivePassword = getUnscrambledPassword(archivePassword, virtualFileSystemEncryptionKey)
}
archiveReadCloser, err := rardecode.OpenReader(virtualFileSystemArchive, archivePassword)
if err != nil {
err = errors.New(fmt.Sprintf("Could not open '%s': %s", virtualFileSystemArchive, err.Error()))
return fileData, err
}
defer archiveReadCloser.Close()
for {
fileHeader, err := archiveReadCloser.Next()
if err == io.EOF {
// If EOF then we are done reading the archive.
if fileData == nil {
err = errors.New(fmt.Sprintf("Could not find file '%s' in archive '%s': %s", fileName, virtualFileSystemArchive, err.Error()))
}
return fileData, err
}
if err != nil {
err = errors.New(fmt.Sprintf("Failed while scanning archive '%s': %s", virtualFileSystemArchive, err.Error()))
return fileData, err
}
if fileHeader.Name == fileName {
fileData, err = ioutil.ReadAll(archiveReadCloser)
if err != nil {
err = errors.New(fmt.Sprintf("Could not read data from the file '%s': %s", fileName, err.Error()))
return fileData, err
}
return fileData, err
}
}
return fileData, err
}