tls: can download kernels now :)
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 24 Jan 2017 17:08:07 +0000 (18:08 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 24 Jan 2017 17:08:07 +0000 (18:08 +0100)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
include/libbb.h
networking/tls.c

index b1ceb32786eceeedc8a4945c9b8617062f502143..8c652e2d7f2b38e6d879120face94541fc427eef 100644 (file)
@@ -721,7 +721,7 @@ typedef struct tls_state {
        int ofd;
        int ifd;
 
-       int      min_encrypted_len_on_read;
+       unsigned min_encrypted_len_on_read;
        uint16_t cipher_id;
        uint8_t  encrypt_on_write;
        unsigned MAC_size;
index dc94a8b9bc5843bf3036625ac27bfb3c6ca1b039..3353245681674d619e5176c683de34339f4d18b7 100644 (file)
 //  ok:   openssl s_client -connect cdn.kernel.org:443 -debug -tls1_2 -no_tls1 -no_tls1_1 -cipher AES128-SHA
 //        (TLS_RSA_WITH_AES_128_CBC_SHA - in TLS 1.2 it's mandated to be always supported)
 #define CIPHER_ID1  TLS_RSA_WITH_AES_256_CBC_SHA256 // no SERVER_KEY_EXCHANGE from peer
-// Does not work yet:
-//#define CIPHER_ID2  TLS_RSA_WITH_AES_128_CBC_SHA
-#define CIPHER_ID2  0
+// Works with "wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.9.5.tar.xz"
+#define CIPHER_ID2  TLS_RSA_WITH_AES_128_CBC_SHA
 
 
-#define TLS_DEBUG      1
-#define TLS_DEBUG_HASH 1
-#define TLS_DEBUG_DER  1
-#define TLS_DEBUG_FIXED_SECRETS 1
+#define TLS_DEBUG      0
+#define TLS_DEBUG_HASH 0
+#define TLS_DEBUG_DER  0
+#define TLS_DEBUG_FIXED_SECRETS 0
 #if 0
 # define dump_raw_out(...) dump_hex(__VA_ARGS__)
 #else
@@ -340,51 +339,48 @@ static void hash_handshake(tls_state_t *tls, const char *fmt, const void *buffer
 // if we often need HMAC hmac with the same key.
 //
 // text is often given in disjoint pieces.
-static unsigned hmac_sha_precomputed_v(uint8_t *out,
-               md5sha_ctx_t *hashed_key_xor_ipad,
-               md5sha_ctx_t *hashed_key_xor_opad,
+typedef struct hmac_precomputed {
+       md5sha_ctx_t hashed_key_xor_ipad;
+       md5sha_ctx_t hashed_key_xor_opad;
+} hmac_precomputed_t;
+
+static unsigned hmac_sha_precomputed_v(
+               hmac_precomputed_t *pre,
+               uint8_t *out,
                va_list va)
 {
        uint8_t *text;
        unsigned len;
 
-       /* hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
-       /* hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
+       /* pre->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
+       /* pre->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
 
        /* calculate out = H((key XOR ipad) + text) */
        while ((text = va_arg(va, uint8_t*)) != NULL) {
                unsigned text_size = va_arg(va, unsigned);
-               md5sha_hash(hashed_key_xor_ipad, text, text_size);
+               md5sha_hash(&pre->hashed_key_xor_ipad, text, text_size);
        }
-       len = sha_end(hashed_key_xor_ipad, out);
+       len = sha_end(&pre->hashed_key_xor_ipad, out);
 
        /* out = H((key XOR opad) + out) */
-       md5sha_hash(hashed_key_xor_opad, out, len);
-       return sha_end(hashed_key_xor_opad, out);
+       md5sha_hash(&pre->hashed_key_xor_opad, out, len);
+       return sha_end(&pre->hashed_key_xor_opad, out);
 }
 
-static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
+static void hmac_sha256_begin(hmac_precomputed_t *pre, uint8_t *key, unsigned key_size)
 {
-       md5sha_ctx_t hashed_key_xor_ipad;
-       md5sha_ctx_t hashed_key_xor_opad;
        uint8_t key_xor_ipad[SHA_INSIZE];
        uint8_t key_xor_opad[SHA_INSIZE];
        uint8_t tempkey[SHA256_OUTSIZE];
-       va_list va;
        unsigned i;
 
-       va_start(va, key_size);
-
        // "The authentication key can be of any length up to INSIZE, the
        // block length of the hash function.  Applications that use keys longer
        // than INSIZE bytes will first hash the key using H and then use the
        // resultant OUTSIZE byte string as the actual key to HMAC."
        if (key_size > SHA_INSIZE) {
                md5sha_ctx_t ctx;
-               if (tls->MAC_size == SHA256_OUTSIZE)
-                       sha256_begin(&ctx);
-               else
-                       sha1_begin(&ctx);
+               sha256_begin(&ctx);
                md5sha_hash(&ctx, key, key_size);
                key_size = sha_end(&ctx, tempkey);
        }
@@ -398,19 +394,77 @@ static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_
                key_xor_opad[i] = 0x5c;
        }
 
-       if (tls->MAC_size == SHA256_OUTSIZE) {
-               sha256_begin(&hashed_key_xor_ipad);
-               sha256_begin(&hashed_key_xor_opad);
-       } else {
-               sha1_begin(&hashed_key_xor_ipad);
-               sha1_begin(&hashed_key_xor_opad);
+       sha256_begin(&pre->hashed_key_xor_ipad);
+       sha256_begin(&pre->hashed_key_xor_opad);
+       md5sha_hash(&pre->hashed_key_xor_ipad, key_xor_ipad, SHA_INSIZE);
+       md5sha_hash(&pre->hashed_key_xor_opad, key_xor_opad, SHA_INSIZE);
+}
+// TODO: ^^^ vvv merge?
+static void hmac_sha1_begin(hmac_precomputed_t *pre, uint8_t *key, unsigned key_size)
+{
+       uint8_t key_xor_ipad[SHA_INSIZE];
+       uint8_t key_xor_opad[SHA_INSIZE];
+       uint8_t tempkey[SHA1_OUTSIZE];
+       unsigned i;
+
+       // "The authentication key can be of any length up to INSIZE, the
+       // block length of the hash function.  Applications that use keys longer
+       // than INSIZE bytes will first hash the key using H and then use the
+       // resultant OUTSIZE byte string as the actual key to HMAC."
+       if (key_size > SHA_INSIZE) {
+               md5sha_ctx_t ctx;
+               sha1_begin(&ctx);
+               md5sha_hash(&ctx, key, key_size);
+               key_size = sha_end(&ctx, tempkey);
+       }
+
+       for (i = 0; i < key_size; i++) {
+               key_xor_ipad[i] = key[i] ^ 0x36;
+               key_xor_opad[i] = key[i] ^ 0x5c;
+       }
+       for (; i < SHA_INSIZE; i++) {
+               key_xor_ipad[i] = 0x36;
+               key_xor_opad[i] = 0x5c;
        }
-       md5sha_hash(&hashed_key_xor_ipad, key_xor_ipad, SHA_INSIZE);
-       md5sha_hash(&hashed_key_xor_opad, key_xor_opad, SHA_INSIZE);
 
-       i = hmac_sha_precomputed_v(out, &hashed_key_xor_ipad, &hashed_key_xor_opad, va);
+       sha1_begin(&pre->hashed_key_xor_ipad);
+       sha1_begin(&pre->hashed_key_xor_opad);
+       md5sha_hash(&pre->hashed_key_xor_ipad, key_xor_ipad, SHA_INSIZE);
+       md5sha_hash(&pre->hashed_key_xor_opad, key_xor_opad, SHA_INSIZE);
+}
+
+static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
+{
+       hmac_precomputed_t pre;
+       va_list va;
+       unsigned len;
+
+       va_start(va, key_size);
+
+       if (tls->MAC_size == SHA256_OUTSIZE)
+               hmac_sha256_begin(&pre, key, key_size);
+       else
+               hmac_sha1_begin(&pre, key, key_size);
+
+       len = hmac_sha_precomputed_v(&pre, out, va);
+
        va_end(va);
-       return i;
+       return len;
+}
+
+static unsigned hmac_sha256(/*tls_state_t *tls,*/ uint8_t *out, uint8_t *key, unsigned key_size, ...)
+{
+       hmac_precomputed_t pre;
+       va_list va;
+       unsigned len;
+
+       va_start(va, key_size);
+
+       hmac_sha256_begin(&pre, key, key_size);
+       len = hmac_sha_precomputed_v(&pre, out, va);
+
+       va_end(va);
+       return len;
 }
 
 // RFC 5246:
@@ -420,6 +474,9 @@ static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_
 // SHA-256 hash function is used for all cipher suites defined in this
 // document and in TLS documents published prior to this document when
 // TLS 1.2 is negotiated.
+// ^^^^^^^^^^^^^ IMPORTANT!
+//               PRF uses sha256 regardless of cipher (at least for all ciphers
+//               defined by RFC5246). It's not sha1 for AES_128_CBC_SHA!
 //...
 //    P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
 //                           HMAC_hash(secret, A(2) + seed) +
@@ -441,7 +498,7 @@ static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_
 //    PRF(secret, label, seed) = P_<hash>(secret, label + seed)
 //
 // The label is an ASCII string.
-static void prf_hmac(tls_state_t *tls,
+static void prf_hmac_sha256(/*tls_state_t *tls,*/
                uint8_t *outbuf, unsigned outbuf_size,
                uint8_t *secret, unsigned secret_size,
                const char *label,
@@ -450,7 +507,7 @@ static void prf_hmac(tls_state_t *tls,
        uint8_t a[TLS_MAX_MAC_SIZE];
        uint8_t *out_p = outbuf;
        unsigned label_size = strlen(label);
-       unsigned MAC_size = tls->MAC_size;
+       unsigned MAC_size = SHA256_OUTSIZE;///tls->MAC_size;
 
        /* In P_hash() calculation, "seed" is "label + seed": */
 #define SEED   label, label_size, seed, seed_size
@@ -458,7 +515,7 @@ static void prf_hmac(tls_state_t *tls,
 #define A      a, MAC_size
 
        /* A(1) = HMAC_hash(secret, seed) */
-       hmac(tls, a, SECRET, SEED, NULL);
+       hmac_sha256(/*tls,*/ a, SECRET, SEED, NULL);
 //TODO: convert hmac to precomputed
 
        for(;;) {
@@ -466,16 +523,16 @@ static void prf_hmac(tls_state_t *tls,
                if (outbuf_size <= MAC_size) {
                        /* Last, possibly incomplete, block */
                        /* (use a[] as temp buffer) */
-                       hmac(tls, a, SECRET, A, SEED, NULL);
+                       hmac_sha256(/*tls,*/ a, SECRET, A, SEED, NULL);
                        memcpy(out_p, a, outbuf_size);
                        return;
                }
                /* Not last block. Store directly to result buffer */
-               hmac(tls, out_p, SECRET, A, SEED, NULL);
+               hmac_sha256(/*tls,*/ out_p, SECRET, A, SEED, NULL);
                out_p += MAC_size;
                outbuf_size -= MAC_size;
                /* A(2) = HMAC_hash(secret, A(1)) */
-               hmac(tls, a, SECRET, A, NULL);
+               hmac_sha256(/*tls,*/ a, SECRET, A, NULL);
        }
 #undef A
 #undef SECRET
@@ -823,9 +880,10 @@ static int tls_xread_record(tls_state_t *tls)
                int padding_len;
 
                if (sz & (AES_BLOCKSIZE-1)
-                || sz < tls->min_encrypted_len_on_read
+                || sz < (int)tls->min_encrypted_len_on_read
                ) {
-                       bb_error_msg_and_die("bad encrypted len:%u", sz);
+                       bb_error_msg_and_die("bad encrypted len:%u < %u",
+                               sz, tls->min_encrypted_len_on_read);
                }
                /* Decrypt content+MAC+padding, moving it over IV in the process */
                psAesInit(&ctx, p, /* IV */
@@ -1301,13 +1359,17 @@ static void get_server_hello(tls_state_t *tls)
        if (cipher == TLS_RSA_WITH_AES_128_CBC_SHA) {
                tls->key_size = AES128_KEYSIZE;
                tls->MAC_size = SHA1_OUTSIZE;
-               sha1_begin(&tls->hsd->handshake_hash_ctx);
        }
        else { /* TLS_RSA_WITH_AES_256_CBC_SHA256 */
                tls->key_size = AES256_KEYSIZE;
                tls->MAC_size = SHA256_OUTSIZE;
-               sha256_begin(&tls->hsd->handshake_hash_ctx);
        }
+       /* Handshake hash eventually destined to FINISHED record
+        * is sha256 regardless of cipher
+        * (at least for all ciphers defined by RFC5246).
+        * It's not sha1 for AES_128_CBC_SHA - only MAC is sha1, not this hash.
+        */
+       sha256_begin(&tls->hsd->handshake_hash_ctx);
        hash_handshake(tls, ">> client hello hash:%s",
                tls->hsd->saved_client_hello, tls->hsd->saved_client_hello_size
        );
@@ -1389,6 +1451,7 @@ static void send_client_key_exchange(tls_state_t *tls)
        // version negotiated for the connection."
        rsa_premaster[0] = TLS_MAJ;
        rsa_premaster[1] = TLS_MIN;
+       dump_hex("premaster:%s\n", rsa_premaster, sizeof(rsa_premaster));
        len = psRsaEncryptPub(/*pool:*/ NULL,
                /* psRsaKey_t* */ &tls->hsd->server_rsa_pub_key,
                rsa_premaster, /*inlen:*/ sizeof(rsa_premaster),
@@ -1417,7 +1480,7 @@ static void send_client_key_exchange(tls_state_t *tls)
        //                          [0..47];
        // The master secret is always exactly 48 bytes in length.  The length
        // of the premaster secret will vary depending on key exchange method.
-       prf_hmac(tls,
+       prf_hmac_sha256(/*tls,*/
                tls->hsd->master_secret, sizeof(tls->hsd->master_secret),
                rsa_premaster, sizeof(rsa_premaster),
                "master secret",
@@ -1465,7 +1528,7 @@ static void send_client_key_exchange(tls_state_t *tls)
                memcpy(&tmp64[0] , &tls->hsd->client_and_server_rand32[32], 32);
                memcpy(&tmp64[32], &tls->hsd->client_and_server_rand32[0] , 32);
 
-               prf_hmac(tls,
+               prf_hmac_sha256(/*tls,*/
                        tls->client_write_MAC_key, 2 * (tls->MAC_size + tls->key_size),
                        // also fills:
                        // server_write_MAC_key[]
@@ -1548,7 +1611,7 @@ static void send_client_finished(tls_state_t *tls)
        fill_handshake_record_hdr(record, HANDSHAKE_FINISHED, sizeof(*record));
 
        len = get_handshake_hash(tls, handshake_hash);
-       prf_hmac(tls,
+       prf_hmac_sha256(/*tls,*/
                record->prf_result, sizeof(record->prf_result),
                tls->hsd->master_secret, sizeof(tls->hsd->master_secret),
                "client finished",
@@ -1645,9 +1708,14 @@ void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
        dbg("<< CHANGE_CIPHER_SPEC\n");
        if (tls->cipher_id == TLS_RSA_WITH_NULL_SHA256)
                tls->min_encrypted_len_on_read = tls->MAC_size;
-       else
-               /* all incoming packets now should be encrypted and have IV + MAC + padding */
-               tls->min_encrypted_len_on_read = AES_BLOCKSIZE + tls->MAC_size + AES_BLOCKSIZE;
+       else {
+               unsigned mac_blocks = (unsigned)(tls->MAC_size + AES_BLOCKSIZE-1) / AES_BLOCKSIZE;
+               /* all incoming packets now should be encrypted and have
+                * at least IV + (MAC padded to blocksize):
+                */
+               tls->min_encrypted_len_on_read = AES_BLOCKSIZE + (mac_blocks * AES_BLOCKSIZE);
+               dbg("min_encrypted_len_on_read: %u", tls->min_encrypted_len_on_read);
+       }
 
        /* Get (encrypted) FINISHED from the server */
        len = tls_xread_record(tls);