1
0
mirror of https://github.com/pavlobu/deskreen.git synced 2025-05-28 05:10:09 -07:00

Merge pull request #6 from pavlobu/test-for-darkwire

tests for darkwire.io code
This commit is contained in:
Pavlo Buidenkov 2020-09-01 16:40:27 +00:00 committed by GitHub
commit 279a358242
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 274 additions and 27 deletions

View 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');
});
});

View File

@ -1,3 +1,8 @@
/*
* original JS code from darkwire.io
* translated to typescript for Deskreen app
* */
/* eslint-disable no-async-promise-executor */
import _ from 'lodash';
import Io from 'socket.io';

View File

@ -1,3 +1,8 @@
/*
* original JS code from darkwire.io
* translated to typescript for Deskreen app
* */
/* eslint-disable no-console */
import getStore from './store';

View File

@ -1,3 +1,8 @@
/*
* original JS code from darkwire.io
* translated to typescript for Deskreen app
* */
/* eslint-disable no-console */
import http, { Server } from 'http';
import express from 'express';
@ -10,7 +15,7 @@ import koaStatic from 'koa-static';
import koaSend from 'koa-send';
import getPort from 'get-port';
// eslint-disable-next-line import/no-cycle
import Socket from './socket';
import DarkwireSocket from './darkwireSocket';
import pollForInactiveRooms from './inactiveRooms';
import getStore from './store';
@ -26,8 +31,6 @@ try {
isDev = true;
}
require('dotenv').config();
const app = new Koa();
const router = new Router();
@ -93,7 +96,7 @@ io.on('connection', async (socket) => {
room = JSON.parse(room || '{}');
// eslint-disable-next-line no-new
new Socket({
new DarkwireSocket({
roomIdOriginal: roomId,
roomId: roomIdHash,
socket,

View File

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/*
* got original code from darkwire.io
* translated code to typescript for Deskreen
* original JS code from darkwire.io
* translated to typescript for Deskreen app
* */
interface MemoryStoreParams {

View File

@ -9,19 +9,9 @@ export default class Crypto {
for (let i = 0; i < str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
}
// convertArrayBufferViewToString(buffer) {
// let str = '';
// for (let i = 0; i < buffer.byteLength; i++) {
// str += String.fromCharCode(buffer[i]);
// }
// return str;
// }
createEncryptDecryptKeys() {
return new Promise<forge.pki.rsa.KeyPair>((resolve) => {
const keypair = forge.pki.rsa.generateKeyPair({
@ -109,10 +99,7 @@ export default class Crypto {
});
}
wrapKeyWithForge(
keyToWrap: string,
publicKeyToWrapWith: forge.pki.rsa.PublicKey
) {
wrapKey(keyToWrap: string, publicKeyToWrapWith: forge.pki.rsa.PublicKey) {
return publicKeyToWrapWith.encrypt(keyToWrap, 'RSA-OAEP');
}

56
app/utils/message.spec.ts Normal file
View 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
);
});
});

View File

@ -7,8 +7,25 @@ import Crypto from './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) =>
new Promise(async (resolve, reject) => {
new Promise(async (resolve) => {
const privateKey = (await crypto.importEncryptDecryptKey(
privateKeyString
)) as forge.pki.rsa.PrivateKey;
@ -41,9 +58,9 @@ export const process = (payload: any, privateKeyString: string) =>
);
if (!verified) {
console.error("recreated signature doesn't match with payload.signature");
reject();
return;
throw new Error(
"recreated signature doesn't match with payload.signature"
);
}
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) =>
new Promise(async (resolve) => {
new Promise<ProcessedPayload>(async (resolve) => {
const myUsername = user.username;
const myId = user.id;
const members = [partner];
@ -92,8 +109,8 @@ export const prepare = (payload: any, user: any, partner: any) =>
member.publicKey
)) as forge.pki.rsa.PublicKey;
const enc = await Promise.all([
crypto.wrapKeyWithForge(secretKeyRandomAES, memberPublicKey),
crypto.wrapKeyWithForge(secretKeyRandomHMAC, memberPublicKey),
crypto.wrapKey(secretKeyRandomAES, memberPublicKey),
crypto.wrapKey(secretKeyRandomHMAC, memberPublicKey),
]);
return {

View 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-----`;
}

View 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-----`;
}