Skip to content

Commit

Permalink
refactor: Add test directory to tsconfig.json
Browse files Browse the repository at this point in the history
The tsconfig.json file in the sessions package has been updated to include the "test" directory in the "include" section. This change ensures that the test files are included in the TypeScript compilation process.
  • Loading branch information
drazisil committed Aug 26, 2024
1 parent e2ecbf1 commit bf8bd2b
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 42 deletions.
133 changes: 100 additions & 33 deletions packages/sessions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
import { createCipheriv, createDecipheriv } from "node:crypto";

/**
* Represents a pair of encryption and decryption functions.
Expand All @@ -14,31 +14,75 @@ type CipherPair = {
* Generates a pair of cipher and decipher functions for game encryption.
* @returns The cipher and decipher functions.
*/
function createGameEncryptionPair(): CipherPair {
const key = randomBytes(32);
const iv = randomBytes(16);
const cipher = createCipheriv("aes-256-cbc", key, iv);
const decipher = createDecipheriv("aes-256-cbc", key, iv);
return {
encrypt: cipher.update.bind(cipher),
decrypt: decipher.update.bind(decipher),
};
function createGameEncryptionPair(key: string): CipherPair {
try {
assertStringIsHex(key);
if (key.length !== 16) {
throw Error(
`Invalid game key length: ${key.length}. The key must be 16 bytes long.`,
);

Check warning on line 23 in packages/sessions/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/sessions/src/index.ts#L21-L23

Added lines #L21 - L23 were not covered by tests
}

// The key used by the game 8 bytes long.
// Since the key is in hex format, we need to slice it to 16 characters.
key = key.slice(0, 16);

// The IV is intentionally required to be all zeros.
const iv = Buffer.alloc(8);
const keyBuffer = Buffer.from(key, "hex");

// The algorithm is intentionally set to "des-cbc".
// This is because the game uses this insecure algorithm.
// We are intentionally using an insecure algorithm here to match the game.
const cipher = createCipheriv("des-cbc", keyBuffer, iv);

Check failure

Code scanning / SonarCloud

Cipher algorithms should be robust High

Use a strong cipher algorithm. See more on SonarCloud

Check failure

Code scanning / SonarCloud

Encryption algorithms should be used with secure mode and padding scheme High

Use a secure mode and padding scheme. See more on SonarCloud
const decipher = createDecipheriv("des-cbc", keyBuffer, iv);

return {
encrypt: cipher.update.bind(cipher),
decrypt: decipher.update.bind(decipher),
};
} catch (error: unknown) {
const err = new Error(`Failed to create game encryption pair`);
err.cause = error;
throw err;
}
}

/**
* Generates a pair of encryption and decryption functions for the server.
*
*
* @param key - The key to use for encryption and decryption. Must be 16 hex characters.
* @returns {CipherPair} The encryption and decryption functions.
*/
function createServerEncryptionPair(): CipherPair {
const key = randomBytes(32);
const iv = randomBytes(16);
const cipher = createCipheriv("aes-256-cbc", key, iv);
const decipher = createDecipheriv("aes-256-cbc", key, iv);
return {
encrypt: cipher.update.bind(cipher),
decrypt: decipher.update.bind(decipher),
};
function createServerEncryptionPair(key: string): CipherPair {
try {
assertStringExists(key);
assertStringIsHex(key);
if (key.length !== 16) {
throw Error(
`Invalid server key length: ${key.length}. The key must be 16 bytes long.`,
);
}

Check warning on line 65 in packages/sessions/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/sessions/src/index.ts#L62-L65

Added lines #L62 - L65 were not covered by tests

// The IV is intentionally required to be empty.
const iv = Buffer.alloc(0);
const keyBuffer = Buffer.from(key, "hex");

// The algorithm is intentionally set to "rc4".
// This is because the game uses this insecure algorithm.
// We are intentionally using an insecure algorithm here to match the game.
const cipher = createCipheriv("rc4", keyBuffer, iv);

Check failure

Code scanning / SonarCloud

Cipher algorithms should be robust High

Use a strong cipher algorithm. See more on SonarCloud
const decipher = createDecipheriv("rc4", keyBuffer, iv);

return {
encrypt: cipher.update.bind(cipher),
decrypt: decipher.update.bind(decipher),
};
} catch (error: unknown) {
const err = new Error(`Failed to create server encryption pair`);
err.cause = error;
throw err;
}

Check warning on line 85 in packages/sessions/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/sessions/src/index.ts#L82-L85

Added lines #L82 - L85 were not covered by tests
}

type ConnectedClient = {
Expand All @@ -58,21 +102,28 @@ type ConnectedClient = {
serverEncryptionHandshakeComplete: boolean;
};



/**
* Sets the client encryption for a connected client.
*
*
* @param client - The connected client to set the encryption for.
* @param sessionKey - The session key to associate with the client.
* @returns The updated connected client with the encryption set.
*/
export function setClientEncryption(client: ConnectedClient, sessionKey: string): ConnectedClient {
const gameEncryptionPair = createGameEncryptionPair();
const serverEncryptionPair = createServerEncryptionPair();
client.sessionKey = sessionKey;
client.gameEncryptionPair = gameEncryptionPair;
client.serverEncryptionPair = serverEncryptionPair;
export function setClientEncryption(
client: ConnectedClient,
sessionKey: string,
): ConnectedClient {
try {
const gameEncryptionPair = createGameEncryptionPair(sessionKey);
const serverEncryptionPair = createServerEncryptionPair(sessionKey);
client.sessionKey = sessionKey;
client.gameEncryptionPair = gameEncryptionPair;
client.serverEncryptionPair = serverEncryptionPair;
} catch (error: unknown) {
const err = new Error(`Failed to set client encryption`);
err.cause = error;
throw err;
}
return client;
}

Expand All @@ -90,9 +141,7 @@ const connectedClients: Record<string, ConnectedClient> = {};
* @returns The connected client with the specified customer ID.
* @throws Error if no client is found with the given customer ID.
*/
export function findClientByCustomerId(
customerId: number,
): ConnectedClient {
export function findClientByCustomerId(customerId: number): ConnectedClient {
const client = Object.values(connectedClients).find(
(client) => client.customerId === customerId,
);
Expand Down Expand Up @@ -145,7 +194,7 @@ export function newClientConnection(

/**
* Saves the client connection with the specified connection ID.
*
*
* @param connectionId - The ID of the connection.
* @param client - The connected client to be saved.
*/
Expand All @@ -164,3 +213,21 @@ export function clearConnectedClients(): void {
delete connectedClients[connectionId];
}
}

function assertStringExists(str: string): void {
if (str === "" || typeof str === "undefined") {
throw new Error("String not provided");
}

Check warning on line 220 in packages/sessions/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/sessions/src/index.ts#L219-L220

Added lines #L219 - L220 were not covered by tests
}

/**
* Asserts that a given string is a valid hexadecimal string.
*
* @param str - The string to be validated.
* @throws {Error} If the string is not a valid hexadecimal string.
*/
function assertStringIsHex(str: string): void {
if (!/^[0-9a-fA-F]+$/.test(str)) {
throw new Error(`Invalid hex string: ${str}`);
}
}
57 changes: 49 additions & 8 deletions packages/sessions/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { beforeEach, describe, expect, it } from "vitest";
import { saveClientConnection, clearConnectedClients, findClientByCustomerId, hasClientEncryptionPair, newClientConnection, setClientEncryption } from "../index.js"
import {
saveClientConnection,
clearConnectedClients,
findClientByCustomerId,
hasClientEncryptionPair,
newClientConnection,
setClientEncryption,
} from "../index.js";

describe("Client connections", () => {
beforeEach(() => {
clearConnectedClients();
});

describe("newClientConnection", () => {
it("should create a new client connection", () => {
const connectionId = "123";
Expand Down Expand Up @@ -29,11 +40,6 @@ describe("Client connections", () => {
});

describe("findClientByCustomerId", () => {

beforeEach(() => {
clearConnectedClients();
});

it("should find a client by customer ID", () => {
const connectionId = "123";
const customerId = 456;
Expand All @@ -52,17 +58,53 @@ describe("Client connections", () => {
});
});

describe("setClientEncryption", () => {
it("should set the client encryption pair", () => {
const connectionId = "123";
const customerId = 456;
const sessionKey = "ea25e21a2a022d71";

const client = newClientConnection(connectionId, customerId);
saveClientConnection(connectionId, client);

setClientEncryption(client, sessionKey);

expect(client.sessionKey).toBe(sessionKey);
});

it("should throw an error if the session key is not provided", () => {
const connectionId = "123";
const customerId = 456;

const client = newClientConnection(connectionId, customerId);
saveClientConnection(connectionId, client);

expect(() => setClientEncryption(client, "")).toThrow();
});

it("should throw an error if the session key is invalid", () => {
const connectionId = "123";
const customerId = 456;

const client = newClientConnection(connectionId, customerId);
saveClientConnection(connectionId, client);

expect(() => setClientEncryption(client, "invalid")).toThrow();
});
});

describe("hasClientEncryptionPair", () => {
it("should return true if the client has an encryption pair", () => {
const connectionId = "123";
const customerId = 456;
const sessionKey = "ea25e21a2a022d71";
const client = newClientConnection(connectionId, customerId);
saveClientConnection(connectionId, client);

expect(hasClientEncryptionPair(client, "game")).toBe(false);
expect(hasClientEncryptionPair(client, "server")).toBe(false);

setClientEncryption(client, "sessionKey");
setClientEncryption(client, sessionKey);

expect(hasClientEncryptionPair(client, "game")).toBe(true);
expect(hasClientEncryptionPair(client, "server")).toBe(true);
Expand All @@ -84,4 +126,3 @@ describe("Client connections", () => {
});
});
});

3 changes: 2 additions & 1 deletion packages/sessions/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
},
"include": [
"index.ts",
"src"
"src",
"test"
]
}

0 comments on commit bf8bd2b

Please sign in to comment.