tink-crypto / tink

Tink is a multi-language, cross-platform, open source library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.
https://developers.google.com/tink
Apache License 2.0
13.49k stars 1.18k forks source link

Hybrid decryption #454

Closed ykharko closed 1 year ago

ykharko commented 3 years ago

Hi there.

I'm trying to use python version of library to decrypt Google pay tokens. https://developers.google.com/pay/api/web/guides/resources/payment-data-cryptography?hl=en#payment-method-token-structure

Algorithm parameters: https://developers.google.com/pay/api/processors/guides/implementation/payment-data-cryptography#encrypt-spec

ECIES, NIST P-256, AES-256-CTR, HMAC-based with SHA-256.

I've prepared keyset and trying to run the code (sorry I don't know how to insert code correctly with new lines):

_reader = CustomReader(keyset) private_keyset_handle = cleartext_keyset_handle.read(reader) hybrid_decrypt = private_keyset_handle.primitive(hybrid.HybridDecrypt) plaintext = hybriddecrypt.decrypt(ciphertext, context)

Where "context" is "Google" string and "ciphertext" is encryptedMessage field from the GooglePay response. This encryptedMessage doesn't contain mac or any additional things, just plaintext.

Python uses C++ implementation and after some investigations of the C++ code I found out that ciphertext has to have format: ephemeralPublicKey + IV + encryptedMessage + tag

There are 2 problems with MAC verification:

  1. https://github.com/google/tink/blob/master/cc/subtle/encrypt_then_authenticate.cc#L107 Here we can see MAC verification and then decryption of ciphertext. Two parameters are passed to VerifyMac method: expected tag from ciphertext and prepared toAuthData string. toAuthData string consists of additional_data (empty all the time) + ciphertext without expected tag. but in line 107 Big Endian representation of size of additional_data is added to toAuthData string. The size of additional_data is 0 and string with 8 null bytes is added to toAuthData string. It leads to wrong calculated tag and failed verification. Commenting this line helps to verify mac.

Question: What I'm doing wrong? is it possible to verify content correctly if it was encoded not by tink library without adding len of additional data at the end of toAuthData?

  1. If we go next into auto pt = ind_cpa_cipher_->Decrypt(payload); This Decrypt method takes IV from rest of ciphertext:

https://github.com/google/tink/blob/master/cc/subtle/aes_ctr_boringssl.cc#L102

So, I have a situation: When I pass IV (zero IV) through ciphertext and format of ciphertext is ephemeralPublicKey + IV + encryptedMessage + tag decryption is working fine but verifying of mac failed. (looks like expected tag was computed without using IV?)

But if I pass ciphertext without IV and comment line from point 1 - verification works but decryption not.

Can someone help me and say what I'm doing wrong and what I can do? Thanks.

ykharko commented 3 years ago

Additionally I would like to clarify if it’s necessary to hardcore using of such things like the length of the aad and IV in the code without any possibility to impact on that?

kste commented 3 years ago

It's actually important to have the aad length here included (or some other form of separating the aad and ciphertext) and not just concatenate aad and ciphertext for the authentication. For example if you have a cipher C = C1||C2 and want to authenticate it you would do something like HMAC(C1||C2) for computing the tag. However, if you now have a ciphertext C = C2 and use aad = C1, this would give you the same tag HMAC(C1||C2). This immediately breaks the security properties of the AEAD, as someone can forge a valid message without the key.

ykharko commented 3 years ago

Thank you for your reply!

I see what you mean. But at the same time I have some questions:

  1. Why this solution isn't used in Java code? Is it a security issue? Implementation of Hmac calculation: https://github.com/google/tink/blob/master/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenUtil.java#L70 Used here: https://github.com/google/tink/blob/acddae66fbcd38d0502c0e65c051a780eca7d6a1/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridDecrypt.java#L102 As you can see there aren't any additional data.

  2. I can't understand why GooglePay documentation says:

Note: We strongly recommend that you use our Tink library. It handles the decryption process outlined in steps 1–6, so it's unnecessary for you to write your own implementation. This outline is provided mainly for reference. If you only want code to decrypt the token, see the Use the Tink library to manage the encrypted response section instead.

For me it looks like I can't use Tink for decryption if I don't use this Java solution because it seems they create tag just from plaintext without any extra data. (commenting line with adding length of aad leads to successful mac verification).

  1. Additionally It looks like I can use Tink without problem only when Tink is used on both sides of communication and it's almost impossible to use for communication with some external service, I can't manage the process of Hmac creation at all. Am I correct?
ykharko commented 3 years ago
adongnmh commented 3 years ago

@ykharko Did you ever figure out how to Decrypt the payload in Python?

tholenst commented 1 year ago

Unfortunately, changing this is infeasible.

tholenst commented 1 year ago

(To clarify: Google Pay tokens are currently not supported in Tink Python).