Skip to content

Commit

Permalink
New Packet Design (active)
Browse files Browse the repository at this point in the history
Merging in new chunking & reliability algo (inactive)
  • Loading branch information
Universal Web committed May 5, 2023
1 parent cc0c32f commit be4e4fb
Show file tree
Hide file tree
Showing 21 changed files with 262 additions and 152 deletions.
4 changes: 2 additions & 2 deletions jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
"udsp/server/*.js"
],
"#client/*": [
"udsp/server/*.js"
"udsp/client/*.js"
],
"#server*": [
"udsp/server/index.js"
],
"#client*": [
"udsp/server/index.js"
"udsp/client/index.js"
],
"#profile/*": [
"./profile/*.js"
Expand Down
2 changes: 1 addition & 1 deletion packetActions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Packet Actions numbers and their meanings

Packet Actions have two formats one which is numerical and one which is character based. Prefered is numerical as its smaller and faster.
Packet Actions have two formats one which is numerical and one which is character based. Preferred is numerical as its smaller & faster.

- 0 (connect)
- 1 (file)
12 changes: 7 additions & 5 deletions packetDesign.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
#### PROPERTY NAMES & MEANINGS

- id - Connection ID (MANDATORY)
- api - API function that is requested (OPTIONAL)
- act - primary action related to primary protocol functions only (OPTIONAL)
- Watcher (OPTIONAL)
- Head (OPTIONAL)
- Body (OPTIONAL) (MSGPack Object)
- Head (OPTIONAL) (Chunked MSGPack Object)
- evnt (Event related to application level events)
- Body (OPTIONAL) (Chunked MSGPack Object)
- Pid - Packet ID (MANDATORY)
- Status - Status Code (OPTIONAL)
- If status is left blank it defaults to 200 or is considered a success
- sid - Stream ID (MANDATORY)
- State - State Code (OPTIONAL)
- If state is left blank it defaults to 200 or is considered a success
- end - Kill connection (OPTIONAL)
- Puzzle - Solve a puzzle to continue (OPTIONAL)
- ReKey (OPTIONAL)
Expand Down
3 changes: 2 additions & 1 deletion scripts/simulateClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ const stateRequest = await uwClient.request({
});
console.timeEnd('File Request');
console.timeEnd('Full script runtime');
console.log('Request state', stateRequest, stateRequest.response.body.data.toString('UTF8'));
console.log('Request state', stateRequest);
console.log(stateRequest.response.body.data.toString('UTF8'));

16 changes: 9 additions & 7 deletions serverApp/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* App server example
* App server example
*/
import { createServer } from '#udsp';
import { info } from '#logs';
Expand All @@ -18,14 +18,16 @@ const appServer = await createServer({
maxResponseRetries: 3,
// Max size of packets
maxPacketSize: 1100,
// Max size of a message
maxMessageSize: 10000,
// Max size of body and head data sections in a single packet
maxPacketPayloadSize: 1000,
// max file size
maxFileSize: 9000,
// Max size of a Response
maxResponseSize: 10000,
// Max size of a Packet for Responses
maxResponsePacketSize: 10000,
// default file extension default is .js but WWW default is www
defaultExtension: 'html',
// Max size of body and head data sections in packets
maxPayloadSize: 1000,
// max data size in a singular packet
maxFileSize: 900,
// Domain certificate to be loaded used for connection encryption
profile: `${currentPath(import.meta)}../services/universal.web.cert`,
// Where to load app resources from
Expand Down
14 changes: 7 additions & 7 deletions udsp/ask.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { promise } from 'Acid';
export class Ask {
constructor(payload, thisContext) {
const askContext = this;
constructor(payload, thisClient) {
const thisAsk = this;
const timeStamp = Date.now();
const {
requestQueue,
packetIdGenerator
} = thisContext;
} = thisClient;
// sid is a Stream ID
const sid = packetIdGenerator.get();
payload.sid = sid;
payload.t = timeStamp;
askContext.payload = payload;
thisAsk.payload = payload;
const awaitingResult = promise((accept) => {
askContext.accept = accept;
thisAsk.accept = accept;
});
requestQueue.set(sid, askContext);
thisContext.send(payload);
requestQueue.set(sid, thisAsk);
thisClient.send(payload);
return awaitingResult;
}
/* `completedChunks = [];` is initializing an empty array called `completedChunks` as a property of
Expand Down
37 changes: 18 additions & 19 deletions udsp/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import {
assign,
construct,
UniqID,
isString
isString,
promise
} from 'Acid';
import dgram from 'dgram';
// Default utility imports
import { success, configure } from '#logs';
import { success, configure, info } from '#logs';
import { buildPacketSize } from '#buildPacketSize';
import { buildStringSize } from '#buildStringSize';
import {
Expand All @@ -38,15 +39,15 @@ import { request } from '#udsp/request';
import { processMessage } from './processMessage.js';
import { onMessage } from './onMessage.js';
import { connect } from './connect.js';
import { onListening, listen } from './listening.js';
import { onListening } from './listening.js';
import { currentPath } from '#directory';
// UNIVERSAL WEB Client Class
export class Client {
type = 'client';
description = `The Universal Web's UDSP client module to initiate connections to a UDSP Server.`;
descriptor = 'UDSP_CLIENT';
constructor(configuration) {
const thisContext = this;
const thisClient = this;
console.log('-------CLIENT INITIALIZING-------\n', configuration);
this.configuration = configuration;
const {
Expand All @@ -72,22 +73,20 @@ export class Client {
this.processMessage = processMessage.bind(this);
this.emit = emit.bind(this);
this.onListening = onListening.bind(this);
this.listen = listen.bind(this);
this.onMessage = onMessage.bind(this);
thisContext.clientId = createClientId();
thisClient.clientId = createClientId();
success(`clientId:`, this.clientId);
success(`Creating Shared Keys`);
const transmitKey = thisContext.transmitKey = createSessionKey();
const receiveKey = thisContext.receiveKey = createSessionKey();
const transmitKey = thisClient.transmitKey = createSessionKey();
const receiveKey = thisClient.receiveKey = createSessionKey();
// Currently unused but may in the future
const ephemeralProfileTransmitKey = thisContext.ephemeralProfileTransmitKey = createSessionKey();
const ephemeralProfileReceiveKey = thisContext.ephemeralProfileReceiveKey = createSessionKey();
console.log(ephemeralProfileTransmitKey, ephemeralProfileReceiveKey);
const ephemeralProfileTransmitKey = thisClient.ephemeralProfileTransmitKey = createSessionKey();
const ephemeralProfileReceiveKey = thisClient.ephemeralProfileReceiveKey = createSessionKey();
success(`Creating Connection Keypair`);
thisContext.keypair = keypair();
thisContext.ephemeralPublic = omit(profile.ephemeral, ['private']);
thisClient.keypair = keypair();
thisClient.ephemeralPublic = omit(profile.ephemeral, ['private']);
if (profile.master) {
thisContext.masterPublic = omit(profile.master, ['private']);
thisClient.masterPublic = omit(profile.master, ['private']);
}
const { ephemeral: { signature: profileSignature } } = profile;
const {
Expand All @@ -99,21 +98,21 @@ export class Client {
const {
publicKey,
secretKey: privateKey,
} = thisContext.keypair;
} = thisClient.keypair;
clientSession(receiveKey, transmitKey, publicKey, privateKey, serverPublicKey);
// Can be used to encrypt-authenticate the profile with the server
// clientSession(ephemeralProfileReceiveKey, ephemeralProfileTransmitKey, profile.ephemeral.publicKey, profile.ephemeral.secretKey, serverPublicKey);
configure(`Shared Keys Created`);
console.log(receiveKey, transmitKey);
this.listen();
thisContext.server.on('message', thisContext.onMessage.bind(thisContext));
const serviceKey = toBase64(serviceSignature);
const profileKey = toBase64(profileSignature);
// Needs to be more complex if forcing no connection with the same credentials
const connectionKey = `${serviceKey}${profileKey}`;
this.connectionKey = connectionKey;
Client.connections.set(connectionKey, thisContext);
return thisContext;
Client.connections.set(connectionKey, thisClient);
thisClient.server.on('message', thisClient.onMessage.bind(thisClient));
thisClient.server.on('listening', thisClient.onListening);
return this;
}
maxMTU = 1000;
encoding = 'binary';
Expand Down
4 changes: 1 addition & 3 deletions udsp/client/listening.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {
success, failed, imported, msgSent, info
} from '#logs';
import { promise } from 'Acid';
export function onListening() {
const connection = this.server.address();
success(`Universal Web Client Server Listening`, connection);
}
export function listen() {
this.server.on('listening', this.onListening);
}
63 changes: 24 additions & 39 deletions udsp/client/send.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
encrypt,
nonceBox,
toBase64,
hashSign
hashSign,
encryptABytes
} from '#crypto';
imported('Client Send');
export async function send(message, priority) {
Expand All @@ -28,74 +29,58 @@ export async function send(message, priority) {
const headers = {};
const clientStatusCode = thisContext.state;
console.log(`client Status Code is ${clientStatusCode}`);
if (clientStatusCode === 0) {
if (!message.head) {
message.head = {};
}
message.head.cert = thisContext.ephemeralPublic;
}
if (message.head) {
message.head = encode(message.head);
}
if (message.body) {
message.body = encode(message.body);
}
info(`Send to server`);
const nonce = nonceBox();
success(`Nonce Size: ${nonce.length} ${toBase64(nonce)}`);
const transmitID = thisContext.serverId || thisContext.clientId;
if (transmitID) {
headers.id = transmitID;
} else {
return console.error(`NO CLIENT ID IS'T ASSIGNED`);
return console.error(`CLIENT ID IS'T ASSIGNED`);
}
headers.nonce = nonce;
if (clientStatusCode === 0) {
// PERFECT FORWARD SECRECY USE RANDOM EPHEMERAL KEY TO ENCRYPT IDENTITY CERT
// PERFECT FORWARD SECRECY USE RANDOM EPHEMERAL KEY TO ENCRYPT
headers.key = thisContext.keypair.publicKey;
headers.sig = hashSign(headers.key, thisContext.profile.ephemeral.private);
console.log(`Sig:${toBase64(headers.sig)}`);
console.log(`Sig Size:${headers.sig.length}`);
const profileKeypairSignature = hashSign(headers.key, thisContext.profile.ephemeral.private);
message.sig = profileKeypairSignature;
message.idc = thisContext.ephemeralPublic;
console.log(`Sig Size:${message.sig.length}`);
console.log(`Setting ephemeral random public key to header & profile cert to message.body`);
}
console.log('PACKET HEADERS', headers);
if (message.head) {
message.head = encode(message.head);
}
if (message.body) {
message.body = encode(message.body);
}
const headersEncoded = encode(headers);
const headersEndIndex = headersEncoded.length + 3;
const headersEndIndexBuffer = buildStringSize(headersEndIndex);
console.log(headersEndIndex, headers);
const headersCompiled = Buffer.concat([headersEndIndexBuffer, headersEncoded]);
success(`Additional Data End Index ${headersEndIndex.toString()}`);
console.log(toBase64(thisContext.transmitKey));
console.log(headers);
success(`Additional Data Headers Encoded Size ${headersEncoded.length}`);
console.log('TransmitKey', toBase64(thisContext.transmitKey));
console.log(message);
const messageEncoded = encode(message);
const encryptedMessage = encrypt(messageEncoded, headersEncoded, nonce, thisContext.transmitKey);
if (!encryptedMessage) {
return failed('Encryption failed');
}
success(`Encrypted Message Size:${encryptedMessage.length} -> ${toBase64(encryptedMessage)}`);
const encryptedLength = encryptedMessage.length;
const encryptedDataEndIndex = buildPacketSize(headersEndIndex + 4 + encryptedLength);
success(`Encrypted Data End Index: ${encryptedDataEndIndex.toString()}`);
const messageBuffer = Buffer.concat([
headersCompiled,
encryptedDataEndIndex,
encryptedMessage,
]);
console.log(toBase64(encryptedMessage));
const packetSize = messageBuffer.length;
success(`Encrypted Message Size:${encryptedMessage.length}`);
const encryptedMessageLength = encryptedMessage.length;
const compactedMessage = encode([headersEncoded, encryptedMessage]);
const packetSize = compactedMessage.length;
success(`Packet End Index ${packetSize}`);
success('Message Buffer Size', Buffer.from(messageBuffer).length);
if (packetSize >= 1280) {
console.log(messageBuffer);
console.log(compactedMessage);
failed(`WARNING: Packet size is larger than max allowed size -> ${packetSize}`);
}
return promise((accept, reject) => {
server.send(messageBuffer, port, ip, (error) => {
server.send(compactedMessage, port, ip, (error) => {
if (error) {
failed(error);
return reject(error);
}
msgSent(messageBuffer);
msgSent(compactedMessage);
accept();
});
});
Expand Down
4 changes: 2 additions & 2 deletions udsp/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { promise, construct } from 'Acid';
import { Ask } from './ask.js';
imported('Request');
export async function request(payload, sendAsIs) {
const thisContext = this;
const thisClient = this;
info(`Requested ${payload}`);
const ask = await (construct(Ask, [payload, thisContext]));
const ask = await (construct(Ask, [payload, thisClient]));
return ask;
}
Loading

0 comments on commit be4e4fb

Please sign in to comment.