by Coranos
camo banano consists of three layers of technology:
- blockchain steganography
- storing ecdh public key in the blockchain 3.a. private reversible transactions, using shared seeds. 3.b. private irreversible transactions, using shared public keys.
it is assumed that each party in the transaction has an existing seed with an existing wallet.
the whole purpose of this system is to send funds to the other wallets on a given seed knowing only the first wallet.
so the parties are in control of the funds but do not show up on a rich list.
Since the banano balance has a precision of 29 places after the decimal, you can store 11 bytes of data in the balance field.
since the representative field is only needed for voting, you can use it to store 32 bytes of data.
the ecdh and EdDSA public keys derived from the same private key are not byte-identical.
so the ecdh public key must be stored in the blockchain.
to do this, we split the 32 byte public key into 3 chunks of 11, 11, and 10 bytes and send three transactions to ourselves encoding these bytes in the balance field.
to help is find the data later, we also change the rep to the blake hash of our public key. it is unlikely to be a real rep, and the low vote weight wont matter much anyways.
the ecdh public keys are used to create a shared secret.
the shared secret is then hashed once, so its more uniform.
a new seed is created, never used before. that seed is stored in the blockchain in a simmilar way as the ecdh publick key. first you xor the hashed shared secret with the new seed, then you split the seed into three chunks, then you send transactions to yourself to store the seed in the blockchain. in this case the representative is set to the hash of the shared secret, to help find the data.
once both parties have the shared seed, transactions are 'reversible' because both parties can spend the coins as they both have the seed.
using the same method as reversible transactions, a new wallet is created instead of a new seed.
the new wallet is controlled by only one party, so transactions are not reversible.
that new wallet is stored on the blockchain in almost the same way as the reversible seed. the only difference is that the rep is set to the hash of the hashed shared secret and the public key, to distinguish it from the reversble transaction.
It is possible to use camo to create a mixer, though this is not trustless. Every day the mixer creates a new seed, never used before, and uses it as a hot wallet. You send reversible transactions to the mixer address. The mixer puts one or two transactions into the hot wallet. The mixer sends reversible transactions back to you. The mixer destroys the new seed.
Now assume the mixer is compromised, it's seed is exposed.
- it will be possible to trace who sent inbound transactions to the mixer.
- it will be computationaly intensive to trace outbound transactions, as the hot wallet consolidates all inbound transactions, so as long as there is sufficient volume going through the mixer.
When making a transfer, it is useful to have several fake transfers made as well, so a third party cannot do timing analysis. This would require the sender to make 1-N transfers to himself, as well as the 1 transfer to the reciever with the same amount.
The irreversible is reversible. It's describe as "new wallet is created instead of a new seed.", who create the wallet? If the sender creates the wallet it's reversible.
The X25519 (ECDH over Curve25519) has a public-key of 32 bytes, also the Ed25519 public-key can be used to a X25519. The LibSodium have a fuction for it,
crypto_sign_ed25519_pk_to_curve25519
. It's remove the "storage" of the ECDH key, the Nano uses Ed25519.The shared secret from ECDH is mathematical, not uniform random. In case of X25519 it simply multiplies a point by a scalar on an elliptic curve.
Saying "first you xor the shared secret with the new seed" means that xor will be biased. The xor is acting as a "OTP", however the key is not random and you can't proof that is uniformally random. This is why you MUST derivate the shared key. It can be done by
SharedKey =Blake2(message = SharedKey, key = (Sender_PK || Receiver_PK))
or just plain hash likeSharedKey =Blake2(message = SharedKey, key = null)
. In this case the SharedKey will be a uniform random, if the Blake2 is a secure PRF. This kind of construction is often use to create a encryption key from ECDH (X25519), it's done by LibSodium and done by Nanollet 2FA.Also, instead of XOR you can use a stream-cipher (like Salsa20, ChaCha20), the stream-cipher has the same length of the input.
It's possible to reduce three-blocks to a single block. One block can hold 64 bytes, abusing of the "Representative" (32 byte) and "Link" (32 byte), you can simply store the data using a single block and sent just a 1 RAW.
The main point: The receiver needs a way to get the public-key of the sender, to do the ECDH, seems that it's not cover here.