Anonymity Revokers
Anonymity Revoker (AR in short) is a role that helps protecting the Shielder from bad actors. The main idea is that an AR would deanonymize all actions within the Shielder of a recognized bad actor. The typical scenario we have in mind here is as follows:
Illicit funds are detected on chain that have been used to interact with the Shielder. For instance the funds come from a well known hack.
A specific Shielder transaction, say a deposit, is determined to be using illicit funds.
A governance process decides whether to deanonymize the user behind this deposit. And if it decides YES, then a request to deanonymize is sent to the AR.
The AR reveals the transaction, and as a consequence (because of how the solution is built technically) this allows everyone to see the details of all other transactions performed by the illicit user, as if they were not using the Shielder at all.
Anonymity Revoker Key
The AR holds a private key AR_sk
for asymmetric encryption and the corresponding public key AR_pk
is a known parameter of the Shielder. The encryption scheme in use must be snark-friendly. Indeed, some transactions will need to include proofs of statements of the form:
where m
is some private input and c
is public input.
User Transactions
For a ZK-ID id
we define by key(id)
a procedure key: Scalar -> Scalar
that produces a symmetric encryption key out of the id
. The key
map should be one-way. The simplest example would be to use key(id) = hash(id)
but there might be other constraints that might force us to use a different key derivation.
When making their first transaction, the user includes on chain (in the form of an event) an encrypted version of the key key(id)
using the Anonymity Revoker's key AR_pk
, thus Enc(AR_pk, key(id)) = e_key
and importantly e_key
must be proved correct via a snark (so e_key
must enter as a public input in the circuit for creating new accounts). Note, since some accounts were created in Version 0.1.0 the migration transaction should expose e_key
instead for these accounts.
Moreover, whenever the user makes a different kind of transaction (not their first), the following data is included
mac = (r, hash(r, key(id))) : (Scalar, Scalar)
-- the HMAC "signature" of the user under a random nonce. This is to be able to identify the user's transactions among all other transactions knowing thekey(id)
.e_op = SymEnc(key(id), op_priv)
-- this is an encryption of the private part of the operationop_priv: OpPriv
the user is performing on its account, encrypted using a symmetric (snark-friendly) encryption scheme (see SNARK-friendly Symmetric Encryption) using keykey(id)
. Note:op_priv
is empty in Version 0.1.0 hence this field can be also omitted.
Modification to Transactions
Note that adding the encryptions to the transaction requires some changes to what we introduced in Notes and Accounts and ZK-ID and Registrars -- for completeness we repeat the parts that change including the necessary modifications. The main idea though is simple: in the new_note
transaction we want to reveal our key(id)
but only to the AR, and in the update_note
transaction we need to put constraints checking the correctness of encryption and forming the mac.
Changes to new_note
We skip the part about registrar, because it's optional and it doesn't change
In the above ASPkey
is the type holding public key of the asymmetric encryption primitive and ASCipherText
is the type of ciphertexts produced using the asymmetric encryption primitive.
Similarly we update the corresponding transaction
Changes to update_note
Revoking Anonymity
In case the anonymity revocation procedure is triggered on a transaction tx
, the AR proceeds in two steps:
1) Find the key(id)
of the user (account) who created this transaction.
2) Find all transactions created by the user with the key(id)
found in step 1).
We now explain both steps in detail.
Step 1)
Each transaction has a mac=(r,c)
attached to it, which is essentially a symmetric signature of a user, but to recognize the user we must know the associated key. To find out the valid key k
such that
mac = (r, c) = (r, hash(r, k))
the AR can simply collect ALL the keys that have been registered by users in their new_note
transactions, and then find the unique key k
that satisfies hash(r, k) = c
.
Note that (although this is not crucial) there exists exactly one such user id
whose key(id)
satisfies the above — this is because we guarantee that there are no duplicates among id'
s (by using the nullifiers upon creating the account) and key(id)
is a deterministic hash-based procedure to generate a key, hence the collision-free property of the hash function guarantees there are no collisions among key(id)
s either.
Having found the key key(id)
the AR can reveal the key publicly — this way each 3rd party observer will be able to perform step 2). Note that, importantly, given just key(id)
the 3rd party observer can verify that the reveal is correct, because they can simply verify that the mac
is valid. In particular, they don't need access to the AR's key.
Step 2)
The AR, or any interested 3rd party observer, given key(id)
can now find all transactions issued by used id
. The method is very simple:
Loop over ALL transactions sent to the Shielder, and check which of the macs were generated using this particular
key(id)
— these are exactly the transactions we are looking for.
Complete Deanonymization
After performing the above steps 1) and 2) one can recover the complete history of this user's account and in particular recover the current state, and see all the new transactions (that they might send in the future) of the user in the plain.
Simpler variant of Revoking
We note that Shielder in Version 0.0.1 is released with a simpler AR scheme based on PoW. The details are presented in PoW Anonymity Revoking.
Last updated