megar 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ 0.0.4 reduce OpenSSL requirements 17-Mar-2013
2
+ ===========================================================
3
+ * switch to a native AES CTR implementation so there are no
4
+ longer any special OpenSSL requirements
5
+
1
6
  0.0.3 put and get ready 9-Mar-2013
2
7
  ===========================================================
3
8
  * basic file upload support added
@@ -2,16 +2,23 @@
2
2
 
3
3
  Megar ("megaargh!" in pirate-speak) is a Ruby wrapper and command-line (CLI) client for the {Mega API}[https://mega.co.nz/#developers].
4
4
 
5
- So far this is "experimental". Megar has coverage of the basic file/folder operations: connect, get file/folder listings and details, and
6
- download files. There's more in the API that would be worth supporting .. a job for tomorrow!
5
+ Megar has coverage of the basic file/folder operations: connect, get file/folder listings and details,
6
+ upload and download files. Read on - the following sections provide cookbook examples for using Megar within Ruby
7
+ and from the command-line.
8
+
9
+ Your help is greatly appreciated on two fronts:
10
+ * Use: It needs hammering to make sure we don't have the edge-case crypto bugs.
11
+ * Code: there's more interesting stuff in the API it would be nice to support. And a lot it would be nice to refactor (the priority so far has been to make it work correctly and testably, not fast or elegantly). Tuck in!
12
+
13
+ NOTE: megar no longer requires OpenSSL 1.0.1+. It is quite happy with any version that implements AES-128-CBC
14
+ (commonly OpenSSL 0.9.8 or greater, which you most likely already have installed).
7
15
 
8
16
 
9
17
  == Requirements and Known Limitations
10
18
 
11
19
  Consider this totally alpha at this point, especially since the Mega API has yet to be formally and fully documented.
12
20
 
13
- * Currently tested with MRI 1.9.3
14
- * Requires OpenSSL 1.0.1+ (and properly linked with ruby)
21
+ * Currently tested with MRI 1.9.3 with OpenSSL 0.9.8+
15
22
  * MRI 1.9.2 and 2.x, Rubinius and JRuby 1.9 modes not yet tested. No plans to support 1.8 Ruby modes.
16
23
 
17
24
  Mega API coverage is far from complete at this point (help appreciated!). Here's the run-down:
@@ -316,12 +323,9 @@ commit and distribute credentials. Change the password on the account first.
316
323
  The tests will still run as intended with the snapshot of crypto details stored in the expectations file.
317
324
 
318
325
 
319
- === Getting my OpenSSL installation up to snuff (MacOSX)
326
+ === Checking my OpenSSL installation
320
327
 
321
- These notes are specific to MacOSX. Mileage on other operating systems may vary, but this should give you
322
- a guide:
323
-
324
- Check the openssl version installed
328
+ Megar will fail with a warning if suitable OpenSSL support is not available. To manually check the OpenSSL version installed:
325
329
 
326
330
  $ irb -r openssl
327
331
  1.9.3p327 :001 > OpenSSL::VERSION
@@ -329,16 +333,18 @@ Check the openssl version installed
329
333
  1.9.3p327 :002 > OpenSSL::OPENSSL_VERSION
330
334
  => "OpenSSL 0.9.8r 8 Feb 2011"
331
335
 
332
- OK, that's too old. The OS-installed version is probably not going to change any time soon, and we don't
333
- want to replace it (too many unintended consequences) but we need a later version to use with ruby.
336
+ OK, that's fine. Note that earlier versions of megar required 1.0.1+ but that is no longer the case.
337
+
338
+ If you do need or want to install or update OpenSSL, you can install from source or rvm.
339
+ On MacOSX, it is probably not a good idea to upgrade the OS-installed version (too many unintended consequences),
340
+ but you can also use brew or macports to install a version in parallel.
334
341
 
335
- You can install form source, use brew or macports, or use rvm.
336
342
 
337
343
  ==== Installing OpenSSL Using RVM:
338
344
 
339
345
  See the {rvm openssl}[https://rvm.io//packages/openssl/] page for details..
340
346
 
341
- $ rvm pkg install openssl
347
+ $ rvm pkg install openssl
342
348
  Fetching openssl-1.0.1c.tar.gz to ...
343
349
 
344
350
  $ rvm reinstall 1.9.3 --with-openssl-dir=$rvm_path/usr
@@ -357,6 +363,9 @@ Sweet.
357
363
  * {Mega API is documentation}[https://mega.co.nz/#developers]
358
364
  * {Using the Mega API, with Python examples}[http://julien-marchand.fr/blog/using-mega-api-with-python-examples/]
359
365
  * {SpiderOak's Analysis and Recommendations for the Crypto in Kim Dotcom's Mega}[https://spideroak.com/blog/20130123130638-spideroaks-analysis-and-recommendations-for-the-crypto-in-kim-dotcoms-mega-part-one]
366
+ * {mega.py - a Python library for Mega}[https://github.com/richardasaurus/mega.py]
367
+ * {My blog post about Megar}[http://tardate.blogspot.sg/2013/03/rolling-mega-api-with-ruby.html]
368
+ * {rmega - another Ruby implementation}[https://github.com/daniele-m/rmega] - it seems rmega came to life on a similar timeline to megar. Some different implementation decisions have been made under the covers, so I don't think we'll see these projects merge anytime soon. But that means you have more choice;-) My thanks to the rmega folks for inspiring the switch in megar from OpenSSL 1.0.1+ AES CTR implementation to a native version that works with OpenSSL 0.9.8.
360
369
 
361
370
 
362
371
  == Contributing
@@ -5,7 +5,7 @@ require 'open-uri'
5
5
  # Javascript reference implementation: function startdownload2(res,ctx)
6
6
  #
7
7
  class Megar::FileDownloader
8
- include Megar::CryptoSupport
8
+ include Megar::Crypto::Support
9
9
 
10
10
  attr_reader :session
11
11
  attr_reader :file
@@ -31,7 +31,7 @@ class Megar::FileDownloader
31
31
  decoded_content = ''
32
32
  calculated_mac = [0, 0, 0, 0]
33
33
 
34
- decryptor = get_file_decrypter(decomposed_key,iv)
34
+ decryptor = get_file_cipher(decomposed_key,iv)
35
35
 
36
36
  get_chunks(download_size).each do |chunk_start, chunk_size|
37
37
  chunk = stream.readpartial(chunk_size)
@@ -10,7 +10,7 @@ require 'pathname'
10
10
  # Javascript reference implementation: function initupload3()
11
11
  #
12
12
  class Megar::FileUploader
13
- include Megar::CryptoSupport
13
+ include Megar::Crypto::Support
14
14
 
15
15
  attr_reader :folder
16
16
  attr_reader :session
@@ -62,7 +62,7 @@ class Megar::FileUploader
62
62
  calculated_mac = [0, 0, 0, 0]
63
63
  completion_file_handle = ''
64
64
 
65
- encryptor = get_file_encrypter(upload_key,iv_str)
65
+ encryptor = get_file_cipher(upload_key,iv)
66
66
 
67
67
  get_chunks(upload_size).each do |chunk_start, chunk_size|
68
68
  chunk = stream.readpartial(chunk_size)
@@ -121,11 +121,8 @@ class Megar::FileUploader
121
121
  end
122
122
 
123
123
  def iv
124
- ((upload_key[4]<<32)+upload_key[5])<<64
125
- end
126
-
127
- def iv_str
128
- hexstr_to_bstr( iv.to_s(16) )
124
+ # ((upload_key[4]<<32)+upload_key[5])<<64
125
+ [upload_key[4], upload_key[5], 0, 0]
129
126
  end
130
127
 
131
128
  # Returns and caches a file upload response
@@ -51,14 +51,11 @@ module Megar::Connection
51
51
  params['sid'] = sid if sid
52
52
  json_data = [data].to_json
53
53
 
54
- response_data = get_api_response(params,json_data).first
55
-
56
- raise Megar::MegaRequestError.new(response_data) if response_data.is_a?(Fixnum)
57
-
58
- response_data
54
+ response_body = get_api_response(params,json_data)
55
+ parse_json_response(response_body)
59
56
  end
60
57
 
61
- # Command: low-level method to actually perform the API request and return the JSON response.
58
+ # Command: low-level method to actually perform the API request and return the response body.
62
59
  # Given +params+ Hash of query string parameters, and +data+ JSON data structure.
63
60
  # Note: there is no handling of network errors or timeouts - any exceptions will bubble up.
64
61
  def get_api_response(params,data)
@@ -66,11 +63,25 @@ module Megar::Connection
66
63
  http.use_ssl = (api_uri.scheme == 'https')
67
64
  uri_path = api_uri.path.empty? ? '/' : api_uri.path
68
65
  uri_path << hash_to_query_string(params)
69
- response = http.post(uri_path,data)
70
- JSON.parse(response.body)
66
+ http.post(uri_path,data).body
71
67
  end
72
68
  protected :get_api_response
73
69
 
70
+ # Returns the first response as JSON from response +body+, raising Mega error if present.
71
+ # Note: expects and currently only handles a single response in the data
72
+ def parse_json_response(body)
73
+ begin
74
+ response_data = JSON.parse(body)
75
+ response_data = response_data.first if response_data.is_a?(Array)
76
+ rescue => e
77
+ raise Megar::MegaRequestError.new(body.to_i) if body =~ /^[-\d]+$/
78
+ raise Megar::BadApiResponseError.new(e)
79
+ end
80
+ raise Megar::MegaRequestError.new(response_data) if response_data.is_a?(Fixnum)
81
+ response_data
82
+ end
83
+ protected :parse_json_response
84
+
74
85
  # Returns Hash +h+ as an encoded query string '?a=b&c=d...'
75
86
  def hash_to_query_string(h)
76
87
  if qs = URI.escape(h.to_a.map{|e| e.join('=') }.join('&'))
@@ -1 +1,5 @@
1
- require 'megar/crypto/support'
1
+ module Megar::Crypto
2
+ end
3
+ require 'megar/crypto/support'
4
+ require 'megar/crypto/aes'
5
+ require 'megar/crypto/aes_ctr'
@@ -0,0 +1,60 @@
1
+ # A convenience wrapper for AES CBC implementation provided by OpenSSL
2
+ class Megar::Crypto::Aes
3
+
4
+ attr_accessor :key
5
+ attr_accessor :iv
6
+
7
+ def initialize(options={})
8
+ self.key = options[:key]
9
+ self.iv = options[:iv]
10
+ end
11
+
12
+ def key=(value)
13
+ @key = value.is_a?(Array) ? value.pack(packing) : value
14
+ end
15
+
16
+ def iv=(value)
17
+ @iv = value || "\x0" * 16
18
+ end
19
+
20
+ def packing
21
+ 'l>*'
22
+ end
23
+
24
+ def mode
25
+ 'AES-128-CBC'
26
+ end
27
+
28
+ def cipher
29
+ @cipher ||= OpenSSL::Cipher::Cipher.new(mode)
30
+ end
31
+
32
+ def encrypt(data)
33
+ a32_mode = data.is_a?(Array)
34
+ d = a32_mode ? data.pack(packing) : data
35
+
36
+ cipher.reset
37
+ cipher.encrypt
38
+ cipher.padding = 0
39
+ cipher.iv = iv
40
+ cipher.key = key
41
+ result = cipher.update d
42
+
43
+ a32_mode ? result.unpack(packing) : result
44
+ end
45
+
46
+ def decrypt(data)
47
+ a32_mode = data.is_a?(Array)
48
+ d = a32_mode ? data.pack(packing) : data
49
+
50
+ cipher.reset
51
+ cipher.decrypt
52
+ cipher.padding = 0
53
+ cipher.iv = iv
54
+ cipher.key = key
55
+ result = cipher.update d
56
+
57
+ a32_mode ? result.unpack(packing) : result
58
+ end
59
+
60
+ end
@@ -0,0 +1,75 @@
1
+ # Implements AES COUNTER mode using base AES CBC implementation provided by OpenSSL
2
+ class Megar::Crypto::AesCtr
3
+
4
+ attr_accessor :key
5
+ attr_accessor :iv
6
+
7
+ # Consturcts a new AES CTR-mode object give +options+
8
+ # options[:key] = encryption key as binary string or array of 32-bit integer (required)
9
+ # options[:iv] = initialisation vector as array of 32-bit integer (nulled by default)
10
+ def initialize(options={})
11
+ self.key = options[:key]
12
+ self.iv = options[:iv]
13
+ end
14
+
15
+ def packing
16
+ 'l>*'
17
+ end
18
+
19
+ def key=(value)
20
+ @key = value.is_a?(Array) ? value.pack(packing) : value
21
+ end
22
+
23
+ def iv=(value)
24
+ @iv = value ? value.dup : [0,0,0,0]
25
+ end
26
+
27
+ # Returns the encrypted binary string of +chunk+ (provided as binary string).
28
+ # Repeated calls will continue the counter sequence.
29
+ def update(chunk)
30
+ a32 = str_to_a32(chunk)
31
+ last_i = 0
32
+
33
+ (0..a32.size - 3).step(4) do |i|
34
+ enc = Megar::Crypto::Aes.new(key: key).encrypt(iv)
35
+ 4.times do |m|
36
+ a32[i+m] = (a32[i+m] || 0) ^ (enc[m] || 0)
37
+ end
38
+ iv[3] += 1
39
+ iv[2] += 1 if iv[3] == 0
40
+ last_i = i + 4
41
+ end
42
+
43
+ remainder = a32.size % 4
44
+
45
+ if remainder > 0
46
+ v = [0, 0, 0, 0]
47
+ (last_i..a32.size - 1).step(1) { |m| v[m-last_i] = a32[m] || 0 }
48
+ enc = Megar::Crypto::Aes.new(key: key).encrypt(iv)
49
+ 4.times { |m| v[m] = v[m] ^ enc[m] }
50
+
51
+ (last_i..a32.size - 1).step(1) { |j| a32[j] = v[j - last_i] || 0 }
52
+ end
53
+
54
+ a32_to_str(a32)[0..chunk.size - 1]
55
+ end
56
+
57
+ # TODO: refactor to pull this method from a shared lib.
58
+ def str_to_a32(b,signed=true)
59
+ a = Array.new((b.length+3) >> 2,0)
60
+ b.length.times { |i| a[i>>2] |= (b.getbyte(i) << (24-(i & 3)*8)) }
61
+ if signed
62
+ a.pack('l>*').unpack('l>*')
63
+ else
64
+ a
65
+ end
66
+ end
67
+
68
+ # TODO: refactor to pull this method from a shared lib.
69
+ def a32_to_str(a)
70
+ b = ''
71
+ (a.size * 4).times { |i| b << ((a[i>>2] >> (24-(i & 3)*8)) & 255).chr }
72
+ b
73
+ end
74
+
75
+ end
@@ -9,11 +9,11 @@ require 'base64'
9
9
  #
10
10
  # Javascript reference implementations quoted here are taken from the Mega javascript source.
11
11
  #
12
- module Megar::CryptoSupport
12
+ module Megar::Crypto::Support
13
13
 
14
14
  # Verifies that the required crypto support is available from ruby/openssl
15
15
  def crypto_requirements_met?
16
- OpenSSL::Cipher.ciphers.include?("AES-128-CTR")
16
+ OpenSSL::Cipher.ciphers.include?("AES-128-CBC")
17
17
  end
18
18
 
19
19
  # Returns encrypted key given an array +a+ of 32-bit integers
@@ -72,17 +72,14 @@ module Megar::CryptoSupport
72
72
  a32_to_str(decrypt_base64_to_a32(data, key))
73
73
  end
74
74
 
75
+ # Returns AES-128 CBC encrypted given +key+ and +data+ (String)
76
+ def aes_cbc_encrypt(data, key)
77
+ Megar::Crypto::Aes.new(key: key).encrypt(data)
78
+ end
75
79
 
76
80
  # Returns AES-128 encrypted given +key+ and +data+ (arrays of 32-bit signed integers)
77
81
  def aes_encrypt_a32(data, key)
78
- aes = OpenSSL::Cipher::Cipher.new('AES-128-ECB')
79
- aes.encrypt
80
- aes.padding = 0
81
- aes.key = key.pack('l>*')
82
- aes.update(data.pack('l>*')).unpack('l>*')
83
- # e = aes.update(data.pack('l>*')).unpack('l>*')
84
- # e << aes.final
85
- # e.unpack('l>*')
82
+ Megar::Crypto::Aes.new(key: key).encrypt(data)
86
83
  end
87
84
 
88
85
  # Returns AES-128 CBC decrypted given +key+ and +data+ (arrays of 32-bit signed integers)
@@ -92,14 +89,7 @@ module Megar::CryptoSupport
92
89
 
93
90
  # Returns AES-128 CBC decrypted given +key+ and +data+ (String)
94
91
  def aes_cbc_decrypt(data, key)
95
- aes = OpenSSL::Cipher::Cipher.new('AES-128-CBC')
96
- aes.decrypt
97
- aes.padding = 0
98
- aes.key = key
99
- aes.iv = "\0" * 16
100
- d = aes.update(data)
101
- d = aes.final if d.empty?
102
- d
92
+ Megar::Crypto::Aes.new(key: key).decrypt(data)
103
93
  end
104
94
 
105
95
  # Returns AES-128 CBC decrypted given +key+ and +data+ (arrays of 32-bit signed integers)
@@ -107,18 +97,6 @@ module Megar::CryptoSupport
107
97
  str_to_a32(aes_cbc_encrypt(a32_to_str(data), a32_to_str(key)),signed)
108
98
  end
109
99
 
110
- # Returns AES-128 CBC encrypted given +key+ and +data+ (String)
111
- def aes_cbc_encrypt(data, key)
112
- aes = OpenSSL::Cipher::Cipher.new('AES-128-CBC')
113
- aes.encrypt
114
- aes.padding = 0
115
- aes.key = key
116
- aes.iv = "\0" * 16
117
- d = aes.update(data)
118
- d = aes.final if d.empty?
119
- d
120
- end
121
-
122
100
  # Returns an array of 32-bit signed integers representing the string +b+
123
101
  #
124
102
  # Javascript reference implementation: function str_to_a32(b)
@@ -127,7 +105,6 @@ module Megar::CryptoSupport
127
105
  a = Array.new((b.length+3) >> 2,0)
128
106
  b.length.times { |i| a[i>>2] |= (b.getbyte(i) << (24-(i & 3)*8)) }
129
107
  if signed
130
- # hack to force to signed 32-bit ... I don't think we really need to do this, but it makes comparison with
131
108
  a.pack('l>*').unpack('l>*')
132
109
  else
133
110
  a
@@ -152,7 +129,7 @@ module Megar::CryptoSupport
152
129
  s32 = str_to_a32(s)
153
130
  h32 = [0,0,0,0]
154
131
  s32.length.times {|i| h32[i&3] ^= s32[i] }
155
- 16384.times {|i| h32 = aes_encrypt_a32(h32, aeskey) }
132
+ 0x4000.times {|i| h32 = aes_encrypt_a32(h32, aeskey) }
156
133
  a32_to_base64([h32[0],h32[2]])
157
134
  end
158
135
 
@@ -278,10 +255,8 @@ module Megar::CryptoSupport
278
255
  4.times do
279
256
  len = ((privk[0].ord * 256 + privk[1].ord + 7) / 8) + 2
280
257
  privk_part = privk[0,len]
281
- # puts "\nprivk_part #{base64urlencode(privk_part)}"
282
258
  privk_part_a32 = mpi_to_a32(privk_part)
283
259
  decomposed_key << privk_part_a32
284
- # puts "decomp: len:#{len} privk_part_a32:#{privk_part_a32.length} first:#{privk_part_a32.first} last:#{privk_part_a32.last}"
285
260
  privk.slice!(0,len)
286
261
  end
287
262
  decomposed_key
@@ -303,8 +278,6 @@ module Megar::CryptoSupport
303
278
  4.times do |i|
304
279
  len = ((privk[0].ord * 256 + privk[1].ord + 7) / 8) + 2
305
280
  privk_part = privk[0,len]
306
- # puts "\nl: ", len
307
- # puts "decrypted rsa part hex: \n", privk_part.unpack('H*').first
308
281
  decomposed_key << privk_part[2,privk_part.length].unpack('H*').first.to_i(16)
309
282
  privk.slice!(0,len)
310
283
  end
@@ -482,26 +455,10 @@ module Megar::CryptoSupport
482
455
  chunks
483
456
  end
484
457
 
485
- # Returns AES CTR-mode decryption cipher given +key+ and +iv+ as array of int
486
- #
487
- def get_file_decrypter(key,iv)
488
- aes = OpenSSL::Cipher::Cipher.new('AES-128-CTR')
489
- aes.decrypt
490
- aes.padding = 0
491
- aes.key = a32_to_str(key)
492
- aes.iv = a32_to_str(iv)
493
- aes
494
- end
495
-
496
- # Returns AES CTR-mode encryption cipher given +key+ as array of int and +iv+ as binary string
458
+ # Returns AES CTR-mode decryption cipher given +key+ and +iv+ as array of 32-bit integer
497
459
  #
498
- def get_file_encrypter(key,iv)
499
- aes = OpenSSL::Cipher::Cipher.new('AES-128-CTR')
500
- aes.encrypt
501
- aes.padding = 0
502
- aes.key = a32_to_str(key)
503
- aes.iv = iv
504
- aes
460
+ def get_file_cipher(key,iv)
461
+ Megar::Crypto::AesCtr.new(key: key, iv: iv)
505
462
  end
506
463
 
507
464
  # Returns the +chunk+ mac (array of unsigned int)
@@ -15,6 +15,9 @@ module Megar
15
15
  # Raised on non-API related file upload errors
16
16
  class FileUploadError < Error; end
17
17
 
18
+ # Raised on non-mega API related request errors
19
+ class BadApiResponseError < Error; end
20
+
18
21
  class MegaRequestError < Error
19
22
 
20
23
  # Initialise with +error_code+ returned from Mega
@@ -1,6 +1,6 @@
1
1
  class Megar::Session
2
2
 
3
- include Megar::CryptoSupport
3
+ include Megar::Crypto::Support
4
4
  include Megar::Connection
5
5
 
6
6
  attr_accessor :options
@@ -1,3 +1,3 @@
1
1
  module Megar
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "email": "megartest@gmail.com",
3
3
  "email_mixed_case": "Megartest@gmail.com",
4
- "password": "NEvfEM5xME7jAnr6YN9JZH8M",
4
+ "password": "arDGGNCVPQEZKqasmfjQSvOmgOPdLhBX",
5
5
  "autoconnect": false,
6
6
  "login_response_data": {
7
- "csid": "CABObd6kRiw5JF_4Fr7z2R_tleh8g3jlsiGl3fX48C3MFq57ni86x3cVOgtNgv48N5ZX_68g-MROc_5j6TP3g2FTBpOhMRf2E4swTAwBQxa0CF04dj6hfHKHIp5XyE1KPGN57GvecLzTV6FgtanY1v41Pm_1H6PE89XSejovQIGOOKwsmeUOlmfLYreMe58Il1aROqzr6E-eybD8BzozQgy8XMO80Gcc6eoja0VbbxehHPdCYS990tAWBYkG8up-rnQqwpln-UrmtZbXtv4WoNO5n3wI4s5abyQl7PbG_42xw9Uk2QYwvIrRSnt4rDB8m-iAihGS7Zp6QQO5RVpuWvU1",
7
+ "csid": "CAArCWzdn3ohzX_8-CYamVViQnGwY3j0tAp35lqN02lOCJM3cdQP42UpVWWcB0Q901XaMf2LtQh94QK9Y-llhC9ktJCjVH7Da_kAy9FpQb46si1nVg1cAjRyXUQqQgyo62r-ooluHxE7gq98JCwH3pmfM7998A7YDKKhfaNpL-TghE6yhhbnCLg_qPy2u4HNIBbYbooOFVSUn1jPmPeLhUbnafHMACygftUXlymocYcbTJRfBxU-gVSLlrHJTw7dm96_nGAfoaOqfr_-IR2yU0Td8Y5XMFMDFcBue_5iE2f1EuZ5tJK_zTz2nDyQPciuf7RKvMLcJQimbwEwEJLujr9o",
8
8
  "privk": "4Iurk4vQ0BlLEEvEyaRUX8QnYzJFIedm0RVKe2pLh4wTzBBtgmafnszD_dP3Y40P-DSNLEGfWghL-0_BIW4XP8tfGNFkMg7mnYgQYC91ccz58BbhGEDf7-j97dstckf3OSSsT9D9H-cocXzyt1m231w9d7YLX23c3KAGz3vxdnl66F4jsFGO9AdIyb1KTEEt7NHkljtbz3WgWlk6W6wBawuEbJvLPHPx2L16i0iacbbkQi8ZF4pmddswMa9yWOSXwUribGDbdgx_jDGjBoKJUJI-7Aa-aOaRPEoVUTft2R2lNAVErvYwD_GIsIhQiCcJWul-KqPhI3vquvzUHd2rHHvKyTLyvXKyIDeVdBGXTaqFA2ni9xsjUNMOX14ftBZ4_fgDce0jvrZzYRs5IuOMt3K9gKMpBpUWtnlaBYbnYdL2zJuFlrM77PlcYab2uLLqlSe4hh-FxMAc_p_8tDAoU4dPgpgOpU3GRR8i7oszu_MKibzNpRBeAX1UtwKwQepjPnipu9aQlPy9OtgB-5KmGi34EcMKBPg8BN6vHRYg0Za_lk_TtTQ4WbkllAqR3Fla5-RBjzB7f3dWae4ES3VX4f-dI-UYrrKDQkqaS0R_xLaMPNG6_GQMDM7L5Fsa8I3hFjdgfs9rV4Xf_uIFrhUjm25EuT5JCdobNmwBi9VU5nCbk0LQJdrXDNYV3fmD-Y_p_KV8x_-opRnXUV03SCopOD6DhitsFtpv065QXcG1SNDu0mM-kgDh9E-QddtQQtXLLPuKFyX5o1ts08EqXTxHsZo5tIsevbExZoz9hxSRrr-V5swEDcJXoV78_k6sR3iniZ_m77JVo9QU572av2MvNhyu5YeTXyc0m6Me7cPGiO0",
9
- "k": "0sdW36PBOcqicUJ7814isw"
9
+ "k": "8FS4LRVtEpzQXKZNUyGBwg"
10
10
  },
11
11
  "master_key": [
12
12
  384287193,
@@ -14,8 +14,8 @@
14
14
  554881366,
15
15
  530403344
16
16
  ],
17
- "expected_uh": "FeGJgLm4ciM",
18
- "sid": "gVes6vS8WZ2c-CB7HJRdXHpBOEN6Q2Y5dFp3CQbFwprvxr0nkhb_XTqQrA",
17
+ "expected_uh": "rMkG_yo0TCw",
18
+ "sid": "WhJ1kiw0-YORXrdnSr8qJnpBOEN6Q2Y5dFp37MVSDV1V6EpwGECdQtgW0Q",
19
19
  "rsa_private_key_b64": "BACOQ7VCjwkiwYCKhqRRvPTMBaQ5Yx2_sszj6ILrxrTBOJ2bu-9LsOSvaGz0HpnM2lXpJg3h6QWJdlQBOhwvfeafHg7rcEaaDuUWONifv5ABpQgeoAij6cgARMvFiOdjarMvu954Eduup8PHcgfjsBSu76qpu3UqkZCpBpByAYU1oQQA1i5oxuA9i7qsXxhJRsBeceI7hZSnNYAQzsWfFD0kDW8GsJBuK-CST6oNNp0z7MQ5Qof6DZ8x9wt5ZJR43HFmD2VtTQUZLvIeemC4URuR74UWn25CETyzQdIhdNSEibaCRvi0sNwb-ikCZVxxo4-O7ZBQPw0egrQOJW06HlCQUykH_00EIoiBRcbJLS6W3nJ8KhddDf_4glZYPz6fBpqu1jIdIBGOGQf4Ttn_DmdAgkLxWF7AF6k59D45J9kxfANRJIBGSWUsI1bQBlrwFtois4O14uQkQ40Jh_d5BCrLPe599zrliGH1SsECx_xYGWcWY_GPsPa99d_St56BB9oUtDW3A1clEx2uVS8OWqRPgVimu6MXs_TEEnjWGRFofHpl_8qGeAoPbNkXsTMQb6uUK4NCYq3UzVfreByEnOqxuQcu2jlF4Ey0UH9w_KjkRyT8xu--kyZAbktYaw4EZmGUxZIlkZLk_OAGaW8KWPZhdGxjfi8K7WeZK2HtLe4At48DC_ED_20pNtkTCrB6-FQlMj6a4O2C1sE6pSdrIPofcK-HpaefOXqYKWpRM-Wnp_qHnuJgEtgodQ-pw74nXkOsVFZoJzVGCepZKoa2xhevAu3CfXMfR97LIski17PPwWV9n7yQ7onSKDmJP-q4mO7PG7LICE22WkgJkk_T1W4UkDTkCxZE_t64F6JOryg",
20
20
  "decomposed_rsa_private_key": [
21
21
  99901518447147034909201847419097315968297147437151469508360346463543063455874100243913856315732601845631262435262260706834732781343251117295631282035669718956849603873760427510697419513879951677293141811802298231979984193675329310799949319780256427743430868153430092107435691279045586737323088097814348379553,
@@ -52,6 +52,16 @@
52
52
  "k": "",
53
53
  "ts": 1361694312
54
54
  },
55
+ {
56
+ "h": "Ot5xDCTI",
57
+ "p": "ewZ1GAqT",
58
+ "u": "zA8CzCf9tZw",
59
+ "t": 0,
60
+ "a": "JKLTa3pcvxf3ICsFHc0aa4JDQt7Dn9ODBZ9S7szZV-4aWMnKoUORIpWxvsTsV4oC",
61
+ "k": "zA8CzCf9tZw:dFvviPw8ljp8cKeEq1sIdVusWdY56kXZKdBQVQeK4qk",
62
+ "s": 5482930,
63
+ "ts": 1363505767
64
+ },
55
65
  {
56
66
  "h": "jtwkAQaK",
57
67
  "p": "ywJVFBKB",
@@ -119,6 +129,258 @@
119
129
  "k": "zA8CzCf9tZw:bLCHGilxhwBlxkZs8uptY87wZf_WNCR6RefwEgtbRmo",
120
130
  "s": 8920445,
121
131
  "ts": 1362235380
132
+ },
133
+ {
134
+ "h": "vxpUhLKT",
135
+ "p": "ywJVFBKB",
136
+ "u": "zA8CzCf9tZw",
137
+ "t": 0,
138
+ "a": "-MsEWITGkFl-E_aM7qWXZLSOpfi9p_mwjGy47IrSLvk",
139
+ "k": "zA8CzCf9tZw:1kZ1iNJoSjwf-X5DtmtIc6rjNouau0CgPANnaPmGCEw",
140
+ "s": 137080,
141
+ "fa": "113:0*DW1HZGACejc",
142
+ "ts": 1362778998
143
+ },
144
+ {
145
+ "h": "ahozjYba",
146
+ "p": "ywJVFBKB",
147
+ "u": "zA8CzCf9tZw",
148
+ "t": 0,
149
+ "a": "2poE9RKoqBfWeK71Vnv8YKyUAKzUqaUcGDhcToD-Brk",
150
+ "k": "zA8CzCf9tZw:-FjfHWTpg9u48DsPNKxjlrslIZXqenC4CeCTkk_aoek",
151
+ "s": 137080,
152
+ "ts": 1362779516
153
+ },
154
+ {
155
+ "h": "asY3gbSa",
156
+ "p": "ywJVFBKB",
157
+ "u": "zA8CzCf9tZw",
158
+ "t": 0,
159
+ "a": "JYggnUiEG4yQ6KeMi7uYVb1Hd20qfpcE_kgt74-WtW8",
160
+ "k": "zA8CzCf9tZw:jqHCYoJVoHSjbfCoaafcStGQhbzG7YlnKaovknUnMhI",
161
+ "s": 137080,
162
+ "ts": 1362793600
163
+ },
164
+ {
165
+ "h": "Wk4nWISA",
166
+ "p": "ywJVFBKB",
167
+ "u": "zA8CzCf9tZw",
168
+ "t": 0,
169
+ "a": "k11jsEyjmBDwbb0nLO8lJq_BZC0-o8lCnvKy4dHTWAM",
170
+ "k": "zA8CzCf9tZw:jLbBi09Xuh4_EkBKojzXRvmAjRiRdMnq6FTpxLraSJs",
171
+ "s": 39,
172
+ "ts": 1362798430
173
+ },
174
+ {
175
+ "h": "2hBgHbAQ",
176
+ "p": "ywJVFBKB",
177
+ "u": "zA8CzCf9tZw",
178
+ "t": 0,
179
+ "a": "HX1V3cQKT7j3U0tc8UWF-FPOoacebme0wJto2kjOIFr_o4rss15XYMibUpHx1kzd",
180
+ "k": "zA8CzCf9tZw:n2E2YxruJFeifhcSM8pxd8D7wOSoOuQcaAsjip_UmMI",
181
+ "s": 7622058,
182
+ "ts": 1362798784
183
+ },
184
+ {
185
+ "h": "LtZE3YKY",
186
+ "p": "ywJVFBKB",
187
+ "u": "zA8CzCf9tZw",
188
+ "t": 0,
189
+ "a": "81ADi8A0sb761XNa7qQ9qYgT7MO8-3n6ctcZq71PHDs",
190
+ "k": "zA8CzCf9tZw:xffmeXDkLZ0WWzioEaMJAEzdLqB53rSEkY1XzR8aR_A",
191
+ "s": 39,
192
+ "ts": 1362801884
193
+ },
194
+ {
195
+ "h": "X5oiVQKa",
196
+ "p": "ywJVFBKB",
197
+ "u": "zA8CzCf9tZw",
198
+ "t": 0,
199
+ "a": "dyvg4y1uxduQCBPxjtANaQ7tse9DAOk_MUeiIZ_Woxs",
200
+ "k": "zA8CzCf9tZw:ECSKwEkyAvS6lWAQJFWZcH3P48eeAQNQxGYAJQFr9_o",
201
+ "s": 39,
202
+ "ts": 1362802310
203
+ },
204
+ {
205
+ "h": "zl5WEBZL",
206
+ "p": "ywJVFBKB",
207
+ "u": "zA8CzCf9tZw",
208
+ "t": 0,
209
+ "a": "WyFR2PQ1HPCdi5X7GUR98o7Posh_gAtwKcgvGI0wPiA",
210
+ "k": "zA8CzCf9tZw:h4OAIIuQ38G41RMhYTro92JZEuzuuo_Ei0ZSiNNe6nY",
211
+ "s": 39,
212
+ "ts": 1362802849
213
+ },
214
+ {
215
+ "h": "H9BwDLRa",
216
+ "p": "ywJVFBKB",
217
+ "u": "zA8CzCf9tZw",
218
+ "t": 0,
219
+ "a": "qHhpeU0sTjxsIWMCnT7zDXjLChHXt_lPmg42bxn93SI",
220
+ "k": "zA8CzCf9tZw:0t8RnutgNVAJrZCCLv9QJGCBZSRC9zG6SGecIvW4rGE",
221
+ "s": 39,
222
+ "ts": 1362803888
223
+ },
224
+ {
225
+ "h": "79gjRRZa",
226
+ "p": "ywJVFBKB",
227
+ "u": "zA8CzCf9tZw",
228
+ "t": 0,
229
+ "a": "MB1tbC7R_H2_fTQZslTfrwI1guKfw6FJrxUzC1yXFeA",
230
+ "k": "zA8CzCf9tZw:UyN77tmMdhv3OIAi64JEf9OwWIMu11nYv1SNNG1orVs",
231
+ "s": 39,
232
+ "ts": 1362803938
233
+ },
234
+ {
235
+ "h": "f4BCSDRa",
236
+ "p": "ywJVFBKB",
237
+ "u": "zA8CzCf9tZw",
238
+ "t": 0,
239
+ "a": "K1lKkodz26q9BnRpoNbuV6QdEBFx1EYSDGlHgbRIUfA",
240
+ "k": "zA8CzCf9tZw:g_-SqiuxXreNze5_g9ZShPBYpZQ1nxKpyNb85P_7N4s",
241
+ "s": 39,
242
+ "ts": 1362804274
243
+ },
244
+ {
245
+ "h": "q9oRUJhI",
246
+ "p": "ywJVFBKB",
247
+ "u": "zA8CzCf9tZw",
248
+ "t": 0,
249
+ "a": "CrTfHaSw2ueOZoVlCynDL8f-f10gKkuurMrXXRsWnZE",
250
+ "k": "zA8CzCf9tZw:rIgbLqS3Fq3acmFZaN6pNa-ArrjCi2gB0k-ngIeHoUA",
251
+ "s": 117,
252
+ "ts": 1362810737
253
+ },
254
+ {
255
+ "h": "r8RynQzR",
256
+ "p": "ywJVFBKB",
257
+ "u": "zA8CzCf9tZw",
258
+ "t": 0,
259
+ "a": "O2kUBPVRHHnNm42hzp9-0-8_rGH4Ym_bTM8APDA9zsU",
260
+ "k": "zA8CzCf9tZw:cNPzHLkA0NTVAys3esnj9QWNXF34nikUt1NhKK_c1Q8",
261
+ "s": 195,
262
+ "ts": 1362810989
263
+ },
264
+ {
265
+ "h": "P8RT0KgS",
266
+ "p": "ywJVFBKB",
267
+ "u": "zA8CzCf9tZw",
268
+ "t": 0,
269
+ "a": "AJNkLW7wKTziPYvxwkTwQx0a9p8CM-lxEIeR5RZu4R4",
270
+ "k": "zA8CzCf9tZw:LbMSMY5HNMeDjdSU3q0nYGmAOt_WerTU_DO3v7iQdkE",
271
+ "s": 39,
272
+ "ts": 1362811065
273
+ },
274
+ {
275
+ "h": "axwRVSzY",
276
+ "p": "ywJVFBKB",
277
+ "u": "zA8CzCf9tZw",
278
+ "t": 0,
279
+ "a": "HRZtXiNle4qhocTLMYW2mmmCV0j_B9bEvK27yD9xQK8",
280
+ "k": "zA8CzCf9tZw:UYpuIGPDzALgy_1CbvLPSA5Sr6Dv6Et2E4EM3Uw0moI",
281
+ "s": 39,
282
+ "ts": 1363516600
283
+ },
284
+ {
285
+ "h": "btpXFCxR",
286
+ "p": "ywJVFBKB",
287
+ "u": "zA8CzCf9tZw",
288
+ "t": 0,
289
+ "a": "_jTsbHtJPR7fjbmqUK8Wsm7yw-d6nryNO7SPw4NO9TM",
290
+ "k": "zA8CzCf9tZw:IxMuRPVCFh-thywK1acaMjRKp3RerDi1-NEpKp59Uh0",
291
+ "s": 137080,
292
+ "fa": "115:0*qOMemq7l9jY",
293
+ "ts": 1363516781
294
+ },
295
+ {
296
+ "h": "boQCiQiT",
297
+ "p": "ywJVFBKB",
298
+ "u": "zA8CzCf9tZw",
299
+ "t": 0,
300
+ "a": "hmWiu5RHYoCKpiQqPx8hkd5t8g_Qcs_mUlDWsUclBct9HRuCUp_L3mEAwd4l0rD5",
301
+ "k": "zA8CzCf9tZw:4QCgBoj9bYA40I_vq8E6-j3TlEUkfAdW_sW4meyoAvQ",
302
+ "s": 5482930,
303
+ "ts": 1363516963
304
+ },
305
+ {
306
+ "h": "zh5hhBAI",
307
+ "p": "ywJVFBKB",
308
+ "u": "zA8CzCf9tZw",
309
+ "t": 0,
310
+ "a": "m1TCrq-Btvsp4ckHFRHuzaaZFiJKeA1f4wD5s2rFvvs",
311
+ "k": "zA8CzCf9tZw:5sushWWIFZAfDLq1ZKNIVu8P2JryHAiWgKkkUl05k6g",
312
+ "s": 5482930,
313
+ "ts": 1363517214
314
+ },
315
+ {
316
+ "h": "ep4XnTxQ",
317
+ "p": "jtwkAQaK",
318
+ "u": "zA8CzCf9tZw",
319
+ "t": 0,
320
+ "a": "Cs1l7Rew5I2GGbyzw8VePAhoPyMkMHTVXoa2-SN4-uo",
321
+ "k": "zA8CzCf9tZw:09CaiWPvTBrPQhxWxKoIgUbkZNKNJRSQmJ0hHzw3JvI",
322
+ "s": 39,
323
+ "ts": 1362803268
324
+ },
325
+ {
326
+ "h": "b4IiHZII",
327
+ "p": "jtwkAQaK",
328
+ "u": "zA8CzCf9tZw",
329
+ "t": 0,
330
+ "a": "uazdd2xapOTa5p_WMSVtav2hytWZw9eYyqTOzUAFpDc",
331
+ "k": "zA8CzCf9tZw:cI9G4pGAAExuZlPILGyqudknvWxh9rse00yWWC7pgp8",
332
+ "s": 78,
333
+ "ts": 1362805795
334
+ },
335
+ {
336
+ "h": "jgQg3ALZ",
337
+ "p": "jtwkAQaK",
338
+ "u": "zA8CzCf9tZw",
339
+ "t": 0,
340
+ "a": "veRpCzk5S22KWwlnOqdgtqt3rWFM76XztTygOevPp34",
341
+ "k": "zA8CzCf9tZw:esxiNRkxr1KscaCsJuxg19qu7mXrgTB5tLwbtHMZoAs",
342
+ "s": 156,
343
+ "ts": 1362805852
344
+ },
345
+ {
346
+ "h": "fkQ0wZjZ",
347
+ "p": "jtwkAQaK",
348
+ "u": "zA8CzCf9tZw",
349
+ "t": 0,
350
+ "a": "cPDFcFwRVTaCB-DxBAGCUxwD8qrgt1B2OMBGK-VNPME",
351
+ "k": "zA8CzCf9tZw:32zJUYIXo4uF-wgbHj-tlCxkKl4pYvs2gP0YIajuWdQ",
352
+ "s": 117,
353
+ "ts": 1362805994
354
+ },
355
+ {
356
+ "h": "DsJClQSZ",
357
+ "p": "jtwkAQaK",
358
+ "u": "zA8CzCf9tZw",
359
+ "t": 0,
360
+ "a": "-oN7uRxlI-WrKsT03Zju5luB7z3Lg7uKVWSRczZsf7k",
361
+ "k": "zA8CzCf9tZw:Ut4_ESCi5yk71HroPJidRIqwPwyy3ZWsjz3NcgdPfBM",
362
+ "s": 195,
363
+ "ts": 1362810877
364
+ },
365
+ {
366
+ "h": "j9RWXTqC",
367
+ "p": "jtwkAQaK",
368
+ "u": "zA8CzCf9tZw",
369
+ "t": 0,
370
+ "a": "h2ekShHqJMnbjk3uvW9n8hTGF07UySjFYvfTo5ktcCk",
371
+ "k": "zA8CzCf9tZw:4Q-l3ygtVC8-0p5CtWVWZkMc0G49aYKxCjx6vWO8Wvw",
372
+ "s": 195,
373
+ "ts": 1362810882
374
+ },
375
+ {
376
+ "h": "jopGGDJZ",
377
+ "p": "jtwkAQaK",
378
+ "u": "zA8CzCf9tZw",
379
+ "t": 0,
380
+ "a": "oJlWwnaj_tjQf__zDiqQm3zKhemldlfH-boZb7Cjri8",
381
+ "k": "zA8CzCf9tZw:Xol_vUW-InH4GMae5dZahrj2noOdLwibtgyGdzNSEvM",
382
+ "s": 195,
383
+ "ts": 1362810883
122
384
  }
123
385
  ],
124
386
  "ok": [
@@ -134,17 +396,18 @@
134
396
  "m": "megartest@gmail.com"
135
397
  }
136
398
  ],
137
- "sn": "rkC92G0-SNg"
399
+ "sn": "71k37B8iTRY"
138
400
  },
139
401
  "file_upload_url_response": {
140
- "p": "http://gfs262n185.userstorage.mega.co.nz/ul/YKxC-sS4xNF8MgL4iddMm9PNukmzMODqpxWsV5CquftEe5YDUZaq2_QSuqt8ubYtt0_VYE-yLk2gN5uJIyDHhQ"
402
+ "p": "http://gfs262n153.userstorage.mega.co.nz/ul/m8ZGZ_6t5WZHlQIlxfoPJjq3g31KcumFhIJiDoWrmTo1UNjleDcbzk0T8aGIsYX10KZccP08iFlTBGXWF79Jnw"
141
403
  },
142
404
  "sample_files": {
143
405
  "megar_test_sample_1.txt": {
144
406
  "file_download_url_response": {
145
407
  "s": 39,
146
408
  "at": "lv-LcMAl0dvpdxQFWNVZ7m9-mFT79mS0Mi_fH9InTqtBgcpe8kRVDGQ5Hj4BrXrc",
147
- "g": "http://gfs262n164.userstorage.mega.co.nz/dl/Hv0Huu5AKOP1CQKwZNxpr7JMVXST1oxYEWT08YF3WIAWIDX4-3-bKAGVDU13ESYqBmcMDQGDsohpihtrDwEIpz3W5yvdTIFAhhWR49qpZvi3yfkhGg"
409
+ "g": "http://gfs262n164.userstorage.mega.co.nz/dl/nZUYBF_XMjz2fQKIdUQ8jsNYgY_Rucr3ykY900Mow4DtWWYq0wt-jt0tBC2cIQTOR6Modf11n-ekN4FZb01mgq2TSPcRDGy2-X_6uumuN0c-n83KZw",
410
+ "pfa": 1
148
411
  },
149
412
  "key": [
150
413
  2080156050,
@@ -179,7 +442,8 @@
179
442
  "file_download_url_response": {
180
443
  "s": 137080,
181
444
  "at": "OFTM18eQGxUTcJP2BQWXBo23vnjqs4b9QzmNZtDqekz4GZESVNiYlqKtasZqdVHN",
182
- "g": "http://gfs262n170.userstorage.mega.co.nz/dl/ZWHZvT4lAtuWdwKYNzKGjqDfAvohHAi7AnKmQCCCWormdDhn-CLkuVb5Onah2GxtzcJ_vQFoI_EWAxIxGyBIXrsRLw60IJ6ZNjNWiDpcPG_6jTgP4A"
445
+ "g": "http://gfs262n170.userstorage.mega.co.nz/dl/RAS3RZCZPTWLCwLef5jWLhfjdHgnQYLqpQLb0xODrKWGcc5Og31zTYWov4dQ1xq65UUrAxOgwUvejHZshD3pBl6zybqiubp0cz_FX4FpsOuHsXVhTg",
446
+ "pfa": 1
183
447
  },
184
448
  "key": [
185
449
  1029178532,
@@ -18,7 +18,8 @@ describe Megar::FileUploader do
18
18
  [
19
19
  {
20
20
  upload_key: [3230625094, 2656764682, 1008836587, 1082599785, 1919494632, 3993726968],
21
- expected_iv: 152078032723025278718811426614033776640,
21
+ # expected_iv: 152078032723025278718811426614033776640,
22
+ expected_iv: [1919494632, 3993726968, 0, 0],
22
23
  expected_mac_iv: [1919494632, 3993726968,1919494632, 3993726968],
23
24
  expected_mac_encryption_key: [3230625094, 2656764682, 1008836587, 1082599785]
24
25
  }
@@ -36,11 +36,40 @@ describe Megar::Connection do
36
36
  describe "#api_request" do
37
37
  let(:data) { {} }
38
38
  subject { harness.api_request(data) }
39
- context "when error response" do
40
- let(:response_data) { JSON.parse("[-15,-15,-15]") }
41
- it "should raise associated error" do
42
- harness.stub(:get_api_response).and_return(response_data)
43
- expect { subject }.to raise_error(Megar::MegaRequestError)
39
+
40
+ {
41
+ as_json: "[-15,-15,-15]",
42
+ as_error_code: "-15"
43
+ }.each do |test_name,given_response|
44
+ context "when mega error response (#{given_response})" do
45
+ it "should raise associated error" do
46
+ harness.stub(:get_api_response).and_return(given_response)
47
+ expect { subject }.to raise_error(Megar::MegaRequestError)
48
+ end
49
+ end
50
+ end
51
+
52
+ {
53
+ as_broken_json: "[{,{},{}]"
54
+ }.each do |test_name,given_response|
55
+ context "when invalid JSON response (#{given_response})" do
56
+ it "should raise associated error" do
57
+ harness.stub(:get_api_response).and_return(given_response)
58
+ expect { subject }.to raise_error(Megar::BadApiResponseError)
59
+ end
60
+ end
61
+ end
62
+
63
+ {
64
+ as_json: { given: '[{"a":1},{"b":2}]', expect: {'a' => 1} },
65
+ as_unarray: { given: '{"a":1}', expect: {'a' => 1} }
66
+ }.each do |test_name,expectations|
67
+ context "when valid response (#{expectations[:given]})" do
68
+ before { harness.stub(:get_api_response).and_return(expectations[:given]) }
69
+ it "should not raise error" do
70
+ expect { subject }.to_not raise_error
71
+ end
72
+ it { should eql(expectations[:expect]) }
44
73
  end
45
74
  end
46
75
 
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Megar::Crypto::AesCtr do
4
+ let(:harness) { Megar::Crypto::AesCtr.new(options) }
5
+ let(:options) { {} }
6
+
7
+ describe "#key" do
8
+ subject { harness.key }
9
+ context "when provided as array of int" do
10
+ let(:options) { { key: [0x40404040,0x40404040,0x40404040,0x40404040] } }
11
+ it { should eql('@@@@@@@@@@@@@@@@') }
12
+ end
13
+ context "when provided as a binary string" do
14
+ let(:options) { { key: 'abcd' } }
15
+ it { should eql('abcd') }
16
+ end
17
+ end
18
+
19
+ describe "#iv" do
20
+ subject { harness.iv }
21
+ context "when not provided" do
22
+ it { should eql([0,0,0,0]) }
23
+ end
24
+ context "when provided as array of int" do
25
+ let(:options) { { iv: [1,2,3,4] } }
26
+ it { should eql([1,2,3,4]) }
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Megar::Crypto::Aes do
4
+ let(:harness) { Megar::Crypto::Aes.new(options) }
5
+ let(:options) { { key: key } }
6
+
7
+ describe "#encrypt" do
8
+ subject { harness.encrypt(data) }
9
+ # expectation generation in Javascript:
10
+ # key = [0,0,0,0]
11
+ # data = [-1965633819,-2121597728,1547823083,-1677263149]
12
+ # cipher = new sjcl.cipher.aes(key)
13
+ # cipher.encrypt(data)
14
+ [
15
+ { data: [0x93C467E3,0x7DB0C7A4,0xD1BE3F81,0x0152CB56], key: [0,0,0,0], expect: [887729479,-1472906423,407560426,1302943674] },
16
+ { data: [887729479,-1472906423,407560426,1302943674], key: [602974403,-1330001938,-1976634718,-894142530], expect: [-19364982,-598654435,1840800477,-1490065331] }
17
+ ].each do |test_case|
18
+ context "given #{test_case[:data]}" do
19
+ let(:key) { test_case[:key] }
20
+ let(:data) { test_case[:data] }
21
+ it { should eql(test_case[:expect]) }
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "#encrypt" do
27
+ subject { harness.decrypt(data) }
28
+ # expectation generation in Javascript:
29
+ # key = prepare_key_pw('NS7j8OKCfGeEEaUK') // [1258112910,-1520042757,-243943422,-1960187198]
30
+ # data = [887729479,-1472906423,407560426,1302943674]
31
+ # cipher = new sjcl.cipher.aes(key)
32
+ # cipher.decrypt(data) // [480935216,755335218,-883525214,599824580]
33
+ [
34
+ { data: [887729479,-1472906423,407560426,1302943674], key: [1258112910,-1520042757,-243943422,-1960187198], expect: [480935216,755335218,-883525214,599824580] },
35
+ { data: [887729479,-1472906423,407560426,1302943674], key: [0,0,0,0], expect: [-1815844893,2108737444,-776061055,22203222] },
36
+ { data: [-19364982,-598654435,1840800477,-1490065331], key: [602974403,-1330001938,-1976634718,-894142530], expect: [887729479,-1472906423,407560426,1302943674] },
37
+ { data: [0x93C467E3,0x7DB0C7A4,0xD1BE3F81,0x0152CB56], key: [0,0,0,0], expect: [-1965633819,-2121597728,1547823083,-1677263149] }
38
+ ].each do |test_case|
39
+ context "given #{test_case[:data]}" do
40
+ let(:key) { test_case[:key] }
41
+ let(:data) { test_case[:data] }
42
+ it { should eql(test_case[:expect]) }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  class CryptoSupportTestHarness
4
- include Megar::CryptoSupport
4
+ include Megar::Crypto::Support
5
5
  end
6
6
 
7
- describe Megar::CryptoSupport do
7
+ describe Megar::Crypto::Support do
8
8
  let(:harness) { CryptoSupportTestHarness.new }
9
9
 
10
10
  describe "#crypto_requirements_met?" do
@@ -7,7 +7,8 @@ describe "Megar Exceptions" do
7
7
  Megar::CryptoSupportRequirementsError,
8
8
  Megar::MacVerificationError,
9
9
  Megar::UnsupportedFileHandleTypeError,
10
- Megar::FileUploadError
10
+ Megar::FileUploadError,
11
+ Megar::BadApiResponseError
11
12
  ].each do |exception_class|
12
13
  describe exception_class do
13
14
  subject { raise exception_class.new("test") }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: megar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-09 00:00:00.000000000 Z
12
+ date: 2013-03-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: getoptions
@@ -181,6 +181,8 @@ files:
181
181
  - lib/megar/catalog/folders.rb
182
182
  - lib/megar/connection.rb
183
183
  - lib/megar/crypto.rb
184
+ - lib/megar/crypto/aes.rb
185
+ - lib/megar/crypto/aes_ctr.rb
184
186
  - lib/megar/crypto/support.rb
185
187
  - lib/megar/exception.rb
186
188
  - lib/megar/session.rb
@@ -202,6 +204,8 @@ files:
202
204
  - spec/unit/catalog/folder_spec.rb
203
205
  - spec/unit/catalog/folders_spec.rb
204
206
  - spec/unit/connection_spec.rb
207
+ - spec/unit/crypto/aes_ctr_spec.rb
208
+ - spec/unit/crypto/aes_spec.rb
205
209
  - spec/unit/crypto/support_spec.rb
206
210
  - spec/unit/exception_spec.rb
207
211
  - spec/unit/extensions/math_spec.rb
@@ -247,6 +251,8 @@ test_files:
247
251
  - spec/unit/catalog/folder_spec.rb
248
252
  - spec/unit/catalog/folders_spec.rb
249
253
  - spec/unit/connection_spec.rb
254
+ - spec/unit/crypto/aes_ctr_spec.rb
255
+ - spec/unit/crypto/aes_spec.rb
250
256
  - spec/unit/crypto/support_spec.rb
251
257
  - spec/unit/exception_spec.rb
252
258
  - spec/unit/extensions/math_spec.rb