22import argparse
33
44from getpass import getpass
5+ from urllib .parse import urlparse
56from Cryptodome .Random import get_random_bytes
67from base64 import (
78 b64decode ,
1112 Graph ,
1213 URIRef ,
1314)
14- from pod_helper import (
15+ from solidpod_helper import (
1516 gen_master_key ,
17+ gen_verify_key ,
1618 encrypt ,
19+ parse_ttl ,
1720 apps_terms ,
1821 path_pred ,
1922 iv_pred ,
20- session_key_pred ,
21- data_pred ,
23+ verify_key_pred ,
24+ indi_key_pred ,
25+ enc_data_pred ,
2226 server_path ,
2327)
2428
2529
26- def upload_file (file_content , file_path , server_url , master_key ):
30+ def upload_file (file_url , file_content , master_key ):
2731 # Encrypt file content
2832
2933 print ('Encrypt content ...' )
30- session_key = get_random_bytes (32 )
34+ indi_key = get_random_bytes (32 )
3135 data_iv = get_random_bytes (16 )
32- enc_data_b64 = encrypt (file_content , session_key , data_iv )
36+ enc_data_b64 = encrypt (file_content , indi_key , data_iv )
3337
3438 # Write encrypted content to file in POD
3539
3640 print ('Write encrypted content ...' )
41+
42+ r = urlparse (file_url )
43+ items = r .path .split ('/' )[1 :] # r.path: '/pod_name/app_name/data/data_file_path'
44+ assert len (items ) >= 4
45+ assert items [2 ] == 'data'
46+ relative_file_path = '/' .join (items [1 :])
3747 g = Graph ()
38- file_url = f'{ server_url } { file_path } '
3948 data_iv_b64 = b64encode (data_iv ).decode ('ascii' )
40- query = f'INSERT DATA {{<{ file_url } > <{ apps_terms } { path_pred } > "{ file_path } "; ' + \
49+ query = f'INSERT DATA {{<{ file_url } > <{ apps_terms } { path_pred } > "{ relative_file_path } "; ' + \
4150 f'<{ apps_terms } { iv_pred } > "{ data_iv_b64 } "; ' + \
42- f'<{ apps_terms } { data_pred } > "{ enc_data_b64 } ".}};'
51+ f'<{ apps_terms } { enc_data_pred } > "{ enc_data_b64 } ".}};'
4352 g .update (query )
4453 ttl_str = g .serialize (format = 'turtle' )
45- abs_path = f'{ server_path } { file_path } '
54+ abs_path = f'{ server_path } { "/" . join ( items ) } '
4655 with open (abs_path , 'w' ) as f :
4756 f .write (ttl_str )
4857
4958 # Add session key to ind-keys.ttl
5059
5160 print ('Add encrypted session key to ind-keys.ttl ...' )
52- add_session_key (file_path , server_url , session_key , master_key )
61+ add_indi_key (file_url , indi_key , master_key )
62+
63+ def add_indi_key (file_url , indi_key , master_key ):
64+ # Add encrypted individual key to ind-keys.ttl
5365
54- def add_session_key (file_path , server_url , session_key , master_key ):
55- # Add encrypted session key to ind-keys.ttl
66+ # Encrypt the individual key
5667
57- # Encrypt the session key
58- session_key_iv = get_random_bytes (16 )
59- session_key_b64 = b64encode (session_key ).decode ('ascii' )
60- enc_session_key_b64 = encrypt (session_key_b64 , master_key , session_key_iv )
68+ indi_key_iv = get_random_bytes (16 )
69+ indi_key_b64 = b64encode (indi_key ).decode ('ascii' )
70+ enc_indi_key_b64 = encrypt (indi_key_b64 , master_key , indi_key_iv )
6171
6272 # Parse the ind-keys.ttl file
63- items = file_path .split ('/' )
73+
74+ r = urlparse (file_url )
75+ items = r .path .split ('/' )[1 :] # r.path: '/pod_name/app_name/data/data_file_path'
6476 assert len (items ) >= 4
6577 pod_name = items [0 ]
6678 app_name = items [1 ]
79+ assert items [2 ] == 'data'
80+ server_url = f'{ r .scheme } ://{ r .netloc } '
81+ relative_file_path = '/' .join (items [1 :])
6782 ind_key_path = f'{ server_path } { pod_name } /{ app_name } /encryption/ind-keys.ttl'
6883 g = Graph ()
6984 g .parse (ind_key_path )
7085
7186 # Replace file:///POD_DIR prefix with server URL
72- file_url = f' { server_url } { file_path } '
87+
7388 for s , p , o in g :
7489 prefix = f'file://{ server_path } '
7590 if str (s ).startswith (prefix ):
@@ -80,15 +95,16 @@ def add_session_key(file_path, server_url, session_key, master_key):
8095 if str (s ) == file_url :
8196 g .remove ((s , p , o ))
8297
83- # Add encrypted session key to ind-keys.ttl
84- session_key_iv_b64 = b64encode ( session_key_iv ). decode ( 'ascii' )
85- fpath = '/' . join ( items [ 1 :] )
86- query = f'INSERT DATA {{<{ file_url } > <{ apps_terms } { path_pred } > "{ fpath } "; ' + \
87- f'<{ apps_terms } { iv_pred } > "{ session_key_iv_b64 } "; ' + \
88- f'<{ apps_terms } { session_key_pred } > "{ enc_session_key_b64 } ".}};'
98+ # Add encrypted individual key to ind-keys.ttl
99+
100+ indi_key_iv_b64 = b64encode ( indi_key_iv ). decode ( 'ascii' )
101+ query = f'INSERT DATA {{<{ file_url } > <{ apps_terms } { path_pred } > "{ relative_file_path } "; ' + \
102+ f'<{ apps_terms } { iv_pred } > "{ indi_key_iv_b64 } "; ' + \
103+ f'<{ apps_terms } { indi_key_pred } > "{ enc_indi_key_b64 } ".}};'
89104 g .update (query )
90105
91106 # Write back ind-keys.ttl
107+
92108 ttl_str = g .serialize (format = 'turtle' , base = server_url )
93109 with open (ind_key_path , 'w' ) as f :
94110 f .write (ttl_str )
@@ -97,25 +113,33 @@ def add_session_key(file_path, server_url, session_key, master_key):
97113 parser = argparse .ArgumentParser (description = 'Process secret and path arguments' )
98114 parser .add_argument ('srcpath' , help = 'Path of the file to be uploaded' )
99115 parser .add_argument ('destpath' , help = 'Path of the destination file within data folder' )
100- parser .add_argument ('--server' , help = 'Name of the POD server' , default = 'pods.test.solidcommunity.au' )
101116 args = parser .parse_args ()
102117
103- server = args .server
104- server_url = f'https://{ args .server } /'
105-
106118 # Destination file path format: server_path/pod_name/app_name/data/data_file
107119
108120 dest_path = os .path .abspath (args .destpath )
109121 assert dest_path .startswith (server_path )
110122 items = dest_path .replace (server_path , '' ).split ('/' )
111123 assert len (items ) >= 4
124+ pod_name = items [0 ]
125+ app_name = items [1 ]
112126 assert items [2 ] == 'data'
113- file_path = '/' . join ( items ) # pod_name/ app_name/data/data_file
127+ app_path = f' { server_path } { pod_name } / { app_name } '
114128
115129 # Generate master encryption key from the user-provided security key
116130
117131 security_key_str = getpass (prompt = 'Security Key: ' )
118132 master_key = gen_master_key (security_key_str )
133+ verify_key = gen_verify_key (security_key_str )
134+
135+ # Verify security key
136+
137+ _map = parse_ttl (f'{ app_path } /encryption/enc-keys.ttl' )
138+ enc_key_url , enc_key_map = list (_map .items ())[0 ]
139+ verify_key_stored = enc_key_map [verify_key_pred ]
140+ if verify_key .decode ('utf-8' ) != verify_key_stored :
141+ print ('ERROR: Incorrect security key (verification failed).' )
142+ sys .exit (0 )
119143
120144 # Read content of source file
121145
@@ -124,5 +148,9 @@ def add_session_key(file_path, server_url, session_key, master_key):
124148 with open (src_path , 'r' ) as f :
125149 file_content = f .read ()
126150
127- upload_file (file_content , file_path , server_url , master_key )
151+ # Write encrypted content to POD
152+
153+ r = urlparse (enc_key_url )
154+ file_url = '/' .join ([f'{ r .scheme } ://{ r .netloc } ' ] + items )
155+ upload_file (file_url , file_content , master_key )
128156
0 commit comments