summaryrefslogtreecommitdiff
path: root/sjcl-front.js
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2016-10-09 01:54:34 +0200
committerDirk Engling <erdgeist@erdgeist.org>2016-10-09 01:54:34 +0200
commitc932cd295d414f78c65167e7717b04f61e93122a (patch)
treeaad57f7461ad72ba4ebf4e411f112e1eacb26ad0 /sjcl-front.js
Initial tests
Diffstat (limited to 'sjcl-front.js')
-rw-r--r--sjcl-front.js264
1 files changed, 264 insertions, 0 deletions
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 @@
1function inject_css() {
2 var s = document.createElement('style');
3 s.setAttribute('type', 'text/css');
4 s.appendChild(document.createTextNode("\
5@font-face { \n\
6font-family: 'fontello'; src: url('data:application/octet-stream;base64,d09GRgABAAAAAAr8AA8AAAAAE2AAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIwleU9TLzIAAAGUAAAAQwAAAFY+IUiuY21hcAAAAdgAAABLAAABcOkpu61jdnQgAAACJAAAABMAAAAgBuP/BGZwZ20AAAI4AAAFkAAAC3CKkZBZZ2FzcAAAB8gAAAAIAAAACAAAABBnbHlmAAAH0AAAAJgAAACY5Sc1UGhlYWQAAAhoAAAALgAAADYLN1RcaGhlYQAACJgAAAAbAAAAJAc8A1VobXR4AAAItAAAAAgAAAAIBi4AAGxvY2EAAAi8AAAABgAAAAYATAAAbWF4cAAACMQAAAAgAAAAIADGC7BuYW1lAAAI5AAAAXcAAALNzJ0cHnBvc3QAAApcAAAAIgAAADMI79TbcHJlcAAACoAAAAB6AAAAhuVBK7x4nGNgZGBg4GIwYLBjYMpJLMlj4HNx8wlhkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAKVkFSAB4nGNgZBZnnMDAysDAVMW0h4GBoQdCMz5gMGRkAooysDIzYAUBaa4pDA4vGF8wMgf9z2KIYo5kmAYUZgTJAQDHEAtNAHicY2BgYGVgYGAGYh0gZmFgYAxhYGQAAT+gKCNYnJmBCyzOwqAEVsMCEn/B+P8/jATyWcAkAyMbwyjgAZMyUB44rCCYgREAMEgJdQB4nGNgQAMSEMgc+T8LhAESsgPrAHicrVZpd9NGFB15SZyELCULLWphxMRpsEYmbMGACUGyYyBdnK2VoIsUO+m+8Ynf4F/zZNpz6Dd+Wu8bLySQtOdwmpOjd+fN1czbZRJaktgL65GUmy/F1NYmjew8CemGTctRfCg7eyFlisnfBVEQrZbatx2HREQiULWusEQQ+x5ZmmR86FFGy7akV03KLT3pLlvjQb1V334aOsqxO6GkZjN0aD2yJVUYVaJIpj1S0qZlqPorSSu8v8LMV81QwohOImm8GcbQSN4bZ7TKaDW24yiKbLLcKFIkmuFBFHmU1RLn5IoJDMoHzZDyyqcR5cP8iKzYo5xWsEu20/y+L3mndzk/sV9vUbbkQB/Ijuzg7HQlX4RbW2HctJPtKFQRdtd3QmzZ7FT/Zo/ymkYDtysyvdCMYKl8hRArP6HM/iFZLZxP+ZJHo1qykRNB62VO7Es+gdbjiClxzRhZ0N3RCRHU/ZIzDPaYPh788d4plgsTAngcy3pHJZwIEylhczRJ2jByYCVliyqp9a6YOOV1WsRbwn7t2tGXzmjjUHdiPFsPHVs5UcnxaFKnmUyd2knNoykNopR0JnjMrwMoP6JJXm1jNYmVR9M4ZsaERCICLdxLU0EsO7GkKQTNoxm9uRumuXYtWqTJA/Xco/f05la4udNT2g70s0Z/VqdiOtgL0+lp5C/xadrlIkXp+ukZfkziQdYCMpEtNsOUgwdv/Q7Sy9eWHIXXBtju7fMrqH3WRPCkAfsb0B5P1SkJTIWYVYhWQGKta1mWydWsFqnI1HdDmla+rNMEinIcF8e+jHH9XzMzlpgSvt+J07MjLj1z7UsI0xx8m3U9mtepxXIBcWZ5TqdZlu/rNMfyA53mWZ7X6QhLW6ejLD/UaYHlRzodY3lBC5p038GQizDkAg6QMISlA0NYXoIhLBUMYbkIQ1gWYQjLJRjC8mMYwnIZhrC8rGXV1FNJ49qZWAZsQmBijh65zEXlaiq5VEK7aFRqQ54SbpVUFM+qf2WgXjzyhjmwFkiXyJpfMc6Vj0bl+NYVLW8aO1fAsepvH472OfFS1ouFPwX/1dZUJb1izcOTq/Abhp5sJ6o2qXh0TZfPVT26/l9UVFgL9BtIhVgoyrJscGcihI86nYZqoJVDzGzMPLTrdcuan8P9NzFCFlD9+DcUGgvcg05ZSVnt4KzV19uy3DuDcjgTLEkxN/P6VvgiI7PSfpFZyp6PfB5wBYxKZdhqA60VvNknMQ+Z3iTPBHFbUTZI2tjOBIkNHPOAefOdBCZh6qoN5E7hhg34BWFuwXknXKJ6oyyH7kXs8yik/Fun4kT2qGiMwLPZG2Gv70LKb3EMJDT5pX4MVBWhqRg1FdA0Um6oBl/G2bptQsYO9CMqdsOyrOLDxxb3lZJtGYR8pIjVo6Of1l6iTqrcfmYUl++dvgXBIDUxf3vfdHGQyrtayTJHbQNTtxqVU9eaQ+NVh+rmUfW94+wTOWuabronHnpf06rbwcVcLLD2bQ7SUiYX1PVhhQ2iy8WlUOplNEnvuAcYFhjQ71CKjf+r+th8nitVhdFxJN9O1LfR52AM/A/Yf0f1A9D3Y+hyDS7P95oTn2704WyZrqIX66foNzBrrblZugbc0HQD4iFHrY64yg18pwZxeqS5HOkh4GPdFeIBwCaAxeAT3bWM5lMAo/mMOT7A58xh0GQOgy3mMNhmzhrADnMY7DKHwR5zGHzBnHWAL5nDIGQOg4g5DJ4wJwB4yhwGXzGHwdfMYfANc+4DfMscBjFzGCTMYbCv6dYwzC1e0F2gtkFVoANTT1jcw+JQU2XI/o4Xhv29Qcz+wSCm/qjp9pD6Ey8M9WeDmPqLQUz9VdOdIfU3Xhjq7wYx9Q+DmPpMvxjLZQa/jHyXCgeUXWw+5++J9w/bxUC5AAEAAf//AA8AAQAAAAACRgNZACEAMkAvGRMCAwIBRwADAgECAwFtBQEBAAIBAGsAAABuAAICBFgABAQMAkkUFBMTIzIGBRorAREUIyEiNRE0MyE1NCYiBh0BIzUzPgEeARczFzc2HgMCRiD9+iAgAYVKa05hAQmCsH4IAQIPCwgSBgYBtf6cICABZCHCMy4yNH2AV2wCfFmsAQECAggMAHicY2BkYGAA4mmzOM7F89t8ZeBmfgEUYbgi0cGPTDO/YI4EUhwMTCAeABkzCPUAAHicY2BkYGAO+p8FJF8wMIBJRgZUwAQAXPYDmQAD6AAAAkYAAAAAAAAATAAAAAEAAAACACIAAQAAAAAAAgAMABwAcwAAAEMLcAAAAAB4nHWQy07CQBSG/5GLCokaTdw6KwMxlksiCxISEgxsdEMMW1NKaUtKh0wHEl7Dd/BhfAmfxZ92MAZim+l855szZ04HwDW+IZA/Txw5C5wxyvkEp+hZLtA/Wy6SXyyXUMWb5TL9u+UKHhBYruIGH6wgiueMFvi0LHAlLi2f4ELcWS7QP1ouknuWS7gVr5bL9J7lCiYitVzFvfgaqNVWR0FoZG1Ql+1mqyOnW6moosSNpbs2odKp7Mu5Sowfx8rx1HLPYz9Yx67eh/t54us0UolsOc29GvmJr13jz3bV003QNmYu51ot5dBmyJVWC98zTmjMqtto/D0PAyissIVGxKsKYSBRo61zbqOJFjqkKTMkM/OsCAlcxDQu1twRZisp4z7HnFFC6zMjJjvw+F0e+TEp4P6YVfTR6mE8Ie3OiDIv2ZfD7g6zRqQky3QzO/vtPcWGp7VpDXftutRZVxLDgxqS97FbW9B49E52K4a2iwbff/7vB+NphE8AeJxjYGKAAC4G7ICJkYmRmYEzJz85Wze/IDWPgQEAG+UDpwAAeJxj8N7BcCIoYiMjY1/kBsadHAwcDMkFGxlYnTYxMDJogRibuZgYOSAsPgYwi81pF9MBoDQnkM3utIvBAcJmZnDZqMLYERixwaEjYiNzistGNRBvF0cDAyOLQ0dySARISSQQbOZhYuTR2sH4v3UDS+9GJgYXAAx2I/QAAA==') format('woff'); } \n\
7.sjcl-undecrypted { background-color: rgba(255, 0, 0, 0.2); cursor: pointer; } \n\
8.sjcl-decrypted { background-color: rgba(0, 255, 0, 0.2); } \n\
9.sjcl-hook { display: inline-block; position: relative; vertical-align: top; } \n\
10.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\
11.sjcl-dropdown, .sjcl-addkey-sub { display: none; border-radius: 0.4em; } \n\
12.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\
13.sjcl-decrypt, .sjcl-usekey, .sjcl-addkey { padding: 2px 3px 2px 6px; cursor: pointer; border-radius: 0 0 0.4em 0.4em; } \n\
14.sjcl-decrypt:hover, .sjcl-usekey:hover, .sjcl-addkey:hover { border-radius: 0.4em; background-color: #f1f1f1; } \n\
15.sjcl-addkey:hover .sjcl-addkey-sub { display: block; } \n\
16.sjcl-addkey-sub label { display: inline-block !important; width: 40%; } \n\
17.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\
18.sjcl-delkey-button { border: solid 0.1em red; } \n\
19.sjcl-addkey-button:hover { background-color: lime; } \n\
20.sjcl-delkey-button:hover { background-color: red; } \
21"));
22 document.getElementsByTagName('head')[0].appendChild(s);
23}
24
25function inject_textarea_hook() {
26 // Get rid of old hooks
27 var hooks = document.getElementsByClassName("sjcl-hook");
28 for(var i = hooks.length - 1; i >= 0; i--) {
29 var n = hooks.item(i);
30 n.parentNode.removeChild(n);
31 }
32 // And insert new hooks
33 var walk=document.createTreeWalker(document.body,NodeFilter.SHOW_ELEMENT,null,false);
34 while(n = walk.nextNode())
35 if (n.type == 'textarea') {
36 var keys = list_keys();
37 var h = document.createElement('div');
38 h.className='sjcl-hook';
39 var dd = '<div class="sjcl-dropdown">'
40 for (var i in keys)
41 dd += '<div class="sjcl-usekey" keyname="'+keys[i]+'">Encrypt with: "'+keys[i]+'"</div>';
42 h.innerHTML = dd + '<div class="sjcl-decrypt">Decrypt</div><hr/><div class="sjcl-addkey">Manage keys<div class="sjcl-addkey-sub"><label>Name:</label><input class="sjcl-addkey-name" type="text"/><label>Key:</label><input class="sjcl-addkey-key" type="password"/><div class="sjcl-addkey-button">add!</div><div class="sjcl-delkey-button">delete!</div></div></div></div>';
43 n.parentNode.insertBefore(h,n.nextSibling);
44 }
45}
46
47function decrypt_string(text, target_node, do_prompt) {
48 var rp, ct, plain, key_name, key, do_store = false;
49
50 /* Clear sjcl classes from target node */
51 target_node.className = target_node.className.replace(/(^| )sjcl-[a-z]+($| )/, '');
52
53 /* Check sjcl signature and bail out */
54 if (text.substring(0,7) != 'sjcl://')
55 throw 'Text is not encrypted.';
56
57 /* Skip signature */
58 text = text.substring(7);
59 /* Retrieve key name */
60 try {
61 ct = JSON.parse(text);
62 } catch (e) {
63 target_node.className += ' sjcl-garbled';
64 throw 'Cipher text is garbled.';
65 }
66 try {
67 key_name = sjcl.codec.utf8String.fromBits(sjcl.codec.base64.toBits(ct.adata));
68 } catch(e) {
69 target_node.className += ' sjcl-garbled';
70 throw 'Can not extract key name';
71 }
72
73 key = retrieve_key(key_name);
74 if (!key && do_prompt) {
75 key = prompt( 'Enter password for the key ' + key_name);
76 do_store = true;
77 }
78 if (!key) {
79 target_node.className += ' sjcl-undecrypted';
80 throw 'You need the key "' + key_name + '" to decrypt.';
81 }
82 try {
83 plain = sjcl.decrypt(key, text, {}, rp);
84 } catch (e) {
85 if (do_store)
86 alert('Could not decrypt');
87 target_node.className += ' sjcl-undecrytable';
88 throw 'Your key "' + key_name + '" can not decrypt.';
89 }
90 if (do_store)
91 store_key(key_name, key);
92
93 target_node.className += ' sjcl-decrypted';
94 return plain.replace(/\s+$/gm,'');
95}
96
97function decrypt_element(n) {
98 try {
99 n.data = decrypt_string(n.data, n.parentElement, false);
100 } catch(e) {
101 n.parentElement.ciphertext = n.data;
102 n.parentElement.ciphertext_node = n;
103 n.data = e;
104 }
105}
106
107function decrypt_undecrypted(n) {
108 if(!n.ciphertext || n.ciphertext_node==null)
109 return;
110 try {
111 n.ciphertext_node.data = decrypt_string(n.ciphertext, n, true);
112 inject_textarea_hook();
113 find_undecrypted_nodes();
114 } catch(e) {
115 n.ciphertext_node.data = e;
116 }
117}
118
119function decrypt_textarea(n) {
120 try {
121 n.value = decrypt_string(n.value, n, false);
122 } catch(e) {
123 alert(e);
124 }
125}
126
127function encrypt_textarea(n,name,key) {
128 var t = n.value + (" ".repeat(17 + Math.floor((Math.random() * 64))));
129 var p = {adata:name, iter:1000, mode:'ccm', ts:128, ks:256, iter: 1000 };
130 n.value = 'sjcl://'+sjcl.encrypt(key, t || '', p);
131 n.className = n.className.replace(/(^| )sjcl-[a-z]+($| )/, '')+' sjcl-undecrypted';
132}
133
134/* Auto node operation */
135function find_encrypted_nodes() {
136 var n, walk = document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT,null,false);
137 while(n = walk.nextNode())
138 if (n.data.substring(0,7) == 'sjcl://')
139 decrypt_element(n, false);
140}
141
142function find_undecrypted_nodes() {
143 var undec = document.getElementsByClassName("sjcl-undecrypted");
144 for(var i = 0; i < undec.length; i++)
145 decrypt_undecrypted(undec.item(i), false);
146}
147
148/* Passphrase accessors */
149function manage_key(n, do_add) {
150 var name = n.parentNode.getElementsByClassName('sjcl-addkey-name')[0];
151 var key = n.parentNode.getElementsByClassName('sjcl-addkey-key')[0];
152 if (!name.value)
153 alert( 'Missing key name.');
154 else
155 store_key(name.value, do_add ? key.value : '');
156 inject_textarea_hook();
157}
158
159/* Try to use the most logliving method to store key array */
160function store_key(name, key) {
161 /* Collect keys from all storage methods */
162 var obj = {};
163 try {
164 obj = JSON.parse(localStorage['frab-sjcl']);
165 } catch(e) {}
166 try {
167 var sso = JSON.parse(sessionStorage['frab-sjcl']);
168 for (var k in sso) { obj[k] = sso[k]; }
169 delete sessionStorage['frab-sjcl'];
170 } catch(e) {}
171 try {
172 wo = JSON.parse(window['frab-sjcl']);
173 for (var k in wo) { obj[k] = wo[k]; }
174 delete window['frab-sjcl'];
175 } catch(e) {}
176
177 if (key)
178 obj[name] = key;
179 else
180 delete obj[name];
181 var out = JSON.stringify(obj);
182
183 /* Try to store in local/sessionStorage and fall back to
184 the window object, so at least we can decrypt all elements
185 locally */
186 try {
187 localStorage['frab-sjcl'] = out;
188 if (localStorage['frab-sjcl'] != out)
189 throw 0;
190 return;
191 } catch(e) {}
192 try {
193 sessionStorage['frab-sjcl'] = out;
194 if (sessionStorage['frab-sjcl'] != out)
195 throw 0;
196 return;
197 } catch(e) {}
198
199 window['frab-sjcl'] = out;
200}
201
202function retrieve_key(name) {
203 try {
204 var obj = JSON.parse(localStorage['frab-sjcl'] || '{}');
205 if (obj && obj[name])
206 return obj[name];
207 } catch(e) {}
208 try {
209 var obj = JSON.parse(sessionStorage['frab-sjcl'] || '{}');
210 if (obj && obj[name])
211 return obj[name];
212 } catch(e) {}
213 try {
214 var obj = JSON.parse(window['frab-sjcl'] || '{}');
215 if (obj && obj[name])
216 return obj[name];
217 } catch(e) {}
218 return '';
219}
220
221function list_keys() {
222 var key_list = [];
223 try {
224 key_list = (Object.keys(JSON.parse(localStorage['frab-sjcl'])));
225 } catch(e) {}
226 try {
227 key_list = key_list.concat(Object.keys(JSON.parse(sessionStorage['frab-sjcl'])));
228 } catch(e) {}
229 try {
230 key_list = key_list.concat(Object.keys(JSON.parse(window['frab-sjcl'])));
231 } catch(e) {}
232 return key_list;
233}
234
235/* Event handler plumbing */
236function click_body(e) {
237 var n = e.target;
238
239 if (n.className=='sjcl-usekey') {
240 var name = n.getAttribute('keyname');
241 encrypt_textarea(n.parentNode.parentNode.previousSibling, name, retrieve_key(name));
242 }
243 if (n.className=='sjcl-decrypt')
244 decrypt_textarea(n.parentNode.parentNode.previousSibling);
245 if ((' '+n.className+' ').indexOf(' sjcl-undecrypted ') > -1)
246 decrypt_undecrypted(n);
247 if (n.className=='sjcl-addkey-button')
248 manage_key(n,true);
249 if (n.className=='sjcl-delkey-button')
250 manage_key(n,false);
251}
252
253function loaded() {
254 sjcl.random.startCollectors();
255 document.body.addEventListener("click", click_body)
256
257 inject_css();
258 inject_textarea_hook();
259
260 /* Try to decrypt all encrypted nodes */
261 find_encrypted_nodes();
262}
263
264window.addEventListener('load', loaded);