bunch of UI cleanup, add MIT license, add readme

This commit is contained in:
Silas 2024-01-29 22:41:01 -05:00
parent 1c75b72b82
commit 5cd3e3f990
Signed by: silentsilas
GPG Key ID: 4199EFB7DAA34349
7 changed files with 90 additions and 48 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
.vercel

8
LICENSE Normal file
View File

@ -0,0 +1,8 @@
Copyright 2024 silentsilas
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

21
README.md Normal file
View File

@ -0,0 +1,21 @@
# Sure - A Secure Message Sharing Web App
Sure is a web application that allows you to share secrets securely using end-to-end encryption without relying on a server. It is built with TypeScript, uses ECDH for secure communication between two parties, and works completely offline.
## Getting Started
If you have `asdf` installed, run `asdf install` at the root of the project. Otherwise install the version of NodeJS specified in `.tool-versions` in this repository before continuing.
```bash
git clone https://git.silentsilas.com/silentsilas/sure.git
cd sure
npm install && npm run dev
```
## Deploy
Output a static build to `./dist` that you can deploy to any host, or open `index.html` for a working offline version of the site.
```bash
npm run build
```

View File

@ -40,8 +40,14 @@
<body> <body>
<header class="container"> <header class="container">
<hgroup> <hgroup>
<h1>SURE</h1> <h1>
<p>Secure URL Requests</p> SURE
</h1>
<p>
<span style="color: #44a616;">S</span>ecure
<span style="color: #44a616;">U</span>RL
<span style="color: #44a616;">Re</span>quests
</p>
</hgroup> </hgroup>
</header> </header>
@ -57,7 +63,40 @@
</article> </article>
</dialog> </dialog>
<main id="app" class="container"></main> <main class="container">
<section>
<div id="app"></div>
<details>
<summary>How it Works:</summary>
<ul>
<li>Each client generates an ECDH keypair, consisting of a public key and a private key.</li>
<li>Your private key is kept in localStorage, and never leaves your device.</li>
<li>Your public key is embedded in the URLs you generate. This key can be safely shared anywhere without
compromising security.</li>
<li>When another client opens your generated URL, they will find your public ECDH key. They then generate a
random IV for this specific message, and use it, along with their private ECDH key and your public ECDH key,
to derive a shared secret (AES-GCM).</li>
<li>This derived shared secret never leaves their device. It is used to encrypt their message to you.
The encrypted message, along with their public key and the IV for this message, are embedded in the URL they
generate.</li>
<li>Upon opening the response URL, your device uses your private ECDH key, along with the public key and IV
from the URL, to recreate the shared secret. This secret is used to decrypt the message. If the message was
properly encrypted using the expected keys, it will be successfully decrypted and displayed to you.</li>
<li>If you clear your browser's local storage, you will not be able to decrypt any response URLs generated
with your previous unique URL.</li>
</ul>
</details>
</section>
</main>
<footer class="container">
<p>
<a href="https://git.silentsilas.com/silentsilas/sure" target="_blank" rel="noopener noreferrer">Source Code</a> |
<a href="https://silentsilas.com" target="_blank" rel="noopener noreferrer">whoami</a>
</p>
</footer>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>

View File

@ -5,31 +5,21 @@ import {
} from "../utils/crypto"; } from "../utils/crypto";
const MESSAGE_OUTPUT_ID = "message"; const MESSAGE_OUTPUT_ID = "message";
const DECRYPT_BUTTON_ID = "decrypt";
const TEMPLATE = ` const TEMPLATE = `
<section> <details open>
<details> <summary>How To Use:</summary>
<summary>How it Works</summary>
<ul> <ul>
<li>Press "Decrypt" to view the information sent by the sender.</li> <li>If someone used your unique URL to generate this response URL, you should see the decrypted message below.</li>
<li>The decrypted message will appear below.</li>
</ul> </ul>
</details> </details>
<form> <form>
<input
id="${DECRYPT_BUTTON_ID}"
type="submit"
value="Decrypt"
aria-label="Decrypt"
/>
<textarea <textarea
id="${MESSAGE_OUTPUT_ID}" id="${MESSAGE_OUTPUT_ID}"
readonly readonly
aria-label="Decrypted Message" aria-label="Decrypted Message"
></textarea> ></textarea>
</form> </form>
</section>
`; `;
export async function setupReceivePage( export async function setupReceivePage(
@ -38,14 +28,7 @@ export async function setupReceivePage(
) { ) {
element.innerHTML = TEMPLATE; element.innerHTML = TEMPLATE;
// Add an event listener to the "Decrypt" button decryptData(params);
const decryptButton = document.getElementById(
DECRYPT_BUTTON_ID
) as HTMLButtonElement;
decryptButton.addEventListener("click", (event: Event) => {
event.preventDefault();
decryptData(params);
});
} }
async function decryptData({ p, iv, m }: { p: string; iv: string; m: string }) { async function decryptData({ p, iv, m }: { p: string; iv: string; m: string }) {

View File

@ -1,14 +1,13 @@
import { retrieveOrGenerateKeyPair } from "../utils/crypto.ts"; import { retrieveOrGenerateKeyPair } from "../utils/crypto.ts";
const URL_INPUT_ID = "request-url"; const URL_INPUT_ID = "request-url";
const TEMPLATE = `<section> const TEMPLATE = `
<details> <details open>
<summary>How it Works</summary> <summary>How To Use:</summary>
<ul> <ul>
<li>Send the generated URL below to whom you wish to request data from.</li> <li>Share the URL below with whom you'd like to request data from.</li>
<li>Upon accessing the URL, the receiver can then enter the data you'd like from them.</li> <li>When the recipient opens the URL, they will be able to enter the data they'd like to send to you.</li>
<li>This generates a URL that they'll send back to you.</li> <li>They will then generate a new URL with their encrypted data, which only your original device can decrypt.</li>
<li>You can now access the encrypted data in the URL which only _your_ browser can decrypt.</li>
</ul> </ul>
</details> </details>
@ -22,7 +21,7 @@ const TEMPLATE = `<section>
required required
/> />
</form> </form>
</section>`; `;
export async function setupRequestPage(element: HTMLElement) { export async function setupRequestPage(element: HTMLElement) {
element.innerHTML = TEMPLATE; element.innerHTML = TEMPLATE;

View File

@ -9,14 +9,12 @@ const MESSAGE_INPUT_ID = "message";
const ENCRYPT_BUTTON_ID = "encrypt"; const ENCRYPT_BUTTON_ID = "encrypt";
const REQUEST_PUBLIC_KEY = "requestPublicKey"; const REQUEST_PUBLIC_KEY = "requestPublicKey";
const TEMPLATE = ` const TEMPLATE = `
<section> <details open>
<details> <summary>How To Use:</summary>
<summary>How it Works</summary>
<ul> <ul>
<li>Enter the information you'd like to send to the person whom generated this link.</li> <li>Enter the information you'd like to send to the person whom generated this link.</li>
<li>Press "Encrypt" to generate a URL that has your encrypted information.</li> <li>Press "Generate Response" to generate a URL below that has your encrypted message.</li>
<li>Send this generated URL to the person who sent this information request.</li> <li>Send this generated URL back to them, and only their browser can decrypt the message.</li>
<li>Only their browser can decrypt the secret in the URL.</li>
</ul> </ul>
</details> </details>
@ -32,18 +30,17 @@ const TEMPLATE = `
<input <input
id="${ENCRYPT_BUTTON_ID}" id="${ENCRYPT_BUTTON_ID}"
type="submit" type="submit"
value="Encrypt" value="Generate Response"
aria-label="Encrypt" aria-label="Generate Response"
/> />
<input <input
type="text" type="text"
name="encrypted-url" name="encrypted-url"
id="${ENCRYPTED_URL_INPUT_ID}" id="${ENCRYPTED_URL_INPUT_ID}"
placeholder="Generated URL will appear here..." placeholder="URL with your encrypted response will appear here..."
aria-label="Encrypted URL" aria-label="Encrypted URL"
/> />
</form> </form>
</section>
`; `;
export async function setupSendPage(element: HTMLElement, key: string) { export async function setupSendPage(element: HTMLElement, key: string) {
@ -110,11 +107,5 @@ async function encryptData(event: Event) {
) )
)}`; )}`;
// url.hash = `p=${btoa(JSON.stringify(ecdhPublicJwk))}&iv=${btoa(
// String.fromCharCode.apply(null, Array.from(iv))
// )}&m=${btoa(
// String.fromCharCode.apply(null, Array.from(new Uint8Array(encryptedData)))
// )}`;
encryptedUrlInput.value = url.toString(); encryptedUrlInput.value = url.toString();
} }