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 +3 -0
- data/LICENSE +21 -0
- data/Manifest +14 -0
- data/README +6 -0
- data/Rakefile +28 -0
- data/imobile.gemspec +40 -0
- data/lib/imobile.rb +8 -0
- data/lib/imobile/crypto_app_fprint.rb +120 -0
- data/lib/imobile/validate_receipt.rb +104 -0
- data/test/crypto_app_fprint_test.rb +39 -0
- data/test/validate_receipt_test.rb +52 -0
- data/testdata/device_attributes.yml +8 -0
- data/testdata/forged_sandbox_receipt +6 -0
- data/testdata/valid_sandbox_receipt +6 -0
- metadata +106 -0
data/CHANGELOG
ADDED
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
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,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,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
|