megar 0.0.3 → 0.0.4
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.
- data/CHANGELOG +5 -0
- data/README.rdoc +22 -13
- data/lib/megar/adapters/file_downloader.rb +2 -2
- data/lib/megar/adapters/file_uploader.rb +4 -7
- data/lib/megar/connection.rb +19 -8
- data/lib/megar/crypto.rb +5 -1
- data/lib/megar/crypto/aes.rb +60 -0
- data/lib/megar/crypto/aes_ctr.rb +75 -0
- data/lib/megar/crypto/support.rb +12 -55
- data/lib/megar/exception.rb +3 -0
- data/lib/megar/session.rb +1 -1
- data/lib/megar/version.rb +1 -1
- data/spec/fixtures/crypto_expectations/sample_user.json +273 -9
- data/spec/unit/adapters/file_uploader_spec.rb +2 -1
- data/spec/unit/connection_spec.rb +34 -5
- data/spec/unit/crypto/aes_ctr_spec.rb +30 -0
- data/spec/unit/crypto/aes_spec.rb +46 -0
- data/spec/unit/crypto/support_spec.rb +2 -2
- data/spec/unit/exception_spec.rb +2 -1
- metadata +8 -2
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
|
data/README.rdoc
CHANGED
@@ -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
|
-
|
6
|
-
download files.
|
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
|
-
===
|
326
|
+
=== Checking my OpenSSL installation
|
320
327
|
|
321
|
-
|
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
|
333
|
-
|
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
|
-
$
|
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::
|
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 =
|
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::
|
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 =
|
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
|
-
|
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
|
data/lib/megar/connection.rb
CHANGED
@@ -51,14 +51,11 @@ module Megar::Connection
|
|
51
51
|
params['sid'] = sid if sid
|
52
52
|
json_data = [data].to_json
|
53
53
|
|
54
|
-
|
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
|
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
|
-
|
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('&'))
|
data/lib/megar/crypto.rb
CHANGED
@@ -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
|
data/lib/megar/crypto/support.rb
CHANGED
@@ -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::
|
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-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
499
|
-
|
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)
|
data/lib/megar/exception.rb
CHANGED
@@ -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
|
data/lib/megar/session.rb
CHANGED
data/lib/megar/version.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"email": "megartest@gmail.com",
|
3
3
|
"email_mixed_case": "Megartest@gmail.com",
|
4
|
-
"password": "
|
4
|
+
"password": "arDGGNCVPQEZKqasmfjQSvOmgOPdLhBX",
|
5
5
|
"autoconnect": false,
|
6
6
|
"login_response_data": {
|
7
|
-
"csid": "
|
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": "
|
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": "
|
18
|
-
"sid": "
|
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": "
|
399
|
+
"sn": "71k37B8iTRY"
|
138
400
|
},
|
139
401
|
"file_upload_url_response": {
|
140
|
-
"p": "http://
|
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/
|
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/
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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::
|
4
|
+
include Megar::Crypto::Support
|
5
5
|
end
|
6
6
|
|
7
|
-
describe Megar::
|
7
|
+
describe Megar::Crypto::Support do
|
8
8
|
let(:harness) { CryptoSupportTestHarness.new }
|
9
9
|
|
10
10
|
describe "#crypto_requirements_met?" do
|
data/spec/unit/exception_spec.rb
CHANGED
@@ -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.
|
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-
|
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
|