megar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +11 -0
  5. data/CHANGELOG +5 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +11 -0
  8. data/LICENSE +22 -0
  9. data/README.rdoc +218 -0
  10. data/Rakefile +33 -0
  11. data/bin/megar +16 -0
  12. data/lib/extensions/math.rb +13 -0
  13. data/lib/js_ref_impl/_README +9 -0
  14. data/lib/js_ref_impl/base64_1.js +83 -0
  15. data/lib/js_ref_impl/crypto_5.js +1795 -0
  16. data/lib/js_ref_impl/download_8.js +867 -0
  17. data/lib/js_ref_impl/hex_1.js +76 -0
  18. data/lib/js_ref_impl/index_9.js +666 -0
  19. data/lib/js_ref_impl/js.manifest +115 -0
  20. data/lib/js_ref_impl/rsa_1.js +456 -0
  21. data/lib/js_ref_impl/sjcl_1.js +1 -0
  22. data/lib/js_ref_impl/upload_10.js +691 -0
  23. data/lib/megar.rb +11 -0
  24. data/lib/megar/catalog.rb +5 -0
  25. data/lib/megar/catalog/catalog_item.rb +90 -0
  26. data/lib/megar/catalog/file.rb +14 -0
  27. data/lib/megar/catalog/files.rb +13 -0
  28. data/lib/megar/catalog/folder.rb +20 -0
  29. data/lib/megar/catalog/folders.rb +28 -0
  30. data/lib/megar/connection.rb +84 -0
  31. data/lib/megar/crypto.rb +399 -0
  32. data/lib/megar/exception.rb +55 -0
  33. data/lib/megar/session.rb +157 -0
  34. data/lib/megar/shell.rb +87 -0
  35. data/lib/megar/version.rb +3 -0
  36. data/megar.gemspec +30 -0
  37. data/spec/fixtures/crypto_expectations/sample_user.json +109 -0
  38. data/spec/spec_helper.rb +24 -0
  39. data/spec/support/crypto_expectations_helper.rb +44 -0
  40. data/spec/support/mocks_helper.rb +22 -0
  41. data/spec/unit/catalog/file_spec.rb +31 -0
  42. data/spec/unit/catalog/files_spec.rb +26 -0
  43. data/spec/unit/catalog/folder_spec.rb +28 -0
  44. data/spec/unit/catalog/folders_spec.rb +49 -0
  45. data/spec/unit/connection_spec.rb +50 -0
  46. data/spec/unit/crypto_spec.rb +476 -0
  47. data/spec/unit/exception_spec.rb +35 -0
  48. data/spec/unit/extensions/math_spec.rb +21 -0
  49. data/spec/unit/session_spec.rb +146 -0
  50. data/spec/unit/shell_spec.rb +18 -0
  51. metadata +238 -0
@@ -0,0 +1,1795 @@
1
+ window.URL = window.URL || window.webkitURL;
2
+ var have_ab = typeof ArrayBuffer != 'undefined' && typeof DataView != 'undefined';
3
+ var use_workers = have_ab && typeof Worker != 'undefined';
4
+
5
+ if ((navigator.appVersion.indexOf('Safari') > 0) && (navigator.appVersion.indexOf('Version/5') > 0))
6
+ {
7
+ use_workers=false;
8
+ have_ab=false;
9
+ }
10
+
11
+ var ssl_off = [ 'Firefox/14', 'Firefox/15', 'Firefox/17', 'Safari', 'Firefox/16' ];
12
+ var ssl_opt = [ 'Chrome/' ];
13
+
14
+ function ssl_needed()
15
+ {
16
+ for (var i = ssl_opt.length; i--; ) if (navigator.userAgent.indexOf(ssl_opt[i]) >= 0) return 0;
17
+ for (var i = ssl_off.length; i--; ) if (navigator.userAgent.indexOf(ssl_off[i]) >= 0) return -1;
18
+ return 1;
19
+ }
20
+
21
+ var use_ssl = ssl_needed();
22
+ if (!use_ssl && localStorage.use_ssl) use_ssl = 1;
23
+ else use_ssl++;
24
+
25
+ var chromehack = navigator.appVersion.indexOf('Chrome/');
26
+ chromehack = chromehack >= 0 && parseInt(navigator.appVersion.substr(chromehack+7)) > 21;
27
+
28
+ // keyboard/mouse entropy
29
+ eventsCollect();
30
+
31
+ var EINTERNAL = -1;
32
+ var EARGS = -2;
33
+ var EAGAIN = -3;
34
+ var ERATELIMIT = -4;
35
+ var EFAILED = -5;
36
+ var ETOOMANY = -6; // too many IP addresses
37
+ var ERANGE = -7; // file packet out of range
38
+ var EEXPIRED = -8;
39
+
40
+ // FS access errors
41
+ var ENOENT = -9;
42
+ var ECIRCULAR = -10;
43
+ var EACCESS = -11;
44
+ var EEXIST = -12;
45
+ var EINCOMPLETE = -13;
46
+
47
+ // crypto errors
48
+ var EKEY = -14;
49
+
50
+ // user errors
51
+ var ESID = -15;
52
+ var EBLOCKED = -16;
53
+ var EOVERQUOTA = -17;
54
+ var ETEMPUNAVAIL = -18;
55
+ var ETOOMANYCONNECTIONS = -19;
56
+
57
+ function benchmark()
58
+ {
59
+ var a = Array(1048577).join('a');
60
+
61
+ var ab = str_to_ab(a);
62
+
63
+ var ab8 = new Uint8Array(ab);
64
+
65
+ var aes = new sjcl.cipher.aes([0,1,2,3]);
66
+
67
+ t = new Date().getTime();
68
+ for (var i = 16; i--; ) encrypt_ab_ctr(aes,ab8,[1,2],30000);
69
+ t = new Date().getTime()-t;
70
+
71
+ console.log((a.length*16/1024)/(t/1000) + " KB/s");
72
+ }
73
+
74
+ var seqno = rand(0x100000000);
75
+
76
+ // compute final MAC from block MACs
77
+ function condenseMacs(macs,key)
78
+ {
79
+ var i, aes;
80
+ mac = [0,0,0,0];
81
+
82
+ aes = new sjcl.cipher.aes([key[0],key[1],key[2],key[3]]);
83
+
84
+ for (i = 0; i < macs.length; i++)
85
+ {
86
+ mac[0] ^= macs[i][0];
87
+ mac[1] ^= macs[i][1];
88
+ mac[2] ^= macs[i][2];
89
+ mac[3] ^= macs[i][3];
90
+
91
+ mac = aes.encrypt(mac);
92
+ }
93
+
94
+ return mac;
95
+ }
96
+
97
+ // convert user-supplied password array
98
+ function prepare_key(a)
99
+ {
100
+ var i, j, r;
101
+ var pkey = [0x93C467E3,0x7DB0C7A4,0xD1BE3F81,0x0152CB56];
102
+
103
+ for (r = 65536; r--; )
104
+ {
105
+ for (j = 0; j < a.length; j += 4)
106
+ {
107
+ key = [0,0,0,0];
108
+
109
+ for (i = 0; i < 4; i++) if (i+j < a.length) key[i] = a[i+j];
110
+
111
+ aes = new sjcl.cipher.aes(key);
112
+ pkey = aes.encrypt(pkey);
113
+ }
114
+ }
115
+
116
+ return pkey;
117
+ }
118
+
119
+ // prepare_key with string input
120
+ function prepare_key_pw(password)
121
+ {
122
+ return prepare_key(str_to_a32(password));
123
+ }
124
+
125
+ var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
126
+ var b64a = b64.split('');
127
+
128
+
129
+ // unsubstitute standard base64 special characters, restore padding
130
+ function base64urldecode(data)
131
+ {
132
+ data += '=='.substr((2-data.length*3)&3)
133
+
134
+ if (typeof atob === 'function')
135
+ {
136
+ data = data.replace(/\-/g,'+').replace(/_/g,'/').replace(/,/g,'');
137
+
138
+ try {
139
+ return atob(data);
140
+ } catch (e) {
141
+ return '';
142
+ }
143
+ }
144
+
145
+ // http://kevin.vanzonneveld.net
146
+ // + original by: Tyler Akins (http://rumkin.com)
147
+ // + improved by: Thunder.m
148
+ // + input by: Aman Gupta
149
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
150
+ // + bugfixed by: Onno Marsman
151
+ // + bugfixed by: Pellentesque Malesuada
152
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
153
+ // + input by: Brett Zamir (http://brett-zamir.me)
154
+ // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
155
+ // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
156
+ // * returns 1: 'Kevin van Zonneveld'
157
+ // mozilla has this native
158
+ // - but breaks in 2.0.0.12!
159
+ //if (typeof this.window['atob'] == 'function') {
160
+ // return atob(data);
161
+ //}
162
+ var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
163
+ ac = 0,
164
+ dec = "",
165
+ tmp_arr = [];
166
+
167
+ if (!data) {
168
+ return data;
169
+ }
170
+
171
+ data += '';
172
+
173
+ do { // unpack four hexets into three octets using index points in b64
174
+ h1 = b64.indexOf(data.charAt(i++));
175
+ h2 = b64.indexOf(data.charAt(i++));
176
+ h3 = b64.indexOf(data.charAt(i++));
177
+ h4 = b64.indexOf(data.charAt(i++));
178
+
179
+ bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
180
+
181
+ o1 = bits >> 16 & 0xff;
182
+ o2 = bits >> 8 & 0xff;
183
+ o3 = bits & 0xff;
184
+
185
+ if (h3 == 64) {
186
+ tmp_arr[ac++] = String.fromCharCode(o1);
187
+ } else if (h4 == 64) {
188
+ tmp_arr[ac++] = String.fromCharCode(o1, o2);
189
+ } else {
190
+ tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
191
+ }
192
+ } while (i < data.length);
193
+
194
+ dec = tmp_arr.join('');
195
+
196
+ return dec;
197
+ }
198
+
199
+ // substitute standard base64 special characters to prevent JSON escaping, remove padding
200
+ function base64urlencode(data)
201
+ {
202
+ if (typeof btoa === 'function') return btoa(data).replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,'');
203
+
204
+ var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
205
+ ac = 0,
206
+ enc = "",
207
+ tmp_arr = [];
208
+
209
+ do { // pack three octets into four hexets
210
+ o1 = data.charCodeAt(i++);
211
+ o2 = data.charCodeAt(i++);
212
+ o3 = data.charCodeAt(i++);
213
+
214
+ bits = o1 << 16 | o2 << 8 | o3;
215
+
216
+ h1 = bits >> 18 & 0x3f;
217
+ h2 = bits >> 12 & 0x3f;
218
+ h3 = bits >> 6 & 0x3f;
219
+ h4 = bits & 0x3f;
220
+
221
+ // use hexets to index into b64, and append result to encoded string
222
+ tmp_arr[ac++] = b64a[h1] + b64a[h2] + b64a[h3] + b64a[h4];
223
+ } while (i < data.length);
224
+
225
+ enc = tmp_arr.join('');
226
+ var r = data.length % 3;
227
+ return (r ? enc.slice(0, r - 3) : enc);
228
+ }
229
+
230
+ // array of 32-bit words to string (big endian)
231
+ function a32_to_str(a)
232
+ {
233
+ var b = '';
234
+
235
+ for (var i = 0; i < a.length*4; i++)
236
+ b = b+String.fromCharCode((a[i>>2] >>> (24-(i & 3)*8)) & 255);
237
+
238
+ return b;
239
+ }
240
+
241
+ function a32_to_base64(a)
242
+ {
243
+ return base64urlencode(a32_to_str(a));
244
+ }
245
+
246
+ // string to array of 32-bit words (big endian)
247
+ function str_to_a32(b)
248
+ {
249
+ var a = Array((b.length+3) >> 2);
250
+
251
+ for (var i = 0; i < b.length; i++) a[i>>2] |= (b.charCodeAt(i) << (24-(i & 3)*8));
252
+
253
+ return a;
254
+ }
255
+
256
+ function base64_to_a32(s)
257
+ {
258
+ return str_to_a32(base64urldecode(s));
259
+ }
260
+
261
+ // ArrayBuffer to binary string
262
+ function ab_to_str(ab)
263
+ {
264
+ var b = '', i;
265
+
266
+ if (have_ab)
267
+ {
268
+ var b = '';
269
+
270
+ var ab8 = new Uint8Array(ab);
271
+
272
+ for (i = 0; i < ab8.length; i++) b = b+String.fromCharCode(ab8[i]);
273
+ }
274
+ else
275
+ {
276
+ return ab.buffer;
277
+ }
278
+
279
+ return b;
280
+ }
281
+
282
+ // ArrayBuffer to binary string
283
+ function ab_to_base64(ab)
284
+ {
285
+ return base64urlencode(ab_to_str(ab));
286
+ }
287
+
288
+ // ArrayBuffer to binary with depadding
289
+ function ab_to_str_depad(ab)
290
+ {
291
+ var b, i;
292
+
293
+ if (have_ab)
294
+ {
295
+ b = '';
296
+
297
+ var ab8 = new Uint8Array(ab);
298
+
299
+ for (i = 0; i < ab8.length && ab8[i]; i++) b = b+String.fromCharCode(ab8[i]);
300
+ }
301
+ else
302
+ {
303
+ b = ab_to_str(ab);
304
+
305
+ for (i = b.length; i-- && !b.charCodeAt(i); );
306
+
307
+ b = b.substr(0,i+1);
308
+ }
309
+
310
+ return b;
311
+ }
312
+
313
+ // binary string to ArrayBuffer, 0-padded to AES block size
314
+ function str_to_ab(b)
315
+ {
316
+ var ab, i;
317
+
318
+ if (have_ab)
319
+ {
320
+ ab = new ArrayBuffer((b.length+15)&-16);
321
+ var ab8 = new Uint8Array(ab);
322
+
323
+ for (i = b.length; i--; ) ab8[i] = b.charCodeAt(i);
324
+
325
+ return ab;
326
+ }
327
+ else
328
+ {
329
+ b += Array(16-((b.length-1)&15)).join(String.fromCharCode(0));
330
+
331
+ ab = { buffer : b };
332
+ }
333
+
334
+ return ab;
335
+ }
336
+
337
+ // binary string to ArrayBuffer, 0-padded to AES block size
338
+ function base64_to_ab(a)
339
+ {
340
+ return str_to_ab(base64urldecode(a));
341
+ }
342
+
343
+ // encrypt ArrayBuffer in CTR mode, return MAC
344
+ function encrypt_ab_ctr(aes,ab,nonce,pos)
345
+ {
346
+ var ctr = [nonce[0],nonce[1],(pos/0x1000000000) >>> 0,(pos/0x10) >>> 0];
347
+ var mac = [ctr[0],ctr[1],ctr[0],ctr[1]];
348
+
349
+ var enc, i, j, len, v;
350
+
351
+ if (have_ab)
352
+ {
353
+ var data0, data1, data2, data3;
354
+
355
+ len = ab.buffer.byteLength-16;
356
+
357
+ var v = new DataView(ab.buffer);
358
+
359
+ for (i = 0; i < len; i += 16)
360
+ {
361
+ data0 = v.getUint32(i,false);
362
+ data1 = v.getUint32(i+4,false);
363
+ data2 = v.getUint32(i+8,false);
364
+ data3 = v.getUint32(i+12,false);
365
+
366
+ // compute MAC
367
+ mac[0] ^= data0;
368
+ mac[1] ^= data1;
369
+ mac[2] ^= data2;
370
+ mac[3] ^= data3;
371
+ mac = aes.encrypt(mac);
372
+
373
+ // encrypt using CTR
374
+ enc = aes.encrypt(ctr);
375
+ v.setUint32(i,data0 ^ enc[0],false);
376
+ v.setUint32(i+4,data1 ^ enc[1],false);
377
+ v.setUint32(i+8,data2 ^ enc[2],false);
378
+ v.setUint32(i+12,data3 ^ enc[3],false);
379
+
380
+ if (!(++ctr[3])) ctr[2]++;
381
+ }
382
+
383
+ if (i < ab.buffer.byteLength)
384
+ {
385
+ var fullbuf = new Uint8Array(ab.buffer);
386
+ var tmpbuf = new ArrayBuffer(16);
387
+ var tmparray = new Uint8Array(tmpbuf);
388
+
389
+ tmparray.set(fullbuf.subarray(i));
390
+
391
+ v = new DataView(tmpbuf);
392
+
393
+ enc = aes.encrypt(ctr);
394
+
395
+ data0 = v.getUint32(0,false);
396
+ data1 = v.getUint32(4,false);
397
+ data2 = v.getUint32(8,false);
398
+ data3 = v.getUint32(12,false);
399
+
400
+ mac[0] ^= data0;
401
+ mac[1] ^= data1;
402
+ mac[2] ^= data2;
403
+ mac[3] ^= data3;
404
+ mac = aes.encrypt(mac);
405
+
406
+ enc = aes.encrypt(ctr);
407
+ v.setUint32(0,data0 ^ enc[0],false);
408
+ v.setUint32(4,data1 ^ enc[1],false);
409
+ v.setUint32(8,data2 ^ enc[2],false);
410
+ v.setUint32(12,data3 ^ enc[3],false);
411
+
412
+ fullbuf.set(tmparray.subarray(0,j = fullbuf.length-i),i);
413
+ }
414
+ }
415
+ else
416
+ {
417
+ var ab32 = _str_to_a32(ab.buffer);
418
+
419
+ len = ab32.length-3;
420
+
421
+ for (i = 0; i < len; i += 4)
422
+ {
423
+ mac[0] ^= ab32[i];
424
+ mac[1] ^= ab32[i+1];
425
+ mac[2] ^= ab32[i+2];
426
+ mac[3] ^= ab32[i+3];
427
+ mac = aes.encrypt(mac);
428
+
429
+ enc = aes.encrypt(ctr);
430
+ ab32[i] ^= enc[0];
431
+ ab32[i+1] ^= enc[1];
432
+ ab32[i+2] ^= enc[2];
433
+ ab32[i+3] ^= enc[3];
434
+
435
+ if (!(++ctr[3])) ctr[2]++;
436
+ }
437
+
438
+ if (i < ab32.length)
439
+ {
440
+ var v = [0,0,0,0];
441
+
442
+ for (j = i; j < ab32.length; j++) v[j-i] = ab32[j];
443
+
444
+ mac[0] ^= v[0];
445
+ mac[1] ^= v[1];
446
+ mac[2] ^= v[2];
447
+ mac[3] ^= v[3];
448
+ mac = aes.encrypt(mac);
449
+
450
+ enc = aes.encrypt(ctr);
451
+ v[0] ^= enc[0];
452
+ v[1] ^= enc[1];
453
+ v[2] ^= enc[2];
454
+ v[3] ^= enc[3];
455
+
456
+ for (j = i; j < ab32.length; j++) ab32[j] = v[j-i];
457
+ }
458
+
459
+ ab.buffer = _a32_to_str(ab32,ab.buffer.length);
460
+ }
461
+
462
+ return mac;
463
+ }
464
+
465
+ function _str_to_a32(b)
466
+ {
467
+ var a = Array((b.length+3) >> 2);
468
+
469
+ if (typeof b == 'string')
470
+ {
471
+ for (var i = 0; i < b.length; i++)
472
+ a[i>>2] |= (b.charCodeAt(i) & 255) << (24-(i & 3)*8);
473
+ }
474
+ else
475
+ {
476
+ for (var i = 0; i < b.length; i++)
477
+ a[i>>2] |= b[i] << ((i & 3)*8);
478
+ }
479
+
480
+ return a;
481
+ }
482
+
483
+ function _a32_to_str(a,len)
484
+ {
485
+ var b = '';
486
+
487
+ for (var i = 0; i < len; i++)
488
+ b = b+String.fromCharCode((a[i>>2] >>> (24-(i & 3)*8)) & 255);
489
+
490
+ return b;
491
+ }
492
+
493
+ // decrypt ArrayBuffer in CTR mode, return MAC
494
+ function decrypt_ab_ctr(aes,ab,nonce,pos)
495
+ {
496
+ var ctr = [nonce[0],nonce[1],(pos/0x1000000000) >>> 0,(pos/0x10) >>> 0];
497
+ var mac = [ctr[0],ctr[1],ctr[0],ctr[1]];
498
+
499
+ var enc, len, i, j, v;
500
+
501
+ if (have_ab)
502
+ {
503
+ var data0, data1, data2, data3;
504
+
505
+ len = ab.buffer.byteLength-16; // @@@ -15?
506
+
507
+ var v = new DataView(ab.buffer);
508
+
509
+ for (i = 0; i < len; i += 16)
510
+ {
511
+ enc = aes.encrypt(ctr);
512
+
513
+ data0 = v.getUint32(i,false)^enc[0];
514
+ data1 = v.getUint32(i+4,false)^enc[1];
515
+ data2 = v.getUint32(i+8,false)^enc[2];
516
+ data3 = v.getUint32(i+12,false)^enc[3];
517
+
518
+ v.setUint32(i,data0,false);
519
+ v.setUint32(i+4,data1,false);
520
+ v.setUint32(i+8,data2,false);
521
+ v.setUint32(i+12,data3,false);
522
+
523
+ mac[0] ^= data0;
524
+ mac[1] ^= data1;
525
+ mac[2] ^= data2;
526
+ mac[3] ^= data3;
527
+
528
+ mac = aes.encrypt(mac);
529
+
530
+ if (!(++ctr[3])) ctr[2]++;
531
+ }
532
+
533
+ if (i < ab.buffer.byteLength)
534
+ {
535
+ var fullbuf = new Uint8Array(ab.buffer);
536
+ var tmpbuf = new ArrayBuffer(16);
537
+ var tmparray = new Uint8Array(tmpbuf);
538
+
539
+ tmparray.set(fullbuf.subarray(i));
540
+
541
+ v = new DataView(tmpbuf);
542
+
543
+ enc = aes.encrypt(ctr);
544
+ data0 = v.getUint32(0,false)^enc[0];
545
+ data1 = v.getUint32(4,false)^enc[1];
546
+ data2 = v.getUint32(8,false)^enc[2];
547
+ data3 = v.getUint32(12,false)^enc[3];
548
+
549
+ v.setUint32(0,data0,false);
550
+ v.setUint32(4,data1,false);
551
+ v.setUint32(8,data2,false);
552
+ v.setUint32(12,data3,false);
553
+
554
+ fullbuf.set(tmparray.subarray(0,j = fullbuf.length-i),i);
555
+
556
+ while (j < 16) tmparray[j++] = 0;
557
+
558
+ mac[0] ^= v.getUint32(0,false);
559
+ mac[1] ^= v.getUint32(4,false);
560
+ mac[2] ^= v.getUint32(8,false);
561
+ mac[3] ^= v.getUint32(12,false);
562
+ mac = aes.encrypt(mac);
563
+ }
564
+ }
565
+ else
566
+ {
567
+ var ab32 = _str_to_a32(ab.buffer);
568
+ len = ab32.length-3;
569
+
570
+ for (i = 0; i < len; i += 4)
571
+ {
572
+ enc = aes.encrypt(ctr);
573
+ mac[0] ^= (ab32[i] ^= enc[0]);
574
+ mac[1] ^= (ab32[i+1] ^= enc[1]);
575
+ mac[2] ^= (ab32[i+2] ^= enc[2]);
576
+ mac[3] ^= (ab32[i+3] ^= enc[3]);
577
+ mac = aes.encrypt(mac);
578
+
579
+ if (!(++ctr[3])) ctr[2]++;
580
+ }
581
+
582
+ if (i < ab32.length)
583
+ {
584
+ var v = [0,0,0,0];
585
+
586
+ for (j = i; j < ab32.length; j++) v[j-i] = ab32[j];
587
+
588
+ enc = aes.encrypt(ctr);
589
+ v[0] ^= enc[0];
590
+ v[1] ^= enc[1];
591
+ v[2] ^= enc[2];
592
+ v[3] ^= enc[3];
593
+
594
+ var j = ab.buffer.length & 15;
595
+
596
+ var m = _str_to_a32(Array(j+1).join(String.fromCharCode(255))+Array(17-j).join(String.fromCharCode(0)));
597
+
598
+ mac[0] ^= v[0] & m[0];
599
+ mac[1] ^= v[1] & m[1];
600
+ mac[2] ^= v[2] & m[2];
601
+ mac[3] ^= v[3] & m[3];
602
+ mac = aes.encrypt(mac);
603
+
604
+ for (j = i; j < ab32.length; j++) ab32[j] = v[j-i];
605
+ }
606
+
607
+ ab.buffer = _a32_to_str(ab32,ab.buffer.length);
608
+ }
609
+
610
+ return mac;
611
+ }
612
+
613
+ // encrypt ArrayBuffer in CBC mode (zero IV)
614
+ function encrypt_ab_cbc(cipher,ab)
615
+ {
616
+ if (have_ab)
617
+ {
618
+ var v = new DataView(ab);
619
+ var iv = [0,0,0,0], d = Array(4);
620
+ var i;
621
+
622
+ for (i = 0; i < ab.byteLength; i += 16)
623
+ {
624
+ d[0] = v.getUint32(i,false) ^ iv[0];
625
+ d[1] = v.getUint32(i+4,false) ^ iv[1];
626
+ d[2] = v.getUint32(i+8,false) ^ iv[2];
627
+ d[3] = v.getUint32(i+12,false) ^ iv[3];
628
+
629
+ iv = cipher.encrypt(d);
630
+
631
+ v.setUint32(i,iv[0],false);
632
+ v.setUint32(i+4,iv[1],false);
633
+ v.setUint32(i+8,iv[2],false);
634
+ v.setUint32(i+12,iv[3],false);
635
+ }
636
+ }
637
+ else
638
+ {
639
+ var ab32 = str_to_a32(ab.buffer);
640
+ var iv = [0,0,0,0], d = Array(4);
641
+ var i;
642
+
643
+ for (i = 0; i < ab32.length; i += 4)
644
+ {
645
+ d[0] = ab32[i] ^ iv[0];
646
+ d[1] = ab32[i+1] ^ iv[1];
647
+ d[2] = ab32[i+2] ^ iv[2];
648
+ d[3] = ab32[i+3] ^ iv[3];
649
+
650
+ iv = cipher.encrypt(d);
651
+
652
+ ab32[i] = iv[0];
653
+ ab32[i+1] = iv[1];
654
+ ab32[i+2] = iv[2];
655
+ ab32[i+3] = iv[3];
656
+ }
657
+
658
+ ab.buffer = a32_to_str(ab32);
659
+ }
660
+ }
661
+
662
+ // decrypt ArrayBuffer in CBC mode (zero IV)
663
+ function decrypt_ab_cbc(cipher,ab)
664
+ {
665
+ if (have_ab)
666
+ {
667
+ var v = new DataView(ab);
668
+ var iv = [0,0,0,0], d = Array(4), t = Array(4);
669
+ var i;
670
+
671
+ for (i = 0; i < ab.byteLength; i += 16)
672
+ {
673
+ d[0] = v.getUint32(i,false);
674
+ d[1] = v.getUint32(i+4,false);
675
+ d[2] = v.getUint32(i+8,false);
676
+ d[3] = v.getUint32(i+12,false);
677
+ t = d;
678
+
679
+ d = cipher.decrypt(d);
680
+
681
+ v.setUint32(i,d[0] ^ iv[0],false);
682
+ v.setUint32(i+4,d[1] ^ iv[1],false);
683
+ v.setUint32(i+8,d[2] ^ iv[2],false);
684
+ v.setUint32(i+12,d[3] ^ iv[3],false);
685
+ iv = t;
686
+ }
687
+ }
688
+ else
689
+ {
690
+ var ab32 = str_to_a32(ab.buffer);
691
+ var iv = [0,0,0,0], d = Array(4), t = Array(4);
692
+ var i;
693
+
694
+ for (i = 0; i < ab32.length; i += 4)
695
+ {
696
+ d[0] = ab32[i];
697
+ d[1] = ab32[i+1];
698
+ d[2] = ab32[i+2];
699
+ d[3] = ab32[i+3];
700
+ t = d;
701
+
702
+ d = cipher.decrypt(d);
703
+
704
+ ab32[i] = d[0] ^ iv[0];
705
+ ab32[i+1] = d[1] ^ iv[1];
706
+ ab32[i+2] = d[2] ^ iv[2];
707
+ ab32[i+3] = d[3] ^ iv[3];
708
+ iv = t;
709
+ }
710
+
711
+ ab.buffer = a32_to_str(ab32);
712
+ }
713
+ }
714
+
715
+ // encrypt/decrypt 4- or 8-element 32-bit integer array
716
+ function encrypt_key(cipher,a)
717
+ {
718
+ if (a.length == 4) return cipher.encrypt(a);
719
+
720
+ var x = [];
721
+ for (var i = 0; i < a.length; i += 4) x = x.concat(cipher.encrypt([a[i],a[i+1],a[i+2],a[i+3]]));
722
+ return x;
723
+ }
724
+
725
+ function decrypt_key(cipher,a)
726
+ {
727
+ if (a.length == 4) return cipher.decrypt(a);
728
+
729
+ var x = [];
730
+ for (var i = 0; i < a.length; i += 4) x = x.concat(cipher.decrypt([a[i],a[i+1],a[i+2],a[i+3]]));
731
+ return x;
732
+ }
733
+
734
+ // generate attributes block using AES-CBC with MEGA canary
735
+ // attr = Object, key = [] (four-word random key will be generated) or Array(8) (lower four words will be used)
736
+ // returns [ArrayBuffer data,Array key]
737
+ function enc_attr(attr,key)
738
+ {
739
+ var aes;
740
+ var ab;
741
+ var b;
742
+
743
+ ab = str_to_ab('MEGA'+to8(JSON.stringify(attr)));
744
+
745
+ // if no key supplied, generate a random one
746
+ if (!key.length) for (i = 4; i--; ) key[i] = rand(0x100000000);
747
+
748
+ aes = new sjcl.cipher.aes([key[0]^key[4],key[1]^key[5],key[2]^key[6],key[3]^key[7]]);
749
+
750
+ encrypt_ab_cbc(aes,ab);
751
+
752
+ return [ab,key];
753
+ }
754
+
755
+ // decrypt attributes block using AES-CBC, check for MEGA canary
756
+ // attr = ab, key as with enc_attr
757
+ // returns [Object] or false
758
+ function dec_attr(attr,key)
759
+ {
760
+ var aes;
761
+ var b;
762
+
763
+ aes = new sjcl.cipher.aes([key[0]^key[4],key[1]^key[5],key[2]^key[6],key[3]^key[7]]);
764
+ decrypt_ab_cbc(aes,attr);
765
+
766
+ b = ab_to_str_depad(attr);
767
+
768
+ if (b.substr(0,6) != 'MEGA{"') return false;
769
+
770
+ // @@@ protect against syntax errors
771
+ try {
772
+ return JSON.parse(from8(b.substr(4)));
773
+ } catch (e) {
774
+ return { n : 'MALFORMED_ATTRIBUTES' };
775
+ }
776
+ }
777
+
778
+ function to8(unicode)
779
+ {
780
+ return unescape(encodeURIComponent(unicode));
781
+ }
782
+
783
+ function from8(utf8)
784
+ {
785
+ return decodeURIComponent(escape(utf8));
786
+ }
787
+
788
+ function getxhr()
789
+ {
790
+ return (typeof XDomainRequest != 'undefined' && typeof ArrayBuffer == 'undefined') ? new XDomainRequest() : new XMLHttpRequest();
791
+ }
792
+
793
+ // API command queueing
794
+ // All commands are executed in sequence, with no overlap
795
+ // @@@ user warning after backoff > 1000
796
+
797
+ var apiq = new Array;
798
+ var apiqtimer = false;
799
+ var apixhr = getxhr();
800
+
801
+ function api_req(req,params)
802
+ {
803
+ apiq.push([typeof req == 'string' ? req : to8(JSON.stringify(req)),params,seqno++,0]);
804
+
805
+ if (apiq.length == 1) api_proc();
806
+ }
807
+
808
+ // execute first pending event
809
+ function api_proc()
810
+ {
811
+ if (apiqtimer)
812
+ {
813
+ // delete timer (should not ever happen)
814
+ clearTimeout(apiqtimer);
815
+ apiqtimer = false;
816
+ }
817
+
818
+ if (apixhr.readyState && apixhr.readyState != apixhr.DONE)
819
+ {
820
+ // wait for apixhr to get ready
821
+ if (d) console.log("XHR not in DONE state: " + apixhr.readyState);
822
+ apiqtimer = setTimeout(api_proc,1000);
823
+ return;
824
+ }
825
+
826
+ // no more commands pending?
827
+ if (!apiq.length) return;
828
+
829
+ // request ready for (re)execution: execute
830
+ apixhr = getxhr();
831
+
832
+
833
+ apixhr.onerror = function()
834
+ {
835
+ if (d) console.log("API request error - retrying");
836
+ api_result(-3);
837
+ }
838
+
839
+ apixhr.onload = function()
840
+ {
841
+ var t;
842
+
843
+ if (this.responseText) this.response = this.responseText;
844
+
845
+ if (d) console.log('API response: ' + this.response);
846
+
847
+ try {
848
+ t = JSON.parse(from8(this.response));
849
+ } catch (e) {
850
+ // bogus response, requeue
851
+ console.log("Bad JSON data in response: " + this.response);
852
+ t = -3;
853
+ }
854
+
855
+ api_result(t);
856
+ }
857
+
858
+ if (d) console.log("Making API request: " + apiq[0][0]);
859
+ apixhr.open('POST', (apiq[0][0].substr(0,4) == 'https') ? apiq[0][0] : (apipath + (apiq[0][0].substr(0,1) == '[' ? ('cs?id=' + apiq[0][2]) : apiq[0][0]) + (u_sid ? ('&sid=' + u_sid) : '')), true);
860
+ apixhr.send(apiq[0][0]);
861
+ }
862
+
863
+ function api_result(res)
864
+ {
865
+ if (res === -3)
866
+ {
867
+ // exponential backoff
868
+ if (apiq[0][3]) apiq[0][3] *= 2;
869
+ else apiq[0][3] = 125;
870
+
871
+ if (d) console.log('Temporary error (' + res + ') - retrying after: ' + (apiq[0][3]/1000));
872
+
873
+ apiqtimer = setTimeout(api_proc,apiq[0][3]);
874
+ }
875
+ else
876
+ {
877
+ if (apiq[0][1]) apiq[0][1].callback(res,apiq[0][1]);
878
+ apiq.shift();
879
+ api_proc();
880
+ }
881
+ }
882
+
883
+ // calls execsc() with server-client requests received
884
+ function getsc()
885
+ {
886
+ ctx = {
887
+ callback : function(res,ctx)
888
+ {
889
+ if (res.w)
890
+ {
891
+ waiturl = res.w;
892
+
893
+ if (waitbackoff > 1000) setTimeout(waitsc,waitbackoff);
894
+ else waitsc();
895
+ }
896
+ else
897
+ {
898
+ if (res.sn) maxaction = res.sn;
899
+ execsc(res.a);
900
+ }
901
+ }
902
+ };
903
+
904
+ api_req('sc?sn=' + maxaction + '&ssl=1',ctx);
905
+ }
906
+
907
+ var waiturl;
908
+ var waitxhr = getxhr();
909
+ var waitbackoff = 125;
910
+ var waitbegin;
911
+
912
+ function waitsc()
913
+ {
914
+ if (waitxhr.readyState != apixhr.DONE) waitxhr = undefined;
915
+
916
+ if (!waitxhr) waitxhr = getxhr();
917
+
918
+ waitxhr.onerror = function()
919
+ {
920
+ if (d) console.log("Error while waiting - retrying, backoff: " + waitbackoff);
921
+ getsc();
922
+ }
923
+
924
+ waitxhr.onload = function()
925
+ {
926
+ var t = new Date().getTime()-waitbegin;
927
+ if (t < 1000) waitbackoff += waitbackoff;
928
+ else waitbackoff = 125;
929
+ getsc();
930
+ }
931
+
932
+ waitbegin = new Date().getTime();
933
+ waitxhr.open('POST',waiturl,true);
934
+ waitxhr.send();
935
+ }
936
+
937
+ function api_create_u_k()
938
+ {
939
+ u_k = Array(4); // static master key, will be stored at the server side encrypted with the master pw
940
+
941
+ for (var i = 4; i--; ) u_k[i] = rand(0x100000000);
942
+ }
943
+
944
+ // If the user triggers an action that requires an account, but hasn't logged in,
945
+ // we create an anonymous preliminary account. Returns userhandle and passwordkey for permanent storage.
946
+ function api_createuser(ctx,invitecode,invitename,uh)
947
+ {
948
+ var i;
949
+ var ssc = Array(4); // session self challenge, will be used to verify password
950
+ var req, res;
951
+
952
+ if (!ctx.passwordkey)
953
+ {
954
+ ctx.passwordkey = Array(4);
955
+ for (i = 4; i--; ) ctx.passwordkey[i] = rand(0x100000000);
956
+ }
957
+
958
+ if (!u_k) api_create_u_k();
959
+
960
+ for (i = 4; i--; ) ssc[i] = rand(0x100000000);
961
+
962
+ if (d) console.log("api_createuser - masterkey: " + u_k + " passwordkey: " + ctx.passwordkey);
963
+
964
+ req = { a : 'up',
965
+ k : a32_to_base64(encrypt_key(new sjcl.cipher.aes(ctx.passwordkey),u_k)),
966
+ ts : base64urlencode(a32_to_str(ssc) + a32_to_str(encrypt_key(new sjcl.cipher.aes(u_k),ssc))) };
967
+
968
+ if (invitecode)
969
+ {
970
+ req.uh = uh;
971
+ req.ic = invitecode;
972
+ req.name = invitename;
973
+ }
974
+
975
+ //if (confirmcode) req.c = confirmcode;
976
+ if (d) console.log("Storing key: " + req.k);
977
+
978
+ api_req([req],ctx);
979
+ }
980
+
981
+ function api_checkconfirmcode(ctx,c)
982
+ {
983
+ res = api_req([{ a : 'uc', c : c }],ctx);
984
+ }
985
+
986
+ // We query the sid using the supplied user handle (or entered email address, if already attached)
987
+ // and check the supplied password key.
988
+ // Returns [decrypted master key,verified session ID(,RSA private key)] or false if API error or
989
+ // supplied information incorrect
990
+ function api_getsid(ctx,user,passwordkey,hash)
991
+ {
992
+ ctx.callback = api_getsid2;
993
+ ctx.passwordkey = passwordkey;
994
+
995
+ api_req([{ a : 'us', user : user, uh : hash }],ctx);
996
+ }
997
+
998
+ function api_getsid2(res,ctx)
999
+ {
1000
+ console.log(new Date().getTime());
1001
+
1002
+ var t, k;
1003
+ var r = false;
1004
+
1005
+ if (typeof res == 'object')
1006
+ {
1007
+ var aes = new sjcl.cipher.aes(ctx.passwordkey);
1008
+
1009
+ // decrypt master key
1010
+ if (typeof res[0].k == 'string')
1011
+ {
1012
+ k = base64_to_a32(res[0].k);
1013
+
1014
+ if (k.length == 4)
1015
+ {
1016
+ k = decrypt_key(aes,k);
1017
+
1018
+ aes = new sjcl.cipher.aes(k);
1019
+
1020
+ if (typeof res[0].tsid == 'string')
1021
+ {
1022
+ t = base64urldecode(res[0].tsid);
1023
+ if (a32_to_str(encrypt_key(aes,str_to_a32(t.substr(0,16)))) == t.substr(-16)) r = [k,res[0].tsid];
1024
+ }
1025
+ else if (typeof res[0].csid == 'string')
1026
+ {
1027
+ var t = mpi2b(base64urldecode(res[0].csid));
1028
+
1029
+ var privk = a32_to_str(decrypt_key(aes,base64_to_a32(res[0].privk)));
1030
+
1031
+ var rsa_privk = Array(4);
1032
+
1033
+ // decompose private key
1034
+ for (var i = 0; i < 4; i++)
1035
+ {
1036
+ var l = ((privk.charCodeAt(0)*256+privk.charCodeAt(1)+7)>>3)+2;
1037
+
1038
+ rsa_privk[i] = mpi2b(privk.substr(0,l));
1039
+ if (typeof rsa_privk[i] == 'number') break;
1040
+ privk = privk.substr(l);
1041
+ }
1042
+
1043
+ // check format
1044
+ if (i == 4 && privk.length < 16)
1045
+ {
1046
+ // @@@ check remaining padding for added early wrong password detection likelihood
1047
+ r = [k,base64urlencode(b2s(RSAdecrypt(t,rsa_privk[2],rsa_privk[0],rsa_privk[1],rsa_privk[3])).substr(0,43)),rsa_privk];
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+ }
1053
+
1054
+ console.log(new Date().getTime());
1055
+
1056
+ ctx.result(ctx,r);
1057
+ }
1058
+
1059
+ // We call ug using the sid from setsid() and the user's master password to obtain the master key (and other credentials)
1060
+ // Returns user credentials (.k being the decrypted master key) or false in case of an error.
1061
+ function api_getuser(ctx)
1062
+ {
1063
+ api_req([{ a : 'ug' }],ctx);
1064
+ }
1065
+
1066
+ // User must be logged in, sid and passwordkey must be valid
1067
+ // return values:
1068
+ // 2 - old & new passwords are the same post-preparation
1069
+ // 1 - old password incorrect
1070
+ // userhandle - success
1071
+ // false - processing error
1072
+ // other negative values - API error
1073
+ function api_changepw(ctx,passwordkey,masterkey,oldpw,newpw,email)
1074
+ {
1075
+ var req, res;
1076
+ var oldkey;
1077
+
1078
+ var newkey = prepare_key_pw(newpw);
1079
+
1080
+ if (oldpw !== false)
1081
+ {
1082
+ var oldkey = prepare_key_pw(oldpw);
1083
+
1084
+ // quick check of old pw
1085
+ if (oldkey[0] != passwordkey[0]
1086
+ || oldkey[1] != passwordkey[1]
1087
+ || oldkey[2] != passwordkey[2]
1088
+ || oldkey[3] != passwordkey[3]) return 1;
1089
+
1090
+ if (oldkey[0] == newkey[0]
1091
+ && oldkey[1] == newkey[1]
1092
+ && oldkey[2] == newkey[2]
1093
+ && oldkey[3] == newkey[3]) return 2;
1094
+ }
1095
+
1096
+ var aes = new sjcl.cipher.aes(newkey);
1097
+
1098
+ // encrypt masterkey with the new password
1099
+ var cmasterkey = encrypt_key(aes,masterkey);
1100
+
1101
+ req = { a : 'up',
1102
+ k : a32_to_base64(cmasterkey) };
1103
+
1104
+ if (email.length) req.email = email;
1105
+
1106
+ api_req([req],ctx);
1107
+ }
1108
+
1109
+ function stringhash(s,aes)
1110
+ {
1111
+ var s32 = str_to_a32(s);
1112
+ var h32 = [0,0,0,0];
1113
+
1114
+ for (i = 0; i < s32.length; i++) h32[i&3] ^= s32[i];
1115
+
1116
+ for (i = 16384; i--; ) h32 = aes.encrypt(h32);
1117
+
1118
+ return a32_to_base64([h32[0],h32[2]]);
1119
+ }
1120
+
1121
+ // Update user
1122
+ // Can also be used to set keys and to confirm accounts (.c)
1123
+ function api_updateuser(ctx,newuser)
1124
+ {
1125
+ newuser.a = 'up';
1126
+
1127
+ res = api_req([newuser],ctx);
1128
+ }
1129
+
1130
+ var u_pubkeys = new Object;
1131
+
1132
+ // Encrypt data to a user's public key
1133
+ // Returns false in case no public key is available
1134
+ function api_cachepubkey(ctx,user)
1135
+ {
1136
+ ctx.user = user;
1137
+ ctx.callback = api_cachepubkey2;
1138
+
1139
+ if (u_pubkeys[user]) ctx.cachepubkeycomplete(ctx,u_pubkeys[user]);
1140
+ else api_req([{ a : 'uk', u : user }],ctx);
1141
+ }
1142
+
1143
+ function api_cachepubkey2(res,ctx)
1144
+ {
1145
+ if (typeof res == 'object' && typeof res[0].pubk == 'string')
1146
+ {
1147
+ var spubkey = base64urldecode(res[0].pubk);
1148
+ var keylen = spubkey.charCodeAt(0)*256+spubkey.charCodeAt(1);
1149
+ var pubkey = Array(3);
1150
+ var i;
1151
+
1152
+ // decompose public key
1153
+ for (i = 0; i < 2; i++)
1154
+ {
1155
+ var l = ((spubkey.charCodeAt(0)*256+spubkey.charCodeAt(1)+7)>>3)+2;
1156
+
1157
+ pubkey[i] = mpi2b(spubkey.substr(0,l));
1158
+ if (typeof pubkey[i] == 'number') break;
1159
+ spubkey = spubkey.substr(l);
1160
+ }
1161
+
1162
+ // check format
1163
+ if (i == 2 && spubkey.length < 16)
1164
+ {
1165
+ pubkey[2] = keylen;
1166
+ u_pubkeys[ctx.user] = pubkey;
1167
+ }
1168
+ else pubkey = false;
1169
+ }
1170
+
1171
+ ctx.cachepubkeycomplete(ctx,pubkey);
1172
+ }
1173
+
1174
+ function encryptto(user,data)
1175
+ {
1176
+ var i, data;
1177
+ var pubkey;
1178
+
1179
+ if (pubkey = u_pubkeys[user])
1180
+ {
1181
+ // random padding
1182
+ for (i = (pubkey[2]>>3)-1-data.length; i-- > 0; ) data = data+String.fromCharCode(rand(256));
1183
+
1184
+ i = data.length*8;
1185
+ data = String.fromCharCode(i >> 8) + String.fromCharCode(i & 255) + data;
1186
+
1187
+ return b2mpi(RSAencrypt(mpi2b(data),pubkey[1],pubkey[0]));
1188
+ }
1189
+
1190
+ return false;
1191
+ }
1192
+
1193
+ var u_sharekeys = {};
1194
+ var u_nodekeys = {};
1195
+
1196
+ // u_nodekeys must be set for all sharenodes
1197
+ // Add/cancel share(s) to a set of users or email addresses
1198
+ // targets is an array of {u,r} - if no r given, cancel share
1199
+ // If no sharekey known, tentatively generates one and encrypts
1200
+ // everything to it. In case of a mismatch, the API call returns
1201
+ // an error, and the whole operation gets repeated (exceedingly
1202
+ // rare race condition).
1203
+ function api_setshare1(node,targets,sharenodes,ctx)
1204
+ {
1205
+ var i, j, n, nk, sharekey, ssharekey;
1206
+ var req, res;
1207
+ var newkey = false;
1208
+
1209
+ req = { a : 's',
1210
+ n : node,
1211
+ s : []
1212
+ };
1213
+
1214
+ ctx.sharenodes = sharenodes;
1215
+ ctx.targets = targets;
1216
+
1217
+ // we only need to generate a key if one or more shares are added
1218
+ for (i = targets.length; i--; )
1219
+ {
1220
+ if (typeof targets[i].r == 'undefined')
1221
+ {
1222
+ // share cancellation
1223
+ req.s.push({ u : targets[i].u });
1224
+ }
1225
+ else
1226
+ {
1227
+ if (!req.ok)
1228
+ {
1229
+ if (u_sharekeys[node]) sharekey = u_sharekeys[node];
1230
+ else
1231
+ {
1232
+ sharekey = Array(4);
1233
+ for (j = 4; j--; ) sharekey[j] = rand(0x100000000);
1234
+ u_sharekeys[node] = sharekey;
1235
+ newkey = true;
1236
+ }
1237
+
1238
+ req.ok = a32_to_base64(encrypt_key(u_k_aes,sharekey));
1239
+ req.ha = crypto_handleauth(node);
1240
+ ssharekey = a32_to_str(sharekey);
1241
+ }
1242
+ }
1243
+ }
1244
+
1245
+ u_sharekeys[node] = sharekey;
1246
+
1247
+ if (newkey) req.cr = crypto_makecr(sharenodes,[node],true);
1248
+
1249
+ ctx.tried = -1;
1250
+ ctx.ssharekey = ssharekey;
1251
+ ctx.req = req;
1252
+ ctx.i = 0;
1253
+ ctx.node = node;
1254
+ ctx.targets = targets;
1255
+ ctx.sharenodes = sharenodes;
1256
+
1257
+ ctx.callback = function(res,ctx)
1258
+ {
1259
+ var pubkey;
1260
+ var i;
1261
+
1262
+ if (typeof res == 'object' && typeof res[0] == 'object')
1263
+ {
1264
+ if (typeof res[0].pubk == 'string') u_pubkeys[ctx.targets[ctx.i].u] = crypto_decodepubkey(res[0].pubk);
1265
+ else if (res[0].ok)
1266
+ {
1267
+ u_sharekeys[node] = decrypt_key(u_k_aes,base64_to_a32(res[0].ok));
1268
+ return api_setshare(ctx.node,ctx.targets,ctx.sharenodes,ctx);
1269
+ }
1270
+ }
1271
+
1272
+ if (ctx.i == ctx.targets.length) ctx.done(ctx);
1273
+ else if (!(pubkey = u_pubkeys[ctx.targets[ctx.i].u]) && ctx.tried < ctx.i)
1274
+ {
1275
+ ctx.tried = ctx.i;
1276
+
1277
+ // no public key cached for this user: get it!
1278
+ api_req([{ a : 'uk', u : ctx.targets[ctx.i].u }],ctx);
1279
+ }
1280
+ else
1281
+ {
1282
+ n = false;
1283
+
1284
+ if (pubkey)
1285
+ {
1286
+ // pubkey found: encrypt share key to it
1287
+ n = crypto_rsaencrypt(pubkey,ctx.ssharekey);
1288
+ }
1289
+
1290
+ if (n) ctx.req.s.push({ u : ctx.targets[ctx.i].u, r : ctx.targets[ctx.i].r, k : base64urlencode(n) });
1291
+ else ctx.req.s.push({ u : ctx.targets[ctx.i].u, r : ctx.targets[ctx.i].r });
1292
+
1293
+ ctx.i++;
1294
+
1295
+ ctx.callback(false,ctx);
1296
+ }
1297
+ }
1298
+
1299
+ ctx.callback(false,ctx);
1300
+ }
1301
+
1302
+ function api_setshare2(res,node)
1303
+ {
1304
+ if (res[0].ok) u_sharekeys[node] = decrypt_key(u_k_aes,base64_to_a32(res[0].ok));
1305
+ }
1306
+
1307
+ function api_setrsa(privk,pubk)
1308
+ {
1309
+ var t, i;
1310
+
1311
+ for (t = '', i = 0; i < privk.length; i++) t = t+b2mpi(privk[i]);
1312
+
1313
+ for (i = (-t.length)&15; i--; ) t = t + String.fromCharCode(rand(256));
1314
+
1315
+ ctx = { callback : function(res,ctx)
1316
+ {
1317
+ if (d) console.log("RSA key put result=" + res);
1318
+
1319
+ u_privk = ctx.privk;
1320
+ u_storage.privk = JSON.stringify(u_privk);
1321
+ u_type = 3;
1322
+
1323
+ ui_keycomplete();
1324
+ },
1325
+ privk : privk
1326
+ };
1327
+
1328
+ api_req([{ a : 'up', privk : a32_to_base64(encrypt_key(u_k_aes,str_to_a32(t))), pubk : base64urlencode(b2mpi(pubk[0])+b2mpi(pubk[1])) }],ctx);
1329
+ }
1330
+
1331
+ function crypto_handleauth(h)
1332
+ {
1333
+ return a32_to_base64(encrypt_key(u_k_aes,str_to_a32(h+h)));
1334
+ }
1335
+
1336
+ function crypto_decodepubkey(pubk)
1337
+ {
1338
+ var i;
1339
+
1340
+ var spubkey = base64urldecode(pubk);
1341
+
1342
+ var keylen = spubkey.charCodeAt(0)*256+spubkey.charCodeAt(1);
1343
+
1344
+ var pubkey = Array(3);
1345
+
1346
+ // decompose public key
1347
+ for (i = 0; i < 2; i++)
1348
+ {
1349
+ var l = ((spubkey.charCodeAt(0)*256+spubkey.charCodeAt(1)+7)>>3)+2;
1350
+
1351
+ pubkey[i] = mpi2b(spubkey.substr(0,l));
1352
+ if (typeof pubkey[i] == 'number') break;
1353
+ spubkey = spubkey.substr(l);
1354
+ }
1355
+
1356
+ // check format
1357
+ if (i == 2 && spubkey.length < 16)
1358
+ {
1359
+ pubkey[2] = keylen;
1360
+ return pubkey;
1361
+ }
1362
+ return false;
1363
+ }
1364
+
1365
+ function crypto_rsaencrypt(pubkey,data)
1366
+ {
1367
+ var i;
1368
+
1369
+ // random padding
1370
+ for (i = (pubkey[2]>>3)-1-data.length; i-- > 0; ) data = data+String.fromCharCode(rand(256));
1371
+
1372
+ i = data.length*8;
1373
+ data = String.fromCharCode(i >> 8) + String.fromCharCode(i & 255) + data;
1374
+
1375
+ return b2mpi(RSAencrypt(mpi2b(data),pubkey[1],pubkey[0]));
1376
+ }
1377
+
1378
+ // Complete upload
1379
+ // We construct a special node put command that uses the upload token
1380
+ // as the source handle
1381
+ function api_completeupload(t,ut,path,n,k,ctx)
1382
+ {
1383
+ ctx2 = { callback : api_completeupload2, t : base64urlencode(t), path : path, n : n, k : k, ctx : ctx };
1384
+
1385
+ api_completeupload2(ctx2,ut);
1386
+ }
1387
+
1388
+ function api_completeupload2(ctx,ut)
1389
+ {
1390
+ var p;
1391
+
1392
+ if (ctx.path && ctx.path != ctx.n && (p = ctx.path.indexOf('/')) > 0)
1393
+ {
1394
+ var pc = ctx.path.substr(0,p);
1395
+
1396
+ ctx.path = ctx.path.substr(p+1);
1397
+
1398
+ fm_requestfolderid(ut,pc,ctx);
1399
+ }
1400
+ else
1401
+ {
1402
+ a = { n : ctx.n };
1403
+
1404
+ if (d) console.log(ctx.k);
1405
+
1406
+ var ea = enc_attr(a,ctx.k);
1407
+
1408
+ if (d) console.log(ea);
1409
+
1410
+ var req = { a : 'p',
1411
+ t : ut,
1412
+ n : [{ h : ctx.t, t : 0, a : ab_to_base64(ea[0]), k : a32_to_base64(encrypt_key(u_k_aes,ctx.k)) }]
1413
+ };
1414
+
1415
+
1416
+ if (ut)
1417
+ {
1418
+ // a target has been supplied: encrypt to all relevant shares
1419
+ var sn = fm_getsharenodes(ut);
1420
+
1421
+ if (sn.length)
1422
+ {
1423
+ req.cr = crypto_makecr([ctx.k],sn,false);
1424
+ req.cr[1][0] = ctx.t;
1425
+ }
1426
+ }
1427
+
1428
+ api_req([req],ctx.ctx);
1429
+ }
1430
+ }
1431
+
1432
+ // generate crypto request response for the given nodes/shares matrix
1433
+ function crypto_makecr(source,shares,source_is_nodes)
1434
+ {
1435
+ var i, j, n;
1436
+ var cr = [shares,[],[]];
1437
+ var aes;
1438
+
1439
+ // if we have node handles, include in cr - otherwise, we have node keys
1440
+ if (source_is_nodes) cr[1] = source;
1441
+
1442
+ // TODO: optimize - keep track of pre-existing/sent keys, only send new ones
1443
+ for (i = shares.length; i--; )
1444
+ {
1445
+ if (u_sharekeys[shares[i]])
1446
+ {
1447
+ aes = new sjcl.cipher.aes(u_sharekeys[shares[i]]);
1448
+
1449
+ for (j = source.length; j--; )
1450
+ {
1451
+ if (source_is_nodes ? (nk = u_nodekeys[source[j]]) : (nk = source[j]))
1452
+ {
1453
+ if (nk.length == 8 || nk.length == 4) cr[2].push(i,j,a32_to_base64(encrypt_key(aes,nk)));
1454
+ }
1455
+ }
1456
+ }
1457
+ }
1458
+ return cr;
1459
+ }
1460
+
1461
+ // RSA-encrypt sharekey to newly RSA-equipped user
1462
+ // TODO: check source/ownership of sharekeys, prevent forged requests
1463
+ function crypto_procsr(sr)
1464
+ {
1465
+ var ctx = new Object;
1466
+ ctx.sr = sr;
1467
+ ctx.i = -2;
1468
+
1469
+ ctx.callback = function(res,ctx)
1470
+ {
1471
+ if (ctx.sr)
1472
+ {
1473
+ var pubkey;
1474
+
1475
+ if (typeof res == 'object' && typeof res[0] == 'object' && typeof res[0].pubk == 'string') u_pubkeys[ctx.sr[ctx.i+1]] = crypto_decodepubkey(res[0].pubk);
1476
+
1477
+ // collect all required pubkeys
1478
+ while (ctx.i < ctx.sr.length-4)
1479
+ {
1480
+ ctx.i += 2;
1481
+
1482
+ if (ctx.sr[ctx.i+1].length == 11 && !(pubkey = u_pubkeys[ctx.sr[ctx.i+1]]))
1483
+ {
1484
+ api_req([{ a : 'uk', u : ctx.sr[ctx.i+1] }],ctx);
1485
+ return;
1486
+ }
1487
+ }
1488
+
1489
+ var rsr = [];
1490
+ var sh;
1491
+ var n;
1492
+
1493
+ for (var i = 0; i < ctx.sr.length; i += 2)
1494
+ {
1495
+ sh = ctx.sr[i];
1496
+
1497
+ if (sh.length == 8)
1498
+ {
1499
+ // @@@ check auth
1500
+ if (u_sharekeys[sh])
1501
+ {
1502
+ if (d) console.log("Encrypting sharekey " + sh + " to user " + ctx.sr[i+1]);
1503
+
1504
+ if (pubkey = u_pubkeys[ctx.sr[i+1]])
1505
+ {
1506
+ // pubkey found: encrypt share key to it
1507
+ if (n = crypto_rsaencrypt(pubkey,a32_to_str(u_sharekeys[sh]))) rsr.push(sh,ctx.sr[i+1],base64urlencode(n));
1508
+ }
1509
+ }
1510
+ }
1511
+
1512
+ if (rsr.length) api_req([{ a : 'k', sr : rsr }]);
1513
+ }
1514
+ }
1515
+ }
1516
+
1517
+ ctx.callback(false,ctx);
1518
+ }
1519
+
1520
+ var keycache = new Object;
1521
+
1522
+ var rsa2aes = new Object;
1523
+
1524
+ // Try to decrypt ufs node.
1525
+ // Parameters: me - my user handle
1526
+ // master_aes - my master password's AES cipher
1527
+ // file - ufs node containing .k and .a
1528
+ // Output: .key and .name set if successful
1529
+ function crypto_processkey(me,master_aes,file)
1530
+ {
1531
+ var id, key, k, n;
1532
+
1533
+ if (!file.k)
1534
+ {
1535
+ if (!keycache[file.h]) return;
1536
+
1537
+ file.k = keycache[file.h];
1538
+ }
1539
+
1540
+ id = me;
1541
+
1542
+ // do I own the file? (user key is guaranteed to be first in .k)
1543
+ var p = file.k.indexOf(id + ':');
1544
+
1545
+ if (p)
1546
+ {
1547
+ // I don't - do I have a suitable sharekey?
1548
+ for (id in u_sharekeys)
1549
+ {
1550
+ p = file.k.indexOf(id + ':');
1551
+
1552
+ if (p >= 0 && (!p || file.k.charAt(p-1) == '/')) break;
1553
+
1554
+ p = -1;
1555
+ }
1556
+ }
1557
+
1558
+ if (p >= 0)
1559
+ {
1560
+ delete keycache[file.h];
1561
+
1562
+ var pp = file.k.indexOf('/',p);
1563
+
1564
+ if (pp < 0) pp = file.k.length;
1565
+
1566
+ p += id.length+1;
1567
+
1568
+ key = file.k.substr(p,pp-p);
1569
+
1570
+ // we have found a suitable key: decrypt!
1571
+ if (key.length < 46)
1572
+ {
1573
+ // short keys: AES
1574
+ k = base64_to_a32(key);
1575
+
1576
+ // check for permitted key lengths (4 == folder, 8 == file)
1577
+ if (k.length == 4 || k.length == 8)
1578
+ {
1579
+ // TODO: cache sharekeys in aes
1580
+ k = decrypt_key(id == me ? master_aes : new sjcl.cipher.aes(u_sharekeys[id]),k);
1581
+ }
1582
+ else
1583
+ {
1584
+ if (d) console.log("Received invalid key length (" + k.length + "): " + file.h);
1585
+ return;
1586
+ }
1587
+ }
1588
+ else
1589
+ {
1590
+ // long keys: RSA
1591
+ if (u_privk)
1592
+ {
1593
+ var t = mpi2b(base64urldecode(key));
1594
+ k = str_to_a32(b2s(RSAdecrypt(t,u_privk[2],u_privk[0],u_privk[1],u_privk[3])).substr(0,file.t ? 16 : 32));
1595
+ }
1596
+ else
1597
+ {
1598
+ if (d) console.log("Received RSA key, but have no public key published: " + file.h);
1599
+ return;
1600
+ }
1601
+ }
1602
+
1603
+ var ab = base64_to_ab(file.a);
1604
+ var o = dec_attr(ab,k);
1605
+
1606
+ if (typeof o == 'object')
1607
+ {
1608
+ if (typeof o.n == 'string')
1609
+ {
1610
+ if (file.h)
1611
+ {
1612
+ u_nodekeys[file.h] = k;
1613
+ if (key.length >= 46) rsa2aes[file.h] = a32_to_str(encrypt_key(u_k_aes,k));
1614
+ }
1615
+
1616
+ file.key = k;
1617
+ file.name = o.n;
1618
+ }
1619
+ }
1620
+ }
1621
+ else
1622
+ {
1623
+ if (d) console.log("Received no suitable key: " + file.h);
1624
+
1625
+ if (!missingkeys[file.h])
1626
+ {
1627
+ newmissingkeys = true;
1628
+ missingkeys[file.h] = true;
1629
+ }
1630
+ keycache[file.h] = file.k;
1631
+ }
1632
+ }
1633
+
1634
+ function crypto_sendrsa2aes()
1635
+ {
1636
+ var n;
1637
+ var nk = [];
1638
+
1639
+ for (n in rsa2aes) nk.push(n,base64urlencode(rsa2aes[n]));
1640
+
1641
+ if (nk.length) api_req([{ a : 'k', nk : nk }]);
1642
+
1643
+ rsa2aes = new Object;
1644
+ }
1645
+
1646
+ var missingkeys = new Object;
1647
+ var newmissingkeys = false;
1648
+
1649
+ function crypto_reqmissingkeys()
1650
+ {
1651
+ if (!newmissingkeys)
1652
+ {
1653
+ if (d) console.log('No new missing keys.');
1654
+ return;
1655
+ }
1656
+
1657
+ var i, j;
1658
+ var n, s, ni, si, sn;
1659
+ var cr = [[],[],[]];
1660
+
1661
+ ni = new Object;
1662
+ si = new Object;
1663
+
1664
+ for (n in missingkeys)
1665
+ {
1666
+ // TODO: optimization: don't request keys for own files
1667
+ sn = fm_getsharenodes(n);
1668
+
1669
+ for (j = sn.length; j--; )
1670
+ {
1671
+ s = sn[j];
1672
+
1673
+ if (typeof si[s] == 'undefined')
1674
+ {
1675
+ si[s] = cr[0].length;
1676
+ cr[0].push(s);
1677
+ }
1678
+
1679
+ if (typeof ni[n] == 'undefined')
1680
+ {
1681
+ ni[n] = cr[1].length;
1682
+ cr[1].push(n);
1683
+ }
1684
+
1685
+ cr[2].push(si[s],ni[n]);
1686
+ }
1687
+ }
1688
+
1689
+ if (!cr[1].length /*&& !missingsharekeys.length*/)
1690
+ {
1691
+ if (d) console.log('No missing keys');
1692
+ return;
1693
+ }
1694
+
1695
+ if (cr[0].length)
1696
+ {
1697
+ var ctx = new Object;
1698
+
1699
+ ctx.callback = function(res,ctx)
1700
+ {
1701
+ if (d) console.log("Processing crypto response");
1702
+
1703
+ if (typeof res == 'object' && typeof res[0] == 'object') crypto_proccr(res[0]);
1704
+ }
1705
+
1706
+ res = api_req([{ a : 'k', cr : cr }],ctx);
1707
+ }
1708
+ else if (d) console.log("Keys " + cr[1] + " missing, but no related shares found.");
1709
+ }
1710
+
1711
+ // process incoming cr, set fm keys and commit
1712
+ function crypto_proccr(cr)
1713
+ {
1714
+ var i;
1715
+
1716
+ // received keys in response, add
1717
+ for (i = 0; i < cr[2].length; i += 3) fm_updatekey(cr[1][cr[2][i+1]],cr[0][cr[2][i]] + ":" + cr[2][i+2]);
1718
+
1719
+ fm_commitkeyupdate();
1720
+ }
1721
+
1722
+ // process incoming missing key cr
1723
+ function crypto_procmcr(mcr)
1724
+ {
1725
+ var i;
1726
+ var si = new Object, ni = new Object;
1727
+ var sh, nh;
1728
+ var sc = new Object;
1729
+ var cr = [[],[],[]];
1730
+
1731
+ // received keys in response, add
1732
+ for (i = 0; i < mcr[2].length; i += 2)
1733
+ {
1734
+ sh = mcr[0][mcr[2][i]];
1735
+
1736
+ if (u_sharekeys[sh])
1737
+ {
1738
+ nh = mcr[1][mcr[2][i+1]];
1739
+
1740
+ if (u_nodekeys[nh])
1741
+ {
1742
+ if (typeof si[sh] == 'undefined')
1743
+ {
1744
+ sc[sh] = new sjcl.cipher.aes(u_sharekeys[sh]);
1745
+ si[sh] = cr[0].length;
1746
+ cr[0].push(sh);
1747
+ }
1748
+
1749
+ if (typeof ni[nh] == 'undefined')
1750
+ {
1751
+ ni[nh] = cr[1].length;
1752
+ cr[1].push(nh);
1753
+ }
1754
+
1755
+ cr[2].push(si[sh],ni[nh],a32_to_base64(encrypt_key(sc[sh],u_nodekeys[nh])));
1756
+ }
1757
+ }
1758
+ }
1759
+
1760
+ if (cr[0].length) api_req([{ a : 'k', cr : cr }]);
1761
+ }
1762
+
1763
+ var rsasharekeys = new Object;
1764
+
1765
+ function crypto_process_sharekey(handle,key)
1766
+ {
1767
+ if (key.length > 22)
1768
+ {
1769
+ key = mpi2b(base64urldecode(key));
1770
+ var k = str_to_a32(b2s(RSAdecrypt(key,u_privk[2],u_privk[0],u_privk[1],u_privk[3])).substr(0,16));
1771
+ rsasharekeys[handle] = true;
1772
+ return k;
1773
+ }
1774
+ else return decrypt_key(u_k_aes,base64_to_a32(key));
1775
+ }
1776
+
1777
+ function crypto_share_rsa2aes()
1778
+ {
1779
+ var rsr = [];
1780
+
1781
+ for (n in rsasharekeys)
1782
+ {
1783
+ if (u_sharekeys[n])
1784
+ {
1785
+ // pubkey found: encrypt share key to it
1786
+ rsr.push(n,u_handle,a32_to_base64(encrypt_key(u_k_aes,u_sharekeys[n])));
1787
+ }
1788
+ }
1789
+
1790
+ if (rsr.length)
1791
+ {
1792
+ api_req([{ a : 'k', sr : rsr }]);
1793
+ rsasharekeys = new Object;
1794
+ }
1795
+ }