extract chachapoly logic in cifra so that it can be reused
diff --git a/lib/chacha20poly1305.h b/lib/chacha20poly1305.h
new file mode 100644
index 0000000..1b78dd2
--- /dev/null
+++ b/lib/chacha20poly1305.h
@@ -0,0 +1,194 @@
+/*
+ * 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"
+
+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[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);
+    ptls_cipher_init(ctx->chacha, tmpbuf);
+
+    /* init poly1305 */
+    memset(tmpbuf, 0, sizeof(tmpbuf));
+    ptls_cipher_encrypt(ctx->chacha, tmpbuf, tmpbuf, 64);
+    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_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+    const uint8_t *bytes = _bytes;
+
+    for (size_t i = 0; i < len; ++i)
+        ctx->static_iv[i] ^= bytes[i];
+}
+
+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_xor_iv = chacha20poly1305_xor_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 3332877..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,155 +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_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
-{
-    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
-    const uint8_t *bytes = _bytes;
-
-    for (size_t i = 0; i < len; ++i)
-        ctx->static_iv[i] ^= bytes[i];
-}
-
-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_xor_iv = chacha20poly1305_xor_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 = {
@@ -230,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/picotls.xcodeproj/project.pbxproj b/picotls.xcodeproj/project.pbxproj
index 9a917b3..17b2483 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,7 @@
 		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>"; };
 		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 +437,7 @@
 				E99B75DE1F5CDDB500CF503E /* asn1.c */,
 				E9E4B12A2180530400514B47 /* certificate_compression.c */,
 				E9F20BDF22E34B210018D260 /* cifra */,
+				08A835E12995E04100D872CE /* chacha20poly1305.h */,
 				1059003F1DC8D53200FB4085 /* cifra.c */,
 				E97577022212405D00D1EF74 /* ffx.c */,
 				E9B43DBF24619D1700824E51 /* fusion.c */,
@@ -560,6 +564,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				105900A21DC8E20D00FB4085 /* openssl.h in Headers */,
+				08A835E32995E04100D872CE /* chacha20poly1305.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -608,6 +613,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 */,