+static void free_sptps_key(sptps_key_t *key) {
+ xzfree(key, sizeof(sptps_key_t));
+}
+
+static bool cipher_init(uint8_t suite, void **ctx, const sptps_key_t *keys, bool key_half) {
+ const uint8_t *key = key_half ? keys->key1 : keys->key0;
+
+ switch(suite) {
+#ifndef HAVE_OPENSSL
+
+ case SPTPS_CHACHA_POLY1305:
+ *ctx = malloc(sizeof(struct chachapoly_ctx));
+ return *ctx && chachapoly_init(*ctx, key, 256) == CHACHAPOLY_OK;
+
+#else
+
+ case SPTPS_CHACHA_POLY1305:
+#ifdef EVP_F_EVP_AEAD_CTX_INIT
+ *ctx = malloc(sizeof(EVP_AEAD_CTX));
+
+ return *ctx && EVP_AEAD_CTX_init(*ctx, EVP_aead_chacha20_poly1305(), key + (key_half ? CIPHER_KEYLEN : 0), 32, 16, NULL);
+#else
+ *ctx = EVP_CIPHER_CTX_new();
+
+ return *ctx
+ && EVP_EncryptInit_ex(*ctx, EVP_chacha20_poly1305(), NULL, NULL, NULL)
+ && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL)
+ && EVP_EncryptInit_ex(*ctx, NULL, NULL, key, key + 32);
+#endif
+
+ case SPTPS_AES256_GCM:
+ *ctx = EVP_CIPHER_CTX_new();
+
+ return *ctx
+ && EVP_EncryptInit_ex(*ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)
+ && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL)
+ && EVP_EncryptInit_ex(*ctx, NULL, NULL, key, key + 32);
+#endif
+
+ default:
+ return false;
+ }
+}
+
+static void cipher_exit(uint8_t suite, void *ctx) {
+ switch(suite) {
+#ifndef HAVE_OPENSSL
+
+ case SPTPS_CHACHA_POLY1305:
+ free(ctx);
+ break;
+
+#else
+
+ case SPTPS_CHACHA_POLY1305:
+#ifdef EVP_F_EVP_AEAD_CTX_INIT
+ EVP_AEAD_CTX_cleanup(ctx);
+ free(ctx);
+ break;
+#endif
+
+ case SPTPS_AES256_GCM:
+ EVP_CIPHER_CTX_free(ctx);
+ break;
+#endif
+
+ default:
+ break;
+ }
+}
+
+static bool cipher_encrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) {
+ switch(suite) {
+#ifndef HAVE_OPENSSL
+
+ case SPTPS_CHACHA_POLY1305: {
+ if(chachapoly_crypt(ctx, nonce, (void *)in, inlen, out, out + inlen, 16, 1) != CHACHAPOLY_OK) {