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

ECv2 support #1

Merged
merged 7 commits into from
Jan 14, 2019
Merged

ECv2 support #1

merged 7 commits into from
Jan 14, 2019

Conversation

kse-clearhaus
Copy link
Contributor

No description provided.

@mt-clearhaus mt-clearhaus changed the title Ecv2 support ECv2 support Jan 14, 2019
def self.generate_signature(*args)
args.map do |s|
four_byte_length(s) + s
end.join('')
Copy link
Contributor

Choose a reason for hiding this comment

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

Space as separator is default.

Copy link
Member

Choose a reason for hiding this comment

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

Empty string as separator is default 🙂

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm sorry... I thought "empty string" but wrote "space"...

def self.generate_token_ecv2(payment, signing_key, intermediate_key, recipient,
signed_message: nil, expire_time: "#{Time.now.to_i + 3600}000")
cipher = OpenSSL::Cipher::AES256.new(:CTR)
signed_message ||= JSON.unparse(encrypt(JSON.unparse(payment), recipient, cipher))
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain the two instances of JSON unparsing here? I am a bit confused 🤔

Copy link
Member

Choose a reason for hiding this comment

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

The inner payment is #to_json'ed (JSON.unparse'd) before it's encrypted ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Exactly. The payment is added as serialized JSON, this is done because it is what is verified by tag, a MAC.
This results in a signed message, which is also a serialized JSON string in the final token, since it is signed by Google.

input_keying_material = ephemeral_public_key + shared_secret
if OpenSSL.const_defined?(:KDF) && OpenSSL::KDF.respond_to?(:hkdf)
h = OpenSSL::Digest::SHA256.new
hbytes = OpenSSL::KDF.hkdf(input_keying_material, hash: h, salt: '', length: 32, info: info)
hbytes = OpenSSL::KDF.hkdf(input_keying_material, hash: h, salt: '', length: length * 2, info: info)
Copy link
Contributor

Choose a reason for hiding this comment

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

So 64 is correct and 32 was incorrect? Did it work before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both 64 and 32 are correct.
For ECv1 you need to generate 32 bytes of key material, 128 bits for AES key and 128 bits for MAC key.
For ECv2you need 64 bytes, 256 bits for each.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since ECv2 has superseded ECv1 the required 64 bytes of keys is now default.

Copy link
Member

Choose a reason for hiding this comment

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

With v1 I suspect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean ct?
Yes, it worked before with ECv1.

aes_key: hbytes[0..15],
mac_key: hbytes[16..32],
aes_key: hbytes[0..length - 1],
mac_key: hbytes[length..2 * length],
Copy link
Contributor

Choose a reason for hiding this comment

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

Not length..(2 * length) - 1 ? I.e. 32..63.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively, length..-1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it is. luckily it still performs as it should. Now I'm considering it, I prefer.

        aes_key: hbytes[0, length],
        mac_key: hbytes[length, length],

lib/aliquot-pay.rb Outdated Show resolved Hide resolved
recipient_id = DEFAULTS[:merchant_id],
sender_id = DEFAULTS[:info],
protocol_version = 'ECv1'
recipient_id: "merchant:#{DEFAULTS[:merchant_id]}",
Copy link
Member

Choose a reason for hiding this comment

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

Uhm, but if the recipient_id does always start with "merchant:" then ... couldn't we "hide" this "deeper" so the user calling .signature_string will not have to prepend the merchant ID with "merchant:"? Perhaps it's nicer to do it like you do — for debugging and spec'ing purposes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm. I see no benefit in doing it like I do it. As in, we don't use it right now.
I agree that it should be merchant_id as a parameter rather than recipient_id.

Copy link
Member

Choose a reason for hiding this comment

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

If convenient for debugging/spec'ing, you could allow both 🙂

@@ -11,18 +11,18 @@ def self.generate_shared_secret(private_key, public_key)
private_key.dh_compute_key(public_key)
end

def self.derive_keys(ephemeral_public_key, shared_secret, info)
def self.derive_keys(ephemeral_public_key, shared_secret, info, length: 32)
Copy link
Member

Choose a reason for hiding this comment

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

Thought: What about supplying the "EC version" rather than length? I think that could perhaps be more user friendly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm. Sure, that makes sense. 👍

@kse-clearhaus
Copy link
Contributor Author

I sort of feel like this library is badly written for generating tokens.
In my opinion a more object oriented construction would be better. The real question is whether I want to spend time on rewriting it.

- `JSON.unparse` => `#to_json`
- Derive cipher from protocol_version instead of passing cipher object
- Fix indexing of MAC/AES keys
@kse-clearhaus
Copy link
Contributor Author

@ct-clearhaus @mt-clearhaus Any more comments?

Copy link
Contributor

@mt-clearhaus mt-clearhaus left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@kse-clearhaus kse-clearhaus merged commit 4907e86 into master Jan 14, 2019
@kse-clearhaus kse-clearhaus deleted the ecv2-support branch January 14, 2019 10:21
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.

3 participants