apple_pay 0.0.1 → 0.0.2

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: 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: []