#include "inner.h"
+#if 0
+/* obsolete */
+
/*
* If BR_USE_URANDOM is not defined, then try to autodetect its presence
* through compiler macros.
#pragma comment(lib, "advapi32")
#endif
+#endif
+
/* ==================================================================== */
/*
* This part of the file does the low-level record management.
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.
*
- * Use similar OS facilities on other OS (getentropy() on OpenBSD,
- * specialized sysctl on NetBSD and FreeBSD...).
+ * 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).
*/
-#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;
}
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;
}
/*
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;
}
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;
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);
}
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);
int
br_ssl_engine_renegotiate(br_ssl_engine_context *cc)
{
+ size_t len;
+
if (br_ssl_engine_closed(cc) || cc->reneg == 1
- || (cc->flags & BR_OPT_NO_RENEGOTIATION) != 0)
+ || (cc->flags & BR_OPT_NO_RENEGOTIATION) != 0
+ || br_ssl_engine_recvapp_buf(cc, &len) != NULL)
{
return 0;
}
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);
}
}
cc->hsrun = hsrun;
cc->shutdown_recv = 0;
cc->application_data = 0;
+ cc->alert = 0;
jump_handshake(cc, 0);
}
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);
}
/*
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 */
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);
+}