mirror of
https://github.com/pavlobu/deskreen.git
synced 2025-05-29 13:50:08 -07:00
tests for darkwire.io code
This commit is contained in:
parent
18d9cad5e2
commit
1b063f2131
134
app/server/darkwireSocket.spec.ts
Normal file
134
app/server/darkwireSocket.spec.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/* eslint-disable class-methods-use-this */
|
||||||
|
/* eslint-disable no-new */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import Io from 'socket.io';
|
||||||
|
import http from 'http';
|
||||||
|
import Koa from 'koa';
|
||||||
|
import DarkwireSocket from './darkwireSocket';
|
||||||
|
|
||||||
|
const protocol = http;
|
||||||
|
|
||||||
|
class MockConnectSocket {
|
||||||
|
testObservers: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.testObservers = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
join() {}
|
||||||
|
|
||||||
|
on(type: string, callback: any) {
|
||||||
|
this.testObservers[type] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(type: string) {
|
||||||
|
if (this.testObservers[type] !== undefined) {
|
||||||
|
this.testObservers[type]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('DarkwireSocket tests', () => {
|
||||||
|
const TEST_ROOM_ID = '123';
|
||||||
|
const TEST_ROOM_ID_HASH = '123321';
|
||||||
|
const makeTestSocketOPTS = (socket: Io.Socket) => {
|
||||||
|
return {
|
||||||
|
roomIdOriginal: TEST_ROOM_ID,
|
||||||
|
roomId: TEST_ROOM_ID_HASH,
|
||||||
|
socket,
|
||||||
|
room: {
|
||||||
|
id: TEST_ROOM_ID_HASH,
|
||||||
|
users: [],
|
||||||
|
isLocked: false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let app: Koa<Koa.DefaultState, Koa.DefaultContext>;
|
||||||
|
let server: http.Server;
|
||||||
|
let io: Io.Server;
|
||||||
|
let socket: Io.Socket;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
app = new Koa();
|
||||||
|
server = protocol.createServer(app.callback());
|
||||||
|
io = Io(server, {
|
||||||
|
pingInterval: 20000,
|
||||||
|
pingTimeout: 5000,
|
||||||
|
serveClient: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
io.on('connection', (receivedSocket) => {
|
||||||
|
socket = receivedSocket;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set internal socket same as passed in constructor', () => {
|
||||||
|
io.emit('connection', {
|
||||||
|
join: () => {},
|
||||||
|
});
|
||||||
|
const customSocket = new DarkwireSocket(makeTestSocketOPTS(socket));
|
||||||
|
|
||||||
|
expect(customSocket.socket).toBe(socket);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit "ROOM_LOCKED" on internal socket object when .sendRoomLocked() is called', () => {
|
||||||
|
const mockEmitProperty = jest.fn();
|
||||||
|
io.emit('connection', {
|
||||||
|
emit: mockEmitProperty,
|
||||||
|
join: () => {},
|
||||||
|
});
|
||||||
|
const customSocket = new DarkwireSocket(makeTestSocketOPTS(socket));
|
||||||
|
|
||||||
|
customSocket.sendRoomLocked();
|
||||||
|
|
||||||
|
expect(mockEmitProperty).toBeCalledWith('ROOM_LOCKED');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call .joinRoom() when socket is created and pass roomId as argument', () => {
|
||||||
|
const mockJoinProperty = jest.fn();
|
||||||
|
io.emit('connection', {
|
||||||
|
join: mockJoinProperty,
|
||||||
|
});
|
||||||
|
const testSocketOPTS = makeTestSocketOPTS(socket);
|
||||||
|
const { roomId } = testSocketOPTS;
|
||||||
|
|
||||||
|
new DarkwireSocket(testSocketOPTS);
|
||||||
|
|
||||||
|
expect(mockJoinProperty).toBeCalledWith(roomId, expect.anything());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call handleDisconnect when socket.on("disconnect") happened', async () => {
|
||||||
|
const mockDisconnectProperty = jest.fn();
|
||||||
|
io.emit('connection', new MockConnectSocket());
|
||||||
|
const darkwireSocket = new DarkwireSocket(makeTestSocketOPTS(socket));
|
||||||
|
Object.defineProperty(darkwireSocket, 'handleDisconnect', {
|
||||||
|
value: mockDisconnectProperty,
|
||||||
|
});
|
||||||
|
|
||||||
|
await darkwireSocket.handleSocket(socket);
|
||||||
|
socket.emit('disconnect');
|
||||||
|
|
||||||
|
expect(mockDisconnectProperty).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set TOGGLE_LOCK_ROOM, USER_DISCONNECT, USER_ENTER callbacks on socket when handleSocket is called', async () => {
|
||||||
|
io.emit('connection', new MockConnectSocket());
|
||||||
|
const darkwireSocket = new DarkwireSocket(makeTestSocketOPTS(socket));
|
||||||
|
|
||||||
|
await darkwireSocket.handleSocket(socket);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
((socket as unknown) as MockConnectSocket).testObservers
|
||||||
|
).toHaveProperty('TOGGLE_LOCK_ROOM');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
((socket as unknown) as MockConnectSocket).testObservers
|
||||||
|
).toHaveProperty('USER_ENTER');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
((socket as unknown) as MockConnectSocket).testObservers
|
||||||
|
).toHaveProperty('USER_DISCONNECT');
|
||||||
|
});
|
||||||
|
});
|
@ -1,3 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* original JS code from darkwire.io
|
||||||
|
* translated to typescript for Deskreen app
|
||||||
|
* */
|
||||||
|
|
||||||
/* eslint-disable no-async-promise-executor */
|
/* eslint-disable no-async-promise-executor */
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Io from 'socket.io';
|
import Io from 'socket.io';
|
@ -1,3 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* original JS code from darkwire.io
|
||||||
|
* translated to typescript for Deskreen app
|
||||||
|
* */
|
||||||
|
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import getStore from './store';
|
import getStore from './store';
|
||||||
|
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* original JS code from darkwire.io
|
||||||
|
* translated to typescript for Deskreen app
|
||||||
|
* */
|
||||||
|
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import http, { Server } from 'http';
|
import http, { Server } from 'http';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
@ -10,7 +15,7 @@ import koaStatic from 'koa-static';
|
|||||||
import koaSend from 'koa-send';
|
import koaSend from 'koa-send';
|
||||||
import getPort from 'get-port';
|
import getPort from 'get-port';
|
||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
import Socket from './socket';
|
import DarkwireSocket from './darkwireSocket';
|
||||||
import pollForInactiveRooms from './inactiveRooms';
|
import pollForInactiveRooms from './inactiveRooms';
|
||||||
import getStore from './store';
|
import getStore from './store';
|
||||||
|
|
||||||
@ -26,8 +31,6 @@ try {
|
|||||||
isDev = true;
|
isDev = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
@ -93,7 +96,7 @@ io.on('connection', async (socket) => {
|
|||||||
room = JSON.parse(room || '{}');
|
room = JSON.parse(room || '{}');
|
||||||
|
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new Socket({
|
new DarkwireSocket({
|
||||||
roomIdOriginal: roomId,
|
roomIdOriginal: roomId,
|
||||||
roomId: roomIdHash,
|
roomId: roomIdHash,
|
||||||
socket,
|
socket,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* got original code from darkwire.io
|
* original JS code from darkwire.io
|
||||||
* translated code to typescript for Deskreen
|
* translated to typescript for Deskreen app
|
||||||
* */
|
* */
|
||||||
|
|
||||||
interface MemoryStoreParams {
|
interface MemoryStoreParams {
|
||||||
|
@ -9,19 +9,9 @@ export default class Crypto {
|
|||||||
for (let i = 0; i < str.length; i++) {
|
for (let i = 0; i < str.length; i++) {
|
||||||
bytes[i] = str.charCodeAt(i);
|
bytes[i] = str.charCodeAt(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertArrayBufferViewToString(buffer) {
|
|
||||||
// let str = '';
|
|
||||||
// for (let i = 0; i < buffer.byteLength; i++) {
|
|
||||||
// str += String.fromCharCode(buffer[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return str;
|
|
||||||
// }
|
|
||||||
|
|
||||||
createEncryptDecryptKeys() {
|
createEncryptDecryptKeys() {
|
||||||
return new Promise<forge.pki.rsa.KeyPair>((resolve) => {
|
return new Promise<forge.pki.rsa.KeyPair>((resolve) => {
|
||||||
const keypair = forge.pki.rsa.generateKeyPair({
|
const keypair = forge.pki.rsa.generateKeyPair({
|
||||||
@ -109,10 +99,7 @@ export default class Crypto {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapKeyWithForge(
|
wrapKey(keyToWrap: string, publicKeyToWrapWith: forge.pki.rsa.PublicKey) {
|
||||||
keyToWrap: string,
|
|
||||||
publicKeyToWrapWith: forge.pki.rsa.PublicKey
|
|
||||||
) {
|
|
||||||
return publicKeyToWrapWith.encrypt(keyToWrap, 'RSA-OAEP');
|
return publicKeyToWrapWith.encrypt(keyToWrap, 'RSA-OAEP');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
app/utils/message.spec.ts
Normal file
56
app/utils/message.spec.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { prepare, process } from './message';
|
||||||
|
import getTestPublickKeyPem from './mocks/getTestPublickKeyPem';
|
||||||
|
import getTestPrivateKeyPem from './mocks/getTestPrivateKeyPem';
|
||||||
|
|
||||||
|
interface TestPayload {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestDecryptedPayload {
|
||||||
|
payload: TestPayload;
|
||||||
|
}
|
||||||
|
describe('message.ts tests for proper encryption and decryption functionality', () => {
|
||||||
|
const TEST_TEXT = 'some test text here';
|
||||||
|
const TEST_TEXT_AS_URL = 'some%20test%20text%20here';
|
||||||
|
const testPayloadToEncrypt = {
|
||||||
|
payload: {
|
||||||
|
text: TEST_TEXT,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const testUser = {
|
||||||
|
username: 'testUsername',
|
||||||
|
id: 'testId',
|
||||||
|
};
|
||||||
|
|
||||||
|
const testPartner = {
|
||||||
|
publicKey: getTestPublickKeyPem(),
|
||||||
|
};
|
||||||
|
it('should create encrypted payload with prepare() method', async () => {
|
||||||
|
const encryptedPayload = await prepare(
|
||||||
|
testPayloadToEncrypt,
|
||||||
|
testUser,
|
||||||
|
testPartner
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(encryptedPayload.toSend.payload).not.toContain(TEST_TEXT);
|
||||||
|
expect(encryptedPayload.toSend.payload).not.toContain(TEST_TEXT_AS_URL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should decrypt encrypted payload with process() method', async () => {
|
||||||
|
const encryptedPayload = await prepare(
|
||||||
|
testPayloadToEncrypt,
|
||||||
|
testUser,
|
||||||
|
testPartner
|
||||||
|
);
|
||||||
|
|
||||||
|
const decryptedPayload = await process(
|
||||||
|
encryptedPayload.toSend,
|
||||||
|
getTestPrivateKeyPem()
|
||||||
|
);
|
||||||
|
|
||||||
|
expect((decryptedPayload as TestDecryptedPayload).payload.text).toContain(
|
||||||
|
TEST_TEXT_AS_URL
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -7,8 +7,25 @@ import Crypto from './crypto';
|
|||||||
|
|
||||||
const crypto = new Crypto();
|
const crypto = new Crypto();
|
||||||
|
|
||||||
|
interface EncryptedPayloadToSend {
|
||||||
|
payload: string;
|
||||||
|
signature: string;
|
||||||
|
iv: string;
|
||||||
|
keys: EncryptedKeys[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EncryptedKeys {
|
||||||
|
sessionKey: string;
|
||||||
|
signingKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProcessedPayload {
|
||||||
|
toSend: EncryptedPayloadToSend;
|
||||||
|
original: any;
|
||||||
|
}
|
||||||
|
|
||||||
export const process = (payload: any, privateKeyString: string) =>
|
export const process = (payload: any, privateKeyString: string) =>
|
||||||
new Promise(async (resolve, reject) => {
|
new Promise(async (resolve) => {
|
||||||
const privateKey = (await crypto.importEncryptDecryptKey(
|
const privateKey = (await crypto.importEncryptDecryptKey(
|
||||||
privateKeyString
|
privateKeyString
|
||||||
)) as forge.pki.rsa.PrivateKey;
|
)) as forge.pki.rsa.PrivateKey;
|
||||||
@ -41,9 +58,9 @@ export const process = (payload: any, privateKeyString: string) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!verified) {
|
if (!verified) {
|
||||||
console.error("recreated signature doesn't match with payload.signature");
|
throw new Error(
|
||||||
reject();
|
"recreated signature doesn't match with payload.signature"
|
||||||
return;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const decryptedPayload = await crypto.decryptMessage(
|
const decryptedPayload = await crypto.decryptMessage(
|
||||||
@ -57,7 +74,7 @@ export const process = (payload: any, privateKeyString: string) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const prepare = (payload: any, user: any, partner: any) =>
|
export const prepare = (payload: any, user: any, partner: any) =>
|
||||||
new Promise(async (resolve) => {
|
new Promise<ProcessedPayload>(async (resolve) => {
|
||||||
const myUsername = user.username;
|
const myUsername = user.username;
|
||||||
const myId = user.id;
|
const myId = user.id;
|
||||||
const members = [partner];
|
const members = [partner];
|
||||||
@ -92,8 +109,8 @@ export const prepare = (payload: any, user: any, partner: any) =>
|
|||||||
member.publicKey
|
member.publicKey
|
||||||
)) as forge.pki.rsa.PublicKey;
|
)) as forge.pki.rsa.PublicKey;
|
||||||
const enc = await Promise.all([
|
const enc = await Promise.all([
|
||||||
crypto.wrapKeyWithForge(secretKeyRandomAES, memberPublicKey),
|
crypto.wrapKey(secretKeyRandomAES, memberPublicKey),
|
||||||
crypto.wrapKeyWithForge(secretKeyRandomHMAC, memberPublicKey),
|
crypto.wrapKey(secretKeyRandomHMAC, memberPublicKey),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
29
app/utils/mocks/getTestPrivateKeyPem.ts
Normal file
29
app/utils/mocks/getTestPrivateKeyPem.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export default function getTestPrivateKeyPem() {
|
||||||
|
return `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEogIBAAKCAQEAoQ8+vdVAerEyaxGaffzrTTv+sgpQ3xToBFYkxrT6f+zP4MqV
|
||||||
|
nTIy2+UMlEGGryMFgJWyurqv+NyoVf7vIFOxQcxJko1BL/oIt/e5YZ/fMIw9AhgP
|
||||||
|
0A0oZyFKNbGesCY3zMdpZqPE0brzfhjr/lu5VzioZI9vxocOSSc3+S8w1EXqujgO
|
||||||
|
X3PWXgZrD6Y//Oo+8BgNQta/e5PUyc9yNchU/W3ddzBdE0iUXTdQt7yFvzy4vTbS
|
||||||
|
ywUxoNNJnD6dUJ6dZ4c24VGvrvhJ5mzNqQjibAhtkPFbvhn0e0BHl0BZ876fLHGg
|
||||||
|
zpHgmmiWbKwBYl1ydv8fW9W3r5nQoW0RThYJjwIDAQABAoIBAHqOrUGrGsvCNwl+
|
||||||
|
db9VTICTHLbCXtPChuN14bpLUSszOuRlhAAAiO8HltDiI+j1j2RPhZfOI8YNsxLt
|
||||||
|
UW2aAhJ9r6aTUn19mFDVcv20uBOrQ2lqge3hdVM049GD/asxCdkMDUqLaGPoDQ1x
|
||||||
|
TXNavOiANrN+6qF5eAd2joNRw6hi7osyWqfpgM9y58kiYYazKHKlI/er19JsD2t5
|
||||||
|
hgRiFti+oN9nqhhVzJI/GYU9JugXnB/Z0uFvqOyt/3YDHPpOC07WYpfA3yzshBkW
|
||||||
|
TiiMp1eJNX/sRh4JHzRI9/MQe9ajlAS7T74HwCkclzaS8ZY6t+dfVZ097/ug77eQ
|
||||||
|
NwY4DAECgYEA0hWv7hpookKKcl0srfxbx+FbtBhzXqz+Tfy3H5do6uXRc0aUwQ25
|
||||||
|
PZLs7UAT3zjt+4aYu1xgkp3cUx5FNr/FAPYLtjXw1/6fmt3HX8820SpCMjIzS1bU
|
||||||
|
UwJvmAut70YA+n2eKwO1qJNeLcpNCDebbIXwj0lKDh5HQDyoswuz7I8CgYEAxEKV
|
||||||
|
N4Ma6GqZ0hgdQlO71oSuftHUI80Iz+Riorrp4AA8hp5A3V6Fd9QDy8PYLz1kD9pM
|
||||||
|
uWdCLqaxdOVDtLVnjNVZH1SI+wSBDA/0OtdvPaONL1JKRA21kol049f0M8wkulAw
|
||||||
|
6lfi2aQwZ3KWyEAfDy5iPdVROnQWqUcLmxO0kwECgYAGpSr4dBtlLoekkG/uXPIm
|
||||||
|
Q2mcK73Se9RbcSf1tttZusVCSTRBWwbF/NTDuGgogmt8rkg8fPKNELM8adO0pKI9
|
||||||
|
oorCS7h/jI1N38ADttE8EoMfhVj8BBYZPhV7kLsCu4siYUDUiXyAhZDQD/sZzHB9
|
||||||
|
IUt3rNDL24dTb9fCOheJ3wKBgGRgpY7Z2DZM51VkDfrxdp3WCKVGTljtMfeaGLSg
|
||||||
|
IqP1mv9DC2vtPxg1cKeUCArJPFc7UIh2/ot7qEFgTQusyERojgePJew0tofj1QcP
|
||||||
|
To7ZConMbb12wYosEYPC3NxtKc+82ffRcW3dIwCVw/axjPEnyQlVBBGAdGKpuo7b
|
||||||
|
Oj0BAoGAMs2zuAJfJB4xE1UZdLXk9uHWPAzgUNDzHuS5mMWuloGvKX+19yck6o2J
|
||||||
|
ZA/iq6GwOgvD2y9MC0mV4WkmwZpVXwPVpZD8GVQXZf2xZgh/q2e3IObAd80NLnaG
|
||||||
|
v86qN98DRTh9L+47Nsaf1J5vDDaAfH2Ir8UgAQ5ZMEFDm7P0hUQ=
|
||||||
|
-----END RSA PRIVATE KEY-----`;
|
||||||
|
}
|
11
app/utils/mocks/getTestPublickKeyPem.ts
Normal file
11
app/utils/mocks/getTestPublickKeyPem.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export default function getTestPublicKeyPem() {
|
||||||
|
return `-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoQ8+vdVAerEyaxGaffzr
|
||||||
|
TTv+sgpQ3xToBFYkxrT6f+zP4MqVnTIy2+UMlEGGryMFgJWyurqv+NyoVf7vIFOx
|
||||||
|
QcxJko1BL/oIt/e5YZ/fMIw9AhgP0A0oZyFKNbGesCY3zMdpZqPE0brzfhjr/lu5
|
||||||
|
VzioZI9vxocOSSc3+S8w1EXqujgOX3PWXgZrD6Y//Oo+8BgNQta/e5PUyc9yNchU
|
||||||
|
/W3ddzBdE0iUXTdQt7yFvzy4vTbSywUxoNNJnD6dUJ6dZ4c24VGvrvhJ5mzNqQji
|
||||||
|
bAhtkPFbvhn0e0BHl0BZ876fLHGgzpHgmmiWbKwBYl1ydv8fW9W3r5nQoW0RThYJ
|
||||||
|
jwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----`;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user