feature: add secured message
- encrypt/decrypt every message (require ^0.0.2) Signed-off-by: pakintada@gmail.com <Pakin>
This commit is contained in:
parent
4ca8b3b270
commit
2a0841a798
14 changed files with 314 additions and 147 deletions
|
|
@ -37,15 +37,23 @@ import { buildOverviewFromServer } from '$lib/data/recipeService';
|
|||
import { auth } from '../client/firebase';
|
||||
import { type RecipeVersion } from '$lib/models/recipe_version.model';
|
||||
import { goto } from '$app/navigation';
|
||||
import { socketAlreadySendHeartbeat, socketConnectionOfflineCount } from '../stores/websocketStore';
|
||||
import {
|
||||
sharedKey as sharedKey,
|
||||
socketAlreadySendHeartbeat,
|
||||
socketConnectionOfflineCount
|
||||
} from '../stores/websocketStore';
|
||||
import type { RecipePrice } from '$lib/models/price.model';
|
||||
import { sendCommandRequest, sendMessage } from './ws_messageSender';
|
||||
import { auth as authStore } from '../stores/auth';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { handleSheetResponseFromNoti } from './sheetNotiHandler';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import * as semver from 'semver';
|
||||
import { WebCryptoHelper } from '../utils/crypto';
|
||||
|
||||
export const messages = writable<string[]>([]);
|
||||
|
||||
type HandshakeAck = { server_public_key: string; status: string };
|
||||
type WSMessage = { type: string; payload: any };
|
||||
|
||||
// MAXIMUM LIMIT = 1814355
|
||||
|
|
@ -131,7 +139,7 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
}
|
||||
}
|
||||
},
|
||||
stream_data_end: (p) => {
|
||||
stream_data_end: async (p) => {
|
||||
recipeLoading.set(false);
|
||||
|
||||
// build overview for recipe from server
|
||||
|
|
@ -154,7 +162,7 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
}
|
||||
|
||||
// send next chain message
|
||||
sendMessage({
|
||||
await sendMessage({
|
||||
type: 'price',
|
||||
payload: {
|
||||
action: {
|
||||
|
|
@ -352,7 +360,7 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
currentRecipeVersionsSelector.set(result);
|
||||
}
|
||||
},
|
||||
price: (p) => {
|
||||
price: async (p) => {
|
||||
let req_action = p.req_action;
|
||||
let status = p.status;
|
||||
let to = p.to;
|
||||
|
|
@ -385,7 +393,7 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
current_streaming_instance[request_id] = '';
|
||||
streamingRawData.set(current_streaming_instance);
|
||||
|
||||
sendCommandRequest('sheet', {
|
||||
await sendCommandRequest('sheet', {
|
||||
country: current_meta?.country ?? '',
|
||||
content: saved_product_code_to_get_from_sheet,
|
||||
param: 'price',
|
||||
|
|
@ -395,59 +403,59 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
|
||||
lastRequestSheetPrice.set(lastRequestPriceInstance);
|
||||
},
|
||||
raw_stream: (p) => {
|
||||
let streamRawInstance = get(streamingRawData);
|
||||
let sub_type = p.sub_type;
|
||||
let request_id = p.request_id;
|
||||
let size_per_chunk = p.size_per_chunk;
|
||||
let total_chunks = p.total_chunks;
|
||||
let idx = p.idx;
|
||||
// raw_stream: (p) => {
|
||||
// let streamRawInstance = get(streamingRawData);
|
||||
// let sub_type = p.sub_type;
|
||||
// let request_id = p.request_id;
|
||||
// let size_per_chunk = p.size_per_chunk;
|
||||
// let total_chunks = p.total_chunks;
|
||||
// let idx = p.idx;
|
||||
|
||||
switch (sub_type) {
|
||||
case 'price':
|
||||
streamingRawMeta.set({
|
||||
id: request_id,
|
||||
total_size: total_chunks,
|
||||
chunk_size: size_per_chunk,
|
||||
progress: 0
|
||||
});
|
||||
break;
|
||||
case 'chunk_price':
|
||||
streamingRawMeta.set({
|
||||
id: request_id,
|
||||
total_size: total_chunks,
|
||||
chunk_size: size_per_chunk,
|
||||
progress: idx
|
||||
});
|
||||
// switch (sub_type) {
|
||||
// case 'price':
|
||||
// streamingRawMeta.set({
|
||||
// id: request_id,
|
||||
// total_size: total_chunks,
|
||||
// chunk_size: size_per_chunk,
|
||||
// progress: 0
|
||||
// });
|
||||
// break;
|
||||
// case 'chunk_price':
|
||||
// streamingRawMeta.set({
|
||||
// id: request_id,
|
||||
// total_size: total_chunks,
|
||||
// chunk_size: size_per_chunk,
|
||||
// progress: idx
|
||||
// });
|
||||
|
||||
let raw_payload = p.raw ?? '';
|
||||
streamRawInstance[request_id] += raw_payload;
|
||||
streamingRawData.set(streamRawInstance);
|
||||
// let raw_payload = p.raw ?? '';
|
||||
// streamRawInstance[request_id] += raw_payload;
|
||||
// streamingRawData.set(streamRawInstance);
|
||||
|
||||
break;
|
||||
case 'end_price':
|
||||
let lastRequestPriceInstance = get(lastRequestSheetPrice);
|
||||
let country = lastRequestPriceInstance[request_id];
|
||||
// break;
|
||||
// case 'end_price':
|
||||
// let lastRequestPriceInstance = get(lastRequestSheetPrice);
|
||||
// let country = lastRequestPriceInstance[request_id];
|
||||
|
||||
try {
|
||||
let raw_payload = JSON.parse(streamRawInstance[request_id]);
|
||||
let ref_from_raw = raw_payload.payload.ref ?? '';
|
||||
let from_service_raw = raw_payload.payload.from ?? '';
|
||||
let parsed_payload = raw_payload.payload ?? '';
|
||||
// try {
|
||||
// let raw_payload = JSON.parse(streamRawInstance[request_id]);
|
||||
// let ref_from_raw = raw_payload.payload.ref ?? '';
|
||||
// let from_service_raw = raw_payload.payload.from ?? '';
|
||||
// let parsed_payload = raw_payload.payload ?? '';
|
||||
|
||||
if (from_service_raw == 'sheet-service') {
|
||||
handleSheetResponseFromNoti(parsed_payload, ref_from_raw, country);
|
||||
delete streamRawInstance[request_id];
|
||||
streamingRawData.set(streamRawInstance);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`end price process error: ${e}`);
|
||||
}
|
||||
// if (from_service_raw == 'sheet-service') {
|
||||
// handleSheetResponseFromNoti(parsed_payload, ref_from_raw, country);
|
||||
// delete streamRawInstance[request_id];
|
||||
// streamingRawData.set(streamRawInstance);
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.log(`end price process error: ${e}`);
|
||||
// }
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
},
|
||||
// break;
|
||||
// default:
|
||||
// }
|
||||
// },
|
||||
heartbeat: (p) => {
|
||||
socketConnectionOfflineCount.set(0);
|
||||
socketAlreadySendHeartbeat.set(0);
|
||||
|
|
@ -476,22 +484,50 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
}
|
||||
};
|
||||
|
||||
export function handleIncomingMessages(raw: string) {
|
||||
const msg: WSMessage = JSON.parse(raw);
|
||||
export async function handleIncomingMessages(raw: string, clientPrivateKey: CryptoKey) {
|
||||
const APP_VERSION = env.PUBLIC_APP_SEMVER;
|
||||
|
||||
const ack: HandshakeAck = JSON.parse(raw);
|
||||
// console.log(`[WS MSG] type=${msg.type}`, msg.payload);
|
||||
if (msg == null) {
|
||||
// error response
|
||||
addNotification('ERR:No response from server');
|
||||
if (ack != null && ack.status === 'authenticated') {
|
||||
// has server response
|
||||
|
||||
sharedKey.set(await WebCryptoHelper.deriveSharedKey(clientPrivateKey, ack.server_public_key));
|
||||
|
||||
addNotification('INFO:Secured Connection');
|
||||
|
||||
return;
|
||||
}
|
||||
if (semver.satisfies(APP_VERSION, '^0.0.2')) {
|
||||
// secured message decryption
|
||||
let sharedKeyStore = get(sharedKey);
|
||||
if (sharedKeyStore) {
|
||||
let raw_payload = JSON.parse(raw);
|
||||
let decrypted_string = await WebCryptoHelper.decryptMessage(
|
||||
sharedKeyStore,
|
||||
raw_payload.ciphertext,
|
||||
raw_payload.iv
|
||||
);
|
||||
let actual_message: WSMessage = JSON.parse(decrypted_string);
|
||||
|
||||
// raw streaming type
|
||||
if (msg.type.startsWith('raw_stream')) {
|
||||
// convert
|
||||
let sub_type = msg.type.replace('raw_stream_', '');
|
||||
msg.payload.sub_type = sub_type;
|
||||
msg.type = 'raw_stream';
|
||||
handlers[actual_message.type]?.(actual_message.payload);
|
||||
}
|
||||
} else {
|
||||
const msg: WSMessage = JSON.parse(raw);
|
||||
if (msg == null) {
|
||||
// error response
|
||||
addNotification('ERR:No response from server');
|
||||
return;
|
||||
}
|
||||
|
||||
// raw streaming type
|
||||
// if (msg.type.startsWith('raw_stream')) {
|
||||
// // convert
|
||||
// let sub_type = msg.type.replace('raw_stream_', '');
|
||||
// msg.payload.sub_type = sub_type;
|
||||
// msg.type = 'raw_stream';
|
||||
// }
|
||||
|
||||
handlers[msg.type]?.(msg.payload);
|
||||
}
|
||||
|
||||
handlers[msg.type]?.(msg.payload);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { get, writable } from 'svelte/store';
|
||||
import type { OutMessage } from '../types/outMessage';
|
||||
import { socketStore } from '../stores/websocketStore';
|
||||
import { sharedKey, socketStore } from '../stores/websocketStore';
|
||||
import { addNotification } from '../stores/noti';
|
||||
import { auth } from '../stores/auth';
|
||||
import { WebCryptoHelper } from '../utils/crypto';
|
||||
import * as semver from 'semver';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
export const queue = writable<string[]>([]);
|
||||
|
||||
|
|
@ -18,7 +21,7 @@ function getServiceName(cmdReq: CommandRequest) {
|
|||
}
|
||||
|
||||
// Websocket message wrapper for commands like `sheet`, `command`
|
||||
export function sendCommandRequest(target: CommandRequest, values: any): boolean {
|
||||
export async function sendCommandRequest(target: CommandRequest, values: any): Promise<boolean> {
|
||||
let srv_name = getServiceName(target);
|
||||
let curr_user = get(auth);
|
||||
|
||||
|
|
@ -31,7 +34,7 @@ export function sendCommandRequest(target: CommandRequest, values: any): boolean
|
|||
};
|
||||
}
|
||||
|
||||
return sendMessage({
|
||||
return await sendMessage({
|
||||
type: target,
|
||||
payload: {
|
||||
user_info: user_info ?? {},
|
||||
|
|
@ -41,9 +44,13 @@ export function sendCommandRequest(target: CommandRequest, values: any): boolean
|
|||
});
|
||||
}
|
||||
|
||||
export function sendMessage(msg: OutMessage, ignore_queue_request: boolean = true): boolean {
|
||||
export async function sendMessage(
|
||||
msg: OutMessage,
|
||||
ignore_queue_request: boolean = true
|
||||
): Promise<boolean> {
|
||||
const APP_VERSION = env.PUBLIC_APP_SEMVER;
|
||||
const socket = get(socketStore);
|
||||
const data = JSON.stringify(msg);
|
||||
let data = JSON.stringify(msg);
|
||||
|
||||
// console.log('try sending ', data);
|
||||
|
||||
|
|
@ -64,6 +71,17 @@ export function sendMessage(msg: OutMessage, ignore_queue_request: boolean = tru
|
|||
return false;
|
||||
}
|
||||
|
||||
// console.log('send v2', APP_VERSION, semver.satisfies(APP_VERSION, '^0.0.2'));
|
||||
|
||||
if (semver.satisfies(APP_VERSION, '^0.0.2')) {
|
||||
console.log('sending secured');
|
||||
let sharedKeyRes = get(sharedKey);
|
||||
|
||||
// do encrypt
|
||||
if (sharedKeyRes != null)
|
||||
data = JSON.stringify(await WebCryptoHelper.encryptMessage(sharedKeyRes, data));
|
||||
}
|
||||
|
||||
socket.send(data);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue