commit 1c75b72b823c8a7234be2fe86c67138dd6536881 Author: Silas Date: Mon Jan 29 19:22:39 2024 -0500 init commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e2f521c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + + [*] + charset = utf-8 + end_of_line = crlf + indent_style = space + insert_final_newline = true + trim_trailing_whitespace = true + + [*.css] + indent_size = 2 + + [*.{js,ts,json}] + indent_size = 2 + + [*.html] + indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..33f3680 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 20.11.0 \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..dba1c31 --- /dev/null +++ b/index.html @@ -0,0 +1,64 @@ + + + + + + + + SURE - Secure URL Requests + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

SURE

+

Secure URL Requests

+
+
+ + + +
+
No Javascript Detected
+

+ Unfortunately, this site requires Javascript in order to generate + keypairs, save them in localstorage, and encrypt/decrypt data. Please + enable Javascript and refresh the page. +

+
+
+ +
+ + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..789bcdb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,791 @@ +{ + "name": "sure-ts", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sure-ts", + "version": "0.0.0", + "dependencies": { + "@picocss/pico": "^1.5.11" + }, + "devDependencies": { + "typescript": "^5.2.2", + "vite": "^5.0.8", + "vite-plugin-make-offline": "^1.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@picocss/pico": { + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/@picocss/pico/-/pico-1.5.11.tgz", + "integrity": "sha512-cDaFiSyNPtuSTwSQt/05xsw8+g2ek4/S58tgh9Nc7miJCCdUrY9PAyl4OPWRJtYgJDdEvkUA9GuGj0J4nDP4Cw==" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz", + "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz", + "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz", + "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz", + "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz", + "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz", + "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz", + "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz", + "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz", + "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz", + "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz", + "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz", + "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz", + "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz", + "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.6", + "@rollup/rollup-android-arm64": "4.9.6", + "@rollup/rollup-darwin-arm64": "4.9.6", + "@rollup/rollup-darwin-x64": "4.9.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.6", + "@rollup/rollup-linux-arm64-gnu": "4.9.6", + "@rollup/rollup-linux-arm64-musl": "4.9.6", + "@rollup/rollup-linux-riscv64-gnu": "4.9.6", + "@rollup/rollup-linux-x64-gnu": "4.9.6", + "@rollup/rollup-linux-x64-musl": "4.9.6", + "@rollup/rollup-win32-arm64-msvc": "4.9.6", + "@rollup/rollup-win32-ia32-msvc": "4.9.6", + "@rollup/rollup-win32-x64-msvc": "4.9.6", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-make-offline": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vite-plugin-make-offline/-/vite-plugin-make-offline-1.0.0.tgz", + "integrity": "sha512-iPsB4QwfvhiftIEPvc3n+ZAO93SppbMZcbX8VAjzCQWHxEmCGpa/2eRgzZZgbuY0Hljs92dOWt05mgZx/5Lvvg==", + "dev": true, + "peerDependencies": { + "rollup": ">=2.79.0", + "vite": ">=3.2.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f9370d8 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "sure", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "^5.2.2", + "vite": "^5.0.8", + "vite-plugin-make-offline": "^1.0.0" + }, + "dependencies": { + "@picocss/pico": "^1.5.11" + } +} diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png new file mode 100644 index 0000000..0a86c15 Binary files /dev/null and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 0000000..4d8ac85 Binary files /dev/null and b/public/android-chrome-512x512.png differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..fd7c427 Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000..feb88b4 Binary files /dev/null and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 0000000..0d98cd2 Binary files /dev/null and b/public/favicon-32x32.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..4807e26 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/img/logo.png b/public/img/logo.png new file mode 100644 index 0000000..cff135a Binary files /dev/null and b/public/img/logo.png differ diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..d1d48d1 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "SURE", + "short_name": "SURE", + "theme_color": "#44a616", + "background_color": "#3d006e", + "display": "minimal-ui", + "scope": "/", + "start_url": "/", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..da35ba6 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,39 @@ +import "@picocss/pico/css/pico.min.css"; +import "./style.css"; +import { setupRequestPage } from "./routes/request.ts"; +import { setupSendPage } from "./routes/send.ts"; +import { setupReceivePage } from "./routes/receive.ts"; + +const start = () => { + const fragmentData = new URLSearchParams(window.location.hash.slice(1)); + const appElement = document.querySelector("#app"); + + if (!appElement) { + throw new Error("No app element found"); + } + + // If the URL contains the 'p', 'iv', and 'm' parameters, then the receiver page is set up + if ( + fragmentData.has("p") && + fragmentData.has("iv") && + fragmentData.has("m") + ) { + setupReceivePage(appElement, { + p: decodeURIComponent(fragmentData.get("p")!), + iv: decodeURIComponent(fragmentData.get("iv")!), + m: decodeURIComponent(fragmentData.get("m")!), + }); + return; + } + + // If the URL contains the 'p' parameter, then the sender page is set up + if (fragmentData.has("p")) { + setupSendPage(appElement, fragmentData.get("p")!); + return; + } + + // Otherwise, the request page is set up + setupRequestPage(appElement); +}; + +start(); diff --git a/src/routes/receive.ts b/src/routes/receive.ts new file mode 100644 index 0000000..6bc9902 --- /dev/null +++ b/src/routes/receive.ts @@ -0,0 +1,80 @@ +import { + deriveSharedSecret, + decrypt, + retrieveOrGenerateKeyPair, +} from "../utils/crypto"; + +const MESSAGE_OUTPUT_ID = "message"; +const DECRYPT_BUTTON_ID = "decrypt"; +const TEMPLATE = ` +
+
+ How it Works +
    +
  • Press "Decrypt" to view the information sent by the sender.
  • +
  • The decrypted message will appear below.
  • +
+
+ +
+ + +
+
+ `; + +export async function setupReceivePage( + element: HTMLElement, + params: { p: string; iv: string; m: string } +) { + element.innerHTML = TEMPLATE; + + // Add an event listener to the "Decrypt" button + 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 }) { + // Parse the 'p' parameter to get publicB + const publicBJwk = JSON.parse(atob(p)); + + // Import publicB as a CryptoKey + const publicB = await window.crypto.subtle.importKey( + "jwk", + publicBJwk, + { + name: "ECDH", + namedCurve: "P-256", + }, + false, + [] + ); + + const keyPairA = await retrieveOrGenerateKeyPair(); + + // Retrieve the AES key from local storage + const aesKey = await deriveSharedSecret(keyPairA.privateKey, publicB); + + // Decrypt the message using the AES key and IV + const decryptedData = await decrypt(aesKey, iv, m); + + // Update the message output with the decrypted message + const messageOutput = document.getElementById( + MESSAGE_OUTPUT_ID + ) as HTMLTextAreaElement; + messageOutput.value = decryptedData; +} diff --git a/src/routes/request.ts b/src/routes/request.ts new file mode 100644 index 0000000..e95cfd7 --- /dev/null +++ b/src/routes/request.ts @@ -0,0 +1,45 @@ +import { retrieveOrGenerateKeyPair } from "../utils/crypto.ts"; + +const URL_INPUT_ID = "request-url"; +const TEMPLATE = `
+
+ How it Works +
    +
  • Send the generated URL below to whom you wish to request data from.
  • +
  • Upon accessing the URL, the receiver can then enter the data you'd like from them.
  • +
  • This generates a URL that they'll send back to you.
  • +
  • You can now access the encrypted data in the URL which only _your_ browser can decrypt.
  • +
+
+ +
+ +
+
`; + +export async function setupRequestPage(element: HTMLElement) { + element.innerHTML = TEMPLATE; + + const url = await generateUrl(); + const input = document.getElementById(URL_INPUT_ID) as HTMLInputElement; + input.value = url.toString(); +} + +async function generateUrl(): Promise { + const { publicKey: ecdhPublic } = await retrieveOrGenerateKeyPair(); + + // Generate URL with public key as the 'p' search parameter + const ecdhPublicJwk = await window.crypto.subtle.exportKey("jwk", ecdhPublic); + const url = new URL(window.location.toString()); + url.hash = `p=${btoa(JSON.stringify(ecdhPublicJwk))}`; + + // Return the generated URL + return url; +} diff --git a/src/routes/send.ts b/src/routes/send.ts new file mode 100644 index 0000000..253aecd --- /dev/null +++ b/src/routes/send.ts @@ -0,0 +1,120 @@ +import { + deriveSharedSecret, + encrypt, + retrieveOrGenerateKeyPair, +} from "../utils/crypto"; + +const ENCRYPTED_URL_INPUT_ID = "encrypted-url"; +const MESSAGE_INPUT_ID = "message"; +const ENCRYPT_BUTTON_ID = "encrypt"; +const REQUEST_PUBLIC_KEY = "requestPublicKey"; +const TEMPLATE = ` +
+
+ How it Works +
    +
  • Enter the information you'd like to send to the person whom generated this link.
  • +
  • Press "Encrypt" to generate a URL that has your encrypted information.
  • +
  • Send this generated URL to the person who sent this information request.
  • +
  • Only their browser can decrypt the secret in the URL.
  • +
+
+ +
+ + + +
+
+ `; + +export async function setupSendPage(element: HTMLElement, key: string) { + element.innerHTML = TEMPLATE; + localStorage.setItem(REQUEST_PUBLIC_KEY, key); + + // Add an event listener to the "Encrypt" button + const encryptButton = document.getElementById( + ENCRYPT_BUTTON_ID + ) as HTMLButtonElement; + encryptButton.addEventListener("click", encryptData); +} + +async function encryptData(event: Event) { + event.preventDefault(); + const key = localStorage.getItem(REQUEST_PUBLIC_KEY)!; + + // Parse the 'p' parameter to get publicA + const publicAJwk = JSON.parse(atob(key)); + + // Import publicA as a CryptoKey + const publicA = await window.crypto.subtle.importKey( + "jwk", + publicAJwk, + { + name: "ECDH", + namedCurve: "P-256", + }, + false, + [] + ); + + const keyPairB = await retrieveOrGenerateKeyPair(); + + // Retrieve the message input + const messageInput = document.getElementById( + MESSAGE_INPUT_ID + ) as HTMLInputElement; + + // Retrieve the AES key from local storage + const aesKey = await deriveSharedSecret(keyPairB.privateKey, publicA); + + // Encrypt the message input value using the AES key + const { encryptedData, iv } = await encrypt(aesKey, messageInput.value); + + // Update the encrypted URL input with the encrypted message + const encryptedUrlInput = document.getElementById( + ENCRYPTED_URL_INPUT_ID + ) as HTMLInputElement; + + const ecdhPublicJwk = await window.crypto.subtle.exportKey( + "jwk", + keyPairB.publicKey + ); + const url = new URL(window.location.toString()); + + url.hash = `p=${encodeURIComponent( + btoa(JSON.stringify(ecdhPublicJwk)) + )}&iv=${encodeURIComponent( + btoa(String.fromCharCode.apply(null, Array.from(iv))) + )}&m=${encodeURIComponent( + btoa( + String.fromCharCode.apply(null, Array.from(new Uint8Array(encryptedData))) + ) + )}`; + + // 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(); +} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..2bbafba --- /dev/null +++ b/src/style.css @@ -0,0 +1,11 @@ +.js-warning { + display: none; +} + +.no-js .js-warning { + display: flex; +} + +.hidden { + display: none; +} diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts new file mode 100644 index 0000000..471ca8e --- /dev/null +++ b/src/utils/crypto.ts @@ -0,0 +1,143 @@ +export async function generateKeyPair(): Promise { + return await window.crypto.subtle.generateKey( + { + name: "ECDH", + namedCurve: "P-256", + }, + true, + ["deriveKey", "deriveBits"] + ); +} + +export async function deriveSharedSecret( + privateKey: CryptoKey, + publicKey: CryptoKey +): Promise { + return await window.crypto.subtle.deriveKey( + { + name: "ECDH", + + public: publicKey, + }, + privateKey, + { + name: "AES-GCM", + length: 256, + }, + true, + ["encrypt", "decrypt"] + ); +} + +export async function retrieveOrGenerateKeyPair(): Promise { + let ecdhPublic: CryptoKey; + let ecdhPrivate: CryptoKey; + + if ( + localStorage.getItem("ecdhPublic") && + localStorage.getItem("ecdhPrivate") + ) { + const ecdhPublicJwk = JSON.parse(localStorage.getItem("ecdhPublic")!); + const ecdhPrivateJwk = JSON.parse(localStorage.getItem("ecdhPrivate")!); + + ecdhPrivate = await window.crypto.subtle.importKey( + "jwk", + ecdhPrivateJwk, + { + name: "ECDH", + namedCurve: "P-256", + }, + true, + ["deriveKey", "deriveBits"] + ); + + ecdhPublic = await window.crypto.subtle.importKey( + "jwk", + ecdhPublicJwk, + { + name: "ECDH", + namedCurve: "P-256", + }, + true, + [] + ); + } else { + const keyPair = await generateKeyPair(); + ecdhPublic = keyPair.publicKey; + ecdhPrivate = keyPair.privateKey; + saveKeys(ecdhPublic, ecdhPrivate); + } + return { publicKey: ecdhPublic, privateKey: ecdhPrivate }; +} + +async function saveKeys(ecdhPublic: CryptoKey, ecdhPrivate: CryptoKey) { + const ecdhPublicJwk = await window.crypto.subtle.exportKey("jwk", ecdhPublic); + const ecdhPrivateJwk = await window.crypto.subtle.exportKey( + "jwk", + ecdhPrivate + ); + + // Store ECDH key pair in local storage + localStorage.setItem("ecdhPublic", JSON.stringify(ecdhPublicJwk)); + localStorage.setItem("ecdhPrivate", JSON.stringify(ecdhPrivateJwk)); +} + +export async function encrypt( + aesKey: CryptoKey, + data: string +): Promise<{ encryptedData: ArrayBuffer; iv: Uint8Array }> { + // Convert the data to a Uint8Array + const dataUint8Array = new TextEncoder().encode(data); + + // Generate a random initialization vector (iv) + const iv = window.crypto.getRandomValues(new Uint8Array(12)); + + // Encrypt the data + const encryptedData = await window.crypto.subtle.encrypt( + { + name: "AES-GCM", + iv: iv, + }, + aesKey, + dataUint8Array + ); + + // Return the combined iv and encrypted data + return { encryptedData, iv }; +} + +export async function decrypt( + aesKey: CryptoKey, + iv: string, + encryptedData: string +): Promise { + console.log(iv); + // Decode the iv and encryptedData from base64 + const ivUint8Array = base64ToUint8Array(iv); + const encryptedDataUint8Array = base64ToUint8Array(encryptedData); + + // Decrypt the data + const decryptedData = await window.crypto.subtle.decrypt( + { + name: "AES-GCM", + iv: ivUint8Array, + }, + aesKey, + encryptedDataUint8Array + ); + + // Convert the decrypted data from a Uint8Array to a string + const decryptedString = new TextDecoder().decode(decryptedData); + + return decryptedString; +} + +function base64ToUint8Array(base64: string): Uint8Array { + const binaryString = atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ee934c4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..f28b278 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "vite"; +import { makeOffline } from "vite-plugin-make-offline"; + +export default defineConfig({ + plugins: [makeOffline()], // This is the plugin 😃 +});