I am analyzing an existing end-to-end encrypted, cloud based service (e.g. email, file storage or chat) which also has a public cloud based address book (for managing contacts) so that users of the cloud service can communicate securely between themselves (e.g. send email, share data or chat).
To log into the cloud based service, all that users need to remember is their password. When logging in, the user's password is fed into a password based key derivation function e.g. PBKDF2 which produces a derived key and that is used to verify and decrypt their account's symmetric cryptographic key e.g. AES-256 (for protecting the user's own data and account operations) and their public/private key pair e.g. RSA 3072 (for sharing data with other users, sending email or chatting). These keys are created at account registration time on the client side and uploaded to the cloud service. Everything is persisted on the server, but data is fetched and cryptographic operations are performed client side. Users can log out and log in on another computer or device and have access to all the data they had before logging out.
For a proper end-to-end paradigm, users need to verify each others' public keys in person but store (pin) the fingerprint of the public key they've seen and that they've verified. Otherwise if users do not verify public keys then they would be vulnerable to an active MITM attack by someone on the network path (e.g. Internet) or the cloud service themselves (either the cloud service is secretly malicious, under duress, an employee is a spy, they get hacked, or they receive a court order etc). So in theory nothing from the cloud service should be trusted. Everything should be re-verified by the user on the client side upon being fetched from the service and security is rooted in the secrecy and strength of the password.
When a user adds a contact (via email address) to their address book, the client fetches the public key for that user from the server database as well. The client accepts the default public key from the server and then uploads a data record to the server to retain the information that they have seen that public key fingerprint.
users table contains a list of user records e.g.
user id], [
user email], [
public key], [
encrypted private key], [
encrypted symmetric key]
A user id / contact id is a 64 bit unique, random string in hex. The
users_contacts table contains a mapping of contacts for each user e.g.
user id], [
The public key fingerprint records are stored in a separate table
contacts_fingerprints. The record that is stored on the server side is authenticated with a MAC e.g. HMAC-SHA256 using the user's symmetric key e.g.
user id], [
contact id], [
public key fingerprint e.g. 256 bits hex], [
verified flag e.g. 0x00 or 0x01], [
MAC(user id || contact id || public key fingerprint || verified flag)]
It will then be up to the users to verify that key is correct (out of band) after that point to set the verified flag to true (0x01).
However after analyzing this design, I see that there are some attacks possible which make this design not truly secure in the end-to-end paradigm:
The cloud provider can selectively delete the records in the
contacts_fingerprintstable, so when a user logs in again to fetch the data, they may not notice that the user they are communicating with is no longer verified. E.g. the client just re-fetches the public key and saves it again because maybe it thinks there was a network error or it got corrupted or something. This can then be used to perform a MITM attack on the user. Because the record is also not encrypted this can be used to target specific users.
The cloud provider can archive and keep previous database records e.g. at a time when the contact relationships were created and the fingerprints weren't verified. Then at a later point, after the record was updated with a verified fingerprint, they can choose to roll it back to a previous version that was unverified. Then when the user logs in and re-fetches the data they download the old record and they may not notice that the user is unverified anymore. This could be used as an advantage for the service provider to MITM the user e.g. when the contact relationship is established, the provider always serves up a MITM public key, that gets saved by the user automatically. When they try verifying, then the client fetches the real key for verification and thus when confirming fingerprints out of band it checks out and they update the record with the correct key and verified flag.
What can be done to redesign this system and prevent these attacks?