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 +2 -0
- data/Manifest +4 -0
- data/Rakefile +6 -0
- data/imobile.gemspec +5 -5
- data/lib/imobile.rb +1 -0
- data/lib/imobile/crypto_app_fprint.rb +120 -0
- data/test/crypto_app_fprint_test.rb +39 -0
- data/test/validate_receipt_test.rb +4 -2
- data/testdata/device_attributes.yml +8 -0
- metadata +8 -3
data/CHANGELOG
CHANGED
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
data/imobile.gemspec
CHANGED
@@ -2,22 +2,22 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{imobile}
|
5
|
-
s.version = "0.0.
|
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-
|
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/
|
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
|
data/lib/imobile.rb
CHANGED
@@ -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
|
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.
|
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-
|
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
|