aboutsummaryrefslogtreecommitdiff
path: root/generator
diff options
context:
space:
mode:
Diffstat (limited to 'generator')
-rw-r--r--generator/listrenderer.js68
-rw-r--r--generator/main.js86
-rw-r--r--generator/postrenderer.js161
-rw-r--r--generator/scanner.js53
-rw-r--r--generator/sha256.js174
5 files changed, 542 insertions, 0 deletions
diff --git a/generator/listrenderer.js b/generator/listrenderer.js
new file mode 100644
index 0000000..fd58b3a
--- /dev/null
+++ b/generator/listrenderer.js
@@ -0,0 +1,68 @@
+module.exports={
+ render:function(outf,pginfo,posts){return _render(outf,pginfo,posts);},
+ set_template:function(tmplf){return _set_template(tmplf);}
+};
+const fs=require('fs');
+const jsdom=require('jsdom');
+const path=require('path');
+let template_file='';
+let tmplcont='';
+
+function _set_template(tmplf)
+{
+ template_file=tmplf;
+ tmplcont=fs.readFileSync(template_file,'utf8');
+}
+
+async function _render(outf,pginfo,posts)
+{
+ const tr=new jsdom.JSDOM(tmplcont);
+ const trd=tr.window.document;
+
+ const tags_list=trd.getElementById('tagslist');
+ const posts_list=trd.getElementById('postslist');
+ const atag_template=trd.getElementById('active_taglist_item_template');
+ const tag_template=trd.getElementById('taglist_item_template');
+ const pitm_template=trd.getElementById('post_item_template');
+
+ for(let i of pginfo.tags)
+ {
+ const newnode=(i==pginfo.atag?atag_template:tag_template).cloneNode(true);
+ newnode.id='';
+ const a=newnode.querySelector('a');
+ a.innerHTML=i;
+ a.href=`/blog/list/${i}`;
+ tags_list.appendChild(newnode);
+ }
+
+ for(let i of posts)
+ {
+ let newnode=pitm_template.cloneNode(true);
+ newnode.id='';
+ const mdate=new Date(i.mdate);
+ const mdstr=`${mdate.getFullYear()}-${(mdate.getMonth()+1+'').padStart(2,'0')}-${(mdate.getDate()+'').padStart(2,'0')}`
+ newnode.querySelector('#title').innerHTML=i.title;
+ newnode.querySelector('#title').href=`/blog/post/${i.file}.html`;
+ newnode.querySelector('#tags').innerHTML='#'+i.tags.split(',').join(' #');
+ newnode.querySelector('#time').innerHTML=`${i.date}/${mdstr}`;
+ newnode.querySelector('#title').id=
+ newnode.querySelector('#tags').id=
+ newnode.querySelector('#time').id='';
+ posts_list.appendChild(newnode);
+ }
+
+ trd.getElementById('page').innerHTML=`${pginfo.cp+1}/${pginfo.pc}`;
+ trd.getElementById('prepage').href=pginfo.cp>0?
+ `/blog/list/${pginfo.atag?pginfo.atag+'/':''}${pginfo.cp-1}.html`
+ :'#';
+ trd.getElementById('nexpage').href=pginfo.cp<pginfo.pc-1?
+ `/blog/list/${pginfo.atag?pginfo.atag+'/':''}${pginfo.cp+1}.html`
+ :'#';
+
+ atag_template.parentNode.removeChild(atag_template);
+ tag_template.parentNode.removeChild(tag_template);
+ pitm_template.parentNode.removeChild(pitm_template);
+
+ fs.writeFileSync(outf,trd.documentElement.outerHTML,'utf8');
+ return outf;
+}
diff --git a/generator/main.js b/generator/main.js
new file mode 100644
index 0000000..0d86ca5
--- /dev/null
+++ b/generator/main.js
@@ -0,0 +1,86 @@
+const content_dir='../content';
+const template_dir='../templates';
+const dest_dir='../generated';
+const posts_per_listpage=5;
+
+const fs=require('fs');
+const path=require('path');
+
+const scanner=require('./scanner');
+const list=scanner.scan(content_dir,dest_dir);
+const tags=scanner.build_list_index();
+const taglist=Object.keys(tags).sort();
+const force=process.argv.indexOf('--force')!=-1;
+
+const postrenderer=require('./postrenderer');
+postrenderer.set_template(path.join(template_dir,'post_template'));
+
+function ensure_dir(p)
+{
+ let needcreation=false;
+ try{
+ if(!fs.statSync(p).isDirectory())
+ {
+ needcreation=true;
+ fs.unlinkSync(p);
+ }
+ }catch(e){needcreation=true;}
+ if(needcreation)fs.mkdirSync(p);
+ if(!fs.statSync(p).isDirectory())throw 'shit';
+}
+
+const post_dir=path.join(dest_dir,'post');
+ensure_dir(post_dir);
+for(let j=0;j<list.length;++j){
+const i=list[j];
+if(i.needsupdate||force)
+ postrenderer.render(
+ path.join(content_dir,`${i.file}.txt`),
+ path.join(post_dir,`${i.file}.html`),
+ j?list[j-1].file:undefined,
+ j<list.length-1?list[j+1].file:undefined
+ )
+ .then((r)=>{console.log(`rendered: ${r}`);})
+}
+
+const listrenderer=require('./listrenderer');
+listrenderer.set_template(path.join(template_dir,'list_template'));
+
+const list_dir=path.join(dest_dir,'list');
+const ppp=posts_per_listpage;
+ensure_dir(list_dir);
+let pc=Math.floor(list.length/ppp)+(list.length%ppp!=0);
+for(let i=0;i<pc;++i)
+{
+ const cl=list.slice(i*ppp,Math.min((i+1)*ppp,list.length));
+ listrenderer.render(
+ path.join(list_dir,`${i}.html`),
+ {atag:'',tags:taglist,cp:i,pc:pc},
+ cl
+ );
+}
+try{
+fs.unlinkSync(path.join(list_dir,'index.html'));
+}catch(e){};
+fs.symlinkSync('0.html',path.join(list_dir,'index.html'));
+
+for(let i of taglist)
+if(tags[i].needsupdate||force)
+{
+ const tl_dir=path.join(list_dir,i);
+ ensure_dir(tl_dir);
+ let pc=Math.floor(tags[i].posts.length/ppp)+(tags[i].posts.length%ppp!=0);
+ for(let j=0;j<pc;++j)
+ {
+ const cl=tags[i].posts.slice(j*ppp,Math.min((j+1)*ppp,tags[i].posts.length));
+ listrenderer.render(
+ path.join(tl_dir,`${j}.html`),
+ {atag:i,tags:taglist,cp:j,pc:pc},
+ cl
+ );
+ }
+ try{
+ fs.unlinkSync(path.join(tl_dir,'index.html'));
+ }catch(e){};
+ fs.symlinkSync('0.html',path.join(tl_dir,'index.html'));
+}
diff --git a/generator/postrenderer.js b/generator/postrenderer.js
new file mode 100644
index 0000000..729c4df
--- /dev/null
+++ b/generator/postrenderer.js
@@ -0,0 +1,161 @@
+module.exports={
+ render:function(inf,outf,np,pp){return _render(inf,outf,np,pp);},
+ set_template:function(tmplf){return _set_template(tmplf);}
+};
+const fs=require('fs');
+const jsdom=require('jsdom');
+const path=require('path');
+const aes=require('aes-js');
+const scrypt=require('scrypt-js');
+const btoa=require('btoa');
+const sha256=require('./sha256').sha256;
+const spawn=require('child_process').spawn;
+let template_file='';
+let tmplcont='';
+let tocid;
+let headerlist;
+
+function _dfs(doc,el,le,p)
+{
+ var e=doc.createElement('li');
+ e.innerHTML=`<a class="toctarg" href="#tocanch${tocid}">${el.innerHTML}</a>`;
+ le.appendChild(e);
+ el.id='tocanch'+(tocid++);
+ el.classList.add('tvis');
+ var che=null,i;
+ for(i=p+1;i<headerlist.length;)
+ {
+ if(headerlist[i].tagName<=el.tagName)break;
+ if(headerlist[i].classList.contains('notoc'))continue;
+ if(che===null)
+ {
+ var te=doc.createElement('li');
+ che=doc.createElement('ul');
+ che.classList.add('tocnode');
+ te.appendChild(che);
+ le.appendChild(te);
+ }
+ i=_dfs(doc,headerlist[i],che,i);
+ }
+ return i;
+}
+function footnoter(doc)
+{
+ var footnotes=doc.body.getElementsByTagName("footnote");
+ let starting=doc.getElementById("notediv").children.length;
+ for(var i=0;i<footnotes.length;++i)
+ {
+ var s=footnotes[i].innerHTML;
+ footnotes[i].innerHTML="";
+ var a=doc.createElement("a");
+ a.setAttribute("id","n"+(starting+i+1));
+ a.setAttribute("href","#note"+(starting+i+1));
+ a.setAttribute("class","note");
+ a.innerHTML="["+(starting+i+1)+"]";
+ footnotes[i].parentNode.insertBefore(a,footnotes[i]);
+ var span=doc.createElement("span");
+ span.setAttribute("class","TText");
+ span.innerHTML="<a id=\"note"+(starting+i+1)+"\" href=\"#n"+(starting+i+1)+"\">["+(starting+i+1)+"]</a>: "+s+"<br>";
+ doc.getElementById("notediv").appendChild(span);
+ }
+ starting+=footnotes.length;
+ while(footnotes.length)footnotes[0].remove();
+}
+async function encrypt(doc)
+{
+ let enid=0;
+ for(let i of doc.querySelectorAll('encrypted'))
+ {
+ const key=i.getAttribute('key');
+ i.removeAttribute('key');
+ const contu8=aes.utils.utf8.toBytes(i.innerHTML);
+ const conths=sha256.hash(aes.utils.hex.fromBytes(contu8),{msgFormat:'hex-bytes'});
+ const keyu8=aes.utils.utf8.toBytes(key);
+ const saltu8=aes.utils.utf8.toBytes('hellwhymustiaddsalttothiscrap');
+ const enckey=await new Promise(
+ (resolv,rej)=>
+ {scrypt(keyu8,saltu8,1024,16,2,32,(e,p,k)=>{e?rej(e):k?resolv(k):undefined;});}
+ );
+ const ctr=new aes.ModeOfOperation.ctr(enckey);
+ const enccont=ctr.encrypt(contu8);
+ let encconts='';for(let i=0;i<enccont.length;++i)encconts+=String.fromCharCode(enccont[i]);
+ encconts=btoa(encconts);
+ i.setAttribute('hash',conths);
+ i.setAttribute('encont',encconts);
+ i.setAttribute('enid',enid);
+ i.innerHTML=
+`This section is encrypted. Click <a href="javascript:void(0)" onclick="decryptui(${enid})">here</a>
+to decrypt. <noscript>Note that you need JavaScript enabled for decryption.</noscript>`;
+ ++enid;
+ }
+}
+function _set_template(tmplf)
+{
+ template_file=tmplf;
+ tmplcont=fs.readFileSync(template_file,'utf8');
+}
+async function _render(inf,outf,np,pp)
+{
+ const postcont=fs.readFileSync(inf,'utf8');
+ const tr=new jsdom.JSDOM(tmplcont);
+ const trd=tr.window.document;
+ const cont=fs.readFileSync(inf,'utf8');
+ const contsplit=cont.split('\n');
+ const meta=contsplit.splice(0,4);
+ const preproc=meta[3].trim();
+ trd.getElementById('title').innerHTML=
+ trd.getElementById('titleh').innerHTML=meta[0].trim();
+ trd.getElementById('datetags').innerHTML=`${meta[1].trim()}<br>#${meta[2].split(',').join(' #')}`;
+ if(preproc.length)
+ {
+ const ppargs=preproc.split(' ');
+ const ppcmd=ppargs.splice(0,1);
+ const pp=spawn(ppcmd[0],ppargs,{maxBuffer:1048576});
+ let out='';
+ pp.stdout.setEncoding('utf8');
+ pp.stdout.on('data',(d)=>{out+=d;});
+ pp.stdin.write(contsplit.join('\n'),'utf8');
+ const ppp=new Promise(
+ (resolv,rej)=>
+ {
+ pp.on('close',(r)=>{if(r)rej(r);resolv(out);});
+ }
+ );
+ pp.stdin.end();
+ trd.getElementById('article').innerHTML=await ppp;
+ }
+ else
+ trd.getElementById('article').innerHTML=contsplit.join('\n');
+
+ //Encryption
+ await encrypt(trd);
+
+ //TOC
+ const l=trd.getElementById('article').querySelectorAll('h2,h3,h4,h5,h6');
+ const tocroot=trd.getElementById('tocroot');
+ tocid=0;
+ headerlist=[];
+ for(let i of l)
+ if(!i.classList.contains('notoc'))headerlist.push(i);
+ for(let i=0;i<headerlist.length;)i=_dfs(trd,headerlist[i],tocroot,i);
+ if(!tocroot.children.length)trd.getElementById('tocouter').style.display='none';
+
+ //Footnotes
+ footnoter(trd);
+
+ //Tag list
+ const tgs=meta[2].split(',');
+ for(let i=0;i<tgs.length;++i)
+ {
+ let l=trd.createElement('li');
+ l.innerHTML=`<a href="/blog/list/${tgs[i]}/">${tgs[i]}</a>`;
+ trd.getElementById('tagslist').appendChild(l);
+ }
+
+ //neighboring posts
+ if(pp)trd.getElementById('prevp').href=`${pp}.html`;
+ if(np)trd.getElementById('nextp').href=`${np}.html`;
+
+ fs.writeFileSync(outf,trd.documentElement.outerHTML,'utf8');
+ return outf;
+}
diff --git a/generator/scanner.js b/generator/scanner.js
new file mode 100644
index 0000000..0817cdb
--- /dev/null
+++ b/generator/scanner.js
@@ -0,0 +1,53 @@
+module.exports={
+ scan:function(s,d){return _scan(s,d);},
+ build_list_index:function(){return _build_list_index();}
+};
+const fs=require('fs');
+const path=require('path');
+const list=[];
+const tags=[];
+function _scan(s,dst)
+{
+ list.splice(0);
+ d=fs.readdirSync(s).reverse();
+ pdst=path.join(dst,'post');
+ let poste=true;
+ try{
+ st=fs.statSync(pdst);
+ if(!st.isDirectory)throw 'shit';
+ }catch(e){poste=false;}
+ for(let i of d)
+ if(i.endsWith('.txt'))
+ {
+ const cont=fs.readFileSync(path.join(s,i),'utf8');
+ const smodt=fs.statSync(path.join(s,i)).mtimeMs;
+ let dmodt=0;
+ try{
+ dmodt=fs.statSync(path.join(pdst,i.substring(0,i.length-4)+'.html')).mtimeMs;
+ }catch(e){};
+ contsplit=cont.split('\n');
+ if(contsplit.length<4)continue;
+ if(contsplit[1].indexOf('WIP')!=-1)continue;
+ list.push({
+ file:i.substring(0,i.length-4),
+ title:contsplit[0].trim(),
+ date:contsplit[1].trim(),
+ tags:contsplit[2].trim(),
+ mdate:smodt,
+ needsupdate:dmodt<smodt
+ });
+
+ }
+ return list;
+}
+function _build_list_index()
+{
+ for(let i of list)
+ for(let tg of i.tags.split(','))
+ {
+ if(!tags[tg])tags[tg]={posts:[],needsupdate:false};
+ tags[tg].posts.push(i);
+ tags[tg].needsupdate|=i.needsupdate;
+ }
+ return tags;
+}
diff --git a/generator/sha256.js b/generator/sha256.js
new file mode 100644
index 0000000..f6c59d8
--- /dev/null
+++ b/generator/sha256.js
@@ -0,0 +1,174 @@
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* SHA-256 (FIPS 180-4) implementation in JavaScript (c) Chris Veness 2002-2017 */
+/* MIT Licence */
+/* www.movable-type.co.uk/scripts/sha256.html */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+'use strict';
+
+/**
+ * SHA-256 hash function reference implementation.
+ *
+ * This is an annotated direct implementation of FIPS 180-4, without any optimisations. It is
+ * intended to aid understanding of the algorithm rather than for production use.
+ *
+ * While it could be used where performance is not critical, I would recommend using the ‘Web
+ * Cryptography API’ (developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest) for the browser,
+ * or the ‘crypto’ library (nodejs.org/api/crypto.html#crypto_class_hash) in Node.js.
+ *
+ * See csrc.nist.gov/groups/ST/toolkit/secure_hashing.html
+ * csrc.nist.gov/groups/ST/toolkit/examples.html
+ */
+class Sha256 {
+
+ /**
+ * Generates SHA-256 hash of string.
+ *
+ * @param {string} msg - (Unicode) string to be hashed.
+ * @param {Object} [options]
+ * @param {string} [options.msgFormat=string] - Message format: 'string' for JavaScript string
+ * (gets converted to UTF-8 for hashing); 'hex-bytes' for string of hex bytes ('616263' ≡ 'abc') .
+ * @param {string} [options.outFormat=hex] - Output format: 'hex' for string of contiguous
+ * hex bytes; 'hex-w' for grouping hex bytes into groups of (4 byte / 8 character) words.
+ * @returns {string} Hash of msg as hex character string.
+ */
+ static hash(msg, options) {
+ const defaults = { msgFormat: 'string', outFormat: 'hex' };
+ const opt = Object.assign(defaults, options);
+
+ // note use throughout this routine of 'n >>> 0' to coerce Number 'n' to unsigned 32-bit integer
+
+ switch (opt.msgFormat) {
+ default: // default is to convert string to UTF-8, as SHA only deals with byte-streams
+ case 'string': msg = utf8Encode(msg); break;
+ case 'hex-bytes':msg = hexBytesToString(msg); break; // mostly for running tests
+ }
+
+ // constants [§4.2.2]
+ 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 ];
+
+ // initial hash value [§5.3.3]
+ const H = [
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ];
+
+ // PREPROCESSING [§6.2.1]
+
+ msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1]
+
+ // convert string msg into 512-bit blocks (array of 16 32-bit integers) [§5.2.1]
+ const l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length
+ const N = Math.ceil(l/16); // number of 16-integer (512-bit) blocks required to hold 'l' ints
+ const M = new Array(N); // message M is N×16 array of 32-bit integers
+
+ for (let i=0; i<N; i++) {
+ M[i] = new Array(16);
+ for (let j=0; j<16; j++) { // encode 4 chars per integer (64 per block), big-endian encoding
+ M[i][j] = (msg.charCodeAt(i*64+j*4+0)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16)
+ | (msg.charCodeAt(i*64+j*4+2)<< 8) | (msg.charCodeAt(i*64+j*4+3)<< 0);
+ } // note running off the end of msg is ok 'cos bitwise ops on NaN return 0
+ }
+ // add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1]
+ // note: most significant word would be (len-1)*8 >>> 32, but since JS converts
+ // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
+ const lenHi = ((msg.length-1)*8) / Math.pow(2, 32);
+ const lenLo = ((msg.length-1)*8) >>> 0;
+ M[N-1][14] = Math.floor(lenHi);
+ M[N-1][15] = lenLo;
+
+
+ // HASH COMPUTATION [§6.2.2]
+
+ for (let i=0; i<N; i++) {
+ const W = new Array(64);
+
+ // 1 - prepare message schedule 'W'
+ for (let t=0; t<16; t++) W[t] = M[i][t];
+ for (let t=16; t<64; t++) {
+ W[t] = (Sha256.σ1(W[t-2]) + W[t-7] + Sha256.σ0(W[t-15]) + W[t-16]) >>> 0;
+ }
+
+ // 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value
+ let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
+
+ // 3 - main loop (note '>>> 0' for 'addition modulo 2^32')
+ for (let t=0; t<64; t++) {
+ const T1 = h + Sha256.Σ1(e) + Sha256.Ch(e, f, g) + K[t] + W[t];
+ const T2 = Sha256.Σ0(a) + Sha256.Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = (d + T1) >>> 0;
+ d = c;
+ c = b;
+ b = a;
+ a = (T1 + T2) >>> 0;
+ }
+
+ // 4 - compute the new intermediate hash value (note '>>> 0' for 'addition modulo 2^32')
+ H[0] = (H[0]+a) >>> 0;
+ H[1] = (H[1]+b) >>> 0;
+ H[2] = (H[2]+c) >>> 0;
+ H[3] = (H[3]+d) >>> 0;
+ H[4] = (H[4]+e) >>> 0;
+ H[5] = (H[5]+f) >>> 0;
+ H[6] = (H[6]+g) >>> 0;
+ H[7] = (H[7]+h) >>> 0;
+ }
+
+ // convert H0..H7 to hex strings (with leading zeros)
+ for (let h=0; h<H.length; h++) H[h] = ('00000000'+H[h].toString(16)).slice(-8);
+
+ // concatenate H0..H7, with separator if required
+ const separator = opt.outFormat=='hex-w' ? ' ' : '';
+
+ return H.join(separator);
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+ function utf8Encode(str) {
+ try {
+ return new TextEncoder().encode(str, 'utf-8').reduce((prev, curr) => prev + String.fromCharCode(curr), '');
+ } catch (e) { // no TextEncoder available?
+ return unescape(encodeURIComponent(str)); // monsur.hossa.in/2012/07/20/utf-8-in-javascript.html
+ }
+ }
+
+ function hexBytesToString(hexStr) { // convert string of hex numbers to a string of chars (eg '616263' -> 'abc').
+ const str = hexStr.replace(' ', ''); // allow space-separated groups
+ return str=='' ? '' : str.match(/.{2}/g).map(byte => String.fromCharCode(parseInt(byte, 16))).join('');
+ }
+ }
+
+
+
+ /**
+ * Rotates right (circular right shift) value x by n positions [§3.2.4].
+ * @private
+ */
+ static ROTR(n, x) {
+ return (x >>> n) | (x << (32-n));
+ }
+
+
+ /**
+ * Logical functions [§4.1.2].
+ * @private
+ */
+ static Σ0(x) { return Sha256.ROTR(2, x) ^ Sha256.ROTR(13, x) ^ Sha256.ROTR(22, x); }
+ static Σ1(x) { return Sha256.ROTR(6, x) ^ Sha256.ROTR(11, x) ^ Sha256.ROTR(25, x); }
+ static σ0(x) { return Sha256.ROTR(7, x) ^ Sha256.ROTR(18, x) ^ (x>>>3); }
+ static σ1(x) { return Sha256.ROTR(17, x) ^ Sha256.ROTR(19, x) ^ (x>>>10); }
+ static Ch(x, y, z) { return (x & y) ^ (~x & z); } // 'choice'
+ static Maj(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); } // 'majority'
+
+}
+
+module.exports.sha256 = Sha256;