From d3be7fce43fe2db62a5998b76d93192bb7cddf6d Mon Sep 17 00:00:00 2001 From: hibanejjari Date: Tue, 11 Mar 2025 12:17:58 +0100 Subject: [PATCH] Fixed onion router issue and improved GET routes --- .idea/.gitignore | 8 ++ .idea/misc.xml | 6 + .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 + .idea/workshop-4.iml | 9 ++ __test__/config/jestSetup.ts | 1 - __test__/tsconfig.json | 14 +-- server_log.txt | Bin 0 -> 2672 bytes src/crypto.ts | 156 +++++++++++++++---------- src/onionRouters/launchOnionRouters.ts | 2 + src/onionRouters/simpleOnionRouter.ts | 64 +++++++--- src/registry/registry.ts | 19 ++- src/users/user.ts | 16 ++- 13 files changed, 213 insertions(+), 96 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workshop-4.iml create mode 100644 server_log.txt diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..31e1ebc --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..0fc6c8d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workshop-4.iml b/.idea/workshop-4.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/workshop-4.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/__test__/config/jestSetup.ts b/__test__/config/jestSetup.ts index 8b13789..e69de29 100644 --- a/__test__/config/jestSetup.ts +++ b/__test__/config/jestSetup.ts @@ -1 +0,0 @@ - diff --git a/__test__/tsconfig.json b/__test__/tsconfig.json index 621202d..f94fa1f 100755 --- a/__test__/tsconfig.json +++ b/__test__/tsconfig.json @@ -1,20 +1,20 @@ { - "exclude": ["node_modules", "build"], "compilerOptions": { + "outDir": "./dist", // Redirect compiled files to 'dist/' + "rootDir": "./src", // Keep TypeScript files inside 'src' "target": "es2016", "lib": ["es6"], "module": "commonjs", - "baseUrl": "./", - "paths": { - "@/*": ["../src/*"] - }, + "strict": true, "resolveJsonModule": true, "allowJs": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "strict": true, "noImplicitAny": true, "skipLibCheck": true, "incremental": true - } + }, + "include": ["src", "tests"], + "exclude": ["node_modules", "dist"] } + diff --git a/server_log.txt b/server_log.txt new file mode 100644 index 0000000000000000000000000000000000000000..a8304f533eaa35a54ed3373c4c93476dbd630cf4 GIT binary patch literal 2672 zcmcJRK~BRk5Jf**B<|n~G|(n((+z?XAR%!8R0=|r6eUK8Yj6eD+=c_N;0!E*|81&9 zLTJmu&60Q)$GL5Ex1;{i)7Fu)28_xc^6<^2(s@s_)WcZ(;+THN6T zLlii2mwH%SGH$W%Y8g|_3RkRCMvFNPdzl5ZJD0DTmRUC>t5b_-R_NJqDj!?8xfK&k}9jD zqn43WnI#>yiloXe>8RsKstl8kI*FvpGU=$(NUBVej#@`jWt()=nWJ9NGWve=Uo*aY UjA_ch?;M$C(_>?l_#WOHzgmpjb^rhX literal 0 HcmV?d00001 diff --git a/src/crypto.ts b/src/crypto.ts index 109b285..168366f 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -11,64 +11,75 @@ function arrayBufferToBase64(buffer: ArrayBuffer): string { // Function to convert Base64 string to ArrayBuffer function base64ToArrayBuffer(base64: string): ArrayBuffer { - var buff = Buffer.from(base64, "base64"); + const buff = Buffer.from(base64, "base64"); return buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength); } // ################ -// ### RSA keys ### +// ### RSA Keys ### // ################ -// Generates a pair of private / public RSA keys +// Generates a pair of private/public RSA keys type GenerateRsaKeyPair = { publicKey: webcrypto.CryptoKey; privateKey: webcrypto.CryptoKey; }; export async function generateRsaKeyPair(): Promise { - // TODO implement this function using the crypto package to generate a public and private RSA key pair. - // the public key should be used for encryption and the private key for decryption. Make sure the - // keys are extractable. - - // remove this - return { publicKey: {} as any, privateKey: {} as any }; + const keyPair = await webcrypto.subtle.generateKey( + { + name: "RSA-OAEP", + modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), + hash: "SHA-256", + }, + true, // Extractable + ["encrypt", "decrypt"] + ); + + return { publicKey: keyPair.publicKey, privateKey: keyPair.privateKey }; } -// Export a crypto public key to a base64 string format +// Export a crypto public key to a Base64 string format export async function exportPubKey(key: webcrypto.CryptoKey): Promise { - // TODO implement this function to return a base64 string version of a public key - - // remove this - return ""; + const exported = await webcrypto.subtle.exportKey("spki", key); + return arrayBufferToBase64(exported); } -// Export a crypto private key to a base64 string format +// Export a crypto private key to a Base64 string format export async function exportPrvKey( key: webcrypto.CryptoKey | null ): Promise { - // TODO implement this function to return a base64 string version of a private key - - // remove this - return ""; + if (!key) return null; + const exported = await webcrypto.subtle.exportKey("pkcs8", key); + return arrayBufferToBase64(exported); } -// Import a base64 string public key to its native format +// Import a Base64 string public key to its native format export async function importPubKey( strKey: string ): Promise { - // TODO implement this function to go back from the result of the exportPubKey function to it's native crypto key object - - // remove this - return {} as any; + const buffer = base64ToArrayBuffer(strKey); + return await webcrypto.subtle.importKey( + "spki", + buffer, + { name: "RSA-OAEP", hash: "SHA-256" }, + true, + ["encrypt"] + ); } -// Import a base64 string private key to its native format +// Import a Base64 string private key to its native format export async function importPrvKey( strKey: string ): Promise { - // TODO implement this function to go back from the result of the exportPrvKey function to it's native crypto key object - - // remove this - return {} as any; + const buffer = base64ToArrayBuffer(strKey); + return await webcrypto.subtle.importKey( + "pkcs8", + buffer, + { name: "RSA-OAEP", hash: "SHA-256" }, + true, + ["decrypt"] + ); } // Encrypt a message using an RSA public key @@ -76,11 +87,13 @@ export async function rsaEncrypt( b64Data: string, strPublicKey: string ): Promise { - // TODO implement this function to encrypt a base64 encoded message with a public key - // tip: use the provided base64ToArrayBuffer function - - // remove this - return ""; + const publicKey = await importPubKey(strPublicKey); + const encrypted = await webcrypto.subtle.encrypt( + { name: "RSA-OAEP" }, + publicKey, + Buffer.from(b64Data, "utf-8") + ); + return arrayBufferToBase64(encrypted); } // Decrypts a message using an RSA private key @@ -88,43 +101,45 @@ export async function rsaDecrypt( data: string, privateKey: webcrypto.CryptoKey ): Promise { - // TODO implement this function to decrypt a base64 encoded message with a private key - // tip: use the provided base64ToArrayBuffer function - - // remove this - return ""; + const decrypted = await webcrypto.subtle.decrypt( + { name: "RSA-OAEP" }, + privateKey, + base64ToArrayBuffer(data) + ); + return Buffer.from(decrypted).toString("utf-8"); } // ###################### -// ### Symmetric keys ### +// ### Symmetric Keys ### // ###################### // Generates a random symmetric key export async function createRandomSymmetricKey(): Promise { - // TODO implement this function using the crypto package to generate a symmetric key. - // the key should be used for both encryption and decryption. Make sure the - // keys are extractable. - - // remove this - return {} as any; + return await webcrypto.subtle.generateKey( + { name: "AES-CBC", length: 256 }, + true, + ["encrypt", "decrypt"] + ); } -// Export a crypto symmetric key to a base64 string format +// Export a crypto symmetric key to a Base64 string format export async function exportSymKey(key: webcrypto.CryptoKey): Promise { - // TODO implement this function to return a base64 string version of a symmetric key - - // remove this - return ""; + const exported = await webcrypto.subtle.exportKey("raw", key); + return arrayBufferToBase64(exported); } -// Import a base64 string format to its crypto native format +// Import a Base64 string format to its crypto native format export async function importSymKey( strKey: string ): Promise { - // TODO implement this function to go back from the result of the exportSymKey function to it's native crypto key object - - // remove this - return {} as any; + const buffer = base64ToArrayBuffer(strKey); + return await webcrypto.subtle.importKey( + "raw", + buffer, + { name: "AES-GCM", length: 256 }, + true, + ["encrypt", "decrypt"] + ); } // Encrypt a message using a symmetric key @@ -132,10 +147,17 @@ export async function symEncrypt( key: webcrypto.CryptoKey, data: string ): Promise { - // TODO implement this function to encrypt a base64 encoded message with a public key - // tip: encode the data to a uin8array with TextEncoder + const encoder = new TextEncoder(); + const encodedData = encoder.encode(data); + const iv = webcrypto.getRandomValues(new Uint8Array(12)); + + const encrypted = await webcrypto.subtle.encrypt( + { name: "AES-GCM", iv }, + key, + encodedData + ); - return ""; + return arrayBufferToBase64(iv) + ":" + arrayBufferToBase64(encrypted); } // Decrypt a message using a symmetric key @@ -143,8 +165,16 @@ export async function symDecrypt( strKey: string, encryptedData: string ): Promise { - // TODO implement this function to decrypt a base64 encoded message with a private key - // tip: use the provided base64ToArrayBuffer function and use TextDecode to go back to a string format - - return ""; + const [ivBase64, encryptedBase64] = encryptedData.split(":"); + const iv = new Uint8Array(base64ToArrayBuffer(ivBase64)); + const encryptedBuffer = base64ToArrayBuffer(encryptedBase64); + + const key = await importSymKey(strKey); + const decrypted = await webcrypto.subtle.decrypt( + { name: "AES-GCM", iv }, + key, + encryptedBuffer + ); + + return new TextDecoder().decode(decrypted); } diff --git a/src/onionRouters/launchOnionRouters.ts b/src/onionRouters/launchOnionRouters.ts index 71203d1..4ce97c4 100644 --- a/src/onionRouters/launchOnionRouters.ts +++ b/src/onionRouters/launchOnionRouters.ts @@ -1,4 +1,6 @@ import { simpleOnionRouter } from "./simpleOnionRouter"; +import { BASE_ONION_ROUTER_PORT } from "../config"; + export async function launchOnionRouters(n: number) { const promises = []; diff --git a/src/onionRouters/simpleOnionRouter.ts b/src/onionRouters/simpleOnionRouter.ts index 4b10d30..e462e42 100644 --- a/src/onionRouters/simpleOnionRouter.ts +++ b/src/onionRouters/simpleOnionRouter.ts @@ -2,21 +2,49 @@ import bodyParser from "body-parser"; import express from "express"; import { BASE_ONION_ROUTER_PORT } from "../config"; -export async function simpleOnionRouter(nodeId: number) { - const onionRouter = express(); - onionRouter.use(express.json()); - onionRouter.use(bodyParser.json()); - - // TODO implement the status route - // onionRouter.get("/status", (req, res) => {}); - - const server = onionRouter.listen(BASE_ONION_ROUTER_PORT + nodeId, () => { - console.log( - `Onion router ${nodeId} is listening on port ${ - BASE_ONION_ROUTER_PORT + nodeId - }` - ); - }); - - return server; -} + export async function simpleOnionRouter(nodeId: number) { + const onionRouter = express(); + onionRouter.use(express.json()); + onionRouter.use(bodyParser.json()); + + + onionRouter.get("/status", (req, res) => { + console.log(` Received request at node ${nodeId} for /status`); + res.send("live"); + }); + + onionRouter.get("/getLastReceivedEncryptedMessage", (req, res) => { + console.log(`🔍 Node ${nodeId}: Returning empty encrypted message`); + res.json({ result: null }); + }); + + onionRouter.get("/getLastReceivedDecryptedMessage", (req, res) => { + console.log(`🔍 Node ${nodeId}: Returning empty decrypted message`); + res.json({ result: null }); + }); + + onionRouter.get("/getLastMessageDestination", (req, res) => { + console.log(`🔍 Node ${nodeId}: Returning null destination`); + res.json({ result: null }); + }); + + // Define the port dynamically based on nodeId + const port = BASE_ONION_ROUTER_PORT + nodeId; + + try { + const server = onionRouter.listen(port, () => { + console.log(` Onion Router ${nodeId} is successfully listening on port ${port}`); + }); + + // Catching server errors + server.on("error", (err) => { + console.error(` Error: Failed to start Onion Router ${nodeId} on port ${port}:`, err); + }); + + return server; + } catch (error) { + console.error(` Critical Error: Failed to start Onion Router ${nodeId}:`, error); + throw error; + } + } + diff --git a/src/registry/registry.ts b/src/registry/registry.ts index 73a6a57..ba1aad9 100644 --- a/src/registry/registry.ts +++ b/src/registry/registry.ts @@ -12,18 +12,25 @@ export type RegisterNodeBody = { export type GetNodeRegistryBody = { nodes: Node[]; }; - export async function launchRegistry() { const _registry = express(); _registry.use(express.json()); _registry.use(bodyParser.json()); - // TODO implement the status route - // _registry.get("/status", (req, res) => {}); + _registry.get("/status", (req, res) => { + console.log("Received request for registry status"); + res.send("live"); // Fix: send plain text + }); - const server = _registry.listen(REGISTRY_PORT, () => { - console.log(`registry is listening on port ${REGISTRY_PORT}`); + const server = await new Promise((resolve) => { + const srv = _registry.listen(REGISTRY_PORT, () => { + console.log(`Registry is listening on port ${REGISTRY_PORT}`); + resolve(srv); + }); }); - return server; + return server; // Ensure the function returns the running server } + + + diff --git a/src/users/user.ts b/src/users/user.ts index 8e017b2..9731bf2 100644 --- a/src/users/user.ts +++ b/src/users/user.ts @@ -13,7 +13,21 @@ export async function user(userId: number) { _user.use(bodyParser.json()); // TODO implement the status route - // _user.get("/status", (req, res) => {}); + _user.get("/status", (req, res) => { + res.json({ status: `User ${userId} is running` }); +}); + +_user.get("/getLastReceivedMessage", (req, res) => { + res.json({ result: null }); +}); + +_user.get("/getLastSentMessage", (req, res) => { + res.json({ result: null }); +}); + + + + const server = _user.listen(BASE_USER_PORT + userId, () => { console.log(