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.