Added API to share precomputations in EAX.
[BearSSL] / src / ssl / ssl_engine.c
index 8af773d..1022d87 100644 (file)
@@ -24,6 +24,9 @@
 
 #include "inner.h"
 
+#if 0
+/* obsolete */
+
 /*
  * If BR_USE_URANDOM is not defined, then try to autodetect its presence
  * through compiler macros.
@@ -75,6 +78,8 @@
 #pragma comment(lib, "advapi32")
 #endif
 
+#endif
+
 /* ==================================================================== */
 /*
  * This part of the file does the low-level record management.
@@ -456,65 +461,71 @@ engine_clearbuf(br_ssl_engine_context *rc)
        make_ready_out(rc);
 }
 
-/* see inner.h */
-int
-br_ssl_engine_init_rand(br_ssl_engine_context *cc)
+/*
+ * Make sure the internal PRNG is initialised (but not necessarily
+ * seeded properly yet).
+ */
+static int
+rng_init(br_ssl_engine_context *cc)
 {
+       const br_hash_class *h;
+
+       if (cc->rng_init_done != 0) {
+               return 1;
+       }
+
        /*
-        * TODO: use getrandom() on Linux systems, with a fallback to
-        * opening /dev/urandom if that system call fails.
+        * If using TLS-1.2, then SHA-256 or SHA-384 must be present (or
+        * both); we prefer SHA-256 which is faster for 32-bit systems.
+        *
+        * If using TLS-1.0 or 1.1 then SHA-1 must be present.
         *
-        * Use similar OS facilities on other OS (getentropy() on OpenBSD,
-        * specialized sysctl on NetBSD and FreeBSD...).
+        * Though HMAC_DRBG/SHA-1 is, as far as we know, as safe as
+        * these things can be, we still prefer the SHA-2 functions over
+        * SHA-1, if only for public relations (known theoretical
+        * weaknesses of SHA-1 with regards to collisions are mostly
+        * irrelevant here, but they still make people nervous).
         */
-#if BR_USE_URANDOM
-       if (!cc->rng_os_rand_done) {
-               int f;
-
-               f = open("/dev/urandom", O_RDONLY);
-               if (f >= 0) {
-                       unsigned char tmp[32];
-                       size_t u;
-
-                       for (u = 0; u < sizeof tmp;) {
-                               ssize_t len;
-
-                               len = read(f, tmp + u, (sizeof tmp) - u);
-                               if (len < 0) {
-                                       if (errno == EINTR) {
-                                               continue;
-                                       }
-                                       break;
-                               }
-                               u += (size_t)len;
-                       }
-                       close(f);
-                       if (u == sizeof tmp) {
-                               br_ssl_engine_inject_entropy(cc, tmp, u);
-                               cc->rng_os_rand_done = 1;
+       h = br_multihash_getimpl(&cc->mhash, br_sha256_ID);
+       if (!h) {
+               h = br_multihash_getimpl(&cc->mhash, br_sha384_ID);
+               if (!h) {
+                       h = br_multihash_getimpl(&cc->mhash,
+                               br_sha1_ID);
+                       if (!h) {
+                               br_ssl_engine_fail(cc, BR_ERR_BAD_STATE);
+                               return 0;
                        }
                }
        }
-#elif BR_USE_WIN32_RAND
-       if (!cc->rng_os_rand_done) {
-               HCRYPTPROV hp;
+       br_hmac_drbg_init(&cc->rng, h, NULL, 0);
+       cc->rng_init_done = 1;
+       return 1;
+}
 
-               if (CryptAcquireContextW(&hp, 0, 0, PROV_RSA_FULL,
-                       CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
-               {
-                       BYTE buf[32];
+/* see inner.h */
+int
+br_ssl_engine_init_rand(br_ssl_engine_context *cc)
+{
+       if (!rng_init(cc)) {
+               return 0;
+       }
 
-                       if (CryptGenRandom(hp, sizeof buf, buf)) {
-                               br_ssl_engine_inject_entropy(cc,
-                                       buf, sizeof buf);
-                               cc->rng_os_rand_done = 1;
-                       }
-                       CryptReleaseContext(hp, 0);
+       /*
+        * We always try OS/hardware seeding once. If it works, then
+        * we assume proper seeding. If not, then external entropy must
+        * have been injected; otherwise, we report an error.
+        */
+       if (!cc->rng_os_rand_done) {
+               br_prng_seeder sd;
+
+               sd = br_prng_seeder_system(NULL);
+               if (sd != 0 && sd(&cc->rng.vtable)) {
+                       cc->rng_init_done = 2;
                }
+               cc->rng_os_rand_done = 1;
        }
-#endif
-
-       if (!cc->rng_init_done) {
+       if (cc->rng_init_done < 2) {
                br_ssl_engine_fail(cc, BR_ERR_NO_RANDOM);
                return 0;
        }
@@ -526,41 +537,17 @@ void
 br_ssl_engine_inject_entropy(br_ssl_engine_context *cc,
        const void *data, size_t len)
 {
-       if (cc->rng_init_done) {
-               br_hmac_drbg_update(&cc->rng, data, len);
-       } else {
-               /*
-                * If using TLS-1.2, then SHA-256 or SHA-384 must be
-                * present (or both); we prefer SHA-256 which is faster
-                * for 32-bit systems.
-                *
-                * If using TLS-1.0 or 1.1 then SHA-1 must be present.
-                *
-                * Though HMAC_DRBG/SHA-1 is, as far as we know, as safe
-                * as these things can be, we still prefer the SHA-2
-                * functions over SHA-1, if only for public relations
-                * (known theoretical weaknesses of SHA-1 with regards to
-                * collisions are mostly irrelevant here, but they still
-                * make people nervous).
-                */
-               const br_hash_class *h;
-
-               h = br_multihash_getimpl(&cc->mhash, br_sha256_ID);
-               if (!h) {
-                       h = br_multihash_getimpl(&cc->mhash, br_sha384_ID);
-                       if (!h) {
-                               h = br_multihash_getimpl(&cc->mhash,
-                                       br_sha1_ID);
-                               if (!h) {
-                                       br_ssl_engine_fail(cc,
-                                               BR_ERR_BAD_STATE);
-                                       return;
-                               }
-                       }
-               }
-               br_hmac_drbg_init(&cc->rng, h, data, len);
-               cc->rng_init_done = 1;
+       /*
+        * Externally provided entropy is assumed to be "good enough"
+        * (we cannot really test its quality) so if the RNG structure
+        * could be initialised at all, then we marked the RNG as
+        * "properly seeded".
+        */
+       if (!rng_init(cc)) {
+               return;
        }
+       br_hmac_drbg_update(&cc->rng, data, len);
+       cc->rng_init_done = 2;
 }
 
 /*
@@ -880,6 +867,12 @@ sendpld_ack(br_ssl_engine_context *rc, size_t len)
        }
        rc->oxa += len;
        if (rc->oxa >= rc->oxb) {
+               /*
+                * Set oxb to one more than oxa so that sendpld_flush()
+                * does not mistakingly believe that a record is
+                * already prepared and being sent.
+                */
+               rc->oxb = rc->oxa + 1;
                sendpld_flush(rc, 0);
        }
 }
@@ -1085,6 +1078,9 @@ jump_handshake(br_ssl_engine_context *cc, int action)
                cc->hlen_out = hlen_out;
                cc->action = action;
                cc->hsrun(&cc->cpu);
+               if (br_ssl_engine_closed(cc)) {
+                       return;
+               }
                if (cc->hbuf_out != cc->saved_hbuf_out) {
                        sendpld_ack(cc, cc->hbuf_out - cc->saved_hbuf_out);
                }
@@ -1123,7 +1119,7 @@ br_ssl_engine_flush_record(br_ssl_engine_context *cc)
 unsigned char *
 br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len)
 {
-       if (!cc->application_data) {
+       if (!(cc->application_data & 1)) {
                *len = 0;
                return NULL;
        }
@@ -1141,7 +1137,7 @@ br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len)
 unsigned char *
 br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len)
 {
-       if (!cc->application_data
+       if (!(cc->application_data & 1)
                || cc->record_type_in != BR_SSL_APPLICATION_DATA)
        {
                *len = 0;
@@ -1171,7 +1167,7 @@ br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len)
        sendrec_ack(cc, len);
        if (len != 0 && !has_rec_tosend(cc)
                && (cc->record_type_out != BR_SSL_APPLICATION_DATA
-               || cc->application_data == 0))
+               || (cc->application_data & 1) == 0))
        {
                jump_handshake(cc, 0);
        }
@@ -1209,9 +1205,20 @@ br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len)
                        jump_handshake(cc, 0);
                        break;
                case BR_SSL_APPLICATION_DATA:
-                       if (cc->application_data) {
+                       if (cc->application_data == 1) {
                                break;
                        }
+
+                       /*
+                        * If we are currently closing, and waiting for
+                        * a close_notify from the peer, then incoming
+                        * application data should be discarded.
+                        */
+                       if (cc->application_data == 2) {
+                               recvpld_ack(cc, len);
+                               break;
+                       }
+
                        /* Fall through */
                default:
                        br_ssl_engine_fail(cc, BR_ERR_UNEXPECTED);
@@ -1233,7 +1240,12 @@ br_ssl_engine_close(br_ssl_engine_context *cc)
 int
 br_ssl_engine_renegotiate(br_ssl_engine_context *cc)
 {
-       if (br_ssl_engine_closed(cc) || cc->reneg == 1) {
+       size_t len;
+
+       if (br_ssl_engine_closed(cc) || cc->reneg == 1
+               || (cc->flags & BR_OPT_NO_RENEGOTIATION) != 0
+               || br_ssl_engine_recvapp_buf(cc, &len) != NULL)
+       {
                return 0;
        }
        jump_handshake(cc, 2);
@@ -1271,7 +1283,7 @@ br_ssl_engine_current_state(const br_ssl_engine_context *cc)
 void
 br_ssl_engine_flush(br_ssl_engine_context *cc, int force)
 {
-       if (!br_ssl_engine_closed(cc) && cc->application_data) {
+       if (!br_ssl_engine_closed(cc) && (cc->application_data & 1) != 0) {
                sendpld_flush(cc, force);
        }
 }
@@ -1288,6 +1300,7 @@ br_ssl_engine_hs_reset(br_ssl_engine_context *cc,
        cc->hsrun = hsrun;
        cc->shutdown_recv = 0;
        cc->application_data = 0;
+       cc->alert = 0;
        jump_handshake(cc, 0);
 }
 
@@ -1312,13 +1325,14 @@ br_ssl_engine_compute_master(br_ssl_engine_context *cc,
        int prf_id, const void *pms, size_t pms_len)
 {
        br_tls_prf_impl iprf;
-       unsigned char seed[64];
+       br_tls_prf_seed_chunk seed[2] = {
+               { cc->client_random, sizeof cc->client_random },
+               { cc->server_random, sizeof cc->server_random }
+       };
 
        iprf = br_ssl_engine_get_PRF(cc, prf_id);
-       memcpy(seed, cc->client_random, 32);
-       memcpy(seed + 32, cc->server_random, 32);
        iprf(cc->session.master_secret, sizeof cc->session.master_secret,
-               pms, pms_len, "master secret", seed, sizeof seed);
+               pms, pms_len, "master secret", 2, seed);
 }
 
 /*
@@ -1329,14 +1343,15 @@ compute_key_block(br_ssl_engine_context *cc, int prf_id,
        size_t half_len, unsigned char *kb)
 {
        br_tls_prf_impl iprf;
-       unsigned char seed[64];
+       br_tls_prf_seed_chunk seed[2] = {
+               { cc->server_random, sizeof cc->server_random },
+               { cc->client_random, sizeof cc->client_random }
+       };
 
        iprf = br_ssl_engine_get_PRF(cc, prf_id);
-       memcpy(seed, cc->server_random, 32);
-       memcpy(seed + 32, cc->client_random, 32);
        iprf(kb, half_len << 1,
                cc->session.master_secret, sizeof cc->session.master_secret,
-               "key expansion", seed, sizeof seed);
+               "key expansion", 2, seed);
 }
 
 /* see inner.h */
@@ -1466,3 +1481,44 @@ br_ssl_engine_switch_gcm_out(br_ssl_engine_context *cc,
        cc->igcm_out->init(&cc->out.gcm.vtable.out,
                bc_impl, cipher_key, cipher_key_len, cc->ighash, iv);
 }
+
+/* see inner.h */
+void
+br_ssl_engine_switch_chapol_in(br_ssl_engine_context *cc,
+       int is_client, int prf_id)
+{
+       unsigned char kb[88];
+       unsigned char *cipher_key, *iv;
+
+       compute_key_block(cc, prf_id, 44, kb);
+       if (is_client) {
+               cipher_key = &kb[32];
+               iv = &kb[76];
+       } else {
+               cipher_key = &kb[0];
+               iv = &kb[64];
+       }
+       cc->ichapol_in->init(&cc->in.chapol.vtable.in,
+               cc->ichacha, cc->ipoly, cipher_key, iv);
+       cc->incrypt = 1;
+}
+
+/* see inner.h */
+void
+br_ssl_engine_switch_chapol_out(br_ssl_engine_context *cc,
+       int is_client, int prf_id)
+{
+       unsigned char kb[88];
+       unsigned char *cipher_key, *iv;
+
+       compute_key_block(cc, prf_id, 44, kb);
+       if (is_client) {
+               cipher_key = &kb[0];
+               iv = &kb[64];
+       } else {
+               cipher_key = &kb[32];
+               iv = &kb[76];
+       }
+       cc->ichapol_out->init(&cc->out.chapol.vtable.out,
+               cc->ichacha, cc->ipoly, cipher_key, iv);
+}