From c932cd295d414f78c65167e7717b04f61e93122a Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Sun, 9 Oct 2016 01:54:34 +0200 Subject: Initial tests --- sjcl-front.js | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 sjcl-front.js (limited to 'sjcl-front.js') diff --git a/sjcl-front.js b/sjcl-front.js new file mode 100644 index 0000000..1a67d64 --- /dev/null +++ b/sjcl-front.js @@ -0,0 +1,264 @@ +function inject_css() { + var s = document.createElement('style'); + s.setAttribute('type', 'text/css'); + s.appendChild(document.createTextNode("\ +@font-face { \n\ +font-family: 'fontello'; src: url('data:application/octet-stream;base64,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') format('woff'); } \n\ +.sjcl-undecrypted { background-color: rgba(255, 0, 0, 0.2); cursor: pointer; } \n\ +.sjcl-decrypted { background-color: rgba(0, 255, 0, 0.2); } \n\ +.sjcl-hook { display: inline-block; position: relative; vertical-align: top; } \n\ +.sjcl-hook:before, .sjcl-decrypted:before, .sjcl-undecrypted:before { content: '\\e801 '; font-family: 'fontello'; opacity: 0.5; font-size: 1.1em; padding: 0 0.3em 0 0.3em; margin-right: 0.2em; box-shadow: 0 0 0.3em currentColor; border-radius: 50%; } \n\ +.sjcl-dropdown, .sjcl-addkey-sub { display: none; border-radius: 0.4em; } \n\ +.sjcl-hook:hover .sjcl-dropdown { display: block; position: absolute; min-width: 300px; background-color: #f9f9f9; box-shadow: 0px 1em 2em 0 rgba(0,0,0,0.2); color: black; } \n\ +.sjcl-decrypt, .sjcl-usekey, .sjcl-addkey { padding: 2px 3px 2px 6px; cursor: pointer; border-radius: 0 0 0.4em 0.4em; } \n\ +.sjcl-decrypt:hover, .sjcl-usekey:hover, .sjcl-addkey:hover { border-radius: 0.4em; background-color: #f1f1f1; } \n\ +.sjcl-addkey:hover .sjcl-addkey-sub { display: block; } \n\ +.sjcl-addkey-sub label { display: inline-block !important; width: 40%; } \n\ +.sjcl-addkey-button, .sjcl-delkey-button { display: inline-block; text-align: center; margin-right: 6px; width: 40%; border-radius: 0.4em; border: solid 0.1em lime; } \n\ +.sjcl-delkey-button { border: solid 0.1em red; } \n\ +.sjcl-addkey-button:hover { background-color: lime; } \n\ +.sjcl-delkey-button:hover { background-color: red; } \ +")); + document.getElementsByTagName('head')[0].appendChild(s); +} + +function inject_textarea_hook() { + // Get rid of old hooks + var hooks = document.getElementsByClassName("sjcl-hook"); + for(var i = hooks.length - 1; i >= 0; i--) { + var n = hooks.item(i); + n.parentNode.removeChild(n); + } + // And insert new hooks + var walk=document.createTreeWalker(document.body,NodeFilter.SHOW_ELEMENT,null,false); + while(n = walk.nextNode()) + if (n.type == 'textarea') { + var keys = list_keys(); + var h = document.createElement('div'); + h.className='sjcl-hook'; + var dd = '
' + for (var i in keys) + dd += '
Encrypt with: "'+keys[i]+'"
'; + h.innerHTML = dd + '
Decrypt

Manage keys
add!
delete!
'; + n.parentNode.insertBefore(h,n.nextSibling); + } +} + +function decrypt_string(text, target_node, do_prompt) { + var rp, ct, plain, key_name, key, do_store = false; + + /* Clear sjcl classes from target node */ + target_node.className = target_node.className.replace(/(^| )sjcl-[a-z]+($| )/, ''); + + /* Check sjcl signature and bail out */ + if (text.substring(0,7) != 'sjcl://') + throw 'Text is not encrypted.'; + + /* Skip signature */ + text = text.substring(7); + /* Retrieve key name */ + try { + ct = JSON.parse(text); + } catch (e) { + target_node.className += ' sjcl-garbled'; + throw 'Cipher text is garbled.'; + } + try { + key_name = sjcl.codec.utf8String.fromBits(sjcl.codec.base64.toBits(ct.adata)); + } catch(e) { + target_node.className += ' sjcl-garbled'; + throw 'Can not extract key name'; + } + + key = retrieve_key(key_name); + if (!key && do_prompt) { + key = prompt( 'Enter password for the key ' + key_name); + do_store = true; + } + if (!key) { + target_node.className += ' sjcl-undecrypted'; + throw 'You need the key "' + key_name + '" to decrypt.'; + } + try { + plain = sjcl.decrypt(key, text, {}, rp); + } catch (e) { + if (do_store) + alert('Could not decrypt'); + target_node.className += ' sjcl-undecrytable'; + throw 'Your key "' + key_name + '" can not decrypt.'; + } + if (do_store) + store_key(key_name, key); + + target_node.className += ' sjcl-decrypted'; + return plain.replace(/\s+$/gm,''); +} + +function decrypt_element(n) { + try { + n.data = decrypt_string(n.data, n.parentElement, false); + } catch(e) { + n.parentElement.ciphertext = n.data; + n.parentElement.ciphertext_node = n; + n.data = e; + } +} + +function decrypt_undecrypted(n) { + if(!n.ciphertext || n.ciphertext_node==null) + return; + try { + n.ciphertext_node.data = decrypt_string(n.ciphertext, n, true); + inject_textarea_hook(); + find_undecrypted_nodes(); + } catch(e) { + n.ciphertext_node.data = e; + } +} + +function decrypt_textarea(n) { + try { + n.value = decrypt_string(n.value, n, false); + } catch(e) { + alert(e); + } +} + +function encrypt_textarea(n,name,key) { + var t = n.value + (" ".repeat(17 + Math.floor((Math.random() * 64)))); + var p = {adata:name, iter:1000, mode:'ccm', ts:128, ks:256, iter: 1000 }; + n.value = 'sjcl://'+sjcl.encrypt(key, t || '', p); + n.className = n.className.replace(/(^| )sjcl-[a-z]+($| )/, '')+' sjcl-undecrypted'; +} + +/* Auto node operation */ +function find_encrypted_nodes() { + var n, walk = document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT,null,false); + while(n = walk.nextNode()) + if (n.data.substring(0,7) == 'sjcl://') + decrypt_element(n, false); +} + +function find_undecrypted_nodes() { + var undec = document.getElementsByClassName("sjcl-undecrypted"); + for(var i = 0; i < undec.length; i++) + decrypt_undecrypted(undec.item(i), false); +} + +/* Passphrase accessors */ +function manage_key(n, do_add) { + var name = n.parentNode.getElementsByClassName('sjcl-addkey-name')[0]; + var key = n.parentNode.getElementsByClassName('sjcl-addkey-key')[0]; + if (!name.value) + alert( 'Missing key name.'); + else + store_key(name.value, do_add ? key.value : ''); + inject_textarea_hook(); +} + +/* Try to use the most logliving method to store key array */ +function store_key(name, key) { + /* Collect keys from all storage methods */ + var obj = {}; + try { + obj = JSON.parse(localStorage['frab-sjcl']); + } catch(e) {} + try { + var sso = JSON.parse(sessionStorage['frab-sjcl']); + for (var k in sso) { obj[k] = sso[k]; } + delete sessionStorage['frab-sjcl']; + } catch(e) {} + try { + wo = JSON.parse(window['frab-sjcl']); + for (var k in wo) { obj[k] = wo[k]; } + delete window['frab-sjcl']; + } catch(e) {} + + if (key) + obj[name] = key; + else + delete obj[name]; + var out = JSON.stringify(obj); + + /* Try to store in local/sessionStorage and fall back to + the window object, so at least we can decrypt all elements + locally */ + try { + localStorage['frab-sjcl'] = out; + if (localStorage['frab-sjcl'] != out) + throw 0; + return; + } catch(e) {} + try { + sessionStorage['frab-sjcl'] = out; + if (sessionStorage['frab-sjcl'] != out) + throw 0; + return; + } catch(e) {} + + window['frab-sjcl'] = out; +} + +function retrieve_key(name) { + try { + var obj = JSON.parse(localStorage['frab-sjcl'] || '{}'); + if (obj && obj[name]) + return obj[name]; + } catch(e) {} + try { + var obj = JSON.parse(sessionStorage['frab-sjcl'] || '{}'); + if (obj && obj[name]) + return obj[name]; + } catch(e) {} + try { + var obj = JSON.parse(window['frab-sjcl'] || '{}'); + if (obj && obj[name]) + return obj[name]; + } catch(e) {} + return ''; +} + +function list_keys() { + var key_list = []; + try { + key_list = (Object.keys(JSON.parse(localStorage['frab-sjcl']))); + } catch(e) {} + try { + key_list = key_list.concat(Object.keys(JSON.parse(sessionStorage['frab-sjcl']))); + } catch(e) {} + try { + key_list = key_list.concat(Object.keys(JSON.parse(window['frab-sjcl']))); + } catch(e) {} + return key_list; +} + +/* Event handler plumbing */ +function click_body(e) { + var n = e.target; + + if (n.className=='sjcl-usekey') { + var name = n.getAttribute('keyname'); + encrypt_textarea(n.parentNode.parentNode.previousSibling, name, retrieve_key(name)); + } + if (n.className=='sjcl-decrypt') + decrypt_textarea(n.parentNode.parentNode.previousSibling); + if ((' '+n.className+' ').indexOf(' sjcl-undecrypted ') > -1) + decrypt_undecrypted(n); + if (n.className=='sjcl-addkey-button') + manage_key(n,true); + if (n.className=='sjcl-delkey-button') + manage_key(n,false); +} + +function loaded() { + sjcl.random.startCollectors(); + document.body.addEventListener("click", click_body) + + inject_css(); + inject_textarea_hook(); + + /* Try to decrypt all encrypted nodes */ + find_encrypted_nodes(); +} + +window.addEventListener('load', loaded); -- cgit v1.2.3