const algo = { name: "AES-GCM", iv: Uint8Array.from([120,1,248,135,62,71,87,156,92,67,155,37]) } , parseAlgo = str=> ({ name: str.split(' ')[0], iv: Uint8Array.from(str.split(' ').slice(1)) }) , algos = [ "AES-GCM 120 1 248 135 62 71 87 156 92 67 155 37", 'AES-CBC' ] //, algo = parseAlgo(algos[0]) , $pass = $`pass input` , $text = $`ui-data` , $filename = $`[filename]` opener && document.documentElement.classList.add('parented') // sel=opener.document.getSelection().type=='None'||'Range' const getParentSelection = ()=> opener && opener.document.getSelection().toString() const setParentSelection = str=> opener && opener.document.activeElement.setRangeText( str ) let key const ivLen = 16 // the IV is always 16 bytes $pass.onchange = async e=> key = await crypto.subtle.importKey( 'raw' , await crypto.subtle.digest( 'SHA-256', new TextEncoder().encode($pass.value) ) , algo, false, ['encrypt','decrypt'] ) const joinIvAndData = (iv, data)=> { var buf = new Uint8Array(iv.length + data.length) Array.prototype.forEach.call(iv, (byte, i)=> buf[i] = byte ) Array.prototype.forEach.call(data, (byte, i)=> buf[ivLen + i] = byte ) return buf } const separateIvFromData = buf=> Array.prototype.reduce.call( new Uint8Array(buf), ( res, byte, i )=> i < ivLen ? ( res.iv[i] = byte, res ) : ( res.data[i - ivLen] = byte, res ) , { iv: new Uint8Array(ivLen), data: new Uint8Array(buf.byteLength - ivLen) } ) const encrypt = ()=> { return crypto.subtle.digest( 'SHA-256', new TextEncoder().encode($pass.value) ) .then( pwHash=> crypto.subtle.importKey('raw', pwHash, algo, false, ['encrypt']) ) .then( key=> crypto.subtle.encrypt(algo, key, new TextEncoder().encode($text.value)) ) //.then( ctBuffer=> $text.value = new Uint8Array(ctBuffer).toString().replace(/,/g,'O') ) .then( ctBuffer=> $text.value = uint2String( new Uint8Array(ctBuffer) ) ) //.then( ctBuffer=> $text.value = new TextDecoder().decode(new Uint8Array(ctBuffer)) ) //.catch( e=> pulse('red') ) } const encrypt2 = str=> crypto.subtle.encrypt( algo, key, new TextEncoder().encode(str) ) //.then( ctBuffer=> $text.value = new Uint8Array(ctBuffer).toString().replace(/,/g,'O') ) // .then( ctBuffer=> uint2String(new Uint8Array(ctBuffer)) ) .then( ctBuffer=> $text.value = new TextDecoder().decode(new Uint8Array(ctBuffer)) ) //.catch( e=> pulse('red') ) const encrypt3 = ( data, iv = crypto.getRandomValues(new Uint8Array(ivLen)) )=> crypto.subtle.encrypt( {name: 'AES-GCM', iv}, key, data ) // .then( encrypted=> bufferToBinaryString( joinIvAndData(iv, new Uint8Array(encrypted)).buffer ) ) .then( encrypted=> joinIvAndData(iv, new Uint8Array(encrypted)).buffer ) // var base64 = Unibabel.bufferToBase64(ciphered) // .replace(/\-/g, '+') // .replace(/_/g, '\/') // ; // while (base64.length % 4) { // base64 += '='; // } // return base64; // }) const decrypt = ()=> { return crypto.subtle.digest( 'SHA-256', new TextEncoder().encode($pass.value) ) .then( pwHash=> crypto.subtle.importKey('raw', pwHash, algo, false, ['decrypt']) ) //.then( key=> crypto.subtle.decrypt(algo, key, Uint8Array.from($text.value.split('O').map(Number)).buffer ) ) .then( key=> crypto.subtle.decrypt(algo, key, string2Uint($text.value).buffer ) ) //.then( key=> crypto.subtle.decrypt(algo, key, new TextEncoder().encode($text.value).buffer ) ) .then( ptBuffer=> $text.value = new TextDecoder().decode(ptBuffer) ) //.catch( e=> pulse('red') ) } const decrypt3 = buf=> { let { iv, data } = separateIvFromData( buf ) return crypto.subtle.decrypt( { name: 'AES-GCM', iv } , key , data ) /*.then(function (decrypted) { var base64 = bufferToUtf8(new Uint8Array(decrypted)) // .replace(/\-/g, '+') // .replace(/_/g, '\/') // ; // while (base64.length % 4) { // base64 += '='; // } return base64 })*/ } var lastSaved const save = ( content, filename )=> { let link = document.createElement('a') link.setAttribute( 'download', filename ) // If we are replacing a previously generated file we need to // manually revoke the object URL to avoid memory leaks. lastSaved && URL.revokeObjectURL( lastSaved ) link.href = lastSaved = URL.createObjectURL( new Blob([content], {type: 'text/plain'}) ) //link.href = makeTextFile( content ) document.body.appendChild( link ) // wait for the link to be added to the document window.requestAnimationFrame( e=> { link.dispatchEvent( new MouseEvent('click') ) document.body.removeChild( link ) }) } const read = file=> new Promise( (ok,ko)=> { let r = new FileReader() r.onload = e=> ok( r.result ) r.onerror = e=> ko() r.readAsArrayBuffer( file ) }) var textFile const makeTextFile = text=> { textFile && URL.revokeObjectURL(textFile) textFile = URL.createObjectURL( new Blob([text], {type: 'text/plain'}) ) // returns a URL you can use as a href return textFile } const save2 = (file, content, type)=> window.open( "data:application/octet-stream," + encodeURIComponent(content), file ) //// conversion //// const uint2string = uintArray=> String.fromCharCode.apply( null, uintArray ) const string2uint = string=> new Uint8Array( btoa( string ).split('') .map( l=>l.charCodeAt(0) ) ) const buffer2bin = buf=> Array.prototype.map.call( new Uint8Array(buf), ch=> String.fromCharCode(ch) ).join('') const buffer2base64 = arr=> btoa( buffer2bin(arr) ) const bin2buffer = binstr=> new Uint8Array( Array.prototype.map.call( new Uint8Array( binstr.length ), (ch, i)=> binstr.charCodeAt(i) ) ) const base642buffer = base64=> bin2buffer( atob(base64) ) const bin2utf8 = binstr=> decodeURIComponent( binstr.replace(/./g, c=> `%${c.charCodeAt(0) < 16 ? '0' : ''}${c.charCodeAt(0).toString(16).toUpperCase()}` ) ) const buffer2utf8 = buf=> bin2utf8( buffer2bin(buf) ) const utf82bin = str=> encodeURIComponent(str) /* replaces any uri escape sequence, such as %0A, */ /* with binary escape, such as 0x0A */ .replace(/%([0-9A-F]{2})/g, (s,$1)=> String.fromCharCode(parseInt($1, 16)) ) const utf82buffer = str=> bin2buffer(utf82bin(str)) const CC = { uint2string , string2uint , buffer2bin , buffer2uint: buf=> new Uint8Array(buf) , buffer2utf8 , buffer2base64 , bin2buffer , base642buffer , bin2utf8 , utf82bin , utf82buffer } /*const uintToString = (uintArray)=> { var encodedString = String.fromCharCode.apply(null, uintArray), decodedString = decodeURIComponent(escape(encodedString)); return decodedString; } function stringToUint(string) { var string = btoa(unescape(encodeURIComponent(string))), charList = string.split(''), uintArray = []; for (var i = 0; i < charList.length; i++) { uintArray.push(charList[i].charCodeAt(0)); } return new Uint8Array(uintArray); } */