EnterRoomPassword

This commit is contained in:
AtHeartEngineer
2023-11-11 23:19:54 +03:00
parent ac2f98dad8
commit 9eb68cdcb5
7 changed files with 197 additions and 39 deletions

View File

@@ -158,3 +158,55 @@ export async function createInvite(
export async function getAllRooms(serverUrl: string, username: string, password: string) {
return getAuth([serverUrl, `admin/rooms`], username, password) as Promise<RoomI[]>;
}
interface CheckResponse {
success: boolean;
message?: string;
// include other properties as needed
}
export async function postCheckRoomPassword(
serverUrl: string,
roomId: string,
passwordHash: string
): Promise<boolean> {
const response = (await post([serverUrl, `room/checkpasswordhash/${roomId}`], {
passwordHash
})) as CheckResponse;
return Boolean(response.success);
}
export async function postSetRoomPassword(
serverUrl: string,
roomId: string,
passwordHash: string
): Promise<boolean> {
const prover = new Prover('idcNullifier/circuit.wasm', 'idcNullifier/final.zkey');
const id = getIdentity() as IdentityData;
const idForProof: Partial<IdentityData> = {};
if (id) {
const timestamp = BigInt(Date.now());
idForProof.trapdoor = id._trapdoor;
idForProof.nullifier = id._nullifier;
idForProof.secret = id._secret;
idForProof.commitment = id._commitment;
// Proves you know the identity secret with a timestamp so this proof can't be replayed
prover
.generateProof({
identity: idForProof as unknown as Identity,
externalNullifier: timestamp
})
.then(async (proof) => {
const response = (await post([serverUrl, `room/setpassword/${roomId}`], {
passwordHash,
proof
})) as CheckResponse;
return response.success;
});
} else {
alertQueue.enqueue('No identity found when fetching rooms', 'error');
return false;
}
return false;
}

View File

@@ -96,10 +96,36 @@ export const saltStore = storable('', 'salt');
* !WARN NEVER CHANGE THE STORE TYPE OR YOU RISK EXPOSING THE KEY
*/
export const keyStore = writable({} as keyStoreI);
/**
* @description This is the in memory cryptokeys derived from the
* room specific passwords stored in roomPassStore, which is encrypted in local storage
* so keyStore is used to decrypt roomPassStore, which is used to decrypt each rooms
* roomKeyStore, which is then used to decrypt messages in each corresponding room
*/
export const roomKeyStore = writable({} as roomKeyStoreI);
export const alertQueue = queueable([]);
export const roomPasswordSet = derived(
[currentSelectedRoom, roomPassStore],
([$currentSelectedRoom, $roomPassStore]) => {
if ($currentSelectedRoom.encrypted == 'AES') {
if ($roomPassStore[$currentSelectedRoom.roomId.toString()]) {
if ($roomPassStore[$currentSelectedRoom.roomId.toString()].length > 0) {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return true;
}
}
);
/**
* @description Configuration store, stores the user's settings
*/

View File

@@ -55,10 +55,15 @@ export interface consoleStoreI {
};
}
// keyed by the roomId, encrypted in localstorage
export interface roomPassStoreI {
[key: string]: string;
[key: string]: {
password: string;
hashedSaltedPassword: string;
};
}
// keyed by the roomId, only in memory
export interface roomKeyStoreI {
[key: string]: CryptoKey;
}

View File

@@ -7,11 +7,13 @@ import {
keyStore,
identityExists,
alertQueue,
roomPassStore
roomPassStore,
selectedServer
} from '$lib/stores';
import { encryptIdentity } from './identity';
import type { IdentityStoreI } from '$lib/types';
import { updateRooms } from '.';
import { postCheckRoomPassword, postSetRoomPassword } from '$lib/services/server';
export async function setPassword(password: string): Promise<'success' | string> {
const hashedPassword = await hashPassword(password);
@@ -79,3 +81,39 @@ export async function unlockPadlock(password: string) {
keyStore.set(null);
}
}
export async function enterRoomPassword(password: string, roomId: string): Promise<boolean> {
const hashedSaltedPassword = await hashPassword(password + roomId);
if (hashedSaltedPassword) {
postCheckRoomPassword(get(selectedServer), roomId, hashedSaltedPassword).then((res) => {
if (res) {
roomPassStore.update((roomPass) => {
roomPass[roomId] = { password, hashedSaltedPassword };
return roomPass;
});
return true;
} else {
return false;
}
});
}
return false;
}
export async function setRoomPassword(password: string, roomId: string): Promise<boolean> {
const hashedSaltedPassword = await hashPassword(password + roomId);
if (hashedSaltedPassword) {
postSetRoomPassword(get(selectedServer), roomId, hashedSaltedPassword).then((res) => {
if (res) {
roomPassStore.update((roomPass) => {
roomPass[roomId] = { password, hashedSaltedPassword };
return roomPass;
});
return true;
} else {
return false;
}
});
}
return false;
}

View File

@@ -37,7 +37,6 @@ function updateRoomStore(rooms: RoomI[], serverURL: string = get(selectedServer)
}
newStore[roomId] = { ...room, server: serverURL };
});
console.debug(newStore);
return newStore;
});
// Update the serverStore
@@ -142,8 +141,6 @@ export function addMessageToRoom(roomId: string, data: MessageI) {
}
// Add the new message
const test = [...currentStore[roomId]];
currentStore[roomId] = [...currentStore[roomId], data];
// Trim messages to the last 500

View File

@@ -6,8 +6,9 @@
selectedServer,
configStore,
currentRoomsStore,
numberServers
roomPasswordSet
} from '$lib/stores';
import RoomPassword from './RoomPassword.svelte';
import { Experiences } from '$lib/types';
import { addMessageToRoom, getTimestampFromEpoch, updateMessages, updateRooms } from '$lib/utils';
import { getToastStore } from '@skeletonlabs/skeleton';
@@ -150,43 +151,47 @@
</script>
{#if $currentSelectedRoom}
<div
id="chat"
class="grid grid-rows-[auto,1fr,auto]">
<ChatRoomHeader
{connected}
{currentEpoch}
{timeLeftInEpoch}
{userMessageLimit}
{roomRateLimit}
{onlineMembers} />
{#if $configStore.experience == Experiences.Chat}
{#key $currentSelectedRoom.roomId}
<Conversation {roomRateLimit} />
{/key}
<InputPrompt
{socket}
{#if $roomPasswordSet}
<div
id="chat"
class="grid grid-rows-[auto,1fr,auto]">
<ChatRoomHeader
{connected}
{currentEpoch}
{timeLeftInEpoch}
{userMessageLimit}
{roomId} />
{:else if $configStore.experience == Experiences.Draw}
<Draw />
{:else}
{#key $currentSelectedRoom.roomId}
<Conversation {roomRateLimit} />
{/key}
<InputPrompt
{socket}
{connected}
{currentEpoch}
{userMessageLimit}
{roomId} />
{/if}
<!-- Conversation -->
{roomRateLimit}
{onlineMembers} />
{#if $configStore.experience == Experiences.Chat}
{#key $currentSelectedRoom.roomId}
<Conversation {roomRateLimit} />
{/key}
<InputPrompt
{socket}
{connected}
{currentEpoch}
{userMessageLimit}
{roomId} />
{:else if $configStore.experience == Experiences.Draw}
<Draw />
{:else}
{#key $currentSelectedRoom.roomId}
<Conversation {roomRateLimit} />
{/key}
<InputPrompt
{socket}
{connected}
{currentEpoch}
{userMessageLimit}
{roomId} />
{/if}
<!-- Conversation -->
<!-- Prompt -->
</div>
<!-- Prompt -->
</div>
{:else}
<div><RoomPassword {roomId} /></div>
{/if}
{:else}
<div class="grid place-content-center">
<h6 class="h2 text-center mb-10">You aren't in any rooms...yet</h6>

View File

@@ -0,0 +1,35 @@
<script lang="ts">
import { enterRoomPassword } from '$lib/utils';
import Container from '$lib/components/Utils/Container.svelte';
export let roomId: string;
const minPasswordLength = 4;
let r = '';
function onSubmit() {
if (r != '' && r != null && r != undefined && r.length >= minPasswordLength) {
enterRoomPassword(r, roomId);
}
}
</script>
<Container heading="Enter Room Password">
<form
on:submit|preventDefault={() => onSubmit()}
class="flex flex-col w-full">
<label
for="enterRoomPassword"
class="label" />
<input
id="enterRoomPassword"
type="password"
name="password"
class="input"
bind:value={r}
required />
<button
class="btn variant-filled-primary mt-3"
type="submit">Enter Room Password</button>
</form>
</Container>