imobile 0.0.1 → 0.0.2

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