Merge pull request #417 from tatsuhiro-t/evp-ticket-funcs

Add ticket functions which use OpenSSL v3 EVP_MAC_CTX
diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
index 867f5d5..ee32f86 100644
--- a/include/picotls/openssl.h
+++ b/include/picotls/openssl.h
@@ -140,6 +140,13 @@
 int ptls_openssl_decrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src,
                                 int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int));
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+int ptls_openssl_encrypt_ticket_evp(ptls_buffer_t *dst, ptls_iovec_t src,
+                                    int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, EVP_MAC_CTX *, int));
+int ptls_openssl_decrypt_ticket_evp(ptls_buffer_t *dst, ptls_iovec_t src,
+                                    int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, EVP_MAC_CTX *, int));
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/openssl.c b/lib/openssl.c
index b83dc36..29d5262 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -1565,6 +1565,157 @@
     return ret;
 }
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+int ptls_openssl_encrypt_ticket_evp(ptls_buffer_t *buf, ptls_iovec_t src,
+                                    int (*cb)(unsigned char *key_name, unsigned char *iv, EVP_CIPHER_CTX *ctx, EVP_MAC_CTX *hctx,
+                                              int enc))
+{
+    EVP_CIPHER_CTX *cctx = NULL;
+    EVP_MAC *mac = NULL;
+    EVP_MAC_CTX *hctx = NULL;
+    size_t hlen;
+    uint8_t *dst;
+    int clen, ret;
+
+    if ((cctx = EVP_CIPHER_CTX_new()) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+    if ((mac = EVP_MAC_fetch(NULL, "HMAC", NULL)) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+    if ((hctx = EVP_MAC_CTX_new(mac)) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+
+    if ((ret = ptls_buffer_reserve(buf, TICKET_LABEL_SIZE + TICKET_IV_SIZE + src.len + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE)) !=
+        0)
+        goto Exit;
+    dst = buf->base + buf->off;
+
+    /* fill label and iv, as well as obtaining the keys */
+    if (!(*cb)(dst, dst + TICKET_LABEL_SIZE, cctx, hctx, 1)) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    dst += TICKET_LABEL_SIZE + TICKET_IV_SIZE;
+
+    /* encrypt */
+    if (!EVP_EncryptUpdate(cctx, dst, &clen, src.base, (int)src.len)) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    dst += clen;
+    if (!EVP_EncryptFinal_ex(cctx, dst, &clen)) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    dst += clen;
+
+    /* append hmac */
+    if (!EVP_MAC_update(hctx, buf->base + buf->off, dst - (buf->base + buf->off)) ||
+        !EVP_MAC_final(hctx, dst, &hlen, EVP_MAC_CTX_get_mac_size(hctx))) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    dst += hlen;
+
+    assert(dst <= buf->base + buf->capacity);
+    buf->off += dst - (buf->base + buf->off);
+    ret = 0;
+
+Exit:
+    if (cctx != NULL)
+        EVP_CIPHER_CTX_free(cctx);
+    if (hctx != NULL)
+        EVP_MAC_CTX_free(hctx);
+    if (mac != NULL)
+        EVP_MAC_free(mac);
+    return ret;
+}
+
+int ptls_openssl_decrypt_ticket_evp(ptls_buffer_t *buf, ptls_iovec_t src,
+                                    int (*cb)(unsigned char *key_name, unsigned char *iv, EVP_CIPHER_CTX *ctx, EVP_MAC_CTX *hctx,
+                                              int enc))
+{
+    EVP_CIPHER_CTX *cctx = NULL;
+    EVP_MAC *mac = NULL;
+    EVP_MAC_CTX *hctx = NULL;
+    size_t hlen;
+    int clen, ret;
+
+    if ((cctx = EVP_CIPHER_CTX_new()) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+    if ((mac = EVP_MAC_fetch(NULL, "HMAC", NULL)) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+    if ((hctx = EVP_MAC_CTX_new(mac)) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+
+    /* obtain cipher and hash context.
+     * Note: no need to handle renew, since in picotls we always send a new ticket to minimize the chance of ticket reuse */
+    if (src.len < TICKET_LABEL_SIZE + TICKET_IV_SIZE) {
+        ret = PTLS_ALERT_DECODE_ERROR;
+        goto Exit;
+    }
+    if (!(*cb)(src.base, src.base + TICKET_LABEL_SIZE, cctx, hctx, 0)) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+
+    /* check hmac, and exclude label, iv, hmac */
+    size_t hmac_size = EVP_MAC_CTX_get_mac_size(hctx);
+    if (src.len < TICKET_LABEL_SIZE + TICKET_IV_SIZE + hmac_size) {
+        ret = PTLS_ALERT_DECODE_ERROR;
+        goto Exit;
+    }
+    src.len -= hmac_size;
+    uint8_t hmac[EVP_MAX_MD_SIZE];
+    if (!EVP_MAC_update(hctx, src.base, src.len) || !EVP_MAC_final(hctx, hmac, &hlen, sizeof(hmac))) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    if (!ptls_mem_equal(src.base + src.len, hmac, hmac_size)) {
+        ret = PTLS_ALERT_HANDSHAKE_FAILURE;
+        goto Exit;
+    }
+    src.base += TICKET_LABEL_SIZE + TICKET_IV_SIZE;
+    src.len -= TICKET_LABEL_SIZE + TICKET_IV_SIZE;
+
+    /* decrypt */
+    if ((ret = ptls_buffer_reserve(buf, src.len)) != 0)
+        goto Exit;
+    if (!EVP_DecryptUpdate(cctx, buf->base + buf->off, &clen, src.base, (int)src.len)) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    buf->off += clen;
+    if (!EVP_DecryptFinal_ex(cctx, buf->base + buf->off, &clen)) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    buf->off += clen;
+
+    ret = 0;
+
+Exit:
+    if (cctx != NULL)
+        EVP_CIPHER_CTX_free(cctx);
+    if (hctx != NULL)
+        EVP_MAC_CTX_free(hctx);
+    if (mac != NULL)
+        EVP_MAC_free(mac);
+    return ret;
+}
+#endif
+
 ptls_key_exchange_algorithm_t ptls_openssl_secp256r1 = {.id = PTLS_GROUP_SECP256R1,
                                                         .name = PTLS_GROUP_NAME_SECP256R1,
                                                         .create = x9_62_create_key_exchange,