class ApplePay::PaymentToken::EncryptedData

Constants

MERCHANT_ID_OID

Attributes

data[RW]

Public Class Methods

new(encoded_data) click to toggle source
# File lib/apple_pay/payment_token/encrypted_data.rb, line 10
def initialize(encoded_data)
  self.data = Base64.decode64 encoded_data
end

Public Instance Methods

decrypt!(client_cert, private_key, ephemeral_public_key_or_wrapped_key) click to toggle source
# File lib/apple_pay/payment_token/encrypted_data.rb, line 14
def decrypt!(client_cert, private_key, ephemeral_public_key_or_wrapped_key) # NOTE: Payment Processing Certificate
  merchant_id = merchant_id_in client_cert
  shared_secret = shared_secret_derived_from private_key, ephemeral_public_key_or_wrapped_key
  symmetric_key = symmetric_key_derived_from merchant_id, shared_secret
  cipher = OpenSSL::Cipher.new('aes-256-gcm')
  cipher.decrypt
  cipher.iv_len = 16 # NOTE: require ruby 2.4.0+ & openssl gem v2.0.0+
  cipher.key = symmetric_key
  cipher.auth_tag = data[-16..-1]
  cipher.update(data[0..-17]) + cipher.final
end

Private Instance Methods

merchant_id_in(client_cert) click to toggle source
# File lib/apple_pay/payment_token/encrypted_data.rb, line 28
def merchant_id_in(client_cert)
  merchant_id_with_prefix = client_cert.extensions.detect do |ext|
    ext.oid == MERCHANT_ID_OID
  end
  raise DecryptionFailed, 'Merchant ID missing' unless merchant_id_with_prefix
  merchant_id_with_prefix.value[2..-1]
end
shared_secret_derived_from(private_key, ephemeral_public_key_or_wrapped_key) click to toggle source
# File lib/apple_pay/payment_token/encrypted_data.rb, line 36
def shared_secret_derived_from(private_key, ephemeral_public_key_or_wrapped_key)
  case private_key
  when OpenSSL::PKey::RSA
    shared_secret_derived_from_rsa(
      private_key,
      ephemeral_public_key_or_wrapped_key
    )
  when OpenSSL::PKey::EC
    shared_secret_derived_from_ec(
      private_key,
      ephemeral_public_key_or_wrapped_key
    )
  else
    raise DecryptionFailed, "Unknown algorithm (#{private_key.class})"
  end
end
shared_secret_derived_from_ec(private_key, ephemeral_public_key) click to toggle source
# File lib/apple_pay/payment_token/encrypted_data.rb, line 57
def shared_secret_derived_from_ec(private_key, ephemeral_public_key)
  public_key = OpenSSL::PKey::EC.new(
    Base64.decode64 ephemeral_public_key
  ).public_key
  point = OpenSSL::PKey::EC::Point.new(
    private_key.group,
    public_key.to_bn
  )
  private_key.dh_compute_key point
end
shared_secret_derived_from_rsa(private_key, wrapped_key) click to toggle source
# File lib/apple_pay/payment_token/encrypted_data.rb, line 53
def shared_secret_derived_from_rsa(private_key, wrapped_key)
  raise DecryptionFailed, 'RSA not supported yet'
end
symmetric_key_derived_from(merchant_id, shared_secret) click to toggle source
# File lib/apple_pay/payment_token/encrypted_data.rb, line 68
def symmetric_key_derived_from(merchant_id, shared_secret)
  OpenSSL::Digest::SHA256.digest(
    "\x00" * 3 + "\x01" +
    shared_secret +
    "\x0D" + 'id-aes256-GCM' + 'Apple' + [merchant_id].pack("H*")
  )
end