/** Returns the OpenSSL keyblock size * * @copyright (c) 2002-2016, Jouni Malinen <*****@*****.**> and contributors * All Rights Reserved. * * These programs are licensed under the BSD license (the one with * advertisement clause removed). * * this function shamelessly stolen from from * hostap:src/crypto/tls_openssl.c:openssl_get_keyblock_size() * * @param[in] request The current request. * @param[in] ssl The current SSL session. * @return * - -1 problem with the session. * - >=0 length of the block. */ int tls_utils_keyblock_size_get(REQUEST *request, SSL *ssl) { const EVP_CIPHER *c; const EVP_MD *h; #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) int md_size; if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL || ssl->read_hash == NULL) return -1; c = ssl->enc_read_ctx->cipher; h = EVP_MD_CTX_md(ssl->read_hash); if (h) md_size = EVP_MD_size(h); else if (ssl->s3) md_size = ssl->s3->tmp.new_mac_secret_size; else return -1; RDEBUG2("OpenSSL: keyblock size: key_len=%d MD_size=%d " "IV_len=%d", EVP_CIPHER_key_length(c), md_size, EVP_CIPHER_iv_length(c)); return 2 * (EVP_CIPHER_key_length(c) + md_size + EVP_CIPHER_iv_length(c)); #else const SSL_CIPHER *ssl_cipher; int cipher, digest; ssl_cipher = SSL_get_current_cipher(ssl); if (!ssl_cipher) return -1; cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher); digest = SSL_CIPHER_get_digest_nid(ssl_cipher); RDEBUG2("OpenSSL: cipher nid %d digest nid %d", cipher, digest); if (cipher < 0 || digest < 0) return -1; c = EVP_get_cipherbynid(cipher); h = EVP_get_digestbynid(digest); if (!c || !h) return -1; RDEBUG2("OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d", EVP_CIPHER_key_length(c), EVP_MD_size(h), EVP_CIPHER_iv_length(c)); return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) + EVP_CIPHER_iv_length(c)); #endif }
int tls13_change_cipher_state(SSL *s, int which) { static const unsigned char client_early_traffic[] = "c e traffic"; static const unsigned char client_handshake_traffic[] = "c hs traffic"; static const unsigned char client_application_traffic[] = "c ap traffic"; static const unsigned char server_handshake_traffic[] = "s hs traffic"; static const unsigned char server_application_traffic[] = "s ap traffic"; static const unsigned char exporter_master_secret[] = "exp master"; static const unsigned char resumption_master_secret[] = "res master"; static const unsigned char early_exporter_master_secret[] = "e exp master"; unsigned char *iv; unsigned char secret[EVP_MAX_MD_SIZE]; unsigned char hashval[EVP_MAX_MD_SIZE]; unsigned char *hash = hashval; unsigned char *insecret; unsigned char *finsecret = NULL; const char *log_label = NULL; EVP_CIPHER_CTX *ciph_ctx; size_t finsecretlen = 0; const unsigned char *label; size_t labellen, hashlen = 0; int ret = 0; const EVP_MD *md = NULL; const EVP_CIPHER *cipher = NULL; if (which & SSL3_CC_READ) { if (s->enc_read_ctx != NULL) { EVP_CIPHER_CTX_reset(s->enc_read_ctx); } else { s->enc_read_ctx = EVP_CIPHER_CTX_new(); if (s->enc_read_ctx == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_MALLOC_FAILURE); goto err; } } ciph_ctx = s->enc_read_ctx; iv = s->read_iv; RECORD_LAYER_reset_read_sequence(&s->rlayer); } else { s->statem.enc_write_state = ENC_WRITE_STATE_INVALID; if (s->enc_write_ctx != NULL) { EVP_CIPHER_CTX_reset(s->enc_write_ctx); } else { s->enc_write_ctx = EVP_CIPHER_CTX_new(); if (s->enc_write_ctx == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_MALLOC_FAILURE); goto err; } } ciph_ctx = s->enc_write_ctx; iv = s->write_iv; RECORD_LAYER_reset_write_sequence(&s->rlayer); } if (((which & SSL3_CC_CLIENT) && (which & SSL3_CC_WRITE)) || ((which & SSL3_CC_SERVER) && (which & SSL3_CC_READ))) { if (which & SSL3_CC_EARLY) { EVP_MD_CTX *mdctx = NULL; long handlen; void *hdata; unsigned int hashlenui; const SSL_CIPHER *sslcipher = SSL_SESSION_get0_cipher(s->session); insecret = s->early_secret; label = client_early_traffic; labellen = sizeof(client_early_traffic) - 1; log_label = CLIENT_EARLY_LABEL; handlen = BIO_get_mem_data(s->s3->handshake_buffer, &hdata); if (handlen <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_CHANGE_CIPHER_STATE, SSL_R_BAD_HANDSHAKE_LENGTH); goto err; } if (s->early_data_state == SSL_EARLY_DATA_CONNECTING && s->max_early_data > 0 && s->session->ext.max_early_data == 0) { /* * If we are attempting to send early data, and we've decided to * actually do it but max_early_data in s->session is 0 then we * must be using an external PSK. */ if (!ossl_assert(s->psksession != NULL && s->max_early_data == s->psksession->ext.max_early_data)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR); goto err; } sslcipher = SSL_SESSION_get0_cipher(s->psksession); } if (sslcipher == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_CHANGE_CIPHER_STATE, SSL_R_BAD_PSK); goto err; } /* * We need to calculate the handshake digest using the digest from * the session. We haven't yet selected our ciphersuite so we can't * use ssl_handshake_md(). */ mdctx = EVP_MD_CTX_new(); if (mdctx == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_MALLOC_FAILURE); goto err; } cipher = EVP_get_cipherbynid(SSL_CIPHER_get_cipher_nid(sslcipher)); md = ssl_md(sslcipher->algorithm2); if (md == NULL || !EVP_DigestInit_ex(mdctx, md, NULL) || !EVP_DigestUpdate(mdctx, hdata, handlen) || !EVP_DigestFinal_ex(mdctx, hashval, &hashlenui)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR); EVP_MD_CTX_free(mdctx); goto err; } hashlen = hashlenui; EVP_MD_CTX_free(mdctx); if (!tls13_hkdf_expand(s, md, insecret, early_exporter_master_secret, sizeof(early_exporter_master_secret) - 1, hashval, hashlen, s->early_exporter_master_secret, hashlen, 1)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR); goto err; } if (!ssl_log_secret(s, EARLY_EXPORTER_SECRET_LABEL, s->early_exporter_master_secret, hashlen)) { /* SSLfatal() already called */ goto err; } } else if (which & SSL3_CC_HANDSHAKE) { insecret = s->handshake_secret; finsecret = s->client_finished_secret; finsecretlen = EVP_MD_size(ssl_handshake_md(s)); label = client_handshake_traffic; labellen = sizeof(client_handshake_traffic) - 1; log_label = CLIENT_HANDSHAKE_LABEL; /* * The handshake hash used for the server read/client write handshake * traffic secret is the same as the hash for the server * write/client read handshake traffic secret. However, if we * processed early data then we delay changing the server * read/client write cipher state until later, and the handshake * hashes have moved on. Therefore we use the value saved earlier * when we did the server write/client read change cipher state. */ hash = s->handshake_traffic_hash; } else { insecret = s->master_secret; label = client_application_traffic; labellen = sizeof(client_application_traffic) - 1; log_label = CLIENT_APPLICATION_LABEL; /* * For this we only use the handshake hashes up until the server * Finished hash. We do not include the client's Finished, which is * what ssl_handshake_hash() would give us. Instead we use the * previously saved value. */ hash = s->server_finished_hash; } } else { /* Early data never applies to client-read/server-write */ if (which & SSL3_CC_HANDSHAKE) { insecret = s->handshake_secret; finsecret = s->server_finished_secret; finsecretlen = EVP_MD_size(ssl_handshake_md(s)); label = server_handshake_traffic; labellen = sizeof(server_handshake_traffic) - 1; log_label = SERVER_HANDSHAKE_LABEL; } else { insecret = s->master_secret; label = server_application_traffic; labellen = sizeof(server_application_traffic) - 1; log_label = SERVER_APPLICATION_LABEL; } } if (!(which & SSL3_CC_EARLY)) { md = ssl_handshake_md(s); cipher = s->s3->tmp.new_sym_enc; if (!ssl3_digest_cached_records(s, 1) || !ssl_handshake_hash(s, hashval, sizeof(hashval), &hashlen)) { /* SSLfatal() already called */; goto err; } } /* * Save the hash of handshakes up to now for use when we calculate the * client application traffic secret */ if (label == server_application_traffic) memcpy(s->server_finished_hash, hashval, hashlen); if (label == server_handshake_traffic) memcpy(s->handshake_traffic_hash, hashval, hashlen); if (label == client_application_traffic) { /* * We also create the resumption master secret, but this time use the * hash for the whole handshake including the Client Finished */ if (!tls13_hkdf_expand(s, ssl_handshake_md(s), insecret, resumption_master_secret, sizeof(resumption_master_secret) - 1, hashval, hashlen, s->resumption_master_secret, hashlen, 1)) { /* SSLfatal() already called */ goto err; } } if (!derive_secret_key_and_iv(s, which & SSL3_CC_WRITE, md, cipher, insecret, hash, label, labellen, secret, iv, ciph_ctx)) { /* SSLfatal() already called */ goto err; } if (label == server_application_traffic) { memcpy(s->server_app_traffic_secret, secret, hashlen); /* Now we create the exporter master secret */ if (!tls13_hkdf_expand(s, ssl_handshake_md(s), insecret, exporter_master_secret, sizeof(exporter_master_secret) - 1, hash, hashlen, s->exporter_master_secret, hashlen, 1)) { /* SSLfatal() already called */ goto err; } if (!ssl_log_secret(s, EXPORTER_SECRET_LABEL, s->exporter_master_secret, hashlen)) { /* SSLfatal() already called */ goto err; } } else if (label == client_application_traffic) memcpy(s->client_app_traffic_secret, secret, hashlen); if (!ssl_log_secret(s, log_label, secret, hashlen)) { /* SSLfatal() already called */ goto err; } if (finsecret != NULL && !tls13_derive_finishedkey(s, ssl_handshake_md(s), secret, finsecret, finsecretlen)) { /* SSLfatal() already called */ goto err; } if (!s->server && label == client_early_traffic) s->statem.enc_write_state = ENC_WRITE_STATE_WRITE_PLAIN_ALERTS; else s->statem.enc_write_state = ENC_WRITE_STATE_VALID; ret = 1; err: OPENSSL_cleanse(secret, sizeof(secret)); return ret; }
/* This equivalent functionality was submitted for OpenSSL 1.1.1+ in * https://github.com/openssl/openssl/pull/1666 */ static int dtls_get_data_mtu(struct openconnect_info *vpninfo, int mtu) { int ivlen, maclen, blocksize = 0, pad = 0; #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) const SSL_CIPHER *s_ciph = SSL_get_current_cipher(vpninfo->dtls_ssl); int cipher_nid; const EVP_CIPHER *e_ciph; const EVP_MD *e_md; char wtf[128]; cipher_nid = SSL_CIPHER_get_cipher_nid(s_ciph); if (cipher_nid == NID_chacha20_poly1305) { ivlen = 0; /* Automatically derived from handshake and seqno */ maclen = 16; /* Poly1305 */ } else { e_ciph = EVP_get_cipherbynid(cipher_nid); switch (EVP_CIPHER_mode(e_ciph)) { case EVP_CIPH_GCM_MODE: ivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN; maclen = EVP_GCM_TLS_TAG_LEN; break; case EVP_CIPH_CCM_MODE: ivlen = EVP_CCM_TLS_EXPLICIT_IV_LEN; SSL_CIPHER_description(s_ciph, wtf, sizeof(wtf)); if (strstr(wtf, "CCM8")) maclen = 8; else maclen = 16; break; case EVP_CIPH_CBC_MODE: blocksize = EVP_CIPHER_block_size(e_ciph); ivlen = EVP_CIPHER_iv_length(e_ciph); pad = 1; e_md = EVP_get_digestbynid(SSL_CIPHER_get_digest_nid(s_ciph)); maclen = EVP_MD_size(e_md); break; default: vpn_progress(vpninfo, PRG_ERR, _("Unable to calculate DTLS overhead for %s\n"), SSL_CIPHER_get_name(s_ciph)); ivlen = 0; maclen = DTLS_OVERHEAD; break; } } #else /* OpenSSL <= 1.0.2 only supports CBC ciphers with PSK */ ivlen = EVP_CIPHER_iv_length(EVP_CIPHER_CTX_cipher(vpninfo->dtls_ssl->enc_read_ctx)); maclen = EVP_MD_CTX_size(vpninfo->dtls_ssl->read_hash); blocksize = ivlen; pad = 1; #endif /* Even when it pretended to, OpenSSL never did encrypt-then-mac. * So the MAC is *inside* the encryption, unconditionally. * https://github.com/openssl/openssl/pull/1705 */ if (mtu < DTLS1_RT_HEADER_LENGTH + ivlen) return 0; mtu -= DTLS1_RT_HEADER_LENGTH + ivlen; /* For CBC mode round down to blocksize */ if (blocksize) mtu -= mtu % blocksize; /* Finally, CBC modes require at least one byte to indicate * padding length, as well as the MAC. */ if (mtu < pad + maclen) return 0; mtu -= pad + maclen; return mtu; }