david wong

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.

About Sweet32 posted November 2016

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.

1 comment

1/n-1 split to circumvent BEAST posted November 2016

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.

cbc encryption

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.

diagram 0 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.

cbc split

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.

cbc split burp

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.

1 comment

What Diffie-Hellman parameters to use? posted October 2016

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.

Less than two weeks ago, a study from Dorey et al. based on these previous results was released, uncovering many problems in how Diffie-Hellman is implemented (or even backdoored!) in the wild.

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:

-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA7WJTTl5HMXOi8+kEeze7ftMRbIiX+P7tLkmwci30S+P6xc6wG1p4
SwbpPyewFlyasdL2Dd8PkhYFtE1xD3Ssj1De+P8T0UcJn5rCHn+g2+0k/CalysKT
XrobEzihlSLeQO1NsgBt1F1XCMO+6inLVvSGVbb3Cei4q+5Djnc7Yjjq0kxGY6Hd
ds/YQnyc1xdJU8NBi3zO1XY2Uk6BSd+NN5KnLh9zRq8t/b0RiIb/fY9mJ9BCtgPo
2m4AfJE8+5dE1ttpQAJFSlA8Ku3/9Vp8sMMWATVk2Q1z9PdkikKQYRfMPYDBSIa/
8Y2l9Hh7vNYOwXd4WF5Q55RHP46RB+F+swIBAg==
-----END DH PARAMETERS-----

You can parse it yourself by piping it to openssl dh -noout -text. It uses 2 as a generator and this big hexstring as a safe prime:

ed62534e5e473173a2f3e9047b37bb7ed3116c8897f8feed2e49b0722df44be3fac5ceb01b5a784b06e93f27b0165c9ab1d2f60ddf0f921605b44d710f74ac8f50def8ff13d147099f9ac21e7fa0dbed24fc26a5cac2935eba1b1338a19522de40ed4db2006dd45d5708c3beea29cb56f48655b6f709e8b8abee438e773b6238ead24c4663a1dd76cfd8427c9cd7174953c3418b7cced57636524e8149df8d3792a72e1f7346af2dfdbd118886ff7d8f6627d042b603e8da6e007c913cfb9744d6db694002454a503c2aedfff55a7cb0c316013564d90d73f4f7648a42906117cc3d80c14886bff18da5f4787bbcd60ec17778585e50e794473f8e9107e1

4096-bit parameters for Diffie-Hellman

Here's the .pem file:

-----BEGIN DH PARAMETERS-----
MIICCAKCAgEA66H+vEJiWaduRv/wwBhPkW1ivReF8yaq2W7166jcw62i069pkXlK
EeLtSDtGKiHu2YrjN03uf+q2urPhG+KXCMBgONlz7eGfPAD/xVSqK53jT8DFgqo6
Y1BccaJeU1iVy3GAVG1xS0aVPqyiNFDGpfd2WSuPIAi3tlgfD0Iu0sCBDLNcuA0d
4pCqBqEKOunD/vDcxp5jnVcj+36fozIkSQ3M2AzG6r44Qb0LFRrAuBM7QKs13COt
oGZwAJE/9GdfNC0jQsScrIVYV0MATv81mbFDAE6e0rLVqMeCdIY7gH0A0qWUVA4S
I3Mr5iPTYxFlA+vWuBPdZ1OXiQN5pzx0TTdnfkI6Q23rOeJG5eIa/LIZ/B+0OlqF
W8UwJL1eZoQGOtx9Al285OIiPktH0fJcpkfbMUmBG9r7xYuCw9vUQ1ed+BIQ366+
9mDTKjTOtmw7HahV48W/T8OPSoSFe/QgnX0VB7c6pnWZ2d4WFu2jVEeG3/pzqMM0
/VCFPXM6Zc0iat5V9qcn2OnOhaSjItuGEc75+8OIeEcdhAUq+PgkKoUIwCNv65RA
A17Skfgi3CLeLy5nKRd7nNv13PSXpjOkNYvRjjatEHZY9DTlfV0vqAlTZNjAt/yx
yNunxd5Sg1kxD1dZCkJXhAbcR8bPSU6OgjSDvGvRk+Dv3Qv5MtTWYisCAQI=
-----END DH PARAMETERS-----

And here is the hexstring value of the safe prime (note that it still uses 2 as a generator):

eba1febc426259a76e46fff0c0184f916d62bd1785f326aad96ef5eba8dcc3ada2d3af6991794a11e2ed483b462a21eed98ae3374dee7feab6bab3e11be29708c06038d973ede19f3c00ffc554aa2b9de34fc0c582aa3a63505c71a25e535895cb7180546d714b46953eaca23450c6a5f776592b8f2008b7b6581f0f422ed2c0810cb35cb80d1de290aa06a10a3ae9c3fef0dcc69e639d5723fb7e9fa33224490dccd80cc6eabe3841bd0b151ac0b8133b40ab35dc23ada0667000913ff4675f342d2342c49cac85585743004eff3599b143004e9ed2b2d5a8c78274863b807d00d2a594540e1223732be623d363116503ebd6b813dd675397890379a73c744d37677e423a436deb39e246e5e21afcb219fc1fb43a5a855bc53024bd5e6684063adc7d025dbce4e2223e4b47d1f25ca647db3149811bdafbc58b82c3dbd443579df81210dfaebef660d32a34ceb66c3b1da855e3c5bf4fc38f4a84857bf4209d7d1507b73aa67599d9de1616eda3544786dffa73a8c334fd50853d733a65cd226ade55f6a727d8e9ce85a4a322db8611cef9fbc38878471d84052af8f8242a8508c0236feb9440035ed291f822dc22de2f2e6729177b9cdbf5dcf497a633a4358bd18e36ad107658f434e57d5d2fa8095364d8c0b7fcb1c8dba7c5de528359310f57590a42578406dc47c6cf494e8e823483bc6bd193e0efdd0bf932d4d6622b
4 comments

Looking for a crypto intern at Cryptography Services! posted October 2016

internship

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 =)

Take a look at the challenges here if you're interested!

4 comments

Common x509 certificate validation/creation pitfalls posted September 2016

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).

  1. KeyUsage
  2. Validity Dates
  3. Critical Extensions
  4. Hostname Validation
  5. Basic Constraints
  6. Name Constraint

KeyUsage

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...

relevant RFC: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile

relevant ASN.1:

KeyUsage ::= BIT STRING {
           digitalSignature        (0),
           nonRepudiation          (1),
           keyEncipherment         (2),
           dataEncipherment        (3),
           keyAgreement            (4),
           keyCertSign             (5),
           cRLSign                 (6),
           encipherOnly            (7),
           decipherOnly            (8) }

seen in attacks: KCI

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.

see more: Extended Key Usage

Validity Dates

explanation: a certificate is only to be valid in a specific interval of time. This interval is specified by the notBefore and notAfter fields.

relevant RFC: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile

relevant ASN.1:

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).

relevant RFC: https://tools.ietf.org/html/rfc5280#section-4.2

relevant ASN.1:

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

relevant RFC:

relevant ASN.1:

TBSCertificate  ::=  SEQUENCE  {
    version         [0]  EXPLICIT Version DEFAULT v1,
    serialNumber         CertificateSerialNumber,
    signature            AlgorithmIdentifier,
    issuer               Name,
    validity             Validity,
    subject              Name,

seen in attacks:

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.

see more:

Basic Constraints

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.

relevant RFC: https://tools.ietf.org/html/rfc5280#section-4.2.1.9

relevant ASN.1:

id-ce-basicConstraints OBJECT IDENTIFIER ::=  { id-ce 19 }

BasicConstraints ::= SEQUENCE {
    cA                      BOOLEAN DEFAULT FALSE,
    pathLenConstraint       INTEGER (0..MAX) OPTIONAL }

seen in attacks: Moxie - New Tricks For Defeating SSL In Practice (2009)

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.

relevant RFC: https://tools.ietf.org/html/rfc5280#section-4.2.1.10

relevant ASN.1:

id-ce-nameConstraints OBJECT IDENTIFIER ::=  { id-ce 30 }

NameConstraints ::= SEQUENCE {
     permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
     excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }

GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree

GeneralSubtree ::= SEQUENCE {
     base                    GeneralName,
     minimum         [0]     BaseDistance DEFAULT 0,
     maximum         [1]     BaseDistance OPTIONAL }

BaseDistance ::= INTEGER (0..MAX)

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.

Out of scope

  • Certificate Chain Validation
  • Certificate Revocation

Acknowledgements

Thanks to Chris Palmer, Vincent Lynch and Jeff Jarmoc

comment on this story

Key Compromise Impersonation attacks (KCI) posted September 2016

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.

1 comment