diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bee8a64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+__pycache__
diff --git a/Discovery.md b/Discovery.md
new file mode 100644
index 0000000..24a80bc
--- /dev/null
+++ b/Discovery.md
@@ -0,0 +1,13 @@
+---
+title: "Secrets: The secure notes"
+tagline: "Your average notes app with a twist, client-side encryption."
+theme_color: "#f26daa"
+git: "https://github.com/el-garro/secrets"
+---
+
+Secrets comes with:
+
+- Pure client-side encryption (Your password will NEVER abandon your computer)
+- Infinite boxes (You can log-in with any password, but it will only show notes that can be decrypted with it)
+- It's pink
+- Did I mentioned it's pink?
\ No newline at end of file
diff --git a/Spacefile b/Spacefile
new file mode 100644
index 0000000..7831b3d
--- /dev/null
+++ b/Spacefile
@@ -0,0 +1,8 @@
+# Spacefile Docs: https://go.deta.dev/docs/spacefile/v0
+v: 0
+icon: ./icon.png
+app_name: "Secrets"
+micros:
+ - name: main
+ src: ./main
+ engine: python3.9
\ No newline at end of file
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..2ad5914
Binary files /dev/null and b/icon.png differ
diff --git a/main/main.py b/main/main.py
new file mode 100644
index 0000000..75cdb91
--- /dev/null
+++ b/main/main.py
@@ -0,0 +1,43 @@
+from fastapi import FastAPI, Request
+from fastapi.staticfiles import StaticFiles
+from fastapi.responses import StreamingResponse
+from deta import Deta
+
+
+app = FastAPI(docs_url=None, openapi_url=None, redoc_url=None)
+deta = Deta()
+drive = deta.Drive("notes")
+
+
+@app.get("/api/v1/list")
+async def list_files():
+ result = drive.list()
+ all_files = result.get("names")
+ paging = result.get("paging")
+ last = paging.get("last") if paging else None
+ while last:
+ result = drive.list(last=last)
+ all_files += result.get("names")
+ paging = result.get("paging")
+ last = paging.get("last") if paging else None
+
+ return all_files
+
+
+@app.get("/api/v1/get/{fname}", response_class=StreamingResponse)
+async def get_file(fname: str):
+ file = drive.get(fname)
+ return StreamingResponse(file.iter_chunks())
+
+
+@app.post("/api/v1/post/{fname}")
+async def write_file(fname: str, request: Request):
+ return drive.put(fname, await request.body())
+
+
+@app.delete("/api/v1/del/{fname}")
+async def delete_file(fname: str):
+ return drive.delete(fname)
+
+
+app.mount("/", StaticFiles(directory="static", html=True))
diff --git a/main/requirements.txt b/main/requirements.txt
new file mode 100644
index 0000000..63a2128
--- /dev/null
+++ b/main/requirements.txt
@@ -0,0 +1,2 @@
+fastapi
+deta
\ No newline at end of file
diff --git a/main/static/css/style.css b/main/static/css/style.css
new file mode 100644
index 0000000..7928c6b
--- /dev/null
+++ b/main/static/css/style.css
@@ -0,0 +1,225 @@
+:root {
+ --color-back-primary: #1c1b1b;
+ --color-back-secondary: #32302f;
+ --color-text-primary: #cec7c1;
+ --color-deta-pink: #ef39a8;
+ --color-deta-pink-dark: #9b256d;
+}
+
+html {
+ color-scheme: dark;
+}
+
+body {
+ padding: 10px;
+ background-color: var(--color-back-primary);
+ color: var(--color-text-primary);
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+@media (min-width: 600px) {
+ body {
+ padding-left: 10vh;
+ padding-right: 10vh;
+ }
+}
+
+
+.header-title {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ column-gap: 5px;
+}
+
+
+.logo-img {
+ width: 50px;
+ height: 50px;
+ flex-grow: 0;
+}
+
+.small-img {
+ width: 25px;
+ height: 25px;
+}
+
+.header-title p {
+ margin: 0;
+ padding: 0;
+ flex-grow: 1;
+ font-size: 40px;
+}
+
+main {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.grid-container {
+ display: grid;
+ padding: 10px;
+ border: 5px dashed var(--color-deta-pink-dark);
+ border-radius: 10px;
+ gap: 10px;
+ grid-auto-rows: 160px;
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
+ overflow: auto;
+ flex-grow: 1;
+}
+
+.card {
+ background-color: var(--color-back-secondary);
+ overflow: hidden;
+ align-content: center;
+ border-radius: 15px;
+ padding: 10px;
+}
+
+.card:hover {
+ border: 2px solid var(--color-deta-pink);
+ padding: 8px;
+}
+
+.note figure {
+ height: 90px;
+ width: min(90px, 100%);
+ margin: auto;
+ border-radius: 10px;
+ overflow: hidden;
+ display: flex;
+
+
+}
+
+.note figure img {
+ object-fit: cover;
+ object-position: center;
+ width: 100%;
+ height: 100%;
+}
+
+.note p {
+ margin: 2px;
+ font-size: smaller;
+ text-align: center;
+ overflow-wrap: break-word;
+}
+
+.newnote {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.newnote p {
+ font-size: 50px;
+ margin: 0;
+ opacity: 0.2;
+}
+
+footer p {
+ text-align: right;
+}
+
+
+.modal {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: var(--color-back-primary);
+ background-color: rgba(var(--color-back-primary), 0.5);
+ backdrop-filter: blur(10px);
+ border-radius: 10px;
+ z-index: 1;
+}
+
+.login {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.login form {
+ text-align: center;
+ padding: 10px;
+ border: 5px dashed var(--color-deta-pink-dark);
+ border-radius: 10px;
+}
+
+.btn-big-pink {
+ border: 0px;
+ border-radius: 5px;
+ height: 40px;
+ padding-left: 10px;
+ padding-right: 10px;
+ background-color: var(--color-deta-pink);
+ overflow: hidden;
+}
+
+.btn-big-pink img {
+ height: 100%;
+ width: 100%;
+}
+
+.login form input {
+ height: 40px;
+ border: 0px;
+ background-color: var(--color-back-secondary);
+}
+
+.editor {
+ display: flex;
+ flex-direction: column;
+ row-gap: 10px;
+ padding: 10px;
+ margin: 1vh;
+ border: 5px dashed var(--color-deta-pink-dark);
+ border-radius: 10px;
+ height: 98vh;
+ align-items: flex-end;
+}
+
+.editor-buttons {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ column-gap: 2px;
+ width: min(200px, 80vw);
+}
+
+.note-contents {
+ height: 100%;
+ width: 100%;
+ border: 0px;
+ resize: none;
+ padding: 10px;
+ background-color: var(--color-back-secondary);
+ border-radius: 10px;
+ outline: none;
+}
+
+
+.popup-message-box {
+ margin: auto;
+ width: min(300px, 90%);
+ padding: 10px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ border: 5px dashed var(--color-deta-pink-dark);
+ background-color: var(--color-back-secondary);
+ border-radius: 10px;
+}
+
+.popup-message-box button {
+ align-self: flex-end;
+}
\ No newline at end of file
diff --git a/main/static/favicon.ico b/main/static/favicon.ico
new file mode 100644
index 0000000..d716274
Binary files /dev/null and b/main/static/favicon.ico differ
diff --git a/main/static/img/cancel_icon.png b/main/static/img/cancel_icon.png
new file mode 100644
index 0000000..1deeeea
Binary files /dev/null and b/main/static/img/cancel_icon.png differ
diff --git a/main/static/img/delete_icon.png b/main/static/img/delete_icon.png
new file mode 100644
index 0000000..b12a8ec
Binary files /dev/null and b/main/static/img/delete_icon.png differ
diff --git a/main/static/img/logo-192x192.png b/main/static/img/logo-192x192.png
new file mode 100644
index 0000000..aefc736
Binary files /dev/null and b/main/static/img/logo-192x192.png differ
diff --git a/main/static/img/logo-512x512.png b/main/static/img/logo-512x512.png
new file mode 100644
index 0000000..2ad5914
Binary files /dev/null and b/main/static/img/logo-512x512.png differ
diff --git a/main/static/img/note.png b/main/static/img/note.png
new file mode 100644
index 0000000..ef825a4
Binary files /dev/null and b/main/static/img/note.png differ
diff --git a/main/static/img/save_icon.png b/main/static/img/save_icon.png
new file mode 100644
index 0000000..2dc94fb
Binary files /dev/null and b/main/static/img/save_icon.png differ
diff --git a/main/static/index.html b/main/static/index.html
new file mode 100644
index 0000000..67254c0
--- /dev/null
+++ b/main/static/index.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+ Secrets
+
+
+
+
+
+
+
+
+ Your encrypted notes app and password manager (soon...maybe).
+
+
+
+
Your notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/static/js/app.js b/main/static/js/app.js
new file mode 100644
index 0000000..cd3006e
--- /dev/null
+++ b/main/static/js/app.js
@@ -0,0 +1,227 @@
+const SESSION_KEY = new Uluru.enc.Hex().decode(new Uluru.Random().fill(new Uint8Array(32)))
+let enc_password = ""
+
+function setPass(passw) {
+ enc_password = Uluru.encrypt(passw, SESSION_KEY)
+}
+
+
+function encrypt(plaintext) {
+ return Uluru.encrypt(plaintext, Uluru.decrypt(enc_password, SESSION_KEY))
+}
+
+function decrypt(ciphertext) {
+ return Uluru.decrypt(ciphertext, Uluru.decrypt(enc_password, SESSION_KEY))
+}
+
+function str2hex(str) {
+ let utf8 = new Uluru.enc.Utf8()
+ let hex = new Uluru.enc.Hex()
+ return hex.decode(utf8.encode(str))
+}
+
+function hex2str(str) {
+ let utf8 = new Uluru.enc.Utf8()
+ let hex = new Uluru.enc.Hex()
+ return utf8.decode(hex.encode(str))
+}
+
+
+async function listNotes() {
+ let request = await fetch("/api/v1/list")
+ let response = await request.json()
+
+ let notes = []
+ response.forEach(item => {
+ try {
+ notes.push([item, decrypt(hex2str(item))])
+ }
+ catch {
+
+ }
+ });
+ return notes
+}
+
+async function getNote(id) {
+ let request = await fetch("/api/v1/get/" + id)
+ let response = await request.text()
+ return decrypt(response)
+}
+
+async function putNote(id, text) {
+ let request = await fetch("/api/v1/post/" + id, {
+ method: "POST", body: encrypt(text)
+ })
+ let response = await request.json()
+
+ return response
+}
+
+async function delNote(id) {
+ let request = await fetch("/api/v1/del/" + id, {
+ method: "DELETE"
+ })
+ let response = await request.json()
+
+ return response
+}
+
+async function populateNotesArea() {
+ let area = document.querySelector("#notes")
+ let notes = await listNotes()
+ area.innerHTML = ""
+ notes.forEach(item => {
+ area.appendChild(createNoteCardHTML(item[0], item[1]))
+ });
+
+ area.innerHTML += ''
+}
+
+function createNoteCardHTML(id, name) {
+ let card = document.createElement("div")
+ card.setAttribute("class", "note card")
+ card.setAttribute("id", id)
+ card.setAttribute("onclick", "noteCard_OnClick(this.id)")
+
+ let figure = document.createElement("figure")
+
+ let img = new Image()
+ img.src = "img/note.png"
+
+ let p = document.createElement("p")
+
+ let text = document.createTextNode(name);
+
+ p.appendChild(text)
+ figure.appendChild(img)
+ card.appendChild(figure)
+ card.appendChild(p)
+
+ return card
+}
+
+
+function showPopup(message, show_button) {
+ let popup_message = document.querySelector("#popupText")
+ popup_message.innerHTML = message
+
+ let close_button = document.querySelector("#btnClosePopup")
+ if (show_button) {
+ close_button.removeAttribute("style")
+ } else {
+ close_button.setAttribute("style", "display: none")
+ }
+
+ let popup_screen = document.querySelector("#popupscreen")
+ popup_screen.removeAttribute("style")
+}
+
+function closePopup() {
+ let popup_screen = document.querySelector("#popupscreen")
+ popup_screen.setAttribute("style", "display: none")
+}
+
+function closeEditor() {
+ let editor_screen = document.querySelector("#editorscreen")
+ editor_screen.setAttribute("style", "display: none")
+}
+
+async function frmLogin_OnSubmit() {
+ let psw = document.forms["frmLogin"]["password"].value
+ if (psw) {
+ showPopup("Loading...", false)
+ setPass(psw)
+ await populateNotesArea()
+ document.forms["frmLogin"]["password"].value = ""
+
+ let login_screen = document.querySelector("#loginscreen")
+ login_screen.setAttribute("style", "display: none")
+ closePopup()
+ }
+ return false
+}
+
+async function noteCard_OnClick(id) {
+ showPopup("Loading note...", false)
+ let text = ""
+
+ if (id) {
+ try {
+ text = await getNote(id)
+ }
+ catch {
+ showPopup("Error loading note.", true)
+ return
+ }
+ } else {
+ id = ""
+ }
+
+ let note_textarea = document.querySelector("#noteText")
+ note_textarea.value = text
+
+ let save_button = document.querySelector("#btnSave")
+ save_button.setAttribute("onclick", `btnSave_OnClick("${id}")`)
+
+ let delete_button = document.querySelector("#btnDelete")
+ delete_button.setAttribute("onclick", `btnDelete_OnClick("${id}")`)
+
+ let editor_screen = document.querySelector("#editorscreen")
+ editor_screen.removeAttribute("style")
+
+ closePopup()
+}
+
+function btnClose_OnClick() {
+ if (confirm("Any changes will be discarded. Are you sure you want to close the editor?")) {
+ closeEditor()
+ }
+}
+
+async function btnSave_OnClick(id) {
+ if (!id) {
+ let note_name = prompt("Note name:", "")
+
+ if (!note_name) { return }
+ if (note_name.length > 20) {
+ showPopup("The name can't be larger than 20 characters.", true)
+ return
+ }
+ id = str2hex(encrypt(note_name))
+ }
+ showPopup("Saving note...", false)
+
+ let note_textarea = document.querySelector("#noteText")
+ await putNote(id, note_textarea.value)
+ await populateNotesArea()
+ closeEditor()
+ note_textarea.value = ""
+ closePopup()
+}
+
+async function btnDelete_OnClick(id) {
+ if (!id) { return }
+
+ if (confirm("Are you sure you want to delete this note?")) {
+ showPopup("Deleting note...", false)
+
+ try {
+ await delNote(id)
+ } catch {
+ showPopup("Error deleting note.", true)
+ }
+
+ await populateNotesArea()
+ closeEditor()
+ let note_textarea = document.querySelector("#noteText")
+ note_textarea.value = ""
+ closePopup()
+ }
+}
+
+
+
+
+
+
diff --git a/main/static/js/uluru.js b/main/static/js/uluru.js
new file mode 100644
index 0000000..af999a5
--- /dev/null
+++ b/main/static/js/uluru.js
@@ -0,0 +1,893 @@
+var crypto = typeof crypto == "object" ? crypto : typeof global == "object" ? function () {
+ var cryp = require("crypto");
+ return {
+ getRandomValues(data) {
+ cryp.randomFillSync(data);
+ return data;
+ }
+ };
+}() : undefined;
+var Uluru;
+(function (Uluru) {
+ let enc;
+ (function (enc) {
+ class Ascii {
+ encode(str) {
+ let u8array = new Uint8Array(str.length);
+ for (let i = 0, l = str.length; i < l; i++)
+ u8array[i] = str.charCodeAt(i);
+ return u8array;
+ }
+ decode(bytes) {
+ let bytearr = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ let str = Array(bytearr.length);
+ for (let i = 0, l = bytearr.length; i < l; i++)
+ str[i] = String.fromCharCode(bytearr[i]);
+ return str.join("");
+ }
+ }
+ enc.Ascii = Ascii;
+ })(enc = Uluru.enc || (Uluru.enc = {}));
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ let enc;
+ (function (enc) {
+ let b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ let b64paddings = ["", "=", "=="];
+ let b64codes = new Uint8Array(256);
+ for (let c = 0; c < 64; c++)
+ b64codes[b64chars.charCodeAt(c)] = c;
+ class Base64 {
+ encode(str) {
+ if (typeof atob == "function")
+ return new enc.Ascii().encode(atob(str));
+ str = str.replace(/[^A-Za-z0-9\+\/]/g, "");
+ let outlen = str.length * 3 + 1 >>> 2;
+ let bytes = new Uint8Array(outlen);
+ let mod3, mod4, out24 = 0, outidx = 0;
+ for (let i = 0, l = str.length; i < l; i++) {
+ mod4 = i & 3;
+ out24 |= b64codes[str.charCodeAt(i)] << 6 * (3 - mod4);
+ if (mod4 == 3 || str.length - i == 1) {
+ for (mod3 = 0; mod3 < 3 && outidx < outlen; mod3++, outidx++)
+ bytes[outidx] = out24 >>> (16 >>> mod3 & 24) & 0xff;
+ out24 = 0;
+ }
+ }
+ return bytes;
+ }
+ decode(bytes) {
+ let bytearr = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ if (typeof btoa == "function")
+ return btoa(new enc.Ascii().decode(bytearr));
+ let str = [];
+ let mod3 = 2, u24 = 0;
+ for (let i = 0, l = bytearr.length; i < l; i++) {
+ mod3 = i % 3;
+ u24 |= bytearr[i] << (16 >>> mod3 & 24);
+ if (mod3 == 2 || bytearr.length - i == 1) {
+ str.push(b64chars.charAt(u24 >>> 18 & 0x3f) +
+ b64chars.charAt(u24 >>> 12 & 0x3f) +
+ b64chars.charAt(u24 >>> 6 & 0x3f) +
+ b64chars.charAt(u24 & 0x3f));
+ u24 = 0;
+ }
+ }
+ str = str.join("");
+ return str.slice(0, str.length - 2 + mod3) + b64paddings[2 - mod3];
+ }
+ }
+ enc.Base64 = Base64;
+ })(enc = Uluru.enc || (Uluru.enc = {}));
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ const CONSTS = new Uint32Array([0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]);
+ class ChaCha20 {
+ constructor(key, mac = true, nonce = new Uint32Array(3), counter = 0) {
+ this.state = new Uint32Array(16);
+ this.xstate = new Uint32Array(16);
+ this.mask = new Uint32Array(16);
+ this.state.set(CONSTS);
+ this.state.set(new Uint32Array(key.buffer, key.byteOffset, key.byteLength >> 2), 4);
+ this.state.set(new Uint32Array(nonce.buffer, nonce.byteOffset, nonce.byteLength >> 2), 12);
+ this.state[15] = counter;
+ this.mask.set(this.state);
+ this.mask[15] = 0;
+ for (let init = 0; init < 10; init++) {
+ for (let ev = 0; ev < 4; ev++)
+ this.QR(this.mask, ev, ev + 4, ev + 8, ev + 12);
+ for (let od = 0; od < 16; od += 4)
+ this.QR(this.mask, od, od + 1, od + 2, od + 3);
+ }
+ this.domac = !!mac;
+ this.reset();
+ }
+ reset() {
+ this.xstate.fill(0);
+ this.pmac = this.cmac = false;
+ if (this.domac) {
+ this.pmac = new Uint32Array(4);
+ for (let i = 0; i < 16; i++)
+ this.pmac[i & 3] ^= this.mask[i];
+ this.cmac = this.pmac.slice();
+ }
+ this.data = new Uint32Array(0);
+ this.pointer = 0;
+ this.sigbytes = 0;
+ }
+ get counter() {
+ return this.state[15];
+ }
+ set counter(ctr) {
+ this.state[15] = ctr;
+ }
+ QR(state, A, B, C, D) {
+ state[A] += state[B];
+ state[D] ^= state[A];
+ state[D] = state[D] << 16 | state[D] >>> 16;
+ state[C] += state[D];
+ state[B] ^= state[C];
+ state[B] = state[B] << 12 | state[B] >>> 20;
+ state[A] += state[B];
+ state[D] ^= state[A];
+ state[D] = state[D] << 8 | state[D] >>> 24;
+ state[C] += state[D];
+ state[B] ^= state[C];
+ state[B] = state[B] << 7 | state[B] >>> 25;
+ }
+ getmac() {
+ if (!this.domac)
+ return false;
+ let pm = this.pmac, cm = this.cmac;
+ let mac = new Uint32Array([
+ pm[0] + cm[0],
+ pm[1] + cm[1],
+ pm[2] + cm[2],
+ pm[3] + cm[3],
+ ]);
+ for (let mr = 0; mr < 5; mr++) {
+ this.QR(mac, 0, 1, 2, 3);
+ this.QR(mac, 3, 0, 1, 2);
+ this.QR(mac, 2, 3, 0, 1);
+ this.QR(mac, 1, 2, 3, 0);
+ }
+ for (let re = 0; re < 4; re++)
+ mac[re] ^= pm[re] ^ cm[re];
+ return new Uint8Array(mac.buffer);
+ }
+ process(flush = false) {
+ let blocks = (flush ? Math.ceil : Math.floor)((this.sigbytes - this.pointer) / 16);
+ let ptw, ctw;
+ let end = Math.ceil(this.sigbytes / 4) - 1;
+ let erase = 4 - this.sigbytes % 4;
+ let domac = !!this.domac;
+ for (let b = 0; b < blocks; b++) {
+ let xs = this.xstate;
+ xs.set(this.state);
+ for (let drnd = 0; drnd < 20; drnd += 2) {
+ this.QR(xs, 0, 4, 8, 12);
+ this.QR(xs, 1, 5, 9, 13);
+ this.QR(xs, 2, 6, 10, 14);
+ this.QR(xs, 3, 7, 11, 15);
+ this.QR(xs, 0, 5, 10, 15);
+ this.QR(xs, 1, 6, 11, 12);
+ this.QR(xs, 2, 7, 8, 13);
+ this.QR(xs, 3, 4, 9, 14);
+ }
+ for (let i = 0; i < 16 && this.pointer + i <= end; i++) {
+ ptw = this.data[this.pointer + i];
+ ctw = ptw ^ (this.xstate[i] + this.mask[i]);
+ if (this.pointer + i == end)
+ ctw = ctw << erase * 8 >>> erase * 8;
+ this.data[this.pointer + i] = ctw;
+ if (domac) {
+ this.pmac[i & 3] ^= ptw;
+ this.cmac[i & 3] ^= ctw;
+ this.QR(this.pmac, (i + 3) & 3, i & 3, (i + 1) & 3, (i + 2) & 3);
+ this.QR(this.cmac, (i + 3) & 3, i & 3, (i + 1) & 3, (i + 2) & 3);
+ }
+ }
+ this.pointer += 16;
+ this.state[15]++;
+ }
+ }
+ append(data) {
+ data = typeof data == "string" ? new Uluru.enc.Utf8().encode(data) : data;
+ let old = this.data;
+ let newlen = Math.ceil((this.sigbytes + data.byteLength) / 64) * 64;
+ this.data = new Uint8Array(newlen);
+ this.data.set(new Uint8Array(old.buffer, 0, this.sigbytes));
+ this.data.set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), this.sigbytes);
+ this.data = new Uint32Array(this.data.buffer);
+ this.sigbytes += data.byteLength;
+ }
+ verify(mac) {
+ if (!this.domac)
+ return null;
+ return this.getmac().join(",") === new Uint8Array(mac.buffer, mac.byteOffset, mac.byteLength).join(",");
+ }
+ update(data) {
+ this.append(data);
+ this.process(false);
+ return this;
+ }
+ finalize() {
+ this.process(true);
+ let result = {
+ data: new Uint8Array(this.data.buffer, 0, this.sigbytes),
+ mac: this.getmac()
+ };
+ this.data = new Uint32Array(0);
+ this.pointer = 0;
+ this.sigbytes = 0;
+ return result;
+ }
+ }
+ Uluru.ChaCha20 = ChaCha20;
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ const SALTsize = 8;
+ function encrypt(plaintext, password) {
+ let salt = new Uluru.Random().fill(new Uint8Array(SALTsize));
+ let key = new Uluru.Pbkdf(32, 1000).compute(new Uluru.enc.Utf8().encode(password), salt).result;
+ let encryptor = new Uluru.ChaCha20(key, true, salt);
+ encryptor.update(new Uluru.enc.Utf8().encode(JSON.stringify(plaintext)));
+ let encrypted = encryptor.finalize();
+ return new Uluru.enc.Hex().decode(salt) +
+ new Uluru.enc.Base64().decode(encrypted.data) +
+ new Uluru.enc.Hex().decode(encrypted.mac);
+ }
+ Uluru.encrypt = encrypt;
+ function decrypt(ciphertext, password) {
+ let salt, cdata, mac;
+ try {
+ salt = new Uluru.enc.Hex().encode(ciphertext.slice(0, SALTsize * 2));
+ cdata = new Uluru.enc.Base64().encode(ciphertext.slice(SALTsize * 2, -32));
+ mac = new Uluru.enc.Hex().encode(ciphertext.slice(-32));
+ }
+ catch (e) {
+ throw "Incorrectly formated ciphertext";
+ }
+ let key = new Uluru.Pbkdf(32, 1000).compute(new Uluru.enc.Utf8().encode(password), salt).result;
+ let decryptor = new Uluru.ChaCha20(key, true, salt);
+ decryptor.update(cdata);
+ let decrypted = decryptor.finalize();
+ if (!decryptor.verify(mac))
+ throw "Invalid authentication";
+ return JSON.parse(new Uluru.enc.Utf8().decode(decrypted.data));
+ }
+ Uluru.decrypt = decrypt;
+ function hash(text) {
+ return new Uluru.Keccak800().update(new Uluru.enc.Utf8().encode(text)).finalize().toString(new Uluru.enc.Hex);
+ }
+ Uluru.hash = hash;
+ function rsaGenerate() {
+ return Uluru.RSAKeyPair.generate(3072).toString();
+ }
+ Uluru.rsaGenerate = rsaGenerate;
+ function rsaSign(message, privkeystr) {
+ return new Uluru.enc.Base64().decode(Uluru.RSAKey.fromString(privkeystr).sign(new Uluru.enc.Utf8().encode(message)).signature);
+ }
+ Uluru.rsaSign = rsaSign;
+ function rsaVerify(message, signature, pubkeystr) {
+ return Uluru.RSAKey.fromString(pubkeystr).verify(new Uluru.enc.Utf8().encode(message), new Uluru.enc.Base64().encode(signature));
+ }
+ Uluru.rsaVerify = rsaVerify;
+ function rsaEncrypt(message, pubkeystr) {
+ let key = Uluru.RSAKey.fromString(pubkeystr);
+ let symkey = new Uluru.Random().fill(new Uint8Array(32));
+ let encsymkey = new Uluru.enc.Base64().decode(key.encrypt(symkey).data);
+ let encryptor = new Uluru.ChaCha20(symkey, true);
+ encryptor.update(new Uluru.enc.Utf8().encode(message));
+ let encptx = encryptor.finalize();
+ return encsymkey + "|" + new Uluru.enc.Base64().decode(encptx.data) + new Uluru.enc.Hex().decode(encptx.mac);
+ }
+ Uluru.rsaEncrypt = rsaEncrypt;
+ function rsaDecrypt(message, privkeystr) {
+ let key, symkey, encptx, mac, splitted;
+ try {
+ key = Uluru.RSAKey.fromString(privkeystr);
+ splitted = message.split("|");
+ symkey = new Uluru.enc.Base64().encode(splitted[0]);
+ encptx = new Uluru.enc.Base64().encode(splitted[1].slice(0, -32));
+ mac = new Uluru.enc.Hex().encode(splitted[1].slice(-32));
+ }
+ catch (e) {
+ throw "Incorrectly formatted RSA ciphertext";
+ }
+ symkey = key.decrypt(symkey).data;
+ let decryptor = new Uluru.ChaCha20(symkey, true);
+ encptx = decryptor.update(encptx).finalize();
+ if (!decryptor.verify(mac))
+ throw "Invalid RSA message authentication code";
+ return new Uluru.enc.Utf8().decode(encptx.data);
+ }
+ Uluru.rsaDecrypt = rsaDecrypt;
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ let enc;
+ (function (enc) {
+ let hexcodes = Array(256);
+ let hexmap = {};
+ for (let h = 0; h < 256; h++) {
+ hexcodes[h] = ("00" + h.toString(16)).slice(-2);
+ hexmap[hexcodes[h]] = h;
+ }
+ class Hex {
+ encode(str) {
+ str = str.replace(/[^A-Fa-f0-9\+\/]/g, "").toLowerCase();
+ let bytes = new Uint8Array(str.length >> 1);
+ for (let hxcode = 0; hxcode < str.length; hxcode += 2)
+ bytes[hxcode >> 1] = hexmap[str.slice(hxcode, hxcode + 2)];
+ return bytes;
+ }
+ decode(bytes) {
+ let bytearr = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ let str = Array(bytearr.length);
+ for (let byte = 0; byte < bytearr.length; byte++)
+ str[byte] = hexcodes[bytearr[byte]];
+ return str.join("");
+ }
+ }
+ enc.Hex = Hex;
+ })(enc = Uluru.enc || (Uluru.enc = {}));
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ const RHOoffsets = new Uint8Array([
+ 0, 1, 30, 28, 27,
+ 4, 12, 6, 23, 20,
+ 3, 10, 11, 25, 7,
+ 9, 13, 15, 21, 8,
+ 18, 2, 29, 24, 14
+ ]);
+ const RCs = new Uint32Array([
+ 0x00000001, 0x00008082, 0x0000808a, 0x80008000, 0x0000808b, 0x80000001, 0x80008081, 0x00008009,
+ 0x0000008a, 0x00000088, 0x80008009, 0x8000000a, 0x8000808b, 0x0000008b, 0x00008089, 0x00008003,
+ 0x00008002, 0x00000080, 0x0000800a, 0x8000000a, 0x80008081, 0x00008080, 0x80000001, 0x80008008
+ ]);
+ const XMP1 = new Uint8Array(25);
+ const XMM1 = new Uint8Array(25);
+ const XYP = new Uint8Array(25);
+ const XP1 = new Uint8Array(25);
+ const XP2 = new Uint8Array(25);
+ let nx, ny;
+ for (let n = 0; n < 25; n++) {
+ nx = n % 5;
+ ny = Math.floor(n / 5);
+ XMP1[n] = (nx + 1) % 5;
+ XMM1[n] = (nx + 4) % 5;
+ XYP[n] = ny * 5 + (2 * nx + 3 * ny) % 5;
+ XP1[n] = ((nx + 1) % 5) * 5 + ny;
+ XP2[n] = ((nx + 2) % 5) * 5 + ny;
+ }
+ class Keccak800 {
+ constructor() {
+ this.state = new Uint32Array(25);
+ this.temp = new Uint32Array(25);
+ this.theta = new Uint32Array(5);
+ this.padblock = new Uint32Array(16);
+ this.reset();
+ }
+ reset() {
+ this.state.fill(0);
+ this.temp.fill(0);
+ this.theta.fill(0);
+ this.data = new Uint32Array(0);
+ this.pointer = 0;
+ this.padblock.fill(0);
+ this.padsigbytes = 0;
+ }
+ keccakF(state) {
+ let temp = this.temp, theta = this.theta;
+ let off, tmp;
+ for (let round = 0; round < 22; round++) {
+ theta[0] = state[0] ^ state[1] ^ state[2] ^ state[3] ^ state[4];
+ theta[1] = state[5] ^ state[6] ^ state[7] ^ state[8] ^ state[9];
+ theta[2] = state[10] ^ state[11] ^ state[12] ^ state[13] ^ state[14];
+ theta[3] = state[15] ^ state[16] ^ state[17] ^ state[18] ^ state[19];
+ theta[4] = state[20] ^ state[21] ^ state[22] ^ state[23] ^ state[24];
+ for (let i = 0; i < 25; i++) {
+ tmp = theta[XMP1[i]];
+ state[i] ^= theta[XMM1[i]] ^ (tmp << 1 | tmp >>> 31);
+ off = RHOoffsets[i];
+ tmp = state[i];
+ temp[XYP[i]] = tmp << off | tmp >>> (32 - off);
+ }
+ for (let i = 0; i < 25; i++)
+ state[i] = temp[i] ^ (~temp[XP1[i]] & temp[XP2[i]]);
+ state[0] ^= RCs[round];
+ }
+ }
+ process(flush = false) {
+ let blocks = (this.data.length - this.pointer) / 16;
+ for (let b = 0; b < blocks; b++) {
+ for (let w = 0; w < 16; w++)
+ this.state[w] ^= this.data[this.pointer + w];
+ this.keccakF(this.state);
+ this.pointer += 16;
+ }
+ if (flush) {
+ for (let w = 0; w < 16; w++)
+ this.state[w] ^= this.padblock[w];
+ this.keccakF(this.state);
+ }
+ }
+ append(data) {
+ data = typeof data == "string" ? new Uluru.enc.Utf8().encode(data) : data;
+ let padblock = this.padblock;
+ let padsigbytes = this.padsigbytes;
+ if (data.byteLength + padsigbytes < 64) {
+ padblock = new Uint8Array(padblock.buffer);
+ padblock.set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), padsigbytes);
+ padblock[padblock.length - 1] = 0x80;
+ padblock[data.byteLength + padsigbytes] ^= 0x06;
+ this.padblock = new Uint32Array(padblock.buffer);
+ this.padsigbytes += data.byteLength;
+ }
+ else {
+ let newlen = (padsigbytes + data.byteLength) >> 6 << 6;
+ let overflow = (padsigbytes + data.byteLength) % 64;
+ this.data = this.data.byteLength > newlen ? new Uint8Array(this.data.buffer, 0, newlen) : new Uint8Array(newlen);
+ this.data.set(new Uint8Array(padblock.buffer, 0, padsigbytes));
+ this.data.set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength - overflow), padsigbytes);
+ this.data = new Uint32Array(this.data.buffer, 0, newlen >> 2);
+ padblock.fill(0);
+ this.padsigbytes = 0;
+ this.append(new Uint8Array(data.buffer, data.byteOffset + data.byteLength - overflow, overflow));
+ }
+ }
+ update(data) {
+ this.append(data);
+ this.process(false);
+ return this;
+ }
+ finalize(outputbytes = 32) {
+ this.process(true);
+ let len = Math.ceil(outputbytes / 64) * 16;
+ let result = new Uint32Array(len);
+ for (let i = 0; i < len; i += 16) {
+ result.set(new Uint32Array(this.state.buffer, 0, 16), i);
+ this.keccakF(this.state);
+ }
+ this.data = new Uint32Array(0);
+ this.pointer = 0;
+ this.padblock.fill(0);
+ this.padsigbytes = 0;
+ return {
+ toString(encoder = new Uluru.enc.Hex) {
+ return encoder.decode(this.hash);
+ },
+ hash: new Uint8Array(result.buffer, 0, outputbytes)
+ };
+ }
+ }
+ Uluru.Keccak800 = Keccak800;
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ const SEEDlen = 12;
+ const HASHlen = 16;
+ const HDRlen = SEEDlen + HASHlen + 4;
+ function merge(...bufferviews) {
+ let len = 0;
+ for (let i = 0; i < bufferviews.length; i++)
+ len += bufferviews[i].byteLength;
+ let result = new Uint8Array(len);
+ let pointer = 0;
+ for (let i = 0; i < bufferviews.length; i++) {
+ result.set(new Uint8Array(bufferviews[i].buffer, bufferviews[i].byteOffset, bufferviews[i].byteLength), pointer);
+ pointer += bufferviews[i].byteLength;
+ }
+ return result;
+ }
+ class OAEP {
+ pad(data, len = 128) {
+ if (len <= HDRlen)
+ throw "OAEP message length too small";
+ let padxdata = new Uint8Array(len - HDRlen);
+ padxdata.set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
+ let datalen = new Uint32Array([data.byteLength]);
+ let seed = new Uluru.Random().fill(new Uint8Array(SEEDlen));
+ let hash = new Uluru.Keccak800().update(padxdata).update(datalen).update(seed).finalize(HASHlen).hash;
+ let header = merge(datalen, seed, hash);
+ let mask = new Uluru.Keccak800().update(header).finalize(len - HDRlen).hash;
+ for (let m0 = 0; m0 < len - HDRlen; m0++)
+ padxdata[m0] ^= mask[m0];
+ mask = new Uluru.Keccak800().update(padxdata).finalize(HDRlen).hash;
+ for (let m1 = 0; m1 < HDRlen; m1++)
+ header[m1] ^= mask[m1];
+ return {
+ data: merge(header, padxdata)
+ };
+ }
+ unpad(data) {
+ let len = data.byteLength;
+ let header = new Uint8Array(data.buffer, 0, HDRlen).slice();
+ let padxdata = new Uint8Array(data.buffer, HDRlen).slice();
+ let mask = new Uluru.Keccak800().update(padxdata).finalize(HDRlen).hash;
+ for (let m1 = 0; m1 < HDRlen; m1++)
+ header[m1] ^= mask[m1];
+ mask = new Uluru.Keccak800().update(header).finalize(len - HDRlen).hash;
+ for (let m0 = 0; m0 < len - HDRlen; m0++)
+ padxdata[m0] ^= mask[m0];
+ let datalen = new Uint32Array(header.buffer, 0, 1);
+ let seed = new Uint8Array(header.buffer, 4, SEEDlen);
+ let hash = new Uint8Array(header.buffer, 4 + SEEDlen, HASHlen);
+ let rehash = new Uluru.Keccak800().update(padxdata).update(datalen).update(seed).finalize(HASHlen).hash;
+ if (rehash.join(",") != hash.join(","))
+ throw "OAEP invalid padding hash";
+ return {
+ data: new Uint8Array(padxdata.buffer, 0, datalen[0])
+ };
+ }
+ }
+ OAEP.seedlen = SEEDlen;
+ OAEP.hashlen = HASHlen;
+ OAEP.hdrlen = HDRlen;
+ Uluru.OAEP = OAEP;
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ class Pbkdf {
+ constructor(outputbytes = 32, iterations = 1000) {
+ this.outputbytes = outputbytes;
+ this.iterations = iterations;
+ }
+ compute(password, salt = new Uint32Array()) {
+ let result = new Uint8Array(this.outputbytes);
+ let block;
+ let hasher = new Uluru.Keccak800();
+ for (let t = 0, lent = result.length; t < lent; t += 64) {
+ hasher.reset();
+ hasher.update(salt).update(new Uint32Array([t >>> 6]));
+ for (let r = 0; r < this.iterations; r++) {
+ block = hasher.update(password).finalize(64).hash;
+ for (let b = 0, l = result.length; (b < 64) && (t + b < l); b++)
+ result[t + b] ^= block[b];
+ }
+ }
+ return { result };
+ }
+ }
+ Uluru.Pbkdf = Pbkdf;
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ const CAPACITY = 16300;
+ let Pool = new Uint32Array(CAPACITY);
+ let Pointer = 0;
+ function reset() {
+ Pointer = 0;
+ if (Random.secure)
+ crypto.getRandomValues(Pool);
+ else
+ for (let i = 0, l = CAPACITY; i < l; i++)
+ Pool[i] = Math.floor(Math.random() * 0x100000000);
+ }
+ class Random {
+ static get secure() {
+ return typeof crypto == "object" && typeof crypto["getRandomValues"] == "function";
+ }
+ word() {
+ if (Pointer >= CAPACITY)
+ reset();
+ return Pool[Pointer++];
+ }
+ fill(arr) {
+ if (ArrayBuffer.isView(arr)) {
+ reset();
+ let wrds = new Uint32Array(arr.buffer, arr.byteOffset, arr.byteLength >> 2);
+ for (let i = 0, l = wrds.length; i < l; i += CAPACITY) {
+ wrds.set(new Uint32Array(Pool.buffer, 0, Math.min((l - i), CAPACITY)), i);
+ reset();
+ }
+ let roundedbytes = arr.byteLength >> 2 << 2;
+ let bytes = new Uint8Array(arr.buffer, roundedbytes, arr.byteLength - roundedbytes);
+ for (let i = 0, l = bytes.length; i < l; i++)
+ bytes[i] = this.word();
+ }
+ else
+ for (let i = 0, l = arr.length; i < l; i++)
+ arr[i] = this.word();
+ return arr;
+ }
+ }
+ Random.capacity = CAPACITY;
+ Uluru.Random = Random;
+ reset();
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ const Bi = BigInt;
+ const n1 = Bi(1);
+ const n0 = Bi(0);
+ const mask = bitlen => (n1 << Bi(bitlen)) - n1;
+ function bitLen(x) {
+ let bits = 0;
+ let bits32 = Bi(0x100000000);
+ let stillbigger = true;
+ while (x) {
+ stillbigger = stillbigger && x > bits32;
+ bits += stillbigger ? 32 : 1;
+ x >>= Bi(stillbigger ? 32 : 1);
+ }
+ return bits;
+ }
+ function modPow(base, exponent, modulus) {
+ let result = n1;
+ while (exponent) {
+ if ((exponent & n1) == n1)
+ result = (result * base) % modulus;
+ exponent >>= n1;
+ base = (base * base) % modulus;
+ }
+ return result;
+ }
+ function randomBi(bitlength) {
+ let result = n0;
+ let rand = new Uluru.Random();
+ for (let w = 0; w * 32 < bitlength; w++)
+ result = (result << Bi(32)) | Bi(rand.word());
+ return result & mask(bitlength);
+ }
+ let smallprimes = [Bi(2)];
+ small: for (let n = 3; n < 1024; n += 2) {
+ for (let co = 1; co < smallprimes.length; co++)
+ if (Bi(n) % smallprimes[co] === n0)
+ continue small;
+ smallprimes.push(Bi(n));
+ }
+ function fermat(prime, iterations = 6) {
+ let randsize = Math.min(16, bitLen(prime) - 1);
+ let base;
+ while (iterations--) {
+ base = randomBi(randsize) + Bi(5);
+ if (modPow(base, prime - n1, prime) != n1)
+ return false;
+ }
+ return true;
+ }
+ function millerRabin(prime, iterations = 6) {
+ let s = n0, d = prime - n1;
+ let randsize = Math.min(16, bitLen(prime) - 1);
+ while (!((d & n1) != n1)) {
+ d >>= n1;
+ s++;
+ }
+ let a, x;
+ let cant1 = n1, cant2 = prime - n1;
+ iter: while (iterations--) {
+ a = randomBi(randsize) + Bi(5);
+ x = modPow(a, d, prime);
+ if (x == cant1 || x == cant2)
+ continue iter;
+ for (let i = n0, l = s - n1; i < l; i++) {
+ x = modPow(x, Bi(2), prime);
+ if (x == cant1)
+ return false;
+ if (x == cant2)
+ continue iter;
+ }
+ return false;
+ }
+ return true;
+ }
+ function isPrime(prime, iterations = 6) {
+ for (let i = 0, l = smallprimes.length; i < l; i++)
+ if (prime % smallprimes[i] == n0)
+ return prime == smallprimes[i];
+ return millerRabin(prime, iterations) && fermat(prime, iterations);
+ }
+ function prime(bitlength, iterations = 6, attempts = 100000) {
+ let candidate;
+ for (let i = 0; i < attempts; i++) {
+ candidate = randomBi(bitlength) | n1 | (n1 << Bi(bitlength - 1));
+ if (isPrime(candidate, iterations))
+ return candidate;
+ }
+ throw "Cannot find a prime";
+ }
+ function modInv(int, modulus) {
+ let mod0 = modulus;
+ let y = n0, x = n1;
+ let quot, temp;
+ while (int > 1) {
+ quot = int / modulus;
+ temp = modulus;
+ modulus = int % modulus;
+ int = temp;
+ temp = y;
+ y = x - quot * y;
+ x = temp;
+ }
+ return x < 0 ? x + mod0 : x;
+ }
+ function buffviewToBi(bufferview) {
+ return Bi("0x" + new Uluru.enc.Hex().decode(new Uint8Array(bufferview.buffer, bufferview.byteOffset || 0, bufferview.byteLength || 0)));
+ }
+ function biToBuffview(bigint) {
+ let stred = bigint.toString(16);
+ return new Uluru.enc.Hex().encode((stred.length % 2 == 1 ? "0" : "") + stred);
+ }
+ class RSAKey {
+ constructor(exponent, mod) {
+ this.E = Bi(exponent);
+ this.M = Bi(mod);
+ }
+ static fromBufferViews(bufferview1, bufferview2) {
+ return new this(buffviewToBi(bufferview1), buffviewToBi(bufferview2));
+ }
+ static fromString(str) {
+ let splitted = str.split("<")[1].split(">")[0].split("|");
+ return this.fromBufferViews(new Uluru.enc.Base64().encode(splitted[0]), new Uluru.enc.Base64().encode(splitted[1]));
+ }
+ toString() {
+ return "<" +
+ new Uluru.enc.Base64().decode(biToBuffview(this.E)) +
+ "|" +
+ new Uluru.enc.Base64().decode(biToBuffview(this.M)) +
+ ">";
+ }
+ process(data) {
+ let databi = buffviewToBi(data);
+ if (databi >= this.M)
+ throw "Data integer too large";
+ return biToBuffview(modPow(databi, this.E, this.M));
+ }
+ encrypt(data) {
+ data = typeof data == "string" ? new Uluru.enc.Utf8().encode(data) : data;
+ let msglen = (bitLen(this.M) >> 3) - 2 - Uluru.OAEP.hdrlen;
+ if (data.byteLength > msglen)
+ throw "Message too long";
+ return {
+ data: this.process(new Uluru.OAEP().pad(data, msglen).data)
+ };
+ }
+ decrypt(data) {
+ return {
+ data: new Uluru.OAEP().unpad(this.process(data)).data
+ };
+ }
+ sign(data) {
+ data = typeof data == "string" ? new Uluru.enc.Utf8().encode(data) : data;
+ let hash = new Uluru.Keccak800().update(data).finalize(64).hash;
+ return {
+ data,
+ signature: this.encrypt(hash).data
+ };
+ }
+ verify(data, signature) {
+ try {
+ data = typeof data == "string" ? new Uluru.enc.Utf8().encode(data) : data;
+ let hash = new Uluru.Keccak800().update(data).finalize(64).hash;
+ let authcode = this.decrypt(signature).data;
+ return hash.join(",") == authcode.join(",");
+ }
+ catch (e) {
+ return false;
+ }
+ }
+ }
+ Uluru.RSAKey = RSAKey;
+ const PUBEXP = Bi(0x101);
+ const PUBLICprefix = ["\n==BEGIN ULURU PUBLIC KEY==\n", "\n==END ULURU PUBLIC KEY==\n"];
+ const PRIVATEprefix = ["\n==BEGIN ULURU PRIVATE KEY==\n", "\n==END ULURU PRIVATE KEY==\n"];
+ class RSAKeyPair {
+ constructor(publickey, privatekey) {
+ this.public = publickey;
+ this.private = privatekey;
+ }
+ static fromString(str) {
+ return new this(RSAKey.fromString(str.split(PUBLICprefix[0])[1].split(PUBLICprefix[1])[0]), RSAKey.fromString(str.split(PRIVATEprefix[0])[1].split(PRIVATEprefix[1])[0]));
+ }
+ static generate(bitlength = 3072) {
+ if (!bitlength)
+ return;
+ bitlength >>= 1;
+ let E = PUBEXP;
+ let prime1 = prime(bitlength), prime2 = prime(bitlength);
+ let N = prime1 * prime2;
+ let phi = (prime1 - n1) * (prime2 - n1);
+ let D = modInv(E, phi);
+ return new this(new RSAKey(E, N), new RSAKey(D, N));
+ }
+ toString() {
+ return PUBLICprefix[0] + this.public.toString() + PUBLICprefix[1] + "\n!\n" +
+ PRIVATEprefix[0] + this.private.toString() + PRIVATEprefix[1];
+ }
+ }
+ RSAKeyPair.pubexp = PUBEXP;
+ RSAKeyPair.publicprefix = PUBLICprefix;
+ RSAKeyPair.privateprefix = PRIVATEprefix;
+ Uluru.RSAKeyPair = RSAKeyPair;
+ const MODPgroup = buffviewToBi(new Uluru.enc.Base64().encode("///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxObIlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjftawv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXTmmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhghfDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq5RXSJhiY+gUQFXKOWoqqxC2tMxcNBFB6M6hVIavfHLpk7PuFBFjb7wqK6nFXXQYMfbOXD4Wm4eTHq/WujNsJM9cejJTgSiVhnc7j0iYa0u5r8S/6BtmKCGTYdgJzPshqZFIfKxgXeyAMu+EXV3phXWx3CYjAutlG4gjiT6B05asxQ9tb/OD9EI5LgtEgqSEIARpyPBKnh+bXiHGaEL26WyaZwycYavTiPBqUaDS2FQvaJYPpyirUTOjbu8LbBN6O+S6O/BQfvsqmKHxZR05rwF2ZspZPoJDDoiM7oYZRW+ftH2EpcM7i16+4G912IXBIHNAGkSfVsFqpk7TqmI2P3cGG/7fckKbAj030Nck0BjGZ//////////8="));
+ const GENERATOR = Bi(2);
+ class DiffieHellman {
+ constructor(ebits = 384) {
+ this.E = randomBi(ebits) | (Bi(1) << Bi(ebits - 1));
+ }
+ send() {
+ return biToBuffview(modPow(GENERATOR, this.E, MODPgroup));
+ }
+ receive(data) {
+ this.secret = modPow(buffviewToBi(data), this.E, MODPgroup);
+ }
+ finalize(length = 32) {
+ if (typeof this.secret != "bigint")
+ throw "Key exchange cannot finalize without receiving";
+ return new Uluru.Pbkdf(length, 2).compute(biToBuffview(this.secret));
+ }
+ }
+ DiffieHellman.generator = GENERATOR;
+ DiffieHellman.group = MODPgroup;
+ Uluru.DiffieHellman = DiffieHellman;
+})(Uluru || (Uluru = {}));
+var Uluru;
+(function (Uluru) {
+ let enc;
+ (function (enc) {
+ class Utf8 {
+ encode(str) {
+ if (typeof TextEncoder == "function")
+ return new TextEncoder().encode(str);
+ let bytes = new Uint8Array(str.length * 4);
+ let pos = 0;
+ let code;
+ for (let i = 0, l = str.length; i < l; i++) {
+ code = str.codePointAt(i);
+ if (code > 0x1000)
+ i++;
+ if (code <= 0x7f)
+ bytes[pos++] = code;
+ else if (code <= 0x7ff) {
+ bytes[pos++] = 0xc0 | ((code >>> 6) & 0x1f);
+ bytes[pos++] = 0x80 | (code & 0x3f);
+ }
+ else if (code <= 0xffff) {
+ bytes[pos++] = 0xe0 | ((code >>> 12) & 0x0f);
+ bytes[pos++] = 0x80 | ((code >>> 6) & 0x3f);
+ bytes[pos++] = 0x80 | (code & 0x3f);
+ }
+ else {
+ bytes[pos++] = 0xf0 | ((code >>> 18) & 0x07);
+ bytes[pos++] = 0x80 | ((code >>> 12) & 0x3f);
+ bytes[pos++] = 0x80 | ((code >>> 6) & 0x3f);
+ bytes[pos++] = 0x80 | (code & 0x3f);
+ }
+ }
+ }
+ decode(bytes) {
+ let bytearr = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ if (typeof TextDecoder == "function")
+ return new TextDecoder().decode(bytearr);
+ let str = [];
+ let ucpoint;
+ for (let i = 0, l = bytearr.length; i < l;) {
+ if (bytearr[i] < 0x80)
+ str.push(String.fromCharCode(bytearr[i++]));
+ else if (bytearr[i] >= 0xc0 && bytearr[i] < 0xe0)
+ str.push(String.fromCharCode(((bytearr[i++] & 0x1f) << 6) | (bytearr[i++] & 0x3f)));
+ else if (bytearr[i] >= 0xe0 && bytearr[i] < 0xf0)
+ str.push(String.fromCharCode(((bytearr[i++] & 0x0f) << 12) | ((bytearr[i++] & 0x3f) << 6) | (bytearr[i++] & 0x3f)));
+ else if (bytearr[i] >= 0xf0 && bytearr[i] < 0xf7) {
+ ucpoint = ((bytearr[i++] & 0x07) << 18) | ((bytearr[i++] & 0x3f) << 12) | ((bytearr[i++] & 0x3f) << 6) | (bytearr[i++] & 0x3f);
+ str.push(String.fromCodePoint(ucpoint));
+ }
+ else
+ i++;
+ }
+ return str.join("");
+ }
+ }
+ enc.Utf8 = Utf8;
+ })(enc = Uluru.enc || (Uluru.enc = {}));
+})(Uluru || (Uluru = {}));
+if (typeof define === "function" && define.amd)
+ define("Uluru", [], () => Uluru);
+if (typeof module != "undefined")
+ module.exports = Uluru;