You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
architecture/docs/relationships-cryptography.md

9.4 KiB

Table of Contents

(Also see Threat models)

Thoughts

Basic scenario

We should somehow identify when two users have sympathies toward each other.

The easiest way to do this would be to derive some kind of a hash that would be the same regardless of who of the two computed it, and impossible to compute by anybody else. In addition, we would need to identify whether two identical versions of that hash were computed by the same user or by different users.

Metamour scenario

We should somehow identify when three users form a full graph.

The easiest way to do this would be to derive some kind of a hash that would be the same regardless of who of the three computed it, and impossible to compute by anybody else.

In addition, we would need to identify whether three identical versions of that hash were computed by three different users; and this should be unforgeable.

This second part could be solved by either storing some additional hashes along the common hash; these hashes would have to be different between users and unforgeable (i.e. derived on a server side only from user's public key, and data used to obtain the common hash).

Or alternatively it could be solved by some form of secret sharing protocol without parties communicating to each other: encrypt some piece of an information with a common public key, attach a part of a common "private" key (only used for that purpose for that triple of users); every user should be able to compute a common public key and their part of a common private key; all three parts are required to decrypt a message. I am not aware of any protocols that allow to do this.

Assumptions

Every user has an elliptic curve keypair; server only knows their public keys.

There is an INSTANCE_ID which is supposed to uniquely identify the instance of the platform used (it could be an instance URL, for example).

There is also a SECRET_PADDING used on the server.

Used cryptographic primitives

  • sign(private_key, data), verify(public_key, signed_data): verify(public_key, sign(private_key, data)) == true. Additionally, sign output should not leak information about the public key.
  • encrypt(public_key, data), decrypt(private_key, encrypted_data): decrypt(private_key, encrypt(public_key, data)) == data. encrypt does not have to be stable. Additionally, encrypt should not leak information about the public key.
  • symmetric_encrypt(key, data), symmetric_decrypt(key, encrypted_data).
  • derive_key(data): generates a new symmetric encryption key, should be stable (always produce the same result for the same data) and irreversible.
  • hash(data1, data2, ...): should be stable (always produce the same result) and irreversible.
  • shared_key2(private_key_a, public_key_b): shared_key2(private_key_a, public_key_b) == shared_key2(private_key_b, public_key_a). shared_key2 has to be stable (to always return the same result for the same input data).
  • shared_key3(private_key_a, public_key_b, public_key_c) == shared_key3(private_key_a, public_key_c, public_key_b) == shared_key3(private_key_b, public_key_a, public_key_c). shared_key3 has to be stable (to always return the same result for the same input data).

The following methods are used

  • ECDSA for signing (does it leak public key?)
  • ECIES for asymmetric encryption (does it leak public key?)
  • AES-256 for symmetric encryption
  • SHA-256 for hashing (if multiple values are supplied, sha256(sha256(data1) + sha256(data2) + sha256(data3) + ...) is used).
  • ECDH for shared_key2

All requests to the server are signed with user's private key. Server verifies the signature against the supplied public key.

All responses from the server are encrypted with that public key. So that the user can decrypt them with their private key.

Scenarios

Basic scenario

Data submitted to the server

If an user X wants to save their sympathy towards Y, they submit the following data to the server:

  • hash(shared_key2(private_X, public_Y), INSTANCE_ID) (referred to as common_hash below)
  • symmetric_encrypt(derive_key(private_X), metadata) (referred to as encrypted_metadata below), where metadata is only used on the client (e.g. Y's display name for X, creation date, etc).

Server logic

  • Compute the following fields:
    • hash(common_hash; SECRET_PADDING_COMMON2) (referred to as padded_common_hash below)
    • public_X (referred to as public_key below)
  • Do the rate limiting: check if the number of non-mutual sympathies for that public_key is within allowed limit.
  • Check if there is an entry with this padded_common_hash but different public_key in the table of pending sympathies.
    • If there is not:
      • Save a new entry to that table with the following fields: padded_common_hash, public_key, encrypted_metadata, creation_date;
      • Respond with "sympathy registered"
    • If there is:
      • Retrieve and remove that entry;
      • Store its public_key and encrypted_metadata in a table of mutual sympathies;
      • Store this request's public_key and encrypted_metadata in a table of completed sympathies;
      • Send a notification to the user from that old entry (using its public_key);
      • Respond with "sympathy is mutual"

Security

Malicious API usage

TODO: to be written...

Stored data

Prior to match, only public_X, padded_common_hash, encrypted_metadata_X, creation_date are stored. encrypted_metadata_X only exposes any information to the holder of private_X, who owns this data anyway. padded_common_hash only exposes any information to the holder of private_X (who owns this data anyway) or private_Y plus SECRET_PADDING_COMMON2. So in case of DB+secrets leak, Y would be able to learn about X's non-mutual sympathy towards Y. public_X and creation_date expose information about who registered how many non-mutual sympathies and when (in case of DB leak).

After match, two entries are stored: (public_X, encrypted_metadata_X) and (public_Y, encrypted_metadata_Y). encrypted_metadata_X only exposes any information to the holder of private_X. public_X and public_Y expose information about who registered how many mutual sympathies. A special care should be taken to make sure there is no way to deduce "mutual date" from these entries, especially since they are inserted into DB in roughly the same time, to avoid an attacker with DB access from deducing that two entries are related.

Metamour scenario

Data submitted to the server

If an user X wants to also learn about the shared connections when registering a sympathy, in addition to the two fields above they also submit the following data to the server:

  • For every mutual and non-mutual sympathy towards every user Z:
    • hash(shared_key2(private_X, public_Y), public_Z, INSTANCE_ID) (referred to as common_XY_Z below);
    • common_XZ_Y
    • encrypted_metadata_X_Y (with an information about Y and Z)
    • encrypted_metadata_X_Z (encrypted with a different nonce, to avoid matching the two)

Additionally, they store all such public_Z in the encrypted_metadata field for their pending sympathy towards Y.

Note that common_XY_Z == common_YX_Z

Server logic

  • For every entry in the list:
    • Compute the following fields:
      • hash(common_XY_Z, SECRET_PADDING_COMMON3) (referred to as padded_common_XY_Z below; note that padded_common_XY_Z == padded_common_YX_Z)
      • padded_common_XZ_Y
      • hash(common_XY_Z, public_X, SECRET_PADDING_ID) (referred to as id_X_Y_Z below; note that id_X_Y_Z is different from any other permutation such as id_Y_X_Z)
      • id_X_Z_Y
      • symmetric_encrypt(derive_key(hash(common_XY_Z, SECRET_PADDING_KEY)), [common_XZ_Y, public_X]) (referred to as encrypted_data_XY_Z below)
      • encrypted_data_XY_Z
    • Check how many entries are there with the same padded_common values but different id values in the table of pending metamour sympathies:
      • If there is not one of each (for the total of two):
        • Add the following two entries to the table:
          • padded_common_XY_Z, id_X_Y_Z, encrypted_data_XY_Z, encrypted_metadata_X_Y
          • padded_common_XZ_Y, id_X_Z_Y, encrypted_data_XZ_Y, encrypted_metadata_X_Z
        • Respond with sympathy registered
      • If there is one of each:
        • Retrieve them and remove them from the table of pending metamour sympathies
        • Decrypt the encrypted_data field, obtain the third remaining padded_common value plus both remaining public keys
        • Remove two entries for the third padded_common value
        • Add the following three entries to the table of completed metamour sympathies:
          • public_X, encrypted_metadata_X (any of the two)
          • public_Y (obtained by decrypting encrypted_data), encrypted_metadata_Y (obtained from the previously existing entry)
          • public_Z, encrypted_metadata_Z
        • Send notifications to Y and Z
        • Respond with sympathy mutual

Explanation

TODO: To be written...

Security

TODO: To be written...

Managing the existing sympathies

Viewing

TODO: To be written...

Removing pending

TODO: To be written...

Removing all data

TODO: To be written...

Managing the existing metamour sympathies

Viewing

TODO: To be written...

Caveat

Removing pending

TODO: To be written...

Caveat

Removing all data

TODO: To be written...