costan-imobile 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|