Start now →

How Two Machines That Have Never Met Agree on a Secret: TLS and the Protocol Behind Every Secure…

By Mikayel Dadayan · Published May 29, 2026 · 14 min read · Source: Level Up Coding
EthereumRegulationPayments
How Two Machines That Have Never Met Agree on a Secret: TLS and the Protocol Behind Every Secure…

How Two Machines That Have Never Met Agree on a Secret: TLS and the Protocol Behind Every Secure Request

Every time you send something sensitive over the internet, it travels through hardware you have never seen, owned by companies you have never heard of, passing through countries you may not even know about. Routers, ISPs, backbone networks, cloud gateways. A dozen strangers between you and your destination.

And yet somehow, none of them can read it.

That is not luck. That is TLS, and the reason it works is one of the most elegant ideas in modern engineering. It is a protocol that lets two machines that have never spoken before agree on a shared secret, over a completely public network, in plain sight of anyone watching, without that secret ever traveling across the wire.

Most engineers know TLS as “the thing that makes the padlock appear.” This article is about what is actually happening underneath: the threat it was built to solve, the exact sequence of messages that run before your data moves, and the mathematics that makes it unbreakable.

First, let’s understand the problem

Before we talk about any solution, we need to feel the problem properly. Because TLS is not just “encryption.” It is solving something more subtle than that.

When your client sends data to a server, think about what actually happens physically. The request does not teleport. It hops through routers, crosses ISP infrastructure, travels through backbone networks, and eventually reaches the server. None of that infrastructure belongs to you. You have zero control over it, zero visibility into it.

And here is the uncomfortable part: every single hop along that path is a machine that, in theory, can see your traffic. If you are sending plain text, a router somewhere in the middle could read it, change a value before forwarding it on, or pretend to be your server and collect whatever you send.

This is called a man-in-the-middle attack, and it is not some exotic hacking scenario. On an open Wi-Fi network, someone at the next table with a laptop and freely available software can intercept everything you send over plain HTTP. The attacker does not need special skills. They just need to be somewhere between you and the server.

Without any protection, that router sees the raw payload. It can read it, log it silently, alter a field and pass it on, or respond itself while pretending to be the server. Your client has no way to detect that any of this happened. With TLS in place, the same router sees only encrypted bytes it cannot read or usefully modify. The network did not become safe. TLS just made it irrelevant.

So the problem is real. Now let’s think about what we actually need to fix it.

What we need

If you think about it carefully, there are three separate things you need to guarantee before you can safely send anything sensitive.

You need to know you are actually talking to the right server, not an imposter. You need the data to be unreadable to anything sitting between you and the server. And you need to know the data arrives exactly as you sent it, with any modification detectable on arrival.

Remove any one of these and the others fall apart. Encrypting data for an imposter is worse than useless. Authenticating a server but sending everything in plain text solves nothing. All three have to hold at once.

Now here is the real challenge: both machines need to establish all three guarantees between themselves, over a network that might be hostile, having never communicated before, with no secret shared in advance. That is the core problem TLS exists to solve.

How TLS solves it

TLS does not solve everything at once. It works in three phases, one building on the previous, and each phase solves exactly one of the problems we named.

The first phase establishes identity: is this actually the right server? The second phase establishes a shared secret: how do we agree on an encryption key without sending it over the wire? The third phase puts that key to work: encrypt everything, fast, and verify every message.

Let’s walk through each one.

Step 1: Proving the server is really who it says it is

When your browser connects to api.example.com, the server says "yes, I am api.example.com." But any server can say that. You need a way to verify the claim without trusting the server to tell the truth about itself.

The solution is to involve a third party that both sides already trust. This is what a Certificate Authority does.

A CA, such as DigiCert, Let’s Encrypt, or GlobalSign, is in the business of verifying that a particular public key genuinely belongs to a particular domain. When a server operator wants a TLS certificate, they generate a key pair, keep the private key on their server, and submit the public key together with their domain name to a CA. The CA then issues a challenge: place this specific file at this URL, or add this record to your DNS. If the operator can do it, they demonstrably control the domain. The CA is satisfied. It issues a signed certificate: a document that binds the domain name to the public key and stamps that binding with the CA’s own cryptographic signature.

Think of it like a notarized document. The CA is saying: “I have personally verified that whoever controls this domain presented this public key. I am signing my name to that.”

Now, here is the clever part. Your browser does not need to call DigiCert at connection time to verify the signature. It already has DigiCert’s public key stored locally.

Every browser and operating system ships with a pre-installed list of trusted CA public keys, called the root store. Before you open a single connection, your device already holds something like this:

Trusted CA public keys (pre-installed):
Let's Encrypt ISRG Root X1
DigiCert Global Root G2
GlobalSign Root CA
... ~150 more

These are baked into Chrome, Firefox, Safari, Windows, macOS, Linux, iOS, and Android.

The important mental shift here: the browser trusts CA public keys, not server public keys. It has no idea what api.example.com's public key is before connecting. That key gets introduced during the handshake, vouched for by a CA the browser already trusts.

So when the server sends its certificate, the browser does one thing:

verify(certificate.signature, CA_public_key)

If that passes, one fact is established: this public key belongs to api.example.com. Not because the server said so. Because a CA the browser already trusts signed that statement.

No network call. No CA contact. The whole check is local math, done in milliseconds. The CA did its job when it issued the certificate. At connection time it is completely out of the picture.

One last thing worth knowing: the certificate itself is entirely public. Anyone can download it. Run openssl s_client -connect google.com:443 and Google's certificate prints in full. That is by design. The public key is meant to be public. Only the private key must stay secret, and that never leaves the server.

The handshake: what the actual messages look like

Before the first message is sent, the state between client and server is:

encryption:   none
trust: none
shared key: none

Client Hello

The client sends the first message. It contains:

At this point there is still no trust and no shared key. The client is simply introducing itself and starting the negotiation.

Server Hello

The server responds with:

This is the first moment the browser sees the server’s public key. It arrives inside the certificate, alongside the CA signature that proves it belongs to this domain.

Browser verifies

The browser now checks the certificate using the CA public key it already has locally:

verify(certificate.signature, CA_public_key)  →  valid
domain matches? → yes
certificate expired? → no

Identity confirmed.

Why an attacker cannot fake this

An attacker could intercept the handshake and send their own FAKE_PUBLIC_KEY. But they cannot produce a valid CA signature over it. Only the CA holds the private key needed to create that signature. The browser's verification fails, and the connection is killed before anything sensitive is exchanged.

The trust model in one sentence: “I do not know you, but I trust DigiCert, and DigiCert says you are api.example.com."

Finished

Both sides derive session keys from the shared secret, and each sends a Finished message: a hash over the entire handshake transcript. This confirms that nothing was tampered with in any of the preceding messages.

Application data then starts flowing encrypted. The entire exchange in TLS 1.3 completes in one round trip. TLS 1.2 required two.

Step 2: Agreeing on a secret key without ever sending it

The handshake showed us that both sides exchanged public keys A and B. But how do two public values produce a shared secret that an attacker cannot derive? This is the part that feels like a magic trick when you first encounter it.

The answer is Diffie-Hellman key exchange, and the insight behind it is genuinely beautiful.

Both sides start by agreeing on two numbers that are completely public. Anyone can see them, including an attacker. Call them g (a small number, like 2 or 5) and p (a very large prime). These are just the starting parameters, and their being public is fine.

Now each side picks a private number that they never share with anyone. The client picks a. The server picks b. Each one then does a calculation using their private number and the public parameters:

The attacker on the wire now has g, p, A, and B. But not a or b.

Here comes the magic. The client takes B and raises it to the power of its own private a:

B^a mod p  =  (g^b mod p)^a mod p

There is a rule in modular arithmetic that the inner mod p is redundant when there is already a mod p on the outside. It simplifies cleanly:

(g^b mod p)^a mod p  =  g^(b*a) mod p

The server does the same thing in the other direction, taking A and raising it to its own private b:

A^b mod p  =  g^(a*b) mod p

And here is the key observation: b*a and a*b are the same number. So both sides computed the same result. They both hold g^(ab) mod p. Neither side transmitted it. It was never on the wire.

Let’s run the actual numbers so this feels real. Both sides agree on g = 5 and p = 23.

The client picks a = 6. Computes 5^6 mod 23 = 8. Sends 8.

The server picks b = 15. Computes 5^15 mod 23 = 19. Sends 19.

The attacker sees: 5, 23, 8, 19. That is everything that crossed the wire.

The client takes B = 19 and computes 19^6 mod 23 = 2.

The server takes A = 8 and computes 8^15 mod 23 = 2.

Both got 2. The shared secret is 2. The attacker has no idea.

You might be thinking: with these small numbers, could the attacker not just try every possible value of a until they find one where 5^a mod 23 = 8? Yes, they could. It would take a fraction of a second.

But TLS does not use p = 23. It uses primes that are 2048 bits long. Written out in decimal, that is roughly 617 digits. The number of possible values for a is around 2 to the power of 2048. To recover a from A, you have to solve what mathematicians call the discrete logarithm problem: given g, p, and A, find a. Computing A from a is fast. Finding a from A, going in reverse, has no known efficient algorithm for large primes. The best-known classical attack, the number field sieve, is so computationally expensive that every computer on Earth running in parallel for longer than the age of the universe would not crack it.

Step 3: Encrypting everything with the shared secret

With a shared secret that both sides hold and nobody else can derive, the expensive work is done. TLS now uses that secret to derive actual encryption keys through a key derivation function called HKDF, and from here, everything is encrypted with AES-GCM.

AES-GCM is a symmetric cipher, meaning both sides use the same key. It is orders of magnitude faster than all the asymmetric math we just did, which is exactly why the handshake exists: do the expensive work once, agree on a key, then use the fast cipher for everything that follows.

There is one more thing AES-GCM gives you. It attaches an authentication tag to every single message it encrypts. If even one bit of a message is changed in transit, the tag fails when the receiver checks it and the message is rejected entirely. This is where the integrity guarantee comes from. Not policy, not trust. Math.

One last detail that matters more than it seems: TLS 1.3 generates fresh Diffie-Hellman key pairs for every session. So even if someone compromises your server’s long-term private key years from now, they still cannot decrypt past sessions. The ephemeral keys that produced those session secrets are gone. This is called forward secrecy, and it is why the “E” in ECDHE matters.

Stepping back

If you have followed all of this, you now understand something that most engineers treat as a black box. Here is the full picture from start to finish.

None of this required the two machines to have ever met before. The trust was bootstrapped from a key your browser already held. The secret was established from math that an eavesdropper cannot reverse. And the cipher makes tampering detectable on every single message.


How Two Machines That Have Never Met Agree on a Secret: TLS and the Protocol Behind Every Secure… was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.

Looking for a crypto payment gateway?

NexaPay lets merchants accept card payments and receive crypto. No KYC required. Instant settlement via Visa, Mastercard, Apple Pay, and Google Pay.

Learn More →
This article was originally published on Level Up Coding and is republished here under RSS syndication for informational purposes. All rights and intellectual property remain with the original author. If you are the author and wish to have this article removed, please contact us at [email protected].

NexaPay — Accept Card Payments, Receive Crypto

No KYC · Instant Settlement · Visa, Mastercard, Apple Pay, Google Pay

Get Started →