sftcd / openssl

TLS/SSL and crypto library
https://www.openssl.org
Apache License 2.0
46 stars 20 forks source link

I have some question about ESNI #8

Closed pol4bear closed 4 years ago

pol4bear commented 4 years ago

Hi, I'm trying to decrypt encrypted sni using SSL_ESNI_dec function in this library. I successfully decrypt ESNI connection via this library. I know that firefox also supports ESNI draft 01 connection so I tried to decrypt ESNI connection via firefox but decrypt fails. Do you have any idea why decrypting ESNI connection via this library is successful otherwise via firefox fails?

sftcd commented 4 years ago

Hiya,

On 13/11/2019 16:24, Hyunhong Kim wrote:

Hi, I'm trying to decrypt encrypted sni using SSL_ESNI_dec function in this library. I successfully decrypt ESNI connection via this library. I know that firefox also supports ESNI draft 01 connection

Actually, IIUC, firefox implements draft-02 but that probably doesn't matter much for this question.

so I tried to decrypt ESNI connection via firefox but decrypt fails. Do you have any idea why decrypting ESNI connection via this library is successful otherwise via firefox fails?

Sorry, I'm not quite getting your question.

SSL_ESNI_dec is a library internal function called on the server side (e.g. from within nginx, lighttpd or s_server) when a server has some ESNI private keys configured and receives a TLS ClientHello containing a matching ESNI extension.

Can you provide a bit more context as to where your code is running and what it's trying to do?

Cheers, S.

PS; BTW, when I say "library internal" there I really mean that it will be internal - I've not yet tried to hide those functions from applications. That's on my TODO list for later.

pol4bear commented 4 years ago

Thank you for nice feedback.

This is my code that decrypts encrypted SNI. First, I created ESNI key and version FF01 custom record via mk_esnikeys in esnistuff. And then, I added record above to my own DoH server's zone file(xxx.com) like _esni IN TXT [custom record].

I captured packet ESNI connection to xxx.com using this library's s_client and try to decrypt code below. The decryption was successful. I captured ESNI connection to xxx.com using firefox(which request record to my own DoH server) and try to decrypt code below. The decryption fails.

Firefox and openssl s_client uses same record(my custom record) but one can decrypted with private key and the other doesn't.

using BIO_ptr = std::unique_ptr<BIO, decltype(&BIO_free)>;
using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>;
using CLIENT_ESNI_ptr = std::unique_ptr<CLIENT_ESNI>;
//using SSL_ESNI_ptr = std::unique_ptr<SSL_ESNI, decltype(&SSL_ESNI_free)>;
using SSL_ESNI_ptr = std::unique_ptr<SSL_ESNI>;

typedef struct KeyShareEntry {
    uint16_t group;
    std::string key;
} KeyShareEntry;

typedef struct ExtensionESN {
    uint16_t suite;
    KeyShareEntry key_share;
    std::string record_digest;
    std::string encrypted_sni;
} ExtensionESN;

uint8_t *constCharPtrToUcharPtr(const char* input) {
    return const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(input));
}

std::string toEncodedKeyShare(KeyShareEntry key_share)
{
    std::stringstream encoded_key_share;
    uint16_t encoded_key_share_length = key_share.key.length() + 4;

    encoded_key_share << char(encoded_key_share_length / 256)
                      << char(encoded_key_share_length % 256)
                      << char(key_share.group / 256)
                      << char(key_share.group % 256)
                      << char(key_share.key.length() / 256)
                      << char(key_share.key.length() % 256)
                      << key_share.key;

    return encoded_key_share.str();
}

std::string decryptEsni(ExtensionESN esni, std::string random, KeyShareEntry key_share, const EVP_PKEY_ptr &private_key, uint16_t version)
{
    //SSL_ESNI_ptr ssl_esni(static_cast<SSL_ESNI*>(calloc(1, sizeof(SSL_ESNI))), SSL_ESNI_free);
    SSL_ESNI_ptr ssl_esni(static_cast<SSL_ESNI*>(calloc(1, sizeof(SSL_ESNI))));
    CLIENT_ESNI_ptr client_esni(static_cast<CLIENT_ESNI*>(calloc(1, sizeof(CLIENT_ESNI))));
    std::string encoded_key_share1, encoded_key_share2;

    client_esni->ciphersuite = esni.suite;
    encoded_key_share1 = toEncodedKeyShare(esni.key_share);
    client_esni->encoded_keyshare = constCharPtrToUcharPtr(encoded_key_share1.c_str());
    client_esni->encoded_keyshare_len = encoded_key_share1.length();
    client_esni->record_digest = constCharPtrToUcharPtr(esni.record_digest.c_str());
    client_esni->record_digest_len = esni.record_digest.length();
    client_esni->encrypted_sni = constCharPtrToUcharPtr(esni.encrypted_sni.c_str());
    client_esni->encrypted_sni_len = esni.encrypted_sni.length();

    ssl_esni->keyshare = private_key.get();
    ssl_esni->the_esni = client_esni.get();
    ssl_esni->rd = constCharPtrToUcharPtr(esni.record_digest.c_str());
    ssl_esni->rd_len = esni.record_digest.length();
    ssl_esni->ciphersuite = esni.suite;
    ssl_esni->version= version;

    encoded_key_share2 = toEncodedKeyShare(key_share);

    size_t server_name_length;

    uint8_t *server_name = SSL_ESNI_dec(
                ssl_esni.get(), random.length(), constCharPtrToUcharPtr(random.c_str()),
                esni.key_share.group, encoded_key_share2.length(),
                constCharPtrToUcharPtr(encoded_key_share2.c_str()), &server_name_length
            );

    if (server_name_length == 0)
        return std::string();

    return std::string(reinterpret_cast<char*>(server_name));
}

And this is not related about this problem but when I call SSL_ESNI_free it throws SIGSEGV.

sftcd commented 4 years ago

Hiya,

Thanks for the explanation.

On 14/11/2019 03:22, Hyunhong Kim wrote:

Firefox and openssl s_client uses same record(my custom record) but one can decrypted with private key and the other doesn't. IIRC firefox sends >1 key share in the TLS h/s and I had to handle that specially in s_server. Maybe your code needs similar changes? (Sorry, don't have time to check in detail right now.)

(And just to re-iterate - please do expect API changes to library-internal things like SSL_ESNI_dec as the ESNI draft specification matures.)

S.

pol4bear commented 4 years ago

I know that firefox sends multiple keys in the TLS h/s. So I made program to give key share using same group(x25519) but it couldn't work.

sftcd commented 4 years ago

Been a while, sorry for not responding but I guess elapsed-time is maybe enough of a reason to close this. If not, I guess we can reopen.