1*12993035SSascha Leib"use strict"; 2*12993035SSascha Leib/* DokuWiki BotMon Captcha JavaScript */ 3*12993035SSascha Leib/* 22.10.2025 - 0.1.0 - pre-release */ 4*12993035SSascha Leib/* Author: Sascha Leib <ad@hominem.info> */ 5*12993035SSascha Leib 6*12993035SSascha Leibconst $BMCaptcha = { 7*12993035SSascha Leib 8*12993035SSascha Leib init: function() { 9*12993035SSascha Leib /* mark the page to contain the captcha styles */ 10*12993035SSascha Leib document.getElementsByTagName('body')[0].classList.add('botmon_captcha'); 11*12993035SSascha Leib 12*12993035SSascha Leib $BMCaptcha.install() 13*12993035SSascha Leib }, 14*12993035SSascha Leib 15*12993035SSascha Leib install: function() { 16*12993035SSascha Leib // find the parent element: 17*12993035SSascha Leib let bm_parent = document.getElementsByTagName('body')[0]; 18*12993035SSascha Leib 19*12993035SSascha Leib // create the dialog: 20*12993035SSascha Leib const dlg = document.createElement('dialog'); 21*12993035SSascha Leib dlg.setAttribute('closedby', 'none'); 22*12993035SSascha Leib dlg.setAttribute('open', 'open'); 23*12993035SSascha Leib dlg.id = 'botmon_captcha_box'; 24*12993035SSascha Leib dlg.innerHTML = '<h2>Captcha box</h2><p>Checking if you are a human …</p><p></p>'; 25*12993035SSascha Leib 26*12993035SSascha Leib // Checkbox: 27*12993035SSascha Leib const lbl = document.createElement('label'); 28*12993035SSascha Leib const cb = document.createElement('input'); 29*12993035SSascha Leib cb.setAttribute('type', 'checkbox'); 30*12993035SSascha Leib cb.addEventListener('click', $BMCaptcha._cbCallback); 31*12993035SSascha Leib lbl.appendChild(cb); 32*12993035SSascha Leib lbl.appendChild(document.createTextNode('I am a human.')); 33*12993035SSascha Leib 34*12993035SSascha Leib dlg.appendChild(lbl); 35*12993035SSascha Leib 36*12993035SSascha Leib bm_parent.appendChild(dlg); 37*12993035SSascha Leib }, 38*12993035SSascha Leib 39*12993035SSascha Leib /* creates a digest hash for the cookie function */ 40*12993035SSascha Leib digest: { 41*12993035SSascha Leib 42*12993035SSascha Leib /* simple SHA hash function - adapted from https://geraintluff.github.io/sha256/ */ 43*12993035SSascha Leib hash: function(ascii) { 44*12993035SSascha Leib 45*12993035SSascha Leib // shortcut: 46*12993035SSascha Leib const sha256 = $BMCaptcha.digest.hash; 47*12993035SSascha Leib 48*12993035SSascha Leib // helper function 49*12993035SSascha Leib const rightRotate = function(v, a) { 50*12993035SSascha Leib return (v>>>a) | (v<<(32 - a)); 51*12993035SSascha Leib }; 52*12993035SSascha Leib 53*12993035SSascha Leib var mathPow = Math.pow; 54*12993035SSascha Leib var maxWord = mathPow(2, 32); 55*12993035SSascha Leib var lengthProperty = 'length' 56*12993035SSascha Leib var i, j; 57*12993035SSascha Leib var result = '' 58*12993035SSascha Leib 59*12993035SSascha Leib var words = []; 60*12993035SSascha Leib var asciiBitLength = ascii[lengthProperty]*8; 61*12993035SSascha Leib 62*12993035SSascha Leib //* caching results is optional - remove/add slash from front of this line to toggle 63*12993035SSascha Leib // Initial hash value: first 32 bits of the fractional parts of the square roots of the first 8 primes 64*12993035SSascha Leib // (we actually calculate the first 64, but extra values are just ignored) 65*12993035SSascha Leib var hash = sha256.h = sha256.h || []; 66*12993035SSascha Leib // Round constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes 67*12993035SSascha Leib var k = sha256.k = sha256.k || []; 68*12993035SSascha Leib var primeCounter = k[lengthProperty]; 69*12993035SSascha Leib /*/ 70*12993035SSascha Leib var hash = [], k = []; 71*12993035SSascha Leib var primeCounter = 0; 72*12993035SSascha Leib //*/ 73*12993035SSascha Leib 74*12993035SSascha Leib var isComposite = {}; 75*12993035SSascha Leib for (var candidate = 2; primeCounter < 64; candidate++) { 76*12993035SSascha Leib if (!isComposite[candidate]) { 77*12993035SSascha Leib for (i = 0; i < 313; i += candidate) { 78*12993035SSascha Leib isComposite[i] = candidate; 79*12993035SSascha Leib } 80*12993035SSascha Leib hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0; 81*12993035SSascha Leib k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0; 82*12993035SSascha Leib } 83*12993035SSascha Leib } 84*12993035SSascha Leib 85*12993035SSascha Leib ascii += '\x80' // Append Ƈ' bit (plus zero padding) 86*12993035SSascha Leib while (ascii[lengthProperty]%64 - 56) ascii += '\x00' // More zero padding 87*12993035SSascha Leib for (i = 0; i < ascii[lengthProperty]; i++) { 88*12993035SSascha Leib j = ascii.charCodeAt(i); 89*12993035SSascha Leib if (j>>8) return; // ASCII check: only accept characters in range 0-255 90*12993035SSascha Leib words[i>>2] |= j << ((3 - i)%4)*8; 91*12993035SSascha Leib } 92*12993035SSascha Leib words[words[lengthProperty]] = ((asciiBitLength/maxWord)|0); 93*12993035SSascha Leib words[words[lengthProperty]] = (asciiBitLength) 94*12993035SSascha Leib 95*12993035SSascha Leib // process each chunk 96*12993035SSascha Leib for (j = 0; j < words[lengthProperty];) { 97*12993035SSascha Leib var w = words.slice(j, j += 16); // The message is expanded into 64 words as part of the iteration 98*12993035SSascha Leib var oldHash = hash; 99*12993035SSascha Leib // This is now the undefinedworking hash", often labelled as variables a...g 100*12993035SSascha Leib // (we have to truncate as well, otherwise extra entries at the end accumulate 101*12993035SSascha Leib hash = hash.slice(0, 8); 102*12993035SSascha Leib 103*12993035SSascha Leib for (i = 0; i < 64; i++) { 104*12993035SSascha Leib var i2 = i + j; 105*12993035SSascha Leib // Expand the message into 64 words 106*12993035SSascha Leib // Used below if 107*12993035SSascha Leib var w15 = w[i - 15], w2 = w[i - 2]; 108*12993035SSascha Leib 109*12993035SSascha Leib // Iterate 110*12993035SSascha Leib var a = hash[0], e = hash[4]; 111*12993035SSascha Leib var temp1 = hash[7] 112*12993035SSascha Leib + (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) // S1 113*12993035SSascha Leib + ((e&hash[5])^((~e)&hash[6])) // ch 114*12993035SSascha Leib + k[i] 115*12993035SSascha Leib // Expand the message schedule if needed 116*12993035SSascha Leib + (w[i] = (i < 16) ? w[i] : ( 117*12993035SSascha Leib w[i - 16] 118*12993035SSascha Leib + (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) // s0 119*12993035SSascha Leib + w[i - 7] 120*12993035SSascha Leib + (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) // s1 121*12993035SSascha Leib )|0 122*12993035SSascha Leib ); 123*12993035SSascha Leib // This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble 124*12993035SSascha Leib var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) // S0 125*12993035SSascha Leib + ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj 126*12993035SSascha Leib 127*12993035SSascha Leib hash = [(temp1 + temp2)|0].concat(hash); // We don't bother trimming off the extra ones, they're harmless as long as we're truncating when we do the slice() 128*12993035SSascha Leib hash[4] = (hash[4] + temp1)|0; 129*12993035SSascha Leib } 130*12993035SSascha Leib 131*12993035SSascha Leib for (i = 0; i < 8; i++) { 132*12993035SSascha Leib hash[i] = (hash[i] + oldHash[i])|0; 133*12993035SSascha Leib } 134*12993035SSascha Leib } 135*12993035SSascha Leib 136*12993035SSascha Leib for (i = 0; i < 8; i++) { 137*12993035SSascha Leib for (j = 3; j + 1; j--) { 138*12993035SSascha Leib var b = (hash[i]>>(j*8))&255; 139*12993035SSascha Leib result += ((b < 16) ? 0 : '') + b.toString(16); 140*12993035SSascha Leib } 141*12993035SSascha Leib } 142*12993035SSascha Leib return result; 143*12993035SSascha Leib } 144*12993035SSascha Leib }, 145*12993035SSascha Leib 146*12993035SSascha Leib _cbCallback: function(e) { 147*12993035SSascha Leib if (e.target.checked) { 148*12993035SSascha Leib //document.getElementById('botmon_captcha_box').close(); 149*12993035SSascha Leib 150*12993035SSascha Leib // make a hash for the cookie: 151*12993035SSascha Leib const seed = document._botmon.seed || ''; 152*12993035SSascha Leib const extIp = document._botmon.ip || '0.0.0.0'; 153*12993035SSascha Leib const d = new Date(document._botmon.t0); 154*12993035SSascha Leib const raw = seed + '|' + location.hostname + '|' + extIp + '|' + d.toISOString().substring(0, 10); 155*12993035SSascha Leib 156*12993035SSascha Leib const hash = $BMCaptcha.digest.hash(raw); 157*12993035SSascha Leib console.log('Setting cookie to:', raw, ' --> ', hash); 158*12993035SSascha Leib document.cookie = "captcha=" + hash + ';'; 159*12993035SSascha Leib 160*12993035SSascha Leib window.location.reload(true); 161*12993035SSascha Leib } 162*12993035SSascha Leib } 163*12993035SSascha Leib 164*12993035SSascha Leib} 165*12993035SSascha Leib// initialise the captcha module: 166*12993035SSascha Leib$BMCaptcha.init();