apple_pay 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e2d670b4b42468a1fd808f6def948711705cc64
4
- data.tar.gz: ae5ed92e30f238e36e9e561dfc80d51fcd62e046
3
+ metadata.gz: 872eee631d7a4283f5db260d42e8c8d7ec74374b
4
+ data.tar.gz: 37de100fe240ee997d2fbc651cde5556c6a1be14
5
5
  SHA512:
6
- metadata.gz: 1f0a8132576548e9c84671c47e680e40fed322fc0e036897f5a3e3f7d07e9c8b52d4fae6225be7649b2277e59e60282dc661b5a4639c4392726cdd39bcc0b9ab
7
- data.tar.gz: 248f63fa443c0c73d40b1035146b6254943d041d75dba31edf5b8a72819886c5675cd3833a92c105a0a0a64f72d1e8d3759eef1adc752b5a0b1c9e4639899f21
6
+ metadata.gz: 4d65c58543c27b2479c0ce62f2645306223a2702b19a7462c744df3be5b4427e446c1ffd368ba86cdea8a5675b6889d619c72cbd75833b5360dbaaabeea04f19
7
+ data.tar.gz: 2983387b72c306189db5407a1654006ba45547423ccb36cc4e7e1ecbaeb665c483e19cd3f903effe2138e9485bc2a63254770aebebe37b846f7ac8db933f8b85
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -4,8 +4,8 @@ Gem::Specification.new do |spec|
4
4
  spec.authors = ['Nov Matake']
5
5
  spec.email = ['nov@matake.jp']
6
6
 
7
- spec.summary = %q{Apple Pay Merchant Backend}
8
- spec.description = %q{Apple Pay Merchant Backend}
7
+ spec.summary = %q{Apple Pay Merchant Backend}
8
+ spec.description = %q{Apple Pay Merchant Backend}
9
9
  spec.homepage = 'https://github.com/nov/apple_pay.git'
10
10
  spec.license = 'MIT'
11
11
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
@@ -7,7 +7,7 @@ module ApplePay
7
7
  File.join(__dir__, '../VERSION')
8
8
  ).strip
9
9
 
10
- class APIError < StandardError; end
10
+ class Error < StandardError; end
11
11
 
12
12
  def self.logger
13
13
  @@logger
@@ -38,4 +38,5 @@ module ApplePay
38
38
  end
39
39
 
40
40
  require 'apple_pay/merchant'
41
+ require 'apple_pay/payment_token'
41
42
  require 'apple_pay/request_filter/debugger'
@@ -1,5 +1,7 @@
1
1
  module ApplePay
2
2
  class Merchant
3
+ class APIError < Error; end
4
+
3
5
  attr_accessor :identifier, :domain, :display_name, :client_cert, :private_key
4
6
 
5
7
  def initialize(identifier, domain:, display_name:)
@@ -9,7 +11,7 @@ module ApplePay
9
11
  end
10
12
 
11
13
  def authenticate(client_cert, private_key)
12
- self.client_cert = client_cert
14
+ self.client_cert = client_cert # NOTE: Apple Pay Merchant Identity
13
15
  self.private_key = private_key
14
16
  self
15
17
  end
@@ -0,0 +1,35 @@
1
+ module ApplePay
2
+ class PaymentToken
3
+ attr_accessor :token
4
+
5
+ def initialize(token)
6
+ self.token = token.with_indifferent_access
7
+ end
8
+
9
+ def verify!
10
+ Signature.new(
11
+ token[:paymentData][:signature],
12
+ data: token[:paymentData][:data],
13
+ ephemeral_public_key: token[:paymentData][:header][:ephemeralPublicKey],
14
+ transaction_id: token[:paymentData][:header][:transactionId],
15
+ application_data: token[:paymentData][:header][:applicationData]
16
+ ).verify!
17
+ self
18
+ end
19
+
20
+ def decrypt!(client_cert, private_key)
21
+ decrypted = EncryptedData.new(
22
+ token[:paymentData][:data]
23
+ ).decrypt!(
24
+ client_cert,
25
+ private_key,
26
+ token[:paymentData][:header][:ephemeralPublicKey]
27
+ )
28
+ JSON.parse decrypted
29
+ end
30
+ end
31
+ end
32
+
33
+ require 'apple_pay/payment_token/certificate_chain'
34
+ require 'apple_pay/payment_token/signature'
35
+ require 'apple_pay/payment_token/encrypted_data'
@@ -0,0 +1,32 @@
1
+ module ApplePay
2
+ class PaymentToken
3
+ class CertificateChain
4
+ CHAIN_OIDS = {
5
+ leaf: '1.2.840.113635.100.6.29',
6
+ intermediate: '1.2.840.113635.100.6.2.14'
7
+ }
8
+
9
+ attr_accessor :pkcs7, :leaf, :intermediate, :root
10
+
11
+ def initialize(pkcs7_encoded)
12
+ self.pkcs7 = OpenSSL::PKCS7.new Base64.decode64(pkcs7_encoded)
13
+ [:leaf, :intermediate].each do |position|
14
+ detected = pkcs7.certificates.detect do |cert|
15
+ cert.extensions.collect(&:oid).include? CHAIN_OIDS[position]
16
+ end
17
+ self.send "#{position}=", detected
18
+ end
19
+ self.root = OpenSSL::X509::Certificate.new(
20
+ File.read File.join(__dir__, 'AppleRootCa-G3.cer')
21
+ )
22
+ end
23
+
24
+ def verify(signature_base_string)
25
+ trusted_store = OpenSSL::X509::Store.new
26
+ trusted_store.add_cert root
27
+ pkcs7.certificates = [leaf, intermediate].compact
28
+ pkcs7.verify nil, trusted_store, signature_base_string
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,77 @@
1
+ module ApplePay
2
+ class PaymentToken
3
+ class EncryptedData
4
+ class DecryptionFailed < Error; end
5
+
6
+ MERCHANT_ID_OID = '1.2.840.113635.100.6.32'
7
+
8
+ attr_accessor :data
9
+
10
+ def initialize(encoded_data)
11
+ self.data = Base64.decode64 encoded_data
12
+ end
13
+
14
+ def decrypt!(client_cert, private_key, ephemeral_public_key_or_wrapped_key) # NOTE: Payment Processing Certificate
15
+ merchant_id = merchant_id_in client_cert
16
+ shared_secret = shared_secret_derived_from private_key, ephemeral_public_key_or_wrapped_key
17
+ symmetric_key = symmetric_key_derived_from merchant_id, shared_secret
18
+ cipher = OpenSSL::Cipher.new('aes-256-gcm')
19
+ cipher.decrypt
20
+ cipher.iv_len = 16 # NOTE: require ruby 2.4.0+ & openssl gem v2.0.0+
21
+ cipher.key = symmetric_key
22
+ cipher.auth_tag = data[-16..-1]
23
+ cipher.update(data[0..-17]) + cipher.final
24
+ end
25
+
26
+ private
27
+
28
+ def merchant_id_in(client_cert)
29
+ merchant_id_with_prefix = client_cert.extensions.detect do |ext|
30
+ ext.oid == MERCHANT_ID_OID
31
+ end
32
+ raise DecryptionFailed, 'Merchant ID missing' unless merchant_id_with_prefix
33
+ merchant_id_with_prefix.value[2..-1]
34
+ end
35
+
36
+ def shared_secret_derived_from(private_key, ephemeral_public_key_or_wrapped_key)
37
+ case private_key
38
+ when OpenSSL::PKey::RSA
39
+ shared_secret_derived_from_rsa(
40
+ private_key,
41
+ ephemeral_public_key_or_wrapped_key
42
+ )
43
+ when OpenSSL::PKey::EC
44
+ shared_secret_derived_from_ec(
45
+ private_key,
46
+ ephemeral_public_key_or_wrapped_key
47
+ )
48
+ else
49
+ raise DecryptionFailed, "Unknown algorithm (#{private_key.class})"
50
+ end
51
+ end
52
+
53
+ def shared_secret_derived_from_rsa(private_key, wrapped_key)
54
+ raise DecryptionFailed, 'RSA not supported yet'
55
+ end
56
+
57
+ def shared_secret_derived_from_ec(private_key, ephemeral_public_key)
58
+ public_key = OpenSSL::PKey::EC.new(
59
+ Base64.decode64 ephemeral_public_key
60
+ ).public_key
61
+ point = OpenSSL::PKey::EC::Point.new(
62
+ private_key.group,
63
+ public_key.to_bn
64
+ )
65
+ private_key.dh_compute_key point
66
+ end
67
+
68
+ def symmetric_key_derived_from(merchant_id, shared_secret)
69
+ OpenSSL::Digest::SHA256.digest(
70
+ "\x00" * 3 + "\x01" +
71
+ shared_secret +
72
+ "\x0D" + 'id-aes256-GCM' + 'Apple' + [merchant_id].pack("H*")
73
+ )
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,31 @@
1
+ module ApplePay
2
+ class PaymentToken
3
+ class Signature
4
+ class VerificationFailed < Error; end
5
+
6
+ attr_accessor :signature, :data, :ephemeral_public_key, :wrapped_key, :transaction_id, :application_data
7
+
8
+ def initialize(signature, data:, ephemeral_public_key: nil, wrapped_key: nil, transaction_id:, application_data: nil)
9
+ self.signature = signature
10
+ self.data = data
11
+ self.ephemeral_public_key = ephemeral_public_key
12
+ self.wrapped_key = wrapped_key
13
+ self.transaction_id = transaction_id
14
+ self.application_data = application_data
15
+ end
16
+
17
+ def verify!
18
+ chain = CertificateChain.new signature
19
+ signature_base_string = [
20
+ Base64.decode64(ephemeral_public_key || wrapped_key),
21
+ Base64.decode64(data),
22
+ [transaction_id].pack('H*'),
23
+ [application_data].pack('H*')
24
+ ].join
25
+ chain.verify(
26
+ signature_base_string
27
+ ) or raise VerificationFailed
28
+ end
29
+ end
30
+ end
31
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apple_pay
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
  - Nov Matake
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-29 00:00:00.000000000 Z
11
+ date: 2016-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -94,7 +94,7 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- description: "\x10Apple Pay Merchant Backend"
97
+ description: Apple Pay Merchant Backend
98
98
  email:
99
99
  - nov@matake.jp
100
100
  executables: []
@@ -114,6 +114,11 @@ files:
114
114
  - bin/setup
115
115
  - lib/apple_pay.rb
116
116
  - lib/apple_pay/merchant.rb
117
+ - lib/apple_pay/payment_token.rb
118
+ - lib/apple_pay/payment_token/AppleRootCA-G3.cer
119
+ - lib/apple_pay/payment_token/certificate_chain.rb
120
+ - lib/apple_pay/payment_token/encrypted_data.rb
121
+ - lib/apple_pay/payment_token/signature.rb
117
122
  - lib/apple_pay/request_filter/debugger.rb
118
123
  homepage: https://github.com/nov/apple_pay.git
119
124
  licenses:
@@ -135,8 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
140
  version: '0'
136
141
  requirements: []
137
142
  rubyforge_project:
138
- rubygems_version: 2.5.1
143
+ rubygems_version: 2.6.6
139
144
  signing_key:
140
145
  specification_version: 4
141
- summary: "\x10Apple Pay Merchant Backend"
146
+ summary: Apple Pay Merchant Backend
142
147
  test_files: []