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 0000000..a8304f5
Binary files /dev/null and b/server_log.txt differ
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(