bunch of UI cleanup, add MIT license, add readme
This commit is contained in:
parent
1c75b72b82
commit
5cd3e3f990
|
@ -22,3 +22,4 @@ dist-ssr
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
.vercel
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
```
|
45
index.html
45
index.html
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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 }) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue