Skip to content

hmatejx/Interstellar-Whisper

Repository files navigation

Interstellar Whisper

Sending whispers across the interstellar space!

stellar rocket in space

This project holds the development of encrypted messaging over the distributed Stellar network.

$./whisper.py -h
     ___  .   __            .    __       ___         .
 . /  _/___  / /____ *__________/ /____  / / /___ ______ +  /\
   / // __ \/ __/ _ \/ ___/ ___/ __/ _ \/ / / __ `/ ___/  .'  '.
 _/ // / / / /_/  __/ /  (__  ) /_/  __/ / / /_/ / /     /======\
/___/_/ /_____/\___/_/  /____/\__/\___/_/_/\__,_/_/     ;:.  _   ;
| |   . / / /_  (_)________  ___  _____                 |:. (_)  |
| | /| / / __ \/ / ___/ __ \/ _ \/ ___/            +    ;:.      ;
| |/ |/ / / / / (__  ) /_/ /  __/ /                   .' \:.XLM / `.
|__/|__/_/ /_/_/____/ .___/\___/_/     .        .    / .-'':._.'`-. \
                   /_/                               |/    /||\    \|
Usage:
  whisper.py (-r [-n N] | -s MSG -a ADDR) [-k FILE] [-e ENC]
  whisper.py -h | --help
  whisper.py -v | --version

Options:
  -r            Read messages.
  -s MSG        The message text to send.
  -a ADDR       The destination address (required for sending).
  -n N          Read last N messages (optional for reading) [default: 1].
  -k FILE       Path to the file containing the password-protected stellar
                seed for your account [default: ~/.stellar/wallet].
  -e ENC        Required encoding for the message text [default: 0].
                Valid options are:
                  0 = raw (no) encoding,
                  1 = GSM 03.38 encoding,
                  2 = Sixbit ASCII encoding,
                  3 = smaz compression.
  -v --version  Display version and exit.
  -h --help     Show this screen.

Similar projects!

As a shout-out to fellow like-minded cryptonauts (is this even a word?) I'm starting with some links to similar projects that I found about recently:

It's inspiring to see other people to come to similar ideas!

Introduction

Previous attempts at ubiquitous encryption, such as the Web of trust concepts of PGP/GPG, PKI, and DNSSEC have been (and to some extent still are) hampered by a lack of proper incentives. Security just isn't considered a must-have in the eyes of a typical user.

For example, I still remember myself trying to use GPG with some discipline approximately 10 years ago, only to lose sync of the public keys of my contacts, and eventually losing my own keys at some point. I didn't even bother to recover them. I had nothing to loose. My interactions continued un-encrypted. Eventually, even the most enthusiastic of my contacts gave up.

On the other hand, the idea of public-key cryptography is essentially embedded into the concept of cryptocurrencies. In simplified terms, your public key (or sometimes a hash of it, as in the case of Bitcoin) is the address, to which you can send a coin, and your secret key (which you should really never tell anyone) is the ticket that allows you to spend the coins that you own.

One of the benefits of the rise, adoption, and proliferation of cryptocurrencies is that adopters are also necessarily (stake)holders of their cryptographic key pairs. As such, they have a direct monetary incentive to never lose their keys! Considering this, it becomes obvious that these exact same cryptographic keys could be further utilized and leveraged.

In this project I propose using cryptocurrency keys for highly secure, persistent, and resilient messaging. What features do we gain by sending messages over cryptocurrency networks?

  • Security: strong encryption with no possibility of man-in-the-middle attacks.
  • Authenticity: message authenticity is guaranteed by the rules of the network.
  • Resilience: the network is decentralized, no single node to attack, making sending messages difficult to block (the only option is to completely block the client from the network).
  • Persistency: the history is immutable, distributed, and protected by the consensus protocol, which allows retrieving messages at any point in the future.
  • Plausible deniability (optional): some protocols contain optional fields for hashes which are indistinguishable from encrypted messages and the sender himself cannot decode the sent messages if ephemeral key pairs were used.

A break of any of the above would at the same time mean a break of the underlying cryptography, breaking the network itself. As such, cryptocurrencies will continue to evolve towards more secure algorithms when security margin become too low.

Using the distributed Stellar network as a vehicle for transporting messages

From the various cryptocurrencies that I am familiar with, I could not find a better candidate than Stellar (XLM) for this project.

Stellar has one of the best developer ecosystems that I have looked at. There are official SDK libraries available for Java, JavaScript, Ruby and Go with excellent documentation. Further community SDKs for Python, C#, and C++ are also available.

Transactions are also very fast (resolve in 2 - 5 seconds) and cheap (100 stroops ~ 0.000003 USD at the time of writing). Decentralization and the size of the network continue to increase and there is currently no end in sight. But the most important criterion is, in my opinion, the open-mindedness of the Stellar Development Foundation.

Proposed protocol

Prior to messaging, Alice and Bob convert their Ed25519 Elliptic Curve keys, corresponding to Stellar's private seed and public address, to Curve25519 keys which will allow them to perform Elliptic Curve Diffie-Hellman exchange according to the rules of X25519.

Let's call these Curve25519 keys sk_Bob, pk_Bob, sk_Alice, and pk_Alice.

Scenario: Bob sends a message to Alice

Step 1: Diffie-Hellman

Bob calculates the shared secret by using his secret key and Alice's public key using the ECDH algorithm. Alice does the same calculation using her secret key and Bob's public key. Let's call this shared 256-bit secret value k,

k = ECDH(pk_Alice, sk_Bob) = ECDH(pk_Bob, sk_Alice)

Step 2: Message encapsulation

The encapsulation is easiest to describe on an example. Imagine wanting to transmit a 46 byte long payload. The payload itself is a suitably encoded plaintext message or a small file that Bob wants to transmit.

|0                  |10                 |20                 |30                 |40
|0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5 6
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
| Payload                                                                            46 bytes |
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
  1. The first step of encapsulation is to split the payload into fragments of maximum length of 31 bytes, resulting in two fragments of lengths of 31 and 15.

    |0                  |10                 |20                 |30
    |0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5 6 7 8 9|0
    ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
    | Fragment 1                                         31 bytes |
    └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
    ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
    | Fragment 2           15 bytes |
    └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
    
  2. Next, a 1 byte header H is attached to each fragment

    |0                  |10                 |20                 |30
    |0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5 6 7 8 9|0 1
    ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
    |H| Fragment 1                                1 + 31 = 32 bytes |
    └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
    ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
    |H| Fragment 2  1 + 15 = 16 bytes |
    └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
    

The 8 bits of the header depend on the length L of the fragment (encoded in bits 0 - 4) and on the encoding E of the payload (encoded in bits 5 - 7). The fragment length is encoded in the header in the following way:

Length (L) Header bits 0-4 Description
1 ... 31 00001 ... 11111 Last fragment of the payload of size L
0 00000 Full-size fragment, payload continues in the next fragment

The next 3 bits of the header encode the encoding:

Header bits 5-7 Content Description Symbol rate
000 binary no encoding, raw 8-bit 31 B / fragment
001 SMS TEXT GSM 03.38 charset 35 characters / fragment
010 restricted uppercase TEXT Sixbit ASCII 41 characters / fragment
011 TEXT smaz/smac compression ~ 31 - 60 B / fragment
100 RESERVED for future use
101 RESERVED for future use
110 RESERVED for future use
111 RESERVED for future use

For instance, the headers of fragments of the above hypothetical 46 B compression-encoded payload look like this:

  |0 1 2 3 4|5 6 7|
  ┌─┬─┬─┬─┬─┬─┬─┬─┐
  |0 0 0 0 0|0 1 1|    header of Fragment 1
  └─┴─┴─┴─┴─┴─┴─┴─┘
  ┌─┬─┬─┬─┬─┬─┬─┬─┐
  |0 1 1 1 1|0 1 1|    header of Fragment 2
  └─┴─┴─┴─┴─┴─┴─┴─┘

The combination of the header and the fragment will be called a block.

Step 3: Encryption

  • Let's call b_i a (zero-padded, if required) block of the payload.

  • The initiation vector (nonce) is given by the sum of the first 16 bytes of Alice's public key (interpreted as an integer) and the sending accounts' sequence number. Specifically,

      IV = (pk_Alice[0:16] + sequence_number)[-16:]
    

    Here sequence_number is the sequential and increasing number attached to the transaction and incremented in Bob's account after the transaction has been settled. This construction assures that IV is unique and direction-dependent. If Alice sends Bob a message, the IV will be given by the sum of Bob's public key and the sequence_number of Alice's account. Never reusing the same IV is critically important. Failing to do so would catastrophically compromise the encryption (remember both Alice and Bob share the same secret k). With the proposed IV construction, even if Alice sends Bob the exact same plaintext message within a transaction with the exact same sequence_number (the sequence number is only guaranteed to be unique for each Stellar account separately), the IV is guaranteed to be different, unless a collision of the first 16 bytes of the public key occurs which is highly unlikely (~1 in 264 chance).

    The encrypted block will then be

      c_i = AES(b_i, IV, k)
    

    With the construction of the IV we have essentially selected the CTR mode of operation for AES.

  • Optional: It is also possible to chain multiple encryptions (even though I don't think that is necessary, AES should provide plenty of security margin). In this case we need to extend the 256-bit shared secret to more, e.g. 512, bits using a suitable Key Derivation Function

      k1 || k2 = KDF(k)
    

    The first encryption round (using AES-256) is then

      c_i' = AES(b_i, IV, k1)
    

    and the second encryption round (for example, using Twofish)

      c_i = Twofish(c_i', IV, k2)
    

Step 4: Sending the message

The encrypted blocks c_i are set to the 32 byte MEMO_HASH field of the Stellar transaction object. A payment transaction is constructed (e.g. using the 0.0000001 XLM minimum amount). The total cost in this case will be 0.00000101 XLM per fragment (the total cost is the sum of the transaction fee and the payment amount). The transactions for all message fragments are executed sequentially with consecutive sequence numbers.

Step 5: Decryption, Assembly, Extraction and Decoding

Basically, for receiving the message, the corresponding inverse operations of steps 1-4 are performed at the receiving site in the opposite order: the encrypted blocks are decrypted and assembled into the payload (removing the headers and padding), and finally the payload is decoded.

Proof of concept

The application is running on TESTNET at the moment, but that can easily be switched as soon as the security measures for protecting your seed are implemented (see TODO).

Bob (identified by this address GD2TA...2TIY) can send a message to Alice (identified by this address GCU2R...7ZDH):

$./whisper.py -s "Wow! A message through Stellar!" -a GCU2RRJHYBEIP6R6SJHLTCC32FVFGATYMTYB3ZBKT3OMPZLCTVSS7ZDH -k .bob_wallet
    ____  .   __            .    __       ___         .
 . /  _/___  / /____ *__________/ /____  / / /___ ______ +  /\
   / // __ \/ __/ _ \/ ___/ ___/ __/ _ \/ / / __ `/ ___/  .'  '.
 _/ // / / / /_/  __/ /  (__  ) /_/  __/ / / /_/ / /     /======\
/___/_/ /_____/\___/_/  /____/\__/\___/_/_/\__,_/_/     ;:.  _   ;
| |   . / / /_  (_)________  ___  _____                 |:. (_)  |
| | /| / / __ \/ / ___/ __ \/ _ \/ ___/            +    ;:.      ;
| |/ |/ / / / / (__  ) /_/ /  __/ /                   .' \:.XLM / `.
|__/|__/_/ /_/_/____/ .___/\___/_/     .        .    / .-'':._.'`-. \
                   /_/                               |/    /||\    \|
Enter password:

Sending message to GCU2RRJHYBEIP6R6SJHLTCC32FVFGATYMTYB3ZBKT3OMPZLCTVSS7ZDH...  Done.

Alice can indeed read the message.

$./whisper.py -r -n 1 -k .alice_wallet
    ____  .   __            .    __       ___         .
 . /  _/___  / /____ *__________/ /____  / / /___ ______ +  /\
   / // __ \/ __/ _ \/ ___/ ___/ __/ _ \/ / / __ `/ ___/  .'  '.
 _/ // / / / /_/  __/ /  (__  ) /_/  __/ / / /_/ / /     /======\
/___/_/ /_____/\___/_/  /____/\__/\___/_/_/\__,_/_/     ;:.  _   ;
| |   . / / /_  (_)________  ___  _____                 |:. (_)  |
| | /| / / __ \/ / ___/ __ \/ _ \/ ___/            +    ;:.      ;
| |/ |/ / / / / (__  ) /_/ /  __/ /                   .' \:.XLM / `.
|__/|__/_/ /_/_/____/ .___/\___/_/     .        .    / .-'':._.'`-. \
                   /_/                               |/    /||\    \|
Enter password:

Date        From            Message
2018-04-24  GD2TA2JCQTM6…   Wow! A message through Stellar!

Note: If you want to play around in the TESTNET using these two demo accounts, the password of both wallet files is aaaa1111, which is something one can reasonably quickly type during testing.

Requirements

The code is written in Python 3. You need the following python packages (install them with pip):

TODO

  • Implement password protection for the seed file.
  • Implement the defined message encodings.
  • Introduce argument for specifying the cursor from where to start reading messages.
  • Integrate federation address handling.
  • Make the implementation cleaner (refactor).
  • Provide a library, e.g. for integrating into wallet software.

About

Sending whispers across the interstellar space!

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages