Authenticated Asymmetric Encryption

Alice would like to securely send a message to Bob. This calls for asymmetric encryption, allowing Alice to encrypt a message only Bob can decrypt. Bob also wants to confirm that the message is really from Alice, which requires authenticated encryption.

Alice begins by importing Panda-Confidential and instantiating the API.

import {confidential} from "panda-confidential"

# Instantiate Panda-Confidential
{EncryptionKeyPair, SharedKey, Message,
  Envelope, encrypt} = confidential()

Obtaining A Shared Key

Since this is asymmetric encryption, Alice will use her private key and Bob’s public key to create a SharedKey, which she’ll use to encrypt the message to Bob. This also means that Bob can be confident someone isn’t trying to impersonate her.

Always Authenticated

Confidential, following the lead of TweetNaCl.js, does not provide an option for unauthenticated asymmetric encryption.

To create a SharedKey, Alice must first obtain her private key and Bob’s public key.

Case 1: Pre-Existing Key Pairs

If Alice already has an EncryptionKeyPair, she may retrieve and deserialize it with the static method from.

alice = EncryptionKeyPair.from "base64", serializedKeyPair

Warning: Encryption key pairs should be stored securely because they contain private keys.

Similarly, she may deserialize Bob’s public key.

bob = publicKey: PublicKey.from "base64", serializedPublicKey

Case 2: New Key Pair

If Alice wants a new key, she can use the static method create.This provides a key-pair suitable for encryption (but not for signing, which requires a SignatureKeyPair).

TweetNaCl.js ensures that Alice’s key is random, providing robust randomness regardless of platform. On some platforms, that’s an asynchronous operation, so Confidential returns a promise to provide a consistent interface.

Alice uses await to wait for the promise to resolve.

alice = await EncryptionKeyPair.create()

Alice should share the resulting public key with Bob and securely store the private key so that she can use them again later.

Creating The Shared Key

Equipped with Bob’s public key and her own private key, Alice can now create the shared key.

key = SharedKey.create alice.privateKey, bob.publicKey

Encrypting

Alice prepares a Message container for the data she wants to encrypt. She can use the static method from, which works the same way as it does for EncryptedKeyPair and PublicKey.

plaintext = Message.from "utf8", "Hello, Bob!"

Alice may now encrypt the Message object. She uses await because encrypt returns a promise.

envelope = await encrypt key, plaintext

Under the hood, Panda-Confidential uses the TweetNaCl.js implementation of asymmetric encryption, which requires a nonce. Confidential will generate one for you if you don’t provide one. encrypt returns an Envelope instance, which includes both the ciphertext and the nonce.

Serializing

Alice uses the to method of an Envelope to serialize the envelope so she can send it to Bob more easily.

string = envelope.to "base64"

Deserializing

Once Bob gets the serialized envelope, he can deserialize it again so that he can decrypt and read the message.

envelope = Envelope.from "base64", string

Decrypting

decrypt and works as simply as encrypt. However, Bob must first create a shared key, just as Alice did. Bob’s shared key will be constructed from his private key and Alice’s public key, the reverse of the way Alice created her shared key.

Thanks to the mathematics underlying public-key cryptography, Bob will still end up with the same shared key, which will allow him to decrypt Alice’s message. That is,

s(a, B) = s(A, b)

where s is a function returning a shared key, A and B are public keys and a and b are the corresponding private keys.

And because the shared key can only have been created with knowledge of either his private key or Alice’s, Bob can be confident that the message is from Alice.

Once Bob has created a shared key from his private key and Alice’s public key, he can decrypt the message.

plaintext = decrypt key, envelope

To read Alice’s message, Bob uses the to method, which works just as it did for the envelope.

# returns 'Hello, Bob!'
message = plaintext.to "utf8"

Bob may respond to the message using the same shared key, effectively creating a secure communication channel between Alice and Bob.