Hey! I'm David, cofounder of zkSecurity and the author of the Real-World Cryptography book. I was previously a crypto architect at O(1) Labs (working on the Mina cryptocurrency), before that I was the security lead for Diem (formerly Libra) at Novi (Facebook), and a security consultant for the Cryptography Services of NCC Group. This is my blog about cryptography and security and other related topics that I find interesting.
I've been thinking a lot about sweet32 recently. And I decided to try to reproduce their results.
First let me tell you that the attack is highly impractical. It requires the user to execute some untrusted javascript for dozens of hours without interruption. The other problem that I encountered was that I couldn't reach the amount of requests they were able to make a client send. In their paper, they claim to be able to reach up to 2,000 requests per second.
I tried to achieve such good numbers with "normal" browsers, and the amount of requests I was able to make was ridiculously low. Then I realized that they used a specific browser: Firefox Developer Edition. A browser made for developing websites. For some unknown reason, it was true that this specific browser was able to send an impressive amount of requests per second. Although I was never able to reach that magical number of 2,000. And even then, who really uses Firefox Developer Edition?
It should be noted that their attack was done in a lab, with a small distance between the client and the server, under perfect condition, when no other traffic was slowing down the attack, etc... I can't imagine this attack being practical at all in real settings.
Note that I can imagine different settings than TLS, at a different point in time in the future, being able to send enough requests per second that this attack would be deemed practical. And in that sense, Sweet32 should be taken seriously. But for now, and especially in the case of TLS, I wouldn't freak out if I see a 64-bit block cipher being used.
A lot of attacks are theorized only to become practical years or decades later. This was the case with Bleichenbacher's and Vaudenay's padding oracle attacks, but also BEAST.
Realizing that Chosen-plaintext attacks were do-able on TLS -- because browsers would execute untrusted code on demand (javascript) -- a myriad of cryptanalysts decided to knock down on TLS.
POODLE was the first vulnerability that made us deprecate SSL 3.0. It broke the protocol in such a critical way that even a RFC was published about it.
BEAST was the one that made us move away from TLS 1.0. But a lot of embedded devices and products still use these lower versions and it would be unfair not to say that an easy patch can be applied to implementations of TLS 1.0 to counter this vulnerability.
BEAST comes from the fact that in TLS 1.0 the next message being encrypted with CBC will use the previous ciphertext's last block as IV. This makes the IV predictable and allow you to decrypt ciphertexts by sending many chosen plaintexts.
the diagram of CBC for encryption taken from wikipedia. Here imagine that the IV is the previous ciphertext's last block.
The counter measures server-side are well known: move to greater versions of TLS. But if the server cannot fix this, one simple counter measure can be applied on the client-side (remember, this is a client-side vulnerability, it allows a MITM attacker to recover session IDs, cookies, etc...).
Again: BEAST works because the MITM attacker can predict the next IV. He can just observe the previous ciphertext block and craft the plaintext based on it. It's an interactive attack.
One way of preventing this is to send an empty message before sending each message. The empty message will produce a ciphertext (of essentially the MAC), which the attacker will not be able to predict. The message that the attacker asked the browser to encrypt will thus be encrypted with this unpredictable IV. The attacked is circumvented.
This counter measure is called a 0/n split.
Unfortunately a lot of servers did not like this countermeasures too much. Chrome pushed that first and kind of broke the web for some users. Adam Langley talks about them paying the price for fixing this "too soon". Presumably this "no data" message would be seen by some implementations as a EOF (End Of File value).
One significant drawback of the current proposed countermeasure (sending empty application data packets) is that the empty packet might be rejected by the TLS peer (see comments #30/#50/others: MSIE does not accept empty fragments, Oracle application server (non-JSSE) cannot accept empty fragments, etc.)
To fix this, Firefox pushed a patch called a 1/n-1 split, where the message to be sent would be split into two messages, the first one containing only 1 byte of the plaintext, and the second one containing the rest.
If you look at a fixed client implementation sending messages over a negotiated TLS 1.0 connection, you will see that first it will send the first byte (in the screenshot below, the "G" letter), and then send the rest in a different TLS message.
If you're curious, you can see that being done in code in the recent BearSSL TLS library of Thomas Porning.
static unsigned char *
cbc_encrypt(br_sslrec_out_cbc_context *cc,
int record_type, unsigned version, void *data, size_t *data_len)
{
unsigned char *buf, *rbuf;
size_t len, blen, plen;
unsigned char tmp[13];
br_hmac_context hc;
buf = data;
len = *data_len;
blen = cc->bc.vtable->block_size;
/*
* If using TLS 1.0, with more than one byte of plaintext, and
* the record is application data, then we need to compute
* a "split". We do not perform the split on other record types
* because it turned out that some existing, deployed
* implementations of SSL/TLS do not tolerate the splitting of
* some message types (in particular the Finished message).
*
* If using TLS 1.1+, then there is an explicit IV. We produce
* that IV by adding an extra initial plaintext block, whose
* value is computed with HMAC over the record sequence number.
*/
if (cc->explicit_IV) {
/*
* We use here the fact that all the HMAC variants we
* support can produce at least 16 bytes, while all the
* block ciphers we support have blocks of no more than
* 16 bytes. Thus, we can always truncate the HMAC output
* down to the block size.
*/
br_enc64be(tmp, cc->seq);
br_hmac_init(&hc, &cc->mac, blen);
br_hmac_update(&hc, tmp, 8);
br_hmac_out(&hc, buf - blen);
rbuf = buf - blen - 5;
} else {
if (len > 1 && record_type == BR_SSL_APPLICATION_DATA) {
/*
* To do the split, we use a recursive invocation;
* since we only give one byte to the inner call,
* the recursion stops there.
*
* We need to compute the exact size of the extra
* record, so that the two resulting records end up
* being sequential in RAM.
*
* We use here the fact that cbc_max_plaintext()
* adjusted the start offset to leave room for the
* initial fragment.
*/
size_t xlen;
rbuf = buf - 4 - ((cc->mac_len + blen + 1) & ~(blen - 1));
rbuf[0] = buf[0];
xlen = 1;
rbuf = cbc_encrypt(cc, record_type, version, rbuf, &xlen);
buf ++;
len --;
} else {
rbuf = buf - 5;
}
}
/*
* Compute MAC.
*/
br_enc64be(tmp, cc->seq ++);
tmp[8] = record_type;
br_enc16be(tmp + 9, version);
br_enc16be(tmp + 11, len);
br_hmac_init(&hc, &cc->mac, cc->mac_len);
br_hmac_update(&hc, tmp, 13);
br_hmac_update(&hc, buf, len);
br_hmac_out(&hc, buf + len);
len += cc->mac_len;
/*
* Add padding.
*/
plen = blen - (len & (blen - 1));
memset(buf + len, (unsigned)plen - 1, plen);
len += plen;
/*
* If an explicit IV is used, the corresponding extra block was
* already put in place earlier; we just have to account for it
* here.
*/
if (cc->explicit_IV) {
buf -= blen;
len += blen;
}
/*
* Encrypt the whole thing. If there is an explicit IV, we also
* encrypt it, which is fine (encryption of a uniformly random
* block is still a uniformly random block).
*/
cc->bc.vtable->run(&cc->bc.vtable, cc->iv, buf, len);
/*
* Add the header and return.
*/
buf[-5] = record_type;
br_enc16be(buf - 4, version);
br_enc16be(buf - 2, len);
*data_len = (size_t)((buf + len) - rbuf);
return rbuf;
}
Note that this does not protect the very first byte we send. Is this an issue? Not for browsers. But the next time you encounter this in a different setting, think about it.
I see some discussions on some mailing lists about what parameters to use for Diffie-Hellman (DH).
It seems like the recent line of papers about weak Diffie-Hellman parameters (Logjam) and Diffie-Hellman backdoors (socat, the RFC 5114, the special primes, ...) has troubled more than one.
This is a non-problem. We don't need a RFC to choose Diffie-Hellman groups. A simple openssl gendh -out keyfile -2 2048 will generate a 2048-bit safe prime along with correct DH parameters for you to use. If you're worried about "special primes" issues, either make it yourself with this command, or pick a larger (let's say 4096-bit safe prime) from a list and verify that it's a safe prime. You can use this tool for that.
But since some people really don't want to do the work, here are some safe parameters you can use.
2048-bit parameters for Diffie-Hellman
Here's is the .pem file containing the parameters:
The Cryptography Services team of NCC Group is looking for a summer 2017 intern!
We are looking for you if you're into cryptography and security! The internship would allow you to follow consultants on the job as well as lead your own research project.
Who are we? We are consultants! Big companies come to us and ask us to hack their stuff (legally), review their code and advise on their design. If we're not doing that, we spend our time reading papers, researching, attending conferences, giving talks and teaching classes, ... whatever floats our boat. Not one week is like the other! If you've spent some time doing cryptopals challenges you will probably like what we are doing.
We can't say much about who are the clients we work for, except for the public audits we sometimes do. For example we've performed public audits for TrueCrypt, OpenSSL, Let's Encrypt, Docker and more recently Zcash.
I was myself the first intern of Cryptography Services and I'd be happy to answer any question you might have =)
I wrote a gist here on certificate validation/creation pitfalls. I don't know if it is up for release but I figured I would get more input, and things to add to it, if I would just released it. So, go check it out and give me your feedback here!
Here's a copy of the current version:
Certificate validation/creation pitfalls
A x509 certificate, and in particular the latest version 3, is the standard for authentication in Public Key Infrastructures (PKIs). Think about Google proving that he's Google before you can communicate with him.
So. Heh. This x509 thing is a tad complicated. Trying to parse such a thing usually end up in the creation of a lot of different vulnerabilities. I won't talk about that here. I will talk about the other complicated thing about them: using them correctly!
So here's a list of pitfalls in the creation of such certificates, but also in the validation and use of them when encountering them in the wild wild web (or in your favorite infrastructure).
explanation: keyUsage is a field inside a x509 v3 certificate that limits the power of the public key inside the certificate. Can you only use it to sign? Or can it be used as part of a key Exchange as well (ECDH)? etc...
best practice: Specify the KeyUsage at creation, verify the keyUsage when encountering the certificate. keyCertSign should be used if the certificate is a CA, keyAgreement should be used if a key exchange can be done with the public key of the certificate.
Validity ::= SEQUENCE {
notBefore Time,
notAfter Time }
best practice: Reject certificates that have a notBefore date posterior to the current date, or that have a notAfter date anterior to the current date.
Critical extensions
explanation: x509 certificate is an evolving standard, exactly like TLS, through extensions. To preserve backward compatibility, not being able to parse an extension is often considered OK, that is unless the extension is considered critical (important).
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING
-- contains the DER encoding of an ASN.1 value
-- corresponding to the extension type identified
-- by extnID
}
best practice: at creation mark every important extensions as critical. At verification make sure to process every critical extensions. If a critical extension is not recognized, the certificate MUST be rejected.
Hostname Validation
explanation:
Knowing who you're talking to is really important. A x509 certificate is tied to a specific domain/organization/email/... if you don't check who it is tied to, you are prone to impersonation attacks. Because of reasons, these things can be seen in different places in the subject field or in the Subject Alternative Name (SAN) extension. For TLS, things are standardized differently and it will always need to be checked in the latter field.
This is one of the trickier issues in this list as hostname validation is protocol specific (as you can see TLS does things differently) and left to the application. To quote OpenSSL:
One common mistake made by users of OpenSSL is to assume that OpenSSL will validate the hostname in the server's certificate
Often, implementations will just check if the subject Name contains the string mywebsite.com, or will use a vulnerable regex that either accept mywebsite.com.evil.com or evil subdomains. Check moxie's presentation (null bytes) to hear more about hostname validation failures.
best practice: During creation, check for the subject as well as the subject alternative name fields. During verification, check that the leaf certificate matches the domain/person you are talking to. If TLS is the protocol being used, check that in the subject alternative name field, only one level of wildcard is allowed and it must be on the leftmost position (*.domain.com is allowed, sub.*.domain.com is forbidden). Consult RFC 6125 for more information.
explanation: the BasicConstraints extension dictates if a certificate is a CA (can sign others) or not. If it is, it also says how many CAs can follow it before a leaf certificate.
best practice: set this field to the relevant value when creating a certificate. When validating a certificate chain, make sure that the pathLen is valid and the cA field is set to TRUE for each non-leaf certificate.
Name Constraints
explanation: the NameConstraints extension contains a set of limitations for CA certificates, on what kind of certificates can follow them in the chain.
best practice: when creating a CA certificate, be aware of the constraints chained certificates should have and document it in the NameConstraints field. When verifying a CA certificate, verify that each certificate in the certificate chain is valid according to the requirements of upper certificates.
You might have heard of KCI attacks on TLS: an attacker gets to install a client certificate on your device and can then impersonate websites to you. I had thought the attack was a highly impractical one, but looking at the video of the attack (done against facebook.com at the time) it seemed to contradict my first instinct.
I skimmed the paper and it answered some of my questions and doubts. So here's a tl;dr of it.
The issue is pretty easy to comprehend once you understand how a Diffie-Hellman key exchange works.
Imagine that you navigate to myCompany.com and you do a key exchange with both of your public keys.
your public key: \(g^a \pmod{n}\)
your company's public key: \(g^b \pmod{n}\)
where \(g\) and \(n\) are public parameters that you agreed on. Let's ignore \(n\) from now on: here, both ends can create the shared secret by doing either \((g^b)^a\) or \((g^a)^b\).
In other words, shared secret = (other dude's public key)(my private key)
If you know either one of them's private key, you can observe the other public key (it's public!) and compute the shared secret. Then you have enough to replace one of the end in the TLS communication.
That's the theory.
If you replace the client's certificate with your own keypair, or know the private key of the certificate, you can break whatever key exchange they try to do with that certificate. What I mean by "break": from a key exchange you can compute the session keys and replace any party during the following communications.
My doubts had originally came from the fact that most implementations rarely use plain Diffie-Hellman, instead they usually offer ephemeral DH or RSA-based key exchanges (which are not vulnerable to this attack). The paper brought me back to reality:
Support for fixed DH client authentication has been very recently added to the OpenSSL 1.0.2 branch.
But then how would you make the client do such a un-used key exchange? The paper washed me from doubts once again:
the attacker, under the hood, interferes with the connection initialization to facebook, and forces the client to use an insecure handshake with client authentication, requesting the previously installed certificate from the system.
Now a couple of questions come to my mind.
How does facebook have these non-ephemeral DH ciphersuites?
→ from the paper, the server doesn't even need to use a static DH ciphersuite. If it has an ECDSA certificate, and didn't specify that it's only to be used to sign then you can use it for the attack (the keyUsage field in the certificate is apparently never used correctly, the few values listed in this RFC tell you how to correctly limit the authorized usages of your public key)
How can you trigger the client to use this ciphersuite?
→ just reply with a serverHello only displaying static ECDH in its ciphersuite list (contrarily to ECDHE, notice the last E for ephemeral). Then show the real ECDSA certificate (the client will interpret that as a ECDH cert because of the fake ciphersuites) and then ask the client for the specific cert you know the private key of.
In addition to the ECDSA → ECDH trumpery, this is all possible because none of these messages are signed at that moment. They are later authenticated via the shared secret, but it is too late =) I don't know if TLS 1.3 is doing a better job in this regard.
It also seems to me that in the attack, instead of installing a client cert you could just install a CA cert and MITM ALL TLS connections (except the pinned ones). But then, this attack is more sneaky, and it's another way of doing exactly this. So I appreciate that.