DEV Community

Cover image for Create your own version of ProtonMail.
Rohit-hooda
Rohit-hooda

Posted on

Create your own version of ProtonMail.

I've been using ProtonMail for quite sometime as my mail provider. If you don't know it, you should definitively give it a look! The mails are encrypted end-to-end, which means that ProtonMail has absolutely no readable version of the stored emails, nor any key to decrypt them.

But there is a caveat to ProtonMail, it doesn't allow us or give the ability to export emails. For example if we want to make back-ups, or move to another provider we get kind of stuck in there. That's why I developed a tool which will give you the magical powers like ProtonMail. To develop this tool we will be using a fantastic library: OpenPGP.js, which is by the way maintained by ProtonMailπŸ˜‰.

If you know asymmetric cryptography principles, I'll just show how OpenPGP.js provides a very easy way to play around with private and public key generation, and of course encrypting/decrypting messages. If you don't know what is asymmetric cryptography & where is it used, this article might be an opportunity to discover what asymmetric cryptography really is with a very simple example. After that you can check this great tutorial regarding Asymmetric Cryptography which might helpful to you.

Generate keys

Let's start with the basics! If you want to send or receive encrypted data to someone, the first thing you'll need is a key, to be more precise a pair of a keys (public & private key). The private key will be used to decrypt data (only you have it, it's very secret🀐), and the public key will be used to encrypt data (you must send it to your friends so that they can send you encrypted data).

The easiest way to put this in practice is to create two different files in the same directory. From one of these files, run the commands npm init -y and npm install openpgp. Then in both files, import the module with:

var openpgp = require('openpgp');

In the below example, we'll imagine that each file represents a person (let's call them Alice and Bob) and these two wants to send encrypted message to each other.

So, let's generate our keys! From Alice's file, run the following:

var options = {
    userIds: [{ name: 'Alice', email: 'alice@example.com' }],
    curve: 'ed25519',
    passphrase: 'secret',
  }
  var publicKey, privateKey;
  openpgp.generateKey(options).then((key) => {
    privateKey = key.privateKeyArmored
    publicKey = key.publicKeyArmored
    console.log(privateKey);
    console.log(publicKey);
  })

The key generation process might take a few seconds. Key generation can be a quite long process sometimes.

So it would be great if the above code is executed asynchronously, so that the main thread can do some other work till the keys are not generated.

Now when the key gets generated you should see the two keys (in base-64), beginning respectively with:
β€œ-----BEGIN PGP PUBLIC KEY BLOCK-----” &
β€œ-----BEGIN PGP PRIVATE KEY BLOCK-----”.

Now in the above code you might be wondering what curve having the value ed25519 is & why do we even need that?

The Curve specifies what is the ECC curve name(😯). ECC stands for Elliptic Curve Cryptography. If you want to learn more about that you can check out an amazing Blog post by Cloud Flare and why they use ECC to secure everything from their customer's HTTPS connections to how they pass data between their data centers.

Encrypt a message

As we discussed earlier, the public key can be sent to everyone to encrypt the data so that they can share it. So, now to encrypt let's copy Alice public key into a variable in Bob's file:

var alicePublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: OpenPGP.js v4.10.8
Comment: https://openpgpjs.org

Zgf7TT22Xa0icFFTHKm0k+MzNvIMIQ
+80dUmljaGFyZCA8cmljaGFyZEBleG

-----END PGP PUBLIC KEY BLOCK-----`;
Note that we use backquotes instead of simple or double quote, because it allows to write strings on multiple lines.

Now that Bob has Alice's public key, Bob wants to send a message to Alice. So let's do it:

(async () => {
var options = {
    message: openpgp.message.fromText('Hello, Alice!'),
    publicKeys: (await openpgp.key.readArmored(alicePublicKey)).keys
}
const { data: encrypted } = await openpgp.encrypt(options);
console.log(encrypted);
})();

You should see the encrypted message, beginning with ”-----BEGIN PGP MESSAGE-----”. That's the data Bob can now send to Alice!

Let's copy this message into Alice's file:

var bobEncryptedMessage = `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js v4.10.8
Comment: https://openpgpjs.org

heLBX8Pq0kUBwQz2iFAzRwOdgTBvH5KsDU9lmE

-----END PGP MESSAGE-----

Decrypt the message

To decrypt Bob's message, all Alice needs is her private key & passphrase(something like password).

Now let's first of all store Alice's private key & passphrase:-

var alicePrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js v4.10.8
Comment: https://openpgpjs.org

xYYEX3Nx8RYJKwYBBAHaRw8BAQdAQyUny
bt14J+kXuzDAc/v3TLnjoq2UxLD
-----END PGP PRIVATE KEY BLOCK-----`;

var passphrase = "secret";

NOTE: The above passphrase should be same as the passphrase which was entered at the time of generation of the keys.

Now let's decrypt the message that bob has sent :

(async () => {
    const { keys: [privateKey] } = await openpgp.key.readArmored(alicePrivateKey);
    await privateKey.decrypt(passphrase);

    var options = {
    message: await openpgp.message.readArmored(bobEncryptedMessage),  
    privateKeys: [privateKey]  
    }
    const { data: decrypted } = await openpgp.decrypt(options);

    console.log(decrypted);
})();

The message which was sent by bob will appear :)

This is it. We have created a barebone ProtonMail for our own usage using OpenPGP. I hope this article might have convinced you it is fairly easy to make your own version ProtonMail.

Further you might use OPENPGP in your own future applications. Just a last thing: OpenPGP.js can be used with Node.js (as we saw it), but also directly client-side in the browser :)

NOTE: I have modified the keys in the above code. The key you generate will be of 64 bits, so use them instead of keys in the above example.

Resources :-
1.OPENPGP.JS
2.Blog post by Cloud Flare
3.What is OpenPGP

Top comments (0)