Class: R2D2::PaymentToken

Inherits:
Object
  • Object
show all
Defined in:
lib/r2d2/payment_token.rb

Defined Under Namespace

Classes: TagVerificationError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(token_attrs) ⇒ PaymentToken

Returns a new instance of PaymentToken.



12
13
14
15
16
# File 'lib/r2d2/payment_token.rb', line 12

def initialize(token_attrs)
  self.ephemeral_public_key = token_attrs["ephemeralPublicKey"]
  self.tag = token_attrs["tag"]
  self.encrypted_message = token_attrs["encryptedMessage"]
end

Instance Attribute Details

#encrypted_messageObject

Returns the value of attribute encrypted_message.



8
9
10
# File 'lib/r2d2/payment_token.rb', line 8

def encrypted_message
  @encrypted_message
end

#ephemeral_public_keyObject

Returns the value of attribute ephemeral_public_key.



8
9
10
# File 'lib/r2d2/payment_token.rb', line 8

def ephemeral_public_key
  @ephemeral_public_key
end

#tagObject

Returns the value of attribute tag.



8
9
10
# File 'lib/r2d2/payment_token.rb', line 8

def tag
  @tag
end

Class Method Details

.decrypt_message(encrypted_data, symmetric_key) ⇒ Object



56
57
58
59
60
61
62
# File 'lib/r2d2/payment_token.rb', line 56

def decrypt_message(encrypted_data, symmetric_key)
  decipher = OpenSSL::Cipher::AES128.new(:CTR)
  decipher.decrypt
  decipher.key = symmetric_key
  decipher.auth_data = ""
  decipher.update(Base64.decode64(encrypted_data)) + decipher.final
end

.derive_hkdf_keys(ephemeral_public_key, shared_secret) ⇒ Object



42
43
44
45
46
47
48
49
# File 'lib/r2d2/payment_token.rb', line 42

def derive_hkdf_keys(ephemeral_public_key, shared_secret)
  key_material = Base64.decode64(ephemeral_public_key) + shared_secret;
  hkdf = HKDF.new(key_material, :algorithm => 'SHA256', :info => 'Android')
  {
    :symmetric_encryption_key => hkdf.next_bytes(16),
    :mac_key => hkdf.next_bytes(16)
  }
end

.generate_shared_secret(private_key, ephemeral_public_key) ⇒ Object



35
36
37
38
39
40
# File 'lib/r2d2/payment_token.rb', line 35

def generate_shared_secret(private_key, ephemeral_public_key)
  ec = OpenSSL::PKey::EC.new('prime256v1')
  bn = OpenSSL::BN.new(Base64.decode64(ephemeral_public_key), 2)
  point = OpenSSL::PKey::EC::Point.new(ec.group, bn)
  private_key.dh_compute_key(point)
end

.verify_mac(digest, mac_key, encrypted_message, tag) ⇒ Object



51
52
53
54
# File 'lib/r2d2/payment_token.rb', line 51

def verify_mac(digest, mac_key, encrypted_message, tag)
  mac = OpenSSL::HMAC.digest(digest, mac_key, Base64.decode64(encrypted_message))
  raise TagVerificationError unless secure_compare(mac, Base64.decode64(tag))
end

Instance Method Details

#decrypt(private_key_pem) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/r2d2/payment_token.rb', line 18

def decrypt(private_key_pem)
  digest = OpenSSL::Digest.new('sha256')
  private_key = OpenSSL::PKey::EC.new(private_key_pem)

  shared_secret = self.class.generate_shared_secret(private_key, ephemeral_public_key)

  # derive the symmetric_encryption_key and mac_key
  hkdf_keys = self.class.derive_hkdf_keys(ephemeral_public_key, shared_secret);

  # verify the tag is a valid value
  self.class.verify_mac(digest, hkdf_keys[:mac_key], encrypted_message, tag)

  self.class.decrypt_message(encrypted_message, hkdf_keys[:symmetric_encryption_key])
end

#secure_compare(a, b) ⇒ Object

constant-time comparison algorithm to prevent timing attacks; borrowed from ActiveSupport::MessageVerifier



70
71
72
# File 'lib/r2d2/payment_token.rb', line 70

def secure_compare(a, b)
  FastSecureCompare.compare(a, b)
end