delivery1 finished

- tests added
- changes
- decryption not fully working

Co-authored-by: Tiago Garcia <tiago.rgarcia@ua.pt>
Co-authored-by: João Bastos <joaop.bastos@ua.pt>
This commit is contained in:
RubenCGomes 2024-11-20 23:52:00 +00:00 committed by Tiago Garcia
parent 7bef5b468c
commit e00c032df8
Signed by: TiagoRG
GPG Key ID: DFCD48E3F420DB42
15 changed files with 200 additions and 73 deletions

View File

@ -51,13 +51,14 @@ def addDoc(args):
args.session = json.load(f)
#Encrypt content
key, nonce = encrypt_file(BASE_DIR + args.file, BASE_DIR + 'encryptedText')
key, content, nonce = encrypt_file(BASE_DIR + args.file, BASE_DIR + 'encryptedText')
#Upload document metadata
doc = {'document_name' : args.name, 'key' : key, 'alg' : 'AES-CFB', 'nonce' : nonce }
doc = {'document_name' : args.name, 'key' : key.hex(), 'alg' : 'AES-CFB', 'nonce' : nonce.hex() }
try:
req = requests.post(f'http://{state['REP_ADDRESS']}/file/upload', json=json.dumps(doc), headers={'Authorization': args.session['token']})
req = requests.post(f'http://{state['REP_ADDRESS']}/file/upload/metadata', json=json.dumps(doc),
headers={'Authorization': args.session['token']})
req.raise_for_status()
except requests.exceptions.RequestException as errex:
@ -65,16 +66,12 @@ def addDoc(args):
sys.exit(-1)
#Upload Document content
with open(BASE_DIR + 'encryptedText', 'rb') as f:
content = f.read()
file = {'file' : open(BASE_DIR + args.file, 'rb')}
file = {'file' : (BASE_DIR + args.file, content)}
try:
req = requests.post(f'http://{state['REP_ADDRESS']}/file/upload',
req = requests.post(f'http://{state['REP_ADDRESS']}/file/upload/content',
files=file,
headers={'Authorization': args.session['token'], 'File-Checksum' : content.hex()})
headers={'Authorization': args.session['token'], 'File-Checksum' : digest.get_hash(content)})
req.raise_for_status()
except requests.exceptions.RequestException as errex:

View File

@ -8,7 +8,8 @@ import argparse
from subject import main
from lib import encryption_functs
sys.path.append(os.path.abspath('../../'))
from lib import asymmetric_functs
logging.basicConfig(format='%(levelname)s\t- %(message)s')
logger = logging.getLogger()
@ -50,13 +51,15 @@ def addSubject(args):
logger.error("File '" + args.file + "' not found")
sys.exit(1)
subject = {'username' : args.username, 'name' : args.name, 'email' : args.email, 'credentials_file' : args.credentials}
pubKey = asymmetric_functs.load_public_key(BASE_DIR + args.credentials)
subject = {'username' : args.username, 'full_name' : args.name, 'email' : args.email, 'public_key' : pubKey}
try:
req = requests.post(f'http://{state['REP_ADDRESS']}/user/create', json=json.dumps(subject), headers={'Authorization': args.session['token']})
req.raise_for_status()
except requests.exceptions.RequestException as errex:
except requests.exceptions.RequestException as errex:
logger.error("Failed to obtain response from server.")
sys.exit(-1)

View File

@ -56,7 +56,7 @@ def createSession(args):
sys.exit(-1)
with open(BASE_DIR + args.session, 'w') as f:
f.write(req.json())
json.dump(req.json(), f)
sys.exit(0)

View File

@ -5,6 +5,7 @@ import logging
import argparse
import json
sys.path.append(os.path.abspath("../../"))
from lib import symmetric_encryption
logging.basicConfig(format='%(levelname)s\t- %(message)s')
@ -33,14 +34,15 @@ def decryptFile(args):
logger.error("File '" + args.encrypted + "' not found.")
sys.exit(1)
if (not os.path.isfile(BASE_DIR + args.metadata)):
logger.error("File '" + args.metadata + "' not found.")
sys.exit(1)
# if (not os.path.isfile(BASE_DIR + args.metadata)):
# logger.error("File '" + args.metadata + "' not found.")
# sys.exit(1)
#Decrypt file
metadata = json.loads(BASE_DIR + args.metadata)
print(args.metadata)
metadata = json.loads(args.metadata)
content = symmetric_encryption.decrypt_file(args.encrypted)
content = symmetric_encryption.decrypt_file(metadata['nonce'].encode(), metadata['key'].encode(), BASE_DIR + args.encrypted)
# Send decrypted content to stdout
sys.stdout.write(content)

View File

@ -5,6 +5,8 @@ import logging
import requests
import json
import argparse
sys.path.append(os.path.abspath("../../"))
from lib import digest
from subject import main
@ -43,7 +45,6 @@ def delDoc(args):
args.session = json.load(f)
doc_name = digest.get_hash(bytes(args.name, encoding='utf-8'))
doc = {'document_name' : doc_name}
try:
req = requests.post(f'http://{state['REP_ADDRESS']}/file/delete/' + doc_name, headers={'Authorization': args.session['token']})

View File

@ -8,6 +8,7 @@ import argparse
from subject import main
sys.path.append(os.path.abspath("../../"))
from lib import digest
from lib import symmetric_encryption
@ -47,10 +48,10 @@ def getDoc(args):
# Get Document metadata
doc_name = digest.get_hash(args.name)
doc_name = digest.get_hash(bytes(args.name, encoding='utf-8'))
try:
metadata = requests.post(f'http://{state['REP_ADDRESS']}/file/' + doc_name, headers={'Authorization': args.session['token']})
metadata = requests.get(f'http://{state['REP_ADDRESS']}/file/get/' + doc_name + '/metadata', headers={'Authorization': args.session['token']})
metadata.raise_for_status()
except requests.exceptions.RequestException as errex:
@ -76,7 +77,6 @@ def getDoc(args):
content = symmetric_encryption.decrypt_file(file)
if args.output:
with open(BASE_DIR + args.output, 'w') as f:
f.write(content)

View File

@ -5,6 +5,8 @@ import logging
import requests
import json
import argparse
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
from lib import digest
from subject import main
@ -46,16 +48,15 @@ def getDocMetadata(args):
doc_name = digest.get_hash(bytes(args.name, encoding='utf-8'))
try:
metadata = requests.post(f'http://{state['REP_ADDRESS']}/file/' + doc_name + '/metadata', headers={'Authorization': args.session['token']})
metadata = requests.get(f'http://{state['REP_ADDRESS']}/file/get/' + doc_name + '/metadata', headers={'Authorization': args.session['token']})
metadata.raise_for_status()
except requests.exceptions.RequestException as errex:
logger.error("Failed to obtain response from server.")
sys.exit(-1)
metadata = metadata.json()
sys.stdout.write(metadata)
sys.stdout.write(json.dumps(metadata))
sys.exit(0)

View File

@ -37,26 +37,26 @@ def getFile(args):
if not args.filehandle:
logger.error("Need a file handle.")
sys.exit(1)
else:
if not os.path.isfile(BASE_DIR + args.filehandle):
logger.error("File '" + args.filehandle + "' not found" )
sys.exit(1)
#else:
# if not os.path.isfile(BASE_DIR + args.filehandle):
# logger.error("File '" + args.filehandle + "' not found" )
# sys.exit(1)
#Get file
try:
file = requests.get(f'http://{state['REP_ADDRESS']}/get/' + args.file_handle + '/content')
file = requests.get(f'http://{state['REP_ADDRESS']}/file/get/' + args.filehandle + '/content')
file.raise_for_status()
except requests.exceptions.RequestException as errex:
logger.error("Failed to obtain response from server.")
sys.exit(-1)
file = file.json()
print(file)
if not args.file:
sys.stdout.write(file)
sys.stdout.write(file.text)
else:
with open(BASE_DIR + args.file, "wb") as f:
f.write(file)
f.write(file.content)
sys.exit(0)

View File

@ -65,9 +65,8 @@ def list_subjects(args):
logger.error("Failed to obtain response from server.")
sys.exit(-1)
for s in subjects.json():
sys.stdout.write(s['id'] + " - " + s['username'])
for s,d in subjects.json().items():
sys.stdout.write(s + " - " + d['username'] + "\n")
sys.exit(0)

View File

@ -0,0 +1,128 @@
import json
import os, subprocess, sys
import requests
DELIVERY_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
FILES_PATH = os.path.join(os.path.expanduser('~'), '.sio/')
# !!! database.db must be deleted/reset before running the tests !!!
requests.post("http://localhost:5000/reset", json={"password": "123"})
os.system(f"rm {FILES_PATH}*")
def test_address_set():
# Initialize the server path on state.json
process = subprocess.Popen(f"python3 {DELIVERY_PATH}/client/bin/subject.py -r localhost:5000 ", shell=True)
process.wait()
assert os.path.exists(os.path.join(FILES_PATH, 'state.json'))
def test_rep_subject_credentials():
# Test the rep_subject_create command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_subject_credentials password pub.pem priv.pem ", shell=True)
process.wait()
assert os.path.exists(os.path.join(FILES_PATH, 'pub.pem')) and os.path.exists(os.path.join(FILES_PATH, 'priv.pem'))
def test_rep_create_org():
# Test the rep_create_org command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_create_org org1 username name email@org.com pub.pem", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_list_orgs():
# Test the list_orgs command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_list_orgs", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_create_session():
# Test the rep_create_session command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_create_session org1 username password pub.pem session.json", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_list_subjects():
#Test the rep_list_subjects command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_list_subjects session.json", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_add_subject():
# Test the rep_subject_create command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_subject_credentials password pub_extra.pem priv_extra.pem ", shell=True)
process.wait()
assert os.path.exists(os.path.join(FILES_PATH, 'pub_extra.pem')) and os.path.exists(os.path.join(FILES_PATH, 'priv_extra.pem'))
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_add_subject session.json username2 name2 name2@any.com pub_extra.pem", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_suspend_subject():
# Test the rep_suspend_subject command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_suspend_subject session.json username2", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_activate_subject():
# Test the rep_activate_subject command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_activate_subject session.json username2", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_add_doc():
# Test the rep_add_doc command
process = subprocess.Popen(f"dd if=/dev/urandom of={FILES_PATH}test.txt bs=1024 count=1000 ", shell=True)
process.wait()
assert process.returncode == 0
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_add_doc session.json doc test.txt ", shell=True)
process.wait()
assert process.returncode == 0
metadata = {}
def test_rep_get_doc_metadata():
# Test the rep_get_doc_metadata command
global metadata
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_get_doc_metadata session.json doc", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
process.wait()
stdout, stderr = process.communicate()
metadata = json.loads(stdout)
assert process.returncode == 0
def test_rep_get_file():
# Test the rep_get_file command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_get_file {metadata['file_handle']} file.txt ", shell=True)
process.wait()
assert process.returncode == 0
def test_decrypt_file():
# Test the rep_decrypt_file command
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_decrypt_file file.txt '{json.dumps(metadata)}'", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_get_doc_file():
# Test the rep_get_doc_file
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_get_doc_file session.json doc ", shell=True)
process.wait()
assert process.returncode == 0
def test_rep_delete_doc():
# Test the rep_get_doc_file
process = subprocess.Popen(f"{DELIVERY_PATH}/client/bin/rep_delete_doc session.json doc ", shell=True)
process.wait()
assert process.returncode == 0

View File

@ -4,57 +4,43 @@ from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
# Function to derive a 256-bit key from a password and salt
def derive_key(salt):
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=10000,
backend=default_backend()
)
return kdf.derive(b'')
# Function to encrypt a file using a salt
def encrypt_file(input_file, output_file=None):
salt = os.urandom(16)
key = derive_key(salt)
key = os.urandom(16)
iv = os.urandom(16)
nonce = os.urandom(16)
cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
cipher = Cipher(algorithms.AES(key), modes.CFB(iv))
encryptor = cipher.encryptor()
with open(input_file, 'rb') as f:
plaintext = f.read()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
ciphertext = iv + ciphertext
if output_file is not None:
with open(output_file, 'wb') as f:
f.write(salt + iv + ciphertext)
f.write(ciphertext)
return salt + iv + ciphertext, nonce
print(iv.hex())
return key, ciphertext, iv
# Function to decrypt a file
def decrypt_file(input_file, output_file=None):
def decrypt_file(nonce, key, input_file, output_file=None):
with open(input_file, 'rb') as f:
encrypted_data = f.read()
salt = encrypted_data[:16]
iv = encrypted_data[16:32]
ciphertext = encrypted_data[32:]
ciphertext = encrypted_data
key = derive_key(salt)
cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
cipher = Cipher(algorithms.AES(key), modes.CFB(nonce))
decryptor = cipher.decryptor()
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
if output_file is None:
return plaintext
else:
if output_file is not None:
with open(output_file, 'wb') as f:
f.write(plaintext)
return plaintext.hex()

View File

@ -83,14 +83,16 @@ def file_upload_content():
if not file:
return jsonify({"error": "Invalid file data"}), 400
file_data = utils.get_hex_from_temp_file(file.stream)
file_sum = request.headers.get("File-Checksum")
if not file_sum:
return jsonify({"error": "No file checksum provided"}), 400
if file_sum != utils.get_hash(file.stream):
if file_sum != str(utils.get_hash(file_data)):
return jsonify({"error": "File checksum mismatch"}), 400
file = upload_service.write_file(session_token, file.stream)
file = upload_service.write_file(session_token, file_sum, file_data)
if isinstance(file, tuple):
return file

View File

@ -28,24 +28,24 @@ class FileService:
creator = user
)
self.current_requests[session_token] = file
db.add(file)
db.commit()
db.refresh(file)
self.current_requests[session_token] = file.id
return file
def write_file(self, session_token: str, file_data: bytes) -> File | tuple:
def write_file(self, session_token: str, file_handle: str, file_data: bytes) -> File | tuple:
if session_token not in self.current_requests:
return jsonify({"error": "No file upload request found"}), 400
file = self.current_requests[session_token]
file = db.query(File).filter(File.id == self.current_requests[session_token]).first()
file_path = os.path.join(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "repository"), file.org.name, file.document_handle)
with open(file_path, "wb") as f:
f.write(file_data)
file.file_handle = get_hash(file_data)
file.file_handle = file_handle
db.commit()
db.refresh(file)

View File

@ -1,2 +1,2 @@
from .checks import check_valid_time
from .hashing import get_hash
from .hashing import get_hash, get_hex_from_temp_file

View File

@ -1,8 +1,16 @@
from tempfile import SpooledTemporaryFile
import cryptography.hazmat.primitives.hashes
def get_hash(data):
if isinstance(data, str):
data = data.encode('utf-8')
digest = cryptography.hazmat.primitives.hashes.Hash(cryptography.hazmat.primitives.hashes.SHA256())
digest.update(data)
return digest.finalize().hex()
return digest.finalize().hex()
def get_hex_from_temp_file(temp_file: SpooledTemporaryFile) -> bytes:
temp_file.seek(0)
file_data = temp_file.read()
return file_data