class AliquotPay
Constants
- DEFAULTS
- EC_CURVE
Attributes
auth_method[RW]
cleartext_message[RW]
cryptogram[RW]
eci_indicator[RW]
encrypted_message[RW]
ephemeral_public_key[RW]
expiration_month[RW]
expiration_year[RW]
info[RW]
intermediate_key[RW]
intermediate_signing_key[RW]
key_expiration[RW]
key_value[RW]
message_expiration[RW]
message_id[RW]
pan[RW]
payment_method[RW]
payment_method_details[RW]
recipient[RW]
recipient_id[W]
root_key[RW]
signature[RW]
signatures[RW]
signed_key[RW]
signed_key_string[W]
signed_message[RW]
tag[RW]
token[W]
Public Class Methods
new(protocol_version = :ECv2)
click to toggle source
# File lib/aliquot-pay.rb, line 28 def initialize(protocol_version = :ECv2) @protocol_version = protocol_version end
Public Instance Methods
build_cleartext_message()
click to toggle source
# File lib/aliquot-pay.rb, line 114 def build_cleartext_message return @cleartext_message if @cleartext_message default_message_id = Base64.strict_encode64(OpenSSL::Random.random_bytes(24)) default_message_expiration = ((Time.now.to_f + 60 * 5) * 1000).round.to_s @cleartext_message = { 'messageExpiration' => @message_expiration || default_message_expiration, 'messageId' => @message_id || default_message_id, 'paymentMethod' => @payment_method || 'CARD', 'paymentMethodDetails' => build_payment_method_details } end
build_payment_method_details()
click to toggle source
# File lib/aliquot-pay.rb, line 95 def build_payment_method_details return @payment_method_details if @payment_method_details value = { 'pan' => @pan || '4111111111111111', 'expirationYear' => @expiration_year || 2023, 'expirationMonth' => @expiration_month || 12, 'authMethod' => @auth_method || 'PAN_ONLY', } if @auth_method == 'CRYPTOGRAM_3DS' value.merge!( 'cryptogram' => @cryptogram || 'SOME CRYPTOGRAM', 'eciIndicator' => @eci_indicator || '05' ) end value end
build_signature()
click to toggle source
# File lib/aliquot-pay.rb, line 173 def build_signature return @signature if @signature key = case @protocol_version when :ECv1 ensure_root_key when :ECv2 ensure_intermediate_key end signature_string = signed_string_message = ['Google', recipient_id, @protocol_version.to_s, signed_message_string].map do |str| [str.length].pack('V') + str end.join @signature = sign(key, signature_string) end
build_signatures()
click to toggle source
# File lib/aliquot-pay.rb, line 192 def build_signatures return @signatures if @signatures signature_string = signed_key_signature = ['Google', 'ECv2', signed_key_string].map do |str| [str.to_s.length].pack('V') + str.to_s end.join @signatures = [sign(ensure_root_key, signature_string)] end
build_signed_key()
click to toggle source
# File lib/aliquot-pay.rb, line 142 def build_signed_key return @signed_key if @signed_key ensure_intermediate_key if @intermediate_key.private_key? || @intermediate_key.public_key? public_key = eckey_to_public(@intermediate_key) else fail 'Intermediate key must be public and private key' end default_key_value = Base64.strict_encode64(public_key.to_der) default_key_expiration = "#{Time.now.to_i + 3600}000" @signed_key = { 'keyExpiration' => @key_expiration || default_key_expiration, 'keyValue' => @key_value || default_key_value, } end
build_signed_message()
click to toggle source
# File lib/aliquot-pay.rb, line 127 def build_signed_message return @signed_message if @signed_message signed_message = encrypt(build_cleartext_message.to_json) signed_message['encryptedMessage'] = @encrypted_message if @encrypted_message signed_message['ephemeralPublicKey'] = @ephemeral_public_key if @ephemeral_public_key signed_message['tag'] = @tag if @tag @signed_message = signed_message end
build_token()
click to toggle source
# File lib/aliquot-pay.rb, line 203 def build_token return @token if @token res = { 'protocolVersion' => @protocol_version.to_s, 'signedMessage' => @signed_message || signed_message_string, 'signature' => build_signature, } if @protocol_version == :ECv2 intermediate = { 'intermediateSigningKey' => @intermediate_signing_key || { 'signedKey' => signed_key_string, 'signatures' => build_signatures, } } res.merge!(intermediate) end @token = res end
eckey_to_public(key)
click to toggle source
# File lib/aliquot-pay.rb, line 46 def eckey_to_public(key) p = OpenSSL::PKey::EC.new(EC_CURVE) p.public_key = key.public_key p end
encrypt(cleartext_message)
click to toggle source
# File lib/aliquot-pay.rb, line 62 def encrypt(cleartext_message) @recipient ||= OpenSSL::PKey::EC.new('prime256v1').generate_key @info ||= 'Google' eph = AliquotPay::Util.generate_ephemeral_key @shared_secret ||= AliquotPay::Util.generate_shared_secret(eph, @recipient.public_key) ss = @shared_secret case @protocol_version when :ECv1 cipher = OpenSSL::Cipher::AES128.new(:CTR) when :ECv2 cipher = OpenSSL::Cipher::AES256.new(:CTR) else raise StandardError, "Invalid protocol_version #{protocol_version}" end keys = AliquotPay::Util.derive_keys(eph.public_key.to_bn.to_s(2), ss, @info, @protocol_version) cipher.encrypt cipher.key = keys[:aes_key] encrypted_message = cipher.update(cleartext_message) + cipher.final tag = AliquotPay::Util.calculate_tag(keys[:mac_key], encrypted_message) { 'encryptedMessage' => Base64.strict_encode64(encrypted_message), 'ephemeralPublicKey' => Base64.strict_encode64(eph.public_key.to_bn.to_s(2)), 'tag' => Base64.strict_encode64(tag), } end
ensure_intermediate_key()
click to toggle source
# File lib/aliquot-pay.rb, line 169 def ensure_intermediate_key @intermediate_key ||= OpenSSL::PKey::EC.new(EC_CURVE).generate_key end
ensure_root_key()
click to toggle source
# File lib/aliquot-pay.rb, line 165 def ensure_root_key @root_key ||= OpenSSL::PKey::EC.new(EC_CURVE).generate_key end
extract_root_signing_keys()
click to toggle source
# File lib/aliquot-pay.rb, line 36 def extract_root_signing_keys key = Base64.strict_encode64(eckey_to_public(ensure_root_key).to_der) { 'keys' => [ 'protocolVersion' => @protocol_version, 'keyValue' => key, ] }.to_json end
recipient_id()
click to toggle source
# File lib/aliquot-pay.rb, line 225 def recipient_id @recipient_id ||= DEFAULTS[:recipient_id] end
sign(key, message)
click to toggle source
private
# File lib/aliquot-pay.rb, line 56 def sign(key, message) d = OpenSSL::Digest::SHA256.new def key.private?; private_key?; end Base64.strict_encode64(key.sign(d, message)) end
signed_key_string()
click to toggle source
# File lib/aliquot-pay.rb, line 161 def signed_key_string @signed_key_string ||= build_signed_key.to_json end
signed_message_string()
click to toggle source
# File lib/aliquot-pay.rb, line 138 def signed_message_string @signed_message_string ||= build_signed_message.to_json end
token()
click to toggle source
# File lib/aliquot-pay.rb, line 32 def token build_token end