[Crypto] SSL/TLS, part 2: Toy TLS 1.2 client with TLS_DHE_RSA ciphersuites support.


Not that these ciphersuites are popular nowadays. Everyone shift towards TLS_ECDHE in TLS, and to EC algorithms in general. Only a small fraction of webservers still support TLS_DHE_RSA today.

But I implemented it nevertheless, because this is a stepping stone towards ECDHE.

For more about DH (Diffie-Hellman algorithm), please read before this. And this is how DH is used in SSH.

TLS_RSA ciphersuites has significant problem: if an attacker collect your traffic (maybe for years or decades) and then, by some occassion, he/she can break into your server and steal your RSA private key, he/she will be able to decrypt all your (past) traffic.

Why is that? Premaster secret is randomly generated by client and encrypted for server using his RSA public key. Server can decrypt it using his RSA private key. Now both parties know premaster secret. After stealing server's private key, attacker can get premaster secret, recalculate all other keys and decrypt all traffic.

There is a better solution. DH algorithm is used to create a shared secret that can be a premaster secret. But DH itself is vulnerable to MITM attack (including DH in SSH).

To prevent this, server sends DH parameters (group + Ys (AKA pubkey)) in plain text, not encrypted. But this block is signed by server's RSA private key. Client can verify this signature because he has server's RSA public key from its cert. Then client sends its Yc (AKA pubkey).

Now both parties has DH shared secret, which is used as premaster secret, and other keys are derived from it. DH shared secret is random each time. An attacker can't recover DH shared secret.

This is why cryptographers talk about 'key exchange' -- (public) keys are exchanged between client and server. In 'Ys' and 'Yc', 's' for server and 'c' for client.

Why "RSA" is still in the ciphersuite name? Because it's still used, but differently -- to sign DH block, not because to encrypt random premaster secret.

To connect to my webserver with this ciphersuite, run:

% openssl s_client -cipher "DHE-RSA-AES128-SHA" -no_tls1_3 -connect yurichev.com:443

I patched LibreSSL again, a bit (for a client part). So that it would use exactly this algorithm to sign: rsa_pkcs1_sha256. In ssl_sigalgs.c comment out other 'sigalgs' in the 'Sigalgs for TLSv1.2, in preference order.' section. (Let's leave RSAE and PSS 'sigalgs' for further work.)

This is Wireshark's dump for SSL3_MT_SERVER_KEY_EXCHANGE packet:

Transport Layer Security
    TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
        Content Type: Handshake (22)
        Version: TLS 1.2 (0x0303)
        Length: 783
        Handshake Protocol: Server Key Exchange
            Handshake Type: Server Key Exchange (12)
            Length: 779
            Diffie-Hellman Server Params
                p Length: 256
                p: ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea6…
                g Length: 1
                g: 02
                Pubkey Length: 256
                Pubkey: db0ca8c4e2b164eac442debbef18dcc0f6f50858483d67931bad84a494d90c6b82c17799…
                Signature Algorithm: rsa_pkcs1_sha256 (0x0401)
                    Signature Hash Algorithm Hash: SHA256 (4)
                    Signature Hash Algorithm Signature: RSA (1)
                Signature Length: 256
                Signature: aaadcd3a7183a596b8e7c3520aeff3bdd615a331b733d2ad4246baef30e4c32c57cf2bc3…

Oh, so well-known 'p' value (ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea6...) So called 'Oakley group': rfc2409, rfc3526.

Both p and Pubkey are of 2048 bit size. This is standard today for DH keys.

And this is SSL3_MT_CLIENT_KEY_EXCHANGE from client:

Transport Layer Security
    TLSv1.2 Record Layer: Handshake Protocol: Client Key Exchange
        Content Type: Handshake (22)
        Version: TLS 1.2 (0x0303)
        Length: 262
        Handshake Protocol: Client Key Exchange
            Handshake Type: Client Key Exchange (16)
            Length: 258
            Diffie-Hellman Client Params
                Pubkey Length: 256
                Pubkey: 836885cbf949721cd9c451751d6f7124bcd149e373a9c6151709358f688a80cd2ca87db3…

This is also called 'forward security'. And it's important, because if you're a big fish (of a high-profile politician level maybe), anyone on ISP can record everything goes to/from your smartphone and stash somewhere. Any network engineer or sysadmin in path between smartphone and server. HDDs are big and cheap nowadays.

And grave vulnerabilities surface from time to time (remember catastrophic Heartbleed in 2014). Which may allow for a patient attacker to break into server or your smartphone and steal all the RSA (private) keys. And then decrypt the traffic he/she collected.

But DH can help to prevent this.

But yet again, client must check that signature of DH block. If it wouldn't, it will 'just work', but MITM attack will be possible. I could remove that check from ToyTLS, and it will make HTTP requests and download content, smoothly, without a warning. And this is dangerous.

DH is so important in cryptography because of this. But everything has its downside. DH's problem is MITM attack possibility. SSH protects from this using server fingerprints. TLS protects by RSA key in cert. And again, SSH and TLS shares that method of using DH. If it's proven to be secure and fast, why not?

DH of small size (512 bits) can be cracked on-fly (Logjam attack for TLS).

Download ToyTLS v2 (~1350 SLOC of Python).

Further work: ECDHE. Stay tuned.

(the post first published at 20231022.)

List of my other blog posts.

Subscribe to my news feed

Yes, I know about these lousy Disqus ads. Please use adblocker. I would consider to subscribe to 'pro' version of Disqus if the signal/noise ratio in comments would be good enough.