X-Git-Url: https://www.bearssl.org/gitweb//home/git/?p=BearSSL;a=blobdiff_plain;f=src%2Fssl%2Fssl_engine.c;h=9c7739e87aeb1d005757857f63b713f91dba1a11;hp=32bc406269151d1e839d7da1c622a1a6c606e93f;hb=946f5bad7683aa79a9f4fcab760206e2aad8c555;hpb=d16eee962a72a600c370f18f2ae25b36b4f532df diff --git a/src/ssl/ssl_engine.c b/src/ssl/ssl_engine.c index 32bc406..9c7739e 100644 --- a/src/ssl/ssl_engine.c +++ b/src/ssl/ssl_engine.c @@ -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. * - * 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; } @@ -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; } /* @@ -1091,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); } @@ -1129,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; } @@ -1147,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; @@ -1177,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); } @@ -1215,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); @@ -1231,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 alreayd 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); } } @@ -1239,8 +1255,11 @@ br_ssl_engine_close(br_ssl_engine_context *cc) 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; } @@ -1279,7 +1298,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); } } @@ -1296,6 +1315,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); } @@ -1320,13 +1340,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); } /* @@ -1337,14 +1358,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 */ @@ -1474,3 +1496,89 @@ 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); +} + +/* 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); +}