[
  {
    "path": ".clang-format",
    "content": "# requires clang-format >= 3.6\nBasedOnStyle: \"LLVM\"\nIndentWidth: 4\nColumnLimit: 132\nBreakBeforeBraces: Linux\nAllowShortFunctionsOnASingleLine: None\nSortIncludes: false\n"
  },
  {
    "path": ".gitignore",
    "content": "# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n# Debug files\n*.dSYM/\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Kazuho Oku, DeNA Co., Ltd.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "Makefile",
    "content": "CC?=     cc\nCFLAGS+= -Wall -fsanitize=address -fstack-protector -g\nLIBS+=   -lpthread -lssl -lcrypto\nTARGET=  test-neverbleed\nOBJS=    test.o neverbleed.o\n\nall:    $(TARGET)\n\n.c.o:\n\t$(CC) $(CFLAGS) -c $<\n\n$(TARGET): $(OBJS)\n\t$(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS)\n\ncheck: $(TARGET)\n\tprove -v t/test_handshake.t\n\nclean:\n\trm -fr $(OBJS) $(TARGET)\n\n.PHONY: clean check\n"
  },
  {
    "path": "README.md",
    "content": "Neverbleed\n===============\n\nNeverbleed is an [OpenSSL engine](https://www.openssl.org/docs/man1.0.2/crypto/engine.html) that runs RSA private key operations in an isolated process, thereby minimizing the risk of private key leak in case of vulnerability such as [Heartbleed](http://heartbleed.com/).\n\nThe engine is known to work together with existing versions of OpenSSL or LibreSSL, with minimal changes to the server source code.\n\nFAQ\n---\n\n### Q. How much is the overhead?\n\nVirtually none.\n\nGenerally speaking, private key operations are much more heavier than the overhead of inter-process communication.\nOn my Linux VM running on Core i7 @ 2.4GHz (MacBook Pro 15\" Late 2013), OpenSSL 1.0.2 without privilege separation processes 319.56 full TLS handshakes per second, whereas OpenSSL with privilege separation processes 316.72 handshakes per second (note: RSA key length: 2,048 bits, selected cipher-suite: ECDHE-RSA-AES128-GCM-SHA256).\n\n### Q. Why does the library only protect the private keys?\n\nBecause private keys are the only _long-term_ secret being used for encrypting and/or digitally-signing the communication.\n\nDepending on how OpenSSL is used, it might be beneficial to separate symmetric cipher operations or TLS operations as a whole.\nBut even in such case, it would still be a good idea to isolate private key operations from them considering the impact of private key leaks.\nIn other words, separating private key operations only to an isolated process in always a good thing to do.\n\n### Q. Is there any HTTP server that uses Neverbleed?\n\nNeverbleed is used by [H2O](https://h2o.examp1e.net/) HTTP2 server since version [1.5.0-beta4](https://github.com/h2o/h2o/releases/tag/v1.5.0-beta4).\n\nHow-to\n------\n\nThe library exposes two functions: `neverbleed_init` and `neverbleed_load_private_key_file`.\n\nThe first function spawns an external process dedicated to private key operations, and the second function assigns a RSA private key stored in the specified file to an existing SSL context (`SSL_CTX`).\n\nBy\n\n1. adding call to `neverbleed_init`\n2. replacing call to `SSL_CTX_use_PrivateKey_file` with `neverbleed_load_private_key_file`\n\nthe privilege separation engine will be used for all the incoming TLS connections.\n\n```\n  neverbleed_t nb;\n  char errbuf[NEVERBLEED_ERRBUF_SIZE];\n\n  /* initialize the OpenSSL library and the neverbleed engine */\n  SSL_load_error_strings();\n  SSL_library_init();\n  OpenSSL_add_all_algorithms();\n  if (neverbleed_init(&nb, errbuf) != 0) {\n    fprintf(stderr, \"neverbleed_init failed: %s\\n\", errbuf);\n    ...\n  }\n\n  ...\n\n  /* load certificate chain and private key */\n  if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certchain_fn) != 1) {\n    fprintf(stderr, \"failed to load certificate chain file:%s\\n\", certchain_fn);\n    ...\n  }\n  if (neverbleed_load_private_key_file(&nb, ctx, privkey_fn, errbuf) != 1) {\n    fprintf(stderr, \"failed to load private key from file:%s:%s\\n\", privkey_fn, errbuf);\n    ...\n  }\n```\n\nAlso, `neverbleed_setuidgid` function can be used to drop the privileges of the daemon process once it completes loading all the private keys.\n"
  },
  {
    "path": "neverbleed.c",
    "content": "/*\n * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#include <assert.h>\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <grp.h>\n#include <limits.h>\n#include <pthread.h>\n#include <pwd.h>\n#include <stdarg.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/select.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <sys/uio.h>\n#include <unistd.h>\n#include <signal.h>\n#if defined(__linux__)\n#include <sys/epoll.h>\n#include <sys/prctl.h>\n#include <sys/syscall.h>\n#elif defined(__APPLE__)\n#include <sys/ptrace.h>\n#elif defined(__FreeBSD__)\n#include <sys/procctl.h>\n#elif defined(__sun)\n#include <priv.h>\n#endif\n\n/* to maximize code-reuse between different stacks, we intentionally use API declared by OpenSSL as legacy */\n#define OPENSSL_SUPPRESS_DEPRECATED\n\n#include <openssl/opensslconf.h>\n#include <openssl/opensslv.h>\n\n#if defined(LIBRESSL_VERSION_NUMBER) ? LIBRESSL_VERSION_NUMBER >= 0x3050000fL : OPENSSL_VERSION_NUMBER >= 0x1010000fL\n/* RSA_METHOD is opaque, so RSA_meth* are used. */\n#define NEVERBLEED_OPAQUE_RSA_METHOD\n#endif\n\n#if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_EC) &&                                                            \\\n    (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2090100fL)\n/* EC_KEY_METHOD and related APIs are avaliable, so ECDSA is enabled. */\n#define NEVERBLEED_ECDSA\n#endif\n\n#include <openssl/bn.h>\n#ifdef NEVERBLEED_ECDSA\n#include <openssl/ec.h>\n#endif\n#include <openssl/rand.h>\n#include <openssl/rsa.h>\n#include <openssl/ssl.h>\n\n#ifdef __linux\n#if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)\n#define USE_OFFLOAD 1\n#endif\n#if defined(OPENSSL_IS_BORINGSSL) && defined(NEVERBLEED_BORINGSSL_USE_QAT)\n#include \"qat_bssl.h\"\n/* the mapping seems to be missing */\n#ifndef ASYNC_WAIT_CTX_get_all_fds\nextern int bssl_async_wait_ctx_get_all_fds(ASYNC_WAIT_CTX *ctx, OSSL_ASYNC_FD *fd, size_t *numfds);\n#define ASYNC_WAIT_CTX_get_all_fds bssl_async_wait_ctx_get_all_fds\n#endif\n#define USE_OFFLOAD 1\n#endif\n#endif\n\n#if OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)\n\nstatic void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)\n{\n    if (n) {\n        *n = rsa->n;\n    }\n\n    if (e) {\n        *e = rsa->e;\n    }\n\n    if (d) {\n        *d = rsa->d;\n    }\n}\n\nstatic int RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d)\n{\n    if (n == NULL || e == NULL) {\n        return 0;\n    }\n\n    BN_free(rsa->n);\n    BN_free(rsa->e);\n    BN_free(rsa->d);\n    rsa->n = n;\n    rsa->e = e;\n    rsa->d = d;\n\n    return 1;\n}\n\nstatic void RSA_set_flags(RSA *r, int flags)\n{\n    r->flags |= flags;\n}\n\n#define EVP_PKEY_up_ref(p) CRYPTO_add(&(p)->references, 1, CRYPTO_LOCK_EVP_PKEY)\n\n#endif\n\n#include \"neverbleed.h\"\n\nenum neverbleed_type { NEVERBLEED_TYPE_ERROR, NEVERBLEED_TYPE_RSA, NEVERBLEED_TYPE_ECDSA };\n\nstruct st_neverbleed_rsa_exdata_t {\n    neverbleed_t *nb;\n    size_t key_index;\n};\n\nstruct st_neverbleed_thread_data_t {\n    pid_t self_pid;\n    int fd;\n};\n\n/**\n * a variant of pthread_once, that does not require you to declare a callback, nor have a global variable\n */\n#define NEVERBLEED_MULTITHREAD_ONCE(block)                                                                                                \\\n    do {                                                                                                                           \\\n        static volatile int lock = 0;                                                                                              \\\n        int lock_loaded = lock;                                                                                                    \\\n        __sync_synchronize();                                                                                                      \\\n        if (!lock_loaded) {                                                                                                        \\\n            static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;                                                              \\\n            pthread_mutex_lock(&mutex);                                                                                            \\\n            if (!lock) {                                                                                                           \\\n                do {                                                                                                               \\\n                    block                                                                                                          \\\n                } while (0);                                                                                                       \\\n                __sync_synchronize();                                                                                              \\\n                lock = 1;                                                                                                          \\\n            }                                                                                                                      \\\n            pthread_mutex_unlock(&mutex);                                                                                          \\\n        }                                                                                                                          \\\n    } while (0)\n\nstatic void warnvf(const char *fmt, va_list args)\n{\n    char errbuf[256];\n\n    if (errno != 0) {\n        strerror_r(errno, errbuf, sizeof(errbuf));\n    } else {\n        errbuf[0] = '\\0';\n    }\n\n    fprintf(stderr, \"[openssl-privsep] \");\n    vfprintf(stderr, fmt, args);\n    if (errbuf[0] != '\\0')\n        fputs(errbuf, stderr);\n    fputc('\\n', stderr);\n}\n\n__attribute__((format(printf, 1, 2))) static void warnf(const char *fmt, ...)\n{\n    va_list args;\n\n    va_start(args, fmt);\n    warnvf(fmt, args);\n    va_end(args);\n}\n\n__attribute__((format(printf, 1, 2), noreturn)) static void dief(const char *fmt, ...)\n{\n    va_list args;\n\n    va_start(args, fmt);\n    warnvf(fmt, args);\n    va_end(args);\n\n    abort();\n}\n\nstatic char *dirname(const char *path)\n{\n    const char *last_slash = strrchr(path, '/');\n    char *ret;\n\n    if (last_slash == NULL) {\n        errno = 0;\n        dief(\"dirname: no slash in given path:%s\", path);\n    }\n    if ((ret = malloc(last_slash + 1 - path)) == NULL)\n        dief(\"no memory\");\n    memcpy(ret, path, last_slash - path);\n    ret[last_slash - path] = '\\0';\n    return ret;\n}\n\nstatic void set_cloexec(int fd)\n{\n    if (fcntl(fd, F_SETFD, O_CLOEXEC) == -1)\n        dief(\"failed to set O_CLOEXEC to fd %d\", fd);\n}\n\nstatic int read_nbytes(int fd, void *p, size_t sz)\n{\n    while (sz != 0) {\n        ssize_t r;\n        while ((r = read(fd, p, sz)) == -1 && errno == EINTR)\n            ;\n        if (r == -1) {\n            return -1;\n        } else if (r == 0) {\n            errno = 0;\n            return -1;\n        }\n        p = (char *)p + r;\n        sz -= r;\n    }\n    return 0;\n}\n\n/**\n * This function disposes of the memory allocated for `neverbleed_iobuf_t`, but retains the value of `next` and `processing` so that\n * the buffer can be \"cleared\" while in use by worker threads.\n */\nstatic void iobuf_dispose(neverbleed_iobuf_t *buf)\n{\n    if (buf->capacity != 0)\n        OPENSSL_cleanse(buf->buf, buf->capacity);\n    free(buf->buf);\n    buf->buf = NULL;\n    buf->start = NULL;\n    buf->end = NULL;\n    buf->capacity = 0;\n}\n\nstatic void iobuf_reserve(neverbleed_iobuf_t *buf, size_t extra)\n{\n    size_t start_off, end_off;\n\n    if (extra <= buf->buf - buf->end + buf->capacity)\n        return;\n\n    if (buf->capacity == 0)\n        buf->capacity = 4096;\n    while (buf->buf - buf->end + buf->capacity < extra)\n        buf->capacity *= 2;\n\n    if (buf->buf != NULL) {\n        start_off = buf->start - buf->buf;\n        end_off = buf->end - buf->buf;\n    } else {\n        /* C99 forbids us doing `buf->start - buf->buf` when both are NULL (undefined behavior) */\n        start_off = 0;\n        end_off = 0;\n    }\n\n    if ((buf->buf = realloc(buf->buf, buf->capacity)) == NULL)\n        dief(\"realloc failed\");\n    buf->start = buf->buf + start_off;\n    buf->end = buf->buf + end_off;\n}\n\nstatic void iobuf_push_num(neverbleed_iobuf_t *buf, size_t v)\n{\n    iobuf_reserve(buf, sizeof(v));\n    memcpy(buf->end, &v, sizeof(v));\n    buf->end += sizeof(v);\n}\n\nstatic void iobuf_push_str(neverbleed_iobuf_t *buf, const char *s)\n{\n    size_t l = strlen(s) + 1;\n    iobuf_reserve(buf, l);\n    memcpy(buf->end, s, l);\n    buf->end += l;\n}\n\nstatic void iobuf_push_bytes(neverbleed_iobuf_t *buf, const void *p, size_t l)\n{\n    iobuf_push_num(buf, l);\n    iobuf_reserve(buf, l);\n    memcpy(buf->end, p, l);\n    buf->end += l;\n}\n\nstatic int iobuf_shift_num(neverbleed_iobuf_t *buf, size_t *v)\n{\n    if (neverbleed_iobuf_size(buf) < sizeof(*v))\n        return -1;\n    memcpy(v, buf->start, sizeof(*v));\n    buf->start += sizeof(*v);\n    return 0;\n}\n\nstatic char *iobuf_shift_str(neverbleed_iobuf_t *buf)\n{\n    char *nul = memchr(buf->start, '\\0', neverbleed_iobuf_size(buf)), *ret;\n    if (nul == NULL)\n        return NULL;\n    ret = buf->start;\n    buf->start = nul + 1;\n    return ret;\n}\n\nstatic void *iobuf_shift_bytes(neverbleed_iobuf_t *buf, size_t *l)\n{\n    void *ret;\n    if (iobuf_shift_num(buf, l) != 0)\n        return NULL;\n    if (neverbleed_iobuf_size(buf) < *l)\n        return NULL;\n    ret = buf->start;\n    buf->start += *l;\n    return ret;\n}\n\nstatic int iobuf_write(neverbleed_iobuf_t *buf, int fd)\n{\n    struct iovec vecs[2] = {{NULL}};\n    size_t bufsz = neverbleed_iobuf_size(buf);\n    int vecindex;\n    ssize_t r;\n\n    vecs[0].iov_base = &bufsz;\n    vecs[0].iov_len = sizeof(bufsz);\n    vecs[1].iov_base = buf->start;\n    vecs[1].iov_len = bufsz;\n\n    for (vecindex = 0; vecindex != sizeof(vecs) / sizeof(vecs[0]);) {\n        while ((r = writev(fd, vecs + vecindex, sizeof(vecs) / sizeof(vecs[0]) - vecindex)) == -1 && errno == EINTR)\n            ;\n        if (r == -1)\n            return -1;\n        assert(r != 0);\n        while (r != 0 && r >= vecs[vecindex].iov_len) {\n            r -= vecs[vecindex].iov_len;\n            ++vecindex;\n        }\n        if (r != 0) {\n            vecs[vecindex].iov_base = (char *)vecs[vecindex].iov_base + r;\n            vecs[vecindex].iov_len -= r;\n        }\n    }\n\n    return 0;\n}\n\nstatic int iobuf_read(neverbleed_iobuf_t *buf, int fd)\n{\n    size_t sz;\n    if (read_nbytes(fd, &sz, sizeof(sz)) != 0)\n        return -1;\n    iobuf_reserve(buf, sz);\n    if (read_nbytes(fd, buf->end, sz) != 0)\n        return -1;\n    buf->end += sz;\n    return 0;\n}\n\nvoid neverbleed_iobuf_dispose(neverbleed_iobuf_t *buf)\n{\n    iobuf_dispose(buf);\n}\n\nstatic void iobuf_transaction_write(neverbleed_iobuf_t *buf, struct st_neverbleed_thread_data_t *thdata)\n{\n    if (iobuf_write(buf, thdata->fd) == -1) {\n        if (errno != 0) {\n            dief(\"write error (%d) %s\", errno, strerror(errno));\n        } else {\n            dief(\"connection closed by daemon\");\n        }\n    }\n}\n\nstatic void iobuf_transaction_read(neverbleed_iobuf_t *buf, struct st_neverbleed_thread_data_t *thdata)\n{\n    iobuf_dispose(buf);\n    if (iobuf_read(buf, thdata->fd) == -1) {\n        if (errno != 0) {\n            dief(\"read error (%d) %s\", errno, strerror(errno));\n        } else {\n            dief(\"connection closed by daemon\");\n        }\n    }\n}\n\n/**\n * Only sends a request, does not read a response\n */\nstatic void iobuf_transaction_no_response(neverbleed_iobuf_t *buf, struct st_neverbleed_thread_data_t *thdata)\n{\n    if (neverbleed_transaction_cb != NULL) {\n        neverbleed_transaction_cb(buf, 1);\n    } else {\n        iobuf_transaction_write(buf, thdata);\n        iobuf_dispose(buf);\n    }\n}\n\n/**\n * Sends a request and reads a response.\n */\nstatic void iobuf_transaction(neverbleed_iobuf_t *buf, struct st_neverbleed_thread_data_t *thdata)\n{\n    if (neverbleed_transaction_cb != NULL) {\n        neverbleed_transaction_cb(buf, 0);\n    } else {\n        iobuf_transaction_write(buf, thdata);\n        iobuf_transaction_read(buf, thdata);\n    }\n}\n\n#if !defined(NAME_MAX) || defined(__linux__)\n/* readdir(3) is known to be thread-safe on Linux and should be thread-safe on a platform that does not have a predefined value for\n   NAME_MAX */\n#define FOREACH_DIRENT(dp, dent)                                                                                                   \\\n    struct dirent *dent;                                                                                                           \\\n    while ((dent = readdir(dp)) != NULL)\n#else\n#define FOREACH_DIRENT(dp, dent)                                                                                                   \\\n    struct {                                                                                                                       \\\n        struct dirent d;                                                                                                           \\\n        char s[NAME_MAX + 1];                                                                                                      \\\n    } dent_;                                                                                                                       \\\n    struct dirent *dentp, *dent = &dent_.d;                                                                                        \\\n    int ret;                                                                                                                       \\\n    while ((ret = readdir_r(dp, dent, &dentp)) == 0 && dentp != NULL)\n#endif /* FOREACH_DIRENT */\n\nstatic void unlink_dir(const char *path)\n{\n    DIR *dp;\n    char buf[PATH_MAX];\n\n    if ((dp = opendir(path)) != NULL) {\n        FOREACH_DIRENT(dp, entp)\n        {\n            if (strcmp(entp->d_name, \".\") == 0 || strcmp(entp->d_name, \"..\") == 0)\n                continue;\n            snprintf(buf, sizeof(buf), \"%s/%s\", path, entp->d_name);\n            unlink_dir(buf);\n        }\n        closedir(dp);\n    }\n    unlink(path);\n    rmdir(path);\n}\n\nstatic void dispose_thread_data(void *_thdata)\n{\n    struct st_neverbleed_thread_data_t *thdata = _thdata;\n\n    assert(thdata->fd >= 0);\n    close(thdata->fd);\n    thdata->fd = -1;\n    free(thdata);\n}\n\nstatic struct st_neverbleed_thread_data_t *get_thread_data(neverbleed_t *nb)\n{\n    struct st_neverbleed_thread_data_t *thdata;\n    pid_t self_pid = getpid();\n    ssize_t r;\n\n    if ((thdata = pthread_getspecific(nb->thread_key)) != NULL) {\n        if (thdata->self_pid == self_pid)\n            return thdata;\n        /* we have been forked! */\n        close(thdata->fd);\n    } else {\n        if ((thdata = malloc(sizeof(*thdata))) == NULL)\n            dief(\"malloc failed\");\n    }\n\n    thdata->self_pid = self_pid;\n#ifdef SOCK_CLOEXEC\n    if ((thdata->fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1)\n        dief(\"socket(2) failed\");\n#else\n    if ((thdata->fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)\n        dief(\"socket(2) failed\");\n    set_cloexec(thdata->fd);\n#endif\n    while (connect(thdata->fd, (void *)&nb->sun_, sizeof(nb->sun_)) != 0)\n        if (errno != EINTR)\n            dief(\"failed to connect to privsep daemon\");\n    while ((r = write(thdata->fd, nb->auth_token, sizeof(nb->auth_token))) == -1 && errno == EINTR)\n        ;\n    if (r != sizeof(nb->auth_token))\n        dief(\"failed to send authentication token\");\n    pthread_setspecific(nb->thread_key, thdata);\n\n    return thdata;\n}\n\nint neverbleed_get_fd(neverbleed_t *nb)\n{\n    struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);\n    return thdata->fd;\n}\n\nvoid neverbleed_transaction_read(neverbleed_t *nb, neverbleed_iobuf_t *buf)\n{\n    struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);\n    iobuf_transaction_read(buf, thdata);\n}\n\nvoid neverbleed_transaction_write(neverbleed_t *nb, neverbleed_iobuf_t *buf)\n{\n    struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);\n    iobuf_transaction_write(buf, thdata);\n}\n\nstatic void do_exdata_free_callback(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)\n{\n    /* when other engines are used, this callback gets called without neverbleed data */\n    if (ptr == NULL)\n        return;\n    struct st_neverbleed_rsa_exdata_t *exdata = ptr;\n    struct st_neverbleed_thread_data_t *thdata = get_thread_data(exdata->nb);\n\n    neverbleed_iobuf_t buf = {NULL};\n    iobuf_push_str(&buf, \"del_pkey\");\n    iobuf_push_num(&buf, exdata->key_index);\n    // \"del_pkey\" command is fire-and-forget, it cannot fail, so doesn't have a response\n    iobuf_transaction_no_response(&buf, thdata);\n\n    free(exdata);\n}\n\nstatic int get_rsa_exdata_idx(void);\nstatic void rsa_exdata_free_callback(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)\n{\n    assert(idx == get_rsa_exdata_idx());\n    do_exdata_free_callback(parent, ptr, ad, idx, argl, argp);\n}\n\nstatic int get_rsa_exdata_idx(void)\n{\n    static volatile int index;\n    NEVERBLEED_MULTITHREAD_ONCE({\n        index = RSA_get_ex_new_index(0, NULL, NULL, NULL, rsa_exdata_free_callback);\n    });\n    return index;\n}\nstatic void get_privsep_data(const RSA *rsa, struct st_neverbleed_rsa_exdata_t **exdata,\n                             struct st_neverbleed_thread_data_t **thdata)\n{\n    *exdata = RSA_get_ex_data(rsa, get_rsa_exdata_idx());\n    if (*exdata == NULL) {\n        errno = 0;\n        dief(\"invalid internal ref\");\n    }\n    *thdata = get_thread_data((*exdata)->nb);\n}\n\nstatic struct {\n    struct {\n        pthread_mutex_t lock;\n        /**\n         * if the slot is use contains a non-NULL key; if not in use, contains the index of the next empty slot or SIZE_MAX if there\n         * are no more empty slots\n         */\n        union {\n            EVP_PKEY *pkey;\n            size_t next_empty;\n        } *slots;\n        size_t num_slots;\n        size_t first_empty;\n    } keys;\n    neverbleed_t *nb;\n} daemon_vars = {{.lock = PTHREAD_MUTEX_INITIALIZER, .first_empty = SIZE_MAX}};\n\nstatic __thread struct {\n    int sockfd;\n#ifdef __linux\n    int epollfd;\n#endif\n    struct {\n        neverbleed_iobuf_t *first, **next;\n    } responses;\n} conn_ctx;\n\nstatic int use_offload = 0;\n\n#if USE_OFFLOAD\n\nstruct engine_request {\n    neverbleed_iobuf_t *buf;\n    int async_fd;\n#ifdef OPENSSL_IS_BORINGSSL\n    struct {\n        RSA *rsa;\n        uint8_t output[512];\n        union {\n            struct {\n                uint8_t padded[512];\n            } digestsign;\n        };\n    } data;\n    async_ctx *async_ctx;\n#else\n    int (*stub)(neverbleed_iobuf_t *);\n    struct {\n        ASYNC_WAIT_CTX *ctx;\n        ASYNC_JOB *job;\n    } async;\n#endif\n};\n\nstatic void offload_free_request(struct engine_request *req)\n{\n#ifdef OPENSSL_IS_BORINGSSL\n    bssl_qat_async_finish_job(req->async_ctx);\n    RSA_free(req->data.rsa);\n#else\n    ASYNC_WAIT_CTX_free(req->async.ctx);\n#endif\n    OPENSSL_cleanse(req, sizeof(*req));\n    free(req);\n}\n\nstatic int do_epoll_ctl(int epollfd, int op, int fd, struct epoll_event *event)\n{\n    int ret;\n    while ((ret = epoll_ctl(epollfd, op, fd, event) != 0) && errno == EINTR)\n        ;\n    return ret;\n}\n\nstatic void register_wait_fd(struct engine_request *req)\n{\n#ifdef OPENSSL_IS_BORINGSSL\n    ASYNC_WAIT_CTX *ctx = req->async_ctx->currjob->waitctx;\n#else\n    ASYNC_WAIT_CTX *ctx = req->async.ctx;\n#endif\n    size_t numfds;\n\n    if (!ASYNC_WAIT_CTX_get_all_fds(ctx, NULL, &numfds) || numfds != 1)\n        dief(\"unexpected number of fds (%zu) requested in async mode\\n\", numfds);\n    if (!ASYNC_WAIT_CTX_get_all_fds(ctx, &req->async_fd, &numfds))\n        dief(\"ASYNC_WAIT_CTX_get_all_fds failed\\n\");\n    struct epoll_event ev = {.events = EPOLLIN, .data.ptr = req};\n    if (do_epoll_ctl(conn_ctx.epollfd, EPOLL_CTL_ADD, req->async_fd, &ev) != 0)\n        dief(\"epoll_ctl failed:%d\\n\", errno);\n}\n\n#endif\n\nstatic int send_responses(int cleanup)\n{\n    neverbleed_iobuf_t *buf;\n    int result = 0;\n\n    /* Send all buffers that have data being filled. The lock is held until everything is being done, as this function can be called\n     * from multiple threads simultaneously. */\n    while ((buf = conn_ctx.responses.first) != NULL && !buf->processing) {\n        if ((conn_ctx.responses.first = buf->next) == NULL)\n            conn_ctx.responses.next = &conn_ctx.responses.first;\n        if (!cleanup && iobuf_write(buf, conn_ctx.sockfd) != 0) {\n            warnf(errno != 0 ? \"write error\" : \"connection closed by client\");\n            result = -1;\n        }\n        iobuf_dispose(buf);\n        free(buf);\n        if (result != 0)\n            break;\n    }\n\n    return result;\n}\n\nstatic RSA *daemon_get_rsa(size_t key_index)\n{\n    RSA *rsa = NULL;\n\n    pthread_mutex_lock(&daemon_vars.keys.lock);\n    if (key_index < daemon_vars.keys.num_slots)\n        rsa = EVP_PKEY_get1_RSA(daemon_vars.keys.slots[key_index].pkey);\n    pthread_mutex_unlock(&daemon_vars.keys.lock);\n\n    return rsa;\n}\n\nsize_t allocate_slot(void)\n{\n    /* expand if all slots are in use */\n    if (daemon_vars.keys.first_empty == SIZE_MAX) {\n        size_t new_capacity = (daemon_vars.keys.num_slots < 4 ? 4 : daemon_vars.keys.num_slots) * 2;\n        if ((daemon_vars.keys.slots = realloc(daemon_vars.keys.slots, sizeof(daemon_vars.keys.slots[0]) * new_capacity)) == NULL)\n            dief(\"no memory\");\n        daemon_vars.keys.first_empty = daemon_vars.keys.num_slots;\n        for (size_t i = daemon_vars.keys.num_slots; i < new_capacity - 1; ++i)\n            daemon_vars.keys.slots[i].next_empty = i + 1;\n        daemon_vars.keys.slots[new_capacity - 1].next_empty = SIZE_MAX;\n        daemon_vars.keys.num_slots = new_capacity;\n    }\n\n    /* detach the first empty slot from the empty list */\n    size_t slot_index = daemon_vars.keys.first_empty;\n    daemon_vars.keys.first_empty = daemon_vars.keys.slots[slot_index].next_empty;\n\n    /* set bogus value in the allocated slot to help figure out what happened upon crash */\n    daemon_vars.keys.slots[slot_index].next_empty = SIZE_MAX - 1;\n\n    return slot_index;\n}\n\nstatic size_t daemon_set_pkey(EVP_PKEY *pkey)\n{\n    assert(pkey != NULL);\n\n    pthread_mutex_lock(&daemon_vars.keys.lock);\n\n    size_t index = allocate_slot();\n    daemon_vars.keys.slots[index].pkey = pkey;\n    EVP_PKEY_up_ref(pkey);\n\n    pthread_mutex_unlock(&daemon_vars.keys.lock);\n\n    return index;\n}\n\nstatic int priv_encdec_proxy(const char *cmd, int flen, const unsigned char *from, unsigned char *_to, RSA *rsa, int padding)\n{\n    struct st_neverbleed_rsa_exdata_t *exdata;\n    struct st_neverbleed_thread_data_t *thdata;\n    neverbleed_iobuf_t buf = {NULL};\n    size_t ret;\n    unsigned char *to;\n    size_t tolen;\n\n    get_privsep_data(rsa, &exdata, &thdata);\n\n    iobuf_push_str(&buf, cmd);\n    iobuf_push_bytes(&buf, from, flen);\n    iobuf_push_num(&buf, exdata->key_index);\n    iobuf_push_num(&buf, padding);\n\n    iobuf_transaction(&buf, thdata);\n\n    if (iobuf_shift_num(&buf, &ret) != 0 || (to = iobuf_shift_bytes(&buf, &tolen)) == NULL) {\n        errno = 0;\n        dief(\"failed to parse response\");\n    }\n    memcpy(_to, to, tolen);\n    iobuf_dispose(&buf);\n\n    return (int)ret;\n}\n\nstatic int priv_encdec_stub(const char *name,\n                            int (*func)(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding),\n                            neverbleed_iobuf_t *buf)\n{\n    unsigned char *from, to[4096];\n    size_t flen;\n    size_t key_index, padding;\n    RSA *rsa;\n    int ret;\n\n    if ((from = iobuf_shift_bytes(buf, &flen)) == NULL || iobuf_shift_num(buf, &key_index) != 0 ||\n        iobuf_shift_num(buf, &padding) != 0) {\n        errno = 0;\n        warnf(\"%s: failed to parse request\", name);\n        return -1;\n    }\n    if ((rsa = daemon_get_rsa(key_index)) == NULL) {\n        errno = 0;\n        warnf(\"%s: invalid key index:%zu\\n\", name, key_index);\n        return -1;\n    }\n    ret = func((int)flen, from, to, rsa, (int)padding);\n    iobuf_dispose(buf);\n    RSA_free(rsa);\n\n    iobuf_push_num(buf, ret);\n    iobuf_push_bytes(buf, to, ret > 0 ? ret : 0);\n\n    return 0;\n}\n\n#if !defined(OPENSSL_IS_BORINGSSL)\n\nstatic int priv_enc_proxy(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)\n{\n    return priv_encdec_proxy(\"priv_enc\", flen, from, to, rsa, padding);\n}\n\nstatic int priv_enc_stub(neverbleed_iobuf_t *buf)\n{\n    return priv_encdec_stub(__FUNCTION__, RSA_private_encrypt, buf);\n}\n\nstatic int priv_dec_proxy(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)\n{\n    return priv_encdec_proxy(\"priv_dec\", flen, from, to, rsa, padding);\n}\n\nstatic int priv_dec_stub(neverbleed_iobuf_t *buf)\n{\n    return priv_encdec_stub(__FUNCTION__, RSA_private_decrypt, buf);\n}\n\nstatic int sign_proxy(int type, const unsigned char *m, unsigned int m_len, unsigned char *_sigret, unsigned *_siglen,\n                      const RSA *rsa)\n{\n    struct st_neverbleed_rsa_exdata_t *exdata;\n    struct st_neverbleed_thread_data_t *thdata;\n    neverbleed_iobuf_t buf = {NULL};\n    size_t ret, siglen;\n    unsigned char *sigret;\n\n    get_privsep_data(rsa, &exdata, &thdata);\n\n    iobuf_push_str(&buf, \"sign\");\n    iobuf_push_num(&buf, type);\n    iobuf_push_bytes(&buf, m, m_len);\n    iobuf_push_num(&buf, exdata->key_index);\n    iobuf_transaction(&buf, thdata);\n\n    if (iobuf_shift_num(&buf, &ret) != 0 || (sigret = iobuf_shift_bytes(&buf, &siglen)) == NULL) {\n        errno = 0;\n        dief(\"failed to parse response\");\n    }\n    memcpy(_sigret, sigret, siglen);\n    *_siglen = (unsigned)siglen;\n    iobuf_dispose(&buf);\n\n    return (int)ret;\n}\n\nstatic int sign_stub(neverbleed_iobuf_t *buf)\n{\n    unsigned char *m, sigret[4096];\n    size_t type, m_len, key_index;\n    RSA *rsa;\n    unsigned siglen = 0;\n    int ret;\n\n    if (iobuf_shift_num(buf, &type) != 0 || (m = iobuf_shift_bytes(buf, &m_len)) == NULL || iobuf_shift_num(buf, &key_index) != 0) {\n        errno = 0;\n        warnf(\"%s: failed to parse request\", __FUNCTION__);\n        return -1;\n    }\n    if ((rsa = daemon_get_rsa(key_index)) == NULL) {\n        errno = 0;\n        warnf(\"%s: invalid key index:%zu\", __FUNCTION__, key_index);\n        return -1;\n    }\n    ret = RSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, rsa);\n    iobuf_dispose(buf);\n    RSA_free(rsa);\n\n    iobuf_push_num(buf, ret);\n    iobuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0);\n\n    return 0;\n}\n\n#endif\n\nstatic EVP_PKEY *create_pkey(neverbleed_t *nb, size_t key_index, const char *ebuf, const char *nbuf)\n{\n    struct st_neverbleed_rsa_exdata_t *exdata;\n    RSA *rsa;\n    EVP_PKEY *pkey;\n    BIGNUM *e = NULL, *n = NULL;\n\n    if ((exdata = malloc(sizeof(*exdata))) == NULL) {\n        fprintf(stderr, \"no memory\\n\");\n        abort();\n    }\n    exdata->nb = nb;\n    exdata->key_index = key_index;\n\n    rsa = RSA_new_method(nb->engine);\n    RSA_set_ex_data(rsa, get_rsa_exdata_idx(), exdata);\n    if (BN_hex2bn(&e, ebuf) == 0) {\n        fprintf(stderr, \"failed to parse e:%s\\n\", ebuf);\n        abort();\n    }\n    if (BN_hex2bn(&n, nbuf) == 0) {\n        fprintf(stderr, \"failed to parse n:%s\\n\", nbuf);\n        abort();\n    }\n    RSA_set0_key(rsa, n, e, NULL);\n#if !defined(OPENSSL_IS_BORINGSSL)\n    RSA_set_flags(rsa, RSA_FLAG_EXT_PKEY);\n#endif\n\n    pkey = EVP_PKEY_new();\n    EVP_PKEY_set1_RSA(pkey, rsa);\n    RSA_free(rsa);\n\n    return pkey;\n}\n\n#ifdef NEVERBLEED_ECDSA\n\nstatic EC_KEY *daemon_get_ecdsa(size_t key_index)\n{\n    EC_KEY *ec_key = NULL;\n\n    pthread_mutex_lock(&daemon_vars.keys.lock);\n    if (key_index < daemon_vars.keys.num_slots)\n        ec_key = EVP_PKEY_get1_EC_KEY(daemon_vars.keys.slots[key_index].pkey);\n    pthread_mutex_unlock(&daemon_vars.keys.lock);\n\n    return ec_key;\n}\n\nstatic int ecdsa_sign_stub(neverbleed_iobuf_t *buf)\n{\n    unsigned char *m, sigret[4096];\n    size_t type, m_len, key_index;\n    EC_KEY *ec_key;\n    unsigned siglen = 0;\n    int ret;\n\n    if (iobuf_shift_num(buf, &type) != 0 || (m = iobuf_shift_bytes(buf, &m_len)) == NULL || iobuf_shift_num(buf, &key_index) != 0) {\n        errno = 0;\n        warnf(\"%s: failed to parse request\", __FUNCTION__);\n        return -1;\n    }\n    if ((ec_key = daemon_get_ecdsa(key_index)) == NULL) {\n        errno = 0;\n        warnf(\"%s: invalid key index:%zu\", __FUNCTION__, key_index);\n        return -1;\n    }\n\n    ret = ECDSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, ec_key);\n    iobuf_dispose(buf);\n\n    EC_KEY_free(ec_key);\n\n    iobuf_push_num(buf, ret);\n    iobuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0);\n\n    return 0;\n}\n\nstatic int get_ecdsa_exdata_idx(void);\nstatic void ecdsa_exdata_free_callback(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)\n{\n    assert(idx == get_ecdsa_exdata_idx());\n    do_exdata_free_callback(parent, ptr, ad, idx, argl, argp);\n}\n\nstatic int get_ecdsa_exdata_idx(void)\n{\n    static volatile int index;\n    NEVERBLEED_MULTITHREAD_ONCE({\n        index = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, ecdsa_exdata_free_callback);\n    });\n    return index;\n}\n\nstatic void ecdsa_get_privsep_data(const EC_KEY *ec_key, struct st_neverbleed_rsa_exdata_t **exdata,\n                                   struct st_neverbleed_thread_data_t **thdata)\n{\n    *exdata = EC_KEY_get_ex_data(ec_key, get_ecdsa_exdata_idx());\n    if (*exdata == NULL) {\n        errno = 0;\n        dief(\"invalid internal ref\");\n    }\n    *thdata = get_thread_data((*exdata)->nb);\n}\n\nstatic int ecdsa_sign_proxy(int type, const unsigned char *m, int m_len, unsigned char *_sigret, unsigned int *_siglen,\n                            const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *ec_key)\n{\n    struct st_neverbleed_rsa_exdata_t *exdata;\n    struct st_neverbleed_thread_data_t *thdata;\n    neverbleed_iobuf_t buf = {NULL};\n    size_t ret, siglen;\n    unsigned char *sigret;\n\n    ecdsa_get_privsep_data(ec_key, &exdata, &thdata);\n\n    /* as far as I've tested so far, kinv and rp are always NULL.\n       Looks like setup_sign will precompute this, but it is only\n       called sign_sig, and it seems to be not used in TLS ECDSA */\n    if (kinv != NULL || rp != NULL) {\n        errno = 0;\n        dief(\"unexpected non-NULL kinv and rp\");\n    }\n\n    iobuf_push_str(&buf, \"ecdsa_sign\");\n    iobuf_push_num(&buf, type);\n    iobuf_push_bytes(&buf, m, m_len);\n    iobuf_push_num(&buf, exdata->key_index);\n    iobuf_transaction(&buf, thdata);\n\n    if (iobuf_shift_num(&buf, &ret) != 0 || (sigret = iobuf_shift_bytes(&buf, &siglen)) == NULL) {\n        errno = 0;\n        dief(\"failed to parse response\");\n    }\n    memcpy(_sigret, sigret, siglen);\n    *_siglen = (unsigned)siglen;\n    iobuf_dispose(&buf);\n\n    return (int)ret;\n}\n\nstatic EVP_PKEY *ecdsa_create_pkey(neverbleed_t *nb, size_t key_index, int curve_name, const void *pubkey, size_t pubkey_len)\n{\n    struct st_neverbleed_rsa_exdata_t *exdata;\n    EC_KEY *ec_key;\n    EC_GROUP *ec_group;\n    EC_POINT *ec_pubkey;\n    EVP_PKEY *pkey;\n\n    if ((exdata = malloc(sizeof(*exdata))) == NULL) {\n        fprintf(stderr, \"no memory\\n\");\n        abort();\n    }\n    exdata->nb = nb;\n    exdata->key_index = key_index;\n\n    ec_key = EC_KEY_new_method(nb->engine);\n    EC_KEY_set_ex_data(ec_key, get_ecdsa_exdata_idx(), exdata);\n\n    ec_group = EC_GROUP_new_by_curve_name(curve_name);\n    if (!ec_group) {\n        fprintf(stderr, \"could not create EC_GROUP\\n\");\n        abort();\n    }\n\n    EC_KEY_set_group(ec_key, ec_group);\n\n    ec_pubkey = EC_POINT_new(ec_group);\n    assert(ec_pubkey != NULL);\n    if (!EC_POINT_oct2point(ec_group, ec_pubkey, pubkey, pubkey_len, NULL)) {\n        fprintf(stderr, \"failed to get ECDSA ephemeral public key from BIGNUM\\n\");\n        abort();\n    }\n    EC_KEY_set_public_key(ec_key, ec_pubkey);\n\n    pkey = EVP_PKEY_new();\n    EVP_PKEY_set1_EC_KEY(pkey, ec_key);\n\n    EC_POINT_free(ec_pubkey);\n    EC_GROUP_free(ec_group);\n    EC_KEY_free(ec_key);\n\n    return pkey;\n}\n\n#endif\n\nstatic EVP_PKEY *daemon_get_pkey(size_t key_index)\n{\n    EVP_PKEY *pkey = NULL;\n\n    pthread_mutex_lock(&daemon_vars.keys.lock);\n    if (key_index < daemon_vars.keys.num_slots) {\n        pkey = daemon_vars.keys.slots[key_index].pkey;\n        EVP_PKEY_up_ref(pkey);\n    }\n    pthread_mutex_unlock(&daemon_vars.keys.lock);\n\n    return pkey;\n}\n\n#if USE_OFFLOAD && defined(OPENSSL_IS_BORINGSSL)\n\nstatic struct engine_request *bssl_offload_create_request(neverbleed_iobuf_t *buf, EVP_PKEY *pkey)\n{\n    RSA *_rsa = EVP_PKEY_get1_RSA(pkey);\n\n    struct engine_request *req = malloc(sizeof(*req));\n    if (req == NULL)\n        dief(\"no memory\\n\");\n    *req = (struct engine_request){.buf = buf, .async_fd = -1, .async_ctx = bssl_qat_async_start_job(), .data.rsa = _rsa};\n\n    if (req->async_ctx == NULL)\n        dief(\"failed to initialize async job\\n\");\n    if (RSA_size(req->data.rsa) > sizeof(req->data.output))\n        dief(\"RSA key too large\\n\");\n\n    return req;\n}\n\nstatic void bssl_offload_digestsign(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const EVP_MD *md, const void *signdata, size_t signlen,\n                                    int rsa_pss)\n{\n    uint8_t digest[EVP_MAX_MD_SIZE];\n    unsigned digestlen;\n\n    { /* generate digest of signdata */\n        EVP_MD_CTX *mdctx = EVP_MD_CTX_new();\n        if (mdctx == NULL)\n            dief(\"no memory\\n\");\n        if (!EVP_DigestInit_ex(mdctx, md, NULL) || !EVP_DigestUpdate(mdctx, signdata, signlen) ||\n            !EVP_DigestFinal_ex(mdctx, digest, &digestlen))\n            dief(\"digest calculation failed\\n\");\n        EVP_MD_CTX_free(mdctx);\n    }\n\n    struct engine_request *req = bssl_offload_create_request(buf, pkey);\n    size_t rsa_size = RSA_size(req->data.rsa), padded_len;\n    int padding;\n\n    /* generate padded octets to be signed */\n    if (rsa_pss) {\n        if (!RSA_padding_add_PKCS1_PSS_mgf1(req->data.rsa, req->data.digestsign.padded, digest, md, md, -1))\n            dief(\"RSA_paddding_add_PKCS1_PSS_mgf1 failed\\n\");\n        padded_len = rsa_size;\n        padding = RSA_NO_PADDING;\n    } else {\n        /* PKCS1 padding */\n        int hash_nid = EVP_MD_type(md), is_alloced;\n        uint8_t *tbs;\n        if (!RSA_add_pkcs1_prefix(&tbs, &padded_len, &is_alloced, hash_nid, digest, digestlen))\n            dief(\"RSA_add_pkcs1_prefix failed\\n\");\n        if (padded_len > rsa_size)\n            dief(\"output of RSA_add_pkcs1_prefix is unexpectedly large\\n\");\n        memcpy(req->data.digestsign.padded, tbs, padded_len);\n        if (is_alloced)\n            OPENSSL_free(tbs);\n        padding = RSA_PKCS1_PADDING;\n    }\n\n    OPENSSL_cleanse(digest, sizeof(digest));\n\n    /* dispatch RSA calculation */\n    RSA_METHOD *meth = bssl_engine_get_rsa_method();\n    if (meth == NULL)\n        dief(\"failed to obtain QAT RSA method table\\n\");\n    size_t siglen;\n    if (!meth->sign_raw(req->data.rsa, &siglen, req->data.output, rsa_size, req->data.digestsign.padded, padded_len, padding))\n        dief(\"sign_raw failure\\n\");\n    if (siglen != 0)\n        dief(\"sign_raw completed synchronously unexpectedly\\n\");\n\n    buf->processing = 1;\n    register_wait_fd(req);\n}\n\nstatic int bssl_offload_decrypt(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const void *src, size_t len)\n{\n    struct engine_request *req = bssl_offload_create_request(buf, pkey);\n\n    /* dispatch RSA calculation */\n    RSA_METHOD *meth = bssl_engine_get_rsa_method();\n    if (meth == NULL)\n        dief(\"failed to obtain QAT RSA method table\\n\");\n    size_t outlen;\n    if (!meth->decrypt(req->data.rsa, &outlen, req->data.output, sizeof(req->data.output), src, len, RSA_NO_PADDING)) {\n        warnf(\"RSA decrypt failure\\n\");\n        goto Exit;\n    }\n    if (outlen != 0)\n        dief(\"RSA decrypt completed synchronously unexpectedly\\n\");\n\n    buf->processing = 1;\n    register_wait_fd(req);\n    return 1;\n\nExit:\n    offload_free_request(req);\n    return 0;\n}\n\n#endif\n\nstatic int digestsign_stub(neverbleed_iobuf_t *buf)\n{\n    size_t key_index, md_nid, signlen;\n    void *signdata;\n    size_t rsa_pss;\n    EVP_PKEY *pkey;\n    const EVP_MD *md;\n\n    /* parse input */\n    if (iobuf_shift_num(buf, &key_index) != 0 || iobuf_shift_num(buf, &md_nid) != 0 ||\n        (signdata = iobuf_shift_bytes(buf, &signlen)) == NULL || iobuf_shift_num(buf, &rsa_pss) != 0) {\n        errno = 0;\n        warnf(\"%s: failed to parse request\", __FUNCTION__);\n        return -1;\n    }\n    if ((pkey = daemon_get_pkey(key_index)) == NULL) {\n        errno = 0;\n        warnf(\"%s: invalid key index:%zu\", __FUNCTION__, key_index);\n        return -1;\n    }\n    if (md_nid != SIZE_MAX) {\n        if ((md = EVP_get_digestbynid((int)md_nid)) == NULL) {\n            errno = 0;\n            warnf(\"%s: invalid EVP_MD nid\", __FUNCTION__);\n            return -1;\n        }\n    } else {\n        md = NULL;\n    }\n\n#if USE_OFFLOAD && defined(OPENSSL_IS_BORINGSSL)\n    if (use_offload && EVP_PKEY_id(pkey) == EVP_PKEY_RSA) {\n        bssl_offload_digestsign(buf, pkey, md, signdata, signlen, rsa_pss);\n        goto Exit;\n    }\n#endif\n\n    /* generate signature */\n    EVP_MD_CTX *mdctx = NULL;\n    EVP_PKEY_CTX *pkey_ctx = NULL;\n    unsigned char digestbuf[4096];\n    size_t digestlen;\n\n    if ((mdctx = EVP_MD_CTX_create()) == NULL)\n        goto Softfail;\n    if (EVP_DigestSignInit(mdctx, &pkey_ctx, md, NULL, pkey) != 1)\n        goto Softfail;\n    if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA && rsa_pss) {\n        if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1 ||\n            EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1)\n            goto Softfail;\n        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, md) != 1)\n            goto Softfail;\n    }\n    /* ED25519 keys can never be loaded, so use the Update -> Final call chain without worrying about backward compatibility */\n    if (EVP_DigestSignUpdate(mdctx, signdata, signlen) != 1)\n        goto Softfail;\n    if (EVP_DigestSignFinal(mdctx, NULL, &digestlen) != 1)\n        goto Softfail;\n    if (sizeof(digestbuf) < digestlen) {\n        warnf(\"%s: digest unexpectedly long as %zu bytes\", __FUNCTION__, digestlen);\n        goto Softfail;\n    }\n    if (EVP_DigestSignFinal(mdctx, digestbuf, &digestlen) != 1)\n        goto Softfail;\n\nRespond: /* build response */\n    iobuf_dispose(buf);\n    iobuf_push_bytes(buf, digestbuf, digestlen);\n    if (mdctx != NULL)\n        EVP_MD_CTX_destroy(mdctx);\nExit: __attribute__((unused))\n    if (pkey != NULL)\n        EVP_PKEY_free(pkey);\n    return 0;\n\nSoftfail:\n    digestlen = 0;\n    goto Respond;\n}\n\nvoid neverbleed_start_digestsign(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const EVP_MD *md, const void *input, size_t len,\n                                 int rsa_pss)\n{\n    struct st_neverbleed_rsa_exdata_t *exdata;\n    struct st_neverbleed_thread_data_t *thdata;\n    const char *cmd = \"digestsign\";\n\n    /* obtain reference */\n    switch (EVP_PKEY_base_id(pkey)) {\n    case EVP_PKEY_RSA: {\n        RSA *rsa = EVP_PKEY_get1_RSA(pkey); /* get0 is available not available in OpenSSL 1.0.2 */\n        get_privsep_data(rsa, &exdata, &thdata);\n        RSA_free(rsa);\n        cmd = \"digestsign-rsa\";\n    } break;\n#ifdef NEVERBLEED_ECDSA\n    case EVP_PKEY_EC:\n        ecdsa_get_privsep_data(EVP_PKEY_get0_EC_KEY(pkey), &exdata, &thdata);\n        break;\n#endif\n    default:\n        dief(\"unexpected private key\");\n        break;\n    }\n\n    *buf = (neverbleed_iobuf_t){NULL};\n    iobuf_push_str(buf, cmd);\n    iobuf_push_num(buf, exdata->key_index);\n    iobuf_push_num(buf, md != NULL ? (size_t)EVP_MD_nid(md) : SIZE_MAX);\n    iobuf_push_bytes(buf, input, len);\n    iobuf_push_num(buf, rsa_pss);\n}\n\nvoid neverbleed_finish_digestsign(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len)\n{\n    const void *src;\n\n    if ((src = iobuf_shift_bytes(buf, digest_len)) == NULL) {\n        errno = 0;\n        dief(\"failed to parse response\");\n    }\n    if ((*digest = malloc(*digest_len)) == NULL)\n        dief(\"no memory\");\n    memcpy(*digest, src, *digest_len);\n\n    iobuf_dispose(buf);\n}\n\nstatic int decrypt_stub(neverbleed_iobuf_t *buf)\n{\n    size_t key_index, srclen;\n    void *src;\n    EVP_PKEY *pkey;\n    RSA *rsa;\n    uint8_t decryptbuf[1024];\n    int decryptlen;\n\n    /* parse input */\n    if (iobuf_shift_num(buf, &key_index) != 0 || (src = iobuf_shift_bytes(buf, &srclen)) == NULL) {\n        errno = 0;\n        warnf(\"%s: failed to parse request\", __FUNCTION__);\n        return -1;\n    }\n    if ((pkey = daemon_get_pkey(key_index)) == NULL) {\n        errno = 0;\n        warnf(\"%s: invalid key index:%zu\", __FUNCTION__, key_index);\n        return -1;\n    }\n\n    rsa = EVP_PKEY_get1_RSA(pkey); /* get0 is available not available in OpenSSL 1.0.2 */\n    assert(rsa != NULL);\n    assert(sizeof(decryptbuf) >= RSA_size(rsa));\n\n#if USE_OFFLOAD && defined(OPENSSL_IS_BORINGSSL)\n    if (use_offload) {\n        if (!bssl_offload_decrypt(buf, pkey, src, srclen))\n            goto Softfail;\n\n        goto Exit;\n    }\n#endif\n\n    if ((decryptlen = RSA_private_decrypt(srclen, src, decryptbuf, rsa, RSA_NO_PADDING)) == -1) {\n        errno = 0;\n        warnf(\"RSA decryption error\");\n        goto Softfail;\n    }\n\nRespond:\n    iobuf_dispose(buf);\n    iobuf_push_bytes(buf, decryptbuf, decryptlen);\nExit: __attribute__((unused))\n    RSA_free(rsa);\n    EVP_PKEY_free(pkey);\n    return 0;\n\nSoftfail:\n    decryptlen = 0;\n    goto Respond;\n}\n\nvoid neverbleed_start_decrypt(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const void *input, size_t len)\n{\n    struct st_neverbleed_rsa_exdata_t *exdata;\n    struct st_neverbleed_thread_data_t *thdata;\n\n    {\n        RSA *rsa = EVP_PKEY_get1_RSA(pkey); /* get0 is available not available in OpenSSL 1.0.2 */\n        assert(rsa != NULL);\n        get_privsep_data(rsa, &exdata, &thdata);\n        RSA_free(rsa);\n    }\n\n    *buf = (neverbleed_iobuf_t){NULL};\n    iobuf_push_str(buf, \"decrypt\");\n    iobuf_push_num(buf, exdata->key_index);\n    iobuf_push_bytes(buf, input, len);\n}\n\nvoid neverbleed_finish_decrypt(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len)\n{\n    neverbleed_finish_digestsign(buf, digest, digest_len);\n}\n\nint neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf)\n{\n    struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);\n    neverbleed_iobuf_t buf = {NULL};\n    int ret = 1;\n    size_t index, type;\n    EVP_PKEY *pkey;\n\n    iobuf_push_str(&buf, \"load_key\");\n    iobuf_push_str(&buf, fn);\n    iobuf_transaction(&buf, thdata);\n\n    if (iobuf_shift_num(&buf, &type) != 0 || iobuf_shift_num(&buf, &index) != 0) {\n        errno = 0;\n        dief(\"failed to parse response\");\n    }\n\n    switch (type) {\n    case NEVERBLEED_TYPE_RSA: {\n        char *estr, *nstr;\n\n        if ((estr = iobuf_shift_str(&buf)) == NULL || (nstr = iobuf_shift_str(&buf)) == NULL) {\n            errno = 0;\n            dief(\"failed to parse response\");\n        }\n        pkey = create_pkey(nb, index, estr, nstr);\n        break;\n    }\n#ifdef NEVERBLEED_ECDSA\n    case NEVERBLEED_TYPE_ECDSA: {\n        size_t curve_name, pubkey_len;\n        void *pubkey_bytes;\n\n        if (iobuf_shift_num(&buf, &curve_name) != 0 || (pubkey_bytes = iobuf_shift_bytes(&buf, &pubkey_len)) == NULL) {\n            errno = 0;\n            dief(\"failed to parse response\");\n        }\n        pkey = ecdsa_create_pkey(nb, index, (int)curve_name, pubkey_bytes, pubkey_len);\n        break;\n    }\n#endif\n    default: {\n        char *errstr;\n\n        if ((errstr = iobuf_shift_str(&buf)) == NULL) {\n            errno = 0;\n            dief(\"failed to parse response\");\n        }\n\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"%s\", errstr);\n        return -1;\n    }\n    }\n\n    iobuf_dispose(&buf);\n\n    /* success */\n    if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"SSL_CTX_use_PrivateKey failed\");\n        ret = 0;\n    }\n\n    EVP_PKEY_free(pkey);\n    return ret;\n}\n\nstatic int load_key_stub(neverbleed_iobuf_t *buf)\n{\n    char *fn;\n    FILE *fp = NULL;\n    RSA *rsa = NULL;\n    size_t key_index = SIZE_MAX;\n    char *estr = NULL, *nstr = NULL, errbuf[NEVERBLEED_ERRBUF_SIZE] = \"\";\n    size_t type = NEVERBLEED_TYPE_ERROR;\n    EVP_PKEY *pkey = NULL;\n#ifdef NEVERBLEED_ECDSA\n    const EC_GROUP *ec_group;\n    void *ec_pubkeybytes = NULL;\n    size_t ec_pubkeylen;\n#endif\n\n    if ((fn = iobuf_shift_str(buf)) == NULL) {\n        warnf(\"%s: failed to parse request\", __FUNCTION__);\n        return -1;\n    }\n\n    if ((fp = fopen(fn, \"rt\")) == NULL) {\n        strerror_r(errno, errbuf, sizeof(errbuf));\n        goto Respond;\n    }\n\n    if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) {\n        snprintf(errbuf, sizeof(errbuf), \"failed to parse the private key\");\n        goto Respond;\n    }\n\n    switch (EVP_PKEY_base_id(pkey)) {\n    case EVP_PKEY_RSA: {\n        const BIGNUM *e, *n;\n\n        rsa = EVP_PKEY_get1_RSA(pkey);\n        type = NEVERBLEED_TYPE_RSA;\n        RSA_get0_key(rsa, &n, &e, NULL);\n        estr = BN_bn2hex(e);\n        nstr = BN_bn2hex(n);\n        break;\n    }\n    case EVP_PKEY_EC: {\n#ifdef NEVERBLEED_ECDSA\n        const EC_POINT *ec_pubkey;\n        EC_KEY *ec_key;\n\n        ec_key = (EC_KEY *)EVP_PKEY_get0_EC_KEY(pkey);\n        type = NEVERBLEED_TYPE_ECDSA;\n        ec_group = EC_KEY_get0_group(ec_key);\n        ec_pubkey = EC_KEY_get0_public_key(ec_key);\n        ec_pubkeylen = EC_POINT_point2oct(ec_group, ec_pubkey, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);\n        if (!(ec_pubkeylen > 0 && (ec_pubkeybytes = malloc(ec_pubkeylen)) != NULL &&\n              EC_POINT_point2oct(ec_group, ec_pubkey, POINT_CONVERSION_UNCOMPRESSED, ec_pubkeybytes, ec_pubkeylen, NULL) ==\n                  ec_pubkeylen))\n            dief(\"failed to serialize EC public key\");\n        break;\n#else\n        snprintf(errbuf, sizeof(errbuf), \"ECDSA support requires OpenSSL >= 1.1.0, LibreSSL >= 2.9.1, or BoringSSL\");\n        goto Respond;\n#endif\n    }\n    default:\n        snprintf(errbuf, sizeof(errbuf), \"unsupported private key: %d\", EVP_PKEY_base_id(pkey));\n        goto Respond;\n    }\n\n    /* store the key */\n    key_index = daemon_set_pkey(pkey);\n\nRespond:\n    iobuf_dispose(buf);\n    iobuf_push_num(buf, type);\n    iobuf_push_num(buf, key_index);\n    switch (type) {\n    case NEVERBLEED_TYPE_RSA:\n        iobuf_push_str(buf, estr != NULL ? estr : \"\");\n        iobuf_push_str(buf, nstr != NULL ? nstr : \"\");\n        break;\n#ifdef NEVERBLEED_ECDSA\n    case NEVERBLEED_TYPE_ECDSA:\n        iobuf_push_num(buf, EC_GROUP_get_curve_name(ec_group));\n        iobuf_push_bytes(buf, ec_pubkeybytes, ec_pubkeylen);\n        break;\n#endif\n    default:\n        iobuf_push_str(buf, errbuf);\n    }\n    if (rsa != NULL)\n        RSA_free(rsa);\n    if (pkey != NULL)\n        EVP_PKEY_free(pkey);\n    if (estr != NULL)\n        OPENSSL_free(estr);\n    if (nstr != NULL)\n        OPENSSL_free(nstr);\n#ifdef NEVERBLEED_ECDSA\n    if (ec_pubkeybytes != NULL)\n        free(ec_pubkeybytes);\n#endif\n    if (fp != NULL)\n        fclose(fp);\n\n    return 0;\n}\n\nint neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership)\n{\n    struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);\n    neverbleed_iobuf_t buf = {NULL};\n    size_t ret;\n\n    iobuf_push_str(&buf, \"setuidgid\");\n    iobuf_push_str(&buf, user);\n    iobuf_push_num(&buf, change_socket_ownership);\n    iobuf_transaction(&buf, thdata);\n\n    if (iobuf_shift_num(&buf, &ret) != 0) {\n        errno = 0;\n        dief(\"failed to parse response\");\n    }\n    iobuf_dispose(&buf);\n\n    return (int)ret;\n}\n\nstatic int setuidgid_stub(neverbleed_iobuf_t *buf)\n{\n    const char *user;\n    size_t change_socket_ownership;\n    struct passwd pwbuf, *pw;\n    char pwstrbuf[65536]; /* should be large enough */\n    int ret = -1;\n\n    if ((user = iobuf_shift_str(buf)) == NULL || iobuf_shift_num(buf, &change_socket_ownership) != 0) {\n        errno = 0;\n        warnf(\"%s: failed to parse request\", __FUNCTION__);\n        return -1;\n    }\n\n    errno = 0;\n    if (getpwnam_r(user, &pwbuf, pwstrbuf, sizeof(pwstrbuf), &pw) != 0) {\n        warnf(\"%s: getpwnam_r failed\", __FUNCTION__);\n        goto Respond;\n    }\n    if (pw == NULL) {\n        warnf(\"%s: failed to obtain information of user:%s\", __FUNCTION__, user);\n        goto Respond;\n    }\n\n    if (change_socket_ownership) {\n        char *dir;\n        if (chown(daemon_vars.nb->sun_.sun_path, pw->pw_uid, pw->pw_gid) != 0)\n            dief(\"chown failed for:%s\", daemon_vars.nb->sun_.sun_path);\n        dir = dirname(daemon_vars.nb->sun_.sun_path);\n        if (chown(dir, pw->pw_uid, pw->pw_gid) != 0)\n            dief(\"chown failed for:%s\", dir);\n        free(dir);\n    }\n\n    /* setuidgid */\n    if (setgid(pw->pw_gid) != 0) {\n        warnf(\"%s: setgid(%d) failed\", __FUNCTION__, (int)pw->pw_gid);\n        goto Respond;\n    }\n    if (initgroups(pw->pw_name, pw->pw_gid) != 0) {\n        warnf(\"%s: initgroups(%s, %d) failed\", __FUNCTION__, pw->pw_name, (int)pw->pw_gid);\n        goto Respond;\n    }\n    if (setuid(pw->pw_uid) != 0) {\n        warnf(\"%s: setuid(%d) failed\\n\", __FUNCTION__, (int)pw->pw_uid);\n        goto Respond;\n    }\n    ret = 0;\n\nRespond:\n    iobuf_dispose(buf);\n    iobuf_push_num(buf, ret);\n    return 0;\n}\n\n#if NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP\nint neverbleed_setaffinity(neverbleed_t *nb, NEVERBLEED_CPU_SET_T *cpuset)\n{\n    struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);\n    neverbleed_iobuf_t buf = {NULL};\n    size_t ret;\n\n    iobuf_push_str(&buf, \"setaffinity\");\n    iobuf_push_bytes(&buf, cpuset, sizeof(*cpuset));\n    iobuf_transaction(&buf, thdata);\n\n    if (iobuf_shift_num(&buf, &ret) != 0) {\n        errno = 0;\n        dief(\"failed to parse response\");\n    }\n    iobuf_dispose(&buf);\n\n    return (int)ret;\n}\n\nstatic int setaffinity_stub(neverbleed_iobuf_t *buf)\n{\n    char *cpuset_bytes;\n    size_t cpuset_len;\n    NEVERBLEED_CPU_SET_T cpuset;\n    int ret = 1;\n\n    if ((cpuset_bytes = iobuf_shift_bytes(buf, &cpuset_len)) == NULL) {\n        errno = 0;\n        warnf(\"%s: failed to parse request\", __FUNCTION__);\n        return -1;\n    }\n\n    assert(cpuset_len == sizeof(NEVERBLEED_CPU_SET_T));\n    memcpy(&cpuset, cpuset_bytes, cpuset_len);\n\n#ifdef __NetBSD__\n    ret = pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset);\n#else\n    ret = pthread_setaffinity_np(pthread_self(), sizeof(NEVERBLEED_CPU_SET_T), &cpuset);\n#endif\n    if (ret != 0) {\n        ret = 1;\n        goto Respond;\n    }\n\n    ret = 0;\n\nRespond:\n    iobuf_dispose(buf);\n    iobuf_push_num(buf, ret);\n    return 0;\n}\n#endif\n\n__attribute__((noreturn)) static void *daemon_close_notify_thread(void *_close_notify_fd)\n{\n    int close_notify_fd = (int)((char *)_close_notify_fd - (char *)NULL);\n    char b;\n    ssize_t r;\n\nRedo:\n    r = read(close_notify_fd, &b, 1);\n    if (r == -1 && errno == EINTR)\n        goto Redo;\n    if (r > 0)\n        goto Redo;\n    /* close or error */\n\n    /* unlink the temporary directory and socket file */\n    unlink_dir(dirname(daemon_vars.nb->sun_.sun_path));\n\n    _exit(0);\n}\n\nstatic int del_pkey_stub(neverbleed_iobuf_t *buf)\n{\n    size_t key_index;\n\n    if (iobuf_shift_num(buf, &key_index) != 0) {\n        errno = 0;\n        warnf(\"%s: failed to parse request\", __FUNCTION__);\n        return -1;\n    }\n\n    pthread_mutex_lock(&daemon_vars.keys.lock);\n    /* set slot as available */\n    if (key_index < daemon_vars.keys.num_slots) {\n        EVP_PKEY_free(daemon_vars.keys.slots[key_index].pkey);\n        daemon_vars.keys.slots[key_index].next_empty = daemon_vars.keys.first_empty;\n        daemon_vars.keys.first_empty = key_index;\n    } else {\n        warnf(\"%s: invalid key index %zu\", __FUNCTION__, key_index);\n    }\n    pthread_mutex_unlock(&daemon_vars.keys.lock);\n\n    return 0;\n}\n\n#define offload_start(stub, buf) ((stub)(buf))\n\n#if USE_OFFLOAD\n#ifdef OPENSSL_IS_BORINGSSL\n\nstatic int offload_resume(struct engine_request *req)\n{\n    size_t outlen;\n\n    if (do_epoll_ctl(conn_ctx.epollfd, EPOLL_CTL_DEL, req->async_fd, NULL) != 0)\n        dief(\"epoll_ctl failed:%d\\n\", errno);\n\n    /* get result */\n    if (bssl_qat_async_ctx_copy_result(req->async_ctx, req->data.output, &outlen, sizeof(req->data.output)) != 0)\n        dief(\"failed to obtain offload result\\n\");\n    if (outlen > sizeof(req->data.output))\n        dief(\"RSA output is unexpectedly large\\n\");\n    /* save the result */\n    iobuf_dispose(req->buf);\n    iobuf_push_bytes(req->buf, req->data.output, outlen);\n\n    req->buf->processing = 0;\n    offload_free_request(req);\n\n    return 0;\n}\n\n#else\n\nstatic int offload_jobfunc(void *_req)\n{\n    struct engine_request *req = *(void **)_req;\n    return req->stub(req->buf);\n}\n\n#undef offload_start\nstatic int offload_start(int (*stub)(neverbleed_iobuf_t *), neverbleed_iobuf_t *buf)\n{\n    /* if engine is not used, run the stub synchronously */\n    if (!use_offload)\n        return stub(buf);\n\n    buf->processing = 1;\n\n    struct engine_request *req = malloc(sizeof(*req));\n    if (req == NULL)\n        dief(\"no memory\");\n    *req = (struct engine_request){.buf = buf, .async_fd = -1, .stub = stub};\n\n    if ((req->async.ctx = ASYNC_WAIT_CTX_new()) == NULL)\n        dief(\"failed to create ASYNC_WAIT_CTX\\n\");\n\n    int ret;\n    switch (ASYNC_start_job(&req->async.job, req->async.ctx, &ret, offload_jobfunc, &req, sizeof(req))) {\n    case ASYNC_PAUSE: /* operation running async; register fd and bail out */\n        register_wait_fd(req);\n        return 0;\n    case ASYNC_FINISH: /* completed synchronously */\n        buf->processing = 0;\n        break;\n    default:\n        dief(\"ASYNC_start_job errored\\n\");\n        break;\n    }\n\n    offload_free_request(req);\n\n    return ret;\n}\n\nstatic int offload_resume(struct engine_request *req)\n{\n    int ret;\n\n    switch (ASYNC_start_job(&req->async.job, req->async.ctx, &ret, offload_jobfunc, &req, sizeof(req))) {\n    case ASYNC_PAUSE:\n        /* assume that wait fd is unchanged */\n        return 0;\n    case ASYNC_FINISH:\n        if (do_epoll_ctl(conn_ctx.epollfd, EPOLL_CTL_DEL, req->async_fd, NULL) != 0)\n            dief(\"epoll_ctl failed:%d\\n\", errno);\n        break;\n    default:\n        dief(\"ASYNC_start_job failed\\n\");\n        break;\n    }\n\n    /* job done */\n    req->buf->processing = 0;\n    offload_free_request(req);\n\n    return ret;\n}\n\n#endif\n#endif\n\n/**\n * This function waits for the provided socket to become readable, then calls `nanosleep(1)` before returning.\n * The intention behind sleep is to provide the application to complete its event loop before the neverbleed process starts\n * spending CPU cycles on the time-consuming RSA operation.\n * In addition, when QAT is used, this function processes completion notifications from QAT and sends the responses.\n */\nstatic int wait_for_data(int cleanup)\n{\n#if USE_OFFLOAD\n\n    struct epoll_event events[20];\n    int has_read = 0, num_events;\n\n    do {\n        while ((num_events = epoll_wait(conn_ctx.epollfd, events, sizeof(events) / sizeof(events[0]), -1)) == -1 &&\n               (errno == EAGAIN || errno == EINTR))\n            ;\n        if (num_events == -1)\n            dief(\"epoll_wait(2):%d\\n\", errno);\n        for (int i = 0; i < num_events; ++i) {\n            if (events[i].data.ptr == NULL) {\n                has_read = 1;\n            } else {\n                struct engine_request *req = events[i].data.ptr;\n                int ret;\n                if ((ret = offload_resume(req)) != 0)\n                    return ret;\n                if ((ret = send_responses(0)) != 0)\n                    return ret;\n            }\n        }\n    } while (!has_read);\n\n#else\n\n    fd_set rfds;\n    int ret;\n    FD_ZERO(&rfds);\n    if (!cleanup)\n        FD_SET(conn_ctx.sockfd, &rfds);\n\n    while ((ret = select(conn_ctx.sockfd + 1, &rfds, NULL, NULL, NULL)) == -1 && (errno == EAGAIN || errno == EINTR))\n        ;\n    if (ret == -1)\n        dief(\"select(2):%d\\n\", errno);\n\n#endif\n\n    // yield when data is available\n    struct timespec tv = {.tv_nsec = 1};\n    (void)nanosleep(&tv, NULL);\n\n    return 0;\n}\n\nstatic void *daemon_conn_thread(void *_sock_fd)\n{\n    conn_ctx.sockfd = (int)((char *)_sock_fd - (char *)NULL);\n    conn_ctx.responses.next = &conn_ctx.responses.first;\n    neverbleed_iobuf_t *buf = NULL;\n\n#if USE_OFFLOAD\n    if ((conn_ctx.epollfd = epoll_create1(EPOLL_CLOEXEC)) == -1)\n        dief(\"epoll_create1 failed:%d\\n\", errno);\n    {\n        struct epoll_event ev = {.events = EPOLLIN};\n        if (do_epoll_ctl(conn_ctx.epollfd, EPOLL_CTL_ADD, conn_ctx.sockfd, &ev) != 0)\n            dief(\"epoll_ctl failed:%d\\n\", errno);\n    }\n#endif\n\n    { /* authenticate */\n        unsigned char auth_token[NEVERBLEED_AUTH_TOKEN_SIZE];\n        if (read_nbytes(conn_ctx.sockfd, &auth_token, sizeof(auth_token)) != 0) {\n            warnf(\"failed to receive authencication token from client\");\n            goto Exit;\n        }\n        if (memcmp(auth_token, daemon_vars.nb->auth_token, NEVERBLEED_AUTH_TOKEN_SIZE) != 0) {\n            warnf(\"client authentication failed\");\n            goto Exit;\n        }\n    }\n\n    while (1) {\n        if (wait_for_data(0) != 0)\n            break;\n        free(buf);\n        buf = malloc(sizeof(*buf));\n        if (buf == NULL)\n            dief(\"no memory\");\n        *buf = (neverbleed_iobuf_t){};\n        char *cmd;\n        if (iobuf_read(buf, conn_ctx.sockfd) != 0) {\n            if (errno != 0)\n                warnf(\"read error\");\n            break;\n        }\n        if ((cmd = iobuf_shift_str(buf)) == NULL) {\n            errno = 0;\n            warnf(\"failed to parse request\");\n            break;\n        }\n#if !defined(OPENSSL_IS_BORINGSSL)\n        if (strcmp(cmd, \"priv_enc\") == 0) {\n            if (offload_start(priv_enc_stub, buf) != 0)\n                break;\n        } else if (strcmp(cmd, \"priv_dec\") == 0) {\n            if (offload_start(priv_dec_stub, buf) != 0)\n                break;\n        } else if (strcmp(cmd, \"sign\") == 0) {\n            if (offload_start(sign_stub, buf) != 0)\n                break;\n#ifdef NEVERBLEED_ECDSA\n        } else if (strcmp(cmd, \"ecdsa_sign\") == 0) {\n            if (ecdsa_sign_stub(buf) != 0)\n                break;\n#endif\n        } else\n#endif\n            if (strcmp(cmd, \"digestsign\") == 0) {\n            if (digestsign_stub(buf) != 0)\n                break;\n        } else if (strcmp(cmd, \"digestsign-rsa\") == 0) {\n            if (offload_start(digestsign_stub, buf) != 0)\n                break;\n        } else if (strcmp(cmd, \"decrypt\") == 0) {\n            if (offload_start(decrypt_stub, buf) != 0)\n                break;\n        } else if (strcmp(cmd, \"load_key\") == 0) {\n            if (load_key_stub(buf) != 0)\n                break;\n        } else if (strcmp(cmd, \"del_pkey\") == 0) {\n            if (del_pkey_stub(buf) != 0)\n                break;\n            iobuf_dispose(buf);\n            // \"del_pkey\" command is fire-and-forget, it cannot fail, so doesn't have a response\n            continue;\n        } else if (strcmp(cmd, \"setuidgid\") == 0) {\n            if (setuidgid_stub(buf) != 0)\n                break;\n#if NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP\n        } else if (strcmp(cmd, \"setaffinity\") == 0) {\n            if (setaffinity_stub(buf) != 0)\n                break;\n#endif\n        } else {\n            warnf(\"unknown command:%s\", cmd);\n            break;\n        }\n        /* add response to chain */\n        *conn_ctx.responses.next = buf;\n        conn_ctx.responses.next = &buf->next;\n        buf = NULL; /* do not free */\n\n        /* send responses if possible */\n        if (send_responses(0) != 0)\n            break;\n    }\n\nExit:\n    free(buf);\n    /* run the loop while async ops are running */\n    while (conn_ctx.responses.first != NULL)\n        wait_for_data(1);\n\n    close(conn_ctx.sockfd);\n#ifdef __linux\n    close(conn_ctx.epollfd);\n#endif\n\n    return NULL;\n}\n\n#if !(defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__))\n#define closefrom my_closefrom\nstatic void my_closefrom(int lowfd)\n{\n    /* On linux, try close_range (2), then fall back to the slow loop if it fails. */\n#if defined(__linux__) && defined(__NR_close_range)\n    if (syscall(__NR_close_range, lowfd, ~0, 0) == 0)\n        return;\n#endif\n\n    for (int fd = (int)sysconf(_SC_OPEN_MAX) - 1; fd >= lowfd; --fd)\n        (void)close(fd);\n}\n#endif\n\nstatic void cleanup_fds(int listen_fd, int close_notify_fd)\n{\n    int maxfd, k;\n\n    maxfd = 0;\n    if (listen_fd > maxfd) {\n        maxfd = listen_fd;\n    }\n    if (close_notify_fd > maxfd) {\n        maxfd = close_notify_fd;\n    }\n    for (k = 0; k < maxfd; k++) {\n        if (k == listen_fd || k == close_notify_fd)\n            continue;\n        switch (k) {\n        case STDOUT_FILENO:\n        case STDERR_FILENO:\n        case STDIN_FILENO:\n            break;\n        default:\n            (void)close(k);\n        }\n    }\n    closefrom(maxfd + 1);\n}\n\n__attribute__((noreturn)) static void daemon_main(int listen_fd, int close_notify_fd, const char *tempdir)\n{\n    pthread_t tid;\n    pthread_attr_t thattr;\n    int sock_fd;\n\n    cleanup_fds(listen_fd, close_notify_fd);\n    pthread_attr_init(&thattr);\n    pthread_attr_setdetachstate(&thattr, 1);\n\n    switch (neverbleed_offload) {\n    case NEVERBLEED_OFFLOAD_QAT_ON:\n    case NEVERBLEED_OFFLOAD_QAT_AUTO: {\n#if USE_OFFLOAD && defined(OPENSSL_IS_BORINGSSL)\n        ENGINE_load_qat();\n        bssl_qat_set_default_string(\"RSA\");\n        use_offload = ENGINE_QAT_PTR_GET() != NULL;\n#elif USE_OFFLOAD && !defined(OPENSSL_IS_BORINGSSL)\n        ENGINE *qat = ENGINE_by_id(\"qatengine\");\n        if (qat != NULL && ENGINE_init(qat)) {\n            if (!ENGINE_set_default_RSA(qat))\n                dief(\"failed to assign RSA operations to QAT\\n\");\n            use_offload = 1;\n        }\n#endif\n        if (!use_offload && neverbleed_offload == NEVERBLEED_OFFLOAD_QAT_ON)\n            dief(\"use of QAT is forced but unavailable\\n\");\n    } break;\n    default:\n        break;\n    }\n\n    if (pthread_create(&tid, &thattr, daemon_close_notify_thread, (char *)NULL + close_notify_fd) != 0)\n        dief(\"pthread_create failed\");\n\n    while (1) {\n        while ((sock_fd = accept(listen_fd, NULL, NULL)) == -1)\n            ;\n        if (pthread_create(&tid, &thattr, daemon_conn_thread, (char *)NULL + sock_fd) != 0)\n            dief(\"pthread_create failed\");\n    }\n}\n\nstatic void set_signal_handler(int signo, void (*cb)(int signo))\n{\n    struct sigaction action;\n\n    memset(&action, 0, sizeof(action));\n    sigemptyset(&action.sa_mask);\n    action.sa_handler = cb;\n    sigaction(signo, &action, NULL);\n}\n\n#ifndef NEVERBLEED_OPAQUE_RSA_METHOD\n\nstatic RSA_METHOD static_rsa_method = {\n    \"privsep RSA method\", /* name */\n    NULL,                 /* rsa_pub_enc */\n    NULL,                 /* rsa_pub_dec */\n    priv_enc_proxy,       /* rsa_priv_enc */\n    priv_dec_proxy,       /* rsa_priv_dec */\n    NULL,                 /* rsa_mod_exp */\n    NULL,                 /* bn_mod_exp */\n    NULL,                 /* init */\n    NULL,                 /* finish */\n    RSA_FLAG_SIGN_VER,    /* flags */\n    NULL,                 /* app data */\n    sign_proxy,           /* rsa_sign */\n    NULL,                 /* rsa_verify */\n    NULL                  /* rsa_keygen */\n};\n\n#endif\n\nint neverbleed_init(neverbleed_t *nb, char *errbuf)\n{\n    int pipe_fds[2] = {-1, -1}, listen_fd = -1;\n    char *tempdir = NULL;\n\n    /* setup the daemon */\n    if (pipe(pipe_fds) != 0) {\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"pipe(2) failed:%s\", strerror(errno));\n        goto Fail;\n    }\n    set_cloexec(pipe_fds[1]);\n    if ((tempdir = strdup(\"/tmp/openssl-privsep.XXXXXX\")) == NULL) {\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"no memory\");\n        goto Fail;\n    }\n    if (mkdtemp(tempdir) == NULL) {\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"failed to create temporary directory under /tmp:%s\", strerror(errno));\n        goto Fail;\n    }\n    memset(&nb->sun_, 0, sizeof(nb->sun_));\n    nb->sun_.sun_family = AF_UNIX;\n    snprintf(nb->sun_.sun_path, sizeof(nb->sun_.sun_path), \"%s/_\", tempdir);\n    RAND_bytes(nb->auth_token, sizeof(nb->auth_token));\n    if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"socket(2) failed:%s\", strerror(errno));\n        goto Fail;\n    }\n    if (bind(listen_fd, (void *)&nb->sun_, sizeof(nb->sun_)) != 0) {\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"failed to bind to %s:%s\", nb->sun_.sun_path, strerror(errno));\n        goto Fail;\n    }\n    if (listen(listen_fd, SOMAXCONN) != 0) {\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"listen(2) failed:%s\", strerror(errno));\n        goto Fail;\n    }\n    nb->daemon_pid = fork();\n    switch (nb->daemon_pid) {\n    case -1:\n        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"fork(2) failed:%s\", strerror(errno));\n        goto Fail;\n    case 0:\n        close(pipe_fds[1]);\n#if defined(__linux__)\n        prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);\n        prctl(PR_SET_PDEATHSIG, SIGTERM);\n#elif defined(__FreeBSD__)\n        int dumpable = PROC_TRACE_CTL_DISABLE;\n        procctl(P_PID, 0, PROC_TRACE_CTL, &dumpable);\n#elif defined(__sun)\n        setpflags(__PROC_PROTECT, 1);\n#elif defined(__APPLE__)\n        ptrace(PT_DENY_ATTACH, 0, 0, 0);\n#endif\n        set_signal_handler(SIGTERM, SIG_IGN);\n        if (neverbleed_post_fork_cb != NULL)\n            neverbleed_post_fork_cb();\n        daemon_vars.nb = nb;\n        daemon_main(listen_fd, pipe_fds[0], tempdir);\n        break;\n    default:\n        break;\n    }\n    close(listen_fd);\n    listen_fd = -1;\n    close(pipe_fds[0]);\n    pipe_fds[0] = -1;\n\n#if defined(OPENSSL_IS_BORINGSSL)\n    nb->engine = NULL;\n#else\n    { /* setup engine */\n        const RSA_METHOD *rsa_default_method;\n        RSA_METHOD *rsa_method;\n#ifdef NEVERBLEED_ECDSA\n        const EC_KEY_METHOD *ecdsa_default_method;\n        EC_KEY_METHOD *ecdsa_method;\n#endif\n\n#ifdef NEVERBLEED_OPAQUE_RSA_METHOD\n        rsa_default_method = RSA_PKCS1_OpenSSL();\n        rsa_method = RSA_meth_dup(rsa_default_method);\n\n        RSA_meth_set1_name(rsa_method, \"privsep RSA method\");\n        RSA_meth_set_priv_enc(rsa_method, priv_enc_proxy);\n        RSA_meth_set_priv_dec(rsa_method, priv_dec_proxy);\n        RSA_meth_set_sign(rsa_method, sign_proxy);\n#else\n        rsa_default_method = RSA_PKCS1_SSLeay();\n        rsa_method = &static_rsa_method;\n\n        rsa_method->rsa_pub_enc = rsa_default_method->rsa_pub_enc;\n        rsa_method->rsa_pub_dec = rsa_default_method->rsa_pub_dec;\n        rsa_method->rsa_verify = rsa_default_method->rsa_verify;\n        rsa_method->bn_mod_exp = rsa_default_method->bn_mod_exp;\n#endif\n\n#ifdef NEVERBLEED_ECDSA\n        ecdsa_default_method = EC_KEY_get_default_method();\n        ecdsa_method = EC_KEY_METHOD_new(ecdsa_default_method);\n\n        /* it seems sign_sig and sign_setup is not used in TLS ECDSA. */\n        EC_KEY_METHOD_set_sign(ecdsa_method, ecdsa_sign_proxy, NULL, NULL);\n#endif\n\n        if ((nb->engine = ENGINE_new()) == NULL || !ENGINE_set_id(nb->engine, \"neverbleed\") ||\n            !ENGINE_set_name(nb->engine, \"privilege separation software engine\") || !ENGINE_set_RSA(nb->engine, rsa_method)\n#ifdef NEVERBLEED_ECDSA\n            || !ENGINE_set_EC(nb->engine, ecdsa_method)\n#endif\n        ) {\n            snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, \"failed to initialize the OpenSSL engine\");\n            goto Fail;\n        }\n        ENGINE_add(nb->engine);\n    }\n#endif\n\n    /* setup thread key */\n    pthread_key_create(&nb->thread_key, dispose_thread_data);\n\n    free(tempdir);\n    return 0;\nFail:\n    if (pipe_fds[0] != -1)\n        close(pipe_fds[0]);\n    if (pipe_fds[1] != -1)\n        close(pipe_fds[1]);\n    if (tempdir != NULL) {\n        unlink_dir(tempdir);\n        free(tempdir);\n    }\n    if (listen_fd != -1)\n        close(listen_fd);\n    if (nb->engine != NULL) {\n        ENGINE_free(nb->engine);\n        nb->engine = NULL;\n    }\n    return -1;\n}\n\nvoid (*neverbleed_post_fork_cb)(void) = NULL;\nvoid (*neverbleed_transaction_cb)(neverbleed_iobuf_t *, int) = NULL;\nenum neverbleed_offload_type neverbleed_offload = NEVERBLEED_OFFLOAD_OFF;\n"
  },
  {
    "path": "neverbleed.h",
    "content": "/*\n * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#ifndef NEVERBLEED_H\n#define NEVERBLEED_H\n\n#include <pthread.h>\n#include <sys/un.h>\n#include <openssl/engine.h>\n\n#ifdef __FreeBSD__\n#include <pthread_np.h>\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__FreeBSD__) || defined(__NetBSD__)\n#define NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP 1\n#if defined(__linux__)\n#define NEVERBLEED_CPU_SET_T cpu_set_t\n#else\n#define NEVERBLEED_CPU_SET_T cpuset_t\n#endif\n#endif\n\n#define NEVERBLEED_ERRBUF_SIZE (256)\n#define NEVERBLEED_AUTH_TOKEN_SIZE 32\n\ntypedef struct st_neverbleed_t {\n    ENGINE *engine;\n    pid_t daemon_pid;\n    struct sockaddr_un sun_;\n    pthread_key_t thread_key;\n    unsigned char auth_token[NEVERBLEED_AUTH_TOKEN_SIZE];\n} neverbleed_t;\n\ntypedef struct st_neverbleed_iobuf_t {\n    char *buf;\n    char *start;\n    char *end;\n    size_t capacity;\n    struct st_neverbleed_iobuf_t *next;\n    unsigned processing : 1;\n} neverbleed_iobuf_t;\n\n/**\n * initializes the privilege separation engine (returns 0 if successful)\n */\nint neverbleed_init(neverbleed_t *nb, char *errbuf);\n/**\n * loads a private key file (returns 1 if successful)\n */\nint neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf);\n/**\n * setuidgid (also changes the file permissions so that `user` can connect to the daemon, if change_socket_ownership is non-zero)\n */\nint neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership);\n\n/**\n * builds a digestsign request\n */\nvoid neverbleed_start_digestsign(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const EVP_MD *md, const void *input, size_t len,\n                                 int rsa_pss);\n/**\n * parses a digestsign response\n */\nvoid neverbleed_finish_digestsign(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len);\n/**\n * builds a RSA decrypt request\n */\nvoid neverbleed_start_decrypt(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const void *input, size_t len);\n/**\n * parses a decrypt response\n */\nvoid neverbleed_finish_decrypt(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len);\n\n#if NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP\n/**\n * set the cpu affinity for the neverbleed thread (returns 0 if successful)\n */\nint neverbleed_setaffinity(neverbleed_t *nb, NEVERBLEED_CPU_SET_T *cpuset);\n#endif\n\n/**\n * an optional callback that can be registered by the application for doing stuff immediately after the neverbleed process is being\n * spawned\n */\nextern void (*neverbleed_post_fork_cb)(void);\n/**\n * An optional callback used for replacing `iobuf_transaction`; i.e., the logic that sends the request and receives the response.\n *\n * If `responseless` equals `1`, the ownership of stack-allocated `req` is given to the callback. In this case, `req` must be free'd using `neverbleed_iobuf_dispose`\n */\nextern void (*neverbleed_transaction_cb)(neverbleed_iobuf_t *req, int responseless);\n\ntypedef void (*neverbleed_cb)(int);\n\nint neverbleed_get_fd(neverbleed_t *nb);\nstatic size_t neverbleed_iobuf_size(neverbleed_iobuf_t *buf);\nvoid neverbleed_iobuf_dispose(neverbleed_iobuf_t *buf);\nvoid neverbleed_transaction_read(neverbleed_t *nb, neverbleed_iobuf_t *buf);\nvoid neverbleed_transaction_write(neverbleed_t *nb, neverbleed_iobuf_t *buf);\n\n/**\n * if set to a non-zero value, RSA operations are offloaded\n */\nextern enum neverbleed_offload_type {\n    NEVERBLEED_OFFLOAD_OFF = 0,\n    NEVERBLEED_OFFLOAD_QAT_ON,\n    NEVERBLEED_OFFLOAD_QAT_AUTO,\n} neverbleed_offload;\n\n/* inline function definitions */\n\ninline size_t neverbleed_iobuf_size(neverbleed_iobuf_t *buf)\n{\n    return buf->end - buf->start;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "t/assets/test-ecdsa.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBfTCCASOgAwIBAgIUWGYyQm67cza/h5J4I+76bYqFnLAwCgYIKoZIzj0EAwIw\nFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDMwNDIzMTMwM1oXDTM2MDMwMTIz\nMTMwM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D\nAQcDQgAEhN/grDfl7Qi4fZEUklFFJ6lcGpOjWrwGPa5WPyUPS2UVx6qXLg5Tgt78\nb67RZO58CQI7amC7iogLtkhSzONs9KNTMFEwHQYDVR0OBBYEFOERv7PPj3kgMch+\ngv2c9l7Yx60HMB8GA1UdIwQYMBaAFOERv7PPj3kgMch+gv2c9l7Yx60HMA8GA1Ud\nEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAOUITjVTvw/wcdqyQPlzSPU/\n249h69Z7eh8XUB2dxvxkAiAaJXL6e4GYo+Kz9dE0lcnvlJtCe7Cts+IPTSci0umh\nLw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/assets/test-ecdsa.key",
    "content": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIEqdJE0WtAI2i+7ugUk7qfj+3SDmA4HmSzYSgBz0ChtwoAoGCCqGSM49\nAwEHoUQDQgAEhN/grDfl7Qi4fZEUklFFJ6lcGpOjWrwGPa5WPyUPS2UVx6qXLg5T\ngt78b67RZO58CQI7amC7iogLtkhSzONs9A==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "t/assets/test.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDCTCCAfGgAwIBAgIUMBYfyg+s4uddFNCoLEyBascfr9EwDQYJKoZIhvcNAQEL\nBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDMwMTAzNTI0NVoXDTM2MDIy\nNzAzNTI0NVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAvthlR+U55PNRcbA4LfSIJCU0L/pNk5Mq4wReXDzUdirO\nd0adQ8D2FOSWMBuxbhMyRLmbjZRPL5qD52vbgiVeKUEJ1AOHCQznRi8qgmdKN5cF\nzFUYqdCJs8HrE7XVPzN9/vi0zNStyvRdJ8zT6BrFelIt/c/do1XOmU+dxujzbgdm\nWsMrodQzS/6Km3XVXfQ77I5blPg+zkkGo2Tqms5e0T4YvrpdgB7nW58cmcTVuhuK\nnox4BkFWcwyJ1EN4VnnnPpX6iyhJrhkfYCy2NMloSprHv2/KFMH42mOTn7TfHXhj\nSBlRAiAy5BiE6DFS+TjbdST9/cQ17V5FHen6TkS8hwIDAQABo1MwUTAdBgNVHQ4E\nFgQUNeZr1Y7Sd2B1sCsHpOjbOA2qzjAwHwYDVR0jBBgwFoAUNeZr1Y7Sd2B1sCsH\npOjbOA2qzjAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEArQRu\nzl7Zn/FqMwECSaRPXXHOqCWcGvQUy1cfZkfn4BnSBOPr7AqUbf3kqTuNyj0IjvLH\nYyA15/Lcjz1qtKBl26coGOQ7WqSflDAdW0TptcjF3YgOMe+oxdiCw6sHJu1jN4td\nWp/DW55AOBAoB0yGhkPd1d80AgeYOaflZBNoRkU713MJVNoGRFHeSaRYm6UAu5DB\n0NYK1OSIEkGOBRHbKOur8vuXThj8wD6V+NIf1fa++4oLD5gohD+L71iW2TvOcbic\neMepCbZ9xTYSmXt2tyI6Bn0rpZNHDzmKsIybO4uJtPE0pBjuVidu/nNUgOIwFeC6\ncqDol1kEF3l1JusTbw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "t/assets/test.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+2GVH5Tnk81Fx\nsDgt9IgkJTQv+k2TkyrjBF5cPNR2Ks53Rp1DwPYU5JYwG7FuEzJEuZuNlE8vmoPn\na9uCJV4pQQnUA4cJDOdGLyqCZ0o3lwXMVRip0ImzwesTtdU/M33++LTM1K3K9F0n\nzNPoGsV6Ui39z92jVc6ZT53G6PNuB2Zawyuh1DNL/oqbddVd9DvsjluU+D7OSQaj\nZOqazl7RPhi+ul2AHudbnxyZxNW6G4qejHgGQVZzDInUQ3hWeec+lfqLKEmuGR9g\nLLY0yWhKmse/b8oUwfjaY5OftN8deGNIGVECIDLkGIToMVL5ONt1JP39xDXtXkUd\n6fpORLyHAgMBAAECggEAIeZ7mzlPPumv4nOMjzE8S7tmGU2roRbHy/q3LkhJ71Gs\nSki7X9EzhUOToCkTK/vx9n5H8O7S4CBg5OdmXyh3IPniHoyf3I4zuOZg9TgW2WgU\nyhalomieRVWhhedLYYYqj/Oq3iW7V21v7MV4MOcshA18CPV3J+/ymo4ndzFjKHyO\nZbigrfnOkzNn5DPNXKUGDRQ3JCk7gxqYrBk9pPHwyc9MLm9zwcStTVQr6jAcisrB\nwVtztqjmDpQZaZHaZoGCMHX0aqxkYzAXW5klu1w5VMnvdZBPDa+USH7g5G0pxRfl\nyDB5FbSKYIZPOXcg5XypDoN9hMFMyajHJEqbnBqo3QKBgQDoDsdp9PG/obgvUVuh\nzl4PKi91ziGnF2yULiPPtpfF9HlxYgClrUbl6l1OehprrSPMtUWNcV0YxKHsrHvI\n/J/ta17kMp65E3UQ6dC/1rHujZtXumuAt6tN3+bmskDCNXm9Hd7xbqEU0kgCEE3D\nltKraFJPRZYjCsQfJYmwp3+9hQKBgQDSiRgJykwBDTLWk16n4TTDuGoKs31UiZ0b\n5mKiiNSrUVY6zVAVXY9b8X54laSdYMyVuLImg4F6Tx47FgGKxfJerFgbwK01tJcB\nFowixtq78JTHeZp3ejY2sTCXb7NrLM9GE5KJkt34hQzvWS8fLSX/P/MKXp4UlE3j\n7o1N0y8ZmwKBgQDT4qqyVLVoBIHosqC4XXYE4r/zEQQpTXoW0wpf3pk2ZsN8g7+T\nh2P1CsmnnlYBe1X01I9tVtVqiCBRuixMmF5uqls6gf3rf5ikmNnCUIanCyWMNOtz\n3EDOGmL6wkffDHTb+SpXyGvMVzTorXpT3KL/X4HIYAF2fZ4V0nCmnEpHAQKBgGPz\n5k+vlUnihEJPEN9PEgfho6aU9GmQM+CtDiLwJ1d2dCPSmbSrCIa0LkD9enulGzvx\nxdJ3GJ+CtG2E0xKZS6oa1HHIlfMrW42OsNVJ50rWuyvA1c7nXJm2ocUjqOC3E2jH\nnghmi6+TK0Lu6mo4uxNlvvMrXI2Uoy4VcUyDeJcFAoGBAKiOMiqe1Koh28nIxKxk\nG+BmsSWZSQzrUALd5BCPajMHOpSStbMNobuNj3yaTO5Nqmda9CmNQVmpZ3rTZXb8\nUW12qOSqsUjtqUkn+coIM9ZJANSeqA8twdAzRCXkviUT9bnvdhaghBRPox9jXvo2\ngCtoHKhngAQedpkREPmGcqPC\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "t/test_handshake.t",
    "content": "#!/usr/bin/env perl\n\nuse strict;\nuse warnings;\nuse Net::EmptyPort qw(check_port empty_port);\nuse POSIX \":sys_wait_h\";\nuse Scope::Guard qw(scope_guard);\nuse Test::More;\nuse Time::HiRes qw(sleep);\n\nmy $server = \"./test-neverbleed\";\n\nsub spawn_server {\n    my ($port, $crt, $key) = @_;\n    my $pid = fork;\n    die \"fork failed:$!\"\n        unless defined $pid;\n    if ($pid == 0) {\n        exec $server, \"privsep\", $port, $crt, $key;\n        die \"failed to exec $server:$!\";\n    }\n    while (!check_port($port)) {\n        sleep 0.1;\n    }\n    return scope_guard(sub {\n        kill 9, $pid;\n        while (waitpid($pid, 0) != $pid) {}\n    });\n}\n\nsub doit {\n    my ($port, $crt, $args) = @_;\n\n    open my $fh, \"-|\", \"printf 'GET / HTTP/1.0\\\\r\\\\n\\\\r\\\\n' | openssl s_client $args -connect 127.0.0.1:$port -CAfile $crt -verify_return_error -ign_eof 2>&1\"\n        or die \"failed to start s_client:$!\";\n    my $content = do { local $/; <$fh> };\n    close $fh;\n\n    like($content, qr/Verification: OK/, \"TLS verification passed\");\n    like($content, qr/HTTP\\/1\\.0 200 OK/, \"HTTP 200 response received\");\n    like($content, qr/hello/, \"Response contains expected content\");\n};\n\nsubtest \"RSA\" => sub {\n    my $port = empty_port();\n    my $crt = \"./t/assets/test.crt\";\n    my $key = \"./t/assets/test.key\";\n    my $guard = spawn_server($port, $crt, $key);\n\n    subtest \"sign\" => sub {\n        doit($port, $crt, \"\");\n    };\n\n    subtest \"decrypt\" => sub {\n        doit($port, $crt, \"-no_tls1_3 -cipher AES128-SHA\");\n    };\n};\n\nsubtest \"ECDSA\" => sub {\n    my $port = empty_port();\n    my $crt = \"./t/assets/test-ecdsa.crt\";\n    my $key = \"./t/assets/test-ecdsa.key\";\n    my $guard = spawn_server($port, $crt, $key);\n\n    subtest \"sign\" => sub {\n        doit($port, $crt, \"\");\n    };\n};\n\ndone_testing;\n"
  },
  {
    "path": "test.c",
    "content": "/*\n * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#include <assert.h>\n#include <errno.h>\n#include <netinet/in.h>\n#include <pthread.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#define OPENSSL_SUPPRESS_DEPRECATED\n#include <openssl/opensslconf.h>\n#include <openssl/opensslv.h>\n\n#if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_EC) &&                                                            \\\n    (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2090100fL)\n#define NEVERBLEED_TEST_ECDSA\n#endif\n\n#ifdef NEVERBLEED_TEST_ECDSA\n#include <openssl/ec.h>\n#endif\n#include <openssl/evp.h>\n#include <openssl/ssl.h>\n\n#include \"neverbleed.h\"\n\nstatic neverbleed_t nb;\n\n#ifdef OPENSSL_IS_BORINGSSL\nstatic void setup_boringssl_key_method(SSL_CTX *ctx);\nstatic int boringssl_get_pkey_index(void);\nstatic void boringssl_free_pkey_callback(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp);\n#endif\n\n#ifdef NEVERBLEED_TEST_ECDSA\nstatic void setup_ecc_key(SSL_CTX *ssl_ctx)\n{\n    int nid = NID_X9_62_prime256v1;\n    EC_KEY *key = EC_KEY_new_by_curve_name(nid);\n    if (key == NULL) {\n        fprintf(stderr, \"Failed to create curve \\\"%s\\\"\\n\", OBJ_nid2sn(nid));\n        return;\n    }\n    SSL_CTX_set_tmp_ecdh(ssl_ctx, key);\n    EC_KEY_free(key);\n}\n#endif\n\n#ifdef OPENSSL_IS_BORINGSSL\nstatic void boringssl_free_pkey_callback(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)\n{\n    if (ptr != NULL)\n        EVP_PKEY_free(ptr);\n}\n\nstatic int boringssl_get_pkey_index(void)\n{\n    static volatile int index;\n    static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;\n    pthread_mutex_lock(&mutex);\n    if (!index) {\n        index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, boringssl_free_pkey_callback);\n    }\n    pthread_mutex_unlock(&mutex);\n    return index;\n}\n\nstatic enum ssl_private_key_result_t boringssl_sign(SSL *ssl, uint8_t *out, size_t *outlen, size_t max_out,\n                                                    uint16_t signature_algorithm, const uint8_t *in, size_t len)\n{\n    neverbleed_iobuf_t buf = {NULL};\n    void *digest = NULL;\n    size_t digestlen = 0;\n    SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);\n    EVP_PKEY *pkey = SSL_CTX_get_ex_data(ctx, boringssl_get_pkey_index());\n    const EVP_MD *md = SSL_get_signature_algorithm_digest(signature_algorithm);\n    int rsa_pss = SSL_is_signature_algorithm_rsa_pss(signature_algorithm);\n\n    neverbleed_start_digestsign(&buf, pkey, md, in, len, rsa_pss);\n    neverbleed_transaction_write(&nb, &buf);\n    neverbleed_transaction_read(&nb, &buf);\n    neverbleed_finish_digestsign(&buf, &digest, &digestlen);\n\n    assert(digestlen <= max_out);\n    memcpy(out, digest, digestlen);\n    *outlen = digestlen;\n\n    free(digest);\n    return ssl_private_key_success;\n}\n\nstatic enum ssl_private_key_result_t boringssl_decrypt(SSL *ssl, uint8_t *out, size_t *outlen, size_t max_out, const uint8_t *in,\n                                                       size_t len)\n{\n    neverbleed_iobuf_t buf = {NULL};\n    void *digest = NULL;\n    size_t digestlen = 0;\n    SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);\n    EVP_PKEY *pkey = SSL_CTX_get_ex_data(ctx, boringssl_get_pkey_index());\n\n    neverbleed_start_decrypt(&buf, pkey, in, len);\n    neverbleed_transaction_write(&nb, &buf);\n    neverbleed_transaction_read(&nb, &buf);\n    neverbleed_finish_decrypt(&buf, &digest, &digestlen);\n\n    assert(digestlen <= max_out);\n    memcpy(out, digest, digestlen);\n    *outlen = digestlen;\n\n    free(digest);\n    return ssl_private_key_success;\n}\n\nstatic void setup_boringssl_key_method(SSL_CTX *ctx)\n{\n    EVP_PKEY *pkey = SSL_CTX_get0_privatekey(ctx);\n    EVP_PKEY_up_ref(pkey);\n    SSL_CTX_set_ex_data(ctx, boringssl_get_pkey_index(), pkey);\n    static const SSL_PRIVATE_KEY_METHOD meth = {\n        .sign = boringssl_sign,\n        .decrypt = boringssl_decrypt,\n    };\n    SSL_CTX_set_private_key_method(ctx, &meth);\n}\n#endif\n\nint dumb_https_server(unsigned short port, SSL_CTX *ctx)\n{\n    int listen_fd, reuse_flag;\n    struct sockaddr_in sin = {};\n\n    if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {\n        fprintf(stderr, \"failed to create socket:%s\\n\", strerror(errno));\n        return 111;\n    }\n    reuse_flag = 1;\n    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_flag, sizeof(reuse_flag));\n    sin.sin_family = AF_INET;\n    sin.sin_addr.s_addr = htonl(0x7f000001);\n    sin.sin_port = htons(port);\n    if (bind(listen_fd, (void *)&sin, sizeof(sin)) != 0) {\n        fprintf(stderr, \"bind failed:%s\\n\", strerror(errno));\n        return 111;\n    }\n    if (listen(listen_fd, SOMAXCONN) != 0) {\n        fprintf(stderr, \"listen failed:%s\\n\", strerror(errno));\n        return 111;\n    }\n\n    while (1) {\n        int conn_fd;\n        SSL *ssl;\n        char buf[4096];\n        /* accept connection */\n        while ((conn_fd = accept(listen_fd, NULL, NULL)) == -1 && errno == EINTR)\n            ;\n        if (conn_fd == -1) {\n            fprintf(stderr, \"accept(2) failed:%s\\n\", strerror(errno));\n            return 111;\n        }\n        ssl = SSL_new(ctx);\n        SSL_set_fd(ssl, conn_fd);\n        if (SSL_accept(ssl) == 1) {\n            SSL_read(ssl, buf, sizeof(buf));\n            const char *resp =\n                \"HTTP/1.0 200 OK\\r\\nContent-Length: 6\\r\\nConnection: close\\r\\nContent-Type: text/plain\\r\\n\\r\\nhello\\n\";\n            SSL_write(ssl, resp, strlen(resp));\n            SSL_shutdown(ssl);\n        } else {\n            fprintf(stderr, \"SSL_accept failed\\n\");\n        }\n        SSL_free(ssl);\n        close(conn_fd);\n    }\n}\n\nint main(int argc, char **argv)\n{\n    unsigned short port;\n    SSL_CTX *ctx;\n    char errbuf[NEVERBLEED_ERRBUF_SIZE];\n    int use_privsep;\n\n    /* initialization */\n    /* FIXME: These APIs are deprecated in favor of OPENSSL_init_crypto in 1.1.0. */\n    SSL_load_error_strings();\n    SSL_library_init();\n    OpenSSL_add_all_algorithms();\n    if (neverbleed_init(&nb, errbuf) != 0) {\n        fprintf(stderr, \"openssl_privsep_init: %s\\n\", errbuf);\n        return 111;\n    }\n    ctx = SSL_CTX_new(SSLv23_server_method());\n    SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);\n#ifdef NEVERBLEED_TEST_ECDSA\n    setup_ecc_key(ctx);\n#endif\n\n    /* parse args */\n    if (argc != 5) {\n        fprintf(stderr, \"Usage: %s <internal|privsep> <port> <certificate-chain-file> <private-key-file>\\n\", argv[0]);\n        return 111;\n    }\n    if (strcmp(argv[1], \"internal\") == 0) {\n        use_privsep = 0;\n    } else if (strcmp(argv[1], \"privsep\") == 0) {\n        use_privsep = 1;\n    } else {\n        fprintf(stderr, \"unknown mode:%s\\n\", argv[1]);\n        return 111;\n    }\n    if (sscanf(argv[2], \"%hu\", &port) != 1) {\n        fprintf(stderr, \"failed to parse port:%s\\n\", argv[2]);\n        return 111;\n    }\n    if (SSL_CTX_use_certificate_chain_file(ctx, argv[3]) != 1) {\n        fprintf(stderr, \"failed to load certificate chain file:%s\\n\", argv[3]);\n        return 111;\n    }\n    if (use_privsep) {\n        if (neverbleed_load_private_key_file(&nb, ctx, argv[4], errbuf) != 1) {\n            fprintf(stderr, \"failed to load private key from file:%s:%s\\n\", argv[4], errbuf);\n            return 111;\n        }\n#ifdef OPENSSL_IS_BORINGSSL\n        setup_boringssl_key_method(ctx);\n#endif\n    } else {\n        if (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) != 1) {\n            fprintf(stderr, \"failed to load private key from file:%s\\n\", argv[4]);\n            return 111;\n        }\n    }\n\n    /* start the httpd */\n    return dumb_https_server(port, ctx);\n}\n"
  }
]