aboutsummaryrefslogtreecommitdiffstats
path: root/js/crypto.js
blob: a144e03eaedc2d7902b428992dc8abe3996be0a5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
function str_rot13 (str) {
  // http://kevin.vanzonneveld.net
  // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
  // +   improved by: Ates Goral (http://magnetiq.com)
  // +   bugfixed by: Onno Marsman
  // +   improved by: Rafa? Kukawski (http://blog.kukawski.pl)
  // *     example 1: str_rot13('Kevin van Zonneveld');
  // *     returns 1: 'Xriva ina Mbaariryq'
  // *     example 2: str_rot13('Xriva ina Mbaariryq');
  // *     returns 2: 'Kevin van Zonneveld'
  // *     example 3: str_rot13(33);
  // *     returns 3: '33'
	return (str + '').replace(/[a-z]/gi, function (s) {
		return String.fromCharCode(s.charCodeAt(0) + (s.toLowerCase() < 'n' ? 13 : -13));
	});
}


// Arrays for pluggable encryptors/decryptors

var red_encryptors = new Array();
var red_decryptors = new Array();

// We probably just want the element where the text is and find it ourself. e.g. if 
// there is highlighted text use it, otherwise use the entire text.
// So the third element may be useless. Fix also in view/tpl/jot.tpl before 
// adding to all the editor templates and enabling the feature

// Should probably do some input sanitising and dealing with bbcode, hiding key text, and displaying
// results in a lightbox and/or popup form are left as an exercise for the reader. 


function red_encrypt(alg, elem,text) {
	var enc_text = '';
	var newdiv = '';

	if(typeof tinyMCE !== "undefined")
		tinyMCE.triggerSave(false,true);

	var text = $(elem).val();

	// key and hint need to be localised

	var enc_key = prompt(aStr['passphrase']);

	// If you don't provide a key you get rot13, which doesn't need a key
	// but consequently isn't secure.  

	if(! enc_key)
		alg = 'rot13';

	if((alg == 'rot13') || (alg == 'triple-rot13'))
		newdiv = "[crypt alg='rot13']" + str_rot13(text) + '[/crypt]';

	if(alg == 'aes256') {

			// This is the prompt we're going to use when the receiver tries to open it.
			// Maybe "Grandma's maiden name" or "our secret place" or something. 

			var enc_hint = prompt(aStr['passhint']);

			enc_text = CryptoJS.AES.encrypt(text,enc_key);

			encrypted = enc_text.toString();

			newdiv = "[crypt alg='aes256' hint='" + enc_hint + "']" + encrypted + '[/crypt]';
	}
	if(alg == 'rabbit') {

			// This is the prompt we're going to use when the receiver tries to open it.
			// Maybe "Grandma's maiden name" or "our secret place" or something. 

			var enc_hint = prompt(aStr['passhint']);

			enc_text = CryptoJS.Rabbit.encrypt(text,enc_key);
			encrypted = enc_text.toString();

			newdiv = "[crypt alg='rabbit' hint='" + enc_hint + "']" + encrypted + '[/crypt]';
	}
	if(alg == '3des') {

			// This is the prompt we're going to use when the receiver tries to open it.
			// Maybe "Grandma's maiden name" or "our secret place" or something. 

			var enc_hint = prompt(aStr['passhint']);

			enc_text = CryptoJS.TripleDES.encrypt(text,enc_key);
			encrypted = enc_text.toString();

			newdiv = "[crypt alg='3des' hint='" + enc_hint + "']" + encrypted + '[/crypt]';
	}
	if((red_encryptors.length) && (! newdiv.length)) {
		for(var i = 0; i < red_encryptors.length; i ++) {
			newdiv = red_encryptors[i](alg,text);
			if(newdiv.length)
				break;
		}
	}

	enc_key = '';

//	alert(newdiv);

	// This might be a comment box on a page with a tinymce editor
	// so check if there is a tinymce editor but also check the display
	// property of our source element - because a tinymce instance
	// will have display "none". If a normal textarea such as in a comment
	// box has display "none" you wouldn't be able to type in it.
	
	if($(elem).css('display') == 'none' && typeof tinyMCE !== "undefined") {
		tinyMCE.activeEditor.setContent(newdiv);
	}
	else {
		$(elem).val(newdiv);
	}

//	textarea = document.getElementById(elem);
//	if (document.selection) {
//		textarea.focus();
//		selected = document.selection.createRange();
//		selected.text = newdiv;
//	} else if (textarea.selectionStart || textarea.selectionStart == "0") {
//		var start = textarea.selectionStart;
//		var end = textarea.selectionEnd;
//		textarea.value = textarea.value.substring(0, start) + newdiv + textarea.value.substring(end, textarea.value.length);
//	}
}

function red_decrypt(alg,hint,text,elem) {

	var dec_text = '';

	if(alg == 'rot13' || alg == 'triple-rot13')
		dec_text = str_rot13(text);
	else {
		var enc_key = prompt((hint.length) ? hint : aStr['passphrase']);
	}

	if(alg == 'aes256') {
		dec_text = CryptoJS.AES.decrypt(text,enc_key);
	}
	if(alg == 'rabbit') {
		dec_text = CryptoJS.Rabbit.decrypt(text,enc_key);
	}
	if(alg == '3des') {
		dec_text = CryptoJS.TripleDES.decrypt(text,enc_key);
	}

	if((red_decryptors.length) && (! dec_text.length)) {
		for(var i = 0; i < red_decryptors.length; i ++) {
			dec_text = red_decryptors[i](alg,text,enc_key);
			if(dec_text.length)
				break;
		}
	}

	enc_key = '';

	// Not sure whether to drop this back in the conversation display.
	// It probably needs a lightbox or popup window because any conversation 
	// updates could 
	// wipe out the text and make you re-enter the key if it was in the
	// conversation. For now we do that so you can read it.

	var dec_result = dec_text.toString(CryptoJS.enc.Utf8);
	delete dec_text;

	// incorrect decryptions *usually* but don't always have zero length
	// If the person typo'd let them try again without reloading the page
	// otherwise they'll have no "padlock" to click to try again.

	if(dec_result.length) {
		$(elem).html(b2h(dec_result));
		dec_result = '';
	}
}
	
	



function base64_encode (data) {
  // http://kevin.vanzonneveld.net
  // +   original by: Tyler Akins (http://rumkin.com)
  // +   improved by: Bayron Guevara
  // +   improved by: Thunder.m
  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // +   bugfixed by: Pellentesque Malesuada
  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // +   improved by: Rafa? Kukawski (http://kukawski.pl)
  // *     example 1: base64_encode('Kevin van Zonneveld');
  // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
  // mozilla has this native
  // - but breaks in 2.0.0.12!
  //if (typeof this.window['btoa'] === 'function') {
  //    return btoa(data);
  //}
	var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
    ac = 0,
    enc = "",
    tmp_arr = [];

	if (!data) {
		return data;
	}

	do { // pack three octets into four hexets
		o1 = data.charCodeAt(i++);
		o2 = data.charCodeAt(i++);
		o3 = data.charCodeAt(i++);

		bits = o1 << 16 | o2 << 8 | o3;

		h1 = bits >> 18 & 0x3f;
		h2 = bits >> 12 & 0x3f;
		h3 = bits >> 6 & 0x3f;
		h4 = bits & 0x3f;

    // use hexets to index into b64, and append result to encoded string
		tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
	} while (i < data.length);

	enc = tmp_arr.join('');

	var r = data.length % 3;

	return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);

}


function base64_decode (data) {
  // http://kevin.vanzonneveld.net
  // +   original by: Tyler Akins (http://rumkin.com)
  // +   improved by: Thunder.m
  // +      input by: Aman Gupta
  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // +   bugfixed by: Onno Marsman
  // +   bugfixed by: Pellentesque Malesuada
  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // +      input by: Brett Zamir (http://brett-zamir.me)
  // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
  // *     returns 1: 'Kevin van Zonneveld'
  // mozilla has this native
  // - but breaks in 2.0.0.12!
  //if (typeof this.window['atob'] === 'function') {
  //    return atob(data);
  //}
	var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
    ac = 0,
    dec = "",
    tmp_arr = [];

	if (!data) {
		return data;
	}

	data += '';

	do { // unpack four hexets into three octets using index points in b64
		h1 = b64.indexOf(data.charAt(i++));
		h2 = b64.indexOf(data.charAt(i++));
		h3 = b64.indexOf(data.charAt(i++));
		h4 = b64.indexOf(data.charAt(i++));

		bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;

		o1 = bits >> 16 & 0xff;
		o2 = bits >> 8 & 0xff;
		o3 = bits & 0xff;

		if (h3 == 64) {
			tmp_arr[ac++] = String.fromCharCode(o1);
		} else if (h4 == 64) {
			tmp_arr[ac++] = String.fromCharCode(o1, o2);
		} else {
			tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
		}
	} while (i < data.length);

	dec = tmp_arr.join('');

	return dec;
}