How End-To-End Encrypted Messages Work On LocalCryptos
14 mins
LocalCryptos is the only popular P2P marketplace that uses end-to-end encryption to protect messages between users.
Messages between users on the LocalCryptos P2P trading platform are end-to-end encrypted. That means that messages are encrypted in transit from the sender's computer to the recipient's, without allowing anyone in between to intercept and read the conversation in transit, including LocalCryptos.
The platform accomplishes this using the Web Cryptography API, a modern JavaScript API that brings advanced cryptographic operations such as encryption, decryption, signature verification and secure key generation, to web browsers.
Although sending and receiving messages on LocalCryptos might feel the same as any other website, what's going on 'under the hood' on your device's CPU is vastly different to nearly every other website out there.
LocalCryptos is among only a few well-known web applications that implement secure end-to-end encryption technology inside a website, such as WhatsApp Web and Skiff.
Before end-to-end encryption
Revelations about mass communications surveillance and major database breaches have made consumers more cautious of their privacy on the internet.
Prominent online services have been the subject of intrusions by anonymous hackers. Troves of sensitive information have been stolen from companies such as Apple, TikTok, Target, Equifax, Yahoo, eBay, Ancestry.com, AOL, Adobe, LinkedIn, Microsoft, YouTube, Instagram, Quora, Airtel, Ashley Madison, Nintendo, Dropbox, T-Mobile, Uber, the United States Postal Service and more in some of the largest data breaches in recent history.
In addition, it was revealed in 2013 that the U.S. government had been secretly collecting data from internet companies. The NSA’s PRISM program siphoned data from a broad range of companies including Google, Microsoft, Yahoo, Facebook, YouTube, Skype, and numerous consumer upstream providers.
Early instant messaging apps provided hardly any protection from these types of breaches. Although messages were sent over encrypted channels, the channel was usually between the client and a service provider. The service provider, such as Facebook, Skype, or Google, was able to read the contents of all messages delivered through the service. If the provider was breached, histories of plain‐text messages could be extracted in mass.
The emergence of end-to-end encryption
In response to these threats, developers and cryptographers accelerated research into new techniques to provide security in a modern digital world where neither internet upstream providers or service providers can be trusted. Many widespread instant messaging apps now employ advanced cryptographic techniques to achieve end‐to‐end confidentiality, including OTR, Apple’s iMessage, WhatsApp, Signal, Telegram, and even Facebook Messenger.
LocalCryptos applies the techniques of end‐to‐end encryption so that its users' financial details and personal information are kept confidential between the involved parties. Messages between users are end‐to‐end encrypted, meaning that even in the event of a compromised database, no sensitive information such as bank account information could be extracted from historic messages.
The developers and researchers behind the Off‐the‐Record Messaging (OTR) instant messaging protocol inspired the technology behind end-to-end encrypted messages on LocalCryptos.
What's not encrypted on LocalCryptos
Before we delve into the technical details of LocalCryptos' end-to-end encryption implementation, let's discuss what is not made secret.
LocalCryptos cannot read the contents of each message without permission from one of the participants, however it can see some metadata related to those messages. For example, we know when a message was sent, who it was sent to, and we also know if and when a particular message was read.
If you think of end-to-end encrypted messages as letters, LocalCryptos is the mail service that delivers the envelopes containing each letter. Although we can't read the letters, we need to know where to deliver an envelope.
By handling the envelopes, we can also make some educated guesses about a letter without reading it:
- If the envelope feels heavier than normal, we can make an educated guess about how large it is by weighing it. However, this is obfuscated by senders padding the letter with blank pages, so we wouldn't be able to determine the exact word count.
- If the envelope is accompanied by a parcel, it's obvious that you've sent an attachment (e.g. an image or document) rather than a plain letter. Plain messages are usually less than a kilobyte while attachments can be several thousand times larger.
Now, let's discuss what is encrypted and how the end-to-end encrypted messaging protocol on LocalCryptos works.
Secretly agreeing on an encryption key
In order for two users to send encrypted messages back and forth, they must both agree on a shared secret to encrypt and decrypt those messages with. However, they can't allow anyone else to see that shared secret key, so passing that key via LocalCryptos' server as an intermediary is out of the question.
LocalCryptos employs an anonymous key agreement scheme known as an Elliptic Curve Diffie–Hellman Key Exchange (ECDH). This cryptographic scheme allows two people with public key pairs to derive the same shared secret using one person's private key and the other's public key.
For this to work, LocalCryptos acts a key server to distribute cryptographic public keys to users. Users' web browsers also keeps the public keys of other users on their device's storage to protect against a breach of the key server, however we'll leave those details for another article.
First, users must already know the other's public identity keys or fetch them from the LocalCryptos key server. These identity key pairs, which never change, aren't used in the ECDH process; however they are used to authenticate another set of key pairs that are used in the Diffie–Hellman method.
In addition to the public identity keys, users silently generate a large number of cryptographically secure 'pre keys' (also 'maker keys') when they first register on the platform. These are ephemeral public-private key pairs that are digitally signed using the user identity keys, then uploaded to the LocalCryptos key server. The purpose of these 'pre keys' is to enable a totally asynchronous key exchange (i.e. allow users to receive messages while offline).
When a user initiates a conversation, they will generate a third ephemeral key pair on the fly. We call this the 'taker key'. This key pair is the last piece of the puzzle to enable the secure and asynchronous key agreement scheme to work flawlessly.
The step-by-step process for initiating a secure end-to-end encryption protocol is:
- The user initiating the conversation (Alice) grabs the recipient's (Bob's) key information from the LocalCryptos key server. This includes:
- Bob's identity key (public key)
- One of Bob's pre-signed 'pre keys' (public key and digital signature)
- Alice will verify that the digital signature attached to Bob's 'pre key' was signed by Bob's identity key.
- Alice will perform a Diffie–Hellman using the private key of her 'taker key' and the public key of Bob's 'pre key'. The output from this algorithm will become the shared secret (
SharedSecretRoot
). - The
SharedSecretRoot
is used as the seed to calculate more secure keys using SHA‐256 as a key derivation function. As it’s considered a poor practice to use one key for multiple purposes because of potential unwanted interactions in the different schemes, different keys are derived from the root for different purposes:SharedSecretEnc
— For message encryption using AES‐256, the result of SHA256(SharedSecretRoot || "enc")
.SharedSecretMac
— For message authentication using HMAC‐SHA256, the result of SHA256(SharedSecretRoot || "mac")
.
- Alice will provide to Bob via LocalCryptos her public key and attached digital signature (signed with her identity key) of the 'taker key' she generated during this procedure.
When Bob obtains her public key, he'll have enough information to be able to discover the same shared secret Alice had computed.
The Diffie–Hellman method produces the same shared secret when given reversed public and private key inputs. For any two private keys pk
and p’k
and their corresponding ECDSA public keys pu
and p’u
:
The shared secret (SharedSecretRoot
) is the ECDH function using the parameters of one party's private key and the other's public key. For maker key pair mk
and mu
and taker key pair tk
and tu
, the shared secret root sr
is:
This means that when Bob logs in to check his messages:
- Bob will receive Alice's 'taker key' and digital signature, the ciphertext of the encrypted message she sent, and her identity key.
- Bob will verify that the digital signature attached to Alice's 'taker key' was signed using her identity key pair to verify that it truly belongs to her.
- Bob will perform a Diffie–Hellman using the private key of his 'pre key' and the public key of Alice's 'taker key'. This will provide Bob the same
SharedSecretRoot
as Alice had generated. - Using the same key derivation functions, Bob will find
SharedSecretEnc
and SharedSecretMac
and use these to decrypt the encrypted message sent by Alice.
The internal structure of messages
When a user writes a message, it's compiled into a JSON-encoded payload before the encryption process begins. At the time of writing, there are two types of message payloads: plain-text messages and attachments. Messages are simple plain‐text messages and attachments are for larger file uploads, for example images or documents.
A message payload is a simple JSON structure. There are three properties that must be included in every payload regardless of the type:
Standard messages
type
— This can be “message” or “attachment”. Support for other types may be added in the future.trade_id
— The unique identifier of this trade. This is to prevent complex replay attacks (i.e. copying messages from other trades and pretending they were meant for this one.)client_timestamp
— The current timestamp on the sender’s computer (ISO 8601). This is also to detect complex replay attacks and it allows the arbitrator to call out a bad actor if they claim to have sent a message later or earlier than reality.
Here's an example:
{
"type": "message",
"client_timestamp": "2022-01-13T00:00:00Z",
"trade_id": "xxxx-xxxx-xxxx-xxx",
"message": "Hello! This is my encrypted message."
}
Encrypted attachments
Sometimes users want to send each other attachments, for example images and documents.
However, including the attachment contents inside the encrypted message could result in a painful user experience, especially for users with poor internet connections. That would mean that the recipient would need to download the entire attachment (which could be several megabytes) before it can even know that it is an attachment.
Instead, we separate the two: the file is encrypted and uploaded separately, and the encrypted message simply points to the location of the encrypted attachment and instructions on how to decrypt it. This gives the recipient the choice to not download the attachment.
The process of sending an attachment is:
- The sender generates a fresh secret 256-bit key and initialization vector for encrypting the attachment (
AttachmentKey
and AttachmentIv
). - The sender encrypts the contents of the attachment using AES256‐CBC, with
AttachmentKey
and AttachmentIv
. - An SHA‐256 hash of
AttachmentCiphertext
is generated to protect the integrity of the attachment (AttachmentHash
). - The sender uploads
AttachmentCiphertext
to cloud storage and is given a unique identifier (AttachmentBlobKey
). - The sender constructs a message payload containing a link to the attachment and the information necessary to authenticate and decrypt it (
AttachmentBlobKey
, AttachmentKey
, AttachmentIv
, and AttachmentHash
). They also include metadata, including the original file name, extension, and size of the attachment.
Here's an example:
{
"type": "attachment",
"client_timestamp": "2022-01-13T00:00:00Z",
"trade_id": "xxxx-xxxx-xxxx-xxx",
"attachment_blob_key": "AttachmentBlobKey",
"attachment_sha256": "AttachmentHash",
"attachment_iv": "AttachmentIv",
"attachment_key": "AttachmentKey",
"filename": "UploadedFile.jpg",
"filesize": 1000000
}
Encrypting message payloads
These JSON-encoded message payloads are encrypted using the conversation's shared secret, signed using the sender's account identity key, and hashed using the shared MAC key. This enables the other user to verify that the message was sent by the sender and hasn't been tampered with along the way.
Here's how that works:
- The sender creates an ECDSA signature (
MessageSignature
) of the keccak‐256 hash of MessagePayload
. - The sender generates an initialization vector for encryption (
MessageIv
). - A JSON‐encoded package (
MessagePackage
) containing MessagePayload
and MessageSignature
is created. MessagePackage
is encrypted using AES256‐CBC to SharedSecretEnc
with IV MessageIv
and padding (MessageCiphertext
).- To protect the integrity of the message, the sender produces a message authentication code (
MessageMac
) as HMAC‐SHA256(SharedSecretMac, MessageCiphertext)
. - The sender submits
MessageCiphertext
, MessageIv
, and MessageMac
to the API, which delivers it to the recipient.
Questions
Won't arbitration defeat the purpose of end-to-end encryption?
There is no such thing as an encrypted messaging protocol that can make it impossible for the recipient of a message to share it with somebody else.
You can't prevent the person you're sending a message to from sharing your message, just as you can't stop someone from taking a screenshot. If somebody decides to share their conversation, including with us, they can do so.
When we need to investigate claims of fraud or abuse, or deal with payment disputes, we encourage users to share the contents of conversations with us.
Sharing the shared secret of a conversation with us allows us to go back and decrypt the transcript for that particular conversation, however it doesn't impact the privacy of your other conversations. This is because each trade uses a unique set of key pairs to derive that shared secret, making shared secrets also unique.
What are the limitations of this system?
We just told you how messages are secure on LocalCryptos. In the spirit of transparency, let's also discuss how they could be insecure.
There are some experts who advocate against building web-based encryption applications that are designed to respect the user's interests rather than the web provider's, which is exactly what we've done. While we ultimately disagree with this blanket recommendation because we believe the benefits outweigh the risks and web-based security will improve over time, the arguments against using the Web Cryptography API in websites are not without merit.
The primary argument against using cryptography within web browsers is that web-applications are ultimately less secure than programs and browser extensions when it comes to delivering application code. This is due to the way web browsing works: when you visit this site or any other, your web browser will fetch LocalCryptos' latest application code directly from LocalCryptos' web server and run it—without doing much to authenticate that the code is truly the official LocalCryptos code and hasn't been modified.
You might think that using HTTPS solves this authentication problem, making it so that LocalCryptos can digitally sign the code it delivers, and you are partially correct, however only to an certain extent. While HTTPS practically eliminates the vast majority of theoretical man-in-the-middle attacks against web users, it doesn't solve for the threat that the LocalCryptos web server could itself be compromised. An intelligent hacker could break into LocalCryptos' web servers and carefully modify one of the JavaScript files delivered to end-users, perhaps to insert a few lines of code to siphon private keys.
LocalCryptos does a lot to minimize that risk, utilizing all the HTTP, DNS, and BGP security mechanisms available, and taking security extremely seriously. However, it's not possible to totally eliminate that threat. The same can be said about literally every other website and web application out there; there is always the possibility of any website or application being vulnerable to a highly sophisticated attack. At the end of the day, cybersecurity is always a sliding scale that requires a nuanced and honest discussion, and anyone who tells you that a certain piece of software can't be hacked is always wrong.
So what would happen to the privacy of your sent and received private messages if the LocalCryptos website suffered a major hack? It really depends. If your web browser executes maliciously poisoned code injected by a sophisticated hacker, there is a possibility that they could gain access to your private key material and decrypt your conversation history. However, if your web browser doesn't execute that hypothetically poisoned code, then your secret conversations would remain secret regardless of the website's breach. In other words, if you visit the site and log in during an active attack, you could be at risk; however, if you're not actively visiting the website at the time of the attack you wouldn't be at any risk. That's because your web browser only executes a website's code when you intentionally navigate to it.
By comparison, if a centralized messaging service was hacked, everyone would be at risk instantaneously. The attacker would be able to download the plain-text of every sent and received message, because every message would simply be stored in a database in plain-text. The fallout from an attack on an unencrypted messaging service would be far more devastating than an attack on an end-to-end encrypted web application.
In the end, we believe the security benefits of using end-to-end encryption technology on the client-side greatly outweigh the risk of a targeted and sophisticated attack, and that the security of application delivery in web-applications will improve over time as new security technologies become available to modern browsers.
Why is there no Double Ratchet?
While some implementations of end-to-end encrypted messaging such as Signal take key renewals a step further with a Diffie–Hellman agreement after each message roundtrip, that practice doesn't benefit LocalCryptos and is actually counterproductive since it complicates the dispute resolution process.
Message‐level key renewals are an overkill for trades since the sessions don’t last for long. It makes sense to keep exchanging new keys for long‐lived conversations (e.g. WhatsApp or Facebook conversations that can go on for years), but most trades are over very quickly.
How can I verify that my messages are being encrypted?
If you enable 'Expert Mode' during a conversation, you can click on each message to reveal the low-level details about each message.
These details include everything we have discussed in the article. However, you will need to have a good understanding of applied cryptography to be able to understand the meaning and significance of these values.
You can click on each value to copy it to your clipboard and manually verify that the math checks out — using any third-party code or tool you choose, or you can write your own.
You can further check that this is the only information being submitted to LocalCryptos by using your browser's Developer Tools.