Use naive, slow implementation

This commit is contained in:
2026-04-15 16:38:46 +02:00
parent c0013e9319
commit 551b41666a
+179 -19
View File
@@ -1,21 +1,159 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>PoW Shield</title> <title>PoW Shield</title>
</head> </head>
<script> <script>
function getCookie(name) {
const cookies = document.cookie.split("; ");
for (const c of cookies) {
if (c.startsWith(name + "=")) {
return decodeURIComponent(c.substring(name.length + 1));
}
}
return null;
}
function b64ToBytes(b64) {
const bin = atob(b64);
const out = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
return out;
}
function bytesToB64(bytes) {
let s = "";
for (let i = 0; i < bytes.length; i++) {
s += String.fromCharCode(bytes[i]);
}
return btoa(s);
}
function checkDifficulty(hash, bits) {
const full = bits >> 3;
const rem = bits & 7;
for (let i = 0; i < full; i++) {
if (hash[i] !== 0) return false;
}
if (rem) {
const mask = 0xff << (8 - rem);
if ((hash[full] & mask) !== 0) return false;
}
return true;
}
async function sha256(buf) {
const h = await crypto.subtle.digest("SHA-256", buf);
return new Uint8Array(h);
}
async function minePow(cookieB64, {
timeoutMs = 5000,
startNonce = 0,
littleEndian = true
} = {}) {
const start = performance.now();
// decode challenge once
const challenge = b64ToBytes(cookieB64);
const challengeLen = challenge.length;
// layout:
// [ challenge | nonce | hash ]
const NONCE_OFFSET = challengeLen;
const HASH_OFFSET = challengeLen + 4;
const TOTAL_SIZE = challengeLen + 4 + 32;
// single working buffer (NO reallocation in loop)
const work = new Uint8Array(TOTAL_SIZE);
work.set(challenge, 0);
const view = new DataView(work.buffer);
// extract difficulty once
const difficulty = view.getUint32(12, littleEndian);
for (nonce = startNonce; ; nonce++) {
// timeout throttle (cheap check)
if ((nonce & 2047) === 0) {
if (performance.now() - start > timeoutMs) {
throw new Error("PoW timeout");
}
}
// Set nonce (this still copies the nonce)
view.setUint32(NONCE_OFFSET, nonce, littleEndian);
// Calculate sha256
const input = work.subarray(0, challengeLen + 4);
const hash = new Uint8Array(
await crypto.subtle.digest("SHA-256", input)
);
// Set hash (this still copies the hash)
work.set(hash, HASH_OFFSET);
if (checkDifficulty(hash, difficulty)) {
return {
nonce,
hash,
resultBytes: work,
resultB64: bytesToB64(work)
};
}
}
}
const powChallengeCookie = getCookie('pow-challenge');
console.log(powChallengeCookie);
minePow(powChallengeCookie)
.then(pow => {
console.log(pow);
document.cookie = `pow = ${pow.resultB64}`
})
.catch(e => console.log(e));
</script>
<!-- script>
const SHA256_MD_LENGTH = 64; const SHA256_MD_LENGTH = 64;
const POW_RANDOM_LENGTH = 64; const POW_RANDOM_LENGTH = 64;
class PowChallenge {
constructor(cookie) {
const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
const view = new DataView(powChallengeBytes.buffer);
let offset = 0;
this.version = view.getUint32(offset, true); offset += 4;
this.created = view.getUint32(offset, true); offset += 4;
this.ttl = view.getUint32(offset, true); offset += 4;
this.difficulty = view.getUint32(offset, true); offset += 4;
this.random = view.buffer.slice(offset, offset + POW_RANDOM_LENGTH); offset += POW_RANDOM_LENGTH;
this.digest = view.buffer.slice(offset);
}
getBytes() {
}
}
function base64ToBytes(b64) { function base64ToBytes(b64) {
return Uint8Array.from(atob(b64), c => c.charCodeAt(0)); return Uint8Array.from(atob(b64), c => c.charCodeAt(0));
} }
function deserializePowChallenge(b64) { function deserializePowChallenge(powChallengeBytes) {
const bytes = base64ToBytes(b64); const view = new DataView(powChallengeBytes.buffer);
const view = new DataView(bytes.buffer);
let offset = 0; let offset = 0;
@@ -53,10 +191,10 @@
return new Uint8Array(hashBuffer); return new Uint8Array(hashBuffer);
} }
function hasLeadingBytes(buf, numBits) { function hasLeadingBits(buf, numBits) {
const zeroBytes = numBits / 8; const zeroBytes = numBits / 8;
const remainingBits = numBits % 8; const remainingBits = numBits % 8;
const lut = Uint8Array([ const lut = new Uint8Array([
0b11111111, 0b01111111, 0b00111111, 0b00011111, 0b11111111, 0b01111111, 0b00111111, 0b00011111,
0b00001111, 0b00000111, 0b00000011, 0b00000001 0b00001111, 0b00000111, 0b00000011, 0b00000001
]); ]);
@@ -67,29 +205,51 @@
} }
} }
if (buf[zeroBytes + 1] ^ lut[remainingBytes]) { if (buf[zeroBytes + 1] ^ lut[remainingBits]) {
return false; return false;
} }
return true; return true;
} }
function pow(challenge) { function powSolve(powChallengeBytes, difficulty) {
const difficulty = deserializePowChallenge(challenge).difficulty const powBytes = new Uint8Array(powChallengeBytes.length
const pow = Uint32 + 4 // Nonce length
+ SHA256_MD_LENGTH);
powBytes.set(powChallengeBytes, 0);
let digest; for (
let nonce = 0; nonce = 0 >>> 0; // Force nonce to be uint32
(!hasLeadingBits(powBytes.slice(powChallengeBytes.length), difficulty)
&& nonce < 100)
|| nonce != 0;
nonce++
) {
powBytes[powChallengeBytes.length + 0] = nonce & 0xFF;
powBytes[powChallengeBytes.length + 1] = (nonce >>> 8) & 0xFF;
powBytes[powChallengeBytes.length + 2] = (nonce >>> 16) & 0xFF;
powBytes[powChallengeBytes.length + 3] = (nonce >>> 24) & 0xFF;
do { sha256(powBytes.slice(0, powChallengeBytes.length + 4))
sha256(challenge).then(d => digest = d); .then(s => powBytes.set(s, powChallengeBytes + 4))
} while (hasLeadingBytes(digest, difficulty)) .catch(e => console.log(e));
}
console.log(digest) return powBytes;
} }
const powChallengeCookie = getCookie("pow-challenge"); const powChallengeCookie = getCookie("pow-challenge");
const powChallenge = deserializePowChallenge(powChallengeCookie); console.log(powChallengeCookie);
console.log(powChallenge, powChallenge.length)
</script> const powChallengeBytes = base64ToBytes(powChallengeCookie);
</html> console.log(powChallengeBytes);
const powChallenge = deserializePowChallenge(powChallengeBytes);
console.log(powChallenge);
const pow = powSolve(powChallengeBytes, powChallenge.difficulty);
console.log(pow);
</script -->
</html>