From 3fb58a5ca8ef1aeeaba8d087c901e8a1b3bd72fe Mon Sep 17 00:00:00 2001 From: Frahane Date: Sat, 23 Mar 2024 11:53:23 +0100 Subject: [PATCH 01/12] File upload, encryption, download, decryption, start, pause and resume --- Encryption-Decryption/index.css | 122 +++++++++++++++++++++++++++ Encryption-Decryption/index.html | 52 ++++++++++++ Encryption-Decryption/script.js | 136 +++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 Encryption-Decryption/index.css create mode 100644 Encryption-Decryption/index.html create mode 100644 Encryption-Decryption/script.js diff --git a/Encryption-Decryption/index.css b/Encryption-Decryption/index.css new file mode 100644 index 0000000..722967a --- /dev/null +++ b/Encryption-Decryption/index.css @@ -0,0 +1,122 @@ + + /* Laptops */ +@media only screen and (min-width: 1025px) and (max-width: 1280px) { + button { + cursor: pointer; + } + + button:hover { + background-color: #1F4E79; + } + + input:hover { + border: 3px solid slateblue; + border-radius: 6px; + } + + input { + padding: 14px; + } + + .container { + align-items: center; + } + + button { + padding-left: 4%; + } + } + + /* Desktops */ +@media only screen and (min-width: 1281px) { + button { + cursor: pointer; + } + + button:hover { + background-color: #1F4E79; + } + + input:hover { + border: 3px solid slateblue; + border-radius: 6px; + } + + input { + padding: 15px; + } + } + + +button:active { + background-color: #1B4F73; + } + +h1 { + color: white; + text-align: center; + font-size: 2em; + background-color: green; + border-radius: 4px; +} + +input { + padding: 5px; + cursor: pointer; + border-radius: 5px; +} + +select, option { + cursor: pointer; +} + + + +.progress { + width: 100%; + height: 15px; + background-color: #f0f0f0; + border: 1px solid #ccc; + margin-top: 10px; + border-radius: 6px; +} + +.progress-bar { + height: 100%; + background-color: #007bff; + border-radius: 5px; +} + +#downloadLinks { + margin-top: 20px; +} + +.container { + padding: 20px; + margin: 20px auto; + max-width: 90%; + border-radius: 10px; + color: green; + box-shadow: 0 0 20px 5px rgba(0, 255, 0, 0.5); + background-color: #15202B; + text-align: center; + } + +body { + background-color: #15202B; + color: #FFFFFF; + } + +button { + background-color: green; + color: #15202B; + font-weight: bold; + border: none; + margin-top: 4%; + margin-left: 1%; + color: white; + border-radius: 5px; + padding: 5px 15px; + cursor: pointer; + } + diff --git a/Encryption-Decryption/index.html b/Encryption-Decryption/index.html new file mode 100644 index 0000000..4447d7b --- /dev/null +++ b/Encryption-Decryption/index.html @@ -0,0 +1,52 @@ + + + + + + Encryption-Decryption + + + +
+

Encryption-Decryption

+ + +
+ + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+
+ + + + + diff --git a/Encryption-Decryption/script.js b/Encryption-Decryption/script.js new file mode 100644 index 0000000..32fa74f --- /dev/null +++ b/Encryption-Decryption/script.js @@ -0,0 +1,136 @@ +$(document).ready(function () { + // Function to refresh download links + function refreshDownloadLinks() { + // Send AJAX request to backend to get list of available files for download + $.ajax({ + url: '/download', + type: 'GET', + success: function (response) { + // Display download links + $('#downloadLinks').html(response); + + // Show download button if there are any files + if ($('#downloadLinks a').length > 0) { + $('#downloadButton').show(); + } + + // Hide progress bar + $('#progressBar').hide(); + $('#uploadForm button, decryptForm button').prop('disabled', false); + }, + error: function (xhr, status, error) { + console.error('Failed to fetch download links:', error); + } + }); + } + + // Initial refresh of download links + refreshDownloadLinks(); + + // File upload form submission + $('#uploadForm').submit(function (e) { + e.preventDefault(); + + // Get selected encryption algorithm + const encryption = $('#encryption').val(); + + // Show progress bar and disable upload button + $('#progressBar').show(); + $('#uploadForm button').prop('disabled', true); + + // Send AJAX request to backend for file upload with encryption option + $.ajax({ + url: '/upload', + type: 'POST', + data: new FormData(this), + contentType: false, + processData: false, + xhr: function () { + const xhr = new window.XMLHttpRequest(); + + xhr.upload.onprogress = function (event) { + if (event.lengthComputable) { + const percent = event.loaded / event.total * 100; + $('#progressBarInner').css('width', percent + '%'); + } + }; + + return xhr; + }, + success: function (response) { + console.log('Upload successful:', response); + refreshDownloadLinks(); + }, + error: function (xhr, status, error) { + console.error('Upload failed:', error); + }, + complete: function () { + // Hide progress bar and enable upload button + $('#progressBar').hide(); + $('#uploadForm button').prop('disabled', false); + } + }); + }); + + // Decryption form submission + $('#decryptForm').submit(function (e) { + e.preventDefault(); + + // Get filename to decrypt + const filename = $('#fileToDecrypt').val(); + + // Show progress bar and disable decrypt button + $('#progressBar').show(); + $('#decryptForm button').prop('disabled', true); + + // Send AJAX request to backend for decryption + $.ajax({ + url: '/decrypt', + type: 'POST', + data: { filename: filename }, + success: function (response) { + console.log('Decryption successful:', response); + refreshDownloadLinks(); + }, + error: function (xhr, status, error) { + console.error('Decryption failed:', error); + }, + complete: function () { + // Hide progress bar and enable decrypt button + $('#progressBar').hide(); + $('#decryptForm button').prop('disabled', false); + } + }); + }); + + // Download button click event + $(document).on('click', '#downloadButton', function () { + // Send AJAX request to backend to initiate file download + $.ajax({ + url: '/download', + type: 'GET', + success: function (response) { + // Handle file download or display message + }, + error: function (xhr, status, error) { + console.error('Failed to initiate download:', error); + } + }); + }); + + // Functions for start, pause, and resume buttons + $('#startButton').click(function () { + console.log('Start button clicked'); + // Send AJAX request to backend to start encryption + }); + + $('#pauseButton').click(function () { + console.log('Pause button clicked'); + // Send AJAX request to backend to pause encryption + }); + + $('#resumeButton').click(function () { + console.log('Resume button clicked'); + // Send AJAX request to backend to resume encryption + }); +}); \ No newline at end of file From 2d531c0813b3861dada3b01c6d51386d0c2906d7 Mon Sep 17 00:00:00 2001 From: Frahane Date: Sun, 24 Mar 2024 16:44:36 +0100 Subject: [PATCH 02/12] improved for block to block encryption --- Encryption-Decryption/index.html | 11 ++- Encryption-Decryption/script.js | 132 ++++++++++++++++++++++--------- 2 files changed, 103 insertions(+), 40 deletions(-) diff --git a/Encryption-Decryption/index.html b/Encryption-Decryption/index.html index 4447d7b..943a80b 100644 --- a/Encryption-Decryption/index.html +++ b/Encryption-Decryption/index.html @@ -15,9 +15,8 @@

Encryption-Decryption

@@ -46,7 +45,11 @@

Encryption-Decryption

+ + + + - + \ No newline at end of file diff --git a/Encryption-Decryption/script.js b/Encryption-Decryption/script.js index 32fa74f..e9bbda8 100644 --- a/Encryption-Decryption/script.js +++ b/Encryption-Decryption/script.js @@ -12,11 +12,13 @@ $(document).ready(function () { // Show download button if there are any files if ($('#downloadLinks a').length > 0) { $('#downloadButton').show(); + } else { + $('#downloadButton').hide(); } // Hide progress bar $('#progressBar').hide(); - $('#uploadForm button, decryptForm button').prop('disabled', false); + $('#uploadForm button, #decryptForm button').prop('disabled', false); }, error: function (xhr, status, error) { console.error('Failed to fetch download links:', error); @@ -34,43 +36,101 @@ $(document).ready(function () { // Get selected encryption algorithm const encryption = $('#encryption').val(); - // Show progress bar and disable upload button - $('#progressBar').show(); - $('#uploadForm button').prop('disabled', true); + // Show progress bar if there are files selected + if ($('#fileInput')[0].files.length > 0) { + $('#progressBar').show(); + $('#uploadForm button').prop('disabled', true); + } + + // Iterate over each file selected by the user + const files = $('#fileInput')[0].files; + for (let i = 0; i < files.length; i++) { + const file = files[i]; + // Read the file contents + const reader = new FileReader(); + reader.onload = function (event) { + const fileData = event.target.result; + + // Encrypt the file data using the selected encryption algorithm + const encryptedData = encryptFileData(fileData, encryption); + + // Upload the encrypted file block by block + uploadFileBlockByBlock(file.name, encryptedData); + }; + reader.readAsArrayBuffer(file); + } + }); - // Send AJAX request to backend for file upload with encryption option - $.ajax({ - url: '/upload', - type: 'POST', - data: new FormData(this), - contentType: false, - processData: false, - xhr: function () { - const xhr = new window.XMLHttpRequest(); - - xhr.upload.onprogress = function (event) { - if (event.lengthComputable) { - const percent = event.loaded / event.total * 100; - $('#progressBarInner').css('width', percent + '%'); + // Function to encrypt file data using the selected encryption algorithm, encryptionKey should be replaced with the actual encryption Key + function encryptFileData(fileData, algorithm, encryptionKey) { + // Convert the file data to a WordArray + const wordArray = CryptoJS.lib.WordArray.create(fileData); + + // Encrypt the file data using the selected algorithm and encryption key + let encryptedData; + switch (algorithm) { + case 'AES': + encryptedData = CryptoJS.AES.encrypt(wordArray, encryptionKey).toString(); + break; + case 'Salsa20': + encryptedData = CryptoJS.Salsa20.encrypt(wordArray, encryptionKey).toString(); + break; + default: + console.error('Unsupported algorithm:', algorithm); + encryptedData = null; + break; + } + + return encryptedData; + } + + // Function to upload encrypted file block by block + function uploadFileBlockByBlock(filename, encryptedData) { + // Determine block size and number of blocks + const blockSize = 1048576; // 1 MB + const totalBlocks = Math.ceil(encryptedData.byteLength / blockSize); + + // Initialize block index + let blockIndex = 0; + + // Define function to handle block upload + function uploadNextBlock() { + // Calculate start and end bytes for the current block + const startByte = blockIndex * blockSize; + const endByte = Math.min(startByte + blockSize, encryptedData.byteLength); + const blockData = encryptedData.slice(startByte, endByte); + + // Send AJAX request to upload the current block + $.ajax({ + url: '/upload', + type: 'POST', + data: { + filename: filename, + blockIndex: blockIndex, + totalBlocks: totalBlocks, + blockData: blockData + }, + success: function (response) { + // Increment block index and check if there are more blocks to upload + blockIndex++; + if (blockIndex < totalBlocks) { + // Upload the next block + uploadNextBlock(); + } else { + // All blocks uploaded successfully + console.log('Upload complete for file: ' + filename); + refreshDownloadLinks(); } - }; + }, + error: function (xhr, status, error) { + console.error('Upload failed:', error); + } + }); + } - return xhr; - }, - success: function (response) { - console.log('Upload successful:', response); - refreshDownloadLinks(); - }, - error: function (xhr, status, error) { - console.error('Upload failed:', error); - }, - complete: function () { - // Hide progress bar and enable upload button - $('#progressBar').hide(); - $('#uploadForm button').prop('disabled', false); - } - }); - }); + // Start uploading the first block + uploadNextBlock(); + } // Decryption form submission $('#decryptForm').submit(function (e) { @@ -85,7 +145,7 @@ $(document).ready(function () { // Send AJAX request to backend for decryption $.ajax({ - url: '/decrypt', + url: '/download', type: 'POST', data: { filename: filename }, success: function (response) { From c874bd702e7a5aadac53cc63e1f422c86941df10 Mon Sep 17 00:00:00 2001 From: Frahane Date: Mon, 15 Apr 2024 16:29:07 +0100 Subject: [PATCH 03/12] Web client for displaying node list --- services/web/index.html | 70 ++++++++++++++++++++++++++++++++++++++ services/web/web_client.py | 18 ++++++++++ 2 files changed, 88 insertions(+) create mode 100644 services/web/index.html create mode 100644 services/web/web_client.py diff --git a/services/web/index.html b/services/web/index.html new file mode 100644 index 0000000..763d988 --- /dev/null +++ b/services/web/index.html @@ -0,0 +1,70 @@ + + + + + + Node List + + + + +

Node List:

+
+ + + + + + + + +
Node NameNode URL
+
+ + + + \ No newline at end of file diff --git a/services/web/web_client.py b/services/web/web_client.py new file mode 100644 index 0000000..5b332a2 --- /dev/null +++ b/services/web/web_client.py @@ -0,0 +1,18 @@ +from flask import Flask, render_template +import subprocess + +app = Flask(__name__, template_folder='../web') + +@app.route('/') +def index(): + # Execute the 'node ls' command from Privatenys Tools + result = subprocess.run(['python', '../../node.py', 'ls'], capture_output=True, text=True) + if result.returncode == 0: + # Split the output into a list of nodes + nodes = result.stdout.strip().split('\n') + return render_template('index.html', nodes=nodes) + else: + return "Error fetching node list" + +if __name__ == '__main__': + app.run(debug=True, threaded=True) \ No newline at end of file From 500ba3eeaf332df21b041af3a4e798b65969b973 Mon Sep 17 00:00:00 2001 From: Frahane Date: Wed, 17 Apr 2024 12:16:05 +0100 Subject: [PATCH 04/12] Having issue with key generation in KeyManager.py while web client to display node list has been improved --- framework/Container.py | 2 +- services/web/index.html | 6 ++-- services/web/web_client.py | 69 ++++++++++++++++++++++++++++++++------ 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/framework/Container.py b/framework/Container.py index 4720ec5..0f8292f 100644 --- a/framework/Container.py +++ b/framework/Container.py @@ -37,5 +37,5 @@ def fs (): return FileManager(Container.KeyManager(), ns, fs) - def output() -> ioutput: + def output() -> output: return ConsoleOutput() diff --git a/services/web/index.html b/services/web/index.html index 763d988..d35dbb9 100644 --- a/services/web/index.html +++ b/services/web/index.html @@ -58,10 +58,10 @@

Node List:

+ + + diff --git a/EncryptDecrypt/utils/db.py b/EncryptDecrypt/utils/db.py new file mode 100644 index 0000000..6e13800 --- /dev/null +++ b/EncryptDecrypt/utils/db.py @@ -0,0 +1,24 @@ +import sqlite3 +from flask import g + +DATABASE = 'filemeta.db' + +def get_db(): + db = getattr(g, '_database', None) + if db is None: + db = g._database = sqlite3.connect(DATABASE) + db.row_factory = sqlite3.Row + return db + +def init_db(): + with sqlite3.connect(DATABASE) as db: + db.execute(''' + CREATE TABLE IF NOT EXISTS files ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + shadowname TEXT UNIQUE, + filename TEXT, + algorithm TEXT, + uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + db.commit() diff --git a/Encryption-Decryption/index.html b/Encryption-Decryption/index.html deleted file mode 100644 index 943a80b..0000000 --- a/Encryption-Decryption/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - Encryption-Decryption - - - -
-

Encryption-Decryption

- - -
- - - - -
- - - - - - - - - - - - - - - - -
- - -
-
- - - - - - - - - \ No newline at end of file diff --git a/Encryption-Decryption/script.js b/Encryption-Decryption/script.js deleted file mode 100644 index e9bbda8..0000000 --- a/Encryption-Decryption/script.js +++ /dev/null @@ -1,196 +0,0 @@ -$(document).ready(function () { - // Function to refresh download links - function refreshDownloadLinks() { - // Send AJAX request to backend to get list of available files for download - $.ajax({ - url: '/download', - type: 'GET', - success: function (response) { - // Display download links - $('#downloadLinks').html(response); - - // Show download button if there are any files - if ($('#downloadLinks a').length > 0) { - $('#downloadButton').show(); - } else { - $('#downloadButton').hide(); - } - - // Hide progress bar - $('#progressBar').hide(); - $('#uploadForm button, #decryptForm button').prop('disabled', false); - }, - error: function (xhr, status, error) { - console.error('Failed to fetch download links:', error); - } - }); - } - - // Initial refresh of download links - refreshDownloadLinks(); - - // File upload form submission - $('#uploadForm').submit(function (e) { - e.preventDefault(); - - // Get selected encryption algorithm - const encryption = $('#encryption').val(); - - // Show progress bar if there are files selected - if ($('#fileInput')[0].files.length > 0) { - $('#progressBar').show(); - $('#uploadForm button').prop('disabled', true); - } - - // Iterate over each file selected by the user - const files = $('#fileInput')[0].files; - for (let i = 0; i < files.length; i++) { - const file = files[i]; - // Read the file contents - const reader = new FileReader(); - reader.onload = function (event) { - const fileData = event.target.result; - - // Encrypt the file data using the selected encryption algorithm - const encryptedData = encryptFileData(fileData, encryption); - - // Upload the encrypted file block by block - uploadFileBlockByBlock(file.name, encryptedData); - }; - reader.readAsArrayBuffer(file); - } - }); - - // Function to encrypt file data using the selected encryption algorithm, encryptionKey should be replaced with the actual encryption Key - function encryptFileData(fileData, algorithm, encryptionKey) { - // Convert the file data to a WordArray - const wordArray = CryptoJS.lib.WordArray.create(fileData); - - // Encrypt the file data using the selected algorithm and encryption key - let encryptedData; - switch (algorithm) { - case 'AES': - encryptedData = CryptoJS.AES.encrypt(wordArray, encryptionKey).toString(); - break; - case 'Salsa20': - encryptedData = CryptoJS.Salsa20.encrypt(wordArray, encryptionKey).toString(); - break; - default: - console.error('Unsupported algorithm:', algorithm); - encryptedData = null; - break; - } - - return encryptedData; - } - - // Function to upload encrypted file block by block - function uploadFileBlockByBlock(filename, encryptedData) { - // Determine block size and number of blocks - const blockSize = 1048576; // 1 MB - const totalBlocks = Math.ceil(encryptedData.byteLength / blockSize); - - // Initialize block index - let blockIndex = 0; - - // Define function to handle block upload - function uploadNextBlock() { - // Calculate start and end bytes for the current block - const startByte = blockIndex * blockSize; - const endByte = Math.min(startByte + blockSize, encryptedData.byteLength); - const blockData = encryptedData.slice(startByte, endByte); - - // Send AJAX request to upload the current block - $.ajax({ - url: '/upload', - type: 'POST', - data: { - filename: filename, - blockIndex: blockIndex, - totalBlocks: totalBlocks, - blockData: blockData - }, - success: function (response) { - // Increment block index and check if there are more blocks to upload - blockIndex++; - if (blockIndex < totalBlocks) { - // Upload the next block - uploadNextBlock(); - } else { - // All blocks uploaded successfully - console.log('Upload complete for file: ' + filename); - refreshDownloadLinks(); - } - }, - error: function (xhr, status, error) { - console.error('Upload failed:', error); - } - }); - } - - // Start uploading the first block - uploadNextBlock(); - } - - // Decryption form submission - $('#decryptForm').submit(function (e) { - e.preventDefault(); - - // Get filename to decrypt - const filename = $('#fileToDecrypt').val(); - - // Show progress bar and disable decrypt button - $('#progressBar').show(); - $('#decryptForm button').prop('disabled', true); - - // Send AJAX request to backend for decryption - $.ajax({ - url: '/download', - type: 'POST', - data: { filename: filename }, - success: function (response) { - console.log('Decryption successful:', response); - refreshDownloadLinks(); - }, - error: function (xhr, status, error) { - console.error('Decryption failed:', error); - }, - complete: function () { - // Hide progress bar and enable decrypt button - $('#progressBar').hide(); - $('#decryptForm button').prop('disabled', false); - } - }); - }); - - // Download button click event - $(document).on('click', '#downloadButton', function () { - // Send AJAX request to backend to initiate file download - $.ajax({ - url: '/download', - type: 'GET', - success: function (response) { - // Handle file download or display message - }, - error: function (xhr, status, error) { - console.error('Failed to initiate download:', error); - } - }); - }); - - // Functions for start, pause, and resume buttons - $('#startButton').click(function () { - console.log('Start button clicked'); - // Send AJAX request to backend to start encryption - }); - - $('#pauseButton').click(function () { - console.log('Pause button clicked'); - // Send AJAX request to backend to pause encryption - }); - - $('#resumeButton').click(function () { - console.log('Resume button clicked'); - // Send AJAX request to backend to resume encryption - }); -}); \ No newline at end of file diff --git a/framework/__init__.py b/framework/__init__.py new file mode 100644 index 0000000..e69de29 From 3665d2a0a0bda91239ee46382f434a3347b45daf Mon Sep 17 00:00:00 2001 From: Frahane Date: Tue, 6 May 2025 21:34:55 +0100 Subject: [PATCH 07/12] Issues --- EncryptDecrypt/app.py | 119 +++++++++++--- EncryptDecrypt/static/index.css | 223 +++++++++++++------------ EncryptDecrypt/static/script.js | 247 ++++++++++++++-------------- EncryptDecrypt/templates/index.html | 76 ++++++--- 4 files changed, 387 insertions(+), 278 deletions(-) diff --git a/EncryptDecrypt/app.py b/EncryptDecrypt/app.py index 7cb928a..e2f4904 100644 --- a/EncryptDecrypt/app.py +++ b/EncryptDecrypt/app.py @@ -1,26 +1,33 @@ import sys import os -PROJECT_ROOT = os.path.dirname (os.path.abspath(__file__)) +PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) +PARENT_DIR = os.path.dirname(PROJECT_ROOT) if PROJECT_ROOT not in sys.path: sys.path.insert(0, PROJECT_ROOT) +if PARENT_DIR not in sys.path: + sys.path.insert(0, PARENT_DIR) +PARENT_DIR = os.path.dirname(os.path.abspath(__file__)) # D:\PrivateNessTools +if PARENT_DIR not in sys.path: + sys.path.insert(0, PARENT_DIR) +print("sys.path:", sys.path) import logging -from flask import ( - Flask, request, jsonify, send_file, render_template, g -) +from flask import Flask, request, jsonify, send_file, render_template, g, abort from werkzeug.utils import secure_filename from framework.Container import Container from utils.db import get_db, init_db -from typing import List, Dict +from NessKeys.Cryptors.Aes import Aes +from NessKeys.Cryptors.Salsa20 import Salsa20 +from NessKeys.Cryptors.BlockCryptor import BlockCryptor +from NessKeys.Cryptors.PasswordCryptor import PasswordCryptor +from NessKeys.Cryptors.TextCryptor import TextCryptor # --- Logging Configuration --- logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = Flask(__name__) -app.config['UPLOAD_FOLDER'] = 'uploaded_files' +app.config['UPLOAD_FOLDER'] = os.path.join(PROJECT_ROOT, 'uploaded_files') app.secret_key = os.environ.get('SECRET_KEY', 'a-very-secret-key') - -# --- Ensure upload folder exists --- os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) # --- Custom Exceptions --- @@ -32,21 +39,43 @@ class FileManager: def __init__(self): self.fm = Container.FileManager() - def upload_file(self, file, encrypt: bool = False) -> Dict: + def upload_file(self, file, algorithm, key) -> dict: try: filename = secure_filename(file.filename) file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(file_path) + enc_filename = filename + '.enc' + enc_file_path = os.path.join(app.config['UPLOAD_FOLDER'], enc_filename) + + # Read file data + with open(file_path, 'rb') as f: + data = f.read() + + # Encrypt data + if algorithm == 'AES': + cryptor = Aes() + elif algorithm == 'Salsa20': + cryptor = Salsa20() + else: + raise EncryptionError(f"Unsupported algorithm: {algorithm}") + + encrypted_data = cryptor.encrypt(data, key) + with open(enc_file_path, 'wb') as f: + f.write(encrypted_data) + # Register in DB db = get_db() db.execute( - 'INSERT INTO files (shadowname, filename, algorithm) VALUES (?, ?, ?)', - (filename, filename, request.form.get('algorithm', 'none')) + 'INSERT INTO files (shadowname, filename, algorithm, key) VALUES (?, ?, ?, ?)', + (enc_filename, filename, algorithm, key) ) db.commit() - return {"shadowname": filename, "filename": filename} + # Remove original file for security + os.remove(file_path) + + return {"shadowname": enc_filename, "filename": filename, "algorithm": algorithm} except Exception as e: - logger.error(f"Error uploading file: {str(e)}") + logger.error(f"Error uploading/encrypting file: {str(e)}") raise def download_file(self, shadowname: str) -> str: @@ -55,12 +84,45 @@ def download_file(self, shadowname: str) -> str: raise FileNotFoundError(f"File {shadowname} not found.") return file_path - def list_files(self) -> List[Dict]: + def decrypt_file(self, shadowname: str, key: str) -> str: + # Get file and algorithm from DB + db = get_db() + file_row = db.execute('SELECT * FROM files WHERE shadowname=?', (shadowname,)).fetchone() + if not file_row: + raise FileNotFoundError(f"File {shadowname} not found in DB.") + algorithm = file_row['algorithm'] + orig_filename = file_row['filename'] + + enc_file_path = os.path.join(app.config['UPLOAD_FOLDER'], shadowname) + dec_filename = orig_filename + '.dec' + dec_file_path = os.path.join(app.config['UPLOAD_FOLDER'], dec_filename) + + with open(enc_file_path, 'rb') as f: + enc_data = f.read() + + if algorithm == 'AES': + cryptor = Aes() + elif algorithm == 'Salsa20': + cryptor = Salsa20() + else: + raise EncryptionError(f"Unsupported algorithm: {algorithm}") + + try: + dec_data = cryptor.decrypt(enc_data, key) + except Exception as e: + raise EncryptionError("Decryption failed. Wrong key or corrupted file.") + + with open(dec_file_path, 'wb') as f: + f.write(dec_data) + + return dec_file_path + + def list_files(self) -> list: db = get_db() files = db.execute('SELECT * FROM files').fetchall() return [dict(f) for f in files] - def file_info(self, shadowname: str) -> Dict: + def file_info(self, shadowname: str) -> dict: db = get_db() file = db.execute('SELECT * FROM files WHERE shadowname=?', (shadowname,)).fetchone() if not file: @@ -68,8 +130,6 @@ def file_info(self, shadowname: str) -> Dict: return dict(file) file_manager = FileManager() - -# --- Operation State (for Pause/Resume/Progress) --- operation_states = {} @app.before_first_request @@ -91,8 +151,12 @@ def upload_file(): if "file" not in request.files: return jsonify({"success": False, "error": "No file part in the request"}), 400 file = request.files["file"] + algorithm = request.form.get("algorithm") + key = request.form.get("key") + if not algorithm or not key: + return jsonify({"success": False, "error": "Algorithm and key are required"}), 400 try: - result = file_manager.upload_file(file) + result = file_manager.upload_file(file, algorithm, key) return jsonify({"success": True, "file": result}) except Exception as e: logger.exception("Upload failed") @@ -109,6 +173,23 @@ def download_file(shadowname): logger.exception("Download failed") return jsonify({"success": False, "error": str(e)}), 500 +@app.route("/decrypt/", methods=["POST"]) +def decrypt_file(shadowname): + key = request.form.get("key") + if not key: + return jsonify({"success": False, "error": "Key is required"}), 400 + try: + dec_file_path = file_manager.decrypt_file(shadowname, key) + return send_file(dec_file_path, as_attachment=True) + except EncryptionError as e: + logger.exception("Decryption failed") + return jsonify({"success": False, "error": str(e)}), 400 + except FileNotFoundError as e: + return jsonify({"success": False, "error": str(e)}), 404 + except Exception as e: + logger.exception("Decryption failed") + return jsonify({"success": False, "error": str(e)}), 500 + @app.route("/files/list", methods=["GET"]) def list_files(): try: @@ -161,4 +242,4 @@ def operation_progress(): return jsonify({"success": True, "progress": state["progress"], "paused": state["paused"]}) if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000, debug=False) \ No newline at end of file + app.run(host="0.0.0.0", port=5000, debug=False) diff --git a/EncryptDecrypt/static/index.css b/EncryptDecrypt/static/index.css index 722967a..222b415 100644 --- a/EncryptDecrypt/static/index.css +++ b/EncryptDecrypt/static/index.css @@ -1,122 +1,127 @@ +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + margin: 0; + padding: 20px; + background-color: #1a1a1a; + color: #ffffff; +} - /* Laptops */ -@media only screen and (min-width: 1025px) and (max-width: 1280px) { - button { - cursor: pointer; - } - - button:hover { - background-color: #1F4E79; - } - - input:hover { - border: 3px solid slateblue; - border-radius: 6px; - } - - input { - padding: 14px; - } - - .container { - align-items: center; - } - - button { - padding-left: 4%; - } - } - - /* Desktops */ -@media only screen and (min-width: 1281px) { - button { - cursor: pointer; - } - - button:hover { - background-color: #1F4E79; - } - - input:hover { - border: 3px solid slateblue; - border-radius: 6px; - } - - input { - padding: 15px; - } - } - - -button:active { - background-color: #1B4F73; - } - -h1 { - color: white; - text-align: center; - font-size: 2em; - background-color: green; - border-radius: 4px; -} - -input { - padding: 5px; - cursor: pointer; - border-radius: 5px; -} - -select, option { - cursor: pointer; +.container { + max-width: 1200px; + margin: 0 auto; +} + +.section { + background: #2d2d2d; + border-radius: 8px; + padding: 20px; + margin-bottom: 20px; +} + +h1, h2 { + color: #4CAF50; } - +/* Form Elements */ +.form-group { + margin-bottom: 15px; +} -.progress { +input[type="file"], +input[type="password"], +select { width: 100%; - height: 15px; - background-color: #f0f0f0; - border: 1px solid #ccc; - margin-top: 10px; - border-radius: 6px; + padding: 8px; + margin: 5px 0; + background: #333; + border: 1px solid #444; + color: white; + border-radius: 4px; } -.progress-bar { - height: 100%; - background-color: #007bff; - border-radius: 5px; +button { + background: #4CAF50; + color: white; + border: none; + padding: 10px 20px; + border-radius: 4px; + cursor: pointer; + transition: background 0.3s; } -#downloadLinks { - margin-top: 20px; +button:hover { + background: #45a049; } -.container { - padding: 20px; - margin: 20px auto; - max-width: 90%; - border-radius: 10px; - color: green; - box-shadow: 0 0 20px 5px rgba(0, 255, 0, 0.5); - background-color: #15202B; +button:disabled { + background: #666; + cursor: not-allowed; +} + +/* File List */ +.file-list { + display: grid; + gap: 10px; +} + +.file-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + background: #333; + border-radius: 4px; + cursor: pointer; +} + +.file-item.selected { + background: #404040; + outline: 2px solid #4CAF50; +} + +.file-info { + display: flex; + flex-direction: column; +} + +.filename { + font-weight: bold; +} + +.algorithm { + color: #888; + font-size: 0.9em; +} + +/* Progress Bar */ +#progressContainer { + margin-top: 15px; +} + +progress { + width: 100%; + height: 20px; + border-radius: 4px; +} + +#progressText { + display: block; text-align: center; - } - -body { - background-color: #15202B; - color: #FFFFFF; - } + margin-top: 5px; +} -button { - background-color: green; - color: #15202B; - font-weight: bold; - border: none; - margin-top: 4%; - margin-left: 1%; - color: white; - border-radius: 5px; - padding: 5px 15px; - cursor: pointer; - } - +/* Responsive Design */ +@media (max-width: 768px) { + .container { + padding: 10px; + } + + .file-item { + flex-direction: column; + align-items: flex-start; + } + + .download-btn { + margin-top: 10px; + } +} diff --git a/EncryptDecrypt/static/script.js b/EncryptDecrypt/static/script.js index 44c4719..aba2102 100644 --- a/EncryptDecrypt/static/script.js +++ b/EncryptDecrypt/static/script.js @@ -1,164 +1,157 @@ -let currentOperationId = null; -let currentFile = null; -let currentAlgorithm = null; +$(document).ready(function() { + let currentOperationId = null; + let selectedFile = null; -// Utility to generate a unique operation ID -function generateOpId() { - return 'op_' + Date.now() + '_' + Math.floor(Math.random() * 100000); -} + // Initialize file list + refreshFileList(); -// Load file list from server -function refreshFileList() { - $.ajax({ - url: '/files/list', - method: 'GET', - success: function(response) { - if (response.success) { - let list = $('#fileList'); - list.empty(); - response.files.forEach(file => { - let li = $('
  • '); - li.text(file.filename + ' (' + file.algorithm + ')'); - let downloadBtn = $('').click(() => downloadFile(file.shadowname)); - let decryptBtn = $('').click(() => decryptFile(file.shadowname)); - li.append(' ', downloadBtn, ' ', decryptBtn); - list.append(li); - }); + // File Upload Handler + $('#uploadForm').on('submit', function(e) { + e.preventDefault(); + const formData = new FormData(); + formData.append('file', $('#fileInput')[0].files[0]); + formData.append('algorithm', $('#algorithm').val()); + formData.append('key', $('#encryptionKey').val()); + + $('#uploadBtn').prop('disabled', true).text('Encrypting...'); + + $.ajax({ + url: '/upload', + method: 'POST', + data: formData, + processData: false, + contentType: false, + success: function(response) { + if (response.success) { + alert('File encrypted successfully!'); + refreshFileList(); + $('#uploadForm')[0].reset(); + } else { + alert('Error: ' + response.error); + } + }, + error: function(xhr) { + alert('Upload failed: ' + xhr.responseJSON.error); + }, + complete: function() { + $('#uploadBtn').prop('disabled', false).text('Upload & Encrypt'); } - } + }); + }); + + // File List Click Handler + $(document).on('click', '.file-item', function() { + $('.file-item').removeClass('selected'); + $(this).addClass('selected'); + selectedFile = $(this).data('shadowname'); + $('#decryptSection').show(); }); -} -// Upload file handler -$('#uploadForm').on('submit', function(e) { - e.preventDefault(); - let fileInput = $('#fileInput')[0]; - if (!fileInput.files.length) return alert('Please select a file.'); - let file = fileInput.files[0]; - let algorithm = $('#algorithm').val(); + // Download Handler + $(document).on('click', '.download-btn', function() { + const shadowname = $(this).closest('.file-item').data('shadowname'); + window.location = `/download/${encodeURIComponent(shadowname)}`; + }); - let formData = new FormData(); - formData.append('file', file); - formData.append('algorithm', algorithm); + // Decrypt Handler + $('#decryptBtn').click(function() { + const key = $('#decryptionKey').val(); + if (!key) { + alert('Please enter decryption key'); + return; + } - $('#uploadBtn').prop('disabled', true).text('Uploading...'); - $.ajax({ - url: '/upload', - method: 'POST', - data: formData, - processData: false, - contentType: false, - success: function(response) { - $('#uploadBtn').prop('disabled', false).text('Upload'); - if (response.success) { - alert('Upload successful!'); - refreshFileList(); - $('#decryptBtn').prop('disabled', false); - } else { - alert('Upload failed: ' + response.error); + $.ajax({ + url: `/decrypt/${selectedFile}`, + method: 'POST', + data: { key: key }, + success: function(response) { + if (response.success) { + alert('Decryption successful!'); + refreshFileList(); + } else { + alert('Decryption failed: ' + response.error); + } + }, + error: function(xhr) { + alert('Decryption error: ' + xhr.responseJSON.error); } - }, - error: function() { - $('#uploadBtn').prop('disabled', false).text('Upload'); - alert('Upload failed due to network/server error.'); - } + }); }); -}); -// Download handler -function downloadFile(shadowname) { - window.location = '/download/' + encodeURIComponent(shadowname); -} + // Operation Controls + $('#startBtn').click(startOperation); + $('#pauseBtn').click(pauseOperation); + $('#resumeBtn').click(resumeOperation); -// Decrypt handler (dummy, as actual decryption logic depends on backend/client-side crypto) -function decryptFile(shadowname) { - alert('Decryption feature to be implemented as per your security model.'); -} + // Refresh file list every 30 seconds + setInterval(refreshFileList, 30000); -// Start operation -$('#startBtn').on('click', function() { - if (!currentOperationId) { - currentOperationId = generateOpId(); + // Utility Functions + function refreshFileList() { + $.get('/files/list', function(response) { + if (response.success) { + const fileList = $('#fileList'); + fileList.empty(); + + response.files.forEach(file => { + fileList.append(` +
    +
    + ${file.filename} + ${file.algorithm} +
    + +
    + `); + }); + } + }); } - $.ajax({ - url: '/operation/start', - method: 'POST', - contentType: 'application/json', - data: JSON.stringify({ op_id: currentOperationId }), - success: function(response) { + + function startOperation() { + currentOperationId = `op_${Date.now()}`; + $.post('/operation/start', { op_id: currentOperationId }, function(response) { if (response.success) { $('#pauseBtn').prop('disabled', false); $('#resumeBtn').prop('disabled', true); - $('#progressContainer').show(); pollProgress(); } - } - }); -}); + }); + } -// Pause operation -$('#pauseBtn').on('click', function() { - if (!currentOperationId) return; - $.ajax({ - url: '/operation/pause', - method: 'POST', - contentType: 'application/json', - data: JSON.stringify({ op_id: currentOperationId }), - success: function(response) { + function pauseOperation() { + $.post('/operation/pause', { op_id: currentOperationId }, function(response) { if (response.success) { $('#pauseBtn').prop('disabled', true); $('#resumeBtn').prop('disabled', false); } - } - }); -}); + }); + } -// Resume operation -$('#resumeBtn').on('click', function() { - if (!currentOperationId) return; - $.ajax({ - url: '/operation/resume', - method: 'POST', - contentType: 'application/json', - data: JSON.stringify({ op_id: currentOperationId }), - success: function(response) { + function resumeOperation() { + $.post('/operation/resume', { op_id: currentOperationId }, function(response) { if (response.success) { $('#pauseBtn').prop('disabled', false); $('#resumeBtn').prop('disabled', true); pollProgress(); } - } - }); -}); + }); + } -// Poll operation progress -function pollProgress() { - if (!currentOperationId) return; - $.ajax({ - url: '/operation/progress', - method: 'GET', - data: { op_id: currentOperationId }, - success: function(response) { + function pollProgress() { + $.get('/operation/progress', { op_id: currentOperationId }, function(response) { if (response.success) { - let progress = response.progress || 0; - $('#progressBar').val(progress); - $('#progressText').text(progress + '%'); - if (!response.paused && progress < 100) { + $('#progressBar').val(response.progress); + $('#progressText').text(response.progress + '%'); + + if (!response.paused && response.progress < 100) { setTimeout(pollProgress, 1000); - } else if (progress >= 100) { + } else if (response.progress >= 100) { $('#pauseBtn').prop('disabled', true); $('#resumeBtn').prop('disabled', true); - $('#progressContainer').hide(); - alert('Operation completed!'); } } - } - }); -} - -// Initial load -$(document).ready(function() { - refreshFileList(); - $('#progressContainer').hide(); - $('#pauseBtn, #resumeBtn, #decryptBtn').prop('disabled', true); + }); + } }); diff --git a/EncryptDecrypt/templates/index.html b/EncryptDecrypt/templates/index.html index 2bfcae7..758c77a 100644 --- a/EncryptDecrypt/templates/index.html +++ b/EncryptDecrypt/templates/index.html @@ -2,39 +2,69 @@ - Encryption-Decryption + + Secure File Manager
    -

    Encryption-Decryption

    -
    - - - - -
    +

    Secure File Manager

    + + +
    +

    Upload & Encrypt File

    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    -
    - - - - + +
    +

    Operations

    +
    + + + +
    +
    + + 0% +
    - - + + From 84ef8f7a9fcd0666706b424dfd2452d7bd6d90e4 Mon Sep 17 00:00:00 2001 From: Frahane Date: Tue, 6 May 2025 21:42:00 +0100 Subject: [PATCH 08/12] Adding init --- NessKeys/__init__.py | 0 NessKeys/cryptors/__init__.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 NessKeys/__init__.py create mode 100644 NessKeys/cryptors/__init__.py diff --git a/NessKeys/__init__.py b/NessKeys/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/NessKeys/cryptors/__init__.py b/NessKeys/cryptors/__init__.py new file mode 100644 index 0000000..e69de29 From ff5f0bc2c1ba51b3f4d0cd682a9aec600c063102 Mon Sep 17 00:00:00 2001 From: Frahane Date: Wed, 7 May 2025 09:37:08 +0100 Subject: [PATCH 09/12] Fixing Issues --- EncryptDecrypt/__init__.py | 0 EncryptDecrypt/app.py | 20 ++++++-------------- 2 files changed, 6 insertions(+), 14 deletions(-) create mode 100644 EncryptDecrypt/__init__.py diff --git a/EncryptDecrypt/__init__.py b/EncryptDecrypt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/EncryptDecrypt/app.py b/EncryptDecrypt/app.py index e2f4904..0ee9b84 100644 --- a/EncryptDecrypt/app.py +++ b/EncryptDecrypt/app.py @@ -1,20 +1,12 @@ -import sys import os -PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) -PARENT_DIR = os.path.dirname(PROJECT_ROOT) -if PROJECT_ROOT not in sys.path: - sys.path.insert(0, PROJECT_ROOT) -if PARENT_DIR not in sys.path: - sys.path.insert(0, PARENT_DIR) -PARENT_DIR = os.path.dirname(os.path.abspath(__file__)) # D:\PrivateNessTools -if PARENT_DIR not in sys.path: - sys.path.insert(0, PARENT_DIR) -print("sys.path:", sys.path) import logging -from flask import Flask, request, jsonify, send_file, render_template, g, abort + +from flask import Flask, request, jsonify, send_file, render_template, g from werkzeug.utils import secure_filename + from framework.Container import Container -from utils.db import get_db, init_db +from EncryptDecrypt.utils.db import get_db, init_db + from NessKeys.Cryptors.Aes import Aes from NessKeys.Cryptors.Salsa20 import Salsa20 from NessKeys.Cryptors.BlockCryptor import BlockCryptor @@ -26,6 +18,7 @@ logger = logging.getLogger(__name__) app = Flask(__name__) +PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) app.config['UPLOAD_FOLDER'] = os.path.join(PROJECT_ROOT, 'uploaded_files') app.secret_key = os.environ.get('SECRET_KEY', 'a-very-secret-key') os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) @@ -85,7 +78,6 @@ def download_file(self, shadowname: str) -> str: return file_path def decrypt_file(self, shadowname: str, key: str) -> str: - # Get file and algorithm from DB db = get_db() file_row = db.execute('SELECT * FROM files WHERE shadowname=?', (shadowname,)).fetchone() if not file_row: From 3435008cb4a7871737f8d1c6e91d96bcb97a286d Mon Sep 17 00:00:00 2001 From: Frahane Date: Thu, 15 May 2025 08:54:06 +0100 Subject: [PATCH 10/12] Modules importation Issue is fixed --- EncryptDecrypt/app.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/EncryptDecrypt/app.py b/EncryptDecrypt/app.py index 0ee9b84..0ae90d1 100644 --- a/EncryptDecrypt/app.py +++ b/EncryptDecrypt/app.py @@ -1,17 +1,15 @@ +import sys import os -import logging +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +import logging from flask import Flask, request, jsonify, send_file, render_template, g from werkzeug.utils import secure_filename from framework.Container import Container from EncryptDecrypt.utils.db import get_db, init_db - -from NessKeys.Cryptors.Aes import Aes -from NessKeys.Cryptors.Salsa20 import Salsa20 -from NessKeys.Cryptors.BlockCryptor import BlockCryptor -from NessKeys.Cryptors.PasswordCryptor import PasswordCryptor -from NessKeys.Cryptors.TextCryptor import TextCryptor +from NessKeys.cryptors.Aes import Aes +from NessKeys.cryptors.Salsa20 import Salsa20 # --- Logging Configuration --- logging.basicConfig(level=logging.INFO) @@ -40,11 +38,9 @@ def upload_file(self, file, algorithm, key) -> dict: enc_filename = filename + '.enc' enc_file_path = os.path.join(app.config['UPLOAD_FOLDER'], enc_filename) - # Read file data with open(file_path, 'rb') as f: data = f.read() - # Encrypt data if algorithm == 'AES': cryptor = Aes() elif algorithm == 'Salsa20': @@ -53,19 +49,17 @@ def upload_file(self, file, algorithm, key) -> dict: raise EncryptionError(f"Unsupported algorithm: {algorithm}") encrypted_data = cryptor.encrypt(data, key) + with open(enc_file_path, 'wb') as f: f.write(encrypted_data) - # Register in DB db = get_db() db.execute( 'INSERT INTO files (shadowname, filename, algorithm, key) VALUES (?, ?, ?, ?)', (enc_filename, filename, algorithm, key) ) db.commit() - # Remove original file for security os.remove(file_path) - return {"shadowname": enc_filename, "filename": filename, "algorithm": algorithm} except Exception as e: logger.error(f"Error uploading/encrypting file: {str(e)}") @@ -82,9 +76,9 @@ def decrypt_file(self, shadowname: str, key: str) -> str: file_row = db.execute('SELECT * FROM files WHERE shadowname=?', (shadowname,)).fetchone() if not file_row: raise FileNotFoundError(f"File {shadowname} not found in DB.") + algorithm = file_row['algorithm'] orig_filename = file_row['filename'] - enc_file_path = os.path.join(app.config['UPLOAD_FOLDER'], shadowname) dec_filename = orig_filename + '.dec' dec_file_path = os.path.join(app.config['UPLOAD_FOLDER'], dec_filename) @@ -101,7 +95,7 @@ def decrypt_file(self, shadowname: str, key: str) -> str: try: dec_data = cryptor.decrypt(enc_data, key) - except Exception as e: + except Exception: raise EncryptionError("Decryption failed. Wrong key or corrupted file.") with open(dec_file_path, 'wb') as f: @@ -124,6 +118,7 @@ def file_info(self, shadowname: str) -> dict: file_manager = FileManager() operation_states = {} +# --- Flask Lifecycle Hooks --- @app.before_first_request def setup(): init_db() @@ -134,6 +129,7 @@ def close_connection(exception): if db is not None: db.close() +# --- Routes --- @app.route("/") def index(): return render_template("index.html") @@ -155,7 +151,7 @@ def upload_file(): return jsonify({"success": False, "error": str(e)}), 500 @app.route("/download/", methods=["GET"]) -def download_file(shadowname): +def download_file_route(shadowname): try: file_path = file_manager.download_file(shadowname) return send_file(file_path, as_attachment=True) @@ -166,7 +162,7 @@ def download_file(shadowname): return jsonify({"success": False, "error": str(e)}), 500 @app.route("/decrypt/", methods=["POST"]) -def decrypt_file(shadowname): +def decrypt_file_route(shadowname): key = request.form.get("key") if not key: return jsonify({"success": False, "error": "Key is required"}), 400 From 9be4dea453cda0f76e048d93a56258a1c70f401b Mon Sep 17 00:00:00 2001 From: Frahane Date: Tue, 27 May 2025 13:54:42 +0100 Subject: [PATCH 11/12] Rebuilding App --- EncryptDecrypt/app.py | 430 +++++++++---------- EncryptDecrypt/static/css/styles.css | 77 ++++ EncryptDecrypt/static/index.css | 127 ------ EncryptDecrypt/static/script.js | 183 ++------ EncryptDecrypt/templates/decrypt.html | 17 + EncryptDecrypt/templates/encrypt.html | 14 + EncryptDecrypt/templates/encrypt_result.html | 11 + EncryptDecrypt/templates/index.html | 76 +--- EncryptDecrypt/templates/keygen.html | 9 + EncryptDecrypt/templates/layout.html | 31 ++ EncryptDecrypt/templates/nodes.html | 33 ++ EncryptDecrypt/templates/publish.html | 9 + EncryptDecrypt/templates/user.html | 12 + services/__init__.py | 0 14 files changed, 462 insertions(+), 567 deletions(-) create mode 100644 EncryptDecrypt/static/css/styles.css delete mode 100644 EncryptDecrypt/static/index.css create mode 100644 EncryptDecrypt/templates/decrypt.html create mode 100644 EncryptDecrypt/templates/encrypt.html create mode 100644 EncryptDecrypt/templates/encrypt_result.html create mode 100644 EncryptDecrypt/templates/keygen.html create mode 100644 EncryptDecrypt/templates/layout.html create mode 100644 EncryptDecrypt/templates/nodes.html create mode 100644 EncryptDecrypt/templates/publish.html create mode 100644 EncryptDecrypt/templates/user.html create mode 100644 services/__init__.py diff --git a/EncryptDecrypt/app.py b/EncryptDecrypt/app.py index 0ae90d1..c57a55f 100644 --- a/EncryptDecrypt/app.py +++ b/EncryptDecrypt/app.py @@ -1,233 +1,225 @@ import sys import os sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - -import logging -from flask import Flask, request, jsonify, send_file, render_template, g -from werkzeug.utils import secure_filename - +import json +import threading +from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_file +from NessKeys.KeyManager import KeyManager from framework.Container import Container -from EncryptDecrypt.utils.db import get_db, init_db -from NessKeys.cryptors.Aes import Aes -from NessKeys.cryptors.Salsa20 import Salsa20 - -# --- Logging Configuration --- -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) +from services.node import node as NodesUpdater +from NessKeys.JsonChecker.Checker import JsonChecker +from Crypto.Cipher import AES, Salsa20 +from Crypto.Random import get_random_bytes +# Initialize Flask app app = Flask(__name__) -PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) -app.config['UPLOAD_FOLDER'] = os.path.join(PROJECT_ROOT, 'uploaded_files') -app.secret_key = os.environ.get('SECRET_KEY', 'a-very-secret-key') -os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) - -# --- Custom Exceptions --- -class EncryptionError(Exception): pass -class DirectoryError(Exception): pass - -# --- FileManager Wrapper --- -class FileManager: - def __init__(self): - self.fm = Container.FileManager() +app.config.from_mapping( + SECRET_KEY=os.environ.get('FLASK_SECRET', 'replace-with-secure-key'), + DATA_DIR=os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data'), + UPLOAD_DIR=os.path.join(os.path.abspath(os.path.dirname(__file__)), 'uploads') +) + +# Ensure necessary directories exist +os.makedirs(app.config['DATA_DIR'], exist_ok=True) +os.makedirs(app.config['UPLOAD_DIR'], exist_ok=True) + +# Globals for background tasks +running_tasks = {} +tasks_lock = threading.Lock() + +# Encryption utilities +def encrypt_data(data: bytes, algo: str, key: bytes) -> dict: + if algo == 'salsa20': + cipher = Salsa20.new(key=key) + return {'nonce': cipher.nonce, 'ciphertext': cipher.encrypt(data)} + elif algo == 'aes': + iv = get_random_bytes(16) + cipher = AES.new(key, AES.MODE_CFB, iv=iv) + return {'iv': iv, 'ciphertext': cipher.encrypt(data)} + else: + raise ValueError('Unsupported algorithm') + +def decrypt_data(enc: dict, algo: str, key: bytes) -> bytes: + if algo == 'salsa20': + cipher = Salsa20.new(key=key, nonce=enc['nonce']) + return cipher.decrypt(enc['ciphertext']) + elif algo == 'aes': + cipher = AES.new(key, AES.MODE_CFB, iv=enc['iv']) + return cipher.decrypt(enc['ciphertext']) + else: + raise ValueError('Unsupported algorithm') + +# Home route +def get_data(filename): + path = os.path.join(app.config['DATA_DIR'], filename) + try: + with open(path, 'r', encoding='utf-8') as f: + return json.load(f) + except FileNotFoundError: + return None - def upload_file(self, file, algorithm, key) -> dict: +@app.route('/') +def index(): + return render_template('index.html') + +# Encryption endpoint +@app.route('/encrypt', methods=['GET', 'POST']) +def encrypt_route(): + if request.method == 'POST': + algo = request.form['algorithm'] + file = request.files['file'] + key = get_random_bytes(32) + in_path = os.path.join(app.config['UPLOAD_DIR'], file.filename) + file.save(in_path) + data = open(in_path, 'rb').read() + enc = encrypt_data(data, algo, key) + out_path = in_path + '.enc' + with open(out_path, 'wb') as f: + f.write(enc['ciphertext']) + metadata = {'algorithm': algo, 'key': key.hex()} + # Include nonce/iv in metadata + if 'nonce' in enc: + metadata['nonce'] = enc['nonce'].hex() + if 'iv' in enc: + metadata['iv'] = enc['iv'].hex() + return render_template('encrypt_result.html', filename=os.path.basename(out_path), metadata=metadata) + return render_template('encrypt.html') + +# Decryption endpoint +@app.route('/decrypt', methods=['GET', 'POST']) +def decrypt_route(): + if request.method == 'POST': + algo = request.form['algorithm'] + key = bytes.fromhex(request.form['key']) + file = request.files['file'] + in_path = os.path.join(app.config['UPLOAD_DIR'], file.filename) + file.save(in_path) + enc = {'ciphertext': open(in_path, 'rb').read()} + if algo == 'salsa20': + enc['nonce'] = bytes.fromhex(request.form['nonce']) + elif algo == 'aes': + enc['iv'] = bytes.fromhex(request.form['iv']) + data = decrypt_data(enc, algo, key) + out_path = in_path + '.dec' + with open(out_path, 'wb') as f: + f.write(data) + return send_file(out_path, as_attachment=True) + return render_template('decrypt.html') + +# Generate user key +@app.route('/keygen', methods=['GET', 'POST']) +def keygen(): + if request.method == 'POST': + username = request.form['username'] + entropy = int(request.form['entropy']) try: - filename = secure_filename(file.filename) - file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) - file.save(file_path) - enc_filename = filename + '.enc' - enc_file_path = os.path.join(app.config['UPLOAD_FOLDER'], enc_filename) - - with open(file_path, 'rb') as f: - data = f.read() - - if algorithm == 'AES': - cryptor = Aes() - elif algorithm == 'Salsa20': - cryptor = Salsa20() - else: - raise EncryptionError(f"Unsupported algorithm: {algorithm}") - - encrypted_data = cryptor.encrypt(data, key) - - with open(enc_file_path, 'wb') as f: - f.write(encrypted_data) - - db = get_db() - db.execute( - 'INSERT INTO files (shadowname, filename, algorithm, key) VALUES (?, ?, ?, ?)', - (enc_filename, filename, algorithm, key) - ) - db.commit() - os.remove(file_path) - return {"shadowname": enc_filename, "filename": filename, "algorithm": algorithm} + km = KeyManager() + km.createUsersKey(username, entropy) + flash(f'User key generated: {username}', 'success') except Exception as e: - logger.error(f"Error uploading/encrypting file: {str(e)}") - raise - - def download_file(self, shadowname: str) -> str: - file_path = os.path.join(app.config['UPLOAD_FOLDER'], shadowname) - if not os.path.exists(file_path): - raise FileNotFoundError(f"File {shadowname} not found.") - return file_path - - def decrypt_file(self, shadowname: str, key: str) -> str: - db = get_db() - file_row = db.execute('SELECT * FROM files WHERE shadowname=?', (shadowname,)).fetchone() - if not file_row: - raise FileNotFoundError(f"File {shadowname} not found in DB.") - - algorithm = file_row['algorithm'] - orig_filename = file_row['filename'] - enc_file_path = os.path.join(app.config['UPLOAD_FOLDER'], shadowname) - dec_filename = orig_filename + '.dec' - dec_file_path = os.path.join(app.config['UPLOAD_FOLDER'], dec_filename) - - with open(enc_file_path, 'rb') as f: - enc_data = f.read() - - if algorithm == 'AES': - cryptor = Aes() - elif algorithm == 'Salsa20': - cryptor = Salsa20() - else: - raise EncryptionError(f"Unsupported algorithm: {algorithm}") - + flash(f'Error: {e}', 'danger') + return redirect(url_for('keygen')) + return render_template('keygen.html') + +# Update nodes list +def update_nodes(rpc_args=None): + updater = NodesUpdater() + if rpc_args: + updater.fetch_from_blockchain(*rpc_args) + else: + updater.fetch_from_public() + updater.save(app.config['DATA_DIR']) + +@app.route('/nodes', methods=['GET', 'POST']) +def nodes(): + if request.method == 'POST': + mode = request.form['mode'] + rpc = None + if mode == 'rpc': + rpc = ( + request.form['rpc_host'], + int(request.form['rpc_port']), + request.form['rpc_user'], + request.form['rpc_password'] + ) try: - dec_data = cryptor.decrypt(enc_data, key) - except Exception: - raise EncryptionError("Decryption failed. Wrong key or corrupted file.") - - with open(dec_file_path, 'wb') as f: - f.write(dec_data) - - return dec_file_path - - def list_files(self) -> list: - db = get_db() - files = db.execute('SELECT * FROM files').fetchall() - return [dict(f) for f in files] - - def file_info(self, shadowname: str) -> dict: - db = get_db() - file = db.execute('SELECT * FROM files WHERE shadowname=?', (shadowname,)).fetchone() - if not file: - raise FileNotFoundError(f"File {shadowname} not found.") - return dict(file) - -file_manager = FileManager() -operation_states = {} - -# --- Flask Lifecycle Hooks --- -@app.before_first_request -def setup(): - init_db() - -@app.teardown_appcontext -def close_connection(exception): - db = getattr(g, '_database', None) - if db is not None: - db.close() - -# --- Routes --- -@app.route("/") -def index(): - return render_template("index.html") - -@app.route("/upload", methods=["POST"]) -def upload_file(): - if "file" not in request.files: - return jsonify({"success": False, "error": "No file part in the request"}), 400 - file = request.files["file"] - algorithm = request.form.get("algorithm") - key = request.form.get("key") - if not algorithm or not key: - return jsonify({"success": False, "error": "Algorithm and key are required"}), 400 - try: - result = file_manager.upload_file(file, algorithm, key) - return jsonify({"success": True, "file": result}) - except Exception as e: - logger.exception("Upload failed") - return jsonify({"success": False, "error": str(e)}), 500 - -@app.route("/download/", methods=["GET"]) -def download_file_route(shadowname): - try: - file_path = file_manager.download_file(shadowname) - return send_file(file_path, as_attachment=True) - except FileNotFoundError as e: - return jsonify({"success": False, "error": str(e)}), 404 - except Exception as e: - logger.exception("Download failed") - return jsonify({"success": False, "error": str(e)}), 500 - -@app.route("/decrypt/", methods=["POST"]) -def decrypt_file_route(shadowname): - key = request.form.get("key") - if not key: - return jsonify({"success": False, "error": "Key is required"}), 400 - try: - dec_file_path = file_manager.decrypt_file(shadowname, key) - return send_file(dec_file_path, as_attachment=True) - except EncryptionError as e: - logger.exception("Decryption failed") - return jsonify({"success": False, "error": str(e)}), 400 - except FileNotFoundError as e: - return jsonify({"success": False, "error": str(e)}), 404 - except Exception as e: - logger.exception("Decryption failed") - return jsonify({"success": False, "error": str(e)}), 500 - -@app.route("/files/list", methods=["GET"]) -def list_files(): + update_nodes(rpc) + flash('Nodes list updated', 'success') + except Exception as e: + flash(f'Error: {e}', 'danger') + return redirect(url_for('nodes')) + data = get_data('nodes.json') or {'nodes': []} + return render_template('nodes.html', nodes=data['nodes']) + +# Select service node +@app.route('/nodes/select', methods=['POST']) +def select_node(): + node_url = request.form['node_url'] try: - files = file_manager.list_files() - return jsonify({"success": True, "files": files}) + km = KeyManager() + km.selectNode(node_url) + flash(f'Selected node: {node_url}', 'success') except Exception as e: - logger.exception("List files failed") - return jsonify({"success": False, "error": str(e)}), 500 - -@app.route("/files/info/", methods=["GET"]) -def file_info(shadowname): + flash(f'Error: {e}', 'danger') + return redirect(url_for('nodes')) + +# Select current user +@app.route('/user', methods=['GET', 'POST']) +def user(): + km = KeyManager() + users = list(km.listUsers()) + if request.method == 'POST': + username = request.form['username'] + try: + km.changeCurrentUser(username) + flash(f'Current user set: {username}', 'success') + except Exception as e: + flash(f'Error: {e}', 'danger') + return redirect(url_for('user')) + return render_template('user.html', users=users) + +# Prepare NVS records +@app.route('/publish', methods=['POST']) +def publish(): + km = KeyManager() + action = request.form['action'] try: - info = file_manager.file_info(shadowname) - return jsonify({"success": True, "info": info}) - except FileNotFoundError as e: - return jsonify({"success": False, "error": str(e)}), 404 + if action == 'user': + km.prepareUserNVS() + elif action == 'worm': + km.prepareWormNVS() + elif action == 'nodes': + km.prepareNodesNVS() + flash(f'{action.capitalize()} NVS prepared', 'success') except Exception as e: - logger.exception("File info failed") - return jsonify({"success": False, "error": str(e)}), 500 - -# --- Pause/Resume/Start/Progress endpoints --- -@app.route("/operation/start", methods=["POST"]) -def start_operation(): - op_id = request.json.get("op_id") - operation_states[op_id] = {"paused": False, "progress": 0} - return jsonify({"success": True, "status": "started"}) - -@app.route("/operation/pause", methods=["POST"]) -def pause_operation(): - op_id = request.json.get("op_id") - if op_id not in operation_states: - return jsonify({"success": False, "error": "Invalid operation ID"}), 400 - operation_states[op_id]["paused"] = True - return jsonify({"success": True, "status": "paused"}) - -@app.route("/operation/resume", methods=["POST"]) -def resume_operation(): - op_id = request.json.get("op_id") - if op_id not in operation_states: - return jsonify({"success": False, "error": "Invalid operation ID"}), 400 - operation_states[op_id]["paused"] = False - return jsonify({"success": True, "status": "resumed"}) - -@app.route("/operation/progress", methods=["GET"]) -def operation_progress(): - op_id = request.args.get("op_id") - state = operation_states.get(op_id) - if not state: - return jsonify({"success": False, "error": "Invalid operation ID"}), 400 - return jsonify({"success": True, "progress": state["progress"], "paused": state["paused"]}) - -if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000, debug=False) + flash(f'Error: {e}', 'danger') + return redirect(url_for('index')) + +# Background task control +@app.route('/task//start', methods=['POST']) +def task_start(task_id): + with tasks_lock: + running_tasks[task_id] = {'status': 'running', 'progress': 0} + return jsonify(running_tasks[task_id]) + +@app.route('/task//pause', methods=['POST']) +def task_pause(task_id): + with tasks_lock: + if task_id in running_tasks: + running_tasks[task_id]['status'] = 'paused' + return jsonify(running_tasks.get(task_id, {})) + +@app.route('/task//resume', methods=['POST']) +def task_resume(task_id): + with tasks_lock: + if task_id in running_tasks: + running_tasks[task_id]['status'] = 'running' + return jsonify(running_tasks.get(task_id, {})) + +@app.route('/task//progress', methods=['GET']) +def task_progress(task_id): + return jsonify(running_tasks.get(task_id, {'status': 'unknown', 'progress': 0})) + +if __name__ == '__main__': + # app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000))) + app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/EncryptDecrypt/static/css/styles.css b/EncryptDecrypt/static/css/styles.css new file mode 100644 index 0000000..8ad0a35 --- /dev/null +++ b/EncryptDecrypt/static/css/styles.css @@ -0,0 +1,77 @@ +body { + background: #101c2a; color: #e0e0e0; + font-family: 'Fira Sans', Arial, sans-serif; + margin: 0; + max-width: 700px; + margin: 40px auto; + background: #171f2e; + border-radius: 12px; + box-shadow: 0 0 12px #0008; + padding: 2em 2.5em; + + } +/*.container { + max-width: 700px; + margin: 40px auto; + background: #171f2e; + border-radius: 12px; + box-shadow: 0 0 12px #0008; + padding: 2em 2.5em; +}*/ +h1 { + color: #6ec6ff; + text-align: center; + } + + nav a { + color: #2196f3; + } + nav a:hover { + color: #e0e0e0; + } + +label { + display: block; + margin-top: 1.3em; + font-weight: bold; +} +input, +textarea, +select { + width: 100%; + margin-top: 0.3em; + padding: 0.5em; + font-size: 1em; + border-radius: 6px; + border: 1px solid #28334a; + background: #101c2a; + color: #e0e0e0; +} +button { + margin-top: 1.5em; + padding: 0.7em 2em; + border: none; + border-radius: 6px; + background: #6ec6ff; + color: #101c2a; + font-weight: bold; + cursor: pointer; + font-size: 1.1em; +} +button:active { + background: #2196f3; +} +.flashes { + list-style: none; + padding: 0; +} +.flashes li { + margin-bottom: 0.5em; +} +.flashes .success { + color: #6efc97; +} +.flashes .danger { + color: #fc6e6e; +} +@media (max-width: 800px) { .container { padding: 1em 0.5em; } } \ No newline at end of file diff --git a/EncryptDecrypt/static/index.css b/EncryptDecrypt/static/index.css deleted file mode 100644 index 222b415..0000000 --- a/EncryptDecrypt/static/index.css +++ /dev/null @@ -1,127 +0,0 @@ -body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - margin: 0; - padding: 20px; - background-color: #1a1a1a; - color: #ffffff; -} - -.container { - max-width: 1200px; - margin: 0 auto; -} - -.section { - background: #2d2d2d; - border-radius: 8px; - padding: 20px; - margin-bottom: 20px; -} - -h1, h2 { - color: #4CAF50; -} - -/* Form Elements */ -.form-group { - margin-bottom: 15px; -} - -input[type="file"], -input[type="password"], -select { - width: 100%; - padding: 8px; - margin: 5px 0; - background: #333; - border: 1px solid #444; - color: white; - border-radius: 4px; -} - -button { - background: #4CAF50; - color: white; - border: none; - padding: 10px 20px; - border-radius: 4px; - cursor: pointer; - transition: background 0.3s; -} - -button:hover { - background: #45a049; -} - -button:disabled { - background: #666; - cursor: not-allowed; -} - -/* File List */ -.file-list { - display: grid; - gap: 10px; -} - -.file-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 10px; - background: #333; - border-radius: 4px; - cursor: pointer; -} - -.file-item.selected { - background: #404040; - outline: 2px solid #4CAF50; -} - -.file-info { - display: flex; - flex-direction: column; -} - -.filename { - font-weight: bold; -} - -.algorithm { - color: #888; - font-size: 0.9em; -} - -/* Progress Bar */ -#progressContainer { - margin-top: 15px; -} - -progress { - width: 100%; - height: 20px; - border-radius: 4px; -} - -#progressText { - display: block; - text-align: center; - margin-top: 5px; -} - -/* Responsive Design */ -@media (max-width: 768px) { - .container { - padding: 10px; - } - - .file-item { - flex-direction: column; - align-items: flex-start; - } - - .download-btn { - margin-top: 10px; - } -} diff --git a/EncryptDecrypt/static/script.js b/EncryptDecrypt/static/script.js index aba2102..a779dc5 100644 --- a/EncryptDecrypt/static/script.js +++ b/EncryptDecrypt/static/script.js @@ -1,157 +1,38 @@ $(document).ready(function() { - let currentOperationId = null; - let selectedFile = null; - - // Initialize file list - refreshFileList(); - - // File Upload Handler - $('#uploadForm').on('submit', function(e) { - e.preventDefault(); - const formData = new FormData(); - formData.append('file', $('#fileInput')[0].files[0]); - formData.append('algorithm', $('#algorithm').val()); - formData.append('key', $('#encryptionKey').val()); - - $('#uploadBtn').prop('disabled', true).text('Encrypting...'); - - $.ajax({ - url: '/upload', - method: 'POST', - data: formData, - processData: false, - contentType: false, - success: function(response) { - if (response.success) { - alert('File encrypted successfully!'); - refreshFileList(); - $('#uploadForm')[0].reset(); - } else { - alert('Error: ' + response.error); - } - }, - error: function(xhr) { - alert('Upload failed: ' + xhr.responseJSON.error); - }, - complete: function() { - $('#uploadBtn').prop('disabled', false).text('Upload & Encrypt'); - } - }); + // Toggle RPC fields in nodes form + $('input[name="mode"]').change(function() { + if ($(this).val() === 'rpc') { + $('#rpc-fields').show(); + } else { + $('#rpc-fields').hide(); + } }); - - // File List Click Handler - $(document).on('click', '.file-item', function() { - $('.file-item').removeClass('selected'); - $(this).addClass('selected'); - selectedFile = $(this).data('shadowname'); - $('#decryptSection').show(); + + // Handle task controls (example) + $('.task-start').click(function() { + const taskId = $(this).data('task-id'); + $.post(`/task/${taskId}/start`, function(res) { + console.log('Started:', res); + }); }); - - // Download Handler - $(document).on('click', '.download-btn', function() { - const shadowname = $(this).closest('.file-item').data('shadowname'); - window.location = `/download/${encodeURIComponent(shadowname)}`; + $('.task-pause').click(function() { + const taskId = $(this).data('task-id'); + $.post(`/task/${taskId}/pause`, function(res) { + console.log('Paused:', res); + }); }); - - // Decrypt Handler - $('#decryptBtn').click(function() { - const key = $('#decryptionKey').val(); - if (!key) { - alert('Please enter decryption key'); - return; - } - - $.ajax({ - url: `/decrypt/${selectedFile}`, - method: 'POST', - data: { key: key }, - success: function(response) { - if (response.success) { - alert('Decryption successful!'); - refreshFileList(); - } else { - alert('Decryption failed: ' + response.error); - } - }, - error: function(xhr) { - alert('Decryption error: ' + xhr.responseJSON.error); - } - }); + $('.task-resume').click(function() { + const taskId = $(this).data('task-id'); + $.post(`/task/${taskId}/resume`, function(res) { + console.log('Resumed:', res); + }); }); - - // Operation Controls - $('#startBtn').click(startOperation); - $('#pauseBtn').click(pauseOperation); - $('#resumeBtn').click(resumeOperation); - - // Refresh file list every 30 seconds - setInterval(refreshFileList, 30000); - - // Utility Functions - function refreshFileList() { - $.get('/files/list', function(response) { - if (response.success) { - const fileList = $('#fileList'); - fileList.empty(); - - response.files.forEach(file => { - fileList.append(` -
    -
    - ${file.filename} - ${file.algorithm} -
    - -
    - `); - }); - } - }); + + // Example: poll progress + function pollProgress(taskId) { + $.get(`/task/${taskId}/progress`, function(res) { + $(`#progress-${taskId}`).text(res.progress + '% - ' + res.status); + if (res.status === 'running') setTimeout(() => pollProgress(taskId), 1000); + }); } - - function startOperation() { - currentOperationId = `op_${Date.now()}`; - $.post('/operation/start', { op_id: currentOperationId }, function(response) { - if (response.success) { - $('#pauseBtn').prop('disabled', false); - $('#resumeBtn').prop('disabled', true); - pollProgress(); - } - }); - } - - function pauseOperation() { - $.post('/operation/pause', { op_id: currentOperationId }, function(response) { - if (response.success) { - $('#pauseBtn').prop('disabled', true); - $('#resumeBtn').prop('disabled', false); - } - }); - } - - function resumeOperation() { - $.post('/operation/resume', { op_id: currentOperationId }, function(response) { - if (response.success) { - $('#pauseBtn').prop('disabled', false); - $('#resumeBtn').prop('disabled', true); - pollProgress(); - } - }); - } - - function pollProgress() { - $.get('/operation/progress', { op_id: currentOperationId }, function(response) { - if (response.success) { - $('#progressBar').val(response.progress); - $('#progressText').text(response.progress + '%'); - - if (!response.paused && response.progress < 100) { - setTimeout(pollProgress, 1000); - } else if (response.progress >= 100) { - $('#pauseBtn').prop('disabled', true); - $('#resumeBtn').prop('disabled', true); - } - } - }); - } -}); + }); \ No newline at end of file diff --git a/EncryptDecrypt/templates/decrypt.html b/EncryptDecrypt/templates/decrypt.html new file mode 100644 index 0000000..f044fec --- /dev/null +++ b/EncryptDecrypt/templates/decrypt.html @@ -0,0 +1,17 @@ +{% extends 'layout.html' %} +{% block content %} +

    Decrypt File

    +
    +
    +
    +
    +
    +
    + +
    +{% endblock %} \ No newline at end of file diff --git a/EncryptDecrypt/templates/encrypt.html b/EncryptDecrypt/templates/encrypt.html new file mode 100644 index 0000000..d336edf --- /dev/null +++ b/EncryptDecrypt/templates/encrypt.html @@ -0,0 +1,14 @@ +{% extends 'layout.html' %} +{% block content %} +

    Encrypt File

    +
    +
    +
    + +
    +{% endblock %} \ No newline at end of file diff --git a/EncryptDecrypt/templates/encrypt_result.html b/EncryptDecrypt/templates/encrypt_result.html new file mode 100644 index 0000000..7a435af --- /dev/null +++ b/EncryptDecrypt/templates/encrypt_result.html @@ -0,0 +1,11 @@ +{% extends 'layout.html' %} +{% block content %} +

    Encryption Result

    +

    Encrypted file: {{ filename }}

    +

    Metadata

    +
      + {% for key, val in metadata.items() %} +
    • {{ key }}: {{ val }}
    • + {% endfor %} +
    +{% endblock %} \ No newline at end of file diff --git a/EncryptDecrypt/templates/index.html b/EncryptDecrypt/templates/index.html index 758c77a..6aefce7 100644 --- a/EncryptDecrypt/templates/index.html +++ b/EncryptDecrypt/templates/index.html @@ -1,70 +1,6 @@ - - - - - - Secure File Manager - - - -
    -

    Secure File Manager

    - - -
    -

    Upload & Encrypt File

    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    - - -
    -

    Operations

    -
    - - - -
    -
    - - 0% -
    -
    - - -
    -

    Encrypted Files

    -
    - -
    -
    - - - -
    - - - - - +{% extends 'layout.html' %} +{% block content %} +

    Welcome to PrivateNess Network

    +

    Use the navigation menu to encrypt, decrypt, manage keys, nodes, and users.

    +

    Or start encrypting now.

    +{% endblock %} \ No newline at end of file diff --git a/EncryptDecrypt/templates/keygen.html b/EncryptDecrypt/templates/keygen.html new file mode 100644 index 0000000..0726c84 --- /dev/null +++ b/EncryptDecrypt/templates/keygen.html @@ -0,0 +1,9 @@ +{% extends 'layout.html' %} +{% block content %} +

    Generate User Key

    +
    +
    +
    + +
    +{% endblock %} \ No newline at end of file diff --git a/EncryptDecrypt/templates/layout.html b/EncryptDecrypt/templates/layout.html new file mode 100644 index 0000000..a435d70 --- /dev/null +++ b/EncryptDecrypt/templates/layout.html @@ -0,0 +1,31 @@ + + + + + EncryptDecrypt + + + +

    PrivateNess Data Security

    + +
    + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
      + {% for category, msg in messages %} +
    • {{ msg }}
    • + {% endfor %} +
    + {% endif %} + {% endwith %} + {% block content %}{% endblock %} +
    + + \ No newline at end of file diff --git a/EncryptDecrypt/templates/nodes.html b/EncryptDecrypt/templates/nodes.html new file mode 100644 index 0000000..aaa95b6 --- /dev/null +++ b/EncryptDecrypt/templates/nodes.html @@ -0,0 +1,33 @@ +{% extends 'layout.html' %} +{% block content %} +

    Manage Nodes

    +
    + +
    + + +
    + +

    Available Nodes

    +
      + {% for node in nodes %} +
    • + {{ node.url }} ({{ node.network }}, Services: {{ node.services|join(', ') }}) +
      + + +
      +
    • + {% endfor %} +
    + +{% endblock %} \ No newline at end of file diff --git a/EncryptDecrypt/templates/publish.html b/EncryptDecrypt/templates/publish.html new file mode 100644 index 0000000..83ae581 --- /dev/null +++ b/EncryptDecrypt/templates/publish.html @@ -0,0 +1,9 @@ +{% extends 'layout.html' %} +{% block content %} +

    Prepare NVS Records

    +
    + + + +
    +{% endblock %} \ No newline at end of file diff --git a/EncryptDecrypt/templates/user.html b/EncryptDecrypt/templates/user.html new file mode 100644 index 0000000..322b4fb --- /dev/null +++ b/EncryptDecrypt/templates/user.html @@ -0,0 +1,12 @@ +{% extends 'layout.html' %} +{% block content %} +

    Select User

    +
    + + +
    +{% endblock %} \ No newline at end of file diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..e69de29 From 0c895a2887ba9181eb9f1d551accb3fa697642f0 Mon Sep 17 00:00:00 2001 From: Frahane Date: Mon, 2 Jun 2025 11:33:43 +0100 Subject: [PATCH 12/12] Fixing user.py sel --- EncryptDecrypt/app.py | 523 +++++++++++++++++---------- EncryptDecrypt/static/css/styles.css | 7 +- EncryptDecrypt/templates/nodes.html | 4 +- EncryptDecrypt/templates/user.html | 14 +- NessKeys/Prng.py | 6 +- 5 files changed, 345 insertions(+), 209 deletions(-) diff --git a/EncryptDecrypt/app.py b/EncryptDecrypt/app.py index c57a55f..208c1cc 100644 --- a/EncryptDecrypt/app.py +++ b/EncryptDecrypt/app.py @@ -1,225 +1,346 @@ import sys import os -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -import json import threading +import json from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_file -from NessKeys.KeyManager import KeyManager +import subprocess +# extend PYTHONPATH to project root +dir_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.insert(0, dir_root) + +# Import container for dependency injection from framework.Container import Container +# Import key management and cryptors from NessKeys +from NessKeys.KeyManager import KeyManager from services.node import node as NodesUpdater +from NessKeys.cryptors.Aes import Aes as AESCipher +from NessKeys.cryptors.Salsa20 import Salsa20 as Salsa20Cipher from NessKeys.JsonChecker.Checker import JsonChecker -from Crypto.Cipher import AES, Salsa20 -from Crypto.Random import get_random_bytes # Initialize Flask app -app = Flask(__name__) -app.config.from_mapping( - SECRET_KEY=os.environ.get('FLASK_SECRET', 'replace-with-secure-key'), - DATA_DIR=os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data'), - UPLOAD_DIR=os.path.join(os.path.abspath(os.path.dirname(__file__)), 'uploads') -) - -# Ensure necessary directories exist -os.makedirs(app.config['DATA_DIR'], exist_ok=True) -os.makedirs(app.config['UPLOAD_DIR'], exist_ok=True) - -# Globals for background tasks -running_tasks = {} -tasks_lock = threading.Lock() - -# Encryption utilities -def encrypt_data(data: bytes, algo: str, key: bytes) -> dict: - if algo == 'salsa20': - cipher = Salsa20.new(key=key) - return {'nonce': cipher.nonce, 'ciphertext': cipher.encrypt(data)} - elif algo == 'aes': - iv = get_random_bytes(16) - cipher = AES.new(key, AES.MODE_CFB, iv=iv) - return {'iv': iv, 'ciphertext': cipher.encrypt(data)} - else: - raise ValueError('Unsupported algorithm') - -def decrypt_data(enc: dict, algo: str, key: bytes) -> bytes: - if algo == 'salsa20': - cipher = Salsa20.new(key=key, nonce=enc['nonce']) - return cipher.decrypt(enc['ciphertext']) - elif algo == 'aes': - cipher = AES.new(key, AES.MODE_CFB, iv=enc['iv']) - return cipher.decrypt(enc['ciphertext']) - else: - raise ValueError('Unsupported algorithm') - -# Home route -def get_data(filename): - path = os.path.join(app.config['DATA_DIR'], filename) - try: - with open(path, 'r', encoding='utf-8') as f: - return json.load(f) - except FileNotFoundError: - return None - -@app.route('/') -def index(): - return render_template('index.html') - -# Encryption endpoint -@app.route('/encrypt', methods=['GET', 'POST']) -def encrypt_route(): - if request.method == 'POST': - algo = request.form['algorithm'] - file = request.files['file'] - key = get_random_bytes(32) - in_path = os.path.join(app.config['UPLOAD_DIR'], file.filename) - file.save(in_path) - data = open(in_path, 'rb').read() - enc = encrypt_data(data, algo, key) - out_path = in_path + '.enc' - with open(out_path, 'wb') as f: - f.write(enc['ciphertext']) - metadata = {'algorithm': algo, 'key': key.hex()} - # Include nonce/iv in metadata - if 'nonce' in enc: - metadata['nonce'] = enc['nonce'].hex() - if 'iv' in enc: - metadata['iv'] = enc['iv'].hex() - return render_template('encrypt_result.html', filename=os.path.basename(out_path), metadata=metadata) - return render_template('encrypt.html') - -# Decryption endpoint -@app.route('/decrypt', methods=['GET', 'POST']) -def decrypt_route(): - if request.method == 'POST': - algo = request.form['algorithm'] - key = bytes.fromhex(request.form['key']) - file = request.files['file'] - in_path = os.path.join(app.config['UPLOAD_DIR'], file.filename) - file.save(in_path) - enc = {'ciphertext': open(in_path, 'rb').read()} - if algo == 'salsa20': - enc['nonce'] = bytes.fromhex(request.form['nonce']) - elif algo == 'aes': - enc['iv'] = bytes.fromhex(request.form['iv']) - data = decrypt_data(enc, algo, key) - out_path = in_path + '.dec' - with open(out_path, 'wb') as f: - f.write(data) - return send_file(out_path, as_attachment=True) - return render_template('decrypt.html') - -# Generate user key -@app.route('/keygen', methods=['GET', 'POST']) -def keygen(): - if request.method == 'POST': +def create_app(): + app = Flask(__name__) + # Configuration + app.config.from_mapping( + SECRET_KEY=os.environ.get('FLASK_SECRET', 'supersecret'), + UPLOAD_FOLDER=os.environ.get('UPLOAD_FOLDER', os.path.join(dir_root, 'uploads')), + NODES_JSON=os.environ.get('NODES_JSON', os.path.join(dir_root, 'nodes.json')), + MAX_CONTENT_LENGTH=16 * 1024 * 1024 # 16MB limit + ) + + # Ensure upload folder exists + os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) + + # Thread-safe storage for background tasks + tasks_lock = threading.Lock() + running_tasks = {} + + # Helpers + def get_km(): + return Container.KeyManager() + filemgr = Container.FileManager() + + # ----- Routes ----- + + @app.route('/') + def index(): + return render_template('index.html') + + @app.route('/keygen', methods=['GET', 'POST']) + def keygen(): + if request.method == 'GET': + return render_template('keygen.html') username = request.form['username'] - entropy = int(request.form['entropy']) + entropy = int(request.form.get('entropy', 256)) try: - km = KeyManager() + km = get_km() km.createUsersKey(username, entropy) flash(f'User key generated: {username}', 'success') except Exception as e: - flash(f'Error: {e}', 'danger') + flash(f'Error generating key: {e}', 'danger') return redirect(url_for('keygen')) - return render_template('keygen.html') - -# Update nodes list -def update_nodes(rpc_args=None): - updater = NodesUpdater() - if rpc_args: - updater.fetch_from_blockchain(*rpc_args) - else: - updater.fetch_from_public() - updater.save(app.config['DATA_DIR']) - -@app.route('/nodes', methods=['GET', 'POST']) -def nodes(): - if request.method == 'POST': - mode = request.form['mode'] - rpc = None - if mode == 'rpc': - rpc = ( - request.form['rpc_host'], - int(request.form['rpc_port']), - request.form['rpc_user'], - request.form['rpc_password'] + + @app.route('/nodes', methods=['GET', 'POST']) + def nodes(): + km = get_km() + if request.method == 'POST': + mode = request.form['mode'] + try: + if mode == 'public': + NodesUpdater.fetch_public_list() + flash('Public list fetched', 'success') + else: + rpc = { + 'host': request.form['rpc_host'], + 'port': request.form['rpc_port'], + 'user': request.form['rpc_user'], + 'password': request.form['rpc_password'] + } + NodesUpdater.update_blockchain(rpc, app.config['NODES_JSON']) + flash('Updated from RPC', 'success') + except Exception as e: + flash(f'Error managing nodes: {e}', 'danger') + return redirect(url_for('nodes')) + nodes = km.listNodes() + return render_template('nodes.html', nodes=nodes) + + @app.route('/node', methods=['POST']) + def select_node(): + """Mark a node as selected in the nodes.json file, creating it if missing.""" + node_url = request.form['node_url'] + nodes_file = app.config['NODES_JSON'] + try: + # Load or initialize nodes JSON + if not os.path.exists(nodes_file): + data = {'nodes': [], 'selected_node': node_url} + else: + with open(nodes_file, 'r') as f: + try: + data = json.load(f) + except json.JSONDecodeError: + data = {} + # Ensure dict + if not isinstance(data, dict): + data = {'nodes': data} + data['selected_node'] = node_url + # Write back + with open(nodes_file, 'w') as f: + json.dump(data, f, indent=2) + flash(f'Node selected: {node_url}', 'success') + except Exception as e: + flash(f'Error selecting node: {e}', 'danger') + return redirect(url_for('nodes')) + + @app.route('/user', methods=['GET', 'POST']) + def user(): + # POST: perform sel | nvs | worm via user.py + if request.method == 'POST': + username = request.form.get('username', '').strip() + action = request.form.get('action', '').strip() + + if not username: + flash('Please select a user first.', 'warning') + return redirect(url_for('user')) + + if action not in ('sel', 'nvs', 'worm'): + flash('Invalid action.', 'danger') + return redirect(url_for('user')) + + cli = os.path.join(dir_root, 'user.py') + cmd = [sys.executable, cli, action, username] + + try: + result = subprocess.run( + cmd, + cwd=dir_root, + capture_output=True, + text=True, + check=True + ) + # Use stdout if available, else fallback to a canned message + out = result.stdout.strip() or f'User {action} completed for {username}.' + flash(out, 'success') + except subprocess.CalledProcessError as e: + err = e.stderr.strip() or e.stdout.strip() or str(e) + flash(f'Error ({action}): {err}', 'danger') + + return redirect(url_for('user')) + + # GET: fetch users via `user.py ls` + users = [] + cli = os.path.join(dir_root, 'user.py') + try: + result = subprocess.run( + [sys.executable, cli, 'ls'], + cwd=dir_root, + capture_output=True, + text=True, + check=True ) + for line in result.stdout.splitlines(): + line = line.strip() + # Lines that start with '|' and contain real content + if line.startswith('|') and not set(line.strip()).issubset({'|','-'}): + name = line.strip('|').strip() + # strip CLI arrows: ==> user <== + name = name.replace('==>', '').replace('<==', '').strip() + if name.lower() != 'username': + users.append(name) + except subprocess.CalledProcessError as e: + flash('Failed to fetch users. Please ensure user.py is working.', 'danger') + + return render_template('user.html', users=users) + + @app.route('/publish', methods=['GET', 'POST']) + def publish(): + if request.method == 'GET': + return render_template('publish.html') + action = request.form['action'] + try: + km = get_km() + if action == 'user': + km.prepareUserNVS() + elif action == 'worm': + km.prepareWormNVS() + elif action == 'nodes': + km.prepareNodesNVS() + flash('Prepared NVS records', 'success') + except Exception as e: + flash(f'Error preparing NVS: {e}', 'danger') + return redirect(url_for('publish')) + + # File system operations + @app.route('/upload', methods=['POST']) + def upload(): + file = request.files['file'] + dest = request.form.get('destination', '') + try: + filemgr.upload(file, dest) + flash('Upload successful', 'success') + except Exception as e: + flash(f'Error uploading: {e}', 'danger') + return redirect(url_for('index')) + + @app.route('/download') + def download(): + path = request.args.get('path') try: - update_nodes(rpc) - flash('Nodes list updated', 'success') + return filemgr.download(path) + except Exception as e: + flash(f'Error downloading: {e}', 'danger') + return redirect(url_for('index')) + + @app.route('/mkdir', methods=['POST']) + def mkdir(): + path = request.form['path'] + try: + filemgr.mkdir(path) + flash('Directory created', 'success') except Exception as e: flash(f'Error: {e}', 'danger') - return redirect(url_for('nodes')) - data = get_data('nodes.json') or {'nodes': []} - return render_template('nodes.html', nodes=data['nodes']) - -# Select service node -@app.route('/nodes/select', methods=['POST']) -def select_node(): - node_url = request.form['node_url'] - try: - km = KeyManager() - km.selectNode(node_url) - flash(f'Selected node: {node_url}', 'success') - except Exception as e: - flash(f'Error: {e}', 'danger') - return redirect(url_for('nodes')) - -# Select current user -@app.route('/user', methods=['GET', 'POST']) -def user(): - km = KeyManager() - users = list(km.listUsers()) - if request.method == 'POST': - username = request.form['username'] + return redirect(url_for('index')) + + @app.route('/rmdir', methods=['POST']) + def rmdir(): + path = request.form['path'] + try: + filemgr.rmdir(path) + flash('Directory removed', 'success') + except Exception as e: + flash(f'Error: {e}', 'danger') + return redirect(url_for('index')) + + @app.route('/ls') + def ls(): + path = request.args.get('path', '') try: - km.changeCurrentUser(username) - flash(f'Current user set: {username}', 'success') + return jsonify(filemgr.ls(path)) + except Exception as e: + return jsonify({'error': str(e)}) + + @app.route('/move', methods=['POST']) + def move(): + src = request.form['src'] + dst = request.form['dst'] + try: + filemgr.move(src, dst) + flash('Move successful', 'success') except Exception as e: flash(f'Error: {e}', 'danger') - return redirect(url_for('user')) - return render_template('user.html', users=users) - -# Prepare NVS records -@app.route('/publish', methods=['POST']) -def publish(): - km = KeyManager() - action = request.form['action'] - try: - if action == 'user': - km.prepareUserNVS() - elif action == 'worm': - km.prepareWormNVS() - elif action == 'nodes': - km.prepareNodesNVS() - flash(f'{action.capitalize()} NVS prepared', 'success') - except Exception as e: - flash(f'Error: {e}', 'danger') - return redirect(url_for('index')) - -# Background task control -@app.route('/task//start', methods=['POST']) -def task_start(task_id): - with tasks_lock: - running_tasks[task_id] = {'status': 'running', 'progress': 0} - return jsonify(running_tasks[task_id]) - -@app.route('/task//pause', methods=['POST']) -def task_pause(task_id): - with tasks_lock: - if task_id in running_tasks: - running_tasks[task_id]['status'] = 'paused' - return jsonify(running_tasks.get(task_id, {})) - -@app.route('/task//resume', methods=['POST']) -def task_resume(task_id): - with tasks_lock: - if task_id in running_tasks: - running_tasks[task_id]['status'] = 'running' - return jsonify(running_tasks.get(task_id, {})) - -@app.route('/task//progress', methods=['GET']) -def task_progress(task_id): - return jsonify(running_tasks.get(task_id, {'status': 'unknown', 'progress': 0})) + return redirect(url_for('index')) + + @app.route('/remove', methods=['POST']) + def remove(): + path = request.form['path'] + try: + filemgr.remove(path) + flash('Removed', 'success') + except Exception as e: + flash(f'Error: {e}', 'danger') + return redirect(url_for('index')) + + @app.route('/tree') + def tree(): + root = request.args.get('root', '') + try: + return jsonify(filemgr.tree(root)) + except Exception as e: + return jsonify({'error': str(e)}) + + @app.route('/quota') + def quota(): + try: + return jsonify(filemgr.quota()) + except Exception as e: + return jsonify({'error': str(e)}) + + @app.route('/fileinfo') + def fileinfo(): + path = request.args.get('path') + try: + return jsonify(filemgr.fileinfo(path)) + except Exception as e: + return jsonify({'error': str(e)}) + + # Encryption/Decryption + @app.route('/encrypt', methods=['GET', 'POST'], endpoint='encrypt_route') + def encrypt(): + if request.method == 'GET': + return render_template('encrypt.html') + algorithm = request.form['algorithm'] + file = request.files['file'] + km = get_km() + key = km.getCurrentKey() # uses selected user context + cipher = AESCipher(key) if algorithm == 'aes' else Salsa20Cipher(key) + infile = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) + file.save(infile) + outfile = cipher.encrypt_file(infile) + metadata = cipher.get_metadata() + return render_template('encrypt_result.html', filename=os.path.basename(outfile), metadata=metadata) + + @app.route('/decrypt', methods=['GET', 'POST'], endpoint='decrypt_route') + def decrypt(): + if request.method == 'GET': + return render_template('decrypt.html') + algorithm = request.form['algorithm'] + key_hex = request.form['key'] + nonce = request.form.get('nonce') + iv = request.form.get('iv') + file = request.files['file'] + key = bytes.fromhex(key_hex) + cipher = AESCipher(key, iv=bytes.fromhex(iv)) if algorithm == 'aes' else Salsa20Cipher(key, nonce=bytes.fromhex(nonce)) + infile = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) + file.save(infile) + outfile = cipher.decrypt_file(infile) + return render_template('decrypt_result.html', filename=os.path.basename(outfile)) + + # Background tasks + @app.route('/task//start', methods=['POST']) + def task_start(task_id): + with tasks_lock: + running_tasks[task_id] = {'status': 'running', 'progress': 0} + return jsonify(running_tasks[task_id]) + + @app.route('/task//pause', methods=['POST']) + def task_pause(task_id): + with tasks_lock: + if task_id in running_tasks: + running_tasks[task_id]['status'] = 'paused' + return jsonify(running_tasks.get(task_id, {})) + + @app.route('/task//resume', methods=['POST']) + def task_resume(task_id): + with tasks_lock: + if task_id in running_tasks: + running_tasks[task_id]['status'] = 'running' + return jsonify(running_tasks.get(task_id, {})) + + @app.route('/task//progress', methods=['GET']) + def task_progress(task_id): + return jsonify(running_tasks.get(task_id, {'status': 'unknown', 'progress': 0})) + + return app if __name__ == '__main__': - # app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000))) - app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file + app = create_app() + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/EncryptDecrypt/static/css/styles.css b/EncryptDecrypt/static/css/styles.css index 8ad0a35..ec8659d 100644 --- a/EncryptDecrypt/static/css/styles.css +++ b/EncryptDecrypt/static/css/styles.css @@ -29,7 +29,12 @@ h1 { nav a:hover { color: #e0e0e0; } - + button .select { + padding: 0.5em 1em; + } + button:hover { + background-color: #e0e0e0; + } label { display: block; margin-top: 1.3em; diff --git a/EncryptDecrypt/templates/nodes.html b/EncryptDecrypt/templates/nodes.html index aaa95b6..5300e04 100644 --- a/EncryptDecrypt/templates/nodes.html +++ b/EncryptDecrypt/templates/nodes.html @@ -13,14 +13,14 @@

    Manage Nodes

    -

    Available Nodes

    +

    Available Nodes:

      {% for node in nodes %}
    • {{ node.url }} ({{ node.network }}, Services: {{ node.services|join(', ') }})
      - +
    • {% endfor %} diff --git a/EncryptDecrypt/templates/user.html b/EncryptDecrypt/templates/user.html index 322b4fb..c1bcd9c 100644 --- a/EncryptDecrypt/templates/user.html +++ b/EncryptDecrypt/templates/user.html @@ -1,12 +1,18 @@ {% extends 'layout.html' %} {% block content %} -

      Select User

      +

      User Management

      - {% for u in users %} {% endfor %} - + +
      + + + +
      -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/NessKeys/Prng.py b/NessKeys/Prng.py index 91afb16..d8750bb 100644 --- a/NessKeys/Prng.py +++ b/NessKeys/Prng.py @@ -162,7 +162,11 @@ def add_entropy(self, *arguments): for i in range(0, len(arguments) - 1): args.append(arguments[i]) - self.hash(str(time.time()) + ' ' + ''.join(args) + str(random.random())) + self.hash( + str(time.time()) + ' ' + + ''.join(a.hex() if isinstance(a, bytes) else str(a) for a in args) + + str(random.random())) + def generate(self, rng: int, count: int): result = []