Added macro that indicates presence of the time callback feature. Also added C++...
[BearSSL] / src / ssl / ssl_engine.c
index 529b107..f59fe1a 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;
 }
 
 /*
@@ -1245,6 +1232,21 @@ void
 br_ssl_engine_close(br_ssl_engine_context *cc)
 {
        if (!br_ssl_engine_closed(cc)) {
+               /*
+                * If we are not already closed, then we need to
+                * initiate the closure. Once closing, any incoming
+                * application data is discarded; we should also discard
+                * application data which is already there but has not
+                * been acknowledged by the application yet (this mimics
+                * usual semantics on BSD sockets: you cannot read()
+                * once you called close(), even if there was some
+                * unread data already buffered).
+                */
+               size_t len;
+
+               if (br_ssl_engine_recvapp_buf(cc, &len) != NULL && len != 0) {
+                       br_ssl_engine_recvapp_ack(cc, len);
+               }
                jump_handshake(cc, 1);
        }
 }
@@ -1535,3 +1537,48 @@ br_ssl_engine_switch_chapol_out(br_ssl_engine_context *cc,
        cc->ichapol_out->init(&cc->out.chapol.vtable.out,
                cc->ichacha, cc->ipoly, cipher_key, iv);
 }
+
+/* see inner.h */
+void
+br_ssl_engine_switch_ccm_in(br_ssl_engine_context *cc,
+       int is_client, int prf_id,
+       const br_block_ctrcbc_class *bc_impl,
+       size_t cipher_key_len, size_t tag_len)
+{
+       unsigned char kb[72];
+       unsigned char *cipher_key, *iv;
+
+       compute_key_block(cc, prf_id, cipher_key_len + 4, kb);
+       if (is_client) {
+               cipher_key = &kb[cipher_key_len];
+               iv = &kb[(cipher_key_len << 1) + 4];
+       } else {
+               cipher_key = &kb[0];
+               iv = &kb[cipher_key_len << 1];
+       }
+       cc->iccm_in->init(&cc->in.ccm.vtable.in,
+               bc_impl, cipher_key, cipher_key_len, iv, tag_len);
+       cc->incrypt = 1;
+}
+
+/* see inner.h */
+void
+br_ssl_engine_switch_ccm_out(br_ssl_engine_context *cc,
+       int is_client, int prf_id,
+       const br_block_ctrcbc_class *bc_impl,
+       size_t cipher_key_len, size_t tag_len)
+{
+       unsigned char kb[72];
+       unsigned char *cipher_key, *iv;
+
+       compute_key_block(cc, prf_id, cipher_key_len + 4, kb);
+       if (is_client) {
+               cipher_key = &kb[0];
+               iv = &kb[cipher_key_len << 1];
+       } else {
+               cipher_key = &kb[cipher_key_len];
+               iv = &kb[(cipher_key_len << 1) + 4];
+       }
+       cc->iccm_out->init(&cc->out.ccm.vtable.out,
+               bc_impl, cipher_key, cipher_key_len, iv, tag_len);
+}