Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name HashBrown 2
- // @namespace http://tampermonkey.net/
- // @version 2026-05-28
- // @description Cryptographically secure identification
- // @author gimmickCellar
- // @match https://*.ourworldoftext.com/*
- // @match https://twot.fbin.duckdns.org/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=ourworldoftext.com
- // @grant none
- // ==/UserScript==
- (function() {
- 'use strict';
- const P256 = {
- p: 2n**256n - 2n**224n + 2n**192n + 2n**96n - 1n,
- a: -3n,
- b: 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604bn,
- n: 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n,
- gx: 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296n,
- gy: 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5n
- };
- const mod = (a, b) => { const r = a % b; return r < 0n ? r + b : r; };
- const modInv = (a, m) => {
- a = mod(a, m);
- if (a === 0n) return 0n;
- let [t, newT, r, newR] = [0n, 1n, m, a];
- while (newR !== 0n) {
- let q = r / newR;
- [t, newT] = [newT, t - q * newT];
- [r, newR] = [newR, r - q * newR];
- }
- return mod(t, m);
- };
- const pointAdd = (P, Q) => {
- if (!P) return Q; if (!Q) return P;
- let lam;
- if (P.x === Q.x && P.y === Q.y) {
- lam = mod((3n * P.x**2n + P256.a) * modInv(2n * P.y, P256.p), P256.p);
- } else {
- if (P.x === Q.x) return null;
- lam = mod((Q.y - P.y) * modInv(Q.x - P.x, P256.p), P256.p);
- }
- const x = mod(lam**2n - P.x - Q.x, P256.p);
- const y = mod(lam * (P.x - x) - P.y, P256.p);
- return { x, y };
- };
- const pointMul = (P, k) => {
- let res = null, base = P;
- while (k > 0n) {
- if (k & 1n) res = pointAdd(res, base);
- base = pointAdd(base, base);
- k >>= 1n;
- }
- return res;
- };
- function sha256(s) {
- const chrsz = 8;
- function safe_add(x, y) {
- const lsw = (x & 0xFFFF) + (y & 0xFFFF);
- const msw = (x >> 16) + (y >> 16) + (lsw >> 16);
- return (msw << 16) | (lsw & 0xFFFF);
- }
- const S = (X, n) => (X >>> n) | (X << (32 - n));
- const R = (X, n) => (X >>> n);
- const Ch = (x, y, z) => ((x & y) ^ ((~x) & z));
- const Maj = (x, y, z) => ((x & y) ^ (x & z) ^ (y & z));
- const Sigma0256 = (x) => (S(x, 2) ^ S(x, 13) ^ S(x, 22));
- const Sigma1256 = (x) => (S(x, 6) ^ S(x, 11) ^ S(x, 25));
- const Gamma0256 = (x) => (S(x, 7) ^ S(x, 18) ^ R(x, 3));
- const Gamma1256 = (x) => (S(x, 17) ^ S(x, 19) ^ R(x, 10));
- const K = [0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2];
- const HASH = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19];
- const utf8 = unescape(encodeURIComponent(s));
- const m = [];
- for (let i = 0; i < utf8.length * chrsz; i += chrsz) m[i >> 5] |= (utf8.charCodeAt(i / chrsz) & 0xFF) << (24 - i % 32);
- const l = utf8.length * chrsz;
- const W = new Array(64);
- m[l >> 5] |= 0x80 << (24 - l % 32);
- m[((l + 64 >> 9) << 4) + 15] = l;
- for (let i = 0; i < m.length; i += 16) {
- let [a, b, c, d, e, f, g, h] = HASH;
- for (let j = 0; j < 64; j++) {
- if (j < 16) W[j] = m[i + j] || 0;
- else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
- let T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
- let T2 = safe_add(Sigma0256(a), Maj(a, b, c));
- h = g; g = f; f = e; e = safe_add(d, T1); d = c; c = b; b = a; a = safe_add(T1, T2);
- }
- HASH[0] = safe_add(a, HASH[0]); HASH[1] = safe_add(b, HASH[1]); HASH[2] = safe_add(c, HASH[2]); HASH[3] = safe_add(d, HASH[3]);
- HASH[4] = safe_add(e, HASH[4]); HASH[5] = safe_add(f, HASH[5]); HASH[6] = safe_add(g, HASH[6]); HASH[7] = safe_add(h, HASH[7]);
- }
- let hex = "";
- for (let i = 0; i < HASH.length * 4; i++) {
- hex += ((HASH[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF).toString(16) + ((HASH[i >> 2] >> ((3 - i % 4) * 8)) & 0xF).toString(16);
- }
- return BigInt('0x' + hex);
- }
- const bufferToBase64 = (buf) => { let bin = ''; for (let i = 0; i < buf.byteLength; i++) bin += String.fromCharCode(buf[i]); return btoa(bin); };
- const base64ToBuffer = (b64) => { const bin = atob(b64); const buf = new Uint8Array(bin.length); for(let i=0; i<bin.length; i++) buf[i] = bin.charCodeAt(i); return buf; };
- const bigToBuf = (bn) => { let hex = bn.toString(16).padStart(64, '0'); const arr = new Uint8Array(32); for (let i = 0; i < 32; i++) arr[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16); return arr; };
- const bufToHex = (buf) => { return Array.from(buf).map(b => b.toString(16).padStart(2, '0')).join(''); };
- function verifyMessage(msg, sigBase64, pubBase64) {
- try {
- const z = sha256(msg);
- const sigBuf = base64ToBuffer(sigBase64);
- const pubBuf = base64ToBuffer(pubBase64);
- if (sigBuf.length !== 64 || pubBuf.length !== 65 || pubBuf[0] !== 4) return false;
- const r = BigInt('0x' + bufToHex(sigBuf.subarray(0, 32)));
- const s = BigInt('0x' + bufToHex(sigBuf.subarray(32, 64)));
- if (r < 1n || r >= P256.n || s < 1n || s >= P256.n) return false;
- const px = BigInt('0x' + bufToHex(pubBuf.subarray(1, 33)));
- const py = BigInt('0x' + bufToHex(pubBuf.subarray(33, 65)));
- const w = modInv(s, P256.n);
- const u1 = mod(z * w, P256.n);
- const u2 = mod(r * w, P256.n);
- const pt1 = pointMul({ x: P256.gx, y: P256.gy }, u1);
- const pt2 = pointMul({ x: px, y: py }, u2);
- const pt = pointAdd(pt1, pt2);
- return pt && mod(pt.x, P256.n) === r;
- } catch(e) { return false; }
- }
- let privKey, pubKeyBase64, activePhraseData = null;
- let linkType = localStorage.getItem("HashBrown_LinkType") || "note";
- let verifierUrl = localStorage.getItem("HashBrown_VerifierURL") || "https://gimmickcellar.nekoweb.org/verifyhash.html";
- let signingEnabled = localStorage.getItem("HashBrown_Signing") !== "false";
- let autoVerifyEnabled = localStorage.getItem("HashBrown_AutoVerify") !== "false";
- function initCrypto() {
- let stored = localStorage.getItem('HashBrown_PrivV4');
- if (!stored) {
- const tmp = new Uint8Array(32); crypto.getRandomValues(tmp);
- stored = Array.from(tmp).map(b => b.toString(16).padStart(2, '0')).join('');
- localStorage.setItem('HashBrown_PrivV4', stored);
- }
- privKey = BigInt('0x' + stored);
- const pt = pointMul({ x: P256.gx, y: P256.gy }, privKey);
- const rawPub = new Uint8Array(65);
- rawPub[0] = 4;
- rawPub.set(bigToBuf(pt.x), 1);
- rawPub.set(bigToBuf(pt.y), 33);
- pubKeyBase64 = bufferToBase64(rawPub);
- }
- function signMessage(msg) {
- const z = sha256(msg);
- let r = 0n, s = 0n;
- while (r === 0n || s === 0n) {
- const kArr = new Uint8Array(32); crypto.getRandomValues(kArr);
- const kStr = Array.from(kArr).map(b => b.toString(16).padStart(2, '0')).join('');
- const k = mod(BigInt('0x' + kStr), P256.n);
- if (k === 0n) continue;
- const kp = pointMul({ x: P256.gx, y: P256.gy }, k);
- r = mod(kp.x, P256.n);
- s = mod(modInv(k, P256.n) * (z + r * privKey), P256.n);
- }
- const sigArr = new Uint8Array(64);
- sigArr.set(bigToBuf(r), 0);
- sigArr.set(bigToBuf(s), 32);
- return bufferToBase64(sigArr);
- }
- function buildUI() {
- const gui = document.getElementById("west_gui");
- if (!gui) return;
- const hb = document.createElement("div");
- hb.style = "background:#E5E5FF;padding:4px;margin-bottom:3px;font-family:Verdana;font-size:12px;";
- hb.innerHTML = `
- <center><b>HashBrown 2</b></center>
- <label style="display:block;margin-top:4px;"><input type="checkbox" id="hb_opt_signing" ${signingEnabled?"checked":""}> Signing</label>
- <label style="display:block;margin-bottom:4px;"><input type="checkbox" id="hb_opt_verify" ${autoVerifyEnabled?"checked":""}> Auto-Verify</label>
- <label><input type="radio" name="hbt" id="hbo_n"> Note</label>
- <label><input type="radio" name="hbt" id="hbo_j"> JS</label>
- <label><input type="radio" name="hbt" id="hbo_u"> URL</label>
- <input type="text" id="hb_url" placeholder="Verifier URL" style="width:100%;font-size:10px;display:none;margin-top:2px;">
- <hr>
- <input type="text" id="hb_in" placeholder="Phrase..." style="width:100%;font-size:11px;">
- <button id="hb_btn" style="width:100%;margin-top:2px;cursor:pointer;">Sign & Write</button>
- `;
- gui.appendChild(hb);
- const uBox = document.getElementById("hb_url");
- uBox.value = verifierUrl;
- const setMode = (m) => { linkType = m; localStorage.setItem("HashBrown_LinkType", m); uBox.style.display = (m==='url'?'block':'none'); };
- if (linkType === "js") document.getElementById("hbo_j").checked = true;
- else if (linkType === "url") { document.getElementById("hbo_u").checked = true; uBox.style.display="block"; }
- else document.getElementById("hbo_n").checked = true;
- document.getElementById("hbo_n").onchange = () => setMode("note");
- document.getElementById("hbo_j").onchange = () => setMode("js");
- document.getElementById("hbo_u").onchange = () => setMode("url");
- uBox.oninput = (e) => { verifierUrl = e.target.value; localStorage.setItem("HashBrown_VerifierURL", verifierUrl); };
- document.getElementById("hb_opt_signing").onchange = (e) => { signingEnabled = e.target.checked; localStorage.setItem("HashBrown_Signing", signingEnabled); };
- document.getElementById("hb_opt_verify").onchange = (e) => { autoVerifyEnabled = e.target.checked; localStorage.setItem("HashBrown_AutoVerify", autoVerifyEnabled); if (autoVerifyEnabled) scanAllTiles(); };
- document.getElementById("hb_btn").onclick = () => {
- const val = document.getElementById("hb_in").value;
- if (!val || !window.cursorCoords || !signingEnabled) return;
- const startX = (cursorCoords[0] * 16) + cursorCoords[2];
- const startY = (cursorCoords[1] * 8) + cursorCoords[3];
- const sig = signMessage(`${state.worldModel.name || "Main"},${startX},${startY},${val},${state.userModel.username || "Anonymous"}`);
- activePhraseData = { text: val, startX, startY, sig };
- for (let i = 0; i < val.length; i++) OWOT.typeChar(val[i], false);
- activePhraseData = null;
- document.getElementById("hb_in").value = "";
- };
- }
- initCrypto();
- if (document.readyState === "complete") buildUI(); else window.addEventListener("load", buildUI);
- const cache = {};
- OWOT.on("write", (data) => {
- if (!signingEnabled || data.char === "\x08" || data.char === " " || data.char === "\xa0") return;
- const id = nextObjId - 1;
- const user = state.userModel.username || "Anonymous";
- const world = state.worldModel.name || "Main";
- if (activePhraseData) {
- cache[id] = { type: "phrase", tx: data.tileX, ty: data.tileY, cx: data.charX, cy: data.charY, user, ...activePhraseData };
- } else {
- const x = (data.tileX * 16) + data.charX, y = (data.tileY * 8) + data.charY;
- let b = data.bold != null ? data.bold : (document.getElementById("text_deco_b")?.style.backgroundColor !== "");
- let i = data.italic != null ? data.italic : (document.getElementById("text_deco_i")?.style.backgroundColor !== "");
- let u = data.underline != null ? data.underline : (document.getElementById("text_deco_u")?.style.backgroundColor !== "");
- let s = data.strikethrough != null ? data.strikethrough : (document.getElementById("text_deco_s")?.style.backgroundColor !== "");
- let c = data.char.codePointAt(0) || 0;
- let code = data.char.charCodeAt(data.char.length - 1) - 0x20F0;
- if (code > 0 && code <= 16) {
- b = b || ((code >> 3) & 1); i = i || ((code >> 2) & 1); u = u || ((code >> 1) & 1); s = s || (code & 1);
- }
- if (c === 32) { b = false; i = false; }
- const d = (b ? 8 : 0) | (i ? 4 : 0) | (u ? 2 : 0) | (s ? 1 : 0);
- cache[id] = { type: "single", tx: data.tileX, ty: data.tileY, cx: data.charX, cy: data.charY, user, sig: signMessage(`${world},${x},${y},${c},${d},${user}`), c, d };
- }
- });
- OWOT.on("writeResponse", (data) => {
- if (!data.accepted) return;
- data.accepted.forEach(id => {
- const info = cache[id];
- if (!info) return;
- const world = state.worldModel.name || "Main";
- const pub = encodeURIComponent(pubKeyBase64).replace(/\+/g, '%2B');
- const sig = encodeURIComponent(info.sig).replace(/\+/g, '%2B');
- let link = "";
- if (info.type === "phrase") {
- if (linkType === "js") link = `javascript:/*\nUser: ${info.user}\nPhrase: ${info.text}\nStartX: ${info.startX}\nStartY: ${info.startY}\nSig: ${info.sig}\nPubKey: ${pubKeyBase64}\n*/`;
- else if (linkType === "url") link = verifierUrl + `?w=${encodeURIComponent(world)}&x=${info.startX}&y=${info.startY}&t=${encodeURIComponent(info.text)}&u=${encodeURIComponent(info.user)}&sig=${sig}&pub=${pub}`;
- else link = `note: User: ${info.user} | Phrase: ${info.text} | Sig: ${info.sig}`;
- } else {
- const gx = (info.tx*16)+info.cx, gy = (info.ty*8)+info.cy;
- if (linkType === "js") link = `javascript:/*\nUser: ${info.user}\nSig: ${info.sig}\nPubKey: ${pubKeyBase64}\n*/`;
- else if (linkType === "url") link = verifierUrl + `?w=${encodeURIComponent(world)}&x=${gx}&y=${gy}&c=${info.c}&d=${info.d}&u=${encodeURIComponent(info.user)}&sig=${sig}&pub=${pub}`;
- else link = `note: User: ${info.user} | Sig: ${info.sig} | PubKey: ${pubKeyBase64}`;
- }
- network.link({ tileX: info.tx, tileY: info.ty, charX: info.cx, charY: info.cy }, "url", { url: link });
- delete cache[id];
- });
- });
- const verifyQueue = [];
- let isBusy = false;
- function parseHash(url) {
- try {
- if (url.includes("sig=") && url.includes("pub=")) {
- const p = new URLSearchParams(url.split('?')[1]);
- return { sig: p.get("sig"), pub: p.get("pub"), w: p.get("w"), x: p.get("x"), y: p.get("y"), c: p.get("c"), d: p.get("d"), u: p.get("u"), t: p.get("t"), type: p.get("t")?"phrase":"single" };
- } else if (url.startsWith("javascript:")) {
- let dUrl = url;
- try { dUrl = decodeURIComponent(url); } catch(e) {}
- const sig = dUrl.match(/Sig:\s*([A-Za-z0-9+/=]+)/), pub = dUrl.match(/PubKey:\s*([A-Za-z0-9+/=]+)/);
- if (!sig || !pub) return null;
- return {
- type: dUrl.includes("Phrase:") ? "phrase" : "single",
- u: (dUrl.match(/User:\s*([^\n\r]+)/)||[,""])[1].trim(),
- t: (dUrl.match(/Phrase:\s*([^\n\r]+)/)||[,""])[1]?.trim() || null,
- x: (dUrl.match(/StartX:\s*(-?\d+)/)||[,""])[1] || null,
- y: (dUrl.match(/StartY:\s*(-?\d+)/)||[,""])[1] || null,
- sig: sig[1], pub: pub[1]
- };
- }
- } catch(e) {} return null;
- }
- async function processVerify(item) {
- const { tx, ty, cx, cy, link } = item;
- const p = parseHash(link.url);
- if (!p) return;
- const tile = OWOT.tile.get(tx, ty);
- if (!tile) return;
- const world = state.worldModel.name || "Main", gridX = (tx * 16) + cx, gridY = (ty * 8) + cy, char = tile.content[cy * 16 + cx];
- let msg = "", contextMatch = false;
- if (p.type === "phrase") {
- msg = `${p.w||world},${p.x},${p.y},${p.t},${p.u}`;
- contextMatch = (p.w||world) === world;
- } else {
- const gridC = char.codePointAt(0) || 0;
- let code = char.charCodeAt(char.length - 1) - 0x20F0;
- let gridD = (code > 0 && code <= 16) ? code : 0;
- if (gridC === 32) {
- let u = (gridD >> 1) & 1;
- let s = gridD & 1;
- gridD = (u ? 2 : 0) | (s ? 1 : 0);
- }
- const linkX = p.x != null ? parseInt(p.x) : gridX;
- const linkY = p.y != null ? parseInt(p.y) : gridY;
- const linkC = p.c != null ? parseInt(p.c) : gridC;
- const linkD = p.d != null ? parseInt(p.d) : gridD;
- msg = `${p.w||world},${linkX},${linkY},${linkC},${linkD},${p.u}`;
- contextMatch = (gridX === linkX && gridY === linkY && gridC === linkC && gridD === linkD && (p.w||world) === world);
- }
- const validMath = verifyMessage(msg, p.sig, p.pub);
- const finalColor = (validMath && contextMatch) ? 0xA0FF90 : 0xFF9090;
- if (!tile.properties.bgcolor) tile.properties.bgcolor = new Array(128).fill(-1);
- tile.properties.bgcolor[cy * 16 + cx] = finalColor;
- OWOT.setTileRedraw(tx, ty, true);
- }
- async function runQueue() {
- if (isBusy) return; isBusy = true;
- while (verifyQueue.length > 0 && autoVerifyEnabled) {
- await processVerify(verifyQueue.shift());
- await new Promise(r => setTimeout(r, 1));
- }
- isBusy = false;
- }
- function scanTile(tile, tx, ty) {
- if (!tile?.properties?.cell_props) return;
- const props = tile.properties.cell_props;
- for (let y in props) for (let x in props[y]) {
- const l = props[y][x].link;
- if (l?.url && (l.url.includes("sig=") || l.url.includes("Sig:"))) verifyQueue.push({ tx: parseInt(tx), ty: parseInt(ty), cx: parseInt(x), cy: parseInt(y), link: l });
- }
- runQueue();
- }
- const scanAllTiles = () => { for (let k in OWOT.tiles) { const [y, x] = k.split(","); scanTile(OWOT.tiles[k], x, y); } };
- OWOT.on("afterFetch", (d) => { if (autoVerifyEnabled) for (let k in d.tiles) { const [y, x] = k.split(","); scanTile(d.tiles[k], x, y); } });
- OWOT.on("afterTileUpdate", (d) => { if (autoVerifyEnabled) for (let k in d.tiles) { const [y, x] = k.split(","); scanTile(OWOT.tiles[k], x, y); } });
- })();
Add Comment
Please, Sign In to add comment