Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#18 Add ECv2 support for google pay tokens #20

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

mlauter
Copy link

@mlauter mlauter commented Oct 8, 2020

Adds ECv2 support according to https://developers.google.com/pay/api/android/guides/resources/payment-data-cryptography

Testing

We've added tests for google_pay ec_v2.

bundle exec rake passes

kellysavietta2 and others added 2 commits October 8, 2020 14:24
Co-authored-by: Miriam Lauter <lauter.miriam@gmail.com>
Co-authored-by: Spencer Alan <hello@spenceralan.com>
Co-authored-by: Miriam Lauter <lauter.miriam@gmail.com>
Co-authored-by: Spencer Alan <hello@spenceralan.com>
Copy link
Contributor

@bdewater bdewater left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, thanks for your contribution! I haven't read up on the ECv2 scheme (ECv1 still works so we didn't need an upgrade) so my small comments are mostly on code style.

I'm also not able to merge or release this so I encourage you to reach out to Spreedly for this :)

lib/r2d2/util.rb Outdated
def decrypt_message(encrypted_data, symmetric_key, cipher_key_length_bits = 128)
raise ArgumentError, "Invalid cipher_key_length #{cipher_key_length_bits} must be 128 or 256" unless [128, 256].include?(cipher_key_length_bits)

decipher = cipher_key_length_bits == 256 ? OpenSSL::Cipher::AES256.new(:CTR) : OpenSSL::Cipher::AES128.new(:CTR)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following reads easier and also doesn't use deprecated constants:

Suggested change
decipher = cipher_key_length_bits == 256 ? OpenSSL::Cipher::AES256.new(:CTR) : OpenSSL::Cipher::AES128.new(:CTR)
decipher = OpenSSL::Cipher.new("aes-#{cipher_key_length_bits}-ctr")

lib/r2d2/util.rb Outdated
@@ -83,8 +86,8 @@ def secure_compare(a, b)
end

if defined?(OpenSSL::KDF) && OpenSSL::KDF.respond_to?(:hkdf)
def hkdf(key_material, info)
OpenSSL::KDF.hkdf(key_material, salt: 0.chr * 32, info: info, length: 32, hash: 'sha256')
def hkdf(key_material, info, length = 32)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question on the need for length to have a default value need here (and below for the alternative implementation.

Comment on lines 140 to 142
def sender_id
'Google'
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it should be a constant and all the other 'Google' strings replaced to use it.

end

def intermediate_key_expired?
cur_millis = (Time.now.to_f * 1000).floor
Copy link
Contributor

@bdewater bdewater Oct 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being used twice in the class, could be extracted to a method.


def valid_key_signatures?(signing_keys, signatures, signed)
signing_keys.product(signatures).any? do |key, sig|
key.verify(OpenSSL::Digest::SHA256.new, Base64.strict_decode64(sig), signed)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use deprecated constants:

Suggested change
key.verify(OpenSSL::Digest::SHA256.new, Base64.strict_decode64(sig), signed)
key.verify(OpenSSL::Digest.new("SHA256"), Base64.strict_decode64(sig), signed)

@@ -44,8 +45,10 @@ def verify_mac(mac_key, encrypted_message, tag)
raise TagVerificationError unless secure_compare(mac, Base64.decode64(tag))
end

def decrypt_message(encrypted_data, symmetric_key)
decipher = OpenSSL::Cipher::AES128.new(:CTR)
def decrypt_message(encrypted_data, symmetric_key, cipher_key_length_bits = 128)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should cipher_key_length_bits have a default value, since we always calculate it on line 30?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same deal here re android_pay

@@ -29,12 +29,13 @@ def generate_shared_secret(private_key, ephemeral_public_key)
private_key.dh_compute_key(point)
end

def derive_hkdf_keys(ephemeral_public_key, shared_secret, info)
def derive_hkdf_keys(ephemeral_public_key, shared_secret, info, key_length_bytes = 16)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should key_length_bytes have a default value?

Copy link
Author

@mlauter mlauter Oct 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the function is also used by android_pay_token which we didn't want to touch, and 16 bytes was an original assumption of the function

@mlauter
Copy link
Author

mlauter commented Oct 13, 2020

This looks good, thanks for your contribution! I haven't read up on the ECv2 scheme (ECv1 still works so we didn't need an upgrade) so my small comments are mostly on code style.

I'm also not able to merge or release this so I encourage you to reach out to Spreedly for this :)

Thank you so much for the feedback @bdewater, will make some updates and try and reach Spreedly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants