costan-imobile 0.0.2

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 ADDED
@@ -0,0 +1,3 @@
1
+ v0.0.2. CryptoSupport fingerprints computation.
2
+
3
+ v0.0.1. Initial release. In-App Purchase receipt validation.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Zergling.Net
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,14 @@
1
+ CHANGELOG
2
+ imobile.gemspec
3
+ lib/imobile/crypto_app_fprint.rb
4
+ lib/imobile/validate_receipt.rb
5
+ lib/imobile.rb
6
+ LICENSE
7
+ Manifest
8
+ Rakefile
9
+ README
10
+ test/crypto_app_fprint_test.rb
11
+ test/validate_receipt_test.rb
12
+ testdata/device_attributes.yml
13
+ testdata/forged_sandbox_receipt
14
+ testdata/valid_sandbox_receipt
data/README ADDED
@@ -0,0 +1,6 @@
1
+ This gem/Rails plugin contains common code for powering iPhone applications. It
2
+ is particularly suitable as a server-side partner to the ZergSupport toolkit,
3
+ also available on Github under the MIT license at
4
+ http://github.com/costan/zergsupport
5
+
6
+
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ # Rakefile that uses echoe to manage imobile's gemspec.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'rubygems'
8
+ require 'echoe'
9
+
10
+ Echoe.new('imobile') do |p|
11
+ p.project = 'zerglings' # rubyforge project
12
+
13
+ p.author = 'Victor Costan'
14
+ p.email = 'victor@zergling.net'
15
+ p.summary = 'Library for servers backing iPhone applications.'
16
+ p.url = 'http://github.com/costan/imobile'
17
+ p.dependencies = ['json >=1.1.7']
18
+ p.development_dependencies = ["echoe >=3.1.1", "flexmock >=0.8.6"]
19
+
20
+ p.need_tar_gz = true
21
+ p.need_zip = true
22
+ p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
23
+ end
24
+
25
+ if $0 == __FILE__
26
+ Rake.application = Rake::Application.new
27
+ Rake.application.run
28
+ end
data/imobile.gemspec ADDED
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{imobile}
5
+ s.version = "0.0.2"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Victor Costan"]
9
+ s.date = %q{2009-07-24}
10
+ s.description = %q{Library for servers backing iPhone applications.}
11
+ s.email = %q{victor@zergling.net}
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/imobile/crypto_app_fprint.rb", "lib/imobile/validate_receipt.rb", "lib/imobile.rb", "LICENSE", "README"]
13
+ s.files = ["CHANGELOG", "imobile.gemspec", "lib/imobile/crypto_app_fprint.rb", "lib/imobile/validate_receipt.rb", "lib/imobile.rb", "LICENSE", "Manifest", "Rakefile", "README", "test/crypto_app_fprint_test.rb", "test/validate_receipt_test.rb", "testdata/device_attributes.yml", "testdata/forged_sandbox_receipt", "testdata/valid_sandbox_receipt"]
14
+ s.homepage = %q{http://github.com/costan/imobile}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Imobile", "--main", "README"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{zerglings}
18
+ s.rubygems_version = %q{1.3.5}
19
+ s.summary = %q{Library for servers backing iPhone applications.}
20
+ s.test_files = ["test/crypto_app_fprint_test.rb", "test/validate_receipt_test.rb"]
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 3
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<json>, [">= 1.1.7"])
28
+ s.add_development_dependency(%q<echoe>, [">= 3.1.1"])
29
+ s.add_development_dependency(%q<flexmock>, [">= 0.8.6"])
30
+ else
31
+ s.add_dependency(%q<json>, [">= 1.1.7"])
32
+ s.add_dependency(%q<echoe>, [">= 3.1.1"])
33
+ s.add_dependency(%q<flexmock>, [">= 0.8.6"])
34
+ end
35
+ else
36
+ s.add_dependency(%q<json>, [">= 1.1.7"])
37
+ s.add_dependency(%q<echoe>, [">= 3.1.1"])
38
+ s.add_dependency(%q<flexmock>, [">= 0.8.6"])
39
+ end
40
+ end
data/lib/imobile.rb ADDED
@@ -0,0 +1,8 @@
1
+ # Master include file for the Imobile gem / plug-in.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'imobile/crypto_app_fprint.rb'
8
+ require 'imobile/validate_receipt.rb'
@@ -0,0 +1,120 @@
1
+ # Application integrity finger-printing used by ZergSupport's CryptoSupport.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'digest/md5'
8
+ require 'digest/sha2'
9
+ require 'set'
10
+ require 'openssl'
11
+
12
+
13
+ # :nodoc: namespace
14
+ module Imobile
15
+
16
+ # An iPhone application's finger-print, as implemented in CryptoSupport.
17
+ #
18
+ # Args:
19
+ # device_or_hash:: a Hash or ActiveRecord model representing the result of
20
+ # calling [ZNDeviceFprint deviceAttributes] on the iMobile
21
+ # device
22
+ # binary_path:: path to the application's binary (executable file)
23
+ # corresponding to the application version on the device
24
+ # (indicated by :app_version in the device attributes)
25
+ #
26
+ # Returns a finger-print that should prove the application's integrity. The
27
+ # finger-print is a string consisting of printable characters.
28
+ def self.crypto_app_fprint(device_or_hash, binary_path)
29
+ CryptoSupportAppFprint.app_fprint device_or_hash, binary_path
30
+ end
31
+
32
+
33
+ # Implementation details for crypto_app_fprint.
34
+ module CryptoSupportAppFprint
35
+ # The finger-print for a device's attributes, as implemented in CryptoSupport.
36
+ #
37
+ # The device attributes should be passed in a Hash that represents the result
38
+ # of calling [ZNDeviceFprint deviceAttributes] on the iMobile device.
39
+ #
40
+ # The finger-print is returned as a raw string (no hex-formatting).
41
+ # In particular, the returned finger-print is suitable to be used as a key or
42
+ # IV for AES-128.
43
+ #
44
+ # The code mirrors the reference code in ZergSupport's test suite.
45
+ def self.device_fprint(device_attributes)
46
+ Digest::MD5.digest device_fprint_data(device_attributes)
47
+ end
48
+
49
+ # The finger-print for a device's attributes, as implemented in CryptoSupport.
50
+ #
51
+ # This method resembles device_fprint, but returns the finger-print in a
52
+ # hex-formatted string, so that it can be used by Web services.
53
+ def self.hex_device_fprint(device_attributes)
54
+ device_fprint(device_attributes).unpack('C*').map {|c| '%02x' % c}.join('')
55
+ end
56
+
57
+ # The device data used for device finger-printing in CryptoSupport.
58
+ #
59
+ # The device attributes should be passed in a Hash that represents the result
60
+ # of calling [ZNDeviceFprint deviceAttributes] on the iMobile device.
61
+ def self.device_fprint_data(device_attributes)
62
+ attrs = device_fprint_attributes
63
+ keys = device_attributes.keys.select { |k| attrs.include? k.to_s }
64
+ 'D|' + keys.sort.map { |k| device_attributes[k] }.join('|')
65
+ end
66
+
67
+ # The device attributes included in the finger-printing operation.
68
+ def self.device_fprint_attributes
69
+ Set.new(['app_id', 'app_version', 'app_provisioning', 'hardware_model',
70
+ 'os_name', 'os_version', 'unique_id'])
71
+ end
72
+
73
+ # The finger-print for a data blob, as implemented in CryptoSupport.
74
+ #
75
+ # Args:
76
+ # data_blob:: a raw string, usually the result of reading a file
77
+ # key:: 16-byte string, to be used as an AES key
78
+ # iv:: 16-byte string, to be used as an AES key
79
+ #
80
+ # The returned finger-print is a hex-formatted string.
81
+ def self.data_fprint(data_blob, key, iv = "\0" * 16)
82
+ cipher = OpenSSL::Cipher::Cipher.new 'aes-128-cbc'
83
+ cipher.encrypt
84
+ cipher.key, cipher.iv = key, iv
85
+
86
+ plain = data_blob + "\0" * ((16 - (data_blob.length & 0x0f)) & 0x0f)
87
+ crypted = cipher.update plain
88
+ Digest::SHA2.hexdigest crypted
89
+ end
90
+
91
+ # An iPhone application's finger-print, as implemented in CryptoSupport.
92
+ #
93
+ # The device attributes should be passed in a Hash that represents the result
94
+ # of calling [ZNDeviceFprint deviceAttributes] on the iMobile device. The
95
+ # manifest data should be the result of reading the application's manifest
96
+ # file (currently its executable file).
97
+ #
98
+ # The returned finger-print is a hex-formatted string.
99
+ def self.app_fprint_from_raw_data(device_attributes, manifest_data)
100
+ key = device_fprint device_attributes
101
+ iv = "\0" * 16
102
+ data_fprint manifest_data, key, iv
103
+ end
104
+
105
+ # An iPhone application's finger-print, as implemented in CryptoSupport.
106
+ def self.app_fprint(device_or_hash, binary_path)
107
+ if device_or_hash.respond_to?(:[]) and device_or_hash.respond_to?(:keys)
108
+ # Hash-like object.
109
+ device_attributes = device_or_hash
110
+ else
111
+ # ActiveRecord model.
112
+ device_attributes = device_or_hash.attributes
113
+ end
114
+
115
+ manifest_data = File.read binary_path
116
+ app_fprint_from_raw_data device_attributes, manifest_data
117
+ end
118
+ end
119
+
120
+ end # namespace Imobile
@@ -0,0 +1,104 @@
1
+ # Online validation for receipt blobs generated by the In-App Purchase process.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'base64'
8
+ require 'date'
9
+ require 'net/http'
10
+ require 'net/https'
11
+ require 'openssl'
12
+ require 'time'
13
+
14
+ require 'rubygems'
15
+ require 'json'
16
+
17
+
18
+ # :nodoc: namespace
19
+ module Imobile
20
+
21
+ # Decodes and validates an In-App Purchase receipt from the App Store.
22
+ #
23
+ # Args:
24
+ # receipt_blob:: raw receipt in SKPaymentTransaction.transactionReceipt
25
+ # server_type:: production or sandbox (the API accepts symbols and strings)
26
+ #
27
+ # The decoded receipt is returned as a Ruby-friendly hash. Keys are converted to
28
+ # snake_case symbols (e.g. 'purchase-date' becomes :purchase_date). Dates and
29
+ # relevant numbers are parsed out of the JSON strings.
30
+ #
31
+ # Returns +false+ if validation fails (the receipt was tampered with). Raises a
32
+ # RuntimeException if Apple's Web service returns a HTTP error code.
33
+ def self.validate_receipt(receipt_blob, server_type = :sandbox)
34
+ AppStoreReceiptValidation.validate_receipt receipt_blob
35
+ end
36
+
37
+
38
+ # Implementation details for validate_receipt.
39
+ module AppStoreReceiptValidation
40
+ # An URI object pointing to the App Store receipt validating server.
41
+ #
42
+ # The server type is production or sandbox (strings and symbols work).
43
+ def self.store_uri(server_type)
44
+ uri_string = {
45
+ :production => "https://buy.itunes.apple.com/verifyReceipt",
46
+ :sandbox => "https://sandbox.itunes.apple.com/verifyReceipt"
47
+ }[server_type.to_sym]
48
+
49
+ uri_string and URI.parse(uri_string)
50
+ end
51
+
52
+ # A Net:HTTP request for validating a receipt.
53
+ def self.request(receipt_blob, store_uri)
54
+ request = Net::HTTP::Post.new store_uri.path
55
+ request.set_content_type 'application/json'
56
+ request.body = {'receipt-data' => Base64.encode64(receipt_blob) }.to_json
57
+ request
58
+ end
59
+
60
+ # Issues a HTTP request to Apple's receipt validating server.
61
+ def self.issue_request(http_request, store_uri)
62
+ http = Net::HTTP.new store_uri.host, store_uri.port
63
+ http.use_ssl = (store_uri.scheme == 'https')
64
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
65
+ http.request http_request
66
+ end
67
+
68
+ # Turns JSON receipt information into a nice Ruby-like receipt.
69
+ #
70
+ # String keys are mapped to Ruby symbols (e.g. 'purchase-date' becomes
71
+ # :purchase_date) and date strings are parsed to Ruby dates.
72
+ def self.nice_receipt(json_receipt)
73
+ nice = Hash[*json_receipt.map { |key, value|
74
+ [key.gsub('-', '_').to_sym, value]
75
+ }.flatten]
76
+
77
+ [:purchase_date, :original_purchase_date].each do |key|
78
+ nice[key] = DateTime.parse(nice[key]) if nice[key]
79
+ end
80
+ nice[:quantity] = nice[:quantity].to_i if nice[:quantity]
81
+
82
+ nice
83
+ end
84
+
85
+ # Processes a HTTP response into a receipt.
86
+ def self.process_response(http_response)
87
+ unless http_response.kind_of? Net::HTTPSuccess
88
+ raise "Internal error in Apple's Web service -- #{http_response.inspect}"
89
+ end
90
+
91
+ response = JSON.parse http_response.body
92
+ return false unless response['status'] == 0
93
+ nice_receipt response['receipt']
94
+ end
95
+
96
+ # Real implementation of Imobile.validate_receipt
97
+ def self.validate_receipt(receipt_blob, server_type = :production)
98
+ uri = store_uri server_type
99
+
100
+ process_response issue_request(request(receipt_blob, uri), uri)
101
+ end
102
+ end
103
+
104
+ end # namespace Imobile
@@ -0,0 +1,39 @@
1
+ # Author:: Victor Costan
2
+ # Copyright:: Copyright (C) 2009 Zergling.Net
3
+ # License:: MIT
4
+
5
+ require 'imobile'
6
+
7
+ require 'time'
8
+ require 'test/unit'
9
+
10
+ require 'rubygems'
11
+ require 'flexmock/test_unit'
12
+
13
+
14
+ class CryptoAppFprintTest < Test::Unit::TestCase
15
+ def setup
16
+ testdata_path = File.join(File.dirname(__FILE__), '..', 'testdata')
17
+ @device_attrs = File.open(File.join(testdata_path,
18
+ 'device_attributes.yml')) do |f|
19
+ YAML.load f
20
+ end
21
+
22
+ @mock_binary = '1a2b3c4d5e6f7g8h' * 16384
23
+ end
24
+
25
+ def test_device_fprint
26
+ fprint = Imobile::CryptoSupportAppFprint.hex_device_fprint @device_attrs
27
+ assert_equal '9cef1c830742fa83ad213281c1ce47b5', fprint
28
+ end
29
+
30
+ def test_crypto_app_fprint
31
+ mock_binary_path = '/binary/path'
32
+ flexmock(File).should_receive(:read).with(mock_binary_path).
33
+ and_return(@mock_binary)
34
+ fprint = Imobile.crypto_app_fprint @device_attrs, mock_binary_path
35
+ gold_fprint =
36
+ 'b5b45dec2177d095bff66dd895c624b1bc264e48575f2b39ed5456c3821c338f'
37
+ assert_equal gold_fprint, fprint
38
+ end
39
+ end
@@ -0,0 +1,52 @@
1
+ # Author:: Victor Costan
2
+ # Copyright:: Copyright (C) 2009 Zergling.Net
3
+ # License:: MIT
4
+
5
+ require 'imobile'
6
+
7
+ require 'time'
8
+ require 'test/unit'
9
+
10
+
11
+ class ValidateReceiptTest < Test::Unit::TestCase
12
+ def setup
13
+ testdata_path = File.join(File.dirname(__FILE__), '..', 'testdata')
14
+ @forged_sandbox_blob = File.read File.join(testdata_path,
15
+ 'forged_sandbox_receipt')
16
+ @valid_sandbox_blob = File.read File.join(testdata_path,
17
+ 'valid_sandbox_receipt')
18
+ end
19
+
20
+ def test_valid_sandbox_receipt
21
+ golden_date = Time.parse('2009-07-22 18:52:46 EEST')
22
+
23
+ receipt = Imobile.validate_receipt @valid_sandbox_blob, :sandbox
24
+ assert receipt, "Genuine receipt failed validation"
25
+
26
+ assert_equal 1, receipt[:quantity], 'Wrong quantity'
27
+ assert_equal 'us.costan.ZergSupportTests', receipt[:bid], 'Wrong bundle ID'
28
+ assert_equal '1.9.8.3', receipt[:bvrs], 'Wrong bundle version'
29
+ assert_equal 'net.zergling.ZergSupport.sub_cheap', receipt[:product_id],
30
+ 'Wrong product ID'
31
+ assert_equal '324740515', receipt[:item_id], 'Wrong item (iTunes app) ID'
32
+ assert_equal '1000000000016600', receipt[:transaction_id],
33
+ 'Wrong transaction ID'
34
+ assert_equal '1000000000016600', receipt[:original_transaction_id],
35
+ 'Wrong original transaction ID'
36
+ assert_equal golden_date.to_f,
37
+ Time.parse(receipt[:purchase_date].to_s).to_f,
38
+ 'Wrong purchase date'
39
+ assert_equal golden_date.to_f,
40
+ Time.parse(receipt[:original_purchase_date].to_s).to_f,
41
+ 'Wrong original purchase date'
42
+ end
43
+
44
+ def test_forged_sandbox_receipt
45
+ assert_equal false,
46
+ Imobile.validate_receipt(@forged_sandbox_blob, :sandbox),
47
+ "Forged receipt passed validation"
48
+ end
49
+
50
+ # TODO(costan): add tests against the real servers, as soon as someone donates
51
+ # a receipt
52
+ end
@@ -0,0 +1,8 @@
1
+ ---
2
+ app_id: us.costan.ZergSupportTests
3
+ app_provisioning: s
4
+ app_version: "1.9.8.3"
5
+ hardware_model: i386
6
+ os_name: iPhone OS
7
+ os_version: "3.0"
8
+ unique_id: sim:EEAE137F-205A-587E-8F62-C6855680879E
@@ -0,0 +1,6 @@
1
+ {
2
+ "signature" = "AUXu7Ofn0I4Zl7kghQOLSDB17PzQT0lscGJfFiFsJFT0q9BFGG0ynHJAOARZMXdUFnb87YxrhqD1pm7KPxqgs18mndMRkw/fcRDgMIRmp4nsQlISF8mRqrkYqehHPTXwd75YxpBRkKil8qYciQRMuQEAhL8C5M+ycXzIpTgF2l1pMIIDUzCCAjugAwIBAgIIZRSRTdlYBLUwDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYDVQQDDCpBcHBsZSBpVHVuZXMgU3RvcmUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDkwNjE1MjIwNTU2WhcNMTQwNjE0MjIwNTU2WjBkMSMwIQYDVQQDDBpQdXJjaGFzZVJlY2VpcHRDZXJ0aWZpY2F0ZTEbMBkGA1UECwwSQXBwbGUgaVR1bmVzIFN0b3JlMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAytGMXZy3gitJ2JMKFojSDynC/9yYezyn9HBX+u3/3VcpWE2XhcgGKYqNBA1+AewOzrKO774OsokTu4qymEx10ph8UTmsZewB0ESMHBEjF7FN6/HccsQUYC3WagrHnT12HG2Ih0OAm/ZhpWzj0HS4m813LpIyo00sewMvMNL2hkcCAwEAAaNyMHAwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQ2HejinYLSARi1MmsO10MLkVhDOjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFKmDg/IZSMU+ElcIFMzNo36ZXyT1MBAGCiqGSIb3Y2QGBQEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQARpJs+O2Y3gL8gHdASkrfZHFpwINd1VcB5VF5LkVpnFz63zylA/3cGIDG91b/d5NIwZjkVt4Bgvd62o/mCbzCsWiNfSKTJVFK1D78BDQoSO2oHTuQuz1BR7xzNHxQZ90zUS6ZX9SC8N3g3A1jEtAyDhZNB+CRBBXLwZdnBUeBsT9QLpjvTnekZcGTnU08zfCjGF3eBJEu9eP6WgexK1xMSp72kEOmYbn6yTi3D4YrcYx4Q3n/57VBP2en8qXWeP5oHDsLTGzLRsWdoB3VxJLrF2ivL8JS8zqC0qyac452pN6xunRuzyyfpaqzQL12BzFEe44xna2byektSbtquA5LNAAAAAA==";
3
+ "purchase-info" = "ewoJIml0ZW0taWQiID0gIjMyNDc0MDUxNSI7Cgkib3JpZ2luYWwtdHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwMTY2MDAiOwoJInB1cmNoYXNlLWRhdGUiID0gIjIwMDktMDctMjIgMjI6NTI6NDYgRXRjL0dNVCI7CgkicHJvZHVjdC1pZCIgPSAibmV0LnplcmdsaW5nLlplcmdTdXBwb3J0LnN1Yl9jaGVhcCI7CgkidHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwMTY2MDAiOwoJInF1YW50aXR5IiA9ICIxIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDA5LTA3LTIyIDIyOjUyOjQ2IEV0Yy9HTVQiOwoJImJpZCIgPSAidXMuY29zdGFuLlplcmdTdXBwb3J0VGVzdHMiOwoJImJ2cnMiID0gIjEuOS44LjMiOwpA";
4
+ "pod" = "100";
5
+ "signing-status" = "0";
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "signature" = "AUXu7Ofn0I4Zl7kghQOLSDB17PzQT0lscGJfFiFsJFT0q9BFGG0ynHJAOARZMXdUFnb87YxrhqD1pm7KPxqgs18mndMRkw/fcRDgMIRmp4nsQlISF8mRqrkYqehHPTXwd75YxpBRkKil8qYciQRMuQEAhL8C5M+ycXzIpTgF2l1pMIIDUzCCAjugAwIBAgIIZRSRTdlYBLUwDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYDVQQDDCpBcHBsZSBpVHVuZXMgU3RvcmUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDkwNjE1MjIwNTU2WhcNMTQwNjE0MjIwNTU2WjBkMSMwIQYDVQQDDBpQdXJjaGFzZVJlY2VpcHRDZXJ0aWZpY2F0ZTEbMBkGA1UECwwSQXBwbGUgaVR1bmVzIFN0b3JlMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAytGMXZy3gitJ2JMKFojSDynC/9yYezyn9HBX+u3/3VcpWE2XhcgGKYqNBA1+AewOzrKO774OsokTu4qymEx10ph8UTmsZewB0ESMHBEjF7FN6/HccsQUYC3WagrHnT12HG2Ih0OAm/ZhpWzj0HS4m813LpIyo00sewMvMNL2hkcCAwEAAaNyMHAwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQ2HejinYLSARi1MmsO10MLkVhDOjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFKmDg/IZSMU+ElcIFMzNo36ZXyT1MBAGCiqGSIb3Y2QGBQEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQARpJs+O2Y3gL8gHdASkrfZHFpwINd1VcB5VF5LkVpnFz63zylA/3cGIDG91b/d5NIwZjkVt4Bgvd62o/mCbzCsWiNfSKTJVFK1D78BDQoSO2oHTuQuz1BR7xzNHxQZ90zUS6ZX9SC8N3g3A1jEtAyDhZNB+CRBBXLwZdnBUeBsT9QLpjvTnekZcGTnU08zfCjGF3eBJEu9eP6WgexK1xMSp72kEOmYbn6yTi3D4YrcYx4Q3n/57VBP2en8qXWeP5oHDsLTGzLRsWdoB3VxJLrF2ivL8JS8zqC0qyac452pN6xunRuzyyfpaqzQL12BzFEe44xna2byektSbtquA5LNAAAAAA==";
3
+ "purchase-info" = "ewoJIml0ZW0taWQiID0gIjMyNDc0MDUxNSI7Cgkib3JpZ2luYWwtdHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwMTY2MDAiOwoJInB1cmNoYXNlLWRhdGUiID0gIjIwMDktMDctMjIgMjI6NTI6NDYgRXRjL0dNVCI7CgkicHJvZHVjdC1pZCIgPSAibmV0LnplcmdsaW5nLlplcmdTdXBwb3J0LnN1Yl9jaGVhcCI7CgkidHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwMTY2MDAiOwoJInF1YW50aXR5IiA9ICIxIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDA5LTA3LTIyIDIyOjUyOjQ2IEV0Yy9HTVQiOwoJImJpZCIgPSAidXMuY29zdGFuLlplcmdTdXBwb3J0VGVzdHMiOwoJImJ2cnMiID0gIjEuOS44LjMiOwp9";
4
+ "pod" = "100";
5
+ "signing-status" = "0";
6
+ }
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: costan-imobile
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Victor Costan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-24 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.7
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: echoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: flexmock
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.8.6
44
+ version:
45
+ description: Library for servers backing iPhone applications.
46
+ email: victor@zergling.net
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - CHANGELOG
53
+ - lib/imobile/crypto_app_fprint.rb
54
+ - lib/imobile/validate_receipt.rb
55
+ - lib/imobile.rb
56
+ - LICENSE
57
+ - README
58
+ files:
59
+ - CHANGELOG
60
+ - imobile.gemspec
61
+ - lib/imobile/crypto_app_fprint.rb
62
+ - lib/imobile/validate_receipt.rb
63
+ - lib/imobile.rb
64
+ - LICENSE
65
+ - Manifest
66
+ - Rakefile
67
+ - README
68
+ - test/crypto_app_fprint_test.rb
69
+ - test/validate_receipt_test.rb
70
+ - testdata/device_attributes.yml
71
+ - testdata/forged_sandbox_receipt
72
+ - testdata/valid_sandbox_receipt
73
+ has_rdoc: false
74
+ homepage: http://github.com/costan/imobile
75
+ post_install_message:
76
+ rdoc_options:
77
+ - --line-numbers
78
+ - --inline-source
79
+ - --title
80
+ - Imobile
81
+ - --main
82
+ - README
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ version:
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: "1.2"
96
+ version:
97
+ requirements: []
98
+
99
+ rubyforge_project: zerglings
100
+ rubygems_version: 1.2.0
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Library for servers backing iPhone applications.
104
+ test_files:
105
+ - test/crypto_app_fprint_test.rb
106
+ - test/validate_receipt_test.rb