aliquot-pay 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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: []