Merge pull request #467 from h2o/kazuho/boring

add support for boringssl
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8b538ad..02cd626 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,6 +23,8 @@
             command: make -f misc/docker-ci.mk CONTAINER_NAME=h2oserver/h2o-ci:ubuntu2204
           - name: "Linux / OpenSSL 1.1.1 + ASan & UBSan"
             command: make -f misc/docker-ci.mk CMAKE_ARGS='-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_FLAGS=-fsanitize=address,undefined -DCMAKE_CXX_FLAGS=-fsanitize=address,undefined' CHECK_ENVS='ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1'
+          - name: "Linux / boringssl"
+            command: make -f misc/docker-ci.mk CONTAINER_NAME=h2oserver/h2o-ci:ubuntu2204 CMAKE_ARGS='-DOPENSSL_ROOT_DIR=/opt/boringssl'
 
     timeout-minutes: 20
     steps:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3af816d..5ad42d8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,6 +11,7 @@
 
 FIND_PACKAGE(PkgConfig REQUIRED)
 INCLUDE(cmake/dtrace-utils.cmake)
+INCLUDE(cmake/boringssl-adjust.cmake)
 
 CHECK_DTRACE(${PROJECT_SOURCE_DIR}/picotls-probes.d)
 IF ((CMAKE_SIZEOF_VOID_P EQUAL 8) AND
@@ -120,6 +121,8 @@
     picotls-minicrypto picotls-core)
 
 FIND_PACKAGE(OpenSSL)
+BORINGSSL_ADJUST()
+
 IF (OPENSSL_FOUND AND NOT (OPENSSL_VERSION VERSION_LESS "1.0.1"))
     MESSAGE(STATUS "  Enabling OpenSSL support")
     INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
diff --git a/cmake/boringssl-adjust.cmake b/cmake/boringssl-adjust.cmake
new file mode 100644
index 0000000..4a2aef6
--- /dev/null
+++ b/cmake/boringssl-adjust.cmake
@@ -0,0 +1,16 @@
+FUNCTION (BORINGSSL_ADJUST)
+    IF (OPENSSL_FOUND AND OPENSSL_VERSION STREQUAL "" AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/base.h")
+        MESSAGE(STATUS "  BoringSSL found; assuming OpenSSL 1.1.1 compatibility")
+        SET(OPENSSL_VERSION "1.1.1" PARENT_SCOPE)
+        LIST(GET OPENSSL_CRYPTO_LIBRARIES 0 OPENSSL_ONE_LIB_PATH)
+        GET_FILENAME_COMPONENT(OPENSSL_LIBDIR "${OPENSSL_ONE_LIB_PATH}" DIRECTORY)
+        SET(LIBDECREPIT_PATH "${OPENSSL_LIBDIR}/libdecrepit.a")
+        IF (NOT EXISTS "${LIBDECREPIT_PATH}")
+            MESSAGE(FATAL_ERROR "libdecrepit.a was not found under ${OPENSSL_LIBDIR}; maybe you need to manually copy the file there")
+        ENDIF ()
+        LIST(APPEND OPENSSL_CRYPTO_LIBRARIES "${LIBDECREPIT_PATH}")
+        SET(OPENSSL_CRYPTO_LIBRARIES "${OPENSSL_CRYPTO_LIBRARIES}" PARENT_SCOPE)
+        LIST(APPEND OPENSSL_LIBRARIES "${LIBDECREPIT_PATH}")
+        SET(OPENSSL_LIBRARIES "${OPENSSL_LIBRARIES}" PARENT_SCOPE)
+    ENDIF ()
+ENDFUNCTION ()
diff --git a/include/picotls.h b/include/picotls.h
index 420861f..0cadb17 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -85,7 +85,7 @@
 #define PTLS_AESCCM_INTEGRITY_LIMIT 0xB504F3                   /* 2^23.5 */
 
 #define PTLS_CHACHA20_KEY_SIZE 32
-#define PTLS_CHACHA20_IV_SIZE 16
+#define PTLS_CHACHA20_IV_SIZE 16 /* contrary to RFC 7539, follow OpenSSL way of using first 32 bits as ctr and latter 96 as IV */
 #define PTLS_CHACHA20POLY1305_IV_SIZE 12
 #define PTLS_CHACHA20POLY1305_TAG_SIZE 16
 #define PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT UINT64_MAX       /* at least 2^64 */
diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
index a892f9b..e0761ed 100644
--- a/include/picotls/openssl.h
+++ b/include/picotls/openssl.h
@@ -65,7 +65,9 @@
 #define PTLS_OPENSSL_HAS_X25519 1 /* deprecated; use HAVE_ */
 extern ptls_key_exchange_algorithm_t ptls_openssl_x25519;
 #endif
-#ifndef OPENSSL_NO_BF
+
+/* when boringssl is used, existence of libdecrepit is assumed */
+#if !defined(OPENSSL_NO_BF) || defined(OPENSSL_IS_BORINGSSL)
 #define PTLS_OPENSSL_HAVE_BF 1
 #endif
 
diff --git a/lib/chacha20poly1305.h b/lib/chacha20poly1305.h
new file mode 100644
index 0000000..ad2d7fe
--- /dev/null
+++ b/lib/chacha20poly1305.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016-2023 DeNA Co., Ltd., Kazuho Oku, Lars Eggert, Christian
+                           Huitema, Fastly
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include <stddef.h>
+#include "picotls.h"
+
+#define CHACHA20POLY1305_BLOCKSIZE 64
+
+struct chacha20poly1305_context_t {
+    ptls_aead_context_t super;
+    ptls_cipher_context_t *chacha;
+    uint8_t static_iv[PTLS_CHACHA20POLY1305_IV_SIZE];
+    size_t aadlen;
+    size_t textlen;
+    void (*poly1305_init)(struct chacha20poly1305_context_t *, const void *);
+    void (*poly1305_update)(struct chacha20poly1305_context_t *, const void *, size_t);
+    void (*poly1305_finish)(struct chacha20poly1305_context_t *, void *);
+};
+
+static void chacha20poly1305_write_u64(uint8_t *buf, uint64_t v)
+{
+    *buf++ = v & 0xff;
+    *buf++ = (v >> 8) & 0xff;
+    *buf++ = (v >> 16) & 0xff;
+    *buf++ = (v >> 24) & 0xff;
+    *buf++ = (v >> 32) & 0xff;
+    *buf++ = (v >> 40) & 0xff;
+    *buf++ = (v >> 48) & 0xff;
+    *buf = (v >> 56) & 0xff;
+}
+
+static void chacha20poly1305_encrypt_pad(struct chacha20poly1305_context_t *ctx, size_t n)
+{
+    static const uint8_t zeros[16] = {0};
+    if (n % 16 != 0)
+        ctx->poly1305_update(ctx, zeros, 16 - (n % 16));
+}
+
+static void chacha20poly1305_finalize(struct chacha20poly1305_context_t *ctx, uint8_t *tag)
+{
+    uint8_t lenbuf[16];
+
+    chacha20poly1305_encrypt_pad(ctx, ctx->textlen);
+
+    chacha20poly1305_write_u64(lenbuf, ctx->aadlen);
+    chacha20poly1305_write_u64(lenbuf + 8, ctx->textlen);
+    ctx->poly1305_update(ctx, lenbuf, sizeof(lenbuf));
+
+    ctx->poly1305_finish(ctx, tag);
+}
+
+static void chacha20poly1305_dispose_crypto(ptls_aead_context_t *_ctx)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+    ptls_cipher_free(ctx->chacha);
+}
+
+static void chacha20poly1305_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+    uint8_t tmpbuf[CHACHA20POLY1305_BLOCKSIZE];
+
+    /* init chacha */
+    memset(tmpbuf, 0, 16 - PTLS_CHACHA20POLY1305_IV_SIZE);
+    ptls_aead__build_iv(ctx->super.algo, tmpbuf + 16 - PTLS_CHACHA20POLY1305_IV_SIZE, ctx->static_iv, seq);
+    ptls_cipher_init(ctx->chacha, tmpbuf);
+
+    /* init poly1305 */
+    memset(tmpbuf, 0, sizeof(tmpbuf));
+    ptls_cipher_encrypt(ctx->chacha, tmpbuf, tmpbuf, CHACHA20POLY1305_BLOCKSIZE);
+    ctx->poly1305_init(ctx, tmpbuf);
+
+    ptls_clear_memory(tmpbuf, sizeof(tmpbuf));
+
+    /* aad */
+    if (aadlen != 0) {
+        ctx->poly1305_update(ctx, aad, aadlen);
+        chacha20poly1305_encrypt_pad(ctx, aadlen);
+    }
+
+    ctx->aadlen = aadlen;
+    ctx->textlen = 0;
+}
+
+static size_t chacha20poly1305_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+
+    ptls_cipher_encrypt(ctx->chacha, output, input, inlen);
+    ctx->poly1305_update(ctx, output, inlen);
+    ctx->textlen += inlen;
+
+    return inlen;
+}
+
+static size_t chacha20poly1305_encrypt_final(ptls_aead_context_t *_ctx, void *output)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+
+    chacha20poly1305_finalize(ctx, output);
+
+    return PTLS_CHACHA20POLY1305_TAG_SIZE;
+}
+
+static size_t chacha20poly1305_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq,
+                                       const void *aad, size_t aadlen)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+    uint8_t tag[PTLS_CHACHA20POLY1305_TAG_SIZE];
+    size_t ret;
+
+    if (inlen < sizeof(tag))
+        return SIZE_MAX;
+
+    chacha20poly1305_init(&ctx->super, seq, aad, aadlen);
+
+    ctx->poly1305_update(ctx, input, inlen - sizeof(tag));
+    ctx->textlen = inlen - sizeof(tag);
+
+    chacha20poly1305_finalize(ctx, tag);
+    if (ptls_mem_equal(tag, (const uint8_t *)input + inlen - sizeof(tag), sizeof(tag))) {
+        ptls_cipher_encrypt(ctx->chacha, output, input, inlen - sizeof(tag));
+        ret = inlen - sizeof(tag);
+    } else {
+        ret = SIZE_MAX;
+    }
+
+    ptls_clear_memory(tag, sizeof(tag));
+
+    return ret;
+}
+
+static void chacha20poly1305_get_iv(ptls_aead_context_t *_ctx, void *iv)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+
+    memcpy(iv, ctx->static_iv, sizeof(ctx->static_iv));
+}
+
+static void chacha20poly1305_set_iv(ptls_aead_context_t *_ctx, const void *iv)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+
+    memcpy(ctx->static_iv, iv, sizeof(ctx->static_iv));
+ }
+
+static int chacha20poly1305_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv,
+                                         ptls_cipher_algorithm_t *chacha,
+                                         void (*poly1305_init)(struct chacha20poly1305_context_t *, const void *),
+                                         void (*poly1305_update)(struct chacha20poly1305_context_t *, const void *, size_t),
+                                         void (*poly1305_finish)(struct chacha20poly1305_context_t *, void *))
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+
+    ctx->super.dispose_crypto = chacha20poly1305_dispose_crypto;
+    ctx->super.do_get_iv = chacha20poly1305_get_iv;
+    ctx->super.do_set_iv = chacha20poly1305_set_iv;
+    if (is_enc) {
+        ctx->super.do_encrypt_init = chacha20poly1305_init;
+        ctx->super.do_encrypt_update = chacha20poly1305_encrypt_update;
+        ctx->super.do_encrypt_final = chacha20poly1305_encrypt_final;
+        ctx->super.do_encrypt = ptls_aead__do_encrypt;
+        ctx->super.do_encrypt_v = ptls_aead__do_encrypt_v;
+        ctx->super.do_decrypt = NULL;
+    } else {
+        ctx->super.do_encrypt_init = NULL;
+        ctx->super.do_encrypt_update = NULL;
+        ctx->super.do_encrypt_final = NULL;
+        ctx->super.do_encrypt = NULL;
+        ctx->super.do_encrypt_v = NULL;
+        ctx->super.do_decrypt = chacha20poly1305_decrypt;
+    }
+
+    if ((ctx->chacha = ptls_cipher_new(chacha, is_enc, key)) == NULL)
+        return PTLS_ERROR_LIBRARY;
+
+    memcpy(ctx->static_iv, iv, sizeof(ctx->static_iv));
+    ctx->poly1305_init = poly1305_init;
+    ctx->poly1305_update = poly1305_update;
+    ctx->poly1305_finish = poly1305_finish;
+
+    return 0;
+}
diff --git a/lib/cifra/chacha20.c b/lib/cifra/chacha20.c
index 95a9e56..d56a8ca 100644
--- a/lib/cifra/chacha20.c
+++ b/lib/cifra/chacha20.c
@@ -27,6 +27,7 @@
 #include "sha2.h"
 #include "picotls.h"
 #include "picotls/minicrypto.h"
+#include "../chacha20poly1305.h"
 
 struct chacha20_context_t {
     ptls_cipher_context_t super;
@@ -64,161 +65,33 @@
     return 0;
 }
 
-struct chacha20poly1305_context_t {
-    ptls_aead_context_t super;
-    uint8_t key[PTLS_CHACHA20_KEY_SIZE];
-    uint8_t static_iv[PTLS_CHACHA20POLY1305_IV_SIZE];
-    cf_chacha20_ctx chacha;
+struct cifra_chacha20poly1305_context_t {
+    struct chacha20poly1305_context_t super;
     cf_poly1305 poly;
-    size_t aadlen;
-    size_t textlen;
 };
 
-static void chacha20poly1305_dispose_crypto(ptls_aead_context_t *_ctx)
+static void cifra_poly1305_init(struct chacha20poly1305_context_t *_ctx, const void *rs)
 {
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-
-    /* clear all memory except super */
-    ptls_clear_memory(&ctx->key, sizeof(*ctx) - offsetof(struct chacha20poly1305_context_t, key));
+    struct cifra_chacha20poly1305_context_t *ctx = (struct cifra_chacha20poly1305_context_t *)_ctx;
+    cf_poly1305_init(&ctx->poly, rs, (const uint8_t *)rs + 16);
 }
 
-static const uint8_t zeros64[64] = {0};
-
-static void chacha20poly1305_encrypt_pad(cf_poly1305 *poly, size_t n)
+static void cifra_poly1305_update(struct chacha20poly1305_context_t *_ctx, const void *input, size_t len)
 {
-    if (n % 16 != 0)
-        cf_poly1305_update(poly, zeros64, 16 - (n % 16));
+    struct cifra_chacha20poly1305_context_t *ctx = (struct cifra_chacha20poly1305_context_t *)_ctx;
+    cf_poly1305_update(&ctx->poly, input, len);
 }
 
-static void chacha20poly1305_finalize(struct chacha20poly1305_context_t *ctx, uint8_t *tag)
+static void cifra_poly1305_finish(struct chacha20poly1305_context_t *_ctx, void *tag)
 {
-    uint8_t lenbuf[16];
-
-    chacha20poly1305_encrypt_pad(&ctx->poly, ctx->textlen);
-
-    write64_le(ctx->aadlen, lenbuf);
-    write64_le(ctx->textlen, lenbuf + 8);
-    cf_poly1305_update(&ctx->poly, lenbuf, sizeof(lenbuf));
-
+    struct cifra_chacha20poly1305_context_t *ctx = (struct cifra_chacha20poly1305_context_t *)_ctx;
     cf_poly1305_finish(&ctx->poly, tag);
 }
 
-static void chacha20poly1305_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
+static int cifra_chacha20poly1305_setup_crypto(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv)
 {
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-    uint8_t tmpbuf[64];
-
-    /* init chacha */
-    memset(tmpbuf, 0, 16 - PTLS_CHACHA20POLY1305_IV_SIZE);
-    ptls_aead__build_iv(ctx->super.algo, tmpbuf + 16 - PTLS_CHACHA20POLY1305_IV_SIZE, ctx->static_iv, seq);
-    cf_chacha20_init_custom(&ctx->chacha, ctx->key, sizeof(ctx->key), tmpbuf, 4);
-
-    /* init poly1305 (by using first 16 bytes of the key stream of the first block) */
-    cf_chacha20_cipher(&ctx->chacha, zeros64, tmpbuf, 64);
-    cf_poly1305_init(&ctx->poly, tmpbuf, tmpbuf + 16);
-
-    ptls_clear_memory(tmpbuf, sizeof(tmpbuf));
-
-    /* aad */
-    if (aadlen != 0) {
-        cf_poly1305_update(&ctx->poly, aad, aadlen);
-        chacha20poly1305_encrypt_pad(&ctx->poly, aadlen);
-    }
-
-    ctx->aadlen = aadlen;
-    ctx->textlen = 0;
-}
-
-static size_t chacha20poly1305_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)
-{
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-
-    cf_chacha20_cipher(&ctx->chacha, input, output, inlen);
-    cf_poly1305_update(&ctx->poly, output, inlen);
-    ctx->textlen += inlen;
-
-    return inlen;
-}
-
-static size_t chacha20poly1305_encrypt_final(ptls_aead_context_t *_ctx, void *output)
-{
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-
-    chacha20poly1305_finalize(ctx, output);
-
-    ptls_clear_memory(&ctx->chacha, sizeof(ctx->chacha));
-    return PTLS_CHACHA20POLY1305_TAG_SIZE;
-}
-
-static size_t chacha20poly1305_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                                       const void *aad, size_t aadlen)
-{
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-    uint8_t tag[PTLS_CHACHA20POLY1305_TAG_SIZE];
-    size_t ret;
-
-    if (inlen < sizeof(tag))
-        return SIZE_MAX;
-
-    chacha20poly1305_init(&ctx->super, seq, aad, aadlen);
-
-    cf_poly1305_update(&ctx->poly, input, inlen - sizeof(tag));
-    ctx->textlen = inlen - sizeof(tag);
-
-    chacha20poly1305_finalize(ctx, tag);
-    if (mem_eq(tag, (const uint8_t *)input + inlen - sizeof(tag), sizeof(tag))) {
-        cf_chacha20_cipher(&ctx->chacha, input, output, inlen - sizeof(tag));
-        ret = inlen - sizeof(tag);
-    } else {
-        ret = SIZE_MAX;
-    }
-
-    ptls_clear_memory(tag, sizeof(tag));
-    ptls_clear_memory(&ctx->poly, sizeof(ctx->poly));
-
-    return ret;
-}
-
-static void chacha20poly1305_get_iv(ptls_aead_context_t *_ctx, void *iv)
-{
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-
-    memcpy(iv, ctx->static_iv, sizeof(ctx->static_iv));
-}
-
-static void chacha20poly1305_set_iv(ptls_aead_context_t *_ctx, const void *iv)
-{
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-
-    memcpy(ctx->static_iv, iv, sizeof(ctx->static_iv));
-}
-
-static int aead_chacha20poly1305_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv)
-{
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-
-    ctx->super.dispose_crypto = chacha20poly1305_dispose_crypto;
-    ctx->super.do_get_iv = chacha20poly1305_get_iv;
-    ctx->super.do_set_iv = chacha20poly1305_set_iv;
-    if (is_enc) {
-        ctx->super.do_encrypt_init = chacha20poly1305_init;
-        ctx->super.do_encrypt_update = chacha20poly1305_encrypt_update;
-        ctx->super.do_encrypt_final = chacha20poly1305_encrypt_final;
-        ctx->super.do_encrypt = ptls_aead__do_encrypt;
-        ctx->super.do_encrypt_v = ptls_aead__do_encrypt_v;
-        ctx->super.do_decrypt = NULL;
-    } else {
-        ctx->super.do_encrypt_init = NULL;
-        ctx->super.do_encrypt_update = NULL;
-        ctx->super.do_encrypt_final = NULL;
-        ctx->super.do_encrypt = NULL;
-        ctx->super.do_encrypt_v = NULL;
-        ctx->super.do_decrypt = chacha20poly1305_decrypt;
-    }
-
-    memcpy(ctx->key, key, sizeof(ctx->key));
-    memcpy(ctx->static_iv, iv, sizeof(ctx->static_iv));
-    return 0;
+    return chacha20poly1305_setup_crypto(ctx, is_enc, key, iv, &ptls_minicrypto_chacha20, cifra_poly1305_init,
+                                         cifra_poly1305_update, cifra_poly1305_finish);
 }
 
 ptls_cipher_algorithm_t ptls_minicrypto_chacha20 = {
@@ -236,8 +109,8 @@
     {PTLS_TLS12_CHACHAPOLY_FIXED_IV_SIZE, PTLS_TLS12_CHACHAPOLY_RECORD_IV_SIZE},
     0,
     0,
-    sizeof(struct chacha20poly1305_context_t),
-    aead_chacha20poly1305_setup_crypto};
+    sizeof(struct cifra_chacha20poly1305_context_t),
+    cifra_chacha20poly1305_setup_crypto};
 ptls_cipher_suite_t ptls_minicrypto_chacha20poly1305sha256 = {.id = PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256,
                                                               .name = PTLS_CIPHER_SUITE_NAME_CHACHA20_POLY1305_SHA256,
                                                               .aead = &ptls_minicrypto_chacha20poly1305,
diff --git a/lib/openssl.c b/lib/openssl.c
index b710e88..0cd3976 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -32,6 +32,11 @@
 #define OPENSSL_API_COMPAT 0x00908000L
 #include <openssl/bn.h>
 #include <openssl/crypto.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/curve25519.h>
+#include <openssl/chacha.h>
+#include <openssl/poly1305.h>
+#endif
 #include <openssl/ec.h>
 #include <openssl/ecdh.h>
 #include <openssl/err.h>
@@ -44,6 +49,9 @@
 #include <openssl/x509_vfy.h>
 #include "picotls.h"
 #include "picotls/openssl.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "./chacha20poly1305.h"
+#endif
 
 #ifdef _WINDOWS
 #ifndef _CRT_SECURE_NO_WARNINGS
@@ -513,6 +521,25 @@
         goto Exit;
     }
 
+#ifdef OPENSSL_IS_BORINGSSL
+    if (ctx->super.algo->id == PTLS_GROUP_X25519) {
+        secret->len = peerkey.len;
+        if ((secret->base = malloc(secret->len)) == NULL) {
+            ret = PTLS_ERROR_NO_MEMORY;
+            goto Exit;
+        }
+        uint8_t sk_raw[32];
+        size_t sk_raw_len = sizeof(sk_raw);
+        if (EVP_PKEY_get_raw_private_key(ctx->privkey, sk_raw, &sk_raw_len) != 1) {
+            ret = PTLS_ERROR_LIBRARY;
+            goto Exit;
+        }
+        X25519(secret->base, sk_raw, peerkey.base);
+        ret = 0;
+        goto Exit;
+    }
+#endif
+
     if ((evppeer = EVP_PKEY_new()) == NULL) {
         ret = PTLS_ERROR_NO_MEMORY;
         goto Exit;
@@ -975,6 +1002,100 @@
 }
 
 #if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
+#ifdef OPENSSL_IS_BORINGSSL
+
+struct boringssl_chacha20_context_t {
+    ptls_cipher_context_t super;
+    uint8_t key[PTLS_CHACHA20_KEY_SIZE];
+    uint8_t iv[12];
+    struct {
+        uint32_t ctr;
+        uint8_t bytes[64];
+        size_t len;
+    } keystream;
+};
+
+static void boringssl_chacha20_dispose(ptls_cipher_context_t *_ctx)
+{
+    struct boringssl_chacha20_context_t *ctx = (struct boringssl_chacha20_context_t *)_ctx;
+
+    ptls_clear_memory(ctx->key, sizeof(ctx->key));
+    ptls_clear_memory(ctx->iv, sizeof(ctx->iv));
+    ptls_clear_memory(ctx->keystream.bytes, sizeof(ctx->keystream.bytes));
+}
+
+static void boringssl_chacha20_init(ptls_cipher_context_t *_ctx, const void *_iv)
+{
+    struct boringssl_chacha20_context_t *ctx = (struct boringssl_chacha20_context_t *)_ctx;
+    const uint8_t *iv = _iv;
+
+    memcpy(ctx->iv, iv + 4, sizeof(ctx->iv));
+    ctx->keystream.ctr = iv[0] | ((uint32_t)iv[1] << 8) | ((uint32_t)iv[2] << 16) | ((uint32_t)iv[3] << 24);
+    ctx->keystream.len = 0;
+}
+
+static inline void boringssl_chacha20_transform_buffered(struct boringssl_chacha20_context_t *ctx, uint8_t **output,
+                                                         const uint8_t **input, size_t *len)
+{
+    size_t apply_len = *len < ctx->keystream.len ? *len : ctx->keystream.len;
+    const uint8_t *ks = ctx->keystream.bytes + sizeof(ctx->keystream.bytes) - ctx->keystream.len;
+    ctx->keystream.len -= apply_len;
+
+    *len -= apply_len;
+    for (size_t i = 0; i < apply_len; ++i)
+        *(*output)++ = *(*input)++ ^ *ks++;
+}
+
+static void boringssl_chacha20_transform(ptls_cipher_context_t *_ctx, void *_output, const void *_input, size_t len)
+{
+    struct boringssl_chacha20_context_t *ctx = (struct boringssl_chacha20_context_t *)_ctx;
+    uint8_t *output = _output;
+    const uint8_t *input = _input;
+
+    if (len == 0)
+        return;
+
+    if (ctx->keystream.len != 0) {
+        boringssl_chacha20_transform_buffered(ctx, &output, &input, &len);
+        if (len == 0)
+            return;
+    }
+
+    assert(ctx->keystream.len == 0);
+
+    if (len >= sizeof(ctx->keystream.bytes)) {
+        size_t blocks = len / CHACHA20POLY1305_BLOCKSIZE;
+        CRYPTO_chacha_20(output, input, blocks * CHACHA20POLY1305_BLOCKSIZE, ctx->key, ctx->iv, ctx->keystream.ctr);
+        ctx->keystream.ctr += blocks;
+        output += blocks * CHACHA20POLY1305_BLOCKSIZE;
+        input += blocks * CHACHA20POLY1305_BLOCKSIZE;
+        len -= blocks * CHACHA20POLY1305_BLOCKSIZE;
+        if (len == 0)
+            return;
+    }
+
+    memset(ctx->keystream.bytes, 0, CHACHA20POLY1305_BLOCKSIZE);
+    CRYPTO_chacha_20(ctx->keystream.bytes, ctx->keystream.bytes, CHACHA20POLY1305_BLOCKSIZE, ctx->key, ctx->iv,
+                     ctx->keystream.ctr++);
+    ctx->keystream.len = sizeof(ctx->keystream.bytes);
+
+    boringssl_chacha20_transform_buffered(ctx, &output, &input, &len);
+    assert(len == 0);
+}
+
+static int boringssl_chacha20_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key)
+{
+    struct boringssl_chacha20_context_t *ctx = (struct boringssl_chacha20_context_t *)_ctx;
+
+    ctx->super.do_dispose = boringssl_chacha20_dispose;
+    ctx->super.do_init = boringssl_chacha20_init;
+    ctx->super.do_transform = boringssl_chacha20_transform;
+    memcpy(ctx->key, key, sizeof(ctx->key));
+
+    return 0;
+}
+
+#else
 
 static int chacha20_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key)
 {
@@ -982,6 +1103,7 @@
 }
 
 #endif
+#endif
 
 #if PTLS_OPENSSL_HAVE_BF
 
@@ -1160,10 +1282,45 @@
 }
 
 #if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
+#ifdef OPENSSL_IS_BORINGSSL
+
+struct boringssl_chacha20poly1305_context_t {
+    struct chacha20poly1305_context_t super;
+    poly1305_state poly1305;
+};
+
+static void boringssl_poly1305_init(struct chacha20poly1305_context_t *_ctx, const void *key)
+{
+    struct boringssl_chacha20poly1305_context_t *ctx = (struct boringssl_chacha20poly1305_context_t *)_ctx;
+    CRYPTO_poly1305_init(&ctx->poly1305, key);
+}
+
+static void boringssl_poly1305_update(struct chacha20poly1305_context_t *_ctx, const void *input, size_t len)
+{
+    struct boringssl_chacha20poly1305_context_t *ctx = (struct boringssl_chacha20poly1305_context_t *)_ctx;
+    CRYPTO_poly1305_update(&ctx->poly1305, input, len);
+}
+
+static void boringssl_poly1305_finish(struct chacha20poly1305_context_t *_ctx, void *tag)
+{
+    struct boringssl_chacha20poly1305_context_t *ctx = (struct boringssl_chacha20poly1305_context_t *)_ctx;
+    CRYPTO_poly1305_finish(&ctx->poly1305, tag);
+}
+
+static int boringssl_chacha20poly1305_setup_crypto(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv)
+{
+    return chacha20poly1305_setup_crypto(ctx, is_enc, key, iv, &ptls_openssl_chacha20, boringssl_poly1305_init,
+                                         boringssl_poly1305_update, boringssl_poly1305_finish);
+}
+
+#else
+
 static int aead_chacha20poly1305_setup_crypto(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv)
 {
     return aead_setup_crypto(ctx, is_enc, key, iv, EVP_chacha20_poly1305());
 }
+
+#endif
 #endif
 
 #define _sha256_final(ctx, md) SHA256_Final((md), (ctx))
@@ -1388,7 +1545,7 @@
             if (ptls_server_name_is_ipaddr(server_name)) {
                 X509_VERIFY_PARAM_set1_ip_asc(params, server_name);
             } else {
-                X509_VERIFY_PARAM_set1_host(params, server_name, 0);
+                X509_VERIFY_PARAM_set1_host(params, server_name, strlen(server_name));
                 X509_VERIFY_PARAM_set_hostflags(params, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
             }
         }
@@ -1558,7 +1715,7 @@
     *verifier = verify_sign;
     ret = 0;
 Exit:
-    free(expected_pubkey.base);
+    OPENSSL_free(expected_pubkey.base);
     return ret;
 }
 
@@ -1957,21 +2114,38 @@
     .hash = &ptls_openssl_sha384};
 #if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
 ptls_cipher_algorithm_t ptls_openssl_chacha20 = {
-    "CHACHA20",           PTLS_CHACHA20_KEY_SIZE, 1 /* block size */, PTLS_CHACHA20_IV_SIZE, sizeof(struct cipher_context_t),
-    chacha20_setup_crypto};
-ptls_aead_algorithm_t ptls_openssl_chacha20poly1305 = {"CHACHA20-POLY1305",
-                                                       PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT,
-                                                       PTLS_CHACHA20POLY1305_INTEGRITY_LIMIT,
-                                                       &ptls_openssl_chacha20,
-                                                       NULL,
-                                                       PTLS_CHACHA20_KEY_SIZE,
-                                                       PTLS_CHACHA20POLY1305_IV_SIZE,
-                                                       PTLS_CHACHA20POLY1305_TAG_SIZE,
-                                                       {PTLS_TLS12_CHACHAPOLY_FIXED_IV_SIZE, PTLS_TLS12_CHACHAPOLY_RECORD_IV_SIZE},
-                                                       0,
-                                                       0,
-                                                       sizeof(struct aead_crypto_context_t),
-                                                       aead_chacha20poly1305_setup_crypto};
+    .name = "CHACHA20",
+    .key_size = PTLS_CHACHA20_KEY_SIZE,
+    .block_size = 1,
+    .iv_size = PTLS_CHACHA20_IV_SIZE,
+#ifdef OPENSSL_IS_BORINGSSL
+    .context_size = sizeof(struct boringssl_chacha20_context_t),
+    .setup_crypto = boringssl_chacha20_setup_crypto,
+#else
+    .context_size = sizeof(struct cipher_context_t),
+    .setup_crypto = chacha20_setup_crypto,
+#endif
+};
+ptls_aead_algorithm_t ptls_openssl_chacha20poly1305 = {
+    .name = "CHACHA20-POLY1305",
+    .confidentiality_limit = PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT,
+    .integrity_limit = PTLS_CHACHA20POLY1305_INTEGRITY_LIMIT,
+    .ctr_cipher = &ptls_openssl_chacha20,
+    .ecb_cipher = NULL,
+    .key_size = PTLS_CHACHA20_KEY_SIZE,
+    .iv_size = PTLS_CHACHA20POLY1305_IV_SIZE,
+    .tag_size = PTLS_CHACHA20POLY1305_TAG_SIZE,
+    .tls12 = {.fixed_iv_size = PTLS_TLS12_CHACHAPOLY_FIXED_IV_SIZE, .record_iv_size = PTLS_TLS12_CHACHAPOLY_RECORD_IV_SIZE},
+    .non_temporal = 0,
+    .align_bits = 0,
+#ifdef OPENSSL_IS_BORINGSSL
+    .context_size = sizeof(struct boringssl_chacha20poly1305_context_t),
+    .setup_crypto = boringssl_chacha20poly1305_setup_crypto,
+#else
+    .context_size = sizeof(struct aead_crypto_context_t),
+    .setup_crypto = aead_chacha20poly1305_setup_crypto,
+#endif
+};
 ptls_cipher_suite_t ptls_openssl_chacha20poly1305sha256 = {.id = PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256,
                                                            .name = PTLS_CIPHER_SUITE_NAME_CHACHA20_POLY1305_SHA256,
                                                            .aead = &ptls_openssl_chacha20poly1305,
diff --git a/picotls.xcodeproj/project.pbxproj b/picotls.xcodeproj/project.pbxproj
index 9a917b3..6882e19 100644
--- a/picotls.xcodeproj/project.pbxproj
+++ b/picotls.xcodeproj/project.pbxproj
@@ -8,6 +8,8 @@
 
 /* Begin PBXBuildFile section */
 		081F00CA2918823200534A86 /* hpke.c in Sources */ = {isa = PBXBuildFile; fileRef = 081F00C92918823200534A86 /* hpke.c */; };
+		08A835E22995E04100D872CE /* chacha20poly1305.h in Headers */ = {isa = PBXBuildFile; fileRef = 08A835E12995E04100D872CE /* chacha20poly1305.h */; };
+		08A835E32995E04100D872CE /* chacha20poly1305.h in Headers */ = {isa = PBXBuildFile; fileRef = 08A835E12995E04100D872CE /* chacha20poly1305.h */; };
 		08F0FDF62910F67A00EE657D /* hpke.c in Sources */ = {isa = PBXBuildFile; fileRef = 08F0FDF52910F67A00EE657D /* hpke.c */; };
 		105900431DC8D57000FB4085 /* picotls.c in Sources */ = {isa = PBXBuildFile; fileRef = 106530E91D9B7C13005B2C60 /* picotls.c */; };
 		105900441DC8D57000FB4085 /* picotest.c in Sources */ = {isa = PBXBuildFile; fileRef = 106530E31D9B4021005B2C60 /* picotest.c */; };
@@ -203,6 +205,8 @@
 		081F00CC291A358800534A86 /* asn1.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = asn1.h; sourceTree = "<group>"; };
 		081F00CD291A358800534A86 /* pembase64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pembase64.h; sourceTree = "<group>"; };
 		081F00CE291A358800534A86 /* ptlsbcrypt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ptlsbcrypt.h; sourceTree = "<group>"; };
+		08A835E12995E04100D872CE /* chacha20poly1305.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = chacha20poly1305.h; sourceTree = "<group>"; };
+		08A835EB2996971300D872CE /* boringssl-adjust.cmake */ = {isa = PBXFileReference; lastKnownFileType = text; path = "boringssl-adjust.cmake"; sourceTree = "<group>"; };
 		08B3298229419DFC009D6766 /* ech-live.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = "ech-live.t"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.perl; };
 		08F0FDF52910F67A00EE657D /* hpke.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hpke.c; sourceTree = "<group>"; };
 		105900241DC8D37500FB4085 /* aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = aes.c; path = src/aes.c; sourceTree = "<group>"; };
@@ -434,6 +438,7 @@
 				E99B75DE1F5CDDB500CF503E /* asn1.c */,
 				E9E4B12A2180530400514B47 /* certificate_compression.c */,
 				E9F20BDF22E34B210018D260 /* cifra */,
+				08A835E12995E04100D872CE /* chacha20poly1305.h */,
 				1059003F1DC8D53200FB4085 /* cifra.c */,
 				E97577022212405D00D1EF74 /* ffx.c */,
 				E9B43DBF24619D1700824E51 /* fusion.c */,
@@ -520,6 +525,7 @@
 		E95EBCC9227E9FF30022C32D /* cmake */ = {
 			isa = PBXGroup;
 			children = (
+				08A835EB2996971300D872CE /* boringssl-adjust.cmake */,
 				E95EBCCA227EA0180022C32D /* dtrace-utils.cmake */,
 			);
 			path = cmake;
@@ -560,6 +566,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				105900A21DC8E20D00FB4085 /* openssl.h in Headers */,
+				08A835E32995E04100D872CE /* chacha20poly1305.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -608,6 +615,7 @@
 				10EACB0E1DCEAF0F00CA0341 /* chash.h in Headers */,
 				10EACB0F1DCEAF0F00CA0341 /* sha2.h in Headers */,
 				E9BC76DE1EF3CCD100EB7A09 /* poly1305.h in Headers */,
+				08A835E22995E04100D872CE /* chacha20poly1305.h in Headers */,
 				10EACB101DCEAF0F00CA0341 /* aes.h in Headers */,
 				10EACB111DCEAF0F00CA0341 /* minicrypto.h in Headers */,
 				10EACB121DCEAF0F00CA0341 /* gf128.h in Headers */,
diff --git a/t/picotls.c b/t/picotls.c
index 14fa849..db7ee76 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -210,6 +210,53 @@
     ptls_aead_free(c);
 }
 
+static void test_ciphersuite_stream(ptls_cipher_suite_t *cs1, ptls_cipher_suite_t *cs2)
+{
+    const char *traffic_secret = "012345678901234567890123456789012345678901234567",
+               *text[] = {
+                   "CHAPTER I.\n",
+                   "Down the Rabbit-Hole\n",
+                   "Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once "
+                   "or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, "
+                   "“and what is the use of a book,” thought Alice “without pictures or conversations?”\n",
+                   "So she was considering in her own mind (as well as she could, for the hot day made her feel very sleepy and "
+                   "stupid), whether the pleasure of making a daisy-chain would be worth the trouble of getting up and picking the "
+                   "daisies, when suddenly a White Rabbit with pink eyes ran close by her.\n",
+                   NULL,
+               };
+    ptls_aead_context_t *c;
+    char enc[1024], dec[1024];
+    size_t enclen, declen;
+
+    /* encrypt */
+    c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
+    assert(c != NULL);
+    ptls_aead_encrypt_init(c, 0, NULL, 0);
+    enclen = 0;
+    for (size_t i = 0; text[i] != NULL; ++i)
+        enclen += ptls_aead_encrypt_update(c, enc + enclen, text[i], strlen(text[i]));
+    enclen += ptls_aead_encrypt_final(c, enc + enclen);
+    ptls_aead_free(c);
+
+    /* decrypt */
+    c = ptls_aead_new(cs2->aead, cs2->hash, 0, traffic_secret, NULL);
+    declen = ptls_aead_decrypt(c, dec, enc, enclen, 0, NULL, 0);
+    ok(declen != SIZE_MAX);
+    ok(declen == enclen - cs1->aead->tag_size);
+    ptls_aead_free(c);
+
+    /* check text */
+    for (size_t i = 0, decoff = 0;; ++i) {
+        if (text[i] == NULL) {
+            ok(decoff == declen);
+            break;
+        }
+        ok(decoff + strlen(text[i]) <= declen);
+        ok(memcmp(dec + decoff, text[i], strlen(text[i])) == 0);
+        decoff += strlen(text[i]);
+    }
+}
+
 static void test_aad_ciphersuite(ptls_cipher_suite_t *cs1, ptls_cipher_suite_t *cs2)
 {
     const char *traffic_secret = "012345678901234567890123456789012345678901234567", *src = "hello world", *aad = "my true aad";
@@ -372,6 +419,7 @@
                         *cs_peer = find_cipher(ctx_peer, PTLS_CIPHER_SUITE_AES_128_GCM_SHA256);
 
     test_ciphersuite(cs, cs_peer);
+    test_ciphersuite_stream(cs, cs_peer);
     test_aad_ciphersuite(cs, cs_peer);
     test_aad96_ciphersuite(cs, cs_peer);
 }
@@ -383,6 +431,7 @@
 
     if (cs != NULL && cs_peer != NULL) {
         test_ciphersuite(cs, cs_peer);
+        test_ciphersuite_stream(cs, cs_peer);
         test_aad_ciphersuite(cs, cs_peer);
         test_aad96_ciphersuite(cs, cs_peer);
     }
@@ -395,6 +444,7 @@
 
     if (cs != NULL && cs_peer != NULL) {
         test_ciphersuite(cs, cs_peer);
+        test_ciphersuite_stream(cs, cs_peer);
         test_aad_ciphersuite(cs, cs_peer);
         test_aad96_ciphersuite(cs, cs_peer);
     }