# Oblivious Address Discovery
Scenario / goals:
- User $A$ wants to enable cryptocurrency account discovery for a list of its
contacts in an asynchronous way, i.e., $A$ does not want to interact with any of
its contacts directly to enable discoverability.
Assumptions:
- There exists a (distributed) salting service that allows a client and a set
of servers to jointly evaluate a (distributed) verifiable oblivious
pseudorandom function (VOPRF) to derive a salt. We denote this by $y =
VOPRF(x)$.
- There exists a cache that manages discovery tuples of the form
$(id_{joint}, id_{target}, proof)$.
- There exists a smart contract that manages on-chain user accounts of the form
$(id, pk, addr)$ where id is a unique VOPRF-based identifier, $pk$ is a public
key, and $addr$ is the cryptocurrency address.
Protocol:
- For the setup, user $A$ executes the following steps:
+ S01: Compute a key pair $(sk_A, pk_A) = (a, g^a)$ and a cryptocurrency address $addr_A = H(pk_A)$.
+ S02: Register phone number $n_A$ on the platform and obtain a proof from the validators that it was successful.
+ S03: Create a zk-proof $p_A$ showing that $A$ is the legitimate owner of phone number $n_A$ as approved during registration (see subroutine below)
+ S04: Interact with the salting service to get $salt_A = VOPRF(n_A)$ legitimizing the request with $p_A$.
+ S05: Compute $id_A = H(n_A || salt_A)$.
+ S06: Store the account information $(id_A, pk_A, addr_A)$ on the blockchain.
- To enable account discovery for a contact $B$, user $A$ executes the following steps:
+ S07: Create a zk-proof $p_{AB}$ showing that $A$ is the legitimate owner of $n_A$ or $n_B$ (see subroutine below).
+ S08: Interact with the salting service to get $salt_{AB} = VOPRF(n_A || n_B)$ legitimizing the request with $p_{AB}$.
+ S09: Compute $id_{AB} = H(n_A || n_B || salt_{AB})$.
+ S10: Create a zk-proof $p_{ABA}$ showing that $id_{AB}$ and $id_A$ share a connection. (TODO)
+ S11: Store in a (distributed) cache the discovery tuple $(id_{AB}, id_A, p_{ABA})$.
- If a user $B$ wants to discover the address of one of its contacts $A$, it executes the following steps:
+ S12: Create a zk-proof $p_{BA}$ showing that $B$ is the legitimate owner of $n_B$ or $n_A$ (see subroutine below).
+ S13: Interact with the salting service to get $salt_{AB} = VOPRF(n_A || n_B)$ legitimizing the request with $p_{BA}$.
+ S14: Compute $id_{AB} = H(n_A || n_B || salt_{AB})$.
+ S15: Retrieve the tuple $(id_{AB}, id_A, p_{ABA})$ from the (distributed) cache and verify $p_{ABA}$.
+ S16: If $p_{ABA}$ is invalid, abort. Otherwise retrieve the account information $(id_A, pk_A, addr_A)$ from the blockchain.
Subroutines:
For S03:
- We denote $g_1$ and $g_2$ as the base points for pairing curves $G_1$ and $G_2$.
- Assume user $A$ has a validator BLS signature $s = H(n_A)^v$ where $v$ is the
(aggregate) secret key and $pk = (g_1)^v$ is the aggregate public key that he
got during registration. We denote the key pair of the VOPRF service by $(k, g^k)$
- A picks a random value $r$, and sends to the salting service $s^r$ and $H(n_A)^r$
(note: the latter will be the actual input to the VOPRF).
- The salting service verifies those values via $e(g_1, s^r) = e(pk, H(n_A)^r)$
and if correct replies with $s_k = (H(n_A)^r)^k$.
- After receiving $s_k$, the user BLS-verifies it against $g^k$, if correct
removes $r$ from $s_k$, and computes $salt_A = H( n_A || H(n_A)^k )$.
For S07 / S12:
- Phone numbers $n_A$ and $n_B$.
- Validator BLS sig $s_v = H(n_A)^v$.
- Pick random $r$ and compute $(s_v)^r$, $H(n_A)^r$, $H(n_B)^r$.
- Authenticate as in S03.
- Salting service sends back $( H(n_A)^r \cdot H(n_B)^r )^k$.
- User verifies that value using $g^k$, if correct removes $r$, and then outputs
$salt_{AB} = H( H(n_A) \cdot H(n_B) || (H(n_A) \cdot H(n_B))^k )$.
- The symmetric approach works for $B$ (S12).
Notes:
- We use the (distributed) cache (e.g., could be something like IPFS) as an
intermediary to save on-chain space, to enable updateability of the
connections between contacts, and to give users the choice for which of their
contacts they would like to enable phone-number-based discoverability and
whether they want to point all of their contacts to one account, have a
separate account for each of their contacts or anything in between.
- The cache learns for how many of its contacts a user has enabled
discoverability.
- The cache does not learn the connection between phone numbers and addresses
as it does not own any of the phone numbers and thus cannot query the VOPRF
service.
- The cache also does not learn whether users share a connection as it only
stores discovery tuples containing oblivious indices and a zk-proof.
- A cache can delete its entries or deny discovery requests. Since caches do not
know the phone numbers that correspond to certain entries, denying requests
selectively seems difficult. Caches can still identify users through other
means such as their IP addresses. Users can counter this by connecting to the
cache through an anonymity network. Another mitigation could be that there are
several caches and users decide where they store their discovery tuples either
randomly or with those caches providing the best service.