itunes_receipt_decoder 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be8ac7da532b84da681984f7e5e51e8677133d77
4
- data.tar.gz: fc0888f8a80f019e20059535fe6e926ed5c56e40
3
+ metadata.gz: a6838ab6252d5ec860f98df20e04bca32f93b801
4
+ data.tar.gz: bfe44e8887c1867ea74b2de25dd8e979383ff97a
5
5
  SHA512:
6
- metadata.gz: 056453f28cb7f4b62464736c07cbdc90bfe53aa1802d371075fe418146646d11312047cbec620edea1620c4f0b1b4a80319992db6bbf63f93b28314ce3ada481
7
- data.tar.gz: b9d31eb78fef4c3753aa19f610cf092474757bee9b01339b303e8a853a286c30405244fcea628a00bb375a9460b97c4cd9b48e3fbab4bb8b1577a8989c39ae89
6
+ metadata.gz: 07bbd06b2c295609c2123fd45a3261bfc8beafa8cdb1453d973c0a651ea3c8fe46ccf28de426b65bcaf9a5f90a5836121d5d57ea345d00f58168f74cef76773a
7
+ data.tar.gz: fb7050fc14c45a0a1affd783ab6fa2d365bae45c849188039203f5436ceaf1e742f9661225bf87a1dcac57f6a4d0464c466823bb06c4b66796e9d1ec61e5dc23
@@ -1,6 +1,7 @@
1
1
  require 'openssl'
2
2
  require 'cfpropertylist'
3
3
  require 'itunes_receipt_decoder/decode/base'
4
+ require 'itunes_receipt_decoder/public_key'
4
5
 
5
6
  ##
6
7
  # ItunesReceiptDecoder
@@ -11,44 +12,77 @@ module ItunesReceiptDecoder
11
12
  ##
12
13
  # ItunesReceiptDecoder::Decode::TransactionReceipt
13
14
  class TransactionReceipt < Base
14
- PUBLIC_KEY = OpenSSL::PKey::RSA.new <<-PUBLIC_KEY
15
- -----BEGIN PUBLIC KEY-----
16
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApLyvMpRDPgu8N4fNY4ny
17
- zNm+IE1atP6HZ9Ka3hpUnaLz34fkTMuTEXigMI80QcHTvmZtR2yYuOx61cndpeTq
18
- xnD0NdCR97PYChGZqzpiOr179FZP258kk1FQfCDVZk1m8xikE5YiFv0xp/Q5Zpv7
19
- YmlcS5+UqEvo7FtkWhh5ihZ1Y0KkSdmMM96te9Y5BPTinQppjOtLEihLNEgHmw5Z
20
- +R9isAOfNrhOo9N1WdTzOgXKxTM7+MAGCQiT2+dNvxHzUiylFjUV80ECzQLR/PX4
21
- xYS9Y2qG1raZ9oauX/0D1CiKWl2vvGV00fcaw5II9BytaegCTA6VFQe8vmpvwbOt
22
- oQIDAQAB
23
- -----END PUBLIC KEY-----
24
- PUBLIC_KEY
15
+ ##
16
+ # ItunesReceiptDecoder::Decode::TransactionReceipt::SignatureValidation
17
+ class SignatureValidation
18
+ attr_reader :blob, :purchase_info
19
+ attr_accessor :version, :signature, :raw_cert, :cert_length
20
+
21
+ def initialize(blob, purchase_info)
22
+ @blob = blob
23
+ @purchase_info = purchase_info
24
+ end
25
+
26
+ def valid?
27
+ unpack &&
28
+ raw_cert.size == cert_length &&
29
+ cert &&
30
+ cert.verify(public_key) &&
31
+ verify
32
+ end
33
+
34
+ private
35
+
36
+ def verify
37
+ data = [version, purchase_info].pack('ca*')
38
+ cert.public_key.verify(OpenSSL::Digest::SHA1.new, signature, data)
39
+ end
40
+
41
+ def cert
42
+ @cert ||= OpenSSL::X509::Certificate.new(raw_cert)
43
+ rescue OpenSSL::X509::CertificateError => _e
44
+ false
45
+ end
46
+
47
+ def unpack
48
+ self.version, following = blob.unpack('c a*')
49
+ return false unless [2, 3].include?(version)
50
+ arrangement =
51
+ case version
52
+ when 2 then 'a128 N a*'
53
+ when 3 then 'a256 N a*'
54
+ end
55
+ self.signature, self.cert_length, self.raw_cert =
56
+ following.unpack(arrangement)
57
+ end
58
+
59
+ def public_key
60
+ @public_key ||=
61
+ case version
62
+ when 2 then PublicKey::V2
63
+ when 3 then PublicKey::V3
64
+ end
65
+ end
66
+ end
25
67
 
26
68
  def style
27
69
  :transaction
28
70
  end
29
71
 
30
72
  def signature_valid?
31
- version, sig, cert_length, cert =
32
- payload.fetch('signature').unpack('m').first.unpack('c a128 N a*')
33
- return false unless
34
- version == 2 &&
35
- sig.size == 128 &&
36
- cert.size == cert_length &&
37
- (cert = OpenSSL::X509::Certificate.new(cert)) &&
38
- cert.verify(PUBLIC_KEY)
39
- data = [version, purchase_info].pack('ca*')
40
- cert.public_key.verify(OpenSSL::Digest::SHA1.new, sig, data)
73
+ SignatureValidation.new(signature, purchase_info).valid?
41
74
  end
42
75
 
43
76
  private
44
77
 
45
78
  def decode
46
79
  @receipt = parse_purchase_info
47
- if payload.fetch('environment', 'Production') == 'Production'
48
- @environment = :production
49
- else
50
- @environment = :sandbox
51
- end
80
+ @environment =
81
+ if payload.fetch('environment', 'Production') == 'Production'
82
+ :production
83
+ else
84
+ :sandbox
85
+ end
52
86
  end
53
87
 
54
88
  def parse_purchase_info
@@ -64,6 +98,10 @@ PUBLIC_KEY
64
98
  @purchase_info ||= payload.fetch('purchase-info').unpack('m').first
65
99
  end
66
100
 
101
+ def signature
102
+ @signature ||= payload.fetch('signature').unpack('m').first
103
+ end
104
+
67
105
  def payload
68
106
  @payload ||= parse_plist(raw_receipt)
69
107
  end
@@ -71,7 +109,7 @@ PUBLIC_KEY
71
109
  def parse_plist(contents)
72
110
  plist = CFPropertyList::List.new(data: contents)
73
111
  hash = CFPropertyList.native_types(plist.value)
74
- fail DecodingError, 'hash not found in plist' unless hash.is_a?(Hash)
112
+ raise DecodingError, 'hash not found in plist' unless hash.is_a?(Hash)
75
113
  hash
76
114
  rescue CFPlistError => e
77
115
  raise DecodingError, e.message
@@ -1,6 +1,7 @@
1
1
  require 'time'
2
2
  require 'openssl'
3
3
  require 'itunes_receipt_decoder/decode/base'
4
+ require 'itunes_receipt_decoder/public_key'
4
5
 
5
6
  ##
6
7
  # ItunesReceiptDecoder
@@ -11,18 +12,6 @@ module ItunesReceiptDecoder
11
12
  ##
12
13
  # ItunesReceiptDecoder::Decode::UnifiedReceipt
13
14
  class UnifiedReceipt < Base
14
- PUBLIC_KEY = OpenSSL::PKey::RSA.new <<-PUBLIC_KEY
15
- -----BEGIN PUBLIC KEY-----
16
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyjhUpstWqsgkOUjpjO7s
17
- X7h/JpG8NFN6znxjgGF3ZF6lByO2Of5QLRVWWHAtfsRuwUqFPi/w3oQaoVfJr3sY
18
- /2r6FRJJFQgZrKrbKjLtlmNoUhU9jIrsv2sYleADrAF9lwVnzg6FlTdq7Qm2rmfN
19
- UWSfxlzRvFduZzWAdjakh4FuOI/YKxVOeyXYWr9Og8GN0pPVGnG1YJydM05V+RJY
20
- DIa4Fg3B5XdFjVBIuist5JSF4ejEncZopbCj/Gd+cLoCWUt3QpE5ufXN4UzvwDtI
21
- jKblIV39amq7pxY1YNLmrfNGKcnow4vpecBqYWcVsvD95Wi8Yl9uz5nd7xtj/pJl
22
- qwIDAQAB
23
- -----END PUBLIC KEY-----
24
- PUBLIC_KEY
25
-
26
15
  ##
27
16
  # ASN.1 Field types
28
17
  #
@@ -46,11 +35,11 @@ PUBLIC_KEY
46
35
  1708 => :expires_date,
47
36
  1712 => :cancellation_date,
48
37
  1711 => :web_order_line_item_id
49
- }
38
+ }.freeze
50
39
 
51
40
  TIMESTAMP_FIELDS = %i(creation_date expiration_date purchase_date
52
41
  original_purchase_date expires_date
53
- cancellation_date)
42
+ cancellation_date).freeze
54
43
 
55
44
  def style
56
45
  :unified
@@ -67,18 +56,19 @@ PUBLIC_KEY
67
56
  def signature_valid?
68
57
  serial = pkcs7.signers.first.serial.to_i
69
58
  cert = pkcs7.certificates.find { |c| c.serial.to_i == serial }
70
- cert && cert.verify(PUBLIC_KEY)
59
+ cert && cert.verify(PublicKey::V3)
71
60
  end
72
61
 
73
62
  private
74
63
 
75
64
  def decode
76
65
  @receipt = parse_app_receipt_fields(payload.value)
77
- if @receipt.fetch(:environment, 'Production') == 'Production'
78
- @environment = :production
79
- else
80
- @environment = :sandbox
81
- end
66
+ @environment =
67
+ if @receipt.fetch(:environment, 'Production') == 'Production'
68
+ :production
69
+ else
70
+ :sandbox
71
+ end
82
72
  end
83
73
 
84
74
  def parse_app_receipt_fields(fields)
@@ -0,0 +1,32 @@
1
+ require 'openssl'
2
+
3
+ ##
4
+ # ItunesReceiptDecoder
5
+ module ItunesReceiptDecoder
6
+ ##
7
+ # ItunesReceiptDecoder::PublicKey
8
+ module PublicKey
9
+ V2 = OpenSSL::PKey::RSA.new <<-PUBLIC_KEY
10
+ -----BEGIN PUBLIC KEY-----
11
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApLyvMpRDPgu8N4fNY4ny
12
+ zNm+IE1atP6HZ9Ka3hpUnaLz34fkTMuTEXigMI80QcHTvmZtR2yYuOx61cndpeTq
13
+ xnD0NdCR97PYChGZqzpiOr179FZP258kk1FQfCDVZk1m8xikE5YiFv0xp/Q5Zpv7
14
+ YmlcS5+UqEvo7FtkWhh5ihZ1Y0KkSdmMM96te9Y5BPTinQppjOtLEihLNEgHmw5Z
15
+ +R9isAOfNrhOo9N1WdTzOgXKxTM7+MAGCQiT2+dNvxHzUiylFjUV80ECzQLR/PX4
16
+ xYS9Y2qG1raZ9oauX/0D1CiKWl2vvGV00fcaw5II9BytaegCTA6VFQe8vmpvwbOt
17
+ oQIDAQAB
18
+ -----END PUBLIC KEY-----
19
+ PUBLIC_KEY
20
+ V3 = OpenSSL::PKey::RSA.new <<-PUBLIC_KEY
21
+ -----BEGIN PUBLIC KEY-----
22
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyjhUpstWqsgkOUjpjO7s
23
+ X7h/JpG8NFN6znxjgGF3ZF6lByO2Of5QLRVWWHAtfsRuwUqFPi/w3oQaoVfJr3sY
24
+ /2r6FRJJFQgZrKrbKjLtlmNoUhU9jIrsv2sYleADrAF9lwVnzg6FlTdq7Qm2rmfN
25
+ UWSfxlzRvFduZzWAdjakh4FuOI/YKxVOeyXYWr9Og8GN0pPVGnG1YJydM05V+RJY
26
+ DIa4Fg3B5XdFjVBIuist5JSF4ejEncZopbCj/Gd+cLoCWUt3QpE5ufXN4UzvwDtI
27
+ jKblIV39amq7pxY1YNLmrfNGKcnow4vpecBqYWcVsvD95Wi8Yl9uz5nd7xtj/pJl
28
+ qwIDAQAB
29
+ -----END PUBLIC KEY-----
30
+ PUBLIC_KEY
31
+ end
32
+ end
@@ -3,5 +3,5 @@
3
3
  module ItunesReceiptDecoder
4
4
  ##
5
5
  # Gem version
6
- VERSION = '0.3.0'
6
+ VERSION = '0.3.1'.freeze
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: itunes_receipt_decoder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - mbaasy.com
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-24 00:00:00.000000000 Z
11
+ date: 2016-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: CFPropertyList
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.4'
33
+ version: '11.1'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.4'
40
+ version: '11.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.3'
47
+ version: '3.4'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.3'
54
+ version: '3.4'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rubygems-tasks
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -72,42 +72,42 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.10'
75
+ version: '0.11'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.10'
82
+ version: '0.11'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: codeclimate-test-reporter
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.4'
89
+ version: '0.5'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.4'
96
+ version: '0.5'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rubocop
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.33'
103
+ version: '0.40'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0.33'
110
+ version: '0.40'
111
111
  description: |2
112
112
  Decode iTunes OS X and iOS receipts without remote server-side validation
113
113
  by using the Apple Inc Root Certificate.
@@ -120,6 +120,7 @@ files:
120
120
  - lib/itunes_receipt_decoder/decode/base.rb
121
121
  - lib/itunes_receipt_decoder/decode/transaction_receipt.rb
122
122
  - lib/itunes_receipt_decoder/decode/unified_receipt.rb
123
+ - lib/itunes_receipt_decoder/public_key.rb
123
124
  - lib/itunes_receipt_decoder/version.rb
124
125
  homepage: https://github.com/mbaasy/itunes_receipt_decoder
125
126
  licenses:
@@ -141,9 +142,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
142
  version: '0'
142
143
  requirements: []
143
144
  rubyforge_project:
144
- rubygems_version: 2.4.6
145
+ rubygems_version: 2.6.3
145
146
  signing_key:
146
147
  specification_version: 4
147
148
  summary: Decode iTunes OS X and iOS receipts
148
149
  test_files: []
149
- has_rdoc: