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 +7 -0
 - data/lib/aliquot-pay/util.rb +29 -0
 - data/lib/aliquot-pay.rb +107 -0
 - metadata +73 -0
 
    
        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
         
     | 
    
        data/lib/aliquot-pay.rb
    ADDED
    
    | 
         @@ -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: []
         
     |