e2ee for twitter dms?
yesterday, twitter rolled out encrypted direct messages.
their help page listed some glaring limitations, so i wanted to take a look inside.
they don’t actually seem to have any write-up of their cryptography. however, the code is, at time of writing, at https://abs.twimg.com/responsive-web/client-web/ondemand.DirectMessagesCrypto.3826782a.js. minified of course, but still easy enough to work with.
how it works
note! i don’t pay twitter, and i don’t think i know anyone who pays twitter, so i haven’t actually watched this in action, it’s just my inference from reading the code. (amusingly enough, there is a check in the client side to see if you and the other person pay twitter, and i mildly tempted to see if there’s no server side enforcement.)
as far as i can tell their setup works something like this:
- every device creates a P-256 private key and registers the matching public key with twitter.
- when Alice begins a conversation with Bob, she generates a 128-bit AES conversation encryption key.
- fetches all of Bob’s registered public keys and her other device’s public keys, for each key she:
- generates a new P-256 private key.
- derives the 256-bit ECDH shared secret between those private keys and Bob’s public keys.
- throws away that private key.
- hashes that shared secret with SHA-256 and splits it into a 128-bit key and 128-bit IV.
- AES-GCM encrypts the conversation encryption key with that key and IV.
- sends the encrypted conversation encryption key to other device
- then when Alice or Bob send a message, they encrypt it with AES-GCM, the conversation encryption key, and a random IV
- they also similarly encrypt each emoji reaction in response to that message
what is not broken
well, the messages are encrypted. and the secret keys don’t leave your devices. and if twitter only eavesdrops they can’t read your messages.
what is broken
twitter has (at least) two cryptographic options for actively interfering to spy on Alice’s messages:
add a public key to Alice’s registry. for every new encrypted conversation that public key gets sent the encryption secret.
create a conversation by sending an encryption key pretending to be the other party. note that this requires even less effort than normal man-in-the-middle attacks, because in 3.1 Alice creates a one-off encryption key. Bob cannot even expect to tell who is initiating contact!
they also have the obvious non-cryptographic option:
- push a code change (possibly targeted only at alice’s web client) that sends the conversation keys (or private key) back to twitter
one could theoretically create a web extension that detected (the possibility of) all three of these attacks. for option 1, it alerts the user when a new encryption key has been added and check whether they logged in on a new device recently. for option 2, it alerts when an encrypted conversation begins and no messages were sent. though twitter could still send an initial message. for option 3, it alerts for every change in code that has access to IndexedDB. and the (technically savvy) Alice reads the minified js diff to make sure that the code change isn’t malicious.
(maybe not that last one.)
however, option 1 in combination with
Currently, we don’t erase the private key from the device on logout. After logging in back on the same device, your device will be able to re-fetch and decrypt the encrypted conversations using the private key that the device had access to before logout. Once we offer the key backup feature, we will start erasing keys on logout. Until then users should exercise caution while using the product on a shared device such as public computers.
definitely raises some questions!
i feel kinda badly for whoever wrote this.
the whole thing with the generating an ephemeral secret key to set up the conversation is clearly groping toward something, that’s not the sort of thing you do because it’s the obvious thing to do. but without specifically setting things up correctly around it, ephemeral keys just undermine the ability to authenticate your conversational partner.
the static derivation of the key and iv to encrypt the conversation key is also strange - it doesn’t create a duplicate use of the iv because the key generated is random / ephemeral, but it also doesn’t add anything relative to a random iv.
i get the sense that there was a fair amount of learning and trying to pull together ideas here, and, well, implementing encrypted dms for twitter is not where i’d want to be learning this stuff.
all in all, there are lots of ways to improve this cryptographically, and some of them would even improve the user experience! and while it does look passably secure against eavesdropping (but not a guarantee), it’s got a long way to go to catch up with signal.