encrpyted rooms work

This commit is contained in:
AtHeartEngineer
2023-11-13 22:43:09 +03:00
parent 3c3b18e18f
commit 096a79f4f4
29 changed files with 408 additions and 245 deletions

8
package-lock.json generated
View File

@@ -22,7 +22,7 @@
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"discreetly-interfaces": "^0.1.42", "discreetly-interfaces": "^0.1.42",
"dompurify": "^3.0.5", "dompurify": "^3.0.5",
"idc-nullifier": "^0.0.6", "idc-nullifier": "^0.0.8",
"libsodium-wrappers": "^0.7.11", "libsodium-wrappers": "^0.7.11",
"marked": "^7.0.5", "marked": "^7.0.5",
"minidenticons": "^4.2.0", "minidenticons": "^4.2.0",
@@ -7209,9 +7209,9 @@
} }
}, },
"node_modules/idc-nullifier": { "node_modules/idc-nullifier": {
"version": "0.0.6", "version": "0.0.8",
"resolved": "https://registry.npmjs.org/idc-nullifier/-/idc-nullifier-0.0.6.tgz", "resolved": "https://registry.npmjs.org/idc-nullifier/-/idc-nullifier-0.0.8.tgz",
"integrity": "sha512-jM2D9ODNnmqn/Ps/cvpxSk4JTIrMG2Ozb0Z3a99jW5cd1DS7U5YBlHJRMRJn3sGyG3YIrIHyJQYoAWPBw5UiTA==", "integrity": "sha512-fNR32XHu53AoAFA7rwVs2J67U/Za1hUpAPkIohwa05lnjXrwPMoA1zBeUutkdVRgmRBMbLpfAfXsUUnjqqpQQQ==",
"dependencies": { "dependencies": {
"@semaphore-protocol/identity": "^3.10.1", "@semaphore-protocol/identity": "^3.10.1",
"circomlib": "^2.0.5", "circomlib": "^2.0.5",

View File

@@ -72,7 +72,7 @@
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"discreetly-interfaces": "^0.1.42", "discreetly-interfaces": "^0.1.42",
"dompurify": "^3.0.5", "dompurify": "^3.0.5",
"idc-nullifier": "^0.0.6", "idc-nullifier": "^0.0.8",
"libsodium-wrappers": "^0.7.11", "libsodium-wrappers": "^0.7.11",
"marked": "^7.0.5", "marked": "^7.0.5",
"minidenticons": "^4.2.0", "minidenticons": "^4.2.0",
@@ -86,4 +86,4 @@
"wagmi": "^1.4.3", "wagmi": "^1.4.3",
"zuauth": "^0.3.0" "zuauth": "^0.3.0"
} }
} }

View File

@@ -63,7 +63,6 @@
loadingRooms = true; loadingRooms = true;
getEthAddressRoomNames($selectedServer, address) getEthAddressRoomNames($selectedServer, address)
.then((groupNames) => { .then((groupNames) => {
console.log(groupNames);
loadingRooms = false; loadingRooms = false;
groups = groupNames; groups = groupNames;
}) })
@@ -84,8 +83,7 @@
<h3 class="h4 mb-2"><span class="text-success-500">Step 1:</span> Connect your wallet</h3> <h3 class="h4 mb-2"><span class="text-success-500">Step 1:</span> Connect your wallet</h3>
<button <button
bind:this={btnEl} bind:this={btnEl}
class="btn variant-outline-tertiary">Connect</button class="btn variant-outline-tertiary">Connect</button>
>
</div> </div>
{#if isConnected} {#if isConnected}
<div> <div>
@@ -106,8 +104,7 @@
<button <button
on:click={proveOwnership} on:click={proveOwnership}
id="btn" id="btn"
class="btn variant-outline-success">Sign</button class="btn variant-outline-success">Sign</button>
>
</div> </div>
{:else if loadingRooms} {:else if loadingRooms}
<p>Loading rooms...</p> <p>Loading rooms...</p>

View File

@@ -20,7 +20,7 @@
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err); console.warn(err);
alertQueue.enqueue(`Unexpected error: ${err.message}`, 'error'); alertQueue.enqueue(`Unexpected error: ${err.message}`, 'error');
}) })
.finally(() => { .finally(() => {
@@ -90,8 +90,7 @@
{#if !hideInput} {#if !hideInput}
<label <label
class="label" class="label"
for="inviteCode" for="inviteCode">
>
<span class="h5">Enter Invite Code:</span> <span class="h5">Enter Invite Code:</span>
<input <input
class="input" class="input"
@@ -99,8 +98,7 @@
placeholder="Invite Code" placeholder="Invite Code"
id="inviteCode" id="inviteCode"
bind:value={code} bind:value={code}
on:keydown={(event) => inviteCodeKeyPress(event)} on:keydown={(event) => inviteCodeKeyPress(event)} />
/>
</label> </label>
{/if} {/if}
{#if !loading} {#if !loading}
@@ -108,8 +106,7 @@
class="btn variant-ghost-success mt-3" class="btn variant-ghost-success mt-3"
type="button" type="button"
disabled={!code} disabled={!code}
on:click={() => addCode(code)}>{buttonText}</button on:click={() => addCode(code)}>{buttonText}</button>
>
{:else} {:else}
<div class="text-primary">Going Anon, please wait...</div> <div class="text-primary">Going Anon, please wait...</div>
<Loading /> <Loading />
@@ -120,8 +117,7 @@
<div> <div>
If you are having trouble and would like help, please message us on <a If you are having trouble and would like help, please message us on <a
href="https://discord.gg/brJQ36KVxk" href="https://discord.gg/brJQ36KVxk"
class="underline link">Discord</a class="underline link">Discord</a>
>
</div> </div>
</aside> </aside>
{/if} {/if}

View File

@@ -16,7 +16,7 @@
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err); console.warn(err);
alertQueue.enqueue(`Unexpected error: ${err.message}`, 'error'); alertQueue.enqueue(`Unexpected error: ${err.message}`, 'error');
}) })
.finally(() => { .finally(() => {
@@ -55,9 +55,7 @@
class="px-2" class="px-2"
name="the-word-proof.json" name="the-word-proof.json"
bind:files bind:files
on:change={onChangeHandler} on:change={onChangeHandler} /></label>
/></label
>
</section> </section>
{#if loading} {#if loading}
<Loading /> <Loading />

View File

@@ -132,7 +132,7 @@ async function genProof(
); );
const proof = prover.generateProof(proofInputs).then((proof: RLNFullProof) => { const proof = prover.generateProof(proofInputs).then((proof: RLNFullProof) => {
console.log('Proof generated!'); console.info('Proof generated!');
const msg: MessageI = { const msg: MessageI = {
messageId: proof.snarkProof.publicSignals.nullifier.toString(), messageId: proof.snarkProof.publicSignals.nullifier.toString(),
message: message, message: message,

View File

@@ -128,3 +128,23 @@ export async function getAuth(
throw new Error(`Failed to post to ${url}`); throw new Error(`Failed to post to ${url}`);
} }
} }
/**
* @description - makes a get request to the api
* @param {string[] | string} urlParts - the url parts to be joined to form the url
* @param {object} data - the data to be sent to the api
* @returns {object} - the response from the api
* @throws {Error} - if the request fails
*/
export async function postRaw(urlParts: string[] | string, data: object): Promise<Response> {
const url = cleanURL(urlParts);
const res = await fetch(url, {
method: 'POST',
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return res;
}

View File

@@ -2,7 +2,7 @@ import type { MessageI, ServerI } from 'discreetly-interfaces';
import type { IdentityStoreI, Invites, JoinResponseI, JubmojiProofI, RoomI } from '$lib/types'; import type { IdentityStoreI, Invites, JoinResponseI, JubmojiProofI, RoomI } from '$lib/types';
import { Prover } from 'idc-nullifier'; import { Prover } from 'idc-nullifier';
import type { Identity } from '@semaphore-protocol/identity'; import type { Identity } from '@semaphore-protocol/identity';
import { get, getAuth, post, postAuth } from './api'; import { get, getAuth, post, postAuth, postRaw } from './api';
import { getIdentity } from '$lib/utils'; import { getIdentity } from '$lib/utils';
import { alertQueue } from '$lib/stores'; import { alertQueue } from '$lib/stores';
@@ -193,6 +193,22 @@ export async function postCheckRoomPassword(
return Boolean(response.success); return Boolean(response.success);
} }
export async function postDoesRoomPasswordExist(
serverUrl: string,
roomId: string
): Promise<boolean> {
const response = await postRaw([serverUrl, `room/checkpasswordhash/${roomId}`], {
passwordHash: 'passwordHash'
});
if (response.status == 200 && (response.json() as unknown as CheckResponse).success == false) {
return false;
} else if (response.status == 400) {
return true;
} else {
return false;
}
}
export async function postSetRoomPassword( export async function postSetRoomPassword(
serverUrl: string, serverUrl: string,
roomId: string, roomId: string,
@@ -207,7 +223,6 @@ export async function postSetRoomPassword(
idForProof.nullifier = id._nullifier; idForProof.nullifier = id._nullifier;
idForProof.secret = id._secret; idForProof.secret = id._secret;
idForProof.commitment = id._commitment; idForProof.commitment = id._commitment;
// Proves you know the identity secret with a timestamp so this proof can't be replayed // Proves you know the identity secret with a timestamp so this proof can't be replayed
prover prover
.generateProof({ .generateProof({

View File

@@ -65,7 +65,7 @@ export const pixelStore = sessionable({} as pixelStoreI, 'pixelmaps');
/** /**
* @description Stores the encrypted key for each room keyed by the roomId * @description Stores the encrypted key for each room keyed by the roomId
*/ */
export const roomPassStore = encryptable({} as roomPassStoreI, 'roomKey'); export const roomPassStore = encryptable({} as roomPassStoreI, 'roomPasses');
/** /**
* @description Derived Store: The messages of the currently selected room * @description Derived Store: The messages of the currently selected room

View File

@@ -32,7 +32,7 @@ export function createIdentity(
alertQueue.enqueue('Identity Created! Congrats on your new journey', 'success'); alertQueue.enqueue('Identity Created! Congrats on your new journey', 'success');
return 'created'; return 'created';
} else { } else {
console.log(get(identityExists)); console.warn(get(identityExists));
alertQueue.enqueue('Error creating identity!!!', 'error'); alertQueue.enqueue('Error creating identity!!!', 'error');
return 'error'; return 'error';
} }

View File

@@ -8,7 +8,8 @@ import {
identityExists, identityExists,
alertQueue, alertQueue,
roomPassStore, roomPassStore,
selectedServer selectedServer,
roomKeyStore
} from '$lib/stores'; } from '$lib/stores';
import { encryptIdentity } from './identity'; import { encryptIdentity } from './identity';
import type { IdentityStoreI } from '$lib/types'; import type { IdentityStoreI } from '$lib/types';
@@ -75,6 +76,17 @@ export async function unlockPadlock(password: string) {
identityKeyStore.read(); identityKeyStore.read();
roomPassStore.read(); roomPassStore.read();
updateRooms(); updateRooms();
console.debug('Unlocking rooms');
setTimeout(() => {
const roomPasses = get(roomPassStore);
Object.keys(roomPasses).forEach(async (roomId) => {
const roomKey = await deriveKey(roomPasses[roomId].password);
roomKeyStore.update((roomKeys) => {
roomKeys[roomId] = roomKey;
return roomKeys;
});
});
}, 50);
}); });
} else { } else {
alertQueue.enqueue('Incorrect Password', 'warning'); alertQueue.enqueue('Incorrect Password', 'warning');
@@ -85,12 +97,17 @@ export async function unlockPadlock(password: string) {
export async function enterRoomPassword(password: string, roomId: string): Promise<boolean> { export async function enterRoomPassword(password: string, roomId: string): Promise<boolean> {
const hashedSaltedPassword = await hashPassword(password + roomId); const hashedSaltedPassword = await hashPassword(password + roomId);
if (hashedSaltedPassword) { if (hashedSaltedPassword) {
postCheckRoomPassword(get(selectedServer), roomId, hashedSaltedPassword).then((res) => { postCheckRoomPassword(get(selectedServer), roomId, hashedSaltedPassword).then(async (res) => {
if (res) { if (res) {
roomPassStore.update((roomPass) => { roomPassStore.update((roomPass) => {
roomPass[roomId] = { password, hashedSaltedPassword }; roomPass[roomId] = { password, hashedSaltedPassword };
return roomPass; return roomPass;
}); });
const key = await deriveKey(password);
roomKeyStore.update((roomKeys) => {
roomKeys[roomId] = key;
return roomKeys;
});
return true; return true;
} else { } else {
return false; return false;
@@ -103,12 +120,17 @@ export async function enterRoomPassword(password: string, roomId: string): Promi
export async function setRoomPassword(password: string, roomId: string): Promise<boolean> { export async function setRoomPassword(password: string, roomId: string): Promise<boolean> {
const hashedSaltedPassword = await hashPassword(password + roomId); const hashedSaltedPassword = await hashPassword(password + roomId);
if (hashedSaltedPassword) { if (hashedSaltedPassword) {
postSetRoomPassword(get(selectedServer), roomId, hashedSaltedPassword).then((res) => { postSetRoomPassword(get(selectedServer), roomId, hashedSaltedPassword).then(async (res) => {
if (res) { if (res) {
roomPassStore.update((roomPass) => { roomPassStore.update((roomPass) => {
roomPass[roomId] = { password, hashedSaltedPassword }; roomPass[roomId] = { password, hashedSaltedPassword };
return roomPass; return roomPass;
}); });
const key = await deriveKey(password);
roomKeyStore.update((roomKeys) => {
roomKeys[roomId] = key;
return roomKeys;
});
return true; return true;
} else { } else {
return false; return false;

View File

@@ -30,7 +30,7 @@
storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow }); storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
onMount(async () => { onMount(async () => {
console.log('Starting Up Application'); console.info('Starting Up Application');
if (getServerList().length === 0) { if (getServerList().length === 0) {
setDefaultServers(); setDefaultServers();
} }
@@ -45,7 +45,7 @@
drawerStore.close(); drawerStore.close();
} }
} else { } else {
console.log('Input field focused, not opening console'); console.debug('Input field focused, not opening console');
} }
} }
}); });

View File

@@ -34,6 +34,9 @@
<a <a
class="btn variant-filled-primary" class="btn variant-filled-primary"
href="/admin/addAdmin">Add Room Admin</a> href="/admin/addAdmin">Add Room Admin</a>
<a
class="btn variant-filled-warning"
href="/settings/debug">Debug</a>
</div> </div>
<style> <style>

View File

@@ -29,7 +29,7 @@
} }
async function newCodes(numCodes: number) { async function newCodes(numCodes: number) {
console.log(`Requesting ${numCodes} codes`); console.debug(`Requesting ${numCodes} codes`);
const canvasContainer = document.getElementById('qr'); const canvasContainer = document.getElementById('qr');
canvasContainer?.replaceChildren(); canvasContainer?.replaceChildren();
if (usesLeft == 0) { if (usesLeft == 0) {
@@ -44,7 +44,6 @@
expiresAt, expiresAt,
usesLeft usesLeft
); );
console.log(resp);
const codes = resp.codes; const codes = resp.codes;
codes.forEach((code) => { codes.forEach((code) => {
makeInviteQRCode(code.claimcode); makeInviteQRCode(code.claimcode);
@@ -68,14 +67,12 @@
<div class="flex flex-col place-content-center max-w-sm m-auto pt-5"> <div class="flex flex-col place-content-center max-w-sm m-auto pt-5">
<div <div
id="qr" id="qr"
class="flex flex-col gap-12 place-content-center" class="flex flex-col gap-12 place-content-center">
>
<div> <div>
<canvas <canvas
class="variant-soft-secondary" class="variant-soft-secondary"
width="250" width="250"
height="250" height="250" />
/>
<p>no code generated yet</p> <p>no code generated yet</p>
</div> </div>
</div> </div>
@@ -83,8 +80,7 @@
class="btn my-12 variant-soft-primary text-center items-center" class="btn my-12 variant-soft-primary text-center items-center"
on:click={() => { on:click={() => {
newCodes(numCodes); newCodes(numCodes);
}} }}>
>
{#if numCodes > 1} {#if numCodes > 1}
Generate New Codes Generate New Codes
{:else} {:else}
@@ -109,10 +105,8 @@
type="range" type="range"
min="1" min="1"
max="5" max="5"
bind:value={numCodes} bind:value={numCodes} />
/> </label></svelte:fragment>
</label></svelte:fragment
>
</AccordionItem> </AccordionItem>
<AccordionItem> <AccordionItem>
<svelte:fragment slot="summary">Number of Uses Per Code</svelte:fragment> <svelte:fragment slot="summary">Number of Uses Per Code</svelte:fragment>
@@ -123,10 +117,8 @@
type="range" type="range"
min="0" min="0"
max="50" max="50"
bind:value={usesLeft} bind:value={usesLeft} />
/> </label></svelte:fragment>
</label></svelte:fragment
>
</AccordionItem> </AccordionItem>
<AccordionItem> <AccordionItem>
<svelte:fragment slot="summary">Expiration</svelte:fragment> <svelte:fragment slot="summary">Expiration</svelte:fragment>
@@ -137,10 +129,8 @@
type="range" type="range"
min="1" min="1"
max="14" max="14"
bind:value={daysFromNow} bind:value={daysFromNow} />
/> </label></svelte:fragment>
</label></svelte:fragment
>
</AccordionItem> </AccordionItem>
<AccordionItem> <AccordionItem>
<svelte:fragment slot="summary">Rooms</svelte:fragment> <svelte:fragment slot="summary">Rooms</svelte:fragment>
@@ -152,13 +142,11 @@
type="checkbox" type="checkbox"
value={room.roomId} value={room.roomId}
on:change={updateRoomList} on:change={updateRoomList}
checked={roomIds.includes(String(room.roomId))} checked={roomIds.includes(String(room.roomId))} />
/>
<span title={String(room.roomId)}>{room.name}</span> <span title={String(room.roomId)}>{room.name}</span>
</label> </label>
{/each} {/each}
</div></svelte:fragment </div></svelte:fragment>
>
</AccordionItem> </AccordionItem>
<AccordionItem> <AccordionItem>
<svelte:fragment slot="summary">API</svelte:fragment> <svelte:fragment slot="summary">API</svelte:fragment>
@@ -168,18 +156,15 @@
<input <input
type="text" type="text"
class="input" class="input"
bind:value={$configStore.apiUsername} bind:value={$configStore.apiUsername} />
/>
</label> </label>
<label class="label"> <label class="label">
<span>Api Password</span> <span>Api Password</span>
<input <input
type="password" type="password"
class="input" class="input"
bind:value={$configStore.apiPassword} bind:value={$configStore.apiPassword} />
/> </label></svelte:fragment>
</label></svelte:fragment
>
</AccordionItem> </AccordionItem>
</Accordion> </Accordion>
</div> </div>

View File

@@ -12,7 +12,7 @@
async function getRooms() { async function getRooms() {
if (username && password) { if (username && password) {
console.log('getting rooms'); console.debug('getting rooms');
rooms = await getAllRooms($selectedServer, username, password); rooms = await getAllRooms($selectedServer, username, password);
} }
} }
@@ -39,7 +39,7 @@
} else { } else {
selectedRoomIds = selectedRoomIds.filter((id) => id !== roomId); selectedRoomIds = selectedRoomIds.filter((id) => id !== roomId);
} }
console.log(selectedRoomIds); console.debug('selectedRoomIds', selectedRoomIds);
} }
</script> </script>

View File

@@ -43,10 +43,10 @@
formData.bandadaGroupId, formData.bandadaGroupId,
formData.bandadaApiKey formData.bandadaApiKey
).then((res) => { ).then((res) => {
console.log(res); console.debug(res);
submitted = true; submitted = true;
createdCodes = res.claimCodes; createdCodes = res.claimCodes;
console.log(createdCodes); console.info(createdCodes);
}); });
} else { } else {
alertQueue.enqueue('You must create an identity before creating a room', 'error'); alertQueue.enqueue('You must create an identity before creating a room', 'error');
@@ -62,8 +62,7 @@
handleSubmit(); handleSubmit();
}} }}
buttonNext="variant-filled-surface-50-900-token" buttonNext="variant-filled-surface-50-900-token"
buttonComplete="variant-filled-success" buttonComplete="variant-filled-success">
>
<RoomName {formData} /> <RoomName {formData} />
<MembershipType {formData} /> <MembershipType {formData} />
<RateLimit {formData} /> <RateLimit {formData} />

View File

@@ -6,9 +6,10 @@
import * as emoji from 'node-emoji'; import * as emoji from 'node-emoji';
export let bubbleText: string; export let bubbleText: string;
let formattedText = ''; let formattedText = '';
onMount(() => { onMount(async () => {
// Only render text on the frontend because DOMPurify // Only render text on the frontend because DOMPurify
// needs access to the DOM. // needs access to the DOM.
formattedText = formatText(bubbleText); formattedText = formatText(bubbleText);

View File

@@ -1,9 +1,7 @@
<script lang="ts"> <script lang="ts">
import { import {
currentSelectedRoom, currentSelectedRoom,
keyStore,
rateLimitStore, rateLimitStore,
roomKeyStore,
alertQueue, alertQueue,
identityExists, identityExists,
configStore configStore
@@ -12,13 +10,16 @@
import type { Socket } from 'socket.io-client'; import type { Socket } from 'socket.io-client';
import { getIdentity, clearMessageHistory } from '$lib/utils'; import { getIdentity, clearMessageHistory } from '$lib/utils';
import Send from 'svelte-material-icons/Send.svelte'; import Send from 'svelte-material-icons/Send.svelte';
import { decrypt, encrypt } from '$lib/crypto/crypto'; import { encrypt } from '$lib/crypto/crypto';
import { onMount } from 'svelte';
export let socket: Socket; export let socket: Socket;
export let connected: boolean; export let connected: boolean;
export let currentEpoch: number; export let currentEpoch: number;
export let userMessageLimit: number; export let userMessageLimit: number;
export let roomId: string; export let roomId: string;
export let getKey: () => Promise<CryptoKey>;
let key: CryptoKey;
let scrollChatEvent = new CustomEvent('scrollChat', { let scrollChatEvent = new CustomEvent('scrollChat', {
detail: { behavior: 'smooth', delay: 20 } detail: { behavior: 'smooth', delay: 20 }
@@ -111,27 +112,12 @@
} }
// Helper function to handle encrypted room messages // Helper function to handle encrypted room messages
async function handleEncryptedMessage( async function handleEncryptedMessage(messageText: string, roomId: string): Promise<string> {
messageText: string, const encryptedMessage = await encrypt(messageText, key);
roomId: string,
keyStore: any,
roomKeyStore: any
): Promise<string> {
if ($currentSelectedRoom.encrypted == 'AES' && !roomKeyStore[roomId]) {
throw new Error('ROOM IS ENCRYPTED BUT NO PASSWORD WAS FOUND');
}
if (!keyStore) {
throw new Error('NO KEYSTORE FOUND');
}
const key = await decrypt(roomKeyStore[roomId], keyStore);
if (!key) {
throw new Error('NO KEY FOUND');
}
const encryptedMessage = await encrypt(messageText, key as unknown as CryptoKey);
if (encryptedMessage == null) { if (encryptedMessage == null) {
throw new Error('ENCRYPTION FAILED'); throw new Error('ENCRYPTION FAILED');
} else { } else {
console.debug(encryptedMessage);
return encryptedMessage; return encryptedMessage;
} }
} }
@@ -164,12 +150,7 @@
let messageToSend: string = messageText; let messageToSend: string = messageText;
if (room.encrypted === 'AES') { if (room.encrypted === 'AES') {
messageToSend = await handleEncryptedMessage( messageToSend = await handleEncryptedMessage(messageText, room.roomId!.toString());
messageText,
room.roomId!.toString(),
$keyStore,
$roomKeyStore
);
} }
const msg = await genProof( const msg = await genProof(
@@ -215,6 +196,12 @@
} }
} }
} }
onMount(() => {
getKey().then((k) => {
key = k;
});
});
</script> </script>
<section class="border-t border-surface-500/30 p-2 md:p-4 !border-dashed"> <section class="border-t border-surface-500/30 p-2 md:p-4 !border-dashed">

View File

@@ -6,7 +6,10 @@
selectedServer, selectedServer,
configStore, configStore,
currentRoomsStore, currentRoomsStore,
roomPasswordSet roomPasswordSet,
identityExists,
roomKeyStore,
roomPassStore
} from '$lib/stores'; } from '$lib/stores';
import RoomPassword from './RoomPassword.svelte'; import RoomPassword from './RoomPassword.svelte';
import { Experiences } from '$lib/types'; import { Experiences } from '$lib/types';
@@ -19,6 +22,7 @@
import Conversation from './Conversation.svelte'; import Conversation from './Conversation.svelte';
import Draw from './Draw.svelte'; import Draw from './Draw.svelte';
import InputPrompt from './ChatInputPrompt.svelte'; import InputPrompt from './ChatInputPrompt.svelte';
import { deriveKey } from '$lib/crypto/crypto';
const toastStore = getToastStore(); const toastStore = getToastStore();
@@ -59,6 +63,20 @@
$: updateRooms($selectedServer, [roomId]); $: updateRooms($selectedServer, [roomId]);
async function getKey(): Promise<CryptoKey> {
let key: CryptoKey;
if (!$roomPassStore[roomId]) {
throw new Error('ROOM IS ENCRYPTED BUT NO PASSWORD WAS FOUND');
}
if (!$roomKeyStore[roomId]) {
key = await deriveKey($roomPassStore[roomId].password);
$roomKeyStore[roomId] = key;
} else {
key = $roomKeyStore[roomId];
}
return key;
}
function updateEpoch() { function updateEpoch() {
if ($currentSelectedRoom === undefined) { if ($currentSelectedRoom === undefined) {
if ($currentRoomsStore[0] === undefined) { if ($currentRoomsStore[0] === undefined) {
@@ -143,6 +161,7 @@
updateEpoch(); updateEpoch();
}, 100); }, 100);
}); });
onDestroy(() => { onDestroy(() => {
unsubscribeStore(); unsubscribeStore();
socket.emit('leavingRoom', $currentSelectedRoom?.roomId); socket.emit('leavingRoom', $currentSelectedRoom?.roomId);
@@ -164,31 +183,39 @@
{onlineMembers} /> {onlineMembers} />
{#if $configStore.experience == Experiences.Chat} {#if $configStore.experience == Experiences.Chat}
{#key $currentSelectedRoom.roomId} {#key $currentSelectedRoom.roomId}
<Conversation {roomRateLimit} /> <Conversation
{roomRateLimit}
{getKey} />
{/key} {/key}
<InputPrompt <InputPrompt
{socket} {socket}
{connected} {connected}
{currentEpoch} {currentEpoch}
{userMessageLimit} {userMessageLimit}
{roomId} /> {roomId}
{getKey} />
{:else if $configStore.experience == Experiences.Draw} {:else if $configStore.experience == Experiences.Draw}
<Draw /> <Draw />
{:else} {:else}
{#key $currentSelectedRoom.roomId} {#key $currentSelectedRoom.roomId}
<Conversation {roomRateLimit} /> <Conversation
{roomRateLimit}
{getKey} />
{/key} {/key}
<InputPrompt <InputPrompt
{socket} {socket}
{connected} {connected}
{currentEpoch} {currentEpoch}
{userMessageLimit} {userMessageLimit}
{roomId} /> {roomId}
{getKey} />
{/if} {/if}
<!-- Conversation --> <!-- Conversation -->
<!-- Prompt --> <!-- Prompt -->
</div> </div>
{:else if $identityExists == 'encrypted'}
<h3 class="h3 my-5 text-center text-primary-500">Unlock your credentials to see this room.</h3>
{:else} {:else}
<div><RoomPassword {roomId} /></div> <div><RoomPassword {roomId} /></div>
{/if} {/if}

View File

@@ -6,8 +6,11 @@
import BubbleText from './BubbleText.svelte'; import BubbleText from './BubbleText.svelte';
import { minidenticon } from 'minidenticons'; import { minidenticon } from 'minidenticons';
import { bubbleBgFromSessionId } from '$lib/utils/color'; import { bubbleBgFromSessionId } from '$lib/utils/color';
import { decrypt } from '$lib/crypto/crypto';
export let roomRateLimit: number; export let roomRateLimit: number;
export let getKey: () => Promise<CryptoKey>;
let key: CryptoKey;
let elemChat: HTMLElement; let elemChat: HTMLElement;
@@ -21,6 +24,21 @@
} }
} }
async function decryptText(text: string): Promise<string> {
if (!key) {
return getKey().then(async (k) => {
key = k;
const result = await decrypt(text, key);
return result ? result : text;
});
} else if (key) {
const result = await decrypt(text, key);
return result ? result : text;
} else {
return text;
}
}
function scrollChatBottom(behavior: ScrollBehavior = 'smooth', delay = 20, count = 0): void { function scrollChatBottom(behavior: ScrollBehavior = 'smooth', delay = 20, count = 0): void {
if (count > 10) { if (count > 10) {
console.warn('scrollChatBottom: elemChat is not defined after multiple attempts, giving up'); console.warn('scrollChatBottom: elemChat is not defined after multiple attempts, giving up');
@@ -56,6 +74,9 @@
const delay = customEvent.detail.delay ? customEvent.detail.delay : 20; const delay = customEvent.detail.delay ? customEvent.detail.delay : 20;
scrollChatBottom(behavior, delay); scrollChatBottom(behavior, delay);
}); });
getKey().then((k) => {
key = k;
});
}); });
</script> </script>
@@ -78,7 +99,14 @@
</div> </div>
{/if} {/if}
{#key msg} {#key msg}
<BubbleText bubbleText={String(msg.message)} /> {#await decryptText(String(msg.message))}
<p>Decrypting...</p>
{:then decryptedText}
<BubbleText bubbleText={decryptedText} />
{:catch error}
<BubbleText bubbleText={String(msg.message)} />
<!-- You can customize this error state -->
{/await}
{/key} {/key}
</div> </div>
<footer class="flex justify-between items-center text-xs md:text-sm pb-1"> <footer class="flex justify-between items-center text-xs md:text-sm pb-1">

View File

@@ -11,7 +11,7 @@
const parent = canvas.parentElement; const parent = canvas.parentElement;
const parentWidth = parent!.clientWidth; const parentWidth = parent!.clientWidth;
const parentHeight = parent!.clientHeight - 150; const parentHeight = parent!.clientHeight - 150;
console.log(parentWidth, parentHeight); console.debug(parentWidth, parentHeight);
let height, width; let height, width;
if (parentHeight < parentWidth / aspectRatio) { if (parentHeight < parentWidth / aspectRatio) {
@@ -24,7 +24,7 @@
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
console.log(canvas.height, canvas.width); console.debug(canvas.height, canvas.width);
} }
onMount(() => { onMount(() => {
@@ -53,5 +53,4 @@
id="drawing-canvas" id="drawing-canvas"
width="250" width="250"
height="250" height="250"
class="border border-surface-300-600-token m-1 sm:m-3" class="border border-surface-300-600-token m-1 sm:m-3" />
/>

View File

@@ -6,12 +6,12 @@
$: if (room.encrypted) { $: if (room.encrypted) {
if ($roomPassStore[room.roomId.toString()] && $keyStore) { if ($roomPassStore[room.roomId.toString()] && $keyStore) {
console.log('Room is encrypted and encrypted password is stored'); console.debug('Room is encrypted and encrypted password is stored');
decrypt($roomPassStore[room.roomId.toString()], $keyStore); decrypt($roomPassStore[room.roomId.toString()].password, $keyStore);
} else { } else {
console.log('Room is encrypted and password is not stored'); console.debug('Room is encrypted and password is not stored');
} }
} else { } else {
console.log('Room is not encrypted'); console.debug('Room is not encrypted');
} }
</script> </script>

View File

@@ -1,35 +1,113 @@
<script lang="ts"> <script lang="ts">
import { enterRoomPassword } from '$lib/utils'; import { enterRoomPassword, getCommitment } from '$lib/utils';
import Container from '$lib/components/Utils/Container.svelte'; import Container from '$lib/components/Utils/Container.svelte';
import { postDoesRoomPasswordExist, postSetRoomPassword } from '$lib/services/server';
import { roomPassStore, selectedServer, roomsStore } from '$lib/stores';
import { onMount } from 'svelte';
import { hashPassword } from '$lib/crypto/crypto';
import Loading from '$lib/components/Utils/Loading.svelte';
export let roomId: string; export let roomId: string;
let _passwordSet: boolean;
const minPasswordLength = 4;
let r = ''; let r = '';
let admin = false;
let loaded = false;
function onSubmit() { function enterPassword() {
if (r != '' && r != null && r != undefined && r.length >= minPasswordLength) { enterRoomPassword(r, roomId);
enterRoomPassword(r, roomId); }
async function setRoomPassword() {
const hashedSaltedPassword = await hashPassword(r + roomId);
if (hashedSaltedPassword) {
postSetRoomPassword($selectedServer, roomId, hashedSaltedPassword).then((answer) => {
if (answer) {
roomPassStore.update((roomPass) => {
roomPass[roomId] = { password: r, hashedSaltedPassword };
return roomPass;
});
}
});
} else {
console.error('Failed to hash password');
} }
} }
function isPasswordSet() {
try {
postDoesRoomPasswordExist($selectedServer, roomId).then((answer) => {
_passwordSet = answer;
});
} catch (e) {
console.warn(e);
}
}
function isAdmin() {
const idc = getCommitment();
$roomsStore[roomId].adminIdentities?.forEach((id) => {
if (id === idc) {
admin = true;
}
});
}
onMount(() => {
isPasswordSet();
isAdmin();
loaded = true;
});
</script> </script>
<Container heading="Enter Room Password"> {#if loaded}
<form {#if _passwordSet}
on:submit|preventDefault={() => onSubmit()} <Container heading="Enter Room Password">
class="flex flex-col w-full"> <form
<label on:submit|preventDefault={() => enterPassword()}
for="enterRoomPassword" class="flex flex-col w-full">
class="label" /> <label
<input for="enterRoomPassword"
id="enterRoomPassword" class="label" />
type="password" <input
name="password" id="enterRoomPassword"
class="input" type="password"
bind:value={r} name="password"
required /> class="input"
<button bind:value={r}
class="btn variant-filled-primary mt-3" required />
type="submit">Enter Room Password</button> <button
</form> class="btn variant-filled-primary mt-3"
</Container> type="submit">Enter Room Password</button>
</form>
</Container>
{:else if admin}
<Container heading="Set Room Password">
<form
on:submit|preventDefault={() => setRoomPassword()}
class="flex flex-col w-full">
<label
for="setRoomPassword"
class="label" />
<input
id="setRoomPassword"
type="password"
name="password"
class="input"
bind:value={r}
required />
<button
class="btn variant-filled-primary mt-3"
type="submit">Set Room Password</button>
</form>
</Container>
{:else}
<Container heading="Password Not Set">
<p class="text-center">
The room you are trying to enter requires a password, but the password has not been set yet.
Annoy the administrators who created this room!
</p>
</Container>
{/if}
{:else}
<Loading />
{/if}

View File

@@ -54,7 +54,6 @@
} }
}) })
.catch((err) => { .catch((err) => {
console.log(err);
addConsoleMessage(err, 'error'); addConsoleMessage(err, 'error');
}); });
break; break;
@@ -161,5 +160,4 @@
class="input py-1 px-2" class="input py-1 px-2"
type="text" type="text"
{placeholder} {placeholder}
on:keypress={handleInput} on:keypress={handleInput} />
/>

View File

@@ -4,6 +4,7 @@
identityExists, identityExists,
identityKeyStore, identityKeyStore,
identityStore, identityStore,
roomPassStore,
lockStateStore, lockStateStore,
passwordSet, passwordSet,
roomsStore, roomsStore,
@@ -49,8 +50,7 @@
<div class="p-4"> <div class="p-4">
<div <div
id="status" id="status"
class="flex flex-col gap-5" class="flex flex-col gap-5">
>
<div> <div>
<h2 class="h3">configStore</h2> <h2 class="h3">configStore</h2>
<div>Completed Signup: {JSON.stringify($configStore.signUpStatus.completedSignup)}</div> <div>Completed Signup: {JSON.stringify($configStore.signUpStatus.completedSignup)}</div>
@@ -103,19 +103,20 @@
<div class="ms-4">membershipType: {membershipType}</div> <div class="ms-4">membershipType: {membershipType}</div>
{/each} {/each}
</div> </div>
<div>
roomPassStore: {JSON.stringify($roomPassStore)}
</div>
</div> </div>
<div class="border-t py-2 my-5"> <div class="border-t py-2 my-5">
<button <button
class="btn variant-outline-primary m-4" class="btn variant-outline-primary m-4"
on:click={triggerAlert}>Test Alert</button on:click={triggerAlert}>Test Alert</button>
>
<select <select
class="select" class="select"
name="alertType" name="alertType"
id="alertType" id="alertType"
bind:value={t} bind:value={t}>
>
{#each c as choice} {#each c as choice}
<option value={choice}>{choice}</option> <option value={choice}>{choice}</option>
{/each} {/each}

View File

@@ -5,7 +5,7 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
$configStore.signUpStatus.inviteCode = $page.params.invite; $configStore.signUpStatus.inviteCode = $page.params.invite;
console.log(`Invited with code: ${$page.params.invite}`); console.info(`Invited with code: ${$page.params.invite}`);
onMount(() => { onMount(() => {
if ($identityExists) { if ($identityExists) {

View File

@@ -4,13 +4,13 @@ Circuit_Type = "idcNullifier"
[Circuit_Build] [Circuit_Build]
Circom_Version = "circom compiler 2.1.5" Circom_Version = "circom compiler 2.1.5"
GitHub_URL = "https://github.com/Discreetly/IdentityCommitmentNullifierCircuit.git" GitHub_URL = "https://github.com/Discreetly/IdentityCommitmentNullifierCircuit.git"
Git_Commit = "b940865" Git_Commit = "a0d6415"
Compilation_Time = 1698174365 Compilation_Time = 1699876330
[Files] [Files]
Wasm = "circuit.wasm" Wasm = "circuit.wasm"
Wasm_SHA256SUM = "410da93349f51f96dfb1bbb2d6578617a3de2494cf46d1ca6c1910a0098c82b8" Wasm_SHA256SUM = "410da93349f51f96dfb1bbb2d6578617a3de2494cf46d1ca6c1910a0098c82b8"
Zkey = "final.zkey" Zkey = "final.zkey"
Zkey_SHA256SUM = "7a626d709b4f6dc1150b554b3e643bd22099eaad1b5ed8d3b0202a8756004248" Zkey_SHA256SUM = "6a375f8c6066abd38535bcadfc03c0616bdd80c2fca96bb6c1e4d4aa41f9662b"
Verification_Key = "verification_key.json" Verification_Key = "verification_key.json"
Verification_Key_SHA256SUM = "1406f9e05c4bbbef93c9053cf0fa10a47881c5e8e3f1ada67e1ef3c5260fce91" Verification_Key_SHA256SUM = "cc7b95eb8adedbdbfe0ff150bca89597d888230a9c11901b7d0a81ad92b9b36d"

Binary file not shown.

View File

@@ -1,95 +1,104 @@
{ {
"protocol": "groth16", "protocol": "groth16",
"curve": "bn128", "curve": "bn128",
"nPublic": 3, "nPublic": 3,
"vk_alpha_1": [ "vk_alpha_1": [
"20491192805390485299153009773594534940189261866228447918068658471970481763042", "20491192805390485299153009773594534940189261866228447918068658471970481763042",
"9383485363053290200918347156157836566562967994039712273449902621266178545958", "9383485363053290200918347156157836566562967994039712273449902621266178545958",
"1" "1"
], ],
"vk_beta_2": [ "vk_beta_2": [
[ [
"6375614351688725206403948262868962793625744043794305715222011528459656738731", "6375614351688725206403948262868962793625744043794305715222011528459656738731",
"4252822878758300859123897981450591353533073413197771768651442665752259397132" "4252822878758300859123897981450591353533073413197771768651442665752259397132"
], ],
[ [
"10505242626370262277552901082094356697409835680220590971873171140371331206856", "10505242626370262277552901082094356697409835680220590971873171140371331206856",
"21847035105528745403288232691147584728191162732299865338377159692350059136679" "21847035105528745403288232691147584728191162732299865338377159692350059136679"
], ],
["1", "0"] [
], "1",
"vk_gamma_2": [ "0"
[ ]
"10857046999023057135944570762232829481370756359578518086990519993285655852781", ],
"11559732032986387107991004021392285783925812861821192530917403151452391805634" "vk_gamma_2": [
], [
[ "10857046999023057135944570762232829481370756359578518086990519993285655852781",
"8495653923123431417604973247489272438418190587263600148770280649306958101930", "11559732032986387107991004021392285783925812861821192530917403151452391805634"
"4082367875863433681332203403145435568316851327593401208105741076214120093531" ],
], [
["1", "0"] "8495653923123431417604973247489272438418190587263600148770280649306958101930",
], "4082367875863433681332203403145435568316851327593401208105741076214120093531"
"vk_delta_2": [ ],
[ [
"17133313670231667487736004097020484843226717741134543071681595192213542404569", "1",
"18673817425770621231905673683883346893405144776326422608131560048409738195792" "0"
], ]
[ ],
"11821370524809748218863201748748705728394144148925669901234409589641087093424", "vk_delta_2": [
"18513628196399124885663344181867643312766015533146283344455531540544551221665" [
], "2263054764437434763570286704031806516721917814051603656052135584430600195849",
["1", "0"] "332267239743604505067953832758848322829862699821984128892005653857113955614"
], ],
"vk_alphabeta_12": [ [
[ "8107422899773313798194038382837705724842762783652811880556511721860229797648",
[ "5609865441509880240754758422778809019695266789051834830754116541635728659997"
"2029413683389138792403550203267699914886160938906632433982220835551125967885", ],
"21072700047562757817161031222997517981543347628379360635925549008442030252106" [
], "1",
[ "0"
"5940354580057074848093997050200682056184807770593307860589430076672439820312", ]
"12156638873931618554171829126792193045421052652279363021382169897324752428276" ],
], "vk_alphabeta_12": [
[ [
"7898200236362823042373859371574133993780991612861777490112507062703164551277", [
"7074218545237549455313236346927434013100842096812539264420499035217050630853" "2029413683389138792403550203267699914886160938906632433982220835551125967885",
] "21072700047562757817161031222997517981543347628379360635925549008442030252106"
], ],
[ [
[ "5940354580057074848093997050200682056184807770593307860589430076672439820312",
"7077479683546002997211712695946002074877511277312570035766170199895071832130", "12156638873931618554171829126792193045421052652279363021382169897324752428276"
"10093483419865920389913245021038182291233451549023025229112148274109565435465" ],
], [
[ "7898200236362823042373859371574133993780991612861777490112507062703164551277",
"4595479056700221319381530156280926371456704509942304414423590385166031118820", "7074218545237549455313236346927434013100842096812539264420499035217050630853"
"19831328484489333784475432780421641293929726139240675179672856274388269393268" ]
], ],
[ [
"11934129596455521040620786944827826205713621633706285934057045369193958244500", [
"8037395052364110730298837004334506829870972346962140206007064471173334027475" "7077479683546002997211712695946002074877511277312570035766170199895071832130",
] "10093483419865920389913245021038182291233451549023025229112148274109565435465"
] ],
], [
"IC": [ "4595479056700221319381530156280926371456704509942304414423590385166031118820",
[ "19831328484489333784475432780421641293929726139240675179672856274388269393268"
"10540353245037691655240058832465935071147222378795512774896416883563614389198", ],
"20042286059857913285646233979306226083538103143380748863555356103197961213823", [
"1" "11934129596455521040620786944827826205713621633706285934057045369193958244500",
], "8037395052364110730298837004334506829870972346962140206007064471173334027475"
[ ]
"5831260214145524146501320942030802135386415386731869532840770699430145685903", ]
"10936406497477698856797103676487112559914492317404578945377836522823774186039", ],
"1" "IC": [
], [
[ "10540353245037691655240058832465935071147222378795512774896416883563614389198",
"1295407852409244811850984901965423450287105807829809873962419792828349329577", "20042286059857913285646233979306226083538103143380748863555356103197961213823",
"11301654800484264796111478656697951131719015244071014736702281514571505462297", "1"
"1" ],
], [
[ "5831260214145524146501320942030802135386415386731869532840770699430145685903",
"6823739927950557742246913693052072220213742766782370219666937126411380340633", "10936406497477698856797103676487112559914492317404578945377836522823774186039",
"19878777316727584245579797737044924949420447706770682437221414501064053790692", "1"
"1" ],
] [
] "1295407852409244811850984901965423450287105807829809873962419792828349329577",
} "11301654800484264796111478656697951131719015244071014736702281514571505462297",
"1"
],
[
"6823739927950557742246913693052072220213742766782370219666937126411380340633",
"19878777316727584245579797737044924949420447706770682437221414501064053790692",
"1"
]
]
}