forked from micheleberetta98/freya-fs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmetadata.py
105 lines (84 loc) · 3.19 KB
/
metadata.py
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
import base64
import getpass
import json
import os
import sys
import nacl.pwhash
import nacl.secret
import nacl.utils
class Info:
def __init__(self, key=None, iv=None, size=None):
self.key = key if key is not None else nacl.utils.random(16)
self.iv = iv if iv is not None else nacl.utils.random(16)
self.size = size if size is not None else 0
class Metadata:
def __init__(self, path):
self.path = path
pw = getpass.getpass("Password: ").encode("utf-8")
if not os.path.isfile(path):
confirm = getpass.getpass("Confirm password: ").encode("utf-8")
if pw != confirm:
print("ERROR: Your password and confirmation password do not match.")
sys.exit()
salt = b'\xd0\xe1\x03\xc2Z<R\xaf]\xfe\xd5\xbf\xf8u|\x8f'
# Generate the key
kdf = nacl.pwhash.argon2id.kdf
self.key = kdf(nacl.secret.SecretBox.KEY_SIZE, pw, salt)
self.metadata = {}
if os.path.isfile(path):
# Read the encrypted metadata
with open(path, 'r') as f:
content = f.read()
encrypted = base64.b64decode(content)
# Decrypt metadata
box = nacl.secret.SecretBox(self.key)
try:
plaintext = box.decrypt(encrypted)
except nacl.exceptions.CryptoError:
print("ERROR: Wrong password.")
sys.exit()
# Read JSON metadata
read = json.loads(plaintext)
# Recreate metadata data structure
for path, info in read.items():
key = base64.b64decode(info['key'].encode("ascii"))
iv = base64.b64decode(info['iv'].encode("ascii"))
self.metadata[path] = Info(key, iv, info['size'])
def __contains__(self, path):
return path in self.metadata
def __getitem__(self, path):
return self.metadata[path]
def add(self, path):
info = Info()
self.metadata[path] = info
return info.key, info.iv
def update(self, path, size):
self.metadata[path].size = size
def renamedir(self, old, new):
for path in list(self.metadata):
if path.startswith(old):
to = path.replace(old, new, 1)
self.rename(path, to)
def rename(self, old, new):
self.metadata[new] = self.metadata[old]
del self.metadata[old]
def remove(self, path):
del self.metadata[path]
def dump(self):
# Convert metadata to JSON-like format
to_write = {}
for path, info in self.metadata.items():
to_write[path] = {
'key': base64.b64encode(info.key).decode("ascii"),
'iv': base64.b64encode(info.iv).decode("ascii"),
'size': info.size
}
# Produce JSON string
plaintext = json.dumps(to_write)
# Encrypt metadata
box = nacl.secret.SecretBox(self.key)
encrypted = box.encrypt(plaintext.encode("utf-8"))
# Store encrypted metadata
with open(self.path, 'w') as f:
content = base64.b64encode(encrypted).decode("ascii")
f.write(content)