aliquot-pay 0.5.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ea8a43259ca5f437e1aa0d86b3c8ee3b93a257ec1ebcd20274cf1d3b0d7fdea6
4
+ data.tar.gz: f231c69d8d5d2314cc0f835cdb55b8e5b60a13200c37a5b6c5ec3eb7fee1c335
5
+ SHA512:
6
+ metadata.gz: 7d7d6ef3b956607876f48f02c29e24a0b9015cbc569470974bcd8232da578f4f3964bc0936d5f579fe7f9ad44146c95913211cac323205aa5076140ab4d4f7b7
7
+ data.tar.gz: 3952d7c0b71eca60300e7cea0472392d96f2827abfa721417ec68a17b5e007237f14698d69f80c3ad1f6ae4b467eddd1970695629b1773b1c4deb72aa7530c52
@@ -0,0 +1,29 @@
1
+ require 'openssl'
2
+ require 'hkdf'
3
+
4
+ module AliquotPay
5
+ class Util
6
+ def self.generate_ephemeral_key
7
+ OpenSSL::PKey::EC.new(AliquotPay::EC_CURVE).generate_key
8
+ end
9
+
10
+ def self.generate_shared_secret(private_key, public_key)
11
+ private_key.dh_compute_key(public_key)
12
+ end
13
+
14
+ def self.derive_keys(ephemeral_public_key, shared_secret, info)
15
+ ikm = ephemeral_public_key + shared_secret
16
+ hbytes = HKDF.new(ikm, algorithm: 'SHA256', info: info).next_bytes(32)
17
+
18
+ {
19
+ aes_key: hbytes[0..15],
20
+ mac_key: hbytes[16..32],
21
+ }
22
+ end
23
+
24
+ def self.calculate_tag(mac_key, encrypted_message)
25
+ digest = OpenSSL::Digest::SHA256.new
26
+ OpenSSL::HMAC.digest(digest, mac_key, encrypted_message)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,107 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+
4
+ require 'aliquot-pay/util'
5
+
6
+ module AliquotPay
7
+ class Error < StandardError; end
8
+
9
+ EC_CURVE = 'prime256v1'.freeze
10
+
11
+ DEFAULTS = {
12
+ info: 'Google',
13
+ merchant_id: 'merchant:0123456789',
14
+ }.freeze
15
+
16
+ def self.sign(key, encrypted_message)
17
+ d = OpenSSL::Digest::SHA256.new
18
+ def key.private?; private_key?; end
19
+ Base64.strict_encode64(key.sign(d, encrypted_message))
20
+ end
21
+
22
+ def self.encrypt(cleartext_message, recipient, info = 'Google')
23
+ eph = AliquotPay::Util.generate_ephemeral_key
24
+ ss = AliquotPay::Util.generate_shared_secret(eph, recipient.public_key)
25
+
26
+ keys = AliquotPay::Util.derive_keys(eph.public_key.to_bn.to_s(2), ss, info)
27
+
28
+ c = OpenSSL::Cipher::AES128.new(:CTR)
29
+ c.encrypt
30
+ c.key = keys[:aes_key]
31
+
32
+ encrypted_message = c.update(cleartext_message) + c.final
33
+
34
+ tag = AliquotPay::Util.calculate_tag(keys[:mac_key], encrypted_message)
35
+
36
+ {
37
+ encryptedMessage: Base64.strict_encode64(encrypted_message),
38
+ ephemeralPublicKey: Base64.strict_encode64(eph.public_key.to_bn.to_s(2)),
39
+ tag: Base64.strict_encode64(tag),
40
+ }
41
+ end
42
+
43
+ # Return a default payment
44
+ def self.payment(
45
+ auth_method: :PAN_ONLY,
46
+ expiration: ((Time.now.to_f + 60 * 5) * 1000).round.to_s
47
+ )
48
+ id = Base64.strict_encode64(OpenSSL::Random.random_bytes(24))
49
+ p = {
50
+ 'messageExpiration' => expiration,
51
+ 'messageId' => id,
52
+ 'paymentMethod' => 'CARD',
53
+ 'paymentMethodDetails' => {
54
+ 'expirationYear' => 2023,
55
+ 'expirationMonth' => 12,
56
+ 'pan' => '4111111111111111',
57
+ 'authMethod' => 'PAN_ONLY',
58
+ },
59
+ }
60
+
61
+ if auth_method == :CRYPTOGRAM_3DS
62
+ p['paymentMethodDetails']['authMethod'] = 'CRYPTOGRAM_3DS'
63
+ p['paymentMethodDetails']['cryptogram'] = 'SOME CRYPTOGRAM'
64
+ p['paymentMethodDetails']['eciIndicator'] = '05'
65
+ end
66
+
67
+ p
68
+ end
69
+
70
+ # Return a string length as a 4byte little-endian integer, as a string
71
+ def self.four_byte_length(str)
72
+ [str.length].pack('V')
73
+ end
74
+
75
+ def self.signature_string(
76
+ message,
77
+ recipient_id = DEFAULTS[:merchant_id],
78
+ sender_id = DEFAULTS[:info],
79
+ protocol_version = 'ECv1'
80
+ )
81
+
82
+ four_byte_length(sender_id) +
83
+ sender_id +
84
+ four_byte_length(recipient_id) +
85
+ recipient_id +
86
+ four_byte_length(protocol_version) +
87
+ protocol_version +
88
+ four_byte_length(message) +
89
+ message
90
+ end
91
+
92
+ # payment:: Google Pay token as a ruby Hash
93
+ # signing_key:: OpenSSL::PKEY::EC
94
+ # recipient:: OpenSSL::PKey::EC
95
+ # message:: SignedMessage
96
+ def self.generate_token(payment, signing_key, recipient, message = nil)
97
+ message ||= AliquotPay.encrypt(JSON.unparse(payment), recipient)
98
+
99
+ signature_string = AliquotPay.signature_string(JSON.unparse(message))
100
+
101
+ {
102
+ 'protocolVersion' => 'ECv1',
103
+ 'signature' => AliquotPay.sign(signing_key, signature_string),
104
+ 'signedMessage' => JSON.unparse(message),
105
+ }
106
+ end
107
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aliquot-pay
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Clearhaus
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-09-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hkdf
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ description:
42
+ email: hello@clearhaus.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/aliquot-pay.rb
48
+ - lib/aliquot-pay/util.rb
49
+ homepage: https://github.com/clearhaus/aliquot-pay
50
+ licenses:
51
+ - MIT
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 2.7.7
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: Generates Google Pay test dummy tokens
73
+ test_files: []