RSA + ECDSA allows an attacker to claim other ciphertexts as their own
katrielalex opened this issue · 5 comments
I noticed something weird about the RSA+ECDSA mode, but I am not sure what the threat model for the library is so I'm not sure if it would be considered an attack.
I figured it would be most sensible not to describe it in a public issue just in case it turns out to be unintended behaviour, but there is no other reporting mechanism listed in the README. What's the right approach here?
Hi Katriel,
We use GitHub Issues to track all issues. Please feel free to describe it here.
The RSA+ECDSA mode uses an encrypt-then-sign:
- first, it encrypts some ciphertext under a public RSA key, and
- second, it signs that ciphertext with the private ECDSA key.
The problem with this pattern in general is that an attacker can strip the signature from a message and re-sign it with their own signature key, thus claiming the message as their own. For example, an aggregator app might have a single RSA private key but accept messages signed by various ECDSA keys (e.g. through instance.updateSenderVerifier(senderVerificationKey)
), in which case a malicious sender could take someone else's message, strip the signature, and claim it as their own.
One fix for this should be to include the expected signature key under the encryption, such as through Tink's context
. However, capillary doesn't use that.
I'm trying to understand at the moment how signing and encryption keys relate to each other; the abov assumes there are multiple signing keys for a particular encryption key, and I can't tell from the API whether that is allowed.
Thanks Katriel for the detailed report!
The security model of the Capillary library is that the endpoints (sender and receivers) are trusted but any intermediate entities (push messaging providers) are not trusted. In addition, we assume that there exists one sender and many receivers.
In the case of RSA-ECDSA, we require the sender to pin the ECDSA verification key in each receiver (as a raw resource in the Android app). This allows receivers to detect any modifications to ciphertexts by the intermediate entities (aka IND-CCA2 security). Ideally, we would use RSA with OAEP padding for this purpose, but Android versions 22 and below do not support OAEP padding.
The attack you mentioned is indeed valid if one were to use RSA-ECDSA in a more general context. But, the attack does not apply in the security model of the Capillary library.
Cool, thanks! Pinning the verification key does indeed seem to rule out the attack I described above (cc @galadran with whom I've been discussing this 😺).
One related weird thing we noticed is that because keys don't seem to be really tied to a notion of app or device identity, an evil app can cause a misbinding if it knows another app's public key. For example, in an HR app, Alex might honestly decrypt a notification "you're fired", even though the honest server sent the notification to someone else entirely. I suspect this is a property of FCM and hence by design, but figured I'd flag it up just in case.
(Details: consider some sender with signature key pair skS/pkS
, and suppose Alex registers an honest app install with encryption key pair skA/pkA
. Eli, a nasty hacker, gets Alex’s key pair and installs the app, but also claims encryption public key skA
to the server. The sender sends a notification sign_skS({You're fired!}pkA})
to Eli, and Eli forwards it to Alex. Alex now thinks that the server intended to send You're fired!
to Alex.)
Thanks Katriel and Dennis!
We intentionally didn't tie the RSA public keys to device identifier due to potential privacy concerns. Instead, we let the developers decide whether to include some form of a device identifier (e.g., the FCM token) in the plaintext to mitigate the attack you described.