How is it possible for us to communicate securely when there’s the possibility of a third party eavesdropping on us? How can we communicate private secrets through public channels? How do such techniques enable us to bank online and carry out other sensitive transactions on the Internet while trusting numerous relays? In this post, I hope to explain public key cryptography, with actual code examples, so that the concepts are a little more concrete.
First, please check out this excellent video on public key crypto:
Meet Alice. Meet Bob. Meet Eve. Alice would like to send Bob a secret message. Alice would not like Eve to view the message. Assume Eve can intercept, but not tamper with, everything Alice and Bob try to share with each other.
Alice chooses a modular exponential key group, such as modp4, then creates a public and private key.
1 2 3
The public key is meant to be shared; it is ok for Eve to know the public key. The private key must not ever be shared, even with the person communicating to.
Alice then shares her public key and group with Bob.
1 2 3 4
Bob now creates a public and private key pair with the same group as Alice.
Bob shares his public key with Alice.
Alice and Bob now compute a shared secret.
Alice and Bob have now derived a shared secret from each others’ public keys.
Meanwhile, Eve has intercepted Alice and Bob’s public keys and group. Eve tries to compute the same secret.
1 2 3 4 5
This is because Alice’s secret is derived from Alice and Bob’s private keys, which Eve does not have. Eve may not realize her secret is not the same as Alice and Bob’s until later.
That was asymmetric encryption; using different keys. The shared secret may now be used in symmetric encryption; using the same keys.
Alice creates a symmetric block cypher using her favorite algorithm, a hash of their secret as a key, and random bytes as an initialization vector.
1 2 3 4 5
Alice then uses her cypher to encrypt her message to Bob.
Alice then sends the cypher text, cypher, and hash to Bob.
1 2 3 4 5 6
Bob now constructs a symmetric block cypher using the algorithm from Alice, and a hash of their shared secret.
Bob now decyphers the encrypted message (cypher text) from Alice.
Eve has intercepted the cypher text, cypher, hash, and tries to decrypt it.
1 2 3 4 5
Here’s where Eve realizes her secret is not correct.
This prevents passive eavesdropping, but not active man-in-the-middle (MITM) attacks. For example, how does Alice know that the messages she was supposedly receiving from Bob actually came from Bob, not Eve posing as Bob?
Today, we use a system of certificates to provide authentication. This system certainly has its flaws, but it is what we use today. This is more advanced topic that won’t be covered here. Trust is a funny thing.
What’s interesting to note is that the prime and generator used to generate Diffie-Hellman public and private keys have strings that represent the corresponding modular key exponential groups, ie “modp14”. Web crypto’s API gives you finer grain control to specify the generator and large prime number in a Typed Array. I’m not sure why this is; if it allows you to have finer grain control, or allows you to support newer groups before the implementation does? To me, it seems like a source for errors to be made; hopefully someone will make a library to provide these prime/generator pairs.
One issue with my approach is that I assumed that Alice and Bob both had
support for the same hashing algorithms, modular exponential key group, and
symmetric block cypher. In the real world, this is not always the case.
Instead, it is much more common for the client to broadcast publicly all of the
algorithms it supports, and the server to pick one. This list of algorithms is
called a “suite,” ie “cypher suit.” I learned this the hard way recently trying
the cypher suit
on my ssh server and finding out that
my client did not support the lastest cyphers. In this case, Alice and Bob might not have the same
versions of Node.js, which statically link their own versions of OpenSSL. Thus,
one should use
crypto.getHashes() before assuming
the party they’re communicating to can do the math to decrypt. We’ll see “cypher
suites” come up again in TLS handshakes. The NSA
publishes a list of endorsed cryptographic components,
for what it’s worth. There are also neat tricks we can do to prevent the
message from being decrypted at a later time should the private key be
compromised and encrytped message recorded, called Perfect Forward Secrecy.
Let’s take a look now at how a browser does a TLS handshake. Here’s a capture from Wireshark of me navigating to https://google.com. First we have a TLSv1.2 Client Hello to start the handshake. Here we can see a list of the cypher suites.
Next is the response from the server, a TLSv1.2 Server Hello. Here you can see the server has picked a cypher to use.
The server then sends its certificate, which contains a copy of its public key.
Now that we’ve agreed on a cypher suite, the client now sends its public key. The server sets up a session, that way it may abbreviate the handshake in the future. Finally, the client may now start making requests to the server with encrypted application data.
For more information on TLS handshakes, you should read Ilya Grigorik’s High Performance Browser Networking book chapter TLS Handshake, Mozilla OpSec’s fantastic wiki, and this exellent Stack Exchange post. As you might imagine, all of these back and forth trips made during the TLS handshake add latency overhead when compared to unencrypted HTTP requests.
I hope this post helped you understand how we can use cryptography to exchange secret information through public channels. This isn’t enough information to implement a perfectly secure system; end to end security means one single mistake can compromise the entire system. Peer review and open source, battle tested implementations go a long way.
A cryptosystem should be secure even if everything about the system, except the key, is public knowledge.
With upcoming APIs like Service Workers requiring TLS, protocols like HTTP2, pushes for all web traffic to be encrypted, and shitty things governments, politicians, and ISPs do, web developers are going to have to start boning up on their crypto knowledge.
What are your recommendations for correctly learning crypto? Leave me some thoughts in the comments below.