imobile 0.0.1 → 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 CHANGED
@@ -1 +1,3 @@
1
+ v0.0.2. CryptoSupport fingerprints computation.
2
+
1
3
  v0.0.1. Initial release. In-App Purchase receipt validation.
data/Manifest CHANGED
@@ -1,10 +1,14 @@
1
1
  CHANGELOG
2
+ imobile.gemspec
3
+ lib/imobile/crypto_app_fprint.rb
2
4
  lib/imobile/validate_receipt.rb
3
5
  lib/imobile.rb
4
6
  LICENSE
5
7
  Manifest
6
8
  Rakefile
7
9
  README
10
+ test/crypto_app_fprint_test.rb
8
11
  test/validate_receipt_test.rb
12
+ testdata/device_attributes.yml
9
13
  testdata/forged_sandbox_receipt
10
14
  testdata/valid_sandbox_receipt
data/Rakefile CHANGED
@@ -1,3 +1,9 @@
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
+
1
7
  require 'rubygems'
2
8
  require 'echoe'
3
9
 
@@ -2,22 +2,22 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{imobile}
5
- s.version = "0.0.1"
5
+ s.version = "0.0.2"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Victor Costan"]
9
- s.date = %q{2009-07-23}
9
+ s.date = %q{2009-07-24}
10
10
  s.description = %q{Library for servers backing iPhone applications.}
11
11
  s.email = %q{victor@zergling.net}
12
- s.extra_rdoc_files = ["CHANGELOG", "lib/imobile/validate_receipt.rb", "lib/imobile.rb", "LICENSE", "README"]
13
- s.files = ["CHANGELOG", "lib/imobile/validate_receipt.rb", "lib/imobile.rb", "LICENSE", "Manifest", "Rakefile", "README", "test/validate_receipt_test.rb", "testdata/forged_sandbox_receipt", "testdata/valid_sandbox_receipt", "imobile.gemspec"]
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
14
  s.homepage = %q{http://github.com/costan/imobile}
15
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Imobile", "--main", "README"]
16
16
  s.require_paths = ["lib"]
17
17
  s.rubyforge_project = %q{zerglings}
18
18
  s.rubygems_version = %q{1.3.5}
19
19
  s.summary = %q{Library for servers backing iPhone applications.}
20
- s.test_files = ["test/validate_receipt_test.rb"]
20
+ s.test_files = ["test/crypto_app_fprint_test.rb", "test/validate_receipt_test.rb"]
21
21
 
22
22
  if s.respond_to? :specification_version then
23
23
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -4,4 +4,5 @@
4
4
  # Copyright:: Copyright (C) 2009 Zergling.Net
5
5
  # License:: MIT
6
6
 
7
+ require 'imobile/crypto_app_fprint.rb'
7
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,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
@@ -10,8 +10,7 @@ require 'test/unit'
10
10
 
11
11
  class ValidateReceiptTest < Test::Unit::TestCase
12
12
  def setup
13
- testdata_path = File.join(File.dirname(__FILE__), '..', 'testdata')
14
-
13
+ testdata_path = File.join(File.dirname(__FILE__), '..', 'testdata')
15
14
  @forged_sandbox_blob = File.read File.join(testdata_path,
16
15
  'forged_sandbox_receipt')
17
16
  @valid_sandbox_blob = File.read File.join(testdata_path,
@@ -47,4 +46,7 @@ class ValidateReceiptTest < Test::Unit::TestCase
47
46
  Imobile.validate_receipt(@forged_sandbox_blob, :sandbox),
48
47
  "Forged receipt passed validation"
49
48
  end
49
+
50
+ # TODO(costan): add tests against the real servers, as soon as someone donates
51
+ # a receipt
50
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imobile
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Costan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-23 00:00:00 -04:00
12
+ date: 2009-07-24 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -50,22 +50,26 @@ extensions: []
50
50
 
51
51
  extra_rdoc_files:
52
52
  - CHANGELOG
53
+ - lib/imobile/crypto_app_fprint.rb
53
54
  - lib/imobile/validate_receipt.rb
54
55
  - lib/imobile.rb
55
56
  - LICENSE
56
57
  - README
57
58
  files:
58
59
  - CHANGELOG
60
+ - imobile.gemspec
61
+ - lib/imobile/crypto_app_fprint.rb
59
62
  - lib/imobile/validate_receipt.rb
60
63
  - lib/imobile.rb
61
64
  - LICENSE
62
65
  - Manifest
63
66
  - Rakefile
64
67
  - README
68
+ - test/crypto_app_fprint_test.rb
65
69
  - test/validate_receipt_test.rb
70
+ - testdata/device_attributes.yml
66
71
  - testdata/forged_sandbox_receipt
67
72
  - testdata/valid_sandbox_receipt
68
- - imobile.gemspec
69
73
  has_rdoc: true
70
74
  homepage: http://github.com/costan/imobile
71
75
  licenses: []
@@ -100,4 +104,5 @@ signing_key:
100
104
  specification_version: 3
101
105
  summary: Library for servers backing iPhone applications.
102
106
  test_files:
107
+ - test/crypto_app_fprint_test.rb
103
108
  - test/validate_receipt_test.rb