megar 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }