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,24 @@
1
+ require 'megar'
2
+
3
+ # Requires supporting files with custom matchers and macros, etc,
4
+ # in ./support/ and its subdirectories.
5
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
6
+
7
+ RSpec.configure do |config|
8
+ # == Mock Framework
9
+ #
10
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
11
+ #
12
+ # config.mock_with :mocha
13
+ # config.mock_with :flexmock
14
+ # config.mock_with :rr
15
+ config.mock_with :rspec
16
+
17
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
18
+ # config.fixture_path = "#{::Rails.root}/spec/fixtures"
19
+
20
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
21
+ # examples within a transaction, remove the following line or assign false
22
+ # instead of true.
23
+ # config.use_transactional_fixtures = true
24
+ end
@@ -0,0 +1,44 @@
1
+ require 'pathname'
2
+ require 'json'
3
+
4
+ module CryptoExpectationsHelper
5
+
6
+ def crypto_expectations_path
7
+ Pathname.new(File.dirname(__FILE__)).join('..','fixtures','crypto_expectations')
8
+ end
9
+
10
+ def crypto_expectations_sample_path(sample_name)
11
+ crypto_expectations_path.join("#{sample_name}.json")
12
+ end
13
+
14
+ # Returns the JSON representation of +sample_name+ expectations
15
+ def crypto_expectations(sample_name)
16
+ JSON.parse(crypto_expectations_sample_path(sample_name).read)
17
+ end
18
+
19
+ def generate_crypto_expectations(email,password)
20
+ STDERR.puts "\nGenerating crypto_expectations for #{email}..."
21
+ e = {email: email.downcase, email_mixed_case: email.capitalize, password: password, autoconnect: false }
22
+ if session = Megar::Session.new(email: email, password: password)
23
+ e[:login_response_data] = session.send(:get_login_response)
24
+ session.send(:handle_login_challenge_response,e[:login_response_data])
25
+ e[:master_key] = session.master_key
26
+ e[:expected_uh] = session.send(:uh)
27
+ e[:sid] = session.sid
28
+ e[:rsa_private_key_b64] = session.rsa_private_key_b64
29
+ e[:decomposed_rsa_private_key] = session.decomposed_rsa_private_key
30
+ e[:files_response_data] = session.send(:get_files_response)
31
+ end
32
+ efn = crypto_expectations_sample_path('sample_user')
33
+ ef = File.open(efn,'w')
34
+ ef.write JSON.pretty_generate(e)
35
+ ef.close
36
+ STDERR.puts "\nDone! New crypto_expectations for unit test written to:\n#{efn}\n\n"
37
+ end
38
+
39
+ end
40
+
41
+
42
+ RSpec.configure do |conf|
43
+ conf.include CryptoExpectationsHelper
44
+ end
@@ -0,0 +1,22 @@
1
+ module MocksHelper
2
+
3
+ def session_with_mocked_api_responses(options, mock_dataset='sample_user')
4
+ test_data = crypto_expectations(mock_dataset)
5
+ session = Megar::Session.new(options.merge(autoconnect: false))
6
+ session.stub(:get_login_response).and_return(test_data['login_response_data'])
7
+ session.stub(:get_files_response).and_return(test_data['files_response_data'])
8
+ session
9
+ end
10
+
11
+ def connected_session_with_mocked_api_responses(options, mock_dataset='sample_user')
12
+ session = session_with_mocked_api_responses(options, mock_dataset='sample_user')
13
+ session.connect!
14
+ session
15
+ end
16
+
17
+ end
18
+
19
+
20
+ RSpec.configure do |conf|
21
+ conf.include MocksHelper
22
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Megar::File do
4
+ let(:model_class) { Megar::File }
5
+ let(:instance) { model_class.new(attributes) }
6
+ let(:attributes) { {} }
7
+
8
+ context "when initialised" do
9
+ let(:id) { 'some id' }
10
+ let(:name) { 'some name' }
11
+ let(:type) { 0 }
12
+ let(:size) { 33 }
13
+ let(:payload) { {
14
+ 's' => size
15
+ } }
16
+ let(:attributes) { {
17
+ id: id,
18
+ type: type,
19
+ payload: payload,
20
+ attributes: {
21
+ 'n' => name
22
+ }
23
+ } }
24
+ subject { instance }
25
+ its(:id) { should eql(id) }
26
+ its(:name) { should eql(name) }
27
+ its(:type) { should eql(type) }
28
+ its(:size) { should eql(size) }
29
+ end
30
+
31
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Megar::Files do
4
+ let(:model_class) { Megar::Files }
5
+ let(:resource_class) { Megar::File }
6
+ let(:instance) { model_class.new }
7
+
8
+ let(:other_resource) { resource_class.new(id: "other_resource_id", type: 1) }
9
+
10
+ describe "#resource_class" do
11
+ subject { instance.resource_class }
12
+ it { should eql(resource_class) }
13
+ end
14
+
15
+ describe "#reset!" do
16
+ before do
17
+ instance.collection << other_resource
18
+ end
19
+ let(:reset) { instance.reset! }
20
+ it "should clear the collection" do
21
+ reset
22
+ instance.collection.should be_empty
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Megar::Folder do
4
+ let(:model_class) { Megar::Folder }
5
+ let(:instance) { model_class.new(attributes) }
6
+ let(:attributes) { {} }
7
+
8
+ context "when initialised" do
9
+ let(:id) { 'some id' }
10
+ let(:name) { 'some name' }
11
+ let(:type) { 1 }
12
+ let(:payload) { {
13
+ } }
14
+ let(:attributes) { {
15
+ id: id,
16
+ type: type,
17
+ payload: payload,
18
+ attributes: {
19
+ 'n' => name
20
+ }
21
+ } }
22
+ subject { instance }
23
+ its(:id) { should eql(id) }
24
+ its(:name) { should eql(name) }
25
+ its(:type) { should eql(type) }
26
+ end
27
+
28
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Megar::Folders do
4
+ let(:model_class) { Megar::Folders }
5
+ let(:resource_class) { Megar::Folder }
6
+ let(:instance) { model_class.new }
7
+
8
+ let(:other_resource) { resource_class.new(id: "other_resource_id", type: 1) }
9
+
10
+ describe "#resource_class" do
11
+ subject { instance.resource_class }
12
+ it { should eql(resource_class) }
13
+ end
14
+
15
+ describe "#reset!" do
16
+ before do
17
+ instance.collection << other_resource
18
+ end
19
+ let(:reset) { instance.reset! }
20
+ it "should clear the collection" do
21
+ reset
22
+ instance.collection.should be_empty
23
+ end
24
+ end
25
+
26
+ {
27
+ root: { type: 2, expected_name: "Cloud Drive" },
28
+ inbox: { type: 3, expected_name: "Inbox" },
29
+ trash: { type: 4, expected_name: "Trash Bin" }
30
+ }.each do |special_method_name,options|
31
+ describe "##{special_method_name}" do
32
+ subject { instance.send(special_method_name) }
33
+ context "when not available or defined" do
34
+ it { should be_nil }
35
+ end
36
+ context "when defined" do
37
+ let(:found_resource) { resource_class.new(id: "#{special_method_name}_id", type: options[:type]) }
38
+ let(:expected_name) { options[:expected_name] }
39
+ before do
40
+ instance.collection << found_resource
41
+ instance.collection << other_resource
42
+ end
43
+ it { should eql(found_resource) }
44
+ its(:name) { should eql(expected_name) }
45
+ end
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ class ConnectionTestHarness
4
+ include Megar::Connection
5
+ end
6
+
7
+ describe Megar::Connection do
8
+ let(:harness) { ConnectionTestHarness.new }
9
+
10
+ describe "#sequence_number" do
11
+ subject { harness.sequence_number }
12
+ it { should_not be_nil }
13
+ end
14
+
15
+ describe "#next_sequence_number!" do
16
+ let!(:current_sequence_number) { harness.sequence_number }
17
+ subject { harness.next_sequence_number! }
18
+ it { should eql(current_sequence_number + 1)}
19
+ end
20
+
21
+ describe "#api_endpoint" do
22
+ subject { harness.api_endpoint }
23
+ it { should eql(Megar::Connection::DEFAULT_API_ENDPOINT) }
24
+ context "when overidden" do
25
+ let(:alternative_endpoint) { 'http://bogative.one' }
26
+ before { harness.api_endpoint = alternative_endpoint }
27
+ it { should eql(alternative_endpoint) }
28
+ end
29
+ end
30
+
31
+ describe "#api_uri" do
32
+ subject { harness.api_uri }
33
+ it { should be_a(URI) }
34
+ end
35
+
36
+ describe "#api_request" do
37
+ let(:data) { {} }
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)
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
@@ -0,0 +1,476 @@
1
+ require 'spec_helper'
2
+
3
+ class CryptoTestHarness
4
+ include Megar::Crypto
5
+ end
6
+
7
+ describe Megar::Crypto do
8
+ let(:harness) { CryptoTestHarness.new }
9
+
10
+ describe "#str_to_a32" do
11
+ subject { harness.str_to_a32(string) }
12
+ # expectation generation in Javascript:
13
+ # str_to_a32(base64urldecode('zL-S9BspoEopTUm3z3O8CA'))
14
+ [
15
+ { given: "\xCC\xBF\x92\xF4\e)\xA0J)MI\xB7\xCFs\xBC\b", expect: [-859860236,455712842,692930999,-814498808] },
16
+ { given: 'a', expect: [1627389952] },
17
+ { given: 'aaaaaaaaaaaaaaa', expect: [1633771873,1633771873,1633771873,1633771776] }
18
+ ].each do |test_case|
19
+ context "given #{test_case[:given]}" do
20
+ let(:string) { test_case[:given] }
21
+ it { should eql(test_case[:expect]) }
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "#a32_to_str" do
27
+ subject { harness.a32_to_str(a32) }
28
+ # expectation generation in Javascript:
29
+ # a32_to_str([602974403,-1330001938,-1976634718,-894142530])
30
+ [
31
+ { given: [1633837924], expect: 'abcd' },
32
+ { given: [602974403,-1330001938,-1976634718,-894142530], expect: "\#\xF0\xA8\xC3\xB0\xB9\xC7\xEE\x8A.\xF2\xA2\xCA\xB4w\xBE" },
33
+ { given: [1633771873,1633771873,1633771873,1633771873], expect: 'aaaaaaaaaaaaaaaa' }
34
+ ].each do |test_case|
35
+ context "given #{test_case[:given]}" do
36
+ let(:a32) { test_case[:given] }
37
+ it { should eql(test_case[:expect]) }
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "#aes_encrypt_a32" do
43
+ subject { harness.aes_encrypt_a32(data,key) }
44
+ # expectation generation in Javascript:
45
+ # key = [0,0,0,0]
46
+ # data = [-1965633819,-2121597728,1547823083,-1677263149]
47
+ # cipher = new sjcl.cipher.aes(key)
48
+ # cipher.encrypt(data)
49
+ [
50
+ { data: [0x93C467E3,0x7DB0C7A4,0xD1BE3F81,0x0152CB56], key: [0,0,0,0], expect: [887729479,-1472906423,407560426,1302943674] },
51
+ { data: [887729479,-1472906423,407560426,1302943674], key: [602974403,-1330001938,-1976634718,-894142530], expect: [-19364982,-598654435,1840800477,-1490065331] }
52
+ ].each do |test_case|
53
+ context "given #{test_case[:data]}" do
54
+ let(:key) { test_case[:key] }
55
+ let(:data) { test_case[:data] }
56
+ it { should eql(test_case[:expect]) }
57
+ end
58
+ end
59
+ end
60
+
61
+ describe "#aes_cbc_decrypt_a32" do
62
+ subject { harness.aes_cbc_decrypt_a32(data,key) }
63
+ # expectation generation in Javascript:
64
+ # key = prepare_key_pw('NS7j8OKCfGeEEaUK') // [1258112910,-1520042757,-243943422,-1960187198]
65
+ # data = [887729479,-1472906423,407560426,1302943674]
66
+ # cipher = new sjcl.cipher.aes(key)
67
+ # cipher.decrypt(data) // [480935216,755335218,-883525214,599824580]
68
+ [
69
+ { data: [887729479,-1472906423,407560426,1302943674], key: [1258112910,-1520042757,-243943422,-1960187198], expect: [480935216,755335218,-883525214,599824580] },
70
+ { data: [887729479,-1472906423,407560426,1302943674], key: [0,0,0,0], expect: [-1815844893,2108737444,-776061055,22203222] },
71
+ { data: [-19364982,-598654435,1840800477,-1490065331], key: [602974403,-1330001938,-1976634718,-894142530], expect: [887729479,-1472906423,407560426,1302943674] },
72
+ { data: [0x93C467E3,0x7DB0C7A4,0xD1BE3F81,0x0152CB56], key: [0,0,0,0], expect: [-1965633819,-2121597728,1547823083,-1677263149] }
73
+ ].each do |test_case|
74
+ context "given #{test_case[:data]}" do
75
+ let(:key) { test_case[:key] }
76
+ let(:data) { test_case[:data] }
77
+ it { should eql(test_case[:expect]) }
78
+ end
79
+ end
80
+ end
81
+
82
+
83
+ describe "#prepare_key" do
84
+ subject { harness.prepare_key(data) }
85
+ [
86
+ { data: [0x93C467E3,0x7DB0C7A4,0xD1BE3F81,0x0152CB56], expect: [ 1611938008, 1148719119, -1340889484, -1964978551] }
87
+ ].each do |test_case|
88
+ context "given #{test_case[:data]}" do
89
+ let(:data) { test_case[:data] }
90
+ it { should eql(test_case[:expect]) }
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "#decrypt_key" do
96
+ subject { harness.decrypt_key(data,key) }
97
+ # expectation generation in Javascript:
98
+ # key = prepare_key_pw('megar123456$') // [-2024856631,-2045176755,-210601452,1003386405]
99
+ # data = base64_to_a32("zL-S9BspoEopTUm3z3O8CA") // [-859860236,455712842,692930999,-814498808]
100
+ # aes = new sjcl.cipher.aes(key)
101
+ # master_key = decrypt_key(aes,data) // [327661033,-2034153005,1144280438,-1676633549]
102
+ #
103
+ # key = prepare_key_pw('NS7j8OKCfGeEEaUK') // [1258112910,-1520042757,-243943422,-1960187198]
104
+ # data = base64_to_a32("7oKN6U8Y0R2ancrbWjmMew") // [-293433879,1327026461,-1700934949,1513720955]
105
+ # aes = new sjcl.cipher.aes(key)
106
+ # master_key = decrypt_key(aes,data) // [384287193,302859698,554881366,530403344]
107
+ #
108
+ [
109
+ { data: [-293433879,1327026461,-1700934949,1513720955], key: [1258112910,-1520042757,-243943422,-1960187198], expect: [384287193,302859698,554881366,530403344] },
110
+ { data: [602974403,-1330001938,-1976634718,-894142530], key: [ 1611938008, 1148719119, -1340889484, -1964978551], expect: [1393105163, -90783891, 1912327600, 1525324017] },
111
+ { data: [-859860236,455712842,692930999,-814498808], key: [-2024856631,-2045176755,-210601452,1003386405], expect: [327661033,-2034153005,1144280438,-1676633549] },
112
+ { data: [-859860236,455712842,692930999,-814498808], key: [602974403,-1330001938,-1976634718,-894142530], expect: [1049027610,743989201,1864038849,230624922] }
113
+ ].each do |test_case|
114
+ context "given #{test_case[:data]}" do
115
+ let(:key) { test_case[:key] }
116
+ let(:data) { test_case[:data] }
117
+ it { should eql(test_case[:expect]) }
118
+ end
119
+ end
120
+ end
121
+
122
+ describe "#prepare_key_pw" do
123
+ subject { harness.prepare_key_pw(password) }
124
+ # expectation generation in Javascript:
125
+ # key = prepare_key_pw('NS7j8OKCfGeEEaUK')
126
+ [
127
+ { given: 'abcd', expect: [-1360067798,1616656778,-731604536,739132024] },
128
+ { given: 'NS7j8OKCfGeEEaUK', expect: [1258112910,-1520042757,-243943422,-1960187198] }
129
+ ].each do |test_case|
130
+ context "given #{test_case[:given]}" do
131
+ let(:password) { test_case[:given] }
132
+ it { should eql(test_case[:expect]) }
133
+ end
134
+ end
135
+ end
136
+
137
+ describe "#base64urlencode" do
138
+ subject { harness.base64urlencode(data) }
139
+ [
140
+ { given: 'abcd1234', expect: 'YWJjZDEyMzQ' }
141
+ ].each do |test_case|
142
+ context "given #{test_case[:given]}" do
143
+ let(:data) { test_case[:given] }
144
+ it { should eql(test_case[:expect]) }
145
+ end
146
+ end
147
+ end
148
+
149
+ describe "#base64urldecode" do
150
+ subject { harness.base64urldecode(data) }
151
+ # expectation generation in Javascript:
152
+ # base64urldecode('zL-S9BspoEopTUm3z3O8CA')
153
+ [
154
+ { given: 'zL-S9BspoEopTUm3z3O8CA', expect: "\xCC\xBF\x92\xF4\e)\xA0J)MI\xB7\xCFs\xBC\b" },
155
+ { given: 'YWJjZDEyMzQ', expect: 'abcd1234' },
156
+ { given: 'YXNkamJhc2RqY2JuYXNrZDtjam47a2Fqc25kO2puYXM7ZGZqbmtqYmFmdg', expect: 'asdjbasdjcbnaskd;cjn;kajsnd;jnas;dfjnkjbafv' }
157
+ ].each do |test_case|
158
+ context "given #{test_case[:given]}" do
159
+ let(:data) { test_case[:given] }
160
+ it { should eql(test_case[:expect]) }
161
+ end
162
+ end
163
+ end
164
+
165
+
166
+ describe "#a32_to_base64" do
167
+ subject { harness.a32_to_base64(data) }
168
+ [
169
+ { given: [-1815844893,2108737444,-776061055,22203222], expect: 'k8Rn432wx6TRvj-BAVLLVg' },
170
+ { given: [0x93C467E3,0x7DB0C7A4,0xD1BE3F81,0x0152CB56], expect: 'k8Rn432wx6TRvj-BAVLLVg' }
171
+ ].each do |test_case|
172
+ context "given #{test_case[:given]}" do
173
+ let(:data) { test_case[:given] }
174
+ it { should eql(test_case[:expect]) }
175
+ end
176
+ end
177
+ end
178
+
179
+ describe "#base64_to_a32" do
180
+ subject { harness.base64_to_a32(data) }
181
+ # expectation generation in Javascript:
182
+ # base64_to_a32("zL-S9BspoEopTUm3z3O8CA")
183
+ [
184
+ { given: 'zL-S9BspoEopTUm3z3O8CA', expect: [-859860236,455712842,692930999,-814498808] }
185
+ # { given: 'k8Rn432wx6TRvj-BAVLLVg', expect: [-1815844893,2108737444,-776061055,22203222] }
186
+ ].each do |test_case|
187
+ context "given #{test_case[:given]}" do
188
+ let(:data) { test_case[:given] }
189
+ it { should eql(test_case[:expect]) }
190
+ end
191
+ end
192
+ end
193
+
194
+
195
+ describe "#mpi_to_a32" do
196
+ subject { harness.mpi_to_a32(data) }
197
+ # expectation generation in Javascript:
198
+ # b64 = "ABwP____"
199
+ # data = base64urldecode(b64) // "\x00\x1C\x0F\xFF\xFF\xFF"
200
+ # mpi2b(data)
201
+ [
202
+ { given: "\x00\x1C\x0F\xFF\xFF\xFF", expect: [0x0FFFFFFF, 0] } # this is an idiosyncratic result of the Javascript mpi2b implementation, last 0 should not be included
203
+ ].each do |test_case|
204
+ context "given #{test_case[:given]}" do
205
+ let(:data) { test_case[:given] }
206
+ it { should eql(test_case[:expect]) }
207
+ end
208
+ end
209
+ end
210
+
211
+ describe "#base64_mpi_to_a32" do
212
+ subject { harness.base64_mpi_to_a32(data) }
213
+ # expectation generation in Javascript:
214
+ # data = "CABTMUpwxe-OSvX_AhWxDzNu-fvisC9oRNxV97EjmBDLmLvyrEkdWRy4jAxQBOEFiqTe8bvH5EJ_HIxg_reA83kFB8UkHp359CPIceDwrTfS1pm3_onh7rWOdanzTXdixqiDRWIPo5dEfsIJixMIXtlBONla8TlTpc6sQ5NsysqMNYBaHD-5Npqj01s-pjkfSVwrtGSVU_b0JlT8acBemb8cukeXYSXaVf6ILgnBGkFYNyzSN5wmDDOU8tySoyKTtaPV9QDym0CrrxeNCYZPeawQQ4C85_dmJTJwSDyUu3ApocY2LPMYvRzo2CEP80eLuLHTSSp8O9_LMi7MrSJxCM9m"
215
+ # mpi2b(base64urldecode(data))
216
+ [
217
+ # { given: "CABTMUpwxe-OSvX_AhWxDzNu-fvisC9oRNxV97EjmBDLmLvyrEkdWRy4jAxQBOEFiqTe8bvH5EJ_HIxg_reA83kFB8UkHp359CPIceDwrTfS1pm3_onh7rWOdanzTXdixqiDRWIPo5dEfsIJixMIXtlBONla8TlTpc6sQ5NsysqMNYBaHD-5Npqj01s-pjkfSVwrtGSVU_b0JlT8acBemb8cukeXYSXaVf6ILgnBGkFYNyzSN5wmDDOU8tySoyKTtaPV9QDym0CrrxeNCYZPeawQQ4C85_dmJTJwSDyUu3ApocY2LPMYvRzo2CEP80eLuLHTSSp8O9_LMi7MrSJxCM9m",
218
+ # expect: [ 17354598,214618663,...,53561968,5] (length 74)
219
+ # }
220
+ { given: 'CABTMUpwxe-OSvX_AhWxDzNu-fvisC9oRNxV97EjmBDLmLvyrEkdWRy4jAxQBOEFiqTe8bvH5EJ_HIxg_reA83kFB8UkHp359CPIceDwrTfS1pm3_onh7rWOdanzTXdixqiDRWIPo5dEfsIJixMIXtlBONla8TlTpc6sQ5NsysqMNYBaHD-5Npqj01s-pjkfSVwrtGSVU_b0JlT8acBemb8cukeXYSXaVf6ILgnBGkFYNyzSN5wmDDOU8tySoyKTtaPV9QDym0CrrxeNCYZPeawQQ4C85_dmJTJwSDyUu3ApocY2LPMYvRzo2CEP80eLuLHTSSp8O9_LMi7MrSJxCM9m',
221
+ expect: { length: 74, first: 17354598, last: 5 } },
222
+ { given: 'BADOtDj2VVSPV2P3DpYOE-n-AkudPs-jvZg4_0T-uB-Vqr5M6PKmN5XrmPX-1JCzl2eeNHBT5vHRCMi0BfKQLplcxiMJAWWLDXDysbAxYRx7QpXlekjmpS3M7MmGdGAP4CK2P802oBGBayBvhVLh-2tjIO6oLyq_SOaOl2b72BT4Gw',
223
+ expect: { length: 37, first: 135591963, last: 52916 } },
224
+ { given: 'AAEB', expect: { length: 1, first: 1 } },
225
+ { given: "ABwP____", expect: { length: 2, first: 0x0FFFFFFF, last: 0 } }
226
+ ].each do |test_case|
227
+ context "given #{test_case[:given]}" do
228
+ let(:data) { test_case[:given] }
229
+ its(:length) { should eql(test_case[:expect][:length]) }
230
+ its(:first) { should eql(test_case[:expect][:first]) } if test_case[:expect][:first]
231
+ its(:last) { should eql(test_case[:expect][:last]) } if test_case[:expect][:last]
232
+ end
233
+ end
234
+ end
235
+
236
+ describe "#base64_mpi_to_bn" do
237
+ subject { harness.base64_mpi_to_bn(data) }
238
+ let(:data) { "CABTMUpwxe-OSvX_AhWxDzNu-fvisC9oRNxV97EjmBDLmLvyrEkdWRy4jAxQBOEFiqTe8bvH5EJ_HIxg_reA83kFB8UkHp359CPIceDwrTfS1pm3_onh7rWOdanzTXdixqiDRWIPo5dEfsIJixMIXtlBONla8TlTpc6sQ5NsysqMNYBaHD-5Npqj01s-pjkfSVwrtGSVU_b0JlT8acBemb8cukeXYSXaVf6ILgnBGkFYNyzSN5wmDDOU8tySoyKTtaPV9QDym0CrrxeNCYZPeawQQ4C85_dmJTJwSDyUu3ApocY2LPMYvRzo2CEP80eLuLHTSSp8O9_LMi7MrSJxCM9m" }
239
+ it { should eql(10502085503323500781668964017618508609411322157602252889058120375390234037936305190318468520089061587385422392468906441970388809208568726068567802356382375886770719186286579637487202524290928558697462287469498082331441069974866156504895644732315656764553928491941718091843907868083134643930511260213733744807826400737798825300540532269568079807932475504054890343173139777024447054332150293043219564634033889501152496876605674176404518453389630198251852934504321636321021090323912128727555904455606013476719568342420710593762431258548723102683181657773609963093684806746300973185952810254832251507735647127973472554854) }
240
+ end
241
+
242
+ describe "#decrypt_base64_to_a32" do
243
+ subject { harness.decrypt_base64_to_a32(data,key) }
244
+ # expectation generation in Javascript:
245
+ # data = "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0"
246
+ # key = [1049027610,743989201,1864038849,230624922]
247
+ # data_a32 = base64_to_a32(data)
248
+ # aes = new sjcl.cipher.aes(key)
249
+ # result_a32 = decrypt_key(aes,data_a32)
250
+ [
251
+ {
252
+ data: "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0",
253
+ key: [1049027610,743989201,1864038849,230624922],
254
+ expect: { length: 164, first_4: [67161780,955667796,-1890098185,244715027], last: 702938221 }
255
+ }
256
+ ].each do |test_case|
257
+ context "given #{test_case[:data]}" do
258
+ let(:data) { test_case[:data] }
259
+ let(:key) { test_case[:key] }
260
+ it "should match expectations" do
261
+ subject.length.should eql(test_case[:expect][:length])
262
+ subject[0,4].should eql(test_case[:expect][:first_4])
263
+ subject.last.should eql(test_case[:expect][:last])
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ describe "#decrypt_base64_to_str" do
270
+ subject { harness.decrypt_base64_to_str(data,key) }
271
+ # expectation generation in Javascript:
272
+ # data = "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0"
273
+ # key = [1049027610,743989201,1864038849,230624922]
274
+ # data_a32 = base64_to_a32(data)
275
+ # aes = new sjcl.cipher.aes(key)
276
+ # result_a32 = decrypt_key(aes,data_a32)
277
+ # result_str = a32_to_str(result_a32)
278
+ [
279
+ {
280
+ data: "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0",
281
+ key: [1049027610,743989201,1864038849,230624922],
282
+ expect: { count: 656, first_8_chars: [4,0,206,180,56,246,85,84] }
283
+ }
284
+ ].each do |test_case|
285
+ context "given #{test_case[:data]}" do
286
+ let(:data) { test_case[:data] }
287
+ let(:key) { test_case[:key] }
288
+ it "should match expectations" do
289
+ subject.size.should eql(test_case[:expect][:count])
290
+ subject[0,8].bytes.map{|c| c }.should eql(test_case[:expect][:first_8_chars])
291
+ end
292
+ end
293
+ end
294
+ end
295
+
296
+ describe "#decompose_rsa_private_key" do
297
+ let(:decompose_rsa_private_key) { harness.decompose_rsa_private_key(data) }
298
+ let(:master_key) { [1049027610,743989201,1864038849,230624922] }
299
+ let(:privk) { "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0" }
300
+ let(:data) { harness.decrypt_base64_to_str(privk, master_key) }
301
+ {
302
+ "p" => {
303
+ index: 0,
304
+ value: 145152480967442902710798365717824992407539346469007950427947366246418381110497813913858957184058405066632963688414200899762074556635208659933679812460151505046070928204691401275085086735464744077411206367875411771694473049724208018450885347494899144266437372521383994850220996849268745979417013187349849634843
305
+ },
306
+ "q" => {
307
+ index: 1,
308
+ value: 168954398786765849191397563548727872242680592890703379738251630959342408442654540439639636383830987645538349601800075764540793199627780254598326754715698681619610952393034627896645230178408131025377491566528255043082418659344085645573554592452837354761723985865145763294326144330279484715521705617485134759141
309
+ },
310
+ "d" => {
311
+ index: 2,
312
+ value: 12983373611079770211598532059277454574873142790933720755590694119490820724884320773416062882872824441407372114886530726474979688565347545388995368928786174580865050206056588702714047747117203947610116835797727211967502240199908615095083794940126702458539176340055473274185983336006544169353743065050853660077890672872837121852471639967733140753846470653509594838648433786451039871566965471109413962221372977032372323023277197880477708795783672178465649341215154594913545491105371339761229791332444362145738176132020340880433834911670388940905488618362858862479443215147800580019791462743546344709458220309095035153113
313
+ },
314
+ "u" => {
315
+ index: 3,
316
+ value: 142281187671710416869275196755125318539473540637032484403888464391289938418112669687981856743914459731863778035997945316748255907504882215849378865708087640335269803246793543314284773670481750851308061490898252628799994463123391676396043625029999665330718877331066039172562710647546041034169018526295459116796
317
+ }
318
+ }.each do |component,expectations|
319
+ describe component do
320
+ subject { decompose_rsa_private_key[expectations[:index]] }
321
+ it { should eql(expectations[:value]) }
322
+ end
323
+ end
324
+ end
325
+
326
+ describe "#decompose_rsa_private_key_a32" do
327
+ subject { harness.decompose_rsa_private_key_a32(data) }
328
+ # expectation generation in Javascript:
329
+ # password = '4leBd7TqgPwTZTByBbHfXo0E'
330
+ # aes = new sjcl.cipher.aes(prepare_key_pw(password))
331
+ # k = "zL-S9BspoEopTUm3z3O8CA"
332
+ # key = base64_to_a32(k)
333
+ # master_key = decrypt_key(aes,key) // [1049027610,743989201,1864038849,230624922]
334
+ #
335
+ # privk = "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0"
336
+ # key = base64_to_a32(privk)
337
+ # aes = new sjcl.cipher.aes(master_key)
338
+ # rsa_private_key = decrypt_key(aes,key)
339
+ #
340
+ # privk = a32_to_str(rsa_private_key) // length = 656
341
+ # rsa_privk = Array(4);
342
+ # // decompose private key
343
+ # //for (var i = 0; i < 4; i++)
344
+ # i = 0
345
+ # l = ((privk.charCodeAt(0)*256+privk.charCodeAt(1)+7)>>3)+2 // 130
346
+ # privk_part = privk.substr(0,l)
347
+ # data = base64urlencode(privk_part) // "BADOtDj2VVSPV2P3DpYOE-n-AkudPs-jvZg4_0T-uB-Vqr5M6PKmN5XrmPX-1JCzl2eeNHBT5vHRCMi0BfKQLplcxiMJAWWLDXDysbAxYRx7QpXlekjmpS3M7MmGdGAP4CK2P802oBGBayBvhVLh-2tjIO6oLyq_SOaOl2b72BT4Gw"
348
+ # rsa_privk[i] = mpi2b(privk_part) // array [ 135591963, ..., 52916 ] length = 37
349
+ # if (typeof rsa_privk[i] == 'number') break; // "object"
350
+ # privk = privk.substr(l)
351
+ # i = 1
352
+ # l = ((privk.charCodeAt(0)*256+privk.charCodeAt(1)+7)>>3)+2 // 130
353
+ # privk_part = privk.substr(0,l)
354
+ # rsa_privk[i] = mpi2b(privk_part) // array [ 203883749, ..., 61593 ] length = 37
355
+ # if (typeof rsa_privk[i] == 'number') break; // "object"
356
+ # privk = privk.substr(l)
357
+ # i = 2
358
+ # l = ((privk.charCodeAt(0)*256+privk.charCodeAt(1)+7)>>3)+2 // 258
359
+ # privk_part = privk.substr(0,l)
360
+ # rsa_privk[i] = mpi2b(privk_part) // array [ 140749529, ..., 6 ] length = 74
361
+ # if (typeof rsa_privk[i] == 'number') break; // "object"
362
+ # privk = privk.substr(l)
363
+ # i = 3
364
+ # l = ((privk.charCodeAt(0)*256+privk.charCodeAt(1)+7)>>3)+2 // 130
365
+ # privk_part = privk.substr(0,l)
366
+ # rsa_privk[i] = mpi2b(privk_part) // array [ 119289596, ..., 51869 ] length = 37
367
+ # if (typeof rsa_privk[i] == 'number') break; // "object"
368
+ # privk = privk.substr(l)
369
+ let(:master_key) { [1049027610,743989201,1864038849,230624922] }
370
+ let(:privk) { "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0" }
371
+ let(:data) {
372
+ harness.decrypt_base64_to_str(privk, master_key)
373
+ }
374
+ it "should be a valid RSA key deconstruct" do
375
+ subject.length.should eql(4)
376
+ part = subject[0]
377
+ part.length.should eql(37)
378
+ part.first.should eql(135591963)
379
+ part.last.should eql(52916)
380
+ part = subject[1]
381
+ part.length.should eql(37)
382
+ part.first.should eql(203883749)
383
+ part.last.should eql(61593)
384
+ part = subject[2]
385
+ part.length.should eql(74)
386
+ part.first.should eql(140749529)
387
+ part.last.should eql(6)
388
+ part = subject[3]
389
+ part.length.should eql(37)
390
+ part.first.should eql(119289596)
391
+ part.last.should eql(51869)
392
+ end
393
+ end
394
+
395
+ describe "#decrypt_session_id" do
396
+ subject { harness.decrypt_session_id(rsa_private_key,csid) }
397
+ let(:rsa_private_key) { 'somthing' }
398
+ let(:csid) { 'somthing' }
399
+
400
+ end
401
+
402
+ describe "#rsa_decrypt" do
403
+ # expectation generation in Javascript:
404
+ #
405
+ # master_key = [1049027610,743989201,1864038849,230624922]
406
+ # privk = "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0"
407
+ # key = base64_to_a32(privk)
408
+ # aes = new sjcl.cipher.aes(master_key)
409
+ # rsa_private_key = decrypt_key(aes,key)
410
+ # //::= decrypt_base64_to_str
411
+ #
412
+ # privk = a32_to_str(rsa_private_key)
413
+ # rsa_privk = Array(4)
414
+ # // decompose private key
415
+ # for (var i = 0; i < 4; i++) {
416
+ # l = ((privk.charCodeAt(0)*256+privk.charCodeAt(1)+7)>>3)+2 // 130
417
+ # privk_part = privk.substr(0,l)
418
+ # data = base64urlencode(privk_part)
419
+ # rsa_privk[i] = mpi2b(privk_part)
420
+ # if (typeof rsa_privk[i] == 'number') break;
421
+ # privk = privk.substr(l)
422
+ # }
423
+ # //::= decompose_rsa_private_key
424
+ #
425
+ # csid = "CABTMUpwxe-OSvX_AhWxDzNu-fvisC9oRNxV97EjmBDLmLvyrEkdWRy4jAxQBOEFiqTe8bvH5EJ_HIxg_reA83kFB8UkHp359CPIceDwrTfS1pm3_onh7rWOdanzTXdixqiDRWIPo5dEfsIJixMIXtlBONla8TlTpc6sQ5NsysqMNYBaHD-5Npqj01s-pjkfSVwrtGSVU_b0JlT8acBemb8cukeXYSXaVf6ILgnBGkFYNyzSN5wmDDOU8tySoyKTtaPV9QDym0CrrxeNCYZPeawQQ4C85_dmJTJwSDyUu3ApocY2LPMYvRzo2CEP80eLuLHTSSp8O9_LMi7MrSJxCM9m"
426
+ # t = mpi2b(base64urldecode(csid))
427
+ # // length: 74, first: 17354598, last: 5
428
+ # //::= base64_mpi_to_a32
429
+ #
430
+ # //if (i == 4 && privk.length < 16)
431
+ # // r = [k,base64urlencode(b2s(RSAdecrypt(t,rsa_privk[2],rsa_privk[0],rsa_privk[1],rsa_privk[3])).substr(0,43)),rsa_privk];
432
+ # csid_decrypt = RSAdecrypt(t,rsa_privk[2],rsa_privk[0],rsa_privk[1],rsa_privk[3]) // (m, d, p, q, u)
433
+ # // length: 73, first: 120147264, last: 14003132
434
+ # csid_decrypt_str = b2s(csid_decrypt)
435
+ # csid_decrypt_s43 = csid_decrypt_str.substr(0,43)
436
+ # csid_decrypt_b64 = base64urlencode(csid_decrypt_s43)
437
+ # // "1au8GQLcKSCkswqio-0PHmFNdXZjYU1vZFhjr1rVWm_USjjSvFhQZbVfDA"
438
+ #
439
+ # k = base64_to_a32("zL-S9BspoEopTUm3z3O8CA")
440
+ # r = [k,csid_decrypt_b64,rsa_privk]
441
+ #
442
+ let(:privk_encoded) { "3SKcQouWFdemgOQWwn_UUcCnHRNUZA0I5og99p_rYe6p2z16CY_qsbjOA5T59g3ClK6afQ9T0-lic79vtPsmRFWd7CY8EbXqZgX8gY8ZmiH0GZpCR57eOoIafpVzXU-OXrcGeJ_fOHQHDj7uEtF6lNaBdLPhdRkhYVno0DmdTcVfE939ESBmsBw_hCNUaicAmYSG1n_fdsiPs0UIOY0m2pjS1TZ-UfUZiLIxJnIujmteEKEWrOIMLnHXVR-V7S_2kZEzgOiRnrDvUIvcItP1xJ3dEvqIFPTTqVDHfEua4wnZPhPwiFg5awLudKigL2MS7kpmg9IuLTeCKytNpcOS9s24FCdIJCtsGqTXccY73Vj8rnRnjjd0iRV83XGpSPvwOa6-IPhhphGWgMNr4atlQzYHq4z3NBMMj9l8LnSOWO6KbUpTuqeQniO_YSQ_TbzjC-3cAEBjB7MpjSuBLexv3JygnpYiWmOJnUoojH3pezGNoNePtshsEllelcMa1_1c1cuJ2stH59HcTgB0u6-cpYqupeHqZB9Bn6U-eVjV1Ut-8LkzkTZjuGmt4YZZdJ-nZrkgsDkcpEFKfmupYDv8_9y69zOQaFXQ8v90KB2DawNWSEDRbAx-EFfu3rIsA3Hz2a9w-wVQQ8PD0C94kn57y4iyYbZDCu9Pal8V27J8eyUCqMh1kYlBkinuat3a2zKjT6bL6Tds6TkLUuzefO6DoPUQGTBZK8ZYi7QotUGI8xYzg6T26vDFQcgaQngSD0ZhZaKeQXdvRI_qjEZqg5Rt1VYZlJF42cinJD0N5blYewxy44Ps5oIAkR8iGjAgVL3KI_LsThWaCEKxTM0ScVGdYoh0vuSdIpAKVQVX_qfC8D0" }
443
+ let(:master_key) { [1049027610,743989201,1864038849,230624922] }
444
+ let(:privk) { harness.decrypt_base64_to_str(privk_encoded, master_key) }
445
+ let(:rsa_private_key) { harness.decompose_rsa_private_key(privk) }
446
+ let(:csid_encoded) { "CABTMUpwxe-OSvX_AhWxDzNu-fvisC9oRNxV97EjmBDLmLvyrEkdWRy4jAxQBOEFiqTe8bvH5EJ_HIxg_reA83kFB8UkHp359CPIceDwrTfS1pm3_onh7rWOdanzTXdixqiDRWIPo5dEfsIJixMIXtlBONla8TlTpc6sQ5NsysqMNYBaHD-5Npqj01s-pjkfSVwrtGSVU_b0JlT8acBemb8cukeXYSXaVf6ILgnBGkFYNyzSN5wmDDOU8tySoyKTtaPV9QDym0CrrxeNCYZPeawQQ4C85_dmJTJwSDyUu3ApocY2LPMYvRzo2CEP80eLuLHTSSp8O9_LMi7MrSJxCM9m" }
447
+
448
+ subject { harness.decrypt_session_id(csid_encoded,rsa_private_key) }
449
+ let(:expected_sid) { "1au8GQLcKSCkswqio-0PHmFNdXZjYU1vZFhjr1rVWm_USjjSvFhQZbVfDA" }
450
+ it { should eql(expected_sid) }
451
+
452
+ end
453
+
454
+ describe "#decrypt_file_attributes" do
455
+ subject { harness.decrypt_file_attributes(f,key) }
456
+ {
457
+ 'simple_folder' => {
458
+ f: { 't' => 1, 'a' => "US0wKXcni_p8dnqRvhR_Otafji3ioNJ5IsgSHB5zhOw" },
459
+ key: [1479379715, 408676944, 1375748016, 1932394997],
460
+ expected_attributes: {"n"=>"Research"}
461
+ },
462
+ 'simple_file' => {
463
+ f: { 't' => 0, 'a' => "n4CazRegf4aLA4BNrdoEsqRLGLQ244NjJUJi53Zz-J4" },
464
+ key: [1281139164, 1127317712, 263279788, 1988157168, 402822759, 1958040625, 716219392, 465402751],
465
+ expected_attributes: {"n"=>"mega.png"}
466
+ }
467
+ }.each do |test_name,expectations|
468
+ context test_name do
469
+ let(:f) { expectations[:f] }
470
+ let(:key) { expectations[:key] }
471
+ it { should eql(expectations[:expected_attributes])}
472
+ end
473
+ end
474
+ end
475
+
476
+ end