int quic_tls_encrypt(unsigned char *buf, size_t len,
const unsigned char *aad, size_t aad_len,
- const EVP_CIPHER *aead,
+ EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead,
const unsigned char *key, const unsigned char *iv);
int quic_tls_decrypt(unsigned char *buf, size_t len,
unsigned char *aad, size_t aad_len,
- const EVP_CIPHER *aead,
+ EVP_CIPHER_CTX *tls_ctx, const EVP_CIPHER *aead,
const unsigned char *key, const unsigned char *iv);
int quic_tls_generate_retry_integrity_tag(unsigned char *odcid, size_t odcid_len,
unsigned char *hp_key, size_t hp_keylen,
const unsigned char *secret, size_t secretlen);
+int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx,
+ const EVP_CIPHER *aead, unsigned char *key);
+int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx,
+ const EVP_CIPHER *aead, unsigned char *key);
+
int quic_tls_sec_update(const EVP_MD *md,
unsigned char *new_sec, size_t new_seclen,
const unsigned char *sec, size_t seclen);
memset(ctx->tx.key, 0, ctx->tx.keylen);
ctx->tx.keylen = 0;
}
+
+ EVP_CIPHER_CTX_free(ctx->rx.ctx);
pool_free(pool_head_quic_tls_iv, ctx->rx.iv);
pool_free(pool_head_quic_tls_key, ctx->rx.key);
+
+ EVP_CIPHER_CTX_free(ctx->tx.ctx);
pool_free(pool_head_quic_tls_iv, ctx->tx.iv);
pool_free(pool_head_quic_tls_key, ctx->tx.key);
+
ctx->rx.iv = ctx->tx.iv = NULL;
ctx->rx.key = ctx->tx.key = NULL;
}
rx_init_sec, sizeof rx_init_sec))
goto err;
+ if (!quic_tls_rx_ctx_init(&rx_ctx->ctx, rx_ctx->aead, rx_ctx->key))
+ goto err;
+
if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md,
tx_ctx->key, tx_ctx->keylen,
tx_ctx->iv, tx_ctx->ivlen,
tx_init_sec, sizeof tx_init_sec))
goto err;
+ if (!quic_tls_tx_ctx_init(&tx_ctx->ctx, tx_ctx->aead, tx_ctx->key))
+ goto err;
+
ctx->flags |= QUIC_FL_TLS_SECRETS_SET;
TRACE_LEAVE(QUIC_EV_CONN_ISEC, NULL, rx_init_sec, tx_init_sec);
return 1;
}
+/* Initialize the cipher context for RX part of <tls_ctx> QUIC TLS context.
+ * Return 1 if succeeded, 0 if not.
+ */
+int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx,
+ const EVP_CIPHER *aead, unsigned char *key)
+{
+ EVP_CIPHER_CTX *ctx;
+ int aead_nid = EVP_CIPHER_nid(aead);
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (!ctx)
+ return 0;
+
+ if (!EVP_DecryptInit_ex(ctx, aead, NULL, NULL, NULL) ||
+ !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL) ||
+ (aead_nid == NID_aes_128_ccm &&
+ !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN, NULL)) ||
+ !EVP_DecryptInit_ex(ctx, NULL, NULL, key, NULL))
+ goto err;
+
+ *rx_ctx = ctx;
+
+ return 1;
+
+ err:
+ EVP_CIPHER_CTX_free(ctx);
+ return 0;
+}
+
+/* Initialize the cipher context for TX part of <tls_ctx> QUIC TLS context.
+ * Return 1 if succeeded, 0 if not.
+ */
+int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx,
+ const EVP_CIPHER *aead, unsigned char *key)
+{
+ EVP_CIPHER_CTX *ctx;
+ int aead_nid = EVP_CIPHER_nid(aead);
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (!ctx)
+ return 0;
+
+ if (!EVP_EncryptInit_ex(ctx, aead, NULL, NULL, NULL) ||
+ !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL) ||
+ (aead_nid == NID_aes_128_ccm &&
+ !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN, NULL)) ||
+ !EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL))
+ goto err;
+
+ *tx_ctx = ctx;
+
+ return 1;
+
+ err:
+ EVP_CIPHER_CTX_free(ctx);
+ return 0;
+}
+
/*
* https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#aead
*
* the AEAD that is in use.
*/
+/* Encrypt in place <buf> plaintext with <len> as length with QUIC_TLS_TAG_LEN
+ * included tailing bytes for the tag.
+ * Note that for CCM mode, we must set the the ciphertext length if AAD data
+ * are provided from <aad> buffer with <aad_len> as length. This is always the
+ * case here. So the caller of this function must provide <aad>.
+ *
+ * https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
+ */
int quic_tls_encrypt(unsigned char *buf, size_t len,
const unsigned char *aad, size_t aad_len,
- const EVP_CIPHER *aead, const unsigned char *key, const unsigned char *iv)
+ EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead,
+ const unsigned char *key, const unsigned char *iv)
{
- EVP_CIPHER_CTX *ctx;
- int ret, outlen;
-
- ret = 0;
- ctx = EVP_CIPHER_CTX_new();
- if (!ctx)
- return 0;
+ int outlen;
+ int aead_nid = EVP_CIPHER_nid(aead);
- if (!EVP_EncryptInit_ex(ctx, aead, NULL, key, iv) ||
+ if (!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv) ||
+ (aead_nid == NID_aes_128_ccm &&
+ !EVP_EncryptUpdate(ctx, NULL, &outlen, NULL, len)) ||
!EVP_EncryptUpdate(ctx, NULL, &outlen, aad, aad_len) ||
!EVP_EncryptUpdate(ctx, buf, &outlen, buf, len) ||
!EVP_EncryptFinal_ex(ctx, buf + outlen, &outlen) ||
!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, QUIC_TLS_TAG_LEN, buf + len))
- goto out;
-
- ret = 1;
-
- out:
- EVP_CIPHER_CTX_free(ctx);
+ return 0;
- return ret;
+ return 1;
}
+/* Decrypt in place <buf> ciphertext with <len> as length with QUIC_TLS_TAG_LEN
+ * included tailing bytes for the tag.
+ * Note that for CCM mode, we must set the the ciphertext length if AAD data
+ * are provided from <aad> buffer with <aad_len> as length. This is always the
+ * case here. So the caller of this function must provide <aad>. Also not the
+ * there is no need to call EVP_DecryptFinal_ex for CCM mode.
+ *
+ * https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
+ */
int quic_tls_decrypt(unsigned char *buf, size_t len,
unsigned char *aad, size_t aad_len,
- const EVP_CIPHER *aead, const unsigned char *key, const unsigned char *iv)
+ EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead,
+ const unsigned char *key, const unsigned char *iv)
{
- int ret, outlen;
- size_t off;
- EVP_CIPHER_CTX *ctx;
+ int outlen;
+ int aead_nid = EVP_CIPHER_nid(aead);
- ret = 0;
- off = 0;
- ctx = EVP_CIPHER_CTX_new();
- if (!ctx)
- return 0;
-
- if (!EVP_DecryptInit_ex(ctx, aead, NULL, key, iv) ||
- !EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_len) ||
- !EVP_DecryptUpdate(ctx, buf, &outlen, buf, len - QUIC_TLS_TAG_LEN))
- goto out;
-
- off += outlen;
-
- if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN,
+ if (!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv) ||
+ !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN,
buf + len - QUIC_TLS_TAG_LEN) ||
- !EVP_DecryptFinal_ex(ctx, buf + off, &outlen))
- goto out;
-
- off += outlen;
-
- ret = off;
+ (aead_nid == NID_aes_128_ccm &&
+ !EVP_DecryptUpdate(ctx, NULL, &outlen, NULL, len - QUIC_TLS_TAG_LEN)) ||
+ !EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_len) ||
+ !EVP_DecryptUpdate(ctx, buf, &outlen, buf, len - QUIC_TLS_TAG_LEN) ||
+ (aead_nid != NID_aes_128_ccm &&
+ !EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen)))
+ return 0;
- out:
- EVP_CIPHER_CTX_free(ctx);
- return ret;
+ return 1;
}
/* Generate the AEAD tag for the Retry packet <pkt> of <pkt_len> bytes and
goto err;
}
+ if (!quic_tls_rx_ctx_init(&rx->ctx, rx->aead, rx->key)) {
+ TRACE_DEVEL("could not initial RX TLS cipher context", QUIC_EV_CONN_RWSEC, qc);
+ goto err;
+ }
+
/* Enqueue this connection asap if we could derive O-RTT secrets as
* listener. Note that a listener derives only RX secrets for this
* level.
goto err;
}
+ if (!quic_tls_tx_ctx_init(&tx->ctx, tx->aead, tx->key)) {
+ TRACE_DEVEL("could not initial RX TLS cipher context", QUIC_EV_CONN_RWSEC, qc);
+ goto err;
+ }
+
if (level == ssl_encryption_application) {
struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
}
if (!quic_tls_encrypt(payload, payload_len, aad, aad_len,
- tls_ctx->tx.aead, tls_ctx->tx.key, iv)) {
+ tls_ctx->tx.ctx, tls_ctx->tx.aead, tls_ctx->tx.key, iv)) {
TRACE_DEVEL("QUIC packet encryption failed", QUIC_EV_CONN_HPKT, qc);
goto err;
}
ret = quic_tls_decrypt(pkt->data + pkt->aad_len, pkt->len - pkt->aad_len,
pkt->data, pkt->aad_len,
- tls_ctx->rx.aead, rx_key, iv);
+ tls_ctx->rx.ctx, tls_ctx->rx.aead, rx_key, iv);
if (!ret)
return 0;
}
/* Update the packet length (required to parse the frames). */
- pkt->len = pkt->aad_len + ret;
+ pkt->len -= QUIC_TLS_TAG_LEN;
return 1;
}