tls: rearrange function order, improve comments
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 17 Jan 2017 16:04:24 +0000 (17:04 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 17 Jan 2017 16:04:24 +0000 (17:04 +0100)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/tls.c

index 60afd30b4c89e70698086c8916908d1449788bef..81820e9a1c1aaf332a9a6055a400522d0c51207c 100644 (file)
@@ -284,6 +284,145 @@ static void sha256_hash_dbg(const char *fmt, sha256_ctx_t *ctx, const void *buff
          sha256_hash(ctx, buffer, len)
 #endif /* not TLS_DEBUG >= 2 */
 
+// RFC 2104
+// HMAC(key, text) based on a hash H (say, sha256) is:
+// ipad = [0x36 x INSIZE]
+// opad = [0x5c x INSIZE]
+// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
+//
+// H(key XOR opad) and H(key XOR ipad) can be precomputed
+// if we often need HMAC hmac with the same key.
+//
+// text is often given in disjoint pieces.
+static void hmac_sha256_precomputed_v(uint8_t out[SHA256_OUTSIZE],
+               sha256_ctx_t *hashed_key_xor_ipad,
+               sha256_ctx_t *hashed_key_xor_opad,
+               va_list va)
+{
+       uint8_t *text;
+
+       /* hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
+       /* 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);
+               sha256_hash(hashed_key_xor_ipad, text, text_size);
+       }
+       sha256_end(hashed_key_xor_ipad, out);
+
+       /* out = H((key XOR opad) + out) */
+       sha256_hash(hashed_key_xor_opad, out, SHA256_OUTSIZE);
+       sha256_end(hashed_key_xor_opad, out);
+}
+
+static void hmac_sha256(uint8_t out[SHA256_OUTSIZE], uint8_t *key, unsigned key_size, ...)
+{
+       sha256_ctx_t hashed_key_xor_ipad;
+       sha256_ctx_t hashed_key_xor_opad;
+       uint8_t key_xor_ipad[SHA256_INSIZE];
+       uint8_t key_xor_opad[SHA256_INSIZE];
+       uint8_t tempkey[SHA256_OUTSIZE];
+       va_list va;
+       int 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 > SHA256_INSIZE) {
+               hash_sha256(tempkey, key, key_size);
+               key = tempkey;
+               key_size = SHA256_OUTSIZE;
+       }
+
+       for (i = 0; i < key_size; i++) {
+               key_xor_ipad[i] = key[i] ^ 0x36;
+               key_xor_opad[i] = key[i] ^ 0x5c;
+       }
+       for (; i < SHA256_INSIZE; i++) {
+               key_xor_ipad[i] = 0x36;
+               key_xor_opad[i] = 0x5c;
+       }
+       sha256_begin(&hashed_key_xor_ipad);
+       sha256_hash(&hashed_key_xor_ipad, key_xor_ipad, SHA256_INSIZE);
+       sha256_begin(&hashed_key_xor_opad);
+       sha256_hash(&hashed_key_xor_opad, key_xor_opad, SHA256_INSIZE);
+
+       hmac_sha256_precomputed_v(out, &hashed_key_xor_ipad, &hashed_key_xor_opad, va);
+       va_end(va);
+}
+
+// RFC 5246:
+// 5.  HMAC and the Pseudorandom Function
+//...
+// In this section, we define one PRF, based on HMAC.  This PRF with the
+// 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.
+//...
+//    P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+//                           HMAC_hash(secret, A(2) + seed) +
+//                           HMAC_hash(secret, A(3) + seed) + ...
+// where + indicates concatenation.
+// A() is defined as:
+//    A(0) = seed
+//    A(1) = HMAC_hash(secret, A(0)) = HMAC_hash(secret, seed)
+//    A(i) = HMAC_hash(secret, A(i-1))
+// P_hash can be iterated as many times as necessary to produce the
+// required quantity of data.  For example, if P_SHA256 is being used to
+// create 80 bytes of data, it will have to be iterated three times
+// (through A(3)), creating 96 bytes of output data; the last 16 bytes
+// of the final iteration will then be discarded, leaving 80 bytes of
+// output data.
+//
+// TLS's PRF is created by applying P_hash to the secret as:
+//
+//    PRF(secret, label, seed) = P_<hash>(secret, label + seed)
+//
+// The label is an ASCII string.
+static void tls_prf_hmac_sha256(
+               uint8_t *outbuf, unsigned outbuf_size,
+               uint8_t *secret, unsigned secret_size,
+               const char *label,
+               uint8_t *seed, unsigned seed_size)
+{
+       uint8_t a[SHA256_OUTSIZE];
+       uint8_t *out_p = outbuf;
+       unsigned label_size = strlen(label);
+
+       /* In P_hash() calculation, "seed" is "label + seed": */
+#define SEED   label, label_size, seed, seed_size
+#define SECRET secret, secret_size
+#define A      a, (int)(sizeof(a))
+
+       /* A(1) = HMAC_hash(secret, seed) */
+       hmac_sha256(a, SECRET, SEED, NULL);
+//TODO: convert hmac_sha256 to precomputed
+
+       for(;;) {
+               /* HMAC_hash(secret, A(1) + seed) */
+               if (outbuf_size <= SHA256_OUTSIZE) {
+                       /* Last, possibly incomplete, block */
+                       /* (use a[] as temp buffer) */
+                       hmac_sha256(a, SECRET, A, SEED, NULL);
+                       memcpy(out_p, a, outbuf_size);
+                       return;
+               }
+               /* Not last block. Store directly to result buffer */
+               hmac_sha256(out_p, SECRET, A, SEED, NULL);
+               out_p += SHA256_OUTSIZE;
+               outbuf_size -= SHA256_OUTSIZE;
+               /* A(2) = HMAC_hash(secret, A(1)) */
+               hmac_sha256(a, SECRET, A, NULL);
+       }
+#undef A
+#undef SECRET
+#undef SEED
+}
+
 static
 tls_state_t *new_tls_state(void)
 {
@@ -293,11 +432,13 @@ tls_state_t *new_tls_state(void)
        return tls;
 }
 
-static void hmac_sha256(uint8_t out[SHA256_OUTSIZE], uint8_t *key, unsigned key_size, ...);
-
-static void xwrite_and_hash(tls_state_t *tls, /*const*/ void *buf, unsigned size)
+static void tls_error_die(tls_state_t *tls)
 {
-// rfc5246
+       dump_tls_record(tls->inbuf, tls->insize + tls->tail);
+       xfunc_die();
+}
+
+// RFC 5246
 // 6.2.3.1.  Null or Standard Stream Cipher
 //
 // Stream ciphers (including BulkCipherAlgorithm.null; see Appendix A.6)
@@ -310,18 +451,14 @@ static void xwrite_and_hash(tls_state_t *tls, /*const*/ void *buf, unsigned size
 //    } GenericStreamCipher;
 //
 // The MAC is generated as:
-//
 //    MAC(MAC_write_key, seq_num +
 //                          TLSCompressed.type +
 //                          TLSCompressed.version +
 //                          TLSCompressed.length +
 //                          TLSCompressed.fragment);
-//
 // where "+" denotes concatenation.
-//
 // seq_num
 //    The sequence number for this record.
-//
 // MAC
 //    The MAC algorithm specified by SecurityParameters.mac_algorithm.
 //
@@ -345,23 +482,26 @@ static void xwrite_and_hash(tls_state_t *tls, /*const*/ void *buf, unsigned size
 // MD5       HMAC-MD5        16            16
 // SHA       HMAC-SHA1       20            20
 // SHA256    HMAC-SHA256     32            32
-
+static void xwrite_and_hash(tls_state_t *tls, /*const*/ void *buf, unsigned size)
+{
        uint8_t mac_hash[SHA256_OUTSIZE];
        struct record_hdr *xhdr = buf;
 
        if (tls->encrypt_on_write) {
+//TODO: convert hmac_sha256 to precomputed
                hmac_sha256(mac_hash,
                        tls->client_write_MAC_key, sizeof(tls->client_write_MAC_key),
                        &tls->write_seq64_be, sizeof(tls->write_seq64_be),
                        buf, size,
                NULL);
                tls->write_seq64_be = SWAP_BE64(1 + SWAP_BE64(tls->write_seq64_be));
+               /* Temporarily change for writing */
                xhdr->len16_lo += SHA256_OUTSIZE;
-//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FIXME
        }
 
        xwrite(tls->fd, buf, size);
        dbg("wrote %u bytes\n", size);
+
        if (tls->encrypt_on_write) {
                xwrite(tls->fd, mac_hash, sizeof(mac_hash));
                dbg("wrote %u bytes of hash\n", (int)sizeof(mac_hash));
@@ -374,12 +514,6 @@ static void xwrite_and_hash(tls_state_t *tls, /*const*/ void *buf, unsigned size
        }
 }
 
-static void tls_error_die(tls_state_t *tls)
-{
-       dump_tls_record(tls->inbuf, tls->insize + tls->tail);
-       xfunc_die();
-}
-
 static int xread_tls_block(tls_state_t *tls)
 {
        struct record_hdr *xhdr;
@@ -417,7 +551,7 @@ static int xread_tls_block(tls_state_t *tls)
        target -= sizeof(*xhdr);
 
        /* RFC 5246 is not saying it explicitly, but sha256 hash
-        * in our FINISHED packet must include hashes of incoming packets too!
+        * in our FINISHED record must include data of incoming packets too!
         */
        if (tls->inbuf[0] == RECORD_TYPE_HANDSHAKE) {
                sha256_hash_dbg("<< sha256:%s", &tls->handshake_sha256_ctx, tls->inbuf + 5, target);
@@ -427,23 +561,9 @@ static int xread_tls_block(tls_state_t *tls)
        return target;
 }
 
-static int xread_tls_handshake_block(tls_state_t *tls, int min_len)
-{
-       struct record_hdr *xhdr;
-       int len = xread_tls_block(tls);
-
-       xhdr = (void*)tls->inbuf;
-       if (len < min_len
-        || xhdr->type != RECORD_TYPE_HANDSHAKE
-        || xhdr->proto_maj != TLS_MAJ
-        || xhdr->proto_min != TLS_MIN
-       ) {
-               tls_error_die(tls);
-       }
-       dbg("got HANDSHAKE\n");
-       return len;
-}
-
+/*
+ * DER parsing routines
+ */
 static unsigned get_der_len(uint8_t **bodyp, uint8_t *der, uint8_t *end)
 {
        unsigned len, len1;
@@ -653,147 +773,26 @@ static void find_key_in_der_cert(tls_state_t *tls, uint8_t *der, int len)
        dbg("server_rsa_pub_key.size:%d\n", tls->server_rsa_pub_key.size);
 }
 
-// RFC 2104: HMAC(key, text) based on a hash H (say, sha256) is:
-// ipad = [0x36 x INSIZE]
-// opad = [0x5c x INSIZE]
-// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
-//
-// H(key XOR opad) and H(key XOR ipad) can be precomputed
-// if we often need HMAC hmac with the same key.
-//
-// text is often given in disjoint pieces.
-static void hmac_sha256_precomputed_v(uint8_t out[SHA256_OUTSIZE],
-               sha256_ctx_t *hashed_key_xor_ipad,
-               sha256_ctx_t *hashed_key_xor_opad,
-               va_list va)
-{
-       uint8_t *text;
-
-       /* hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
-       /* 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);
-               sha256_hash(hashed_key_xor_ipad, text, text_size);
-       }
-       sha256_end(hashed_key_xor_ipad, out);
-
-       /* out = H((key XOR opad) + out) */
-       sha256_hash(hashed_key_xor_opad, out, SHA256_OUTSIZE);
-       sha256_end(hashed_key_xor_opad, out);
-}
-
-static void hmac_sha256(uint8_t out[SHA256_OUTSIZE], uint8_t *key, unsigned key_size, ...)
-{
-       sha256_ctx_t hashed_key_xor_ipad;
-       sha256_ctx_t hashed_key_xor_opad;
-       uint8_t key_xor_ipad[SHA256_INSIZE];
-       uint8_t key_xor_opad[SHA256_INSIZE];
-       uint8_t tempkey[SHA256_OUTSIZE];
-       va_list va;
-       int 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 > SHA256_INSIZE) {
-               hash_sha256(tempkey, key, key_size);
-               key = tempkey;
-               key_size = SHA256_OUTSIZE;
-       }
-
-       for (i = 0; i < key_size; i++) {
-               key_xor_ipad[i] = key[i] ^ 0x36;
-               key_xor_opad[i] = key[i] ^ 0x5c;
-       }
-       for (; i < SHA256_INSIZE; i++) {
-               key_xor_ipad[i] = 0x36;
-               key_xor_opad[i] = 0x5c;
-       }
-       sha256_begin(&hashed_key_xor_ipad);
-       sha256_hash(&hashed_key_xor_ipad, key_xor_ipad, SHA256_INSIZE);
-       sha256_begin(&hashed_key_xor_opad);
-       sha256_hash(&hashed_key_xor_opad, key_xor_opad, SHA256_INSIZE);
-
-       hmac_sha256_precomputed_v(out, &hashed_key_xor_ipad, &hashed_key_xor_opad, va);
-       va_end(va);
-}
-
-// RFC 5246:
-// 5.  HMAC and the Pseudorandom Function
-//...
-// In this section, we define one PRF, based on HMAC.  This PRF with the
-// 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.
-//...
-//    P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
-//                           HMAC_hash(secret, A(2) + seed) +
-//                           HMAC_hash(secret, A(3) + seed) + ...
-// where + indicates concatenation.
-// A() is defined as:
-//    A(0) = seed
-//    A(1) = HMAC_hash(secret, A(0)) = HMAC_hash(secret, seed)
-//    A(i) = HMAC_hash(secret, A(i-1))
-// P_hash can be iterated as many times as necessary to produce the
-// required quantity of data.  For example, if P_SHA256 is being used to
-// create 80 bytes of data, it will have to be iterated three times
-// (through A(3)), creating 96 bytes of output data; the last 16 bytes
-// of the final iteration will then be discarded, leaving 80 bytes of
-// output data.
-//
-// TLS's PRF is created by applying P_hash to the secret as:
-//
-//    PRF(secret, label, seed) = P_<hash>(secret, label + seed)
-//
-// The label is an ASCII string.
-static void tls_prf_hmac_sha256(
-               uint8_t *outbuf, unsigned outbuf_size,
-               uint8_t *secret, unsigned secret_size,
-               const char *label,
-               uint8_t *seed, unsigned seed_size)
+/*
+ * TLS Handshake routines
+ */
+static int xread_tls_handshake_block(tls_state_t *tls, int min_len)
 {
-       uint8_t a[SHA256_OUTSIZE];
-       uint8_t *out_p = outbuf;
-       unsigned label_size = strlen(label);
-
-       /* In P_hash() calculation, "seed" is "label + seed": */
-#define SEED   label, label_size, seed, seed_size
-#define SECRET secret, secret_size
-#define A      a, (int)(sizeof(a))
-
-       /* A(1) = HMAC_hash(secret, seed) */
-       hmac_sha256(a, SECRET, SEED, NULL);
-//TODO: convert hmac_sha256 to precomputed
+       struct record_hdr *xhdr;
+       int len = xread_tls_block(tls);
 
-       for(;;) {
-               /* HMAC_hash(secret, A(1) + seed) */
-               if (outbuf_size <= SHA256_OUTSIZE) {
-                       /* Last, possibly incomplete, block */
-                       /* (use a[] as temp buffer) */
-                       hmac_sha256(a, SECRET, A, SEED, NULL);
-                       memcpy(out_p, a, outbuf_size);
-                       return;
-               }
-               /* Not last block. Store directly to result buffer */
-               hmac_sha256(out_p, SECRET, A, SEED, NULL);
-               out_p += SHA256_OUTSIZE;
-               outbuf_size -= SHA256_OUTSIZE;
-               /* A(2) = HMAC_hash(secret, A(1)) */
-               hmac_sha256(a, SECRET, A, NULL);
+       xhdr = (void*)tls->inbuf;
+       if (len < min_len
+        || xhdr->type != RECORD_TYPE_HANDSHAKE
+        || xhdr->proto_maj != TLS_MAJ
+        || xhdr->proto_min != TLS_MIN
+       ) {
+               tls_error_die(tls);
        }
-#undef A
-#undef SECRET
-#undef SEED
+       dbg("got HANDSHAKE\n");
+       return len;
 }
 
-/*
- * TLS Handshake routines
- */
 static void send_client_hello(tls_state_t *tls)
 {
        struct client_hello {
@@ -1055,7 +1054,7 @@ static void send_change_cipher_spec(tls_state_t *tls)
        dbg(">> CHANGE_CIPHER_SPEC\n");
        xwrite(tls->fd, rec, sizeof(rec));
 
-       tls->write_seq64_be = 0;
+       /* tls->write_seq64_be = 0; - already is */
        tls->encrypt_on_write = 1;
 }