[
  {
    "path": ".ackrc",
    "content": "--ignore-dir=build\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "---\nname: Build examples\non:\n  push:\n  pull_request:\n     branches:\n        - main\n\n  # include workflow_dispatch to enable manual trigger from Web UI.\n  workflow_dispatch:\njobs:\n  pre_build:\n\n    # A job to see if the entrire jobs should be skipped. each job for a\n    # target should have:\n    #\n    # needs: pre_build\n    # if: ${{ needs.pre_build.outputs.should_skip != 'true' }}\n    runs-on: ubuntu-latest\n    outputs:\n      should_skip: ${{ steps.skip_check.outputs.should_skip }}\n    steps:\n      - id: skip_check\n        uses: fkirc/skip-duplicate-actions@v3.4.0\n        with:\n          concurrent_skipping: same_content\n\n          # if the change includes documentation, or ruby files changes only, skip.\n          paths_ignore: '[\"docs/**\", \"**/*.md\", \"*.md\", \"**/*.rb\"]'\n\n          # but do not skip if the triggered event is one of these.\n          do_not_skip: '[\"workflow_dispatch\", \"schedule\", \"pull_request\"]'\n\n  # XXX create multiple jobs for major versions\n  #\n  # for those who want to _refactor_ the jobs:\n  #\n  # in the previous CI implementation, all builds share a single build\n  # process. that way, you can remove duplications. however, every time a\n  # version changes the build process, the change affects all other build\n  # processes. I am tired of tracking changes and workarounds in the build\n  # process. the result is many `if`s. assuming major version does not change\n  # (a lot) its build process, creating multiple jobs, and using matrix is the\n  # only sane way. as GitHub Actions does not support reusable steps, there\n  # are many duplications. but no need to modify the entire build process to\n  # adopt changes in master.\n  build_esp32_master:\n    needs: pre_build\n    if: ${{ needs.pre_build.outputs.should_skip != 'true' }}\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        target:\n          - esp32\n        ip_version:\n          - ipv4\n          - ipv6\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n\n      - name: Install python\n        uses: actions/setup-python@v2\n        with:\n\n          # XXX install python 3.8 because the official python package\n          # segfaults when installing modules in the runner.\n          #\n          # 2020-09-03T02:29:58.2517141Z Successfully installed cffi-1.14.2 cryptography-3.1 future-0.18.2 pycparser-2.20 pyparsing-2.3.1 pyserial-3.4 setuptools-50.1.0 six-1.15.0\n          # 2020-09-03T02:30:09.0409148Z /home/runner/work/_temp/66c91304-eef8-456d-84a1-7299428a62f7.sh: line 5:  4140 Segmentation fault      (core dumped) python3 -m pip install --user -r ${IDF_PATH}/requirements.txt\n          # 2020-09-03T02:30:09.0414254Z ##[error]Process completed with exit code 139.\n          #\n          # possibly related issue:\n          # https://github.com/actions/virtual-environments/issues/159\n          python-version: 3.8\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get install \\\n            bison \\\n            ccache \\\n            flex \\\n            gcc \\\n            git \\\n            gperf \\\n            libffi-dev \\\n            libncurses-dev \\\n            libssl-dev \\\n            make \\\n            wget\n\n      - name: Set environment variables\n        id: set_env\n        run: |\n          SDK_NAME=\"esp-idf\"\n          GCC_PREFIX=\"xtensa-${{ matrix.target }}-elf\"\n          GCC_FILE=\"${GCC_PREFIX}-gcc\"\n          TOOLCHAIN_DIR=\"${HOME}/.espressif/tools\"\n          TOOLCHAIN_VERSION=\"esp-2021r2-8.4.0\"\n          REPO_DIR=`pwd`\n          EXAMPLE_DIR=\"${REPO_DIR}/examples\"\n          DISTFILE_DIR=\"${HOME}/distfiles\"\n          __PROJECT_PATH=`pwd`\n\n          # XXX actions/checkout does not allow to checkout a repository other\n          # than under __PROJECT_PATH\n          IDF_PATH=\"${__PROJECT_PATH}/idf\"\n\n          echo \"IDF_PATH=${IDF_PATH}\" >> ${GITHUB_ENV}\n          echo \"IDF_TARGET=${{ matrix.target }}\" >> ${GITHUB_ENV}\n\n          # cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR\n          echo \"::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}\"\n\n          # XXX prefix all the environment variables with `__PROJECT_` to avoid pollution\n          echo \"__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_GCC_FILE=${GCC_FILE}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_GCC_PREFIX=${GCC_PREFIX}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_SDK_NAME=${SDK_NAME}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_TOOLCHAIN_VERSION=${TOOLCHAIN_VERSION}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_DISTFILE_DIR=${DISTFILE_DIR}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_PATH=${__PROJECT_PATH}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}\" >> ${GITHUB_ENV}\n\n      - name: Checkout the SDK\n        uses: actions/checkout@v2\n        with:\n          repository: espressif/esp-idf\n          path: idf\n          submodules: recursive\n          ref: master\n\n      - name: Fixup the SDK\n        run: |\n\n          # XXX workaround removed option, --no-site-packages, from virtualenv. should\n          # be removed when the following commit is merged\n          # https://github.com/espressif/esp-idf/commit/7a18f02acd7005f7c56e62175a8d1968a1a9019d\n          sed -i -e \"s/'--no-site-packages',//\" ${IDF_PATH}/tools/idf_tools.py\n\n      - name: Run idf_tools.py install\n        run: |\n          ${IDF_PATH}/tools/idf_tools.py install\n\n      - name: Run idf_tools.py install-python-env\n        run: |\n          ${IDF_PATH}/tools/idf_tools.py install-python-env\n\n      - name: Build (idf.py)\n        run: |\n          IGNORE_FILE=\"ci-ignore\"\n          . ${IDF_PATH}/export.sh\n\n          # XXX share cache between examples.\n          # see \"Compiling In Different Directories\" in ccache(1)\n          # |                                        |  4.0.1  | master  |\n          # |----------------------------------------|---------|---------|\n          # | without ccache                         | 33m 42s | 50m 27s |\n          # | CCACHE_BASEDIR and CCACHE_NOHASHDIR    | 10m 41s | 16m 38s |\n          export CCACHE_BASEDIR=\"${__PROJECT_EXAMPLE_DIR}\"\n          export CCACHE_NOHASHDIR=true\n\n          cd \"${__PROJECT_EXAMPLE_DIR}\"\n          for i in $(ls -d *); do\n            if [ ! -e \"${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}\" ]; then\n              cd \"${__PROJECT_EXAMPLE_DIR}/${i}\"\n              # FIXME Remove this workaround when esp-idf issue #7621 will be fixed\n              echo \"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n\" >> sdkconfig.defaults\n              echo \"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n\" >> sdkconfig.defaults\n              if [ ${{ matrix.ip_version }} == \"ipv6\" ]; then\n                echo \"CONFIG_LWIP_IPV6=y\" >> sdkconfig.defaults\n              fi\n              echo \"Building ${i}...\"\n              idf.py --ccache build\n            fi\n          done\n\n  build_esp32_v4_x:\n    needs: pre_build\n    if: ${{ needs.pre_build.outputs.should_skip != 'true' }}\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        build_method:\n          - idf\n        branch:\n\n          # for supported versions by espressif, see:\n          # https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html\n          #\n          # see issue #2\n          # - v4.1.2\n          - v4.2.3\n          - v4.3.2\n          - v4.4.1\n        target:\n          - esp32\n        ip_version:\n          - ipv4\n          - ipv6\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n\n      - name: Install python\n        uses: actions/setup-python@v2\n        with:\n\n          # XXX install python 3.8 because the official python package\n          # segfaults when installing modules in the runner.\n          #\n          # 2020-09-03T02:29:58.2517141Z Successfully installed cffi-1.14.2 cryptography-3.1 future-0.18.2 pycparser-2.20 pyparsing-2.3.1 pyserial-3.4 setuptools-50.1.0 six-1.15.0\n          # 2020-09-03T02:30:09.0409148Z /home/runner/work/_temp/66c91304-eef8-456d-84a1-7299428a62f7.sh: line 5:  4140 Segmentation fault      (core dumped) python3 -m pip install --user -r ${IDF_PATH}/requirements.txt\n          # 2020-09-03T02:30:09.0414254Z ##[error]Process completed with exit code 139.\n          #\n          # possibly related issue:\n          # https://github.com/actions/virtual-environments/issues/159\n          python-version: 3.8\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get install \\\n            bison \\\n            ccache \\\n            flex \\\n            gcc \\\n            git \\\n            gperf \\\n            libffi-dev \\\n            libncurses-dev \\\n            libssl-dev \\\n            make \\\n            wget\n\n      - name: Set environment variables\n        id: set_env\n        run: |\n          SDK_NAME=\"esp-idf\"\n          GCC_PREFIX=\"xtensa-${{ matrix.target }}-elf\"\n          GCC_FILE=\"${GCC_PREFIX}-gcc\"\n          TOOLCHAIN_DIR=\"${HOME}/.espressif/tools\"\n          case \"${{ matrix.branch }}\" in\n          v4.0.*)\n            TOOLCHAIN_VERSION=\"esp-2020r3-8.4.0\"\n            ;;\n          v4.1.*)\n            TOOLCHAIN_VERSION=\"esp-2020r3-8.4.0\"\n            ;;\n          v4.2.*)\n            TOOLCHAIN_VERSION=\"esp-2020r3-8.4.0\"\n            ;;\n          v4.3.*)\n            TOOLCHAIN_VERSION=\"esp-2021r2-8.4.0\"\n            ;;\n          v4.4.*)\n            TOOLCHAIN_VERSION=\"esp-2021r2-patch3-8.4.0\"\n            ;;\n          *)\n            echo \"Unknown matrix.branch: ${{ matrix.branch }}\"\n            exit 1\n            ;;\n          esac\n          REPO_DIR=`pwd`\n          EXAMPLE_DIR=\"${REPO_DIR}/examples\"\n          DISTFILE_DIR=\"${HOME}/distfiles\"\n          __PROJECT_PATH=`pwd`\n\n          # XXX actions/checkout does not allow to checkout a repository other\n          # than under __PROJECT_PATH\n          IDF_PATH=\"${__PROJECT_PATH}/idf\"\n\n          echo \"IDF_PATH=${IDF_PATH}\" >> ${GITHUB_ENV}\n          echo \"IDF_TARGET=${{ matrix.target }}\" >> ${GITHUB_ENV}\n\n          # cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR\n          echo \"::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}\"\n\n          # XXX prefix all the environment variables with `__PROJECT_` to avoid pollution\n          echo \"__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_GCC_FILE=${GCC_FILE}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_GCC_PREFIX=${GCC_PREFIX}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_SDK_NAME=${SDK_NAME}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_TOOLCHAIN_VERSION=${TOOLCHAIN_VERSION}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_DISTFILE_DIR=${DISTFILE_DIR}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_PATH=${__PROJECT_PATH}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}\" >> ${GITHUB_ENV}\n\n      - name: Checkout the SDK\n        uses: actions/checkout@v2\n        with:\n          repository: espressif/esp-idf\n          path: idf\n          submodules: recursive\n          ref: ${{ matrix.branch }}\n\n      - name: Fixup the SDK\n        run: |\n\n          # XXX workaround removed option, --no-site-packages, from virtualenv. should\n          # be removed when the following commit is merged\n          # https://github.com/espressif/esp-idf/commit/7a18f02acd7005f7c56e62175a8d1968a1a9019d\n          sed -i -e \"s/'--no-site-packages',//\" ${IDF_PATH}/tools/idf_tools.py\n\n      - name: Cache esp-idf tools\n\n        # cache esp-idf tools. each tagged branch has fixed versions of tools.\n        # the versions do not change. the master is an exception as it is a\n        # moving target. do NOT cache tools if the branch is master.\n        uses: actions/cache@v2\n        id: cache-tools\n        with:\n          path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }}\n          key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools\n\n      - name: Run idf_tools.py install\n        if: ${{ steps.cache-tools.outputs.cache-hit != 'true' }}\n        run: |\n          ${IDF_PATH}/tools/idf_tools.py install\n\n      - name: Run idf_tools.py install-python-env\n        run: |\n          ${IDF_PATH}/tools/idf_tools.py install-python-env\n\n      - name: Build (idf.py)\n        if: ${{ matrix.build_method == 'idf' }}\n        run: |\n          IGNORE_FILE=\"ci-ignore\"\n          . ${IDF_PATH}/export.sh\n\n          # XXX share cache between examples.\n          # see \"Compiling In Different Directories\" in ccache(1)\n          # |                                        |  4.0.1  | master  |\n          # |----------------------------------------|---------|---------|\n          # | without ccache                         | 33m 42s | 50m 27s |\n          # | CCACHE_BASEDIR and CCACHE_NOHASHDIR    | 10m 41s | 16m 38s |\n          export CCACHE_BASEDIR=\"${__PROJECT_EXAMPLE_DIR}\"\n          export CCACHE_NOHASHDIR=true\n\n          cd \"${__PROJECT_EXAMPLE_DIR}\"\n          for i in $(ls -d *); do\n            if [ ! -e \"${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}\" ]; then\n              cd \"${__PROJECT_EXAMPLE_DIR}/${i}\"\n              # FIXME Remove this workaround when esp-idf issue #7621 will be fixed\n              echo \"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n\" >> sdkconfig.defaults\n              echo \"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n\" >> sdkconfig.defaults\n              if [ ${{ matrix.ip_version }} == \"ipv6\" ]; then\n                echo \"CONFIG_LWIP_IPV6=y\" >> sdkconfig.defaults\n              fi\n              echo \"Building ${i}...\"\n              idf.py --ccache build\n            fi\n          done\n\n  build_esp8266:\n    runs-on: ubuntu-latest\n    needs: pre_build\n    if: ${{ needs.pre_build.outputs.should_skip != 'true' }}\n    strategy:\n      fail-fast: false\n      matrix:\n        build_method:\n\n          # XXX build examples with make only\n          # idf.py in ESP8266 RTOS SDK is broken in many ways.\n          - make\n        branch:\n          - v3.4\n          - master\n        ip_version:\n          - ipv4\n          - ipv6\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n\n      - name: Install python\n        uses: actions/setup-python@v2\n        with:\n          python-version: 3.8\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get install \\\n            bison \\\n            ccache \\\n            flex \\\n            gcc \\\n            git \\\n            gperf \\\n            libffi-dev \\\n            libncurses-dev \\\n            libssl-dev \\\n            make \\\n            wget\n\n      - name: Set environment variables\n        id: set_env\n        run: |\n          SDK_NAME=\"ESP8266_RTOS_SDK\"\n          GCC_PREFIX=\"xtensa-lx106-elf\"\n          GCC_FILE=\"${GCC_PREFIX}-gcc\"\n          TOOLCHAIN_DIR=\"${HOME}/.espressif/tools\"\n          REPO_DIR=`pwd`\n          EXAMPLE_DIR=\"${REPO_DIR}/examples\"\n          __PROJECT_PATH=`pwd`\n          __PROJECT_TOOLCHAIN_VERSION=\"esp-2020r3-49-gd5524c1-8.4.0\"\n\n          # XXX actions/checkout does not allow to checkout a repository other\n          # than under __PROJECT_PATH\n          IDF_PATH=\"${__PROJECT_PATH}/idf\"\n          echo \"IDF_PATH=${IDF_PATH}\" >> ${GITHUB_ENV}\n\n          # cache-idf-tools needs PROJECT_TOOLCHAIN_DIR\n          echo \"::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}\"\n\n          # XXX prefix all the environment variables with `__PROJECT_` to avoid pollution\n          echo \"__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_GCC_FILE=${GCC_FILE}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_GCC_PREFIX=${GCC_PREFIX}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_PATH=${__PROJECT_PATH}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_TOOLCHAIN_VERSION=${__PROJECT_TOOLCHAIN_VERSION}\" >> ${GITHUB_ENV}\n\n      - name: Checkout the SDK\n        uses: actions/checkout@v2\n        with:\n          repository: espressif/ESP8266_RTOS_SDK\n          path: idf\n          submodules: recursive\n          ref: ${{ matrix.branch }}\n          # XXX git.eclipse.org does not allow to fetch a commit. fetch all\n          # the commits.\n          fetch-depth: 0\n\n      - name: Install python requirements (pip)\n        run: |\n          python -m pip install --user -r ${IDF_PATH}/requirements.txt\n\n      - name: Cache toolchain\n        id: cache-idf-tools\n        if: ${{ matrix.branch != 'master' }}\n        uses: actions/cache@v2\n        with:\n          path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }}\n          key: ${{ runner.os }}-${{ matrix.branch }}-esp8266-cache-tools\n\n      - name: Install toolchain\n        if: ${{ steps.cache-idf-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }}\n        run: |\n          ${IDF_PATH}/install.sh\n\n      - name: Setup ccache (make)\n        run: |\n          __PROJECT_CCACHE_BIN_DIR=\"${HOME}/ccache_bin\"\n          mkdir -p \"${__PROJECT_CCACHE_BIN_DIR}\"\n          (cd \"${__PROJECT_CCACHE_BIN_DIR}\" && ln -s /usr/bin/ccache \"${__PROJECT_GCC_FILE}\")\n          echo \"PATH=${__PROJECT_CCACHE_BIN_DIR}:$PATH:${__PROJECT_TOOLCHAIN_DIR}/${__PROJECT_GCC_PREFIX}/${__PROJECT_TOOLCHAIN_VERSION}/${__PROJECT_GCC_PREFIX}/bin\" >> ${GITHUB_ENV}\n          echo \"CCACHE_BASEDIR=${__PROJECT_EXAMPLE_DIR}\" >> ${GITHUB_ENV}\n          echo \"CCACHE_NOHASHDIR=true\" >> ${GITHUB_ENV}\n\n      - name: Build (make)\n        if: ${{ matrix.build_method == 'make' }}\n        run: |\n          IGNORE_FILE=\"ci-ignore-esp8266\"\n\n          cd \"${__PROJECT_EXAMPLE_DIR}\"\n          for i in $(ls -d *); do\n            if [ ! -e \"${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}\" ]; then\n              cd \"${__PROJECT_EXAMPLE_DIR}/${i}\"\n              # XXX ESP8266 RTOS SDK does not support\n              # `sdkconfig.defaults.TARGET_NAME` yet. create\n              # sdkconfig.defaults for ESP8266\n              echo \"CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER=y\" >> sdkconfig.defaults\n              if [ ${{ matrix.ip_version }} == \"ipv6\" ]; then\n                echo \"CONFIG_LWIP_IPV6=y\" >> sdkconfig.defaults\n              fi\n              echo \"Building ${i}...\"\n              make defconfig\n              make -j$(nproc)\n            fi\n          done\n\n  # XXX esp32s2 support was introduced in v4.2. older esp-idf does not install\n  # toolchains for esp32s2. thus, you cannot add `esp32s2` target to\n  # build_esp32_v4_x.\n  #\n  # this job can be removed when either one of the followings are met:\n  #\n  # * GitHub Actions supports \"early exit\" (successfully exit if a condition is\n  #   true).\n  # * all branches in build_esp32_v4_x supports esp32s2\n  #\n  # additionally, esp32s2 build requires idf.py. make is not supported.\n  build_esp32s2_v4_x:\n    runs-on: ubuntu-latest\n    needs: pre_build\n    if: ${{ needs.pre_build.outputs.should_skip != 'true' }}\n    strategy:\n      fail-fast: false\n      matrix:\n        build_method:\n          - idf\n        branch:\n\n          # esp32s2 support since v4.2.x\n          - master\n          - v4.2.2\n          - v4.3.1\n          - v4.4.1\n        target:\n          - esp32s2\n        ip_version:\n          - ipv4\n          - ipv6\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n\n      - name: Install python\n        uses: actions/setup-python@v2\n        with:\n          python-version: 3.8\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get install \\\n            bison \\\n            ccache \\\n            flex \\\n            gcc \\\n            git \\\n            gperf \\\n            libffi-dev \\\n            libncurses-dev \\\n            libssl-dev \\\n            make \\\n            wget\n\n      - name: Set environment variables\n        id: set_env\n        run: |\n          TOOLCHAIN_DIR=\"${HOME}/.espressif/tools\"\n          REPO_DIR=`pwd`\n          EXAMPLE_DIR=\"${REPO_DIR}/examples\"\n          __PROJECT_PATH=`pwd`\n          IDF_PATH=\"${__PROJECT_PATH}/idf\"\n\n          # cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR\n          echo \"::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}\"\n\n          echo \"IDF_PATH=${IDF_PATH}\" >> ${GITHUB_ENV}\n          echo \"IDF_TARGET=${{ matrix.target }}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}\" >> ${GITHUB_ENV}\n\n      - name: Checkout the SDK\n        uses: actions/checkout@v2\n        with:\n          repository: espressif/esp-idf\n          path: idf\n          submodules: recursive\n          ref: ${{ matrix.branch }}\n\n      - name: Fixup the SDK\n        run: |\n\n          sed -i -e \"s/'--no-site-packages',//\" ${IDF_PATH}/tools/idf_tools.py\n\n      - name: Cache esp-idf tools\n\n        # cache esp-idf tools. each tagged branch has fixed versions of tools.\n        # the versions do not change. the master is an exception as it is a\n        # moving target. do NOT cache tools if the branch is master.\n        uses: actions/cache@v2\n        id: cache-tools\n        if: ${{ matrix.branch != 'master' }}\n        with:\n          path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }}\n          key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools\n\n      - name: Run install.sh\n        if: ${{ steps.cache-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }}\n        run: |\n          ${IDF_PATH}/install.sh\n\n      - name: Run idf_tools.py install-python-env\n        run: |\n          ${IDF_PATH}/tools/idf_tools.py install-python-env\n\n      - name: Build (idf.py)\n        if: ${{ matrix.build_method == 'idf' }}\n        run: |\n          IGNORE_FILE=\"ci-ignore\"\n          . ${IDF_PATH}/export.sh\n\n          export CCACHE_BASEDIR=\"${__PROJECT_EXAMPLE_DIR}\"\n          export CCACHE_NOHASHDIR=true\n\n          cd \"${__PROJECT_EXAMPLE_DIR}\"\n          for i in $(ls -d *); do\n            if [ ! -e \"${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}\" ]; then\n              cd \"${__PROJECT_EXAMPLE_DIR}/${i}\"\n              echo \"Building ${i}...\"\n              # FIXME Remove this workaround when esp-idf issue #7621 will be fixed\n              echo \"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n\" >> sdkconfig.defaults\n              echo \"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n\" >> sdkconfig.defaults\n              if [ ${{ matrix.ip_version }} == \"ipv6\" ]; then\n                echo \"CONFIG_LWIP_IPV6=y\" >> sdkconfig.defaults\n              fi\n              idf.py --ccache build\n            fi\n          done\n\n  build_esp32c3_v4_x:\n    runs-on: ubuntu-latest\n    needs: pre_build\n    if: ${{ needs.pre_build.outputs.should_skip != 'true' }}\n    strategy:\n      fail-fast: false\n      matrix:\n        build_method:\n          - idf\n        branch:\n          # esp32c3 support was introduced in v4.3.\n          - master\n          - v4.3.2\n        target:\n          - esp32c3\n        ip_version:\n          - ipv4\n          - ipv6\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n\n      - name: Install python\n        uses: actions/setup-python@v2\n        with:\n          python-version: 3.8\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get install \\\n            bison \\\n            ccache \\\n            flex \\\n            gcc \\\n            git \\\n            gperf \\\n            libffi-dev \\\n            libncurses-dev \\\n            libssl-dev \\\n            make \\\n            wget\n\n      - name: Set environment variables\n        id: set_env\n        run: |\n          TOOLCHAIN_DIR=\"${HOME}/.espressif/tools\"\n          REPO_DIR=`pwd`\n          EXAMPLE_DIR=\"${REPO_DIR}/examples\"\n          __PROJECT_PATH=`pwd`\n          IDF_PATH=\"${__PROJECT_PATH}/idf\"\n\n          # cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR\n          echo \"::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}\"\n\n          echo \"IDF_PATH=${IDF_PATH}\" >> ${GITHUB_ENV}\n          echo \"IDF_TARGET=${{ matrix.target }}\" >> ${GITHUB_ENV}\n          echo \"__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}\" >> ${GITHUB_ENV}\n\n      - name: Checkout the SDK\n        uses: actions/checkout@v2\n        with:\n          repository: espressif/esp-idf\n          path: idf\n          submodules: recursive\n          ref: ${{ matrix.branch }}\n\n      - name: Fixup the SDK\n        run: |\n\n          sed -i -e \"s/'--no-site-packages',//\" ${IDF_PATH}/tools/idf_tools.py\n\n      - name: Cache esp-idf tools\n\n        # cache esp-idf tools. each tagged branch has fixed versions of tools.\n        # the versions do not change. the master is an exception as it is a\n        # moving target. do NOT cache tools if the branch is master.\n        uses: actions/cache@v2\n        id: cache-tools\n        if: ${{ matrix.branch != 'master' }}\n        with:\n          path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }}\n          key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools-1\n\n      - name: Run install.sh\n        if: ${{ steps.cache-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }}\n        run: |\n          ${IDF_PATH}/install.sh\n\n      - name: Run idf_tools.py install-python-env\n        run: |\n          ${IDF_PATH}/tools/idf_tools.py install-python-env\n\n      - name: Build (idf.py)\n        if: ${{ matrix.build_method == 'idf' }}\n        run: |\n          IGNORE_FILE=\"ci-ignore\"\n          . ${IDF_PATH}/export.sh\n\n          export CCACHE_BASEDIR=\"${__PROJECT_EXAMPLE_DIR}\"\n          export CCACHE_NOHASHDIR=true\n\n          cd \"${__PROJECT_EXAMPLE_DIR}\"\n          for i in $(ls -d *); do\n            if [ ! -e \"${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}\" ]; then\n              cd \"${__PROJECT_EXAMPLE_DIR}/${i}\"\n              echo \"Building ${i}...\"\n              # FIXME Remove this workaround when esp-idf issue #7621 will be fixed\n              echo \"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n\" >> sdkconfig.defaults\n              echo \"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n\" >> sdkconfig.defaults\n              if [ ${{ matrix.ip_version }} == \"ipv6\" ]; then\n                echo \"CONFIG_LWIP_IPV6=y\" >> sdkconfig.defaults\n              fi\n              idf.py --ccache build\n            fi\n          done\n\n  all_build:\n    # a meta job that requires all of the above so that repository\n    # admin can choose a single test name in \"Require status checks to pass\n    # before merging\". A trick obtained from:\n    #\n    # https://github.com/jazzband/pip-tools/issues/1085#issuecomment-619172509\n    name: All build\n    runs-on: ubuntu-latest\n    needs:\n      - build_esp32_master\n      - build_esp32_v4_x\n      - build_esp8266\n      - build_esp32s2_v4_x\n      - build_esp32c3_v4_x\n    steps:\n      - name:\n        run: |\n          echo \"All builds finished\"\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "---\n\nname: Push component to https://components.espressif.com\non:\n  push:\n    tags:\n      - v*\njobs:\n  upload_components:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: \"recursive\"\n      - name: Upload component to the component registry\n        uses: espressif/upload-components-ci-action@b78a19fa5424714997596d3ecffa634aef8ae20b\n        with:\n          name: \"esp_wireguard\"\n          version: ${{ github.ref_name }}\n          namespace: \"trombik\"\n          api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.swp\n/examples/*/build\n/examples/*/sdkconfig\n/examples/*/sdkconfig.*\n/examples/*/.vagrant/\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "idf_component_register(\n    SRCS \"src/crypto.c\"\n         \"src/wireguard.c\"\n         \"src/wireguardif.c\"\n         \"src/wireguard-platform.c\"\n         \"src/crypto/refc/blake2s.c\"\n         \"src/crypto/refc/chacha20.c\"\n         \"src/crypto/refc/chacha20poly1305.c\"\n         \"src/crypto/refc/poly1305-donna.c\"\n         \"src/crypto/refc/x25519.c\"\n         \"src/esp_wireguard.c\"\n         \"src/nacl/crypto_scalarmult/curve25519/ref/smult.c\"\n    INCLUDE_DIRS \"include\"\n    PRIV_INCLUDE_DIRS \"src\"\n    REQUIRES esp_netif lwip mbedtls)\n\nif(${IDF_VERSION_MAJOR} STREQUAL 5)\n    set_source_files_properties(src/crypto/refc/x25519.c\n        PROPERTIES COMPILE_FLAGS\n        -Wno-error=stringop-overread)\nendif()\n"
  },
  {
    "path": "Kconfig",
    "content": "menu \"WireGuard\"\n\nchoice WIREGUARD_ESP_ADAPTER_SELECTION\n    prompt \"TCP/IP adapter to use\"\n    default WIREGUARD_ESP_NETIF\n    config WIREGUARD_ESP_TCPIP_ADAPTER\n        bool \"TCP/IP Adapter (pre esp-idf v4.1, ESP8266 RTOS SDK)\"\n    config WIREGUARD_ESP_NETIF\n        bool \"ESP-NETIF\"\nendchoice\n\nconfig WIREGUARD_MAX_PEERS\n    int \"Max number of peers\"\n    default 1\n\nconfig WIREGUARD_MAX_SRC_IPS\n\tint \"Max number of SRC IP addresses\"\n\tdefault 2\n\nconfig MAX_INITIATIONS_PER_SECOND\n    int \"Per device limit on accepting (valid) initiation requests\"\n    default 2\n    help\n        Per device limit on accepting (valid) initiation requests - per peer.\nchoice WIREGUARD_x25519_IMPLEMENTATION\n    prompt \"x25519 implementation to use\"\n    default WIREGUARD_x25519_IMPLEMENTATION_DEFAULT\n    config WIREGUARD_x25519_IMPLEMENTATION_DEFAULT\n        bool \"Default (originally from wireguard-lwip)\"\n    config WIREGUARD_x25519_IMPLEMENTATION_NACL\n        bool \"NaCL\"\nendchoice\nendmenu\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2021 Kenta Ida (fuga@fugafuga.org)\n\nThe original license is below:\nCopyright (c) 2021 Daniel Hope (www.floorsense.nz)\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n* Redistributions in binary form must reproduce the above copyright notice, this\n  list of conditions and the following disclaimer in the documentation and/or\n  other materials provided with the distribution.\n* Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of \n  its contributors may be used to endorse or promote products derived from this\n  software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nAuthor: Daniel Hope <daniel.hope@smartalock.com>\n\n"
  },
  {
    "path": "README.md",
    "content": "# `esp_wireguard`, WireGuard Implementation for ESP-IDF\n\nThis is an implementation of the [WireGuard&reg;](https://www.wireguard.com/)\nfor ESP-IDF, based on\n[WireGuard Implementation for lwIP](https://github.com/smartalock/wireguard-lwip).\n\n[![Build examples](https://github.com/trombik/esp_wireguard/actions/workflows/build.yml/badge.svg)](https://github.com/trombik/esp_wireguard/actions/workflows/build.yml)\n\n## Status\n\nThe code is alpha.\n\nA single tunnel to a WireGuard peer has been working.\n\n## Supported ESP-IDF versions and targets\n\nThe following ESP-IDF versions are supported:\n\n* `esp-idf` `master`\n* `esp-idf` `v4.2.x`\n* `esp-idf` `v4.3.x`\n* `esp-idf` `v4.4.x`\n* ESP8266 RTOS SDK `v3.4`\n\nThe following targets are supported:\n\n* `esp32`\n* `esp32s2`\n* `esp32c3`\n* `esp8266`\n\n## Usage\n\nIn `menuconfig` under `WireGuard`, choose a TCP/IP adapter. The default is\n`ESP-NETIF`. SDKs older than `esp-idf` `v4.1`, including ESP8266 RTOS SDK v3.4\nrequires `TCP/IP Adapter`.\n\nBoth peers must have synced time. The library does not sync time.\n\nA working network interface is required.\n\nCreate WireGuard configuration, `wireguard_config_t`. Use\n`ESP_WIREGUARD_CONFIG_DEFAULT` to initialize `wireguard_config_t` variable.\nCreate `wireguard_ctx_t`.  Pass the variables to `esp_wireguard_init()`. Then,\ncall `esp_wireguard_connect()`. Call `esp_wireguard_disconnect()` to\ndisconnect from the peer (and destroy the WireGuard interface).\n\n```c\n#include <esp_wireguard.h>\n\nesp_err_t err = ESP_FAIL;\n\nwireguard_config_t wg_config = ESP_WIREGUARD_CONFIG_DEFAULT();\n\nwg_config.private_key = CONFIG_WG_PRIVATE_KEY;\nwg_config.listen_port = CONFIG_WG_LOCAL_PORT;\nwg_config.public_key = CONFIG_WG_PEER_PUBLIC_KEY;\nwg_config.allowed_ip = CONFIG_WG_LOCAL_IP_ADDRESS;\nwg_config.allowed_ip_mask = CONFIG_WG_LOCAL_IP_NETMASK;\nwg_config.endpoint = CONFIG_WG_PEER_ADDRESS;\nwg_config.port = CONFIG_WG_PEER_PORT;\n\n/* If the device is behind NAT or stateful firewall, set persistent_keepalive.\n   persistent_keepalive is disabled by default */\n// wg_config.persistent_keepalive = 10;\n\nwireguard_ctx_t ctx = {0};\nerr = esp_wireguard_init(&wg_config, &ctx);\n\n/* start establishing the link. after this call, esp_wireguard start\n   establishing connection. */\nerr = esp_wireguard_connect(&ctx);\n\n/* after some time, see if the link is up. note that it takes some time to\n   establish the link */\nerr = esp_wireguardif_peer_is_up(&ctx);\nif (err == ESP_OK) {\n    /* the link is up */\nelse {\n    /* the link is not up */\n}\n\n/* do something */\n\nerr = esp_wireguard_disconnect(&ctx);\n```\n\nSee examples at [examples](examples).\n\n## IPv6 support\n\nEnable `CONFIG_LWIP_IPV6` under `lwip` component in `menuconfig`.\n\nIPv6 support is alpha and probably broken. See also Known issues.\n\n## Driver configuration\n\nThe driver configuration is under `[Component config]` -> `[WireGuard]`.\n\nUnder `WIREGUARD_x25519_IMPLEMENTATION`, you may choose an implementation of\nscalar multiplication. The default is\n`WIREGUARD_x25519_IMPLEMENTATION_DEFAULT`, which is derived from\n[WireGuard Implementation for lwIP](https://github.com/smartalock/wireguard-lwip).\n`WIREGUARD_x25519_IMPLEMENTATION_NACL` uses\n[crypto_scalarmult()](https://nacl.cr.yp.to/scalarmult.html) from NaCL. Note\nthat, with `WIREGUARD_x25519_IMPLEMENTATION_NACL`,\nsome stack sizes must be increased.  In my test, 5KB for both\n`CONFIG_LWIP_TCPIP_TASK_STACK_SIZE`, and `CONFIG_MAIN_TASK_STACK_SIZE` is\nknown to work on `ESP32-D0WD-V3`.\n\n## Known issues\n\nThe implementation uses `LwIP` as TCP/IP protocol stack.\n\nIPv6 support is not tested.  Dual stack (IPv4 and IPv6) is not supported (see\nIssue #5). The first address of `endpoint` is used to choose IPv4 or IPv6 as a\ntransport. The chosen transport must be available and usable.\n\nThe library assumes the interface is WiFi interface. Ethernet is not\nsupported.\n\nOlder `esp-idf` versions with `TCP/IP Adapter`, such as v4.1.x, should work,\nbut there are others issues, not directly related to the library.\n\n## License\n\nBSD 3-Clause \"New\" or \"Revised\" License (SPDX ID: BSD-3-Clause).\nSee [LICENSE](LICENSE) for details.\n\n[src/nacl/crypto_scalarmult/curve25519/ref/smult.c] is Public domain.\n\n## Authors\n\n* Daniel Hope (daniel.hope@smartalock.com)\n* Kenta Ida (fuga@fugafuga.org)\n* Matthew Dempsky\n* D. J. Bernstein\n"
  },
  {
    "path": "component.mk",
    "content": "COMPONENT_SRCDIRS = \\\n\tsrc \\\n\tsrc/crypto/refc\nCOMPONENT_ADD_INCLUDEDIRS = \\\n\tinclude\nCOMPONENT_PRIV_INCLUDEDIRS = \\\n\tsrc\n\nCOMPONENT_DEPENDS = lwip mbedtls\n"
  },
  {
    "path": "examples/demo/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\n\ninclude($ENV{IDF_PATH}/tools/cmake/project.cmake)\nproject(demo)\n"
  },
  {
    "path": "examples/demo/Makefile",
    "content": "PROJECT_NAME := demo\n\ninclude $(IDF_PATH)/make/project.mk\n"
  },
  {
    "path": "examples/demo/README.md",
    "content": "# demo example\n\n## What the example does\n\nThe example connects to a WireGuard server. When the link is up, the device\nsends ICMP echo requests, and shows ping statistics. The ICMP session loops\nforever.\n\nThe main task then disconnects from the peer, and re-connects to the peer.\n\n## Requirements\n\n* An ESP32 or ESP8266 development board\n* WiFi network\n* [`wireguard-tools`](https://github.com/WireGuard/wireguard-tools)\n* A WireGuard server\n\n## Generating keys\n\n```console\nwg genkey | tee private.key | wg pubkey > public.key\n```\n\n## Log\n\n```console\nI (100) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=1600ch ( 90124) map\nI (141) esp_image: segment 1: paddr=00026034 vaddr=3ffb0000 size=03f0ch ( 16140) load\nI (148) esp_image: segment 2: paddr=00029f48 vaddr=40080000 size=060d0h ( 24784) load\nI (158) esp_image: segment 3: paddr=00030020 vaddr=400d0020 size=7691ch (485660) map\nI (334) esp_image: segment 4: paddr=000a6944 vaddr=400860d0 size=0f14ch ( 61772) load\nI (360) esp_image: segment 5: paddr=000b5a98 vaddr=50000000 size=00010h (    16) load\nI (371) boot: Loaded app from partition at offset 0x10000\nI (371) boot: Disabling RNG early entropy source...\nI (382) cpu_start: Pro cpu up.\nI (382) cpu_start: Starting app cpu, entry point is 0x4008127c\n0x4008127c: call_start_cpu1 at /usr/home/trombik/github/esp-idf/components/esp_system/port/cpu_start.c:150\n\nI (0) cpu_start: App cpu up.\nI (397) cpu_start: Pro cpu start user code\nI (397) cpu_start: cpu freq: 160000000\nI (397) cpu_start: Application information:\nI (401) cpu_start: Project name:     demo\nI (406) cpu_start: App version:      4a3c45b\nI (411) cpu_start: Compile time:     Jan  6 2022 15:39:56\nI (417) cpu_start: ELF file SHA256:  45cef6b78497cd9f...\nI (423) cpu_start: ESP-IDF:          v4.3.2\nI (428) heap_init: Initializing. RAM available for dynamic allocation:\nI (435) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM\nI (441) heap_init: At 3FFB8150 len 00027EB0 (159 KiB): DRAM\nI (447) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM\nI (454) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM\nI (460) heap_init: At 4009521C len 0000ADE4 (43 KiB): IRAM\nI (467) spi_flash: detected chip: generic\nI (471) spi_flash: flash io: dio\nW (475) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.\nI (489) cpu_start: Starting scheduler on PRO CPU.\nI (0) cpu_start: Starting scheduler on APP CPU.\nI (602) wifi:wifi driver task: 3ffc1e00, prio:23, stack:6656, core=0\nI (602) system_api: Base MAC address is not set\nI (602) system_api: read default base MAC address from EFUSE\nI (622) wifi:wifi firmware version: eb52264\nI (622) wifi:wifi certification version: v7.0\nI (622) wifi:config NVS flash: enabled\nI (622) wifi:config nano formating: disabled\nI (632) wifi:Init data frame dynamic rx buffer num: 32\nI (632) wifi:Init management frame dynamic rx buffer num: 32\nI (642) wifi:Init management short buffer num: 32\nI (642) wifi:Init dynamic tx buffer num: 32\nI (652) wifi:Init static rx buffer size: 1600\nI (652) wifi:Init static rx buffer num: 10\nI (662) wifi:Init dynamic rx buffer num: 32\nI (662) wifi_init: rx ba win: 6\nI (662) wifi_init: tcpip mbox: 32\nI (672) wifi_init: udp mbox: 6\nI (672) wifi_init: tcp mbox: 6\nI (672) wifi_init: tcp tx win: 5744\nI (682) wifi_init: tcp rx win: 5744\nI (682) wifi_init: tcp mss: 1440\nI (692) wifi_init: WiFi IRAM OP enabled\nI (692) wifi_init: WiFi RX IRAM OP enabled\nI (702) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07\nI (812) wifi:mode : sta (24:62:ab:ff:2f:d0)\nI (812) wifi:enable tsf\nI (822) wifi:new:<11,0>, old:<1,0>, ap:<255,255>, sta:<11,0>, prof:1\nI (822) wifi:state: init -> auth (b0)\nI (832) wifi:state: auth -> assoc (0)\nI (832) wifi:state: assoc -> run (10)\nI (852) wifi:connected with makers, aid = 2, channel 11, BW20, bssid = 18:c2:bf:d2:de:d8\nI (852) wifi:security: WPA2-PSK, phy: bg, rssi: -85\nI (862) wifi:pm start, type: 1\n\nI (952) wifi:AP's beacon interval = 102400 us, DTIM period = 2\nI (2082) esp_netif_handlers: sta ip: 192.168.99.52, mask: 255.255.255.0, gw: 192.168.99.254\nI (2082) demo: got ip:192.168.99.52\nI (2082) demo: Connected to ap SSID:makers\nI (2092) sync_time: Initializing SNTP\nI (2092) sync_time: Waiting for system time to be set... (1/20)\nI (3792) sync_time: Time synced\nI (4102) demo: The current date/time in New York is: Thu Jan  6 03:40:19 2022\nI (4102) demo: Initializing WireGuard.\nI (4102) demo: Connecting to the peer.\nI (4102) esp_wireguard: allowed_ip: 192.168.4.58\nI (4162) esp_wireguard: Peer: 192.168.99.19 (192.168.99.19:12912)\nI (4212) esp_wireguard: Connecting to 192.168.99.19:12912\nI (5212) demo: Peer is down\nI (6212) demo: Peer is down\nI (7212) demo: Peer is down\nI (8212) demo: Peer is up\nI (8212) demo: Initializing ping...\nI (8212) demo: ICMP echo target: 192.168.4.1\nI (8222) demo: 64 bytes from 192.168.4.1 icmp_seq=1 ttl=255 time=6 ms\nI (9222) demo: 64 bytes from 192.168.4.1 icmp_seq=2 ttl=255 time=9 ms\nI (10222) demo: 64 bytes from 192.168.4.1 icmp_seq=3 ttl=255 time=7 ms\nI (11222) demo: 64 bytes from 192.168.4.1 icmp_seq=4 ttl=255 time=6 ms\nI (12212) demo: 64 bytes from 192.168.4.1 icmp_seq=5 ttl=255 time=3 ms\nI (13222) demo: 64 bytes from 192.168.4.1 icmp_seq=6 ttl=255 time=7 ms\nI (14222) demo: 64 bytes from 192.168.4.1 icmp_seq=7 ttl=255 time=8 ms\nI (15212) demo: 64 bytes from 192.168.4.1 icmp_seq=8 ttl=255 time=4 ms\nI (16212) demo: 64 bytes from 192.168.4.1 icmp_seq=9 ttl=255 time=2 ms\nI (17222) demo: 64 bytes from 192.168.4.1 icmp_seq=10 ttl=255 time=12 ms\nI (18212) demo: Disconnecting.\nI (18212) demo: Disconnected.\nI (19212) demo: From 192.168.4.1 icmp_seq=11 timeout\nI (20212) demo: From 192.168.4.1 icmp_seq=12 timeout\nI (21212) demo: From 192.168.4.1 icmp_seq=13 timeout\nI (22212) demo: From 192.168.4.1 icmp_seq=14 timeout\nI (23212) demo: From 192.168.4.1 icmp_seq=15 timeout\nI (24212) demo: From 192.168.4.1 icmp_seq=16 timeout\nI (25212) demo: From 192.168.4.1 icmp_seq=17 timeout\nI (26212) demo: From 192.168.4.1 icmp_seq=18 timeout\nI (27212) demo: From 192.168.4.1 icmp_seq=19 timeout\nI (28212) demo: From 192.168.4.1 icmp_seq=20 timeout\nI (28212) demo: Connecting.\nI (28212) esp_wireguard: allowed_ip: 192.168.4.58\nI (28262) esp_wireguard: Peer: 192.168.99.19 (192.168.99.19:12912)\nI (28312) esp_wireguard: Connecting to 192.168.99.19:12912\nI (29212) demo: From 192.168.4.1 icmp_seq=21 timeout\nI (30212) demo: From 192.168.4.1 icmp_seq=22 timeout\nI (31212) demo: From 192.168.4.1 icmp_seq=23 timeout\nI (32212) demo: From 192.168.4.1 icmp_seq=24 timeout\nI (33212) demo: From 192.168.4.1 icmp_seq=25 timeout\nI (34312) demo: Peer is up\nI (35132) demo: From 192.168.4.1 icmp_seq=26 timeout\nI (35132) demo: 64 bytes from 192.168.4.1 icmp_seq=27 ttl=255 time=3 ms\nI (35212) demo: 64 bytes from 192.168.4.1 icmp_seq=28 ttl=255 time=5 ms\nI (36212) demo: 64 bytes from 192.168.4.1 icmp_seq=29 ttl=255 time=3 ms\nI (37222) demo: 64 bytes from 192.168.4.1 icmp_seq=30 ttl=255 time=6 ms\nI (38212) demo: 64 bytes from 192.168.4.1 icmp_seq=31 ttl=255 time=3 ms\nI (39222) demo: 64 bytes from 192.168.4.1 icmp_seq=32 ttl=255 time=5 ms\nI (40222) demo: 64 bytes from 192.168.4.1 icmp_seq=33 ttl=255 time=13 ms\nI (41222) demo: 64 bytes from 192.168.4.1 icmp_seq=34 ttl=255 time=5 ms\nI (42222) demo: 64 bytes from 192.168.4.1 icmp_seq=35 ttl=255 time=7 ms\nI (43222) demo: 64 bytes from 192.168.4.1 icmp_seq=36 ttl=255 time=8 ms\nI (44232) demo: 64 bytes from 192.168.4.1 icmp_seq=37 ttl=255 time=18 ms\nI (44312) demo: Disconnecting.\nI (44312) demo: Disconnected.\nI (46212) demo: From 192.168.4.1 icmp_seq=38 timeout\nI (47212) demo: From 192.168.4.1 icmp_seq=39 timeout\nI (48212) demo: From 192.168.4.1 icmp_seq=40 timeout\nI (49212) demo: From 192.168.4.1 icmp_seq=41 timeout\nI (50212) demo: From 192.168.4.1 icmp_seq=42 timeout\nI (51212) demo: From 192.168.4.1 icmp_seq=43 timeout\nI (52212) demo: From 192.168.4.1 icmp_seq=44 timeout\nI (53212) demo: From 192.168.4.1 icmp_seq=45 timeout\nI (54212) demo: From 192.168.4.1 icmp_seq=46 timeout\nI (54312) demo: Connecting.\nI (54312) esp_wireguard: allowed_ip: 192.168.4.58\nI (54362) esp_wireguard: Peer: 192.168.99.19 (192.168.99.19:12912)\nI (54412) esp_wireguard: Connecting to 192.168.99.19:12912\nI (55212) demo: From 192.168.4.1 icmp_seq=47 timeout\nI (56212) demo: From 192.168.4.1 icmp_seq=48 timeout\nI (57212) demo: From 192.168.4.1 icmp_seq=49 timeout\nI (58212) demo: From 192.168.4.1 icmp_seq=50 timeout\nI (59212) demo: From 192.168.4.1 icmp_seq=51 timeout\nI (60212) demo: From 192.168.4.1 icmp_seq=52 timeout\nI (61212) demo: From 192.168.4.1 icmp_seq=53 timeout\nI (61412) demo: Peer is up\nI (62272) demo: From 192.168.4.1 icmp_seq=54 timeout\nI (62272) demo: 64 bytes from 192.168.4.1 icmp_seq=55 ttl=255 time=4 ms\nI (63212) demo: 64 bytes from 192.168.4.1 icmp_seq=56 ttl=255 time=4 ms\nI (64212) demo: 64 bytes from 192.168.4.1 icmp_seq=57 ttl=255 time=3 ms\nI (65222) demo: 64 bytes from 192.168.4.1 icmp_seq=58 ttl=255 time=5 ms\nI (66212) demo: 64 bytes from 192.168.4.1 icmp_seq=59 ttl=255 time=3 ms\nI (67212) demo: 64 bytes from 192.168.4.1 icmp_seq=60 ttl=255 time=3 ms\nI (68212) demo: 64 bytes from 192.168.4.1 icmp_seq=61 ttl=255 time=3 ms\nI (69212) demo: 64 bytes from 192.168.4.1 icmp_seq=62 ttl=255 time=3 ms\n```\n"
  },
  {
    "path": "examples/demo/Vagrantfile",
    "content": "# A vagrant box for testing. The VM is configured to use a brigde network,\n# i.e. the VM is attached to the same network of the host OS. The interface\n# uses DHCP.\n#\n# How to test the example:\n#\n# Make sure the follwings are installed on local machine:\n#\n# * `vagrant`\n# * `virtualbox`\n#\n# Boot the VM. To boot the VM, run:\n# > vagrant up\n#\n# At the initial boot, `vagrant` downloads my VM image (~700MB).\n#\n# Login to the server. To login to the VM, run:\n# > vagrant ssh\n#\n# sudo requires no password.\n#\n# See the IP address of the server.\n# > ifconfig em1\n# em1: flags=808843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF4> mtu 1500\n#         lladdr 08:00:27:02:ba:ab\n#         index 2 priority 0 llprio 3\n#         groups: egress\n#         media: Ethernet autoselect (1000baseT full-duplex)\n#         status: active\n#         inet 192.168.99.25 netmask 0xffffff00 broadcast 192.168.99.255\n#\n# in this case, the IP address of the server is `192.168.99.25`.\n#\n# Ensure there is no `sdkconfig`. Delete it if there is.\n#\n# Configure the example by running `idf.py menuconfig`. You must change at\n# least the followings:\n#\n# * ESP_WIFI_SSID\n# * ESP_WIFI_PASSWORD\n# * WG_PEER_ADDRESS\n#\n# Change `ESP_WIFI_SSID` and `ESP_WIFI_PASSWORD` to your SSID and password.\n# Use the IP adddress of the server for `WG_PEER_ADDRESS`.\n#\n# Additionally, modify the maximum log verbosity.  Select [Component config] ->\n# [Log output] -> [Maximum log verbosity], and choose `Debug`.\n#\n# Flash the example by running `idf.py flash monitor`.\n#\n# Below is the default configuration of the example.\n#\n# client secret key: IsvT72MAXzA8EtV0FSD1QT59B4x0oe6Uea5rd/dDzhE=\n# client public key: uyCfLulk5l7Bv/yCJ0nm1J3VL71YU4LISK/EHhwe43g=\n#\n# server secret key: iN8Rsdc10MFjkeqJ352OvtoMhkG5AFZWc/k4cS9odHM=\n# server public key: FjrsQ/HD1Q8fUlFILIasDlOuajMeZov4NGqMJpkswiw=\n#\n# preshared key: 0/2H97Sd5EJ9LAAAYUglVjPYv7ihNIm/ziuv6BtSI50=\n#\n# wg(4) network: 192.168.4.0/24\n# IP address of the server: 192.168.4.254\n# allowed IP address of the client: 192.168.4.58\n# the server port: 12912\n#\n# Other useful commands for the test:\n#\n# for details of wg(4) interface, run:\n# ifconfig wg0\n#\n# to destroy wg(4), run:\n# ifconfig wg0 destroy\n#\n# to create wg(4), run:\n# sh /etc/netstart wg0\n#\n# to see packets from the client to the server, run:\n# tcpdump -ni em1 host $ip.add.re.ss\n#\n# replace $ip.add.re.ss with the client IP address.\n#\n# to see decrypted packets, run:\n# tcpdump -ni wg0\n#\n# to see debug log from wg(4), run:\n# tail -f /var/log/messages\n\nVagrant.configure(\"2\") do |config|\n  config.vm.box = \"trombik/ansible-openbsd-7.1-amd64\"\n  config.vm.network \"public_network\"\n  config.vm.provision \"shell\", inline: <<-SHELL\n    rcctl enable ntpd\n    rcctl start ntpd\n    touch /etc/hostname.wg0\n    chmod 600 /etc/hostname.wg0\n    echo \"debug\" >> /etc/hostname.wg0\n    echo \"wgkey iN8Rsdc10MFjkeqJ352OvtoMhkG5AFZWc/k4cS9odHM= wgport 12912\" >> /etc/hostname.wg0\n    echo \"inet 192.168.4.254 255.255.255.0\" >> /etc/hostname.wg0\n    echo \"wgpeer uyCfLulk5l7Bv/yCJ0nm1J3VL71YU4LISK/EHhwe43g= wgaip 192.168.4.58/32 wgpsk 0/2H97Sd5EJ9LAAAYUglVjPYv7ihNIm/ziuv6BtSI50=\" >> /etc/hostname.wg0\n    sh /etc/netstart wg0 start\n  SHELL\nend\n"
  },
  {
    "path": "examples/demo/main/CMakeLists.txt",
    "content": "idf_component_register(SRCS \"main.c\" \"sync_time.c\"\n                    INCLUDE_DIRS \".\"\n                    REQUIRES esp_wifi lwip esp_wireguard nvs_flash)\n"
  },
  {
    "path": "examples/demo/main/Kconfig.projbuild",
    "content": "menu \"Example Configuration\"\n\n    config ESP_WIFI_SSID\n        string \"WiFi SSID\"\n        default \"myssid\"\n        help\n            SSID (network name) for the example to connect to.\n\n    config ESP_WIFI_PASSWORD\n        string \"WiFi Password\"\n        default \"mypassword\"\n        help\n            WiFi password (WPA or WPA2) for the example to use.\n\n    config ESP_MAXIMUM_RETRY\n        int \"Maximum retry\"\n        default 5\n        help\n            Set the Maximum retry to avoid station reconnecting to the AP\n            unlimited when the AP is really inexistent.\n\n    config WG_PRIVATE_KEY\n        string \"Wireguard Private Key\"\n        default \"IsvT72MAXzA8EtV0FSD1QT59B4x0oe6Uea5rd/dDzhE=\"\n        help\n            Private key of the WireGuard device.\n\n    config WG_LOCAL_IP_ADDRESS\n        string \"Wireguard local IP address\"\n        default \"192.168.4.58\"\n        help\n            Local IP address of the WireGuard device.\n\n    config WG_LOCAL_IP_NETMASK\n        string \"Wireguard local netmask\"\n        default \"255.255.255.0\"\n        help\n            Netmask of the local network the WireGuard device belongs to.\n\n    config WG_LOCAL_PORT\n        int \"Wireguard local port\"\n        default 11010\n        help\n            Local port to listen.\n\n    config WG_PEER_PUBLIC_KEY\n        string \"Wireguard remote peer public key\"\n        default \"FjrsQ/HD1Q8fUlFILIasDlOuajMeZov4NGqMJpkswiw=\"\n        help\n            Public key of the remote peer.\n\n    config WG_PRESHARED_KEY\n        string \"Wireguard pre-shared symmetric key\"\n        default \"0/2H97Sd5EJ9LAAAYUglVjPYv7ihNIm/ziuv6BtSI50=\"\n        help\n            Public key of the remote peer.\n\n    config WG_PEER_ADDRESS\n        string \"Wireguard remote peer address\"\n        default \"demo.wireguard.com\"\n        help\n            Address of the remote peer.\n\n    config WG_PEER_PORT\n        int \"Wireguard remote peer port\"\n        default 12912\n        help\n            Port number of the remote peer.\n\n    config WG_PERSISTENT_KEEP_ALIVE\n        int \"Interval to send an authenticated empty packet\"\n        default 0\n        help\n            A seconds interval, between 1 and 65535 inclusive, of how often to\n            send an authenticated empty packet to the peer for the purpose of\n            keeping a stateful firewall or NAT mapping valid persistently\n    config EXAMPLE_PING_ADDRESS\n        string \"Target IP address or name\"\n        default \"192.168.4.254\"\n        help\n            Target IP address to send ICMP echo requests.\nendmenu\n"
  },
  {
    "path": "examples/demo/main/component.mk",
    "content": ""
  },
  {
    "path": "examples/demo/main/main.c",
    "content": "/* WireGuard demo example\n\n   This example code is in the Public Domain (or CC0 licensed, at your option.)\n\n   Unless required by applicable law or agreed to in writing, this\n   software is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n   CONDITIONS OF ANY KIND, either express or implied.\n*/\n#include <string.h>\n#include <inttypes.h>\n#include <time.h>\n#include <sys/time.h>\n#include <freertos/FreeRTOS.h>\n#include <freertos/task.h>\n#include <freertos/event_groups.h>\n#include <esp_event.h>\n#include <esp_idf_version.h>\n#include <esp_log.h>\n#include <esp_system.h>\n#include <esp_wifi.h>\n#include <nvs_flash.h>\n#include <lwip/netdb.h>\n#include <ping/ping_sock.h>\n\n#include <esp_wireguard.h>\n#include \"sync_time.h\"\n\n#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID\n#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD\n#define EXAMPLE_ESP_MAXIMUM_RETRY  CONFIG_ESP_MAXIMUM_RETRY\n\n#if defined(CONFIG_IDF_TARGET_ESP8266)\n#include <esp_netif.h>\n#elif ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0)\n#include <tcpip_adapter.h>\n#else\n#include <esp_netif.h>\n#endif\n\n/* FreeRTOS event group to signal when we are connected*/\nstatic EventGroupHandle_t s_wifi_event_group;\n\n/* The event group allows multiple bits for each event, but we only care about two events:\n * - we are connected to the AP with an IP\n * - we failed to connect after the maximum amount of retries */\n#define WIFI_CONNECTED_BIT BIT0\n#define WIFI_FAIL_BIT      BIT1\n\nstatic const char *TAG = \"demo\";\nstatic int s_retry_num = 0;\nstatic wireguard_config_t wg_config = ESP_WIREGUARD_CONFIG_DEFAULT();\n\nstatic esp_err_t wireguard_setup(wireguard_ctx_t* ctx)\n{\n    esp_err_t err = ESP_FAIL;\n\n    ESP_LOGI(TAG, \"Initializing WireGuard.\");\n    wg_config.private_key = CONFIG_WG_PRIVATE_KEY;\n    wg_config.listen_port = CONFIG_WG_LOCAL_PORT;\n    wg_config.public_key = CONFIG_WG_PEER_PUBLIC_KEY;\n    if (strcmp(CONFIG_WG_PRESHARED_KEY, \"\") != 0) {\n        wg_config.preshared_key = CONFIG_WG_PRESHARED_KEY;\n    } else {\n        wg_config.preshared_key = NULL;\n    }\n    wg_config.allowed_ip = CONFIG_WG_LOCAL_IP_ADDRESS;\n    wg_config.allowed_ip_mask = CONFIG_WG_LOCAL_IP_NETMASK;\n    wg_config.endpoint = CONFIG_WG_PEER_ADDRESS;\n    wg_config.port = CONFIG_WG_PEER_PORT;\n    wg_config.persistent_keepalive = CONFIG_WG_PERSISTENT_KEEP_ALIVE;\n\n    err = esp_wireguard_init(&wg_config, ctx);\n    if (err != ESP_OK) {\n        ESP_LOGE(TAG, \"esp_wireguard_init: %s\", esp_err_to_name(err));\n        goto fail;\n    }\n\n    ESP_LOGI(TAG, \"Connecting to the peer.\");\n    err = esp_wireguard_connect(ctx);\n    if (err != ESP_OK) {\n        ESP_LOGE(TAG, \"esp_wireguard_connect: %s\", esp_err_to_name(err));\n        goto fail;\n    }\n\n    err = ESP_OK;\nfail:\n    return err;\n}\n\nstatic void event_handler(void* arg, esp_event_base_t event_base,\n                                int32_t event_id, void* event_data)\n{\n    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {\n        esp_wifi_connect();\n    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {\n        if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {\n            esp_wifi_connect();\n            s_retry_num++;\n            ESP_LOGI(TAG, \"retry to connect to the AP\");\n        } else {\n            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);\n        }\n        ESP_LOGI(TAG,\"connect to the AP fail\");\n    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {\n        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;\n        ESP_LOGI(TAG, \"got ip:\" IPSTR, IP2STR(&event->ip_info.ip));\n        s_retry_num = 0;\n        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);\n    }\n}\n\n#ifdef CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER\nstatic esp_err_t wifi_init_tcpip_adaptor(void)\n{\n    esp_err_t err = ESP_FAIL;\n    s_wifi_event_group = xEventGroupCreate();\n\n    tcpip_adapter_init();\n\n    ESP_ERROR_CHECK(esp_event_loop_create_default());\n\n    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();\n    ESP_ERROR_CHECK(esp_wifi_init(&cfg));\n\n    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));\n    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));\n\n    wifi_config_t wifi_config = {\n        .sta = {\n            .ssid = EXAMPLE_ESP_WIFI_SSID,\n            .password = EXAMPLE_ESP_WIFI_PASS\n        },\n    };\n\n    /* Setting a password implies station will connect to all security modes including WEP/WPA.\n        * However these modes are deprecated and not advisable to be used. Incase your Access point\n        * doesn't support WPA2, these mode can be enabled by commenting below line */\n\n    if (strlen((char *)wifi_config.sta.password)) {\n        wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;\n    }\n\n    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );\n    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );\n    ESP_ERROR_CHECK(esp_wifi_start() );\n\n    ESP_LOGI(TAG, \"wifi_init_sta finished.\");\n\n    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum\n     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */\n    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,\n            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,\n            pdFALSE,\n            pdFALSE,\n            portMAX_DELAY);\n\n    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually\n     * happened. */\n    if (bits & WIFI_CONNECTED_BIT) {\n        ESP_LOGI(TAG, \"connected to ap SSID:%s\", EXAMPLE_ESP_WIFI_SSID);\n    } else if (bits & WIFI_FAIL_BIT) {\n        ESP_LOGI(TAG, \"Failed to connect to SSID:%s\", EXAMPLE_ESP_WIFI_SSID);\n        err = ESP_FAIL;\n        goto fail;\n    } else {\n        ESP_LOGE(TAG, \"Unknown event\");\n        err = ESP_FAIL;\n        goto fail;\n    }\n\n    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));\n    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));\n    vEventGroupDelete(s_wifi_event_group);\n\n    err = ESP_OK;\nfail:\n    return err;\n}\n#endif // CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER\n\n#ifdef CONFIG_WIREGUARD_ESP_NETIF\nstatic esp_err_t wifi_init_netif(void)\n{\n    esp_err_t err = ESP_FAIL;\n    esp_netif_t *sta_netif;\n\n    s_wifi_event_group = xEventGroupCreate();\n    ESP_ERROR_CHECK(esp_netif_init());\n\n    ESP_ERROR_CHECK(esp_event_loop_create_default());\n    sta_netif = esp_netif_create_default_wifi_sta();\n    assert(sta_netif);\n\n    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();\n    ESP_ERROR_CHECK(esp_wifi_init(&cfg));\n\n    esp_event_handler_instance_t instance_any_id;\n    esp_event_handler_instance_t instance_got_ip;\n    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,\n                                                        ESP_EVENT_ANY_ID,\n                                                        &event_handler,\n                                                        NULL,\n                                                        &instance_any_id));\n    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,\n                                                        IP_EVENT_STA_GOT_IP,\n                                                        &event_handler,\n                                                        NULL,\n                                                        &instance_got_ip));\n\n    wifi_config_t wifi_config = {\n        .sta = {\n            .ssid = EXAMPLE_ESP_WIFI_SSID,\n            .password = EXAMPLE_ESP_WIFI_PASS,\n            /* Setting a password implies station will connect to all security modes including WEP/WPA.\n             * However these modes are deprecated and not advisable to be used. Incase your Access point\n             * doesn't support WPA2, these mode can be enabled by commenting below line */\n         .threshold.authmode = WIFI_AUTH_WPA2_PSK,\n\n            .pmf_cfg = {\n                .capable = true,\n                .required = false\n            },\n        },\n    };\n    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );\n    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );\n    ESP_ERROR_CHECK(esp_wifi_start() );\n\n    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum\n     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */\n    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,\n            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,\n            pdFALSE,\n            pdFALSE,\n            portMAX_DELAY);\n\n    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually\n     * happened. */\n    if (bits & WIFI_CONNECTED_BIT) {\n        ESP_LOGI(TAG, \"Connected to ap SSID:%s\", EXAMPLE_ESP_WIFI_SSID);\n    } else if (bits & WIFI_FAIL_BIT) {\n        ESP_LOGI(TAG, \"Failed to connect to SSID:%s\", EXAMPLE_ESP_WIFI_SSID);\n        err = ESP_FAIL;\n        goto fail;\n    } else {\n        ESP_LOGE(TAG, \"Unknown event\");\n        err = ESP_FAIL;\n        goto fail;\n    }\n\n    err = esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip);\n    if (err != ESP_OK) {\n        ESP_LOGE(TAG, \"esp_event_handler_instance_unregister: %s\", esp_err_to_name(err));\n        goto fail;\n    }\n    err = esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id);\n    if (err != ESP_OK) {\n        ESP_LOGE(TAG, \"esp_event_handler_instance_unregister: %s\", esp_err_to_name(err));\n        goto fail;\n    }\n    vEventGroupDelete(s_wifi_event_group);\n\n    err = ESP_OK;\nfail:\n    return err;\n}\n#endif // CONFIG_WIREGUARD_ESP_NETIF\n\nstatic esp_err_t wifi_init_sta(void)\n{\n#if defined(CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER)\n    return wifi_init_tcpip_adaptor();\n#endif\n#if defined(CONFIG_WIREGUARD_ESP_NETIF)\n    return wifi_init_netif();\n#endif\n}\nstatic void test_on_ping_success(esp_ping_handle_t hdl, void *args)\n{\n    uint8_t ttl;\n    uint16_t seqno;\n    uint32_t elapsed_time, recv_len;\n    ip_addr_t target_addr;\n    esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));\n    esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));\n    esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));\n    esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));\n    esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));\n    ESP_LOGI(TAG, \"%\" PRIu32 \" bytes from %s icmp_seq=%\" PRIu16 \" ttl=%\" PRIi8 \" time=%\" PRIu32 \" ms\",\n           recv_len, ipaddr_ntoa(&target_addr), seqno, ttl, elapsed_time);\n}\n\nstatic void test_on_ping_timeout(esp_ping_handle_t hdl, void *args)\n{\n    uint16_t seqno;\n    ip_addr_t target_addr;\n    esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));\n    esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));\n    ESP_LOGI(TAG, \"From %s icmp_seq=%\" PRIu16 \" timeout\", ipaddr_ntoa(&target_addr), seqno);\n}\n\nstatic void test_on_ping_end(esp_ping_handle_t hdl, void *args)\n{\n    uint32_t transmitted;\n    uint32_t received;\n    uint32_t total_time_ms;\n\n    esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));\n    esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));\n    esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));\n    ESP_LOGI(TAG, \"%\" PRIu32 \" packets transmitted, %\" PRIu32 \" received, time %\" PRIu32 \"ms\", transmitted, received, total_time_ms);\n}\n\nvoid start_ping()\n{\n    ESP_LOGI(TAG, \"Initializing ping...\");\n    /* convert URL to IP address */\n    ip_addr_t target_addr;\n    struct addrinfo *res = NULL;\n    struct addrinfo hint;\n    memset(&hint, 0, sizeof(hint));\n    memset(&target_addr, 0, sizeof(target_addr));\n    ESP_ERROR_CHECK(lwip_getaddrinfo(CONFIG_EXAMPLE_PING_ADDRESS, NULL, &hint, &res) == 0 ? ESP_OK : ESP_FAIL);\n    struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;\n    inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);\n    lwip_freeaddrinfo(res);\n    ESP_LOGI(TAG, \"ICMP echo target: %s\", CONFIG_EXAMPLE_PING_ADDRESS);\n    esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();\n    ping_config.target_addr = target_addr;          // target IP address\n    ping_config.count = ESP_PING_COUNT_INFINITE;    // ping in infinite mode, esp_ping_stop can stop it\n\n    /* set callback functions */\n    esp_ping_callbacks_t cbs;\n    cbs.on_ping_success = test_on_ping_success;\n    cbs.on_ping_timeout = test_on_ping_timeout;\n    cbs.on_ping_end = test_on_ping_end;\n    cbs.cb_args = NULL;\n\n    esp_ping_handle_t ping;\n    ESP_ERROR_CHECK(esp_ping_new_session(&ping_config, &cbs, &ping));\n    esp_ping_start(ping);\n}\n\nvoid app_main(void)\n{\n    esp_err_t err;\n    time_t now;\n    struct tm timeinfo;\n    char strftime_buf[64];\n    wireguard_ctx_t ctx = {0};\n\n    esp_log_level_set(\"esp_wireguard\", ESP_LOG_DEBUG);\n    esp_log_level_set(\"wireguardif\", ESP_LOG_DEBUG);\n    esp_log_level_set(\"wireguard\", ESP_LOG_DEBUG);\n    err = nvs_flash_init();\n#if defined(CONFIG_IDF_TARGET_ESP8266) && ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(3, 4, 0)\n    if (err == ESP_ERR_NVS_NO_FREE_PAGES) {\n#else\n    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {\n#endif\n      ESP_ERROR_CHECK(nvs_flash_erase());\n      err = nvs_flash_init();\n    }\n    ESP_ERROR_CHECK(err);\n\n    err = wifi_init_sta();\n    if (err != ESP_OK) {\n        ESP_LOGE(TAG, \"wifi_init_sta: %s\", esp_err_to_name(err));\n        goto fail;\n    }\n\n    obtain_time();\n    time(&now);\n\n    setenv(\"TZ\", \"EST5EDT,M3.2.0/2,M11.1.0\", 1);\n    tzset();\n    localtime_r(&now, &timeinfo);\n    strftime(strftime_buf, sizeof(strftime_buf), \"%c\", &timeinfo);\n    ESP_LOGI(TAG, \"The current date/time in New York is: %s\", strftime_buf);\n\n    err = wireguard_setup(&ctx);\n    if (err != ESP_OK) {\n        ESP_LOGE(TAG, \"wireguard_setup: %s\", esp_err_to_name(err));\n        goto fail;\n    }\n\n    while (1) {\n        vTaskDelay(1000 / portTICK_PERIOD_MS);\n        err = esp_wireguardif_peer_is_up(&ctx);\n        if (err == ESP_OK) {\n            ESP_LOGI(TAG, \"Peer is up\");\n            break;\n        } else {\n            ESP_LOGI(TAG, \"Peer is down\");\n        }\n    }\n    start_ping();\n\n    while (1) {\n        vTaskDelay(1000 * 10 / portTICK_PERIOD_MS);\n        ESP_LOGI(TAG, \"Disconnecting.\");\n        esp_wireguard_disconnect(&ctx);\n        ESP_LOGI(TAG, \"Disconnected.\");\n\n        vTaskDelay(1000 * 10 / portTICK_PERIOD_MS);\n        ESP_LOGI(TAG, \"Connecting.\");\n        err = esp_wireguard_connect(&ctx);\n        if (err != ESP_OK) {\n            ESP_LOGE(TAG, \"esp_wireguard_connect: %s\", esp_err_to_name(err));\n            goto fail;\n        }\n        while (esp_wireguardif_peer_is_up(&ctx) != ESP_OK) {\n            vTaskDelay(1000 / portTICK_PERIOD_MS);\n        }\n        ESP_LOGI(TAG, \"Peer is up\");\n        esp_wireguard_set_default(&ctx);\n    }\n\nfail:\n    ESP_LOGE(TAG, \"Halting due to error\");\n    while (1) {\n        vTaskDelay(1000 / portTICK_PERIOD_MS);\n    }\n}\n"
  },
  {
    "path": "examples/demo/main/sync_time.c",
    "content": "#include <time.h>\n#include <inttypes.h>\n#include <esp_sntp.h>\n#include <esp_log.h>\n\n#include \"sync_time.h\"\n\n#define TAG \"sync_time\"\n\nstatic void time_sync_notification_cb(struct timeval *tv)\n{\n\tESP_LOGI(TAG, \"Time synced\");\n}\n\nstatic void initialize_sntp(void)\n{\n\tESP_LOGI(TAG, \"Initializing SNTP\");\n\tsntp_setoperatingmode(SNTP_OPMODE_POLL);\n\tsntp_setservername(0, \"pool.ntp.org\");\n\tsntp_set_time_sync_notification_cb(time_sync_notification_cb);\n#ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH\n\tsntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);\n#endif\n\tsntp_init();\n}\n\nvoid obtain_time(void)\n{\n\tint retry = 0;\n\tconst int retry_count = 20;\n\n\tinitialize_sntp();\n\twhile (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) {\n\t\tESP_LOGI(TAG, \"Waiting for system time to be set... (%i/%i)\", retry, retry_count);\n\t\tvTaskDelay(2000 / portTICK_PERIOD_MS);\n\t}\n}\n// vim: noexpandtab\n"
  },
  {
    "path": "examples/demo/main/sync_time.h",
    "content": "#if !defined(__SYNC_TIME__H__)\n#define __SYNC_TIME__H__\n\nvoid obtain_time(void);\n\n#endif\n"
  },
  {
    "path": "idf_component.yml",
    "content": "---\nversion: \"0.9.0\"\ndescription: WireGuard Implementation for ESP-IDF\nurl: https://github.com/trombik/esp_wireguard\nlicense: BSD-3-Clause\n"
  },
  {
    "path": "include/esp_wireguard.h",
    "content": "/*\n * Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n */\n#if !defined(__ESP_WIREGUARD__H__)\n#define __ESP_WIREGUARD__H__\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <esp_err.h>\n#include <lwip/netif.h>\n\n#define ESP_WIREGUARD_CONFIG_DEFAULT() { \\\n    .private_key = NULL, \\\n    .listen_port = 0, \\\n    .fw_mark = 0, \\\n    .public_key = NULL, \\\n    .preshared_key = NULL, \\\n    .allowed_ip = NULL, \\\n    .allowed_ip_mask = NULL, \\\n    .endpoint = NULL, \\\n    .port = 51820, \\\n    .persistent_keepalive = 0, \\\n}\n\ntypedef struct {\n    /* interface config */\n    char*       private_key;            /**< a base64 private key generated by wg genkey. Required. */\n    int         listen_port;            /**< a 16-bit port for listening */\n    uint32_t    fw_mark;                /**< a 32-bit fwmark for outgoing packets */\n    /* peer config */\n    char*       public_key;             /**< a base64 public key calculated by wg pubkey from a private key. Required. */\n    char*       preshared_key;          /**< a base64 preshared key generated by wg genpsk. */\n    char*       allowed_ip;             /**< a local IP address. */\n    char*       allowed_ip_mask;        /**< a subnet mask of the local IP address. */\n    char*       endpoint;               /**< an endpoint IP address or hostname. */\n    int         port;                   /**< a port number of remote endpoint. Default is 51820. */\n    int         persistent_keepalive;   /**< a seconds interval, between 1 and 65535 inclusive, of how often to send an\n                                             authenticated empty packet to the peer for the purpose of keeping a stateful\n                                             firewall or NAT mapping valid persistently. Set zero to disable the feature.\n                                             Default is zero. */\n} wireguard_config_t;\n\ntypedef struct {\n    wireguard_config_t* config;        /**< a pointer to wireguard config */\n    struct netif*       netif;         /**< a pointer to configured netif */\n    struct netif*       netif_default; /**< a pointer to the default netif. */\n} wireguard_ctx_t;\n\n/**\n * @brief Initialize WireGuard\n *\n * Call this function to initilize the context of WireGuard.\n *\n * Do not call this function multiple times.\n *\n * To connect to other peer, use `esp_wireguard_disconnect()`, and\n * `esp_wireguard_init()` with a new configuration.\n *\n * @param       config WireGuard configuration.\n * @param[out]  ctx Context of WireGuard.\n *\n * @return\n *      - ESP_OK: Successfully initilized WireGuard interface.\n *      - ESP_ERR_INVALID_ARG: given argument is invalid.\n *      - ESP_FAIL: Other error.\n */\nesp_err_t esp_wireguard_init(wireguard_config_t *config, wireguard_ctx_t *ctx);\n\n/**\n * @brief Create a WireGuard interface and start establishing the connection\n *        to the peer.\n *\n * Call the funtion to start establishing the connection. Note that `ESP_OK`\n * does not mean the connection is established. To see if the connection is\n * established, or the peer is up, use `esp_wireguardif_peer_is_up()`.\n *\n * Do not call this function multiple times.\n *\n * @param       ctx Context of WireGuard.\n * @return\n *      - ESP_OK on success.\n *      - ESP_FAIL on failure.\n */\nesp_err_t esp_wireguard_connect(wireguard_ctx_t *ctx);\n\n/**\n * @brief Set the default gateway to the peer.\n * @param ctx Context of WireGuard\n * @return\n *      - ESP_OK on success.\n */\nesp_err_t esp_wireguard_set_default(wireguard_ctx_t *ctx);\n\n/**\n * @brief Test if the peer is up.\n */\nesp_err_t esp_wireguardif_peer_is_up(wireguard_ctx_t *ctx);\n\n/**\n * @brief Disconnect from the peer\n *\n * @param ctx Context of WireGuard.\n * @return\n *      - ESP_OK on success.\n */\nesp_err_t esp_wireguard_disconnect(wireguard_ctx_t *ctx);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n// vim: expandtab\n"
  },
  {
    "path": "src/crypto/refc/blake2s.c",
    "content": "// Taken from RFC7693 - https://tools.ietf.org/html/rfc7693\n\n#include \"blake2s.h\"\n#include \"../../crypto.h\"\n\n// Cyclic right rotation.\n\n#ifndef ROTR32\n#define ROTR32(x, y)  (((x) >> (y)) ^ ((x) << (32 - (y))))\n#endif\n\n// Mixing function G.\n#define B2S_G(a, b, c, d, x, y) {   \\\n\tv[a] = v[a] + v[b] + x;         \\\n\tv[d] = ROTR32(v[d] ^ v[a], 16); \\\n\tv[c] = v[c] + v[d];             \\\n\tv[b] = ROTR32(v[b] ^ v[c], 12); \\\n\tv[a] = v[a] + v[b] + y;         \\\n\tv[d] = ROTR32(v[d] ^ v[a], 8);  \\\n\tv[c] = v[c] + v[d];             \\\n\tv[b] = ROTR32(v[b] ^ v[c], 7); }\n\n// Initialization Vector.\nstatic const uint32_t blake2s_iv[8] =\n{\n\t0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,\n\t0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19\n};\n\n// Compression function. \"last\" flag indicates last block.\nstatic void blake2s_compress(blake2s_ctx *ctx, int last)\n{\n\tconst uint8_t sigma[10][16] = {\n\t\t{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },\n\t\t{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },\n\t\t{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },\n\t\t{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },\n\t\t{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },\n\t\t{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },\n\t\t{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },\n\t\t{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },\n\t\t{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },\n\t\t{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }\n\t};\n\tint i;\n\tuint32_t v[16], m[16];\n\n\tfor (i = 0; i < 8; i++) {           // init work variables\n\t   v[i] = ctx->h[i];\n\t   v[i + 8] = blake2s_iv[i];\n\t}\n\n\tv[12] ^= ctx->t[0];                 // low 32 bits of offset\n\tv[13] ^= ctx->t[1];                 // high 32 bits\n\tif (last)                           // last block flag set ?\n\t\tv[14] = ~v[14];\n\tfor (i = 0; i < 16; i++)            // get little-endian words\n\t\tm[i] = U8TO32_LITTLE(&ctx->b[4 * i]);\n\n\tfor (i = 0; i < 10; i++) {          // ten rounds\n\t\tB2S_G( 0, 4,  8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);\n\t\tB2S_G( 1, 5,  9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);\n\t\tB2S_G( 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);\n\t\tB2S_G( 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);\n\t\tB2S_G( 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);\n\t\tB2S_G( 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);\n\t\tB2S_G( 2, 7,  8, 13, m[sigma[i][12]], m[sigma[i][13]]);\n\t\tB2S_G( 3, 4,  9, 14, m[sigma[i][14]], m[sigma[i][15]]);\n\t}\n\n\tfor( i = 0; i < 8; ++i )\n\t\tctx->h[i] ^= v[i] ^ v[i + 8];\n}\n\n// Initialize the hashing context \"ctx\" with optional key \"key\".\n//      1 <= outlen <= 32 gives the digest size in bytes.\n//      Secret key (also <= 32 bytes) is optional (keylen = 0).\nint blake2s_init(blake2s_ctx *ctx, size_t outlen,\n\tconst void *key, size_t keylen)     // (keylen=0: no key)\n{\n\tsize_t i;\n\n\tif (outlen == 0 || outlen > 32 || keylen > 32)\n\treturn -1;                      // illegal parameters\n\n\tfor (i = 0; i < 8; i++)             // state, \"param block\"\n\t\tctx->h[i] = blake2s_iv[i];\n\tctx->h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen;\n\n\tctx->t[0] = 0;                      // input count low word\n\tctx->t[1] = 0;                      // input count high word\n\tctx->c = 0;                         // pointer within buffer\n\tctx->outlen = outlen;\n\n\tfor (i = keylen; i < 64; i++)       // zero input block\n\t\tctx->b[i] = 0;\n\tif (keylen > 0) {\n\t\tblake2s_update(ctx, key, keylen);\n\t\tctx->c = 64;                    // at the end\n\t}\n\n\treturn 0;\n}\n\n// Add \"inlen\" bytes from \"in\" into the hash.\nvoid blake2s_update(blake2s_ctx *ctx,\n\tconst void *in, size_t inlen)       // data bytes\n{\n\tsize_t i;\n\n\tfor (i = 0; i < inlen; i++) {\n\t\tif (ctx->c == 64) {             // buffer full ?\n\t\t\tctx->t[0] += ctx->c;        // add counters\n\t\t\tif (ctx->t[0] < ctx->c)     // carry overflow ?\n\t\t\t\tctx->t[1]++;            // high word\n\t\t\tblake2s_compress(ctx, 0);   // compress (not last)\n\t\t\tctx->c = 0;                 // counter to zero\n\t\t}\n\t\tctx->b[ctx->c++] = ((const uint8_t *) in)[i];\n\t}\n}\n\n// Generate the message digest (size given in init).\n//      Result placed in \"out\".\nvoid blake2s_final(blake2s_ctx *ctx, void *out)\n{\n\tsize_t i;\n\n\tctx->t[0] += ctx->c;                // mark last block offset\n\tif (ctx->t[0] < ctx->c)             // carry overflow\n\t\tctx->t[1]++;                    // high word\n\n\twhile (ctx->c < 64)                 // fill up with zeros\n\t\tctx->b[ctx->c++] = 0;\n\tblake2s_compress(ctx, 1);           // final block flag = 1\n\n\t// little endian convert and store\n\tfor (i = 0; i < ctx->outlen; i++) {\n\t\t((uint8_t *) out)[i] =\n\t\t(ctx->h[i >> 2] >> (8 * (i & 3))) & 0xFF;\n\t}\n}\n\n// Convenience function for all-in-one computation.\nint blake2s(void *out, size_t outlen,\n\tconst void *key, size_t keylen,\n\tconst void *in, size_t inlen)\n{\n\tblake2s_ctx ctx;\n\tif (blake2s_init(&ctx, outlen, key, keylen))\n\t\treturn -1;\n\tblake2s_update(&ctx, in, inlen);\n\tblake2s_final(&ctx, out);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/crypto/refc/blake2s.h",
    "content": "// Taken from RFC7693 - https://tools.ietf.org/html/rfc7693\n// BLAKE2s Hashing Context and API Prototypes\n#ifndef _BLAKE2S_H\n#define _BLAKE2S_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define BLAKE2S_BLOCK_SIZE 64\n\n#include <stdint.h>\n#include <stddef.h>\n\n// state context\ntypedef struct {\n    uint8_t b[64];                      // input buffer\n    uint32_t h[8];                      // chained state\n    uint32_t t[2];                      // total number of bytes\n    size_t c;                           // pointer for b[]\n    size_t outlen;                      // digest size\n} blake2s_ctx;\n\n// Initialize the hashing context \"ctx\" with optional key \"key\".\n//      1 <= outlen <= 32 gives the digest size in bytes.\n//      Secret key (also <= 32 bytes) is optional (keylen = 0).\nint blake2s_init(blake2s_ctx *ctx, size_t outlen,\n    const void *key, size_t keylen);    // secret key\n\n// Add \"inlen\" bytes from \"in\" into the hash.\nvoid blake2s_update(blake2s_ctx *ctx,   // context\n    const void *in, size_t inlen);      // data to be hashed\n\n// Generate the message digest (size given in init).\n//      Result placed in \"out\".\nvoid blake2s_final(blake2s_ctx *ctx, void *out);\n\n// All-in-one convenience function.\nint blake2s(void *out, size_t outlen,   // return buffer for digest\n    const void *key, size_t keylen,     // optional secret key\n    const void *in, size_t inlen);      // data to be hashed\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/crypto/refc/chacha20.c",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n// RFC7539 implementation of ChaCha20 with modified nonce size for WireGuard\n// https://tools.ietf.org/html/rfc7539\n// Adapted from https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c by D. J. Bernstein (Public Domain)\n// HChaCha20 is described here: https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html\n\n#include \"chacha20.h\"\n\n#include <string.h>\n#include <stdint.h>\n#include \"../../crypto.h\"\n\n// 2.3.  The ChaCha20 Block Function\n// The first four words (0-3) are constants: 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574\nstatic const uint32_t CHACHA20_CONSTANT_1 = 0x61707865;\nstatic const uint32_t CHACHA20_CONSTANT_2 = 0x3320646e;\nstatic const uint32_t CHACHA20_CONSTANT_3 = 0x79622d32;\nstatic const uint32_t CHACHA20_CONSTANT_4 = 0x6b206574;\n\n#define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n))))\n\n#define PLUS(v,w) (U32V((v) + (w)))\n#define PLUSONE(v) (PLUS((v),1))\n\n// 2.1. The ChaCha Quarter Round\n// 1.  a += b; d ^= a; d <<<= 16;\n// 2.  c += d; b ^= c; b <<<= 12;\n// 3.  a += b; d ^= a; d <<<= 8;\n// 4.  c += d; b ^= c; b <<<= 7;\n\n#define QUARTERROUND(a, b, c, d)       \\\n    a += b;  d ^= a;  d = ROTL32(d, 16);  \\\n    c += d;  b ^= c;  b = ROTL32(b, 12);  \\\n    a += b;  d ^= a;  d = ROTL32(d,  8);  \\\n    c += d;  b ^= c;  b = ROTL32(b,  7)\n\nstatic inline void INNER_BLOCK(uint32_t *block) {\n\tQUARTERROUND(block[0], block[4], block[ 8], block[12]); // column 0\n\tQUARTERROUND(block[1], block[5], block[ 9], block[13]); // column 1\n\tQUARTERROUND(block[2], block[6], block[10], block[14]); // column 2\n\tQUARTERROUND(block[3], block[7], block[11], block[15]); // column 3\n\tQUARTERROUND(block[0], block[5], block[10], block[15]); // diagonal 1\n\tQUARTERROUND(block[1], block[6], block[11], block[12]); // diagonal 2\n\tQUARTERROUND(block[2], block[7], block[ 8], block[13]); // diagonal 3\n\tQUARTERROUND(block[3], block[4], block[ 9], block[14]); // diagonal 4\n}\n\n#define TWENTY_ROUNDS(x) ( \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x), \\\n\tINNER_BLOCK(x) \\\n)\n\n// 2.3.  The ChaCha20 Block Function\n// chacha20_block(key, counter, nonce):\n//  state = constants | key | counter | nonce\n//  working_state = state\n//\tfor i=1 upto 10\n//   inner_block(working_state)\n//  end\n//\tstate += working_state\n//\treturn serialize(state)\n// end\nstatic void chacha20_block(struct chacha20_ctx *ctx, uint8_t *stream) {\n\tuint32_t working_state[16];\n\tint i;\n\n\tfor (i = 0; i < 16; ++i) {\n\t\tworking_state[i] = ctx->state[i];\n\t}\n\n\tTWENTY_ROUNDS(working_state);\n\n\tfor (i = 0; i < 16; ++i) {\n\t\tU32TO8_LITTLE(stream + (4 * i), PLUS(working_state[i], ctx->state[i]));\n\t}\n}\n\nvoid chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in, uint32_t len) {\n\tuint8_t output[CHACHA20_BLOCK_SIZE];\n\tint i;\n\n\tif (len) {\n\t\tfor (;;) {\n\t\t\tchacha20_block(ctx, output);\n\t\t\t// Word 12 is a block counter\n\t\t\tctx->state[12] = PLUSONE(ctx->state[12]);\n\t\t\tif (len <= 64) {\n\t\t\t\tfor (i = 0;i < len;++i) {\n\t\t\t\t\tout[i] = in[i] ^ output[i];\n\t\t\t    }\n\t\t\t    return;\n\t\t\t}\n\t\t\tfor (i = 0;i < 64;++i) {\n\t\t\t\tout[i] = in[i] ^ output[i];\n\t\t\t}\n\t\t\tlen -= 64;\n\t\t\tout += 64;\n\t\t\tin += 64;\n\t\t}\n\t}\n}\n\n\n// 2.3.  The ChaCha20 Block Function\n// The first four words (0-3) are constants: 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574\n// The next eight words (4-11) are taken from the 256-bit key by reading the bytes in little-endian order, in 4-byte chunks.\n// Word 12 is a block counter.  Since each block is 64-byte, a 32-bit word is enough for 256 gigabytes of data.\n// Words 13-15 are a nonce, which should not be repeated for the same key.\n// For wireguard: \"nonce being composed of 32 bits of zeros followed by the 64-bit little-endian value of counter.\" where counter comes from the Wireguard layer and is separate from the block counter in word 12\nvoid chacha20_init(struct chacha20_ctx *ctx, const uint8_t *key, const uint64_t nonce) {\n\tctx->state[0] = CHACHA20_CONSTANT_1;\n\tctx->state[1] = CHACHA20_CONSTANT_2;\n\tctx->state[2] = CHACHA20_CONSTANT_3;\n\tctx->state[3] = CHACHA20_CONSTANT_4;\n\tctx->state[4] = U8TO32_LITTLE(key + 0);\n\tctx->state[5] = U8TO32_LITTLE(key + 4);\n\tctx->state[6] = U8TO32_LITTLE(key + 8);\n\tctx->state[7] = U8TO32_LITTLE(key + 12);\n\tctx->state[8] = U8TO32_LITTLE(key + 16);\n\tctx->state[9] = U8TO32_LITTLE(key + 20);\n\tctx->state[10] = U8TO32_LITTLE(key + 24);\n\tctx->state[11] = U8TO32_LITTLE(key + 28);\n\tctx->state[12] = 0;\n\tctx->state[13] = 0;\n\tctx->state[14] = nonce & 0xFFFFFFFF;\n\tctx->state[15] = nonce >> 32;\n}\n\n// 2.2. HChaCha20\n// HChaCha20 is initialized the same way as the ChaCha cipher, except that HChaCha20 uses a 128-bit nonce and has no counter.\n// After initialization, proceed through the ChaCha rounds as usual.\n// Once the 20 ChaCha rounds have been completed, the first 128 bits and last 128 bits of the ChaCha state (both little-endian) are concatenated, and this 256-bit subkey is returned.\nvoid hchacha20(uint8_t *out, const uint8_t *nonce, const uint8_t *key) {\n\tuint32_t state[16];\n\tstate[0] = CHACHA20_CONSTANT_1;\n\tstate[1] = CHACHA20_CONSTANT_2;\n\tstate[2] = CHACHA20_CONSTANT_3;\n\tstate[3] = CHACHA20_CONSTANT_4;\n\tstate[4] = U8TO32_LITTLE(key + 0);\n\tstate[5] = U8TO32_LITTLE(key + 4);\n\tstate[6] = U8TO32_LITTLE(key + 8);\n\tstate[7] = U8TO32_LITTLE(key + 12);\n\tstate[8] = U8TO32_LITTLE(key + 16);\n\tstate[9] = U8TO32_LITTLE(key + 20);\n\tstate[10] = U8TO32_LITTLE(key + 24);\n\tstate[11] = U8TO32_LITTLE(key + 28);\n\tstate[12] = U8TO32_LITTLE(nonce +  0);\n\tstate[13] = U8TO32_LITTLE(nonce +  4);\n\tstate[14] = U8TO32_LITTLE(nonce +  8);\n\tstate[15] = U8TO32_LITTLE(nonce + 12);\n\n\tTWENTY_ROUNDS(state);\n\n\t// Concatenate first/last 128 bits into 256bit output (as little endian)\n\tU32TO8_LITTLE(out + 0, state[0]);\n\tU32TO8_LITTLE(out + 4, state[1]);\n\tU32TO8_LITTLE(out + 8, state[2]);\n\tU32TO8_LITTLE(out + 12, state[3]);\n\tU32TO8_LITTLE(out + 16, state[12]);\n\tU32TO8_LITTLE(out + 20, state[13]);\n\tU32TO8_LITTLE(out + 24, state[14]);\n\tU32TO8_LITTLE(out + 28, state[15]);\n}\n"
  },
  {
    "path": "src/crypto/refc/chacha20.h",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n// RFC7539 implementation of ChaCha20 with modified nonce size for WireGuard\n// https://tools.ietf.org/html/rfc7539\n// Adapted from https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c by D. J. Bernstein (Public Domain)\n// HChaCha20 is described here: https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html\n#ifndef _CHACHA20_H_\n#define _CHACHA20_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#define CHACHA20_BLOCK_SIZE\t\t(64)\n#define CHACHA20_KEY_SIZE\t\t(32)\n\nstruct chacha20_ctx {\n\tuint32_t state[16];\n};\n\nvoid chacha20_init(struct chacha20_ctx *ctx, const uint8_t *key, const uint64_t nonce);\nvoid chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in, uint32_t len);\nvoid hchacha20(uint8_t *out, const uint8_t *nonce, const uint8_t *key);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CHACHA20_H_ */\n"
  },
  {
    "path": "src/crypto/refc/chacha20poly1305.c",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n// AEAD_CHACHA20_POLY1305 as described in https://tools.ietf.org/html/rfc7539\n// AEAD_XChaCha20_Poly1305 as described in https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html\n#include \"chacha20poly1305.h\"\n#include \"chacha20.h\"\n#include \"poly1305-donna.h\"\n\n#include <stdlib.h>\n#include <stdint.h>\n#include \"../../crypto.h\"\n\n#define POLY1305_KEY_SIZE\t\t32\n#define POLY1305_MAC_SIZE\t\t16\n\nstatic const uint8_t zero[CHACHA20_BLOCK_SIZE] = { 0 };\n\n// 2.6.  Generating the Poly1305 Key Using ChaCha20\nstatic void generate_poly1305_key(struct poly1305_context *poly1305_state, struct chacha20_ctx *chacha20_state, const uint8_t *key, uint64_t nonce) {\n\tuint8_t block[POLY1305_KEY_SIZE] = {0};\n\n\t// The method is to call the block function with the following parameters:\n\t// - The 256-bit session integrity key is used as the ChaCha20 key.\n\t// - The block counter is set to zero.\n\t// - The protocol will specify a 96-bit or 64-bit nonce\n\tchacha20_init(chacha20_state, key, nonce);\n\n\t// We take the first 256 bits or the serialized state, and use those as the one-time Poly1305 key\n\tchacha20(chacha20_state, block, block, sizeof(block));\n\n\tpoly1305_init(poly1305_state, block);\n\n\tcrypto_zero(&block, sizeof(block));\n}\n\n// 2.8.  AEAD Construction (Encryption)\nvoid chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, uint64_t nonce, const uint8_t *key) {\n\tstruct poly1305_context poly1305_state;\n\tstruct chacha20_ctx chacha20_state;\n\tuint8_t block[8];\n\tsize_t padded_len;\n\n\t// First, a Poly1305 one-time key is generated from the 256-bit key and nonce using the procedure described in Section 2.6.\n\tgenerate_poly1305_key(&poly1305_state, &chacha20_state, key, nonce);\n\n\t// Next, the ChaCha20 encryption function is called to encrypt the plaintext, using the same key and nonce, and with the initial counter set to 1.\n\tchacha20(&chacha20_state, dst, src, src_len);\n\n\t// Finally, the Poly1305 function is called with the Poly1305 key calculated above, and a message constructed as a concatenation of the following:\n\t// - The AAD\n\tpoly1305_update(&poly1305_state, ad, ad_len);\n\t// - padding1 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16\n\tpadded_len = (ad_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes\n\tpoly1305_update(&poly1305_state, zero, padded_len - ad_len);\n\t// - The ciphertext\n\tpoly1305_update(&poly1305_state, dst, src_len);\n\t// - padding2 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16.\n\tpadded_len = (src_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes\n\tpoly1305_update(&poly1305_state, zero, padded_len - src_len);\n\t// - The length of the additional data in octets (as a 64-bit little-endian integer)\n\tU64TO8_LITTLE(block, (uint64_t)ad_len);\n\tpoly1305_update(&poly1305_state, block, sizeof(block));\n\t// - The length of the ciphertext in octets (as a 64-bit little-endian integer).\n\tU64TO8_LITTLE(block, (uint64_t)src_len);\n\tpoly1305_update(&poly1305_state, block, sizeof(block));\n\n\t// The output from the AEAD is twofold:\n\t// - A ciphertext of the same length as the plaintext. (above, output of chacha20 into dst)\n\t// - A 128-bit tag, which is the output of the Poly1305 function. (append to dst)\n\tpoly1305_finish(&poly1305_state, dst + src_len);\n\n\t// Make sure we leave nothing sensitive on the stack\n\tcrypto_zero(&chacha20_state, sizeof(chacha20_state));\n\tcrypto_zero(&block, sizeof(block));\n}\n\n// 2.8.  AEAD Construction (Decryption)\nbool chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, uint64_t nonce, const uint8_t *key) {\n\tstruct poly1305_context poly1305_state;\n\tstruct chacha20_ctx chacha20_state;\n\tuint8_t block[8];\n\tuint8_t mac[POLY1305_MAC_SIZE];\n\tsize_t padded_len;\n\tint dst_len;\n\tbool result = false;\n\n\t// Decryption is similar [to encryption] with the following differences:\n\t// - The roles of ciphertext and plaintext are reversed, so the ChaCha20 encryption function is applied to the ciphertext, producing the plaintext.\n\t// - The Poly1305 function is still run on the AAD and the ciphertext, not the plaintext.\n\t// - The calculated tag is bitwise compared to the received tag.  The message is authenticated if and only if the tags match.\n\n\tif (src_len >= POLY1305_MAC_SIZE) {\n\t\tdst_len = src_len - POLY1305_MAC_SIZE;\n\n\t\t// First, a Poly1305 one-time key is generated from the 256-bit key and nonce using the procedure described in Section 2.6.\n\t\tgenerate_poly1305_key(&poly1305_state, &chacha20_state, key, nonce);\n\n\t\t// Calculate the MAC before attempting decryption\n\n\t\t// the Poly1305 function is called with the Poly1305 key calculated above, and a message constructed as a concatenation of the following:\n\t\t// - The AAD\n\t\tpoly1305_update(&poly1305_state, ad, ad_len);\n\t\t// - padding1 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16\n\t\tpadded_len = (ad_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes\n\t\tpoly1305_update(&poly1305_state, zero, padded_len - ad_len);\n\t\t// - The ciphertext (note the Poly1305 function is still run on the AAD and the ciphertext, not the plaintext)\n\t\tpoly1305_update(&poly1305_state, src, dst_len);\n\t\t// - padding2 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16.\n\t\tpadded_len = (dst_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes\n\t\tpoly1305_update(&poly1305_state, zero, padded_len - dst_len);\n\t\t// - The length of the additional data in octets (as a 64-bit little-endian integer)\n\t\tU64TO8_LITTLE(block, (uint64_t)ad_len);\n\t\tpoly1305_update(&poly1305_state, block, sizeof(block));\n\t\t// - The length of the ciphertext in octets (as a 64-bit little-endian integer).\n\t\tU64TO8_LITTLE(block, (uint64_t)dst_len);\n\t\tpoly1305_update(&poly1305_state, block, sizeof(block));\n\n\t\t// The output from the AEAD is twofold:\n\t\t// - A plaintext of the same length as the ciphertext. (below, output of chacha20 into dst)\n\t\t// - A 128-bit tag, which is the output of the Poly1305 function. (into mac for checking against passed mac)\n\t\tpoly1305_finish(&poly1305_state, mac);\n\n\n\t\tif (crypto_equal(mac, src + dst_len, POLY1305_MAC_SIZE)) {\n\t\t\t// mac is correct - do the decryption\n\t\t\t// Next, the ChaCha20 encryption function is called to decrypt the ciphertext, using the same key and nonce, and with the initial counter set to 1.\n\t\t\tchacha20(&chacha20_state, dst, src, dst_len);\n\t\t\tresult = true;\n\t\t}\n\t}\n\treturn result;\n}\n\n// AEAD_XChaCha20_Poly1305\n// XChaCha20-Poly1305 is a variant of the ChaCha20-Poly1305 AEAD construction as defined in [RFC7539] that uses a 192-bit nonce instead of a 96-bit nonce.\n// The algorithm for XChaCha20-Poly1305 is as follows:\n// 1. Calculate a subkey from the first 16 bytes of the nonce and the key, using HChaCha20 (Section 2.2).\n// 2. Use the subkey and remaining 8 bytes of the nonce (prefixed with 4 NUL bytes) with AEAD_CHACHA20_POLY1305 from [RFC7539] as normal. The definition for XChaCha20 is given in Section 2.3.\nvoid xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, const uint8_t *nonce, const uint8_t *key) {\n\tuint8_t subkey[CHACHA20_KEY_SIZE];\n\tuint64_t new_nonce;\n\n\tnew_nonce = U8TO64_LITTLE(nonce + 16);\n\n\thchacha20(subkey, nonce, key);\n\tchacha20poly1305_encrypt(dst, src, src_len, ad, ad_len, new_nonce, subkey);\n\n\tcrypto_zero(subkey, sizeof(subkey));\n}\n\nbool xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, const uint8_t *nonce, const uint8_t *key) {\n\tuint8_t subkey[CHACHA20_KEY_SIZE];\n\tuint64_t new_nonce;\n\tbool result;\n\n\tnew_nonce = U8TO64_LITTLE(nonce + 16);\n\n\thchacha20(subkey, nonce, key);\n\tresult = chacha20poly1305_decrypt(dst, src, src_len, ad, ad_len, new_nonce, subkey);\n\n\tcrypto_zero(subkey, sizeof(subkey));\n\treturn result;\n}\n"
  },
  {
    "path": "src/crypto/refc/chacha20poly1305.h",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n#ifndef _CHACHA20POLY1305_H_\n#define _CHACHA20POLY1305_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdint.h>\n\n// Aead(key, counter, plain text, auth text) ChaCha20Poly1305 AEAD, as specified in RFC7539 [17], with its nonce being composed of 32 bits of zeros followed by the 64-bit little-endian value of counter.\n// AEAD_CHACHA20_POLY1305 as described in https://tools.ietf.org/html/rfc7539\nvoid chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, uint64_t nonce, const uint8_t *key);\nbool chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, uint64_t nonce, const uint8_t *key);\n\n// Xaead(key, nonce, plain text, auth text) XChaCha20Poly1305 AEAD, with a 24-byte random nonce, instantiated using HChaCha20 [6] and ChaCha20Poly1305.\n// AEAD_XChaCha20_Poly1305 as described in https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html\nvoid xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, const uint8_t *nonce, const uint8_t *key);\nbool xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, const uint8_t *nonce, const uint8_t *key);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CHACHA20POLY1305_H_ */\n"
  },
  {
    "path": "src/crypto/refc/poly1305-donna-32.h",
    "content": "// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT\n/*\n\tpoly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition\n*/\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if defined(_MSC_VER)\n\t#define POLY1305_NOINLINE __declspec(noinline)\n#elif defined(__GNUC__)\n\t#define POLY1305_NOINLINE __attribute__((noinline))\n#else\n\t#define POLY1305_NOINLINE\n#endif\n\n#define poly1305_block_size 16\n\n/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */\ntypedef struct poly1305_state_internal_t {\n\tunsigned long r[5];\n\tunsigned long h[5];\n\tunsigned long pad[4];\n\tsize_t leftover;\n\tunsigned char buffer[poly1305_block_size];\n\tunsigned char final;\n} poly1305_state_internal_t;\n\n/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */\nstatic unsigned long\nU8TO32(const unsigned char *p) {\n\treturn\n\t\t(((unsigned long)(p[0] & 0xff)      ) |\n\t     ((unsigned long)(p[1] & 0xff) <<  8) |\n         ((unsigned long)(p[2] & 0xff) << 16) |\n         ((unsigned long)(p[3] & 0xff) << 24));\n}\n\n/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */\nstatic void\nU32TO8(unsigned char *p, unsigned long v) {\n\tp[0] = (v      ) & 0xff;\n\tp[1] = (v >>  8) & 0xff;\n\tp[2] = (v >> 16) & 0xff;\n\tp[3] = (v >> 24) & 0xff;\n}\n\nvoid\npoly1305_init(poly1305_context *ctx, const unsigned char key[32]) {\n\tpoly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;\n\n\t/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */\n\tst->r[0] = (U8TO32(&key[ 0])     ) & 0x3ffffff;\n\tst->r[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03;\n\tst->r[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff;\n\tst->r[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff;\n\tst->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;\n\n\t/* h = 0 */\n\tst->h[0] = 0;\n\tst->h[1] = 0;\n\tst->h[2] = 0;\n\tst->h[3] = 0;\n\tst->h[4] = 0;\n\n\t/* save pad for later */\n\tst->pad[0] = U8TO32(&key[16]);\n\tst->pad[1] = U8TO32(&key[20]);\n\tst->pad[2] = U8TO32(&key[24]);\n\tst->pad[3] = U8TO32(&key[28]);\n\n\tst->leftover = 0;\n\tst->final = 0;\n}\n\nstatic void\npoly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {\n\tconst unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */\n\tunsigned long r0,r1,r2,r3,r4;\n\tunsigned long s1,s2,s3,s4;\n\tunsigned long h0,h1,h2,h3,h4;\n\tunsigned long long d0,d1,d2,d3,d4;\n\tunsigned long c;\n\n\tr0 = st->r[0];\n\tr1 = st->r[1];\n\tr2 = st->r[2];\n\tr3 = st->r[3];\n\tr4 = st->r[4];\n\n\ts1 = r1 * 5;\n\ts2 = r2 * 5;\n\ts3 = r3 * 5;\n\ts4 = r4 * 5;\n\n\th0 = st->h[0];\n\th1 = st->h[1];\n\th2 = st->h[2];\n\th3 = st->h[3];\n\th4 = st->h[4];\n\n\twhile (bytes >= poly1305_block_size) {\n\t\t/* h += m[i] */\n\t\th0 += (U8TO32(m+ 0)     ) & 0x3ffffff;\n\t\th1 += (U8TO32(m+ 3) >> 2) & 0x3ffffff;\n\t\th2 += (U8TO32(m+ 6) >> 4) & 0x3ffffff;\n\t\th3 += (U8TO32(m+ 9) >> 6) & 0x3ffffff;\n\t\th4 += (U8TO32(m+12) >> 8) | hibit;\n\n\t\t/* h *= r */\n\t\td0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1);\n\t\td1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2);\n\t\td2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3);\n\t\td3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4);\n\t\td4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0);\n\n\t\t/* (partial) h %= p */\n\t\t              c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff;\n\t\td1 += c;      c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff;\n\t\td2 += c;      c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff;\n\t\td3 += c;      c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff;\n\t\td4 += c;      c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff;\n\t\th0 += c * 5;  c =                (h0 >> 26); h0 =                h0 & 0x3ffffff;\n\t\th1 += c;\n\n\t\tm += poly1305_block_size;\n\t\tbytes -= poly1305_block_size;\n\t}\n\n\tst->h[0] = h0;\n\tst->h[1] = h1;\n\tst->h[2] = h2;\n\tst->h[3] = h3;\n\tst->h[4] = h4;\n}\n\nPOLY1305_NOINLINE void\npoly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {\n\tpoly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;\n\tunsigned long h0,h1,h2,h3,h4,c;\n\tunsigned long g0,g1,g2,g3,g4;\n\tunsigned long long f;\n\tunsigned long mask;\n\n\t/* process the remaining block */\n\tif (st->leftover) {\n\t\tsize_t i = st->leftover;\n\t\tst->buffer[i++] = 1;\n\t\tfor (; i < poly1305_block_size; i++)\n\t\t\tst->buffer[i] = 0;\n\t\tst->final = 1;\n\t\tpoly1305_blocks(st, st->buffer, poly1305_block_size);\n\t}\n\n\t/* fully carry h */\n\th0 = st->h[0];\n\th1 = st->h[1];\n\th2 = st->h[2];\n\th3 = st->h[3];\n\th4 = st->h[4];\n\n\t             c = h1 >> 26; h1 = h1 & 0x3ffffff;\n\th2 +=     c; c = h2 >> 26; h2 = h2 & 0x3ffffff;\n\th3 +=     c; c = h3 >> 26; h3 = h3 & 0x3ffffff;\n\th4 +=     c; c = h4 >> 26; h4 = h4 & 0x3ffffff;\n\th0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;\n\th1 +=     c;\n\n\t/* compute h + -p */\n\tg0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;\n\tg1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;\n\tg2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;\n\tg3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;\n\tg4 = h4 + c - (1UL << 26);\n\n\t/* select h if h < p, or h + -p if h >= p */\n\tmask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;\n\tg0 &= mask;\n\tg1 &= mask;\n\tg2 &= mask;\n\tg3 &= mask;\n\tg4 &= mask;\n\tmask = ~mask;\n\th0 = (h0 & mask) | g0;\n\th1 = (h1 & mask) | g1;\n\th2 = (h2 & mask) | g2;\n\th3 = (h3 & mask) | g3;\n\th4 = (h4 & mask) | g4;\n\n\t/* h = h % (2^128) */\n\th0 = ((h0      ) | (h1 << 26)) & 0xffffffff;\n\th1 = ((h1 >>  6) | (h2 << 20)) & 0xffffffff;\n\th2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;\n\th3 = ((h3 >> 18) | (h4 <<  8)) & 0xffffffff;\n\n\t/* mac = (h + pad) % (2^128) */\n\tf = (unsigned long long)h0 + st->pad[0]            ; h0 = (unsigned long)f;\n\tf = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f;\n\tf = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f;\n\tf = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f;\n\n\tU32TO8(mac +  0, h0);\n\tU32TO8(mac +  4, h1);\n\tU32TO8(mac +  8, h2);\n\tU32TO8(mac + 12, h3);\n\n\t/* zero out the state */\n\tst->h[0] = 0;\n\tst->h[1] = 0;\n\tst->h[2] = 0;\n\tst->h[3] = 0;\n\tst->h[4] = 0;\n\tst->r[0] = 0;\n\tst->r[1] = 0;\n\tst->r[2] = 0;\n\tst->r[3] = 0;\n\tst->r[4] = 0;\n\tst->pad[0] = 0;\n\tst->pad[1] = 0;\n\tst->pad[2] = 0;\n\tst->pad[3] = 0;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "src/crypto/refc/poly1305-donna.c",
    "content": "// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT\n\n#include \"poly1305-donna.h\"\n#include \"poly1305-donna-32.h\"\n\nvoid\npoly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {\n\tpoly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;\n\tsize_t i;\n\n\t/* handle leftover */\n\tif (st->leftover) {\n\t\tsize_t want = (poly1305_block_size - st->leftover);\n\t\tif (want > bytes)\n\t\t\twant = bytes;\n\t\tfor (i = 0; i < want; i++)\n\t\t\tst->buffer[st->leftover + i] = m[i];\n\t\tbytes -= want;\n\t\tm += want;\n\t\tst->leftover += want;\n\t\tif (st->leftover < poly1305_block_size)\n\t\t\treturn;\n\t\tpoly1305_blocks(st, st->buffer, poly1305_block_size);\n\t\tst->leftover = 0;\n\t}\n\n\t/* process full blocks */\n\tif (bytes >= poly1305_block_size) {\n\t\tsize_t want = (bytes & ~(poly1305_block_size - 1));\n\t\tpoly1305_blocks(st, m, want);\n\t\tm += want;\n\t\tbytes -= want;\n\t}\n\n\t/* store leftover */\n\tif (bytes) {\n\t\tfor (i = 0; i < bytes; i++)\n\t\t\tst->buffer[st->leftover + i] = m[i];\n\t\tst->leftover += bytes;\n\t}\n}\n"
  },
  {
    "path": "src/crypto/refc/poly1305-donna.h",
    "content": "// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT\n#ifndef POLY1305_DONNA_H\n#define POLY1305_DONNA_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stddef.h>\n\ntypedef struct poly1305_context {\n\tsize_t aligner;\n\tunsigned char opaque[136];\n} poly1305_context;\n\nvoid poly1305_init(poly1305_context *ctx, const unsigned char key[32]);\nvoid poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes);\nvoid poly1305_finish(poly1305_context *ctx, unsigned char mac[16]);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* POLY1305_DONNA_H */\n"
  },
  {
    "path": "src/crypto/refc/x25519-license.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015-2016 Cryptography Research, Inc.\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\nall copies 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\nTHE SOFTWARE.\n"
  },
  {
    "path": "src/crypto/refc/x25519.c",
    "content": "// Taken from https://sourceforge.net/p/strobe (MIT Licence)\n/**\n * @cond internal\n * @file x25519.c\n * @copyright\n *   Copyright (c) 2015-2016 Cryptography Research, Inc.  \\n\n *   Released under the MIT License.  See LICENSE.txt for license information.\n * @author Mike Hamburg\n * @brief Key exchange and signatures based on X25519.\n */\n#include <stdint.h>\n#include \"x25519.h\"\n//#include \"strobe.h\"\n//#include \"strobe_config.h\"\n// STROBE header replacement\n#include <string.h>\n#define X25519_WBITS 32\n#define X25519_SUPPORT_SIGN 0\n#define X25519_MEMCPY_PARAMS 1\n#define X25519_USE_POWER_CHAIN 1\n#if BYTE_ORDER == LITTLE_ENDIAN\nstatic inline uint32_t eswap_letoh_32(uint32_t w) { return w; }\n#else\n#error \"Fix eswap() on non-little-endian machine\"\n#endif\n\n#if X25519_WBITS == 64\n    typedef uint64_t limb_t;\n    typedef __uint128_t dlimb_t;\n    typedef __int128_t sdlimb_t;\n    #define eswap_limb eswap_letoh_64\n    #define LIMB(x) x##ull\n#elif X25519_WBITS == 32\n    typedef uint32_t limb_t;\n    typedef uint64_t dlimb_t;\n    typedef int64_t sdlimb_t;\n    #define eswap_limb eswap_letoh_32\n    #define LIMB(x) (uint32_t)(x##ull),(uint32_t)((x##ull)>>32)\n#else\n    #error \"Need to know X25519_WBITS\"\n#endif\n\n#define NLIMBS (256/X25519_WBITS)\ntypedef limb_t fe[NLIMBS];\n\n#if X25519_SUPPORT_SIGN\ntypedef limb_t scalar_t[NLIMBS];\nstatic const limb_t MONTGOMERY_FACTOR = (limb_t)0xd2b51da312547e1bull;\nstatic const scalar_t sc_p = {\n    LIMB(0x5812631a5cf5d3ed), LIMB(0x14def9dea2f79cd6),\n    LIMB(0x0000000000000000), LIMB(0x1000000000000000)\n}, sc_r2 = {\n    LIMB(0xa40611e3449c0f01), LIMB(0xd00e1ba768859347),\n    LIMB(0xceec73d217f5be65), LIMB(0x0399411b7c309a3d)\n};\n#endif\n\nstatic inline limb_t umaal(\n    limb_t *carry, limb_t acc, limb_t mand, limb_t mier\n) {\n    dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry;\n    *carry = tmp >> X25519_WBITS;\n    return tmp;\n}\n\n/* These functions are implemented in terms of umaal on ARM */\nstatic inline limb_t adc(limb_t *carry, limb_t acc, limb_t mand) {\n    dlimb_t total = (dlimb_t)*carry + acc + mand;\n    *carry = total>>X25519_WBITS;\n    return total;\n}\n\nstatic inline limb_t adc0(limb_t *carry, limb_t acc) {\n    dlimb_t total = (dlimb_t)*carry + acc;\n    *carry = total>>X25519_WBITS;\n    return total;\n}\n\n/* Precondition: carry is small.\n * Invariant: result of propagate is < 2^255 + 1 word\n * In particular, always less than 2p.\n * Also, output x >= min(x,19)\n */\nstatic void propagate(fe x, limb_t over) {\n    unsigned i;\n    over = x[NLIMBS-1]>>(X25519_WBITS-1) | over<<1;\n    x[NLIMBS-1] &= ~((limb_t)1<<(X25519_WBITS-1));\n\n    limb_t carry = over * 19;\n    for (i=0; i<NLIMBS; i++) {\n        x[i] = adc0(&carry, x[i]);\n    }\n}\n\nstatic void add(fe out, const fe a, const fe b) {      \n    unsigned i;\n    limb_t carry = 0;\n    for (i=0; i<NLIMBS; i++) {\n        out[i] = adc(&carry, a[i], b[i]);\n    }\n    propagate(out,carry);\n}\n\nstatic void sub(fe out, const fe a, const fe b) {\n    unsigned i;\n    sdlimb_t carry = -38;\n    for (i=0; i<NLIMBS; i++) {\n        out[i] = carry = carry + a[i] - b[i];\n        carry >>= X25519_WBITS;\n    }\n    propagate(out,1+carry);\n}\n\nstatic void __attribute__((unused))\nswapin(limb_t *x, const uint8_t *in) {\n    memcpy(x,in,sizeof(fe));\n    unsigned i;\n    for (i=0; i<NLIMBS; i++) {\n        x[i] = eswap_limb(x[i]);\n    }\n}\n\nstatic void __attribute__((unused))\nswapout(uint8_t *out, limb_t *x) {\n    unsigned i;\n    for (i=0; i<NLIMBS; i++) {\n        x[i] = eswap_limb(x[i]);\n    }\n    memcpy(out,x,sizeof(fe));\n}\n\nstatic void mul(fe out, const fe a, const fe b, unsigned nb) {\n    /* GCC at least produces pretty decent asm for this, so don't need to have dedicated asm. */\n    limb_t accum[2*NLIMBS] = {0};\n    unsigned i,j;\n\n    limb_t carry2;\n    for (i=0; i<nb; i++) {\n        carry2 = 0;\n        limb_t mand = b[i];\n        for (j=0; j<NLIMBS; j++) {\n            accum[i+j] = umaal(&carry2, accum[i+j], mand, a[j]);\n        }\n        accum[i+j] = carry2;\n    }\n\n    carry2 = 0;\n    const limb_t mand = 38;\n    for (j=0; j<NLIMBS; j++) {\n         out[j] = umaal(&carry2, accum[j], mand, accum[j+NLIMBS]);\n    }\n    propagate(out,carry2);\n}\n\nstatic void sqr(fe out, const fe a) { mul(out,a,a,NLIMBS); }\nstatic void mul1(fe out, const fe a) { mul(out,a,out,NLIMBS); }\nstatic void sqr1(fe a) { mul1(a,a); }\n\nstatic void condswap(limb_t a[2*NLIMBS], limb_t b[2*NLIMBS], limb_t doswap) {\n    unsigned i;\n    for (i=0; i<2*NLIMBS; i++) {\n        limb_t xor = (a[i]^b[i]) & doswap;\n        a[i] ^= xor; b[i] ^= xor;\n    }\n}\n\nstatic limb_t canon(fe x) {\n    /* Canonicalize a field element x, reducing it to the least residue\n     * which is congruent to it mod 2^255-19.\n     *\n     * Precondition: x < 2^255 + 1 word\n     */\n\n    /* First, add 19. */\n    unsigned i;\n    limb_t carry0 = 19;\n    for (i=0; i<NLIMBS; i++) {\n        x[i] = adc0(&carry0, x[i]);\n    }\n    propagate(x,carry0);\n\n    /* Here, 19 <= x2 < 2^255\n     *\n     * This is because we added 19, so before propagate it can't be less than 19.\n     * After propagate, it still can't be less than 19, because if propagate does\n     * anything it adds 19.\n     *\n     * We know that the high bit must be clear, because either the input was\n     * ~ 2^255 + one word + 19 (in which case it propagates to at most 2 words)\n     * or it was < 2^255.\n     *\n     * So now, if we subtract 19, we will get back to something in [0,2^255-19).\n     */\n    sdlimb_t carry = -19;\n    limb_t res = 0;\n    for (i=0; i<NLIMBS; i++) {\n        res |= x[i] = carry += x[i];\n        carry >>= X25519_WBITS;\n    }\n    return ((dlimb_t)res - 1) >> X25519_WBITS;\n}\n\nstatic const limb_t a24[1]={121665};\n\nstatic void ladder_part1(fe xs[5]) {\n    limb_t *x2 = xs[0], *z2=xs[1],*x3=xs[2],*z3=xs[3],*t1=xs[4];\n    add(t1,x2,z2);  // t1 = A\n    sub(z2,x2,z2);  // z2 = B\n    add(x2,x3,z3);  // x2 = C\n    sub(z3,x3,z3);  // z3 = D\n    mul1(z3,t1);    // z3 = DA\n    mul1(x2,z2);    // x3 = BC\n    add(x3,z3,x2);  // x3 = DA+CB\n    sub(z3,z3,x2);  // z3 = DA-CB\n    sqr1(t1);       // t1 = AA\n    sqr1(z2);       // z2 = BB\n    sub(x2,t1,z2);  // x2 = E = AA-BB\n    mul(z2,x2,a24,sizeof(a24)/sizeof(a24[0])); // z2 = E*a24\n    add(z2,z2,t1);  // z2 = E*a24 + AA\n}\nstatic void ladder_part2(fe xs[5], const fe x1) {\n    limb_t *x2 = xs[0], *z2=xs[1],*x3=xs[2],*z3=xs[3],*t1=xs[4];\n    sqr1(z3);       // z3 = (DA-CB)^2\n    mul1(z3,x1);    // z3 = x1 * (DA-CB)^2\n    sqr1(x3);       // x3 = (DA+CB)^2\n    mul1(z2,x2);    // z2 = AA*(E*a24+AA)\n    sub(x2,t1,x2);  // x2 = BB again\n    mul1(x2,t1);    // x2 = AA*BB\n}\n\nstatic void x25519_core(fe xs[5], const uint8_t scalar[X25519_BYTES], const uint8_t *x1, int clamp) {\n    int i;\n#if X25519_MEMCPY_PARAMS\n    fe x1i;\n    swapin(x1i,x1);\n    x1 = (const uint8_t *)x1;\n#endif\n    limb_t swap = 0;\n    limb_t *x2 = xs[0],*x3=xs[2],*z3=xs[3];\n    memset(xs,0,4*sizeof(fe));\n    x2[0] = z3[0] = 1;\n    memcpy(x3,x1,sizeof(fe));\n\n    for (i=255; i>=0; i--) {\n        uint8_t bytei = scalar[i/8];\n        if (clamp) {\n            if (i/8 == 0) {\n                bytei &= ~7;\n            } else if (i/8 == X25519_BYTES-1) {\n                bytei &= 0x7F;\n                bytei |= 0x40;\n            }\n        }\n        limb_t doswap = -(limb_t)((bytei>>(i%8)) & 1);\n        condswap(x2,x3,swap^doswap);\n        swap = doswap;\n\n        ladder_part1(xs);\n        ladder_part2(xs,(const limb_t *)x1);\n    }\n    condswap(x2,x3,swap);\n}\n\nint x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], const uint8_t x1[X25519_BYTES], int clamp) {\n    fe xs[5];\n    x25519_core(xs,scalar,x1,clamp);\n\n    /* Precomputed inversion chain */\n    limb_t *x2 = xs[0], *z2=xs[1], *z3=xs[3];\n    int i;\n\n    limb_t *prev = z2;\n#if X25519_USE_POWER_CHAIN\n    static const struct { uint8_t a,c,n; } steps[13] = {\n        {2,1,1  },\n        {2,1,1  },\n        {4,2,3  },\n        {2,4,6  },\n        {3,1,1  },\n        {3,2,12 },\n        {4,3,25 },\n        {2,3,25 },\n        {2,4,50 },\n        {3,2,125},\n        {3,1,2  },\n        {3,1,2  },\n        {3,1,1  }\n    };\n    for (i=0; i<13; i++) {\n        int j;\n        limb_t *a = xs[steps[i].a];\n        for (j=steps[i].n; j>0; j--) {\n            sqr(a, prev);\n            prev = a;\n        }\n        mul1(a,xs[steps[i].c]);\n    }\n#else\n    /* Raise to the p-2 = 0x7f..ffeb */\n    for (i=253; i>=0; i--) {\n        sqr(z3,prev);\n        prev = z3;\n        if (i>=8 || (0xeb>>i & 1)) {\n            mul1(z3,z2);\n        }\n    }\n#endif\n\n    /* Here prev = z3 */\n    /* x2 /= z2 */\n#if X25519_MEMCPY_PARAMS\n    mul1(x2,z3);\n    int ret = canon(x2);\n    swapout(out,x2);\n#else\n    mul((limb_t *)out, x2, z3, NLIMBS);\n    int ret = canon((limb_t*)out);\n#endif\n    if (clamp) return ret;\n    else return 0;\n}\n\nconst uint8_t X25519_BASE_POINT[X25519_BYTES] = {9};\n\n#if X25519_SUPPORT_VERIFY\nstatic limb_t x25519_verify_core(\n    fe xs[5],\n    const limb_t *other1,\n    const uint8_t other2[X25519_BYTES]\n) {\n    limb_t *z2=xs[1],*x3=xs[2],*z3=xs[3];\n#if X25519_MEMCPY_PARAMS\n    fe xo2;\n    swapin(xo2,other2);\n#else\n    const limb_t *xo2 = (const limb_t *)other2;\n#endif\n\n    memcpy(x3, other1, 2*sizeof(fe));\n\n    ladder_part1(xs);\n\n    /* Here z2 = t2^2 */\n    mul1(z2,other1);\n    mul1(z2,other1+NLIMBS);\n    mul1(z2,xo2);\n    const limb_t sixteen = 16;\n    mul (z2,z2,&sixteen,1);\n\n    mul1(z3,xo2);\n    sub(z3,z3,x3);\n    sqr1(z3);\n\n    /* check equality */\n    sub(z3,z3,z2);\n\n    /* If canon(z2) then both sides are zero.\n     * If canon(z3) then the two sides are equal.\n     *\n     * Reject sigs where both sides are zero, because\n     * that can happen if an input causes the ladder to\n     * return 0/0.\n     */\n    return canon(z2) | ~canon(z3);\n}\n\nint x25519_verify_p2 (\n    const uint8_t response[X25519_BYTES],\n    const uint8_t challenge[X25519_BYTES],\n    const uint8_t eph[X25519_BYTES],\n    const uint8_t pub[X25519_BYTES]\n) {\n    fe xs[7];\n    x25519_core(&xs[0],challenge,pub,0);\n    x25519_core(&xs[2],response,X25519_BASE_POINT,0);\n    return x25519_verify_core(&xs[2],xs[0],eph);\n}\n#endif // X25519_SUPPORT_VERIFY\n\n#if X25519_SUPPORT_SIGN\nstatic void sc_montmul (\n    scalar_t out,\n    const scalar_t a,\n    const scalar_t b\n) {\n   /**\n    * OK, so carry bounding.  We're using a high carry, so that the\n    * inputs don't have to be reduced.\n    *\n    * First montmul: output < (M^2 + Mp)/M = M+p, subtract p, < M.  This gets rid of high carry.\n    * Second montmul, by r^2 mod p < p: output < (Mp + Mp)/M = 2p, subtract p, < p, done.\n    */\n    unsigned i,j;\n    limb_t hic = 0;\n    for (i=0; i<NLIMBS; i++) {\n        limb_t carry=0, carry2=0, mand = a[i], mand2 = MONTGOMERY_FACTOR;\n\n        for (j=0; j<NLIMBS; j++) {\n            limb_t acc = out[j];\n            acc = umaal(&carry, acc, mand, b[j]);\n            if (j==0) mand2 *= acc;\n            acc = umaal(&carry2, acc, mand2, sc_p[j]);\n            if (j>0) out[j-1] = acc;\n        }\n\n        /* Add two carry registers and high carry */\n        out[NLIMBS-1] = adc(&hic, carry, carry2);\n    }\n\n    /* Reduce */\n    sdlimb_t scarry = 0;\n    for (i=0; i<NLIMBS; i++) {\n        out[i] = scarry = scarry + out[i] - sc_p[i];\n        scarry >>= X25519_WBITS;\n    }\n    limb_t need_add = -(scarry + hic);\n\n    limb_t carry = 0;\n    for (i=0; i<NLIMBS; i++) {\n        out[i] = umaal(&carry, out[i], need_add, sc_p[i]);\n    }\n}\n\nvoid x25519_sign_p2 (\n    uint8_t response[X25519_BYTES],\n    const uint8_t challenge[X25519_BYTES],\n    const uint8_t eph_secret[X25519_BYTES],\n    const uint8_t secret[X25519_BYTES]\n) {\n    /* FUTURE memory/code size: just make eph_secret non-const? */\n    scalar_t scalar1;\n    swapin(scalar1,eph_secret);\n\n    #if X25519_MEMCPY_PARAMS\n    scalar_t scalar2, scalar3;\n    swapin(scalar2,secret);\n    swapin(scalar3,challenge);\n    sc_montmul(scalar1,scalar2,scalar3);\n    memset(scalar2,0,sizeof(scalar2));\n    sc_montmul(scalar2,scalar1,sc_r2);\n    swapout(response,scalar2);\n    #else\n    sc_montmul(scalar1,(const limb_t *)secret,(const limb_t *)challenge);\n    memset(response,0,X25519_BYTES);\n    sc_montmul((limb_t *)response,scalar1,sc_r2);\n    #endif\n}\n#endif // X25519_SUPPORT_SIGN\n"
  },
  {
    "path": "src/crypto/refc/x25519.h",
    "content": "// Taken from https://sourceforge.net/p/strobe (MIT Licence)\n/**\n * @file x25519.h\n * @copyright\n *   Copyright (c) 2016 Cryptography Research, Inc.  \\n\n *   Released under the MIT License.  See LICENSE.txt for license information.\n * @author Mike Hamburg\n * @brief X25519 key exchange and signatures.\n */\n\n#ifndef __X25519_H__\n#define __X25519_H__\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define X25519_BYTES (256/8)\n\n/* The base point (9) */\nextern const unsigned char X25519_BASE_POINT[X25519_BYTES];\n\n/** Number of bytes in an EC public key */\n#define EC_PUBLIC_BYTES 32\n\n/** Number of bytes in an EC private key */\n#define EC_PRIVATE_BYTES 32\n\n/**\n * Number of bytes in a Schnorr challenge.\n * Could be set to 16 in a pinch.  (FUTURE?)\n */\n#define EC_CHALLENGE_BYTES 32\n\n/** Enough bytes to get a uniform sample mod #E.  For eg a Brainpool\n * curve this would need to be more than for a private key, but due\n * to the special prime used by Curve25519, the same size is enough.\n */\n#define EC_UNIFORM_BYTES 32\n\n/* x25519 scalar multiplication.  Sets out to scalar*base.\n *\n * If clamp is set (and supported by X25519_INTEROP_SUPPORT_CLAMP)\n * then the scalar will be \"clamped\" like a Curve25519 secret key.\n * This adds almost no security, but permits interop with other x25519\n * implementations without manually clamping the keys.\n *\n * Per RFC 7748, this function returns failure (-1) if the output\n * is zero and clamp is set.  This indicates \"non-contributory behavior\",\n * meaning that one party might steer the key so that the other party's\n * contribution doesn't matter, or contributes only a little entropy.\n *\n * WARNING: however, this function differs from RFC 7748 in another way:\n * it pays attention to the high bit base[EC_PUBLIC_BYTES-1] & 0x80, but\n * RFC 7748 says to ignore this bit.  For compatibility with RFC 7748,\n * you must also clear this bit by running base[EC_PUBLIC_BYTES-1] &= 0x7F.\n * This library won't clear it for you because it takes the base point as\n * const, and (depending on build flags) dosen't copy it.\n *\n * If clamp==0, or if X25519_INTEROP_SUPPORT_CLAMP==0, then this function\n * always returns 0.\n */\nint x25519 (\n    unsigned char out[EC_PUBLIC_BYTES],\n    const unsigned char scalar[EC_PRIVATE_BYTES],\n    const unsigned char base[EC_PUBLIC_BYTES],\n    int clamp\n);\n\n/**\n * Returns 0 on success, -1 on failure.\n *\n * Per RFC 7748, this function returns failure if the output\n * is zero and clamp is set.  This usually doesn't matter for\n * base scalarmuls.\n *\n * If clamp==0, or if X25519_INTEROP_SUPPORT_CLAMP==0, then this function\n * always returns 0.\n *\n * Same as x255(out,scalar,X255_BASE_POINT), except that\n * other implementations may optimize it.\n */\nstatic inline int x25519_base (\n    unsigned char out[EC_PUBLIC_BYTES],\n    const unsigned char scalar[EC_PRIVATE_BYTES],\n    int clamp\n) {\n    return x25519(out,scalar,X25519_BASE_POINT,clamp);\n}\n\n/**\n * As x25519_base, but with a scalar that's EC_UNIFORM_BYTES long,\n * and clamp always 0 (and thus, no return value).\n *\n * This is used for signing.  Implementors must replace it for\n * curves that require more bytes for uniformity (Brainpool).\n */\nstatic inline void x25519_base_uniform (\n    unsigned char out[EC_PUBLIC_BYTES],\n    const unsigned char scalar[EC_UNIFORM_BYTES]\n) {\n    (void)x25519_base(out,scalar,0);\n}\n\n/**\n * STROBE-compatible Schnorr signatures using curve25519 (not ed25519)\n *\n * The user will call x25519_base_uniform(eph,eph_secret) to schedule\n * a random ephemeral secret key.  They then call a Schnorr oracle to\n * get a challenge, and compute the response using this function.\n */\nvoid x25519_sign_p2 (\n    unsigned char response[EC_PRIVATE_BYTES],\n    const unsigned char challenge[EC_CHALLENGE_BYTES],\n    const unsigned char eph_secret[EC_UNIFORM_BYTES],\n    const unsigned char secret[EC_PRIVATE_BYTES]\n);\n\n/**\n * STROBE-compatible signature verification using curve25519 (not ed25519).\n * This function is the public equivalent x25519_sign_p2, taking the long-term\n * and ephemeral public keys instead of secret ones.\n *\n * Returns -1 on failure and 0 on success.\n */\nint x25519_verify_p2 (\n    const unsigned char response[X25519_BYTES],\n    const unsigned char challenge[X25519_BYTES],\n    const unsigned char eph[X25519_BYTES],\n    const unsigned char pub[X25519_BYTES]\n);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __X25519_H__ */\n"
  },
  {
    "path": "src/crypto.c",
    "content": "#include \"crypto.h\"\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nvoid crypto_zero(void *dest, size_t len) {\n\tvolatile uint8_t *p = (uint8_t *)dest;\n\twhile (len--) {\n\t\t*p++ = 0;\n\t}\n}\n\nbool crypto_equal(const void *a, const void *b, size_t size) {\n\tuint8_t neq = 0;\n\twhile (size > 0) {\n\t\tneq |= *(uint8_t *)a ^ *(uint8_t *)b;\n\t\ta += 1;\n\t\tb += 1;\n\t\tsize -= 1;\n\t}\n\treturn (neq) ? false : true;\n}\n"
  },
  {
    "path": "src/crypto.h",
    "content": "#ifndef _CRYPTO_H_\n#define _CRYPTO_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n// BLAKE2S IMPLEMENTATION\n#include \"crypto/refc/blake2s.h\"\n#define wireguard_blake2s_ctx blake2s_ctx\n#define wireguard_blake2s_init(ctx,outlen,key,keylen) blake2s_init(ctx,outlen,key,keylen)\n#define wireguard_blake2s_update(ctx,in,inlen) blake2s_update(ctx,in,inlen)\n#define wireguard_blake2s_final(ctx,out) blake2s_final(ctx,out)\n#define wireguard_blake2s(out,outlen,key,keylen,in,inlen) blake2s(out,outlen,key,keylen,in,inlen)\n\n// X25519 IMPLEMENTATION\n#if defined(CONFIG_WIREGUARD_x25519_IMPLEMENTATION_DEFAULT)\n#include \"crypto/refc/x25519.h\"\n#define wireguard_x25519(a,b,c)\tx25519(a,b,c,1)\n#endif\n\n#if defined(CONFIG_WIREGUARD_x25519_IMPLEMENTATION_NACL)\n#include \"nacl/crypto_scalarmult/curve25519/ref/crypto_scalarmult.h\"\n#define wireguard_x25519(a,b,c)\tcrypto_scalarmult(a,b,c)\n#endif\n\n//#include \"crypto/cortex/scalarmult.h\"\n//#define wireguard_x25519(a,b,c)\tcrypto_scalarmult_curve25519(a,b,c)\n\n// CHACHA20POLY1305 IMPLEMENTATION\n#include \"crypto/refc/chacha20poly1305.h\"\n#define wireguard_aead_encrypt(dst,src,srclen,ad,adlen,nonce,key) chacha20poly1305_encrypt(dst,src,srclen,ad,adlen,nonce,key)\n#define wireguard_aead_decrypt(dst,src,srclen,ad,adlen,nonce,key) chacha20poly1305_decrypt(dst,src,srclen,ad,adlen,nonce,key)\n#define wireguard_xaead_encrypt(dst,src,srclen,ad,adlen,nonce,key) xchacha20poly1305_encrypt(dst,src,srclen,ad,adlen,nonce,key)\n#define wireguard_xaead_decrypt(dst,src,srclen,ad,adlen,nonce,key) xchacha20poly1305_decrypt(dst,src,srclen,ad,adlen,nonce,key)\n\n\n// Endian / unaligned helper macros\n#define U8C(v) (v##U)\n#define U32C(v) (v##U)\n\n#define U8V(v) ((uint8_t)(v) & U8C(0xFF))\n#define U32V(v) ((uint32_t)(v) & U32C(0xFFFFFFFF))\n\n#define U8TO32_LITTLE(p) \\\n  (((uint32_t)((p)[0])      ) | \\\n   ((uint32_t)((p)[1]) <<  8) | \\\n   ((uint32_t)((p)[2]) << 16) | \\\n   ((uint32_t)((p)[3]) << 24))\n\n#define U8TO64_LITTLE(p) \\\n  (((uint64_t)((p)[0])      ) | \\\n   ((uint64_t)((p)[1]) <<  8) | \\\n   ((uint64_t)((p)[2]) << 16) | \\\n   ((uint64_t)((p)[3]) << 24) | \\\n   ((uint64_t)((p)[4]) << 32) | \\\n   ((uint64_t)((p)[5]) << 40) | \\\n   ((uint64_t)((p)[6]) << 48) | \\\n   ((uint64_t)((p)[7]) << 56))\n\n#define U16TO8_BIG(p, v) \\\n  do { \\\n    (p)[1] = U8V((v)      ); \\\n    (p)[0] = U8V((v) >>  8); \\\n  } while (0)\n\n#define U32TO8_LITTLE(p, v) \\\n  do { \\\n    (p)[0] = U8V((v)      ); \\\n    (p)[1] = U8V((v) >>  8); \\\n    (p)[2] = U8V((v) >> 16); \\\n    (p)[3] = U8V((v) >> 24); \\\n  } while (0)\n\n#define U32TO8_BIG(p, v) \\\n  do { \\\n    (p)[3] = U8V((v)      ); \\\n    (p)[2] = U8V((v) >>  8); \\\n    (p)[1] = U8V((v) >> 16); \\\n    (p)[0] = U8V((v) >> 24); \\\n  } while (0)\n\n#define U64TO8_LITTLE(p, v) \\\n  do { \\\n    (p)[0] = U8V((v)      ); \\\n    (p)[1] = U8V((v) >>  8); \\\n    (p)[2] = U8V((v) >> 16); \\\n    (p)[3] = U8V((v) >> 24); \\\n    (p)[4] = U8V((v) >> 32); \\\n    (p)[5] = U8V((v) >> 40); \\\n    (p)[6] = U8V((v) >> 48); \\\n    (p)[7] = U8V((v) >> 56); \\\n} while (0)\n\n#define U64TO8_BIG(p, v) \\\n  do { \\\n    (p)[7] = U8V((v)      ); \\\n    (p)[6] = U8V((v) >>  8); \\\n    (p)[5] = U8V((v) >> 16); \\\n    (p)[4] = U8V((v) >> 24); \\\n    (p)[3] = U8V((v) >> 32); \\\n    (p)[2] = U8V((v) >> 40); \\\n    (p)[1] = U8V((v) >> 48); \\\n    (p)[0] = U8V((v) >> 56); \\\n} while (0)\n\n\nvoid crypto_zero(void *dest, size_t len);\nbool crypto_equal(const void *a, const void *b, size_t size);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CRYPTO_H_ */\n\n"
  },
  {
    "path": "src/esp_wireguard.c",
    "content": "/*\n * Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n */\n\n#include <assert.h>\n#include <string.h>\n#include <inttypes.h>\n#include <lwip/ip.h>\n#include <lwip/netdb.h>\n#include <lwip/err.h>\n#include <esp_err.h>\n#include <esp_log.h>\n#include <esp_wireguard.h>\n#include <mbedtls/base64.h>\n\n#include \"wireguard-platform.h\"\n#include \"wireguardif.h\"\n\n#define TAG \"esp_wireguard\"\n#define WG_KEY_LEN  (32)\n#define WG_B64_KEY_LEN (4 * ((WG_KEY_LEN + 2) / 3))\n#if defined(CONFIG_LWIP_IPV6)\n#define WG_ADDRSTRLEN  INET6_ADDRSTRLEN\n#else\n#define WG_ADDRSTRLEN  INET_ADDRSTRLEN\n#endif\n\nstatic struct netif wg_netif_struct = {0};\nstatic struct netif *wg_netif = NULL;\nstatic struct wireguardif_peer peer = {0};\nstatic uint8_t wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;\nstatic uint8_t preshared_key_decoded[WG_KEY_LEN];\n\nstatic esp_err_t esp_wireguard_peer_init(const wireguard_config_t *config, struct wireguardif_peer *peer)\n{\n    esp_err_t err;\n    char addr_str[WG_ADDRSTRLEN];\n    struct addrinfo *res = NULL;\n    struct addrinfo hints;\n\n    memset(&hints, 0, sizeof(hints));\n    hints.ai_family = AF_UNSPEC;\n\n    if (!config || !peer) {\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n\n    peer->public_key = config->public_key;\n    if (config->preshared_key != NULL) {\n        size_t len;\n        int res;\n\n        ESP_LOGI(TAG, \"using preshared_key\");\n        ESP_LOGD(TAG, \"preshared_key: %s\", config->preshared_key);\n#if defined(CONFIG_WIREGUARD_x25519_IMPLEMENTATION_DEFAULT)\n        ESP_LOGI(TAG, \"X25519: default\");\n#elif defined(CONFIG_WIREGUARD_x25519_IMPLEMENTATION_NACL)\n        ESP_LOGI(TAG, \"X25519: NaCL\");\n#endif\n        res = mbedtls_base64_decode(preshared_key_decoded, WG_KEY_LEN, &len, (unsigned char *)config->preshared_key, WG_B64_KEY_LEN);\n        if (res != 0 || len != WG_KEY_LEN) {\n            err = ESP_FAIL;\n            ESP_LOGE(TAG, \"base64_decode: %i\", res);\n            if (len != WG_KEY_LEN) {\n                ESP_LOGE(TAG, \"invalid decoded length, len: %u, should be %u\", len, WG_KEY_LEN);\n            }\n            goto fail;\n        }\n        peer->preshared_key = preshared_key_decoded;\n    } else {\n        peer->preshared_key = NULL;\n    }\n    peer->keep_alive = config->persistent_keepalive;\n\n    /* Allow all IPs through tunnel */\n    {\n        ip_addr_t allowed_ip = IPADDR4_INIT_BYTES(0, 0, 0, 0);\n        peer->allowed_ip = allowed_ip;\n        ip_addr_t allowed_mask = IPADDR4_INIT_BYTES(0, 0, 0, 0);\n        peer->allowed_mask = allowed_mask;\n    }\n\n    /* resolve peer name or IP address */\n    {\n        ip_addr_t endpoint_ip;\n        memset(&endpoint_ip, 0, sizeof(endpoint_ip));\n\n        /* XXX lwip_getaddrinfo returns only the first address of a host at the moment */\n        if (getaddrinfo(config->endpoint, NULL, &hints, &res) != 0) {\n            err = ESP_FAIL;\n\n            /* XXX gai_strerror() is not implemented */\n            ESP_LOGE(TAG, \"getaddrinfo: unable to resolve `%s`\", config->endpoint);\n            goto fail;\n        }\n\n        if (res->ai_family == AF_INET) {\n            struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;\n            inet_addr_to_ip4addr(ip_2_ip4(&endpoint_ip), &addr4);\n        } else {\n#if defined(CONFIG_LWIP_IPV6)\n            struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;\n            inet6_addr_to_ip6addr(ip_2_ip6(&endpoint_ip), &addr6);\n#endif\n        }\n        peer->endpoint_ip = endpoint_ip;\n\n        if (inet_ntop(res->ai_family, &(peer->endpoint_ip), addr_str, WG_ADDRSTRLEN) == NULL) {\n            ESP_LOGW(TAG, \"inet_ntop: %i\", errno);\n        } else {\n            ESP_LOGI(TAG, \"Peer: %s (%s:%i)\",\n                                            config->endpoint,\n                                            addr_str,\n                                            config->port);\n        }\n    }\n    peer->endport_port = config->port;\n    peer->keep_alive = config->persistent_keepalive;\n    err = ESP_OK;\nfail:\n    freeaddrinfo(res);\n    return err;\n}\n\nstatic esp_err_t esp_wireguard_netif_create(const wireguard_config_t *config)\n{\n    esp_err_t err;\n    ip_addr_t ip_addr;\n    ip_addr_t netmask;\n    ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0);\n    struct wireguardif_init_data wg = {0};\n\n    if (!config) {\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n\n    /* Setup the WireGuard device structure */\n    wg.private_key = config->private_key;\n    wg.listen_port = config->listen_port;\n    wg.bind_netif = NULL;\n\n    ESP_LOGI(TAG, \"allowed_ip: %s\", config->allowed_ip);\n\n    if (ipaddr_aton(config->allowed_ip, &ip_addr) != 1) {\n        ESP_LOGE(TAG, \"ipaddr_aton: invalid allowed_ip: `%s`\", config->allowed_ip);\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n    if (ipaddr_aton(config->allowed_ip_mask, &netmask) != 1) {\n        ESP_LOGE(TAG, \"ipaddr_aton: invalid allowed_ip_mask: `%s`\", config->allowed_ip_mask);\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n\n    /* Register the new WireGuard network interface with lwIP */\n    wg_netif = netif_add(\n            &wg_netif_struct,\n            ip_2_ip4(&ip_addr),\n            ip_2_ip4(&netmask),\n            ip_2_ip4(&gateway),\n            &wg, &wireguardif_init,\n            &ip_input);\n    if (wg_netif == NULL) {\n        ESP_LOGE(TAG, \"netif_add: failed\");\n        err = ESP_FAIL;\n        goto fail;\n    }\n\n    /* Mark the interface as administratively up, link up flag is set\n     * automatically when peer connects */\n    netif_set_up(wg_netif);\n    err = ESP_OK;\nfail:\n    return err;\n}\n\nesp_err_t esp_wireguard_init(wireguard_config_t *config, wireguard_ctx_t *ctx)\n{\n    esp_err_t err = ESP_FAIL;\n\n    if (!config || !ctx) {\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n\n    err = wireguard_platform_init();\n    if (err != ESP_OK) {\n        ESP_LOGE(TAG, \"wireguard_platform_init: %s\", esp_err_to_name(err));\n        goto fail;\n    }\n    ctx->config = config;\n    ctx->netif = NULL;\n    ctx->netif_default = netif_default;\n\n    err = ESP_OK;\nfail:\n    return err;\n}\n\nesp_err_t esp_wireguard_connect(wireguard_ctx_t *ctx)\n{\n    esp_err_t err = ESP_FAIL;\n    err_t lwip_err = -1;\n\n    if (!ctx) {\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n\n    if (ctx->netif == NULL) {\n        err = esp_wireguard_netif_create(ctx->config);\n        if (err != ESP_OK) {\n            ESP_LOGE(TAG, \"esp_wireguard_netif_create: %s\", esp_err_to_name(err));\n            goto fail;\n        }\n\n        /* Initialize the first WireGuard peer structure */\n        err = esp_wireguard_peer_init(ctx->config, &peer);\n        if (err != ESP_OK) {\n            ESP_LOGE(TAG, \"esp_wireguard_peer_init: %s\", esp_err_to_name(err));\n            goto fail;\n        }\n\n        /* Register the new WireGuard peer with the network interface */\n        lwip_err = wireguardif_add_peer(wg_netif, &peer, &wireguard_peer_index);\n        if (lwip_err != ERR_OK || wireguard_peer_index == WIREGUARDIF_INVALID_INDEX) {\n            ESP_LOGE(TAG, \"wireguardif_add_peer: %i\", lwip_err);\n            goto fail;\n        }\n        if (ip_addr_isany(&peer.endpoint_ip)) {\n            err = ESP_FAIL;\n            goto fail;\n        }\n        ctx->netif = wg_netif;\n        ctx->netif_default = netif_default;\n    }\n\n    ESP_LOGI(TAG, \"Connecting to %s:%i\", ctx->config->endpoint, ctx->config->port);\n    lwip_err = wireguardif_connect(wg_netif, wireguard_peer_index);\n    if (lwip_err != ERR_OK) {\n        ESP_LOGE(TAG, \"wireguardif_connect: %i\", lwip_err);\n        err = ESP_FAIL;\n        goto fail;\n    }\n    err = ESP_OK;\nfail:\n    return err;\n}\n\nesp_err_t esp_wireguard_set_default(wireguard_ctx_t *ctx)\n{\n    esp_err_t err;\n    if (!ctx) {\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n    netif_set_default(ctx->netif);\n    err = ESP_OK;\nfail:\n    return err;\n}\n\nesp_err_t esp_wireguard_disconnect(wireguard_ctx_t *ctx)\n{\n    esp_err_t err;\n    err_t lwip_err;\n\n    if (!ctx) {\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n\n    // Clear the IP address to gracefully disconnect any clients while the\n    // peers are still valid\n    netif_set_ipaddr(ctx->netif, IP4_ADDR_ANY4);\n\n    lwip_err = wireguardif_disconnect(ctx->netif, wireguard_peer_index);\n    if (lwip_err != ERR_OK) {\n        ESP_LOGW(TAG, \"wireguardif_disconnect: peer_index: %\" PRIu8 \" err: %i\", wireguard_peer_index, lwip_err);\n    }\n\n    lwip_err = wireguardif_remove_peer(ctx->netif, wireguard_peer_index);\n    if (lwip_err != ERR_OK) {\n        ESP_LOGW(TAG, \"wireguardif_remove_peer: peer_index: %\" PRIu8 \" err: %i\", wireguard_peer_index, lwip_err);\n    }\n\n    wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;\n    wireguardif_shutdown(ctx->netif);\n    netif_remove(ctx->netif);\n    wireguardif_fini(ctx->netif);\n    netif_set_default(ctx->netif_default);\n    ctx->netif = NULL;\n\n    err = ESP_OK;\nfail:\n    return err;\n}\n\nesp_err_t esp_wireguardif_peer_is_up(wireguard_ctx_t *ctx)\n{\n    esp_err_t err;\n    err_t lwip_err;\n\n    if (!ctx) {\n        err = ESP_ERR_INVALID_ARG;\n        goto fail;\n    }\n\n    lwip_err = wireguardif_peer_is_up(\n            ctx->netif,\n            wireguard_peer_index,\n            &peer.endpoint_ip,\n            &peer.endport_port);\n\n    if (lwip_err != ERR_OK) {\n        err = ESP_FAIL;\n        goto fail;\n    }\n    err = ESP_OK;\nfail:\n    return err;\n}\n"
  },
  {
    "path": "src/nacl/crypto_scalarmult/curve25519/ref/crypto_scalarmult.h",
    "content": "\nint crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned char *p);\n"
  },
  {
    "path": "src/nacl/crypto_scalarmult/curve25519/ref/smult.c",
    "content": "/*\nversion 20081011\nMatthew Dempsky\nPublic domain.\nDerived from public domain code by D. J. Bernstein.\n*/\n\n#include \"crypto_scalarmult.h\"\n\nstatic void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])\n{\n  unsigned int j;\n  unsigned int u;\n  u = 0;\n  for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; }\n  u += a[31] + b[31]; out[31] = u;\n}\n\nstatic void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])\n{\n  unsigned int j;\n  unsigned int u;\n  u = 218;\n  for (j = 0;j < 31;++j) {\n    u += a[j] + 65280 - b[j];\n    out[j] = u & 255;\n    u >>= 8;\n  }\n  u += a[31] - b[31];\n  out[31] = u;\n}\n\nstatic void squeeze(unsigned int a[32])\n{\n  unsigned int j;\n  unsigned int u;\n  u = 0;\n  for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }\n  u += a[31]; a[31] = u & 127;\n  u = 19 * (u >> 7);\n  for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }\n  u += a[31]; a[31] = u;\n}\n\nstatic const unsigned int minusp[32] = {\n 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128\n} ;\n\nstatic void freeze(unsigned int a[32])\n{\n  unsigned int aorig[32];\n  unsigned int j;\n  unsigned int negative;\n\n  for (j = 0;j < 32;++j) aorig[j] = a[j];\n  add(a,a,minusp);\n  negative = -((a[31] >> 7) & 1);\n  for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]);\n}\n\nstatic void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])\n{\n  unsigned int i;\n  unsigned int j;\n  unsigned int u;\n\n  for (i = 0;i < 32;++i) {\n    u = 0;\n    for (j = 0;j <= i;++j) u += a[j] * b[i - j];\n    for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j];\n    out[i] = u;\n  }\n  squeeze(out);\n}\n\nstatic void mult121665(unsigned int out[32],const unsigned int a[32])\n{\n  unsigned int j;\n  unsigned int u;\n\n  u = 0;\n  for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; }\n  u += 121665 * a[31]; out[31] = u & 127;\n  u = 19 * (u >> 7);\n  for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; }\n  u += out[j]; out[j] = u;\n}\n\nstatic void square(unsigned int out[32],const unsigned int a[32])\n{\n  unsigned int i;\n  unsigned int j;\n  unsigned int u;\n\n  for (i = 0;i < 32;++i) {\n    u = 0;\n    for (j = 0;j < i - j;++j) u += a[j] * a[i - j];\n    for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j];\n    u *= 2;\n    if ((i & 1) == 0) {\n      u += a[i / 2] * a[i / 2];\n      u += 38 * a[i / 2 + 16] * a[i / 2 + 16];\n    }\n    out[i] = u;\n  }\n  squeeze(out);\n}\n\nstatic void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b)\n{\n  unsigned int j;\n  unsigned int t;\n  unsigned int bminus1;\n\n  bminus1 = b - 1;\n  for (j = 0;j < 64;++j) {\n    t = bminus1 & (r[j] ^ s[j]);\n    p[j] = s[j] ^ t;\n    q[j] = r[j] ^ t;\n  }\n}\n\nstatic void mainloop(unsigned int work[64],const unsigned char e[32])\n{\n  unsigned int xzm1[64];\n  unsigned int xzm[64];\n  unsigned int xzmb[64];\n  unsigned int xzm1b[64];\n  unsigned int xznb[64];\n  unsigned int xzn1b[64];\n  unsigned int a0[64];\n  unsigned int a1[64];\n  unsigned int b0[64];\n  unsigned int b1[64];\n  unsigned int c1[64];\n  unsigned int r[32];\n  unsigned int s[32];\n  unsigned int t[32];\n  unsigned int u[32];\n  unsigned int i;\n  unsigned int j;\n  unsigned int b;\n  int pos;\n\n  for (j = 0;j < 32;++j) xzm1[j] = work[j];\n  xzm1[32] = 1;\n  for (j = 33;j < 64;++j) xzm1[j] = 0;\n\n  xzm[0] = 1;\n  for (j = 1;j < 64;++j) xzm[j] = 0;\n\n  for (pos = 254;pos >= 0;--pos) {\n    b = e[pos / 8] >> (pos & 7);\n    b &= 1;\n    select(xzmb,xzm1b,xzm,xzm1,b);\n    add(a0,xzmb,xzmb + 32);\n    sub(a0 + 32,xzmb,xzmb + 32);\n    add(a1,xzm1b,xzm1b + 32);\n    sub(a1 + 32,xzm1b,xzm1b + 32);\n    square(b0,a0);\n    square(b0 + 32,a0 + 32);\n    mult(b1,a1,a0 + 32);\n    mult(b1 + 32,a1 + 32,a0);\n    add(c1,b1,b1 + 32);\n    sub(c1 + 32,b1,b1 + 32);\n    square(r,c1 + 32);\n    sub(s,b0,b0 + 32);\n    mult121665(t,s);\n    add(u,t,b0);\n    mult(xznb,b0,b0 + 32);\n    mult(xznb + 32,s,u);\n    square(xzn1b,c1);\n    mult(xzn1b + 32,r,work);\n    select(xzm,xzm1,xznb,xzn1b,b);\n  }\n\n  for (j = 0;j < 64;++j) work[j] = xzm[j];\n}\n\nstatic void recip(unsigned int out[32],const unsigned int z[32])\n{\n  unsigned int z2[32];\n  unsigned int z9[32];\n  unsigned int z11[32];\n  unsigned int z2_5_0[32];\n  unsigned int z2_10_0[32];\n  unsigned int z2_20_0[32];\n  unsigned int z2_50_0[32];\n  unsigned int z2_100_0[32];\n  unsigned int t0[32];\n  unsigned int t1[32];\n  int i;\n\n  /* 2 */ square(z2,z);\n  /* 4 */ square(t1,z2);\n  /* 8 */ square(t0,t1);\n  /* 9 */ mult(z9,t0,z);\n  /* 11 */ mult(z11,z9,z2);\n  /* 22 */ square(t0,z11);\n  /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9);\n\n  /* 2^6 - 2^1 */ square(t0,z2_5_0);\n  /* 2^7 - 2^2 */ square(t1,t0);\n  /* 2^8 - 2^3 */ square(t0,t1);\n  /* 2^9 - 2^4 */ square(t1,t0);\n  /* 2^10 - 2^5 */ square(t0,t1);\n  /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0);\n\n  /* 2^11 - 2^1 */ square(t0,z2_10_0);\n  /* 2^12 - 2^2 */ square(t1,t0);\n  /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); }\n  /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0);\n\n  /* 2^21 - 2^1 */ square(t0,z2_20_0);\n  /* 2^22 - 2^2 */ square(t1,t0);\n  /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); }\n  /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0);\n\n  /* 2^41 - 2^1 */ square(t1,t0);\n  /* 2^42 - 2^2 */ square(t0,t1);\n  /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); }\n  /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0);\n\n  /* 2^51 - 2^1 */ square(t0,z2_50_0);\n  /* 2^52 - 2^2 */ square(t1,t0);\n  /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }\n  /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0);\n\n  /* 2^101 - 2^1 */ square(t1,z2_100_0);\n  /* 2^102 - 2^2 */ square(t0,t1);\n  /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); }\n  /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0);\n\n  /* 2^201 - 2^1 */ square(t0,t1);\n  /* 2^202 - 2^2 */ square(t1,t0);\n  /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }\n  /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0);\n\n  /* 2^251 - 2^1 */ square(t1,t0);\n  /* 2^252 - 2^2 */ square(t0,t1);\n  /* 2^253 - 2^3 */ square(t1,t0);\n  /* 2^254 - 2^4 */ square(t0,t1);\n  /* 2^255 - 2^5 */ square(t1,t0);\n  /* 2^255 - 21 */ mult(out,t1,z11);\n}\n\nint crypto_scalarmult(unsigned char *q,\n  const unsigned char *n,\n  const unsigned char *p)\n{\n  unsigned int work[96];\n  unsigned char e[32];\n  unsigned int i;\n  for (i = 0;i < 32;++i) e[i] = n[i];\n  e[0] &= 248;\n  e[31] &= 127;\n  e[31] |= 64;\n  for (i = 0;i < 32;++i) work[i] = p[i];\n  mainloop(work,e);\n  recip(work + 32,work + 32);\n  mult(work + 64,work,work + 32);\n  freeze(work + 64);\n  for (i = 0;i < 32;++i) q[i] = work[64 + i];\n  return 0;\n}\n"
  },
  {
    "path": "src/wireguard-platform.c",
    "content": "#include \"wireguard-platform.h\"\n\n#include <stdlib.h>\n#include <time.h>\n#include <inttypes.h>\n#include <lwip/sys.h>\n#include <mbedtls/entropy.h>\n#include <mbedtls/ctr_drbg.h>\n#include <esp_system.h>\n#include <esp_err.h>\n#include <esp_log.h>\n\n#include \"crypto.h\"\n\n#define ENTROPY_MINIMUM_REQUIRED_THRESHOLD\t(134)\n#define ENTROPY_FUNCTION_DATA\tNULL\n#define ENTROPY_CUSTOM_DATA\t\tNULL\n#define ENTROPY_CUSTOM_DATA_LENGTH (0)\n#define TAG \"wireguard-platform\"\n\nstatic struct mbedtls_ctr_drbg_context random_context;\nstatic struct mbedtls_entropy_context entropy_context;\n\nstatic int entropy_hw_random_source( void *data, unsigned char *output, size_t len, size_t *olen ) {\n\tesp_fill_random(output, len);\n\t*olen = len;\n\treturn 0;\n}\n\nesp_err_t wireguard_platform_init() {\n\tint mbedtls_err;\n\tesp_err_t err;\n\n\tmbedtls_entropy_init(&entropy_context);\n\tmbedtls_ctr_drbg_init(&random_context);\n\tmbedtls_err = mbedtls_entropy_add_source(\n\t\t\t&entropy_context,\n\t\t\tentropy_hw_random_source,\n\t\t\tENTROPY_FUNCTION_DATA,\n\t\t\tENTROPY_MINIMUM_REQUIRED_THRESHOLD,\n\t\t\tMBEDTLS_ENTROPY_SOURCE_STRONG);\n\tif (mbedtls_err != 0) {\n\t\tESP_LOGE(TAG, \"mbedtls_entropy_add_source: %i\", mbedtls_err);\n\t\terr = ESP_FAIL;\n\t\tgoto fail;\n\t}\n\tmbedtls_err = mbedtls_ctr_drbg_seed(\n\t\t\t&random_context,\n\t\t\tmbedtls_entropy_func,\n\t\t\t&entropy_context,\n\t\t\tENTROPY_CUSTOM_DATA,\n\t\t\tENTROPY_CUSTOM_DATA_LENGTH);\n\tif (mbedtls_err != 0) {\n\t\tESP_LOGE(TAG, \"mbedtls_ctr_drbg_seed: %i\", mbedtls_err);\n\t\terr = ESP_FAIL;\n\t\tgoto fail;\n\t}\n\terr = ESP_OK;\nfail:\n\treturn err;\n}\n\nvoid wireguard_random_bytes(void *bytes, size_t size) {\n\tmbedtls_ctr_drbg_random(&random_context, bytes, size);\n}\n\nuint32_t wireguard_sys_now() {\n\t// Default to the LwIP system time\n\treturn sys_now();\n}\n\nvoid wireguard_tai64n_now(uint8_t *output) {\n\t// See https://cr.yp.to/libtai/tai64.html\n\t// 64 bit seconds from 1970 = 8 bytes\n\t// 32 bit nano seconds from current second\n\n\tstruct timeval tv;\n\tgettimeofday(&tv, NULL);\n\n\tuint64_t seconds = 0x400000000000000aULL + tv.tv_sec;\n\tuint32_t nanos = tv.tv_usec * 1000;\n\tU64TO8_BIG(output + 0, seconds);\n\tU32TO8_BIG(output + 8, nanos);\n}\n\nbool wireguard_is_under_load() {\n\treturn false;\n}\n// vim: noexpandtab\n"
  },
  {
    "path": "src/wireguard-platform.h",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n#ifndef _WIREGUARD_PLATFORM_H_\n#define _WIREGUARD_PLATFORM_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <esp_err.h>\n\n// Peers are allocated statically inside the device structure to avoid malloc\n#define WIREGUARD_MAX_PEERS CONFIG_WIREGUARD_MAX_PEERS\n#define WIREGUARD_MAX_SRC_IPS CONFIG_WIREGUARD_MAX_SRC_IPS\n\n// Per device limit on accepting (valid) initiation requests - per peer\n#define MAX_INITIATIONS_PER_SECOND\t(CONFIG_MAX_INITIATIONS_PER_SECOND)\n\n/**\n * @brief Initialize crpyto backend\n *\n * @return ESP_OK on success.\n */\nesp_err_t wireguard_platform_init();\n\n// The number of milliseconds since system boot - for LwIP systems this could be sys_now()\nuint32_t wireguard_sys_now();\n\n// Fill the supplied buffer with random data - random data is used for generating new session keys periodically\nvoid wireguard_random_bytes(void *bytes, size_t size);\n\n// Get the current time in tai64n format - 8 byte seconds, 4 byte nano sub-second - see https://cr.yp.to/libtai/tai64.html for details\n// Output buffer passed is 12 bytes\n// The Wireguard implementation doesn't strictly need this to be a time, but instead an increasing value\n// The remote end of the Wireguard tunnel will use this value in handshake replay detection\nvoid wireguard_tai64n_now(uint8_t *output);\n\n// Is the system under load - i.e. should we generate cookie reply message in response to initiation messages\nbool wireguard_is_under_load();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _WIREGUARD_PLATFORM_H_ */\n"
  },
  {
    "path": "src/wireguard.c",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n#include \"wireguard.h\"\n\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n\n#include \"crypto.h\"\n#include <esp_log.h>\n\n#define TAG \"wireguard\"\n// For HMAC calculation\n#define WIREGUARD_BLAKE2S_BLOCK_SIZE (64)\n\n// 5.4 Messages\n// Constants\nstatic const uint8_t CONSTRUCTION[37] = \"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s\"; // The UTF-8 string literal \"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s\", 37 bytes of output\nstatic const uint8_t IDENTIFIER[34] = \"WireGuard v1 zx2c4 Jason@zx2c4.com\"; // The UTF-8 string literal \"WireGuard v1 zx2c4 Jason@zx2c4.com\", 34 bytes of output\nstatic const uint8_t LABEL_MAC1[8] = \"mac1----\"; // Label-Mac1 The UTF-8 string literal \"mac1----\", 8 bytes of output.\nstatic const uint8_t LABEL_COOKIE[8] = \"cookie--\"; // Label-Cookie The UTF-8 string literal \"cookie--\", 8 bytes of output\n\nstatic const char *base64_lookup = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\nstatic const uint8_t zero_key[WIREGUARD_PUBLIC_KEY_LEN] = { 0 };\n\n// Calculated in wireguard_init\nstatic uint8_t construction_hash[WIREGUARD_HASH_LEN];\nstatic uint8_t identifier_hash[WIREGUARD_HASH_LEN];\n\nvoid wireguard_init() {\n\twireguard_blake2s_ctx ctx;\n\t// Pre-calculate chaining key hash\n\twireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);\n\twireguard_blake2s_update(&ctx, CONSTRUCTION, sizeof(CONSTRUCTION));\n\twireguard_blake2s_final(&ctx, construction_hash);\n\t// Pre-calculate initial handshake hash - uses construction_hash calculated above\n\twireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);\n\twireguard_blake2s_update(&ctx, construction_hash, sizeof(construction_hash));\n\twireguard_blake2s_update(&ctx, IDENTIFIER, sizeof(IDENTIFIER));\n\twireguard_blake2s_final(&ctx, identifier_hash);\n}\n\nstruct wireguard_peer *peer_alloc(struct wireguard_device *device) {\n\tstruct wireguard_peer *result = NULL;\n\tstruct wireguard_peer *tmp;\n\tint x;\n\tfor (x=0; x < WIREGUARD_MAX_PEERS; x++) {\n\t\ttmp = &device->peers[x];\n\t\tif (!tmp->valid) {\n\t\t\tresult = tmp;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn result;\n}\n\nstruct wireguard_peer *peer_lookup_by_pubkey(struct wireguard_device *device, uint8_t *public_key) {\n\tstruct wireguard_peer *result = NULL;\n\tstruct wireguard_peer *tmp;\n\tint x;\n\tfor (x=0; x < WIREGUARD_MAX_PEERS; x++) {\n\t\ttmp = &device->peers[x];\n\t\tif (tmp->valid) {\n\t\t\tif (memcmp(tmp->public_key, public_key, WIREGUARD_PUBLIC_KEY_LEN) == 0) {\n\t\t\t\tresult = tmp;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nuint8_t wireguard_peer_index(struct wireguard_device *device, struct wireguard_peer *peer) {\n\tuint8_t result = 0xFF;\n\tuint8_t x;\n\tfor (x=0; x < WIREGUARD_MAX_PEERS; x++) {\n\t\tif (peer == &device->peers[x]) {\n\t\t\tresult = x;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn result;\n}\n\nstruct wireguard_peer *peer_lookup_by_peer_index(struct wireguard_device *device, uint8_t peer_index) {\n\tstruct wireguard_peer *result = NULL;\n\tif (peer_index < WIREGUARD_MAX_PEERS) {\n\t\tif (device->peers[peer_index].valid) {\n\t\t\tresult = &device->peers[peer_index];\n\t\t}\n\t}\n\treturn result;\n}\n\nstruct wireguard_peer *peer_lookup_by_receiver(struct wireguard_device *device, uint32_t receiver) {\n\tstruct wireguard_peer *result = NULL;\n\tstruct wireguard_peer *tmp;\n\tint x;\n\tfor (x=0; x < WIREGUARD_MAX_PEERS; x++) {\n\t\ttmp = &device->peers[x];\n\t\tif (tmp->valid) {\n\t\t\tif ((tmp->curr_keypair.valid && (tmp->curr_keypair.local_index == receiver)) ||\n\t\t\t\t(tmp->next_keypair.valid && (tmp->next_keypair.local_index == receiver)) ||\n\t\t\t\t(tmp->prev_keypair.valid && (tmp->prev_keypair.local_index == receiver))\n\t\t\t\t) {\n\t\t\t\tresult = tmp;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nstruct wireguard_peer *peer_lookup_by_handshake(struct wireguard_device *device, uint32_t receiver) {\n\tstruct wireguard_peer *result = NULL;\n\tstruct wireguard_peer *tmp;\n\tint x;\n\tfor (x=0; x < WIREGUARD_MAX_PEERS; x++) {\n\t\ttmp = &device->peers[x];\n\t\tif (tmp->valid) {\n\t\t\tif (tmp->handshake.valid && tmp->handshake.initiator && (tmp->handshake.local_index == receiver)) {\n\t\t\t\tresult = tmp;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nbool wireguard_expired(uint32_t created_millis, uint32_t valid_seconds) {\n\tuint32_t diff = wireguard_sys_now() - created_millis;\n\treturn (diff >= (valid_seconds * 1000));\n}\n\n\nstatic void generate_cookie_secret(struct wireguard_device *device) {\n\twireguard_random_bytes(device->cookie_secret, WIREGUARD_HASH_LEN);\n\tdevice->cookie_secret_millis = wireguard_sys_now();\n}\n\nstatic void generate_peer_cookie(struct wireguard_device *device, uint8_t *cookie, uint8_t *source_addr_port, size_t source_length) {\n\twireguard_blake2s_ctx ctx;\n\n\tif (wireguard_expired(device->cookie_secret_millis, COOKIE_SECRET_MAX_AGE)) {\n\t\t// Generate new random bytes\n\t\tgenerate_cookie_secret(device);\n\t}\n\n\t// Mac(key, input) Keyed-Blake2s(key, input, 16), the keyed MAC variant of the BLAKE2s hash function, returning 16 bytes of output\n\twireguard_blake2s_init(&ctx, WIREGUARD_COOKIE_LEN, device->cookie_secret, WIREGUARD_HASH_LEN);\n\t// 5.4.7 Under Load: Cookie Reply Message\n\t// Mix in the IP address and port - have the IP layer pass this in as byte array to avoid using Lwip specific APIs in this module\n\tif ((source_addr_port) && (source_length > 0)) {\n\t\twireguard_blake2s_update(&ctx, source_addr_port, source_length);\n\t}\n\twireguard_blake2s_final(&ctx, cookie);\n}\n\nstatic void wireguard_mac(uint8_t *dst, const void *message, size_t len, const uint8_t *key, size_t keylen) {\n\twireguard_blake2s(dst, WIREGUARD_COOKIE_LEN, key, keylen, message, len);\n}\n\nstatic void wireguard_mac_key(uint8_t *key, const uint8_t *public_key, const uint8_t *label, size_t label_len) {\n\tblake2s_ctx ctx;\n\tblake2s_init(&ctx, WIREGUARD_SESSION_KEY_LEN, NULL, 0);\n\tblake2s_update(&ctx, label, label_len);\n\tblake2s_update(&ctx, public_key, WIREGUARD_PUBLIC_KEY_LEN);\n\tblake2s_final(&ctx, key);\n}\n\nstatic void wireguard_mix_hash(uint8_t *hash, const uint8_t *src, size_t src_len) {\n\twireguard_blake2s_ctx ctx;\n\twireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);\n\twireguard_blake2s_update(&ctx, hash, WIREGUARD_HASH_LEN);\n\twireguard_blake2s_update(&ctx, src, src_len);\n\twireguard_blake2s_final(&ctx, hash);\n}\n\nstatic void wireguard_hmac(uint8_t *digest, const uint8_t *key, size_t key_len, const uint8_t *text, size_t text_len) {\n\t// Adapted from appendix example in RFC2104 to use BLAKE2S instead of MD5 - https://tools.ietf.org/html/rfc2104\n\twireguard_blake2s_ctx ctx;\n\tuint8_t k_ipad[WIREGUARD_BLAKE2S_BLOCK_SIZE]; // inner padding - key XORd with ipad\n\tuint8_t k_opad[WIREGUARD_BLAKE2S_BLOCK_SIZE]; // outer padding - key XORd with opad\n\n\tuint8_t tk[WIREGUARD_HASH_LEN];\n\tint i;\n\t// if key is longer than BLAKE2S_BLOCK_SIZE bytes reset it to key=BLAKE2S(key)\n\tif (key_len > WIREGUARD_BLAKE2S_BLOCK_SIZE) {\n\t\twireguard_blake2s_ctx tctx;\n\t\twireguard_blake2s_init(&tctx, WIREGUARD_HASH_LEN, NULL, 0);\n\t\twireguard_blake2s_update(&tctx, key, key_len);\n\t\twireguard_blake2s_final(&tctx, tk);\n\t\tkey = tk;\n\t\tkey_len = WIREGUARD_HASH_LEN;\n\t}\n\n\t// the HMAC transform looks like:\n\t// HASH(K XOR opad, HASH(K XOR ipad, text))\n\t// where K is an n byte key\n\t// ipad is the byte 0x36 repeated BLAKE2S_BLOCK_SIZE times\n\t// opad is the byte 0x5c repeated BLAKE2S_BLOCK_SIZE times\n\t// and text is the data being protected\n\tmemset(k_ipad, 0, sizeof(k_ipad));\n\tmemset(k_opad, 0, sizeof(k_opad));\n\tmemcpy(k_ipad, key, key_len);\n\tmemcpy(k_opad, key, key_len);\n\n\t// XOR key with ipad and opad values\n\tfor (i=0; i < WIREGUARD_BLAKE2S_BLOCK_SIZE; i++) {\n\t\tk_ipad[i] ^= 0x36;\n\t\tk_opad[i] ^= 0x5c;\n\t}\n\t// perform inner HASH\n\twireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0); // init context for 1st pass\n\twireguard_blake2s_update(&ctx, k_ipad, WIREGUARD_BLAKE2S_BLOCK_SIZE); // start with inner pad\n\twireguard_blake2s_update(&ctx, text, text_len); // then text of datagram\n\twireguard_blake2s_final(&ctx, digest); // finish up 1st pass\n\n\t// perform outer HASH\n\twireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0); // init context for 2nd pass\n\twireguard_blake2s_update(&ctx, k_opad, WIREGUARD_BLAKE2S_BLOCK_SIZE); // start with outer pad\n\twireguard_blake2s_update(&ctx, digest, WIREGUARD_HASH_LEN); // then results of 1st hash\n\twireguard_blake2s_final(&ctx, digest); // finish up 2nd pass\n}\n\nstatic void wireguard_kdf1(uint8_t *tau1, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {\n\tuint8_t tau0[WIREGUARD_HASH_LEN];\n\tuint8_t output[WIREGUARD_HASH_LEN + 1];\n\n\t// tau0 = Hmac(key, input)\n\twireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);\n\t// tau1 := Hmac(tau0, 0x1)\n\toutput[0] = 1;\n\twireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);\n\tmemcpy(tau1, output, WIREGUARD_HASH_LEN);\n\n\t// Wipe intermediates\n\tcrypto_zero(tau0, sizeof(tau0));\n\tcrypto_zero(output, sizeof(output));\n}\n\nstatic void wireguard_kdf2(uint8_t *tau1, uint8_t *tau2, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {\n\tuint8_t tau0[WIREGUARD_HASH_LEN];\n\tuint8_t output[WIREGUARD_HASH_LEN + 1];\n\n\t// tau0 = Hmac(key, input)\n\twireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);\n\t// tau1 := Hmac(tau0, 0x1)\n\toutput[0] = 1;\n\twireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);\n\tmemcpy(tau1, output, WIREGUARD_HASH_LEN);\n\n\t// tau2 := Hmac(tau0,tau1 || 0x2)\n\toutput[WIREGUARD_HASH_LEN] = 2;\n\twireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);\n\tmemcpy(tau2, output, WIREGUARD_HASH_LEN);\n\n\t// Wipe intermediates\n\tcrypto_zero(tau0, sizeof(tau0));\n\tcrypto_zero(output, sizeof(output));\n}\n\nstatic void wireguard_kdf3(uint8_t *tau1, uint8_t *tau2, uint8_t *tau3, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {\n\tuint8_t tau0[WIREGUARD_HASH_LEN];\n\tuint8_t output[WIREGUARD_HASH_LEN + 1];\n\n\t// tau0 = Hmac(key, input)\n\twireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);\n\t// tau1 := Hmac(tau0, 0x1)\n\toutput[0] = 1;\n\twireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);\n\tmemcpy(tau1, output, WIREGUARD_HASH_LEN);\n\n\t// tau2 := Hmac(tau0,tau1 || 0x2)\n\toutput[WIREGUARD_HASH_LEN] = 2;\n\twireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);\n\tmemcpy(tau2, output, WIREGUARD_HASH_LEN);\n\n\t// tau3 := Hmac(tau0,tau1,tau2 || 0x3)\n\toutput[WIREGUARD_HASH_LEN] = 3;\n\twireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);\n\tmemcpy(tau3, output, WIREGUARD_HASH_LEN);\n\n\t// Wipe intermediates\n\tcrypto_zero(tau0, sizeof(tau0));\n\tcrypto_zero(output, sizeof(output));\n}\n\nbool wireguard_check_replay(struct wireguard_keypair *keypair, uint64_t seq) {\n\t// Implementation of packet replay window - as per RFC2401\n\t// Adapted from code in Appendix C at https://tools.ietf.org/html/rfc2401\n\tuint32_t diff;\n\tbool result = false;\n\tsize_t ReplayWindowSize = sizeof(keypair->replay_bitmap) * CHAR_BIT; // 32 bits\n\n\t// WireGuard data packet counter starts from 0 but algorithm expects packet numbers to start from 1\n\tseq++;\n\n\tif (seq != 0) {\n\t\tif (seq > keypair->replay_counter) {\n\t\t\t// new larger sequence number\n\t\t\tdiff = seq - keypair->replay_counter;\n\t\t\tif (diff < ReplayWindowSize) {\n\t\t\t\t// In window\n\t\t\t\tkeypair->replay_bitmap <<= diff;\n\t\t\t\t// set bit for this packet\n\t\t\t\tkeypair->replay_bitmap |= 1;\n\t\t\t} else {\n\t\t\t\t// This packet has a \"way larger\"\n\t\t\t\tkeypair->replay_bitmap = 1;\n\t\t\t}\n\t\t\tkeypair->replay_counter = seq;\n\t\t\t// larger is good\n\t\t\tresult = true;\n\t\t} else {\n\t\t\tdiff = keypair->replay_counter - seq;\n\t\t\tif (diff < ReplayWindowSize) {\n\t\t\t\tif (keypair->replay_bitmap & ((uint32_t)1 << diff)) {\n\t\t\t\t\t// already seen\n\t\t\t\t} else {\n\t\t\t\t\t// mark as seen\n\t\t\t\t\tkeypair->replay_bitmap |= ((uint32_t)1 << diff);\n\t\t\t\t\t// out of order but good\n\t\t\t\t\tresult = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// too old or wrapped\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// first == 0 or wrapped\n\t}\n\treturn result;\n}\n\nstruct wireguard_keypair *get_peer_keypair_for_idx(struct wireguard_peer *peer, uint32_t idx) {\n\tif (peer->curr_keypair.valid && peer->curr_keypair.local_index == idx) {\n\t\treturn &peer->curr_keypair;\n\t} else if (peer->next_keypair.valid && peer->next_keypair.local_index == idx) {\n\t\treturn &peer->next_keypair;\n\t} else if (peer->prev_keypair.valid && peer->prev_keypair.local_index == idx) {\n\t\treturn &peer->prev_keypair;\n\t}\n\treturn NULL;\n}\n\nstatic uint32_t wireguard_generate_unique_index(struct wireguard_device *device) {\n\t// We need a random 32-bit number but make sure it's not already been used in the context of this device\n\tuint32_t result;\n\tuint8_t buf[4];\n\tint x;\n\tstruct wireguard_peer *peer;\n\tbool existing;\n\tdo {\n\t\tdo {\n\t\t\twireguard_random_bytes(buf, 4);\n\t\t\tresult = U8TO32_LITTLE(buf);\n\t\t} while ((result == 0) || (result == 0xFFFFFFFF)); // Don't allow 0 or 0xFFFFFFFF as valid values\n\n\t\texisting = false;\n\t\tfor (x=0; x < WIREGUARD_MAX_PEERS; x++) {\n\t\t\tpeer = &device->peers[x];\n\t\t\texisting = (result == peer->curr_keypair.local_index) ||\n\t\t\t\t\t(result == peer->prev_keypair.local_index) ||\n\t\t\t\t\t(result == peer->next_keypair.local_index) ||\n\t\t\t\t\t(result == peer->handshake.local_index);\n\n\t\t}\n\t} while (existing);\n\n\treturn result;\n}\n\nstatic void wireguard_clamp_private_key(uint8_t *key) {\n\tkey[0] &= 248;\n\tkey[31] = (key[31] & 127) | 64;\n}\n\nstatic void wireguard_generate_private_key(uint8_t *key) {\n\twireguard_random_bytes(key, WIREGUARD_PRIVATE_KEY_LEN);\n\twireguard_clamp_private_key(key);\n}\n\nstatic bool wireguard_generate_public_key(uint8_t *public_key, const uint8_t *private_key) {\n\tstatic const uint8_t basepoint[WIREGUARD_PUBLIC_KEY_LEN] = { 9 };\n\tbool result = false;\n\tif (memcmp(private_key, zero_key, WIREGUARD_PUBLIC_KEY_LEN) != 0) {\n\t\tresult = (wireguard_x25519(public_key, private_key, basepoint) == 0);\n\t}\n\treturn result;\n}\n\nbool wireguard_check_mac1(struct wireguard_device *device, const uint8_t *data, size_t len, const uint8_t *mac1) {\n\tbool result = false;\n\tuint8_t calculated[WIREGUARD_COOKIE_LEN];\n\twireguard_mac(calculated, data, len, device->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);\n\tif (crypto_equal(calculated, mac1, WIREGUARD_COOKIE_LEN)) {\n\t\tresult = true;\n\t}\n\treturn result;\n}\n\nbool wireguard_check_mac2(struct wireguard_device *device, const uint8_t *data, size_t len, uint8_t *source_addr_port, size_t source_length, const uint8_t *mac2) {\n\tbool result = false;\n\tuint8_t cookie[WIREGUARD_COOKIE_LEN];\n\tuint8_t calculated[WIREGUARD_COOKIE_LEN];\n\n\tgenerate_peer_cookie(device, cookie, source_addr_port, source_length);\n\n\twireguard_mac(calculated, data, len, cookie, WIREGUARD_COOKIE_LEN);\n\tif (crypto_equal(calculated, mac2, WIREGUARD_COOKIE_LEN)) {\n\t\tresult = true;\n\t}\n\treturn result;\n}\n\nvoid keypair_destroy(struct wireguard_keypair *keypair) {\n\tcrypto_zero(keypair, sizeof(struct wireguard_keypair));\n\tkeypair->valid = false;\n}\n\nvoid keypair_update(struct wireguard_peer *peer, struct wireguard_keypair *received_keypair) {\n\tbool key_is_next = (received_keypair == &peer->next_keypair);\n\tif (key_is_next) {\n\t\tpeer->prev_keypair = peer->curr_keypair;\n\t\tpeer->curr_keypair = peer->next_keypair;\n\t\tkeypair_destroy(&peer->next_keypair);\n\t}\n}\n\nstatic void add_new_keypair(struct wireguard_peer *peer, struct wireguard_keypair new_keypair) {\n\tif (new_keypair.initiator) {\n\t\tif (peer->next_keypair.valid) {\n\t\t\tpeer->prev_keypair = peer->next_keypair;\n\t\t\tkeypair_destroy(&peer->next_keypair);\n\t\t} else  {\n\t\t\tpeer->prev_keypair = peer->curr_keypair;\n\t\t}\n\t\tpeer->curr_keypair = new_keypair;\n\t} else {\n\t\tpeer->next_keypair =  new_keypair;\n\t\tkeypair_destroy(&peer->prev_keypair);\n\t}\n}\n\nvoid wireguard_start_session(struct wireguard_peer *peer, bool initiator) {\n\tstruct wireguard_handshake *handshake = &peer->handshake;\n\tstruct wireguard_keypair new_keypair;\n\n\tcrypto_zero(&new_keypair, sizeof(struct wireguard_keypair));\n\tnew_keypair.initiator = initiator;\n\tnew_keypair.local_index = handshake->local_index;\n\tnew_keypair.remote_index = handshake->remote_index;\n\n\tnew_keypair.keypair_millis = wireguard_sys_now();\n\tnew_keypair.sending_valid = true;\n\tnew_keypair.receiving_valid = true;\n\n\t// 5.4.5 Transport Data Key Derivation\n\t// (Tsendi = Trecvr, Trecvi = Tsendr) := Kdf2(Ci = Cr,E)\n\tif (new_keypair.initiator) {\n\t\twireguard_kdf2(new_keypair.sending_key, new_keypair.receiving_key, handshake->chaining_key, NULL, 0);\n\t} else {\n\t\twireguard_kdf2(new_keypair.receiving_key, new_keypair.sending_key, handshake->chaining_key, NULL, 0);\n\t}\n\n\tnew_keypair.replay_bitmap = 0;\n\tnew_keypair.replay_counter = 0;\n\n\tnew_keypair.last_tx = 0;\n\tnew_keypair.last_rx = 0; // No packets received yet\n\n\tnew_keypair.valid = true;\n\n\t// Eprivi = Epubi = Eprivr = Epubr = Ci = Cr := E\n\tcrypto_zero(handshake->ephemeral_private, WIREGUARD_PUBLIC_KEY_LEN);\n\tcrypto_zero(handshake->remote_ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\tcrypto_zero(handshake->hash, WIREGUARD_HASH_LEN);\n\tcrypto_zero(handshake->chaining_key, WIREGUARD_HASH_LEN);\n\thandshake->remote_index = 0;\n\thandshake->local_index = 0;\n\thandshake->valid = false;\n\n\tadd_new_keypair(peer, new_keypair);\n}\n\nuint8_t wireguard_get_message_type(const uint8_t *data, size_t len) {\n\tuint8_t result = MESSAGE_INVALID;\n\tif (len >= 4) {\n\t\tif ((data[1] == 0) && (data[2] == 0) && (data[3] == 0)) {\n\t\t\tswitch (data[0]) {\n\t\t\t\tcase MESSAGE_HANDSHAKE_INITIATION:\n\t\t\t\t\tif (len == sizeof(struct message_handshake_initiation)) {\n\t\t\t\t\t\tresult = MESSAGE_HANDSHAKE_INITIATION;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase MESSAGE_HANDSHAKE_RESPONSE:\n\t\t\t\t\tif (len == sizeof(struct message_handshake_response)) {\n\t\t\t\t\t\tresult = MESSAGE_HANDSHAKE_RESPONSE;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase MESSAGE_COOKIE_REPLY:\n\t\t\t\t\tif (len == sizeof(struct message_cookie_reply)) {\n\t\t\t\t\t\tresult = MESSAGE_COOKIE_REPLY;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase MESSAGE_TRANSPORT_DATA:\n\t\t\t\t\tif (len >= sizeof(struct message_transport_data) + WIREGUARD_AUTHTAG_LEN) {\n\t\t\t\t\t\tresult = MESSAGE_TRANSPORT_DATA;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nstruct wireguard_peer *wireguard_process_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg) {\n\tstruct wireguard_peer *ret_peer = NULL;\n\tstruct wireguard_peer *peer = NULL;\n\tstruct wireguard_handshake *handshake;\n\tuint8_t key[WIREGUARD_SESSION_KEY_LEN];\n\tuint8_t chaining_key[WIREGUARD_HASH_LEN];\n\tuint8_t hash[WIREGUARD_HASH_LEN];\n\tuint8_t s[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t e[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t t[WIREGUARD_TAI64N_LEN];\n\tuint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint32_t now;\n\tbool rate_limit;\n\tbool replay;\n\n\t// We are the responder, other end is the initiator\n\n\t// Ci := Hash(Construction) (precalculated hash)\n\tmemcpy(chaining_key, construction_hash, WIREGUARD_HASH_LEN);\n\n\t// Hi := Hash(Ci || Identifier\n\tmemcpy(hash, identifier_hash, WIREGUARD_HASH_LEN);\n\n\t// Hi := Hash(Hi || Spubr)\n\twireguard_mix_hash(hash, device->public_key, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t // Ci := Kdf1(Ci, Epubi)\n\twireguard_kdf1(chaining_key, chaining_key, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t// msg.ephemeral := Epubi\n\tmemcpy(e, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t// Hi := Hash(Hi || msg.ephemeral)\n\twireguard_mix_hash(hash, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t// Calculate DH(Eprivi,Spubr)\n\twireguard_x25519(dh_calculation, device->private_key, e);\n\tif (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {\n\n\t\t// (Ci,k) := Kdf2(Ci,DH(Eprivi,Spubr))\n\t\twireguard_kdf2(chaining_key, key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t// msg.static := AEAD(k, 0, Spubi, Hi)\n\t\tif (wireguard_aead_decrypt(s, msg->enc_static, sizeof(msg->enc_static), hash, WIREGUARD_HASH_LEN, 0, key)) {\n\t\t\t// Hi := Hash(Hi || msg.static)\n\t\t\twireguard_mix_hash(hash, msg->enc_static, sizeof(msg->enc_static));\n\n\t\t\tpeer = peer_lookup_by_pubkey(device, s);\n\t\t\tif (peer) {\n\t\t\t\thandshake = &peer->handshake;\n\n\t\t\t\t// (Ci,k) := Kdf2(Ci,DH(Sprivi,Spubr))\n\t\t\t\twireguard_kdf2(chaining_key, key, chaining_key, peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t\t// msg.timestamp := AEAD(k, 0, Timestamp(), Hi)\n\t\t\t\tif (wireguard_aead_decrypt(t, msg->enc_timestamp, sizeof(msg->enc_timestamp), hash, WIREGUARD_HASH_LEN, 0, key)) {\n\t\t\t\t\t// Hi := Hash(Hi || msg.timestamp)\n\t\t\t\t\twireguard_mix_hash(hash, msg->enc_timestamp, sizeof(msg->enc_timestamp));\n\n\t\t\t\t\tnow = wireguard_sys_now();\n\n\t\t\t\t\t// Check that timestamp is increasing and we haven't had too many initiations (should only get one per peer every 5 seconds max?)\n\t\t\t\t\treplay = (memcmp(t, peer->greatest_timestamp, WIREGUARD_TAI64N_LEN) <= 0); // tai64n is big endian so we can use memcmp to compare\n\t\t\t\t\trate_limit = (peer->last_initiation_rx - now) < (1000 / MAX_INITIATIONS_PER_SECOND);\n\n\t\t\t\t\tif (!replay && !rate_limit) {\n\t\t\t\t\t\t// Success! Copy everything to peer\n\t\t\t\t\t\tpeer->last_initiation_rx = now;\n\t\t\t\t\t\tif (memcmp(t, peer->greatest_timestamp, WIREGUARD_TAI64N_LEN) > 0) {\n\t\t\t\t\t\t\tmemcpy(peer->greatest_timestamp, t, WIREGUARD_TAI64N_LEN);\n\t\t\t\t\t\t\t// TODO: Need to notify if the higher layers want to persist latest timestamp/nonce somewhere\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmemcpy(handshake->remote_ephemeral, e, WIREGUARD_PUBLIC_KEY_LEN);\n\t\t\t\t\t\tmemcpy(handshake->hash, hash, WIREGUARD_HASH_LEN);\n\t\t\t\t\t\tmemcpy(handshake->chaining_key, chaining_key, WIREGUARD_HASH_LEN);\n\t\t\t\t\t\thandshake->remote_index = msg->sender;\n\t\t\t\t\t\thandshake->valid = true;\n\t\t\t\t\t\thandshake->initiator = false;\n\t\t\t\t\t\tret_peer = peer;\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Ignore\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tESP_LOGE(TAG, \"wireguard_process_initiation_message: failed to decrypt\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tESP_LOGE(TAG, \"peer not found\");\n\t\t\t}\n\t\t} else {\n\t\t\tESP_LOGE(TAG, \"Failed to decrypt\");\n\t\t}\n\t} else {\n\t\tESP_LOGE(TAG, \"Bad X25519\");\n\t}\n\n\tcrypto_zero(key, sizeof(key));\n\tcrypto_zero(hash, sizeof(hash));\n\tcrypto_zero(chaining_key, sizeof(chaining_key));\n\tcrypto_zero(dh_calculation, sizeof(dh_calculation));\n\n\treturn ret_peer;\n}\n\nbool wireguard_process_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *src) {\n\tstruct wireguard_handshake *handshake = &peer->handshake;\n\n\tbool result = false;\n\tuint8_t key[WIREGUARD_SESSION_KEY_LEN];\n\tuint8_t hash[WIREGUARD_HASH_LEN];\n\tuint8_t chaining_key[WIREGUARD_HASH_LEN];\n\tuint8_t e[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t ephemeral_private[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t static_private[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t preshared_key[WIREGUARD_SESSION_KEY_LEN];\n\tuint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t tau[WIREGUARD_PUBLIC_KEY_LEN];\n\n\tif (handshake->valid && handshake->initiator) {\n\n\t\tmemcpy(hash, handshake->hash, WIREGUARD_HASH_LEN);\n\t\tmemcpy(chaining_key, handshake->chaining_key, WIREGUARD_HASH_LEN);\n\t\tmemcpy(ephemeral_private, handshake->ephemeral_private, WIREGUARD_PUBLIC_KEY_LEN);\n\t\tmemcpy(preshared_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);\n\n\t\t// (Eprivr, Epubr) := DH-Generate()\n\t\t// Not required\n\n\t\t// Cr := Kdf1(Cr,Epubr)\n\t\twireguard_kdf1(chaining_key, chaining_key, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t// msg.ephemeral := Epubr\n\t\tmemcpy(e, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t// Hr := Hash(Hr || msg.ephemeral)\n\t\twireguard_mix_hash(hash, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t// Cr := Kdf1(Cr, DH(Eprivr, Epubi))\n\t\t// Calculate DH(Eprivr, Epubi)\n\t\twireguard_x25519(dh_calculation, ephemeral_private, e);\n\t\tif (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {\n\t\t\twireguard_kdf1(chaining_key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t// Cr := Kdf1(Cr, DH(Eprivr, Spubi))\n\t\t\t// CalculateDH(Eprivr, Spubi)\n\t\t\twireguard_x25519(dh_calculation, device->private_key, e);\n\t\t\tif (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {\n\t\t\t\twireguard_kdf1(chaining_key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t\t// (Cr, t, k) := Kdf3(Cr, Q)\n\t\t\t\twireguard_kdf3(chaining_key, tau, key, chaining_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);\n\n\t\t\t\t// Hr := Hash(Hr | t)\n\t\t\t\twireguard_mix_hash(hash, tau, WIREGUARD_HASH_LEN);\n\n\t\t\t\t// msg.empty := AEAD(k, 0, E, Hr)\n\t\t\t\tif (wireguard_aead_decrypt(NULL, src->enc_empty, sizeof(src->enc_empty), hash, WIREGUARD_HASH_LEN, 0, key)) {\n\t\t\t\t\t// Hr := Hash(Hr | msg.empty)\n\t\t\t\t\t// Not required as discarded\n\n\t\t\t\t\t//Copy details to handshake\n\t\t\t\t\tmemcpy(handshake->remote_ephemeral, e, WIREGUARD_HASH_LEN);\n\t\t\t\t\tmemcpy(handshake->hash, hash, WIREGUARD_HASH_LEN);\n\t\t\t\t\tmemcpy(handshake->chaining_key, chaining_key, WIREGUARD_HASH_LEN);\n\t\t\t\t\thandshake->remote_index = src->sender;\n\n\t\t\t\t\tresult = true;\n\t\t\t\t} else {\n\t\t\t\t\tESP_LOGE(TAG, \"Decrypt failed\");\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tESP_LOGE(TAG, \"X25519 fail\");\n\t\t\t}\n\n\t\t} else {\n\t\t\tESP_LOGE(TAG, \"X25519 fail 2\");\n\t\t}\n\n\t}\n\tcrypto_zero(key, sizeof(key));\n\tcrypto_zero(hash, sizeof(hash));\n\tcrypto_zero(chaining_key, sizeof(chaining_key));\n\tcrypto_zero(ephemeral_private, sizeof(ephemeral_private));\n\tcrypto_zero(static_private, sizeof(static_private));\n\tcrypto_zero(preshared_key, sizeof(preshared_key));\n\tcrypto_zero(tau, sizeof(tau));\n\n\treturn result;\n}\n\nbool wireguard_process_cookie_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_cookie_reply *src) {\n\tuint8_t cookie[WIREGUARD_COOKIE_LEN];\n\tbool result = false;\n\n\tif (peer->handshake_mac1_valid) {\n\n\t\tresult = wireguard_xaead_decrypt(cookie, src->enc_cookie, sizeof(src->enc_cookie), peer->handshake_mac1, WIREGUARD_COOKIE_LEN, src->nonce, peer->label_cookie_key);\n\n\t\tif (result) {\n\t\t\t// 5.4.7 Under Load: Cookie Reply Message\n\t\t\t// Upon receiving this message, if it is valid, the only thing the recipient of this message should do is store the cookie along with the time at which it was received\n\t\t\tmemcpy(peer->cookie, cookie, WIREGUARD_COOKIE_LEN);\n\t\t\tpeer->cookie_millis = wireguard_sys_now();\n\t\t\tpeer->handshake_mac1_valid = false;\n\t\t}\n\t} else {\n\t\t// We didn't send any initiation packet so we shouldn't be getting a cookie reply!\n\t}\n\treturn result;\n}\n\nbool wireguard_create_handshake_initiation(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_initiation *dst) {\n\tuint8_t timestamp[WIREGUARD_TAI64N_LEN];\n\tuint8_t key[WIREGUARD_SESSION_KEY_LEN];\n\tuint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];\n\tbool result = false;\n\n\tstruct wireguard_handshake *handshake = &peer->handshake;\n\n\tmemset(dst, 0, sizeof(struct message_handshake_initiation));\n\n\t// Ci := Hash(Construction) (precalculated hash)\n\tmemcpy(handshake->chaining_key, construction_hash, WIREGUARD_HASH_LEN);\n\n\t// Hi := Hash(Ci || Identifier)\n\tmemcpy(handshake->hash, identifier_hash, WIREGUARD_HASH_LEN);\n\n\t// Hi := Hash(Hi || Spubr)\n\twireguard_mix_hash(handshake->hash, peer->public_key, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t// (Eprivi, Epubi) := DH-Generate()\n\twireguard_generate_private_key(handshake->ephemeral_private);\n\tif (wireguard_generate_public_key(dst->ephemeral, handshake->ephemeral_private)) {\n\n\t\t// Ci := Kdf1(Ci, Epubi)\n\t\twireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t// msg.ephemeral := Epubi\n\t\t// Done above - public keys is calculated into dst->ephemeral\n\n\t\t// Hi := Hash(Hi || msg.ephemeral)\n\t\twireguard_mix_hash(handshake->hash, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t// Calculate DH(Eprivi,Spubr)\n\t\twireguard_x25519(dh_calculation, handshake->ephemeral_private, peer->public_key);\n\t\tif (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {\n\n\t\t\t// (Ci,k) := Kdf2(Ci,DH(Eprivi,Spubr))\n\t\t\twireguard_kdf2(handshake->chaining_key, key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t// msg.static := AEAD(k,0,Spubi, Hi)\n\t\t\twireguard_aead_encrypt(dst->enc_static, device->public_key, WIREGUARD_PUBLIC_KEY_LEN, handshake->hash, WIREGUARD_HASH_LEN, 0, key);\n\n\t\t\t// Hi := Hash(Hi || msg.static)\n\t\t\twireguard_mix_hash(handshake->hash, dst->enc_static, sizeof(dst->enc_static));\n\n\t\t\t// (Ci,k) := Kdf2(Ci,DH(Sprivi,Spubr))\n\t\t\t// note DH(Sprivi,Spubr) is precomputed per peer\n\t\t\twireguard_kdf2(handshake->chaining_key, key, handshake->chaining_key, peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t// msg.timestamp := AEAD(k, 0, Timestamp(), Hi)\n\t\t\twireguard_tai64n_now(timestamp);\n\t\t\twireguard_aead_encrypt(dst->enc_timestamp, timestamp, WIREGUARD_TAI64N_LEN, handshake->hash, WIREGUARD_HASH_LEN, 0, key);\n\n\t\t\t// Hi := Hash(Hi || msg.timestamp)\n\t\t\twireguard_mix_hash(handshake->hash, dst->enc_timestamp, sizeof(dst->enc_timestamp));\n\n\t\t\tdst->type = MESSAGE_HANDSHAKE_INITIATION;\n\t\t\tdst->sender = wireguard_generate_unique_index(device);\n\n\t\t\thandshake->valid = true;\n\t\t\thandshake->initiator = true;\n\t\t\thandshake->local_index = dst->sender;\n\n\t\t\tresult = true;\n\t\t}\n\t}\n\n\tif (result) {\n\t\t// 5.4.4 Cookie MACs\n\t\t// msg.mac1 := Mac(Hash(Label-Mac1 || Spubm' ), msgA)\n\t\t// The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed\n\t\twireguard_mac(dst->mac1, dst, (sizeof(struct message_handshake_initiation)-(2*WIREGUARD_COOKIE_LEN)), peer->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);\n\n\t\t// if Lm = E or Lm ≥ 120:\n\t\tif ((peer->cookie_millis == 0) || wireguard_expired(peer->cookie_millis, COOKIE_SECRET_MAX_AGE)) {\n\t\t\t// msg.mac2 := 0\n\t\t\tcrypto_zero(dst->mac2, WIREGUARD_COOKIE_LEN);\n\t\t} else {\n\t\t\t// msg.mac2 := Mac(Lm, msgB)\n\t\t\twireguard_mac(dst->mac2, dst, (sizeof(struct message_handshake_initiation)-(WIREGUARD_COOKIE_LEN)), peer->cookie, WIREGUARD_COOKIE_LEN);\n\n\t\t}\n\t}\n\n\tcrypto_zero(key, sizeof(key));\n\tcrypto_zero(dh_calculation, sizeof(dh_calculation));\n\treturn result;\n}\n\nbool wireguard_create_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *dst) {\n\tstruct wireguard_handshake *handshake = &peer->handshake;\n\tuint8_t key[WIREGUARD_SESSION_KEY_LEN];\n\tuint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t tau[WIREGUARD_HASH_LEN];\n\tbool result = false;\n\n\tmemset(dst, 0, sizeof(struct message_handshake_response));\n\n\tif (handshake->valid && !handshake->initiator) {\n\n\t\t// (Eprivr, Epubr) := DH-Generate()\n\t\twireguard_generate_private_key(handshake->ephemeral_private);\n\t\tif (wireguard_generate_public_key(dst->ephemeral, handshake->ephemeral_private)) {\n\n\t\t\t// Cr := Kdf1(Cr,Epubr)\n\t\t\twireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t// msg.ephemeral := Epubr\n\t\t\t// Copied above when generated\n\n\t\t\t// Hr := Hash(Hr || msg.ephemeral)\n\t\t\twireguard_mix_hash(handshake->hash, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t// Cr := Kdf1(Cr, DH(Eprivr, Epubi))\n\t\t\t// Calculate DH(Eprivi,Spubr)\n\t\t\twireguard_x25519(dh_calculation, handshake->ephemeral_private, handshake->remote_ephemeral);\n\t\t\tif (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {\n\t\t\t\twireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t\t// Cr := Kdf1(Cr, DH(Eprivr, Spubi))\n\t\t\t\t// Calculate DH(Eprivi,Spubr)\n\t\t\t\twireguard_x25519(dh_calculation, handshake->ephemeral_private, peer->public_key);\n\t\t\t\tif (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {\n\t\t\t\t\twireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);\n\n\t\t\t\t\t// (Cr, t, k) := Kdf3(Cr, Q)\n\t\t\t\t\twireguard_kdf3(handshake->chaining_key, tau, key, handshake->chaining_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);\n\n\t\t\t\t\t// Hr := Hash(Hr | t)\n\t\t\t\t\twireguard_mix_hash(handshake->hash, tau, WIREGUARD_HASH_LEN);\n\n\t\t\t\t\t// msg.empty := AEAD(k, 0, E, Hr)\n\t\t\t\t\twireguard_aead_encrypt(dst->enc_empty, NULL, 0, handshake->hash, WIREGUARD_HASH_LEN, 0, key);\n\n\t\t\t\t\t// Hr := Hash(Hr | msg.empty)\n\t\t\t\t\twireguard_mix_hash(handshake->hash, dst->enc_empty, sizeof(dst->enc_empty));\n\n\t\t\t\t\tdst->type = MESSAGE_HANDSHAKE_RESPONSE;\n\t\t\t\t\tdst->receiver = handshake->remote_index;\n\t\t\t\t\tdst->sender = wireguard_generate_unique_index(device);\n\t\t\t\t\t// Update handshake object too\n\t\t\t\t\thandshake->local_index = dst->sender;\n\n\t\t\t\t\tresult = true;\n\t\t\t\t} else {\n\t\t\t\t\t// Bad x25519\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Bad x25519\n\t\t\t}\n\n\t\t} else {\n\t\t\t// Failed to generate DH\n\t\t}\n\t}\n\n\tif (result) {\n\t\t// 5.4.4 Cookie MACs\n\t\t// msg.mac1 := Mac(Hash(Label-Mac1 || Spubm' ), msgA)\n\t\t// The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed\n\t\twireguard_mac(dst->mac1, dst, (sizeof(struct message_handshake_response)-(2*WIREGUARD_COOKIE_LEN)), peer->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);\n\n\t\t// if Lm = E or Lm ≥ 120:\n\t\tif ((peer->cookie_millis == 0) || wireguard_expired(peer->cookie_millis, COOKIE_SECRET_MAX_AGE)) {\n\t\t\t// msg.mac2 := 0\n\t\t\tcrypto_zero(dst->mac2, WIREGUARD_COOKIE_LEN);\n\t\t} else {\n\t\t\t// msg.mac2 := Mac(Lm, msgB)\n\t\t\twireguard_mac(dst->mac2, dst, (sizeof(struct message_handshake_response)-(WIREGUARD_COOKIE_LEN)), peer->cookie, WIREGUARD_COOKIE_LEN);\n\t\t}\n\t}\n\n\tcrypto_zero(key, sizeof(key));\n\tcrypto_zero(dh_calculation, sizeof(dh_calculation));\n\tcrypto_zero(tau, sizeof(tau));\n\treturn result;\n}\n\nvoid wireguard_create_cookie_reply(struct wireguard_device *device, struct message_cookie_reply *dst, const uint8_t *mac1, uint32_t index, uint8_t *source_addr_port, size_t source_length) {\n\tuint8_t cookie[WIREGUARD_COOKIE_LEN];\n\tcrypto_zero(dst, sizeof(struct message_cookie_reply));\n\tdst->type = MESSAGE_COOKIE_REPLY;\n\tdst->receiver = index;\n\twireguard_random_bytes(dst->nonce, COOKIE_NONCE_LEN);\n\tgenerate_peer_cookie(device, cookie, source_addr_port, source_length);\n\twireguard_xaead_encrypt(dst->enc_cookie, cookie, WIREGUARD_COOKIE_LEN, mac1, WIREGUARD_COOKIE_LEN, dst->nonce, device->label_cookie_key);\n}\n\nbool wireguard_peer_init(struct wireguard_device *device, struct wireguard_peer *peer, const uint8_t *public_key, const uint8_t *preshared_key) {\n\t// Clear out structure\n\tmemset(peer, 0, sizeof(struct wireguard_peer));\n\n\tif (device->valid) {\n\t\t// Copy across the public key into our peer structure\n\t\tmemcpy(peer->public_key, public_key, WIREGUARD_PUBLIC_KEY_LEN);\n\t\tif (preshared_key) {\n\t\t\tmemcpy(peer->preshared_key, preshared_key, WIREGUARD_SESSION_KEY_LEN);\n\t\t} else {\n\t\t\tcrypto_zero(peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);\n\t\t}\n\n\t\tif (wireguard_x25519(peer->public_key_dh, device->private_key, peer->public_key) == 0) {\n\t\t\t// Zero out handshake\n\t\t\tmemset(&peer->handshake, 0, sizeof(struct wireguard_handshake));\n\t\t\tpeer->handshake.valid = false;\n\n\t\t\t// Zero out any cookie info - we haven't received one yet\n\t\t\tpeer->cookie_millis = 0;\n\t\t\tmemset(&peer->cookie, 0, WIREGUARD_COOKIE_LEN);\n\n\t\t\t// Precompute keys to deal with mac1/2 calculation\n\t\t\twireguard_mac_key(peer->label_mac1_key, peer->public_key, LABEL_MAC1, sizeof(LABEL_MAC1));\n\t\t\twireguard_mac_key(peer->label_cookie_key, peer->public_key, LABEL_COOKIE, sizeof(LABEL_COOKIE));\n\n\t\t\tpeer->valid = true;\n\t\t} else {\n\t\t\tcrypto_zero(peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);\n\t\t}\n\t}\n\treturn peer->valid;\n}\n\nbool wireguard_device_init(struct wireguard_device *device, const uint8_t *private_key) {\n\t// Set the private key and calculate public key from it\n\tmemcpy(device->private_key, private_key, WIREGUARD_PRIVATE_KEY_LEN);\n\t// Ensure private key is correctly \"clamped\"\n\twireguard_clamp_private_key(device->private_key);\n\tdevice->valid = wireguard_generate_public_key(device->public_key, private_key);\n\tif (device->valid) {\n\t\tgenerate_cookie_secret(device);\n\t\t// 5.4.4 Cookie MACs - The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed.\n\t\twireguard_mac_key(device->label_mac1_key, device->public_key, LABEL_MAC1, sizeof(LABEL_MAC1));\n\t\t// 5.4.7 Under Load: Cookie Reply Message - The value Hash(Label-Cookie || Spubm) above can be pre-computed.\n\t\twireguard_mac_key(device->label_cookie_key, device->public_key, LABEL_COOKIE, sizeof(LABEL_COOKIE));\n\n\t} else {\n\t\tcrypto_zero(device->private_key, WIREGUARD_PRIVATE_KEY_LEN);\n\t}\n\treturn device->valid;\n}\n\nvoid wireguard_encrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, struct wireguard_keypair *keypair) {\n\twireguard_aead_encrypt(dst, src, src_len, NULL, 0, keypair->sending_counter, keypair->sending_key);\n\tkeypair->sending_counter++;\n}\n\nbool wireguard_decrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, uint64_t counter, struct wireguard_keypair *keypair) {\n\treturn wireguard_aead_decrypt(dst, src, src_len, NULL, 0, counter, keypair->receiving_key);\n}\n\nbool wireguard_base64_decode(const char *str, uint8_t *out, size_t *outlen) {\n\tuint32_t accum = 0; // We accumulate upto four blocks of 6 bits into this to form 3 bytes output\n\tuint8_t char_count = 0; // How many characters have we processed in this block\n\tint byte_count = 3; // How many bytes are we expecting in current 4 char block\n\tint len = 0; // result length in bytes\n\tbool result = true;\n\tuint8_t bits;\n\tchar c;\n\tchar *ptr;\n\tint x;\n\tsize_t inlen;\n\n\tif (!str) {\n\t\treturn false;\n\t}\n\n\tinlen = strlen(str);\n\n\tfor (x = 0; x < inlen; x++) {\n\t\tc = str[x];\n\t\tif (c == '=') {\n\t\t\t// This is '=' padding at end of string - decrease the number of bytes to write\n\t\t\tbits = 0;\n\t\t\tbyte_count--;\n\t\t\tif (byte_count < 0) {\n\t\t\t\t// Too much padding!\n\t\t\t\tresult = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\tif (byte_count != 3) {\n\t\t\t\t// Padding only allowed at end - this is a valid byte and we have already seen padding\n\t\t\t\tresult = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tptr = strchr(base64_lookup, c);\n\t\t\tif (ptr) {\n\t\t\t\tbits = (uint8_t)((ptr - base64_lookup) & 0x3F);\n\t\t\t} else {\n\t\t\t\t// invalid character in input string\n\t\t\t\tresult = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\taccum = (accum << 6) | bits;\n\t\tchar_count++;\n\n\t\tif (char_count == 4) {\n\t\t\tif (len + byte_count > *outlen) {\n\t\t\t\t// Output buffer overflow\n\t\t\t\tresult = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tout[len++] = (uint8_t)((accum >> 16) & 0xFF);\n\t\t\tif (byte_count > 1) {\n\t\t\t\tout[len++] = (uint8_t)((accum >> 8) & 0xFF);\n\t\t\t}\n\t\t\tif (byte_count > 2) {\n\t\t\t\tout[len++] = (uint8_t)(accum & 0xFF);\n\t\t\t}\n\t\t\tchar_count = 0;\n\t\t\taccum = 0;\n\t\t}\n\t}\n\tif (char_count != 0) {\n\t\t// We require padding to multiple of 3 input length - bytes are missing from output!\n\t\tresult = false;\n\t}\n\t*outlen = len;\n\treturn result;\n}\n\nbool wireguard_base64_encode(const uint8_t *in, size_t inlen, char *out, size_t *outlen) {\n\tbool result = false;\n\tint read_offset = 0;\n\tint write_offset = 0;\n\tuint8_t byte1, byte2, byte3;\n\tuint32_t tmp;\n\tchar c;\n\tsize_t len = 4 * ((inlen + 2) / 3);\n\tint padding = (3 - (inlen % 3));\n\tif (padding > 2) padding = 0;\n\tif (*outlen > len) {\n\n\t\twhile (read_offset < inlen) {\n\t\t\t// Read three bytes\n\t\t\tbyte1 = (read_offset < inlen) ? in[read_offset++] : 0;\n\t\t\tbyte2 = (read_offset < inlen) ? in[read_offset++] : 0;\n\t\t\tbyte3 = (read_offset < inlen) ? in[read_offset++] : 0;\n\t\t\t// Turn into 24 bit intermediate\n\t\t\ttmp = (byte1 << 16) | (byte2 << 8) | (byte3);\n\t\t\t// Write out 4 characters each representing 6 bits of input\n\t\t\tout[write_offset++] = base64_lookup[(tmp >> 18) & 0x3F];\n\t\t\tout[write_offset++] = base64_lookup[(tmp >> 12) & 0x3F];\n\t\t\tc = (write_offset < len - padding) ? base64_lookup[(tmp >> 6) & 0x3F] : '=';\n\t\t\tout[write_offset++] = c;\n\t\t\tc = (write_offset < len - padding) ? base64_lookup[(tmp) & 0x3F] : '=';\n\t\t\tout[write_offset++] = c;\n\t\t}\n\t\tout[len] = '\\0';\n\t\t*outlen = len;\n\t\tresult = true;\n\t} else {\n\t\t// Not enough data to put in base64 and null terminate\n\t}\n\treturn result;\n}\n"
  },
  {
    "path": "src/wireguard.h",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n#ifndef _WIREGUARD_H_\n#define _WIREGUARD_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdbool.h>\n\n// Note: these are only required for definitions in device/peer for netif, udp_pcb, ip_addr_t and u16_t\n#include \"lwip/netif.h\"\n#include \"lwip/udp.h\"\n#include \"lwip/ip_addr.h\"\n#include \"lwip/arch.h\"\n\n// Platform-specific functions that need to be implemented per-platform\n#include \"wireguard-platform.h\"\n\n// tai64n contains 64-bit seconds and 32-bit nano offset (12 bytes)\n#define WIREGUARD_TAI64N_LEN\t\t(12)\n// Auth algorithm is chacha20pol1305 which is 128bit (16 byte) authenticator\n#define WIREGUARD_AUTHTAG_LEN\t\t(16)\n// Hash algorithm is blake2s which makes 32 byte hashes\n#define WIREGUARD_HASH_LEN\t\t\t(32)\n// Public key algo is curve22519 which uses 32 byte keys\n#define WIREGUARD_PUBLIC_KEY_LEN\t(32)\n// Public key algo is curve22519 which uses 32 byte keys\n#define WIREGUARD_PRIVATE_KEY_LEN\t(32)\n// Symmetric session keys are chacha20/poly1305 which uses 32 byte keys\n#define WIREGUARD_SESSION_KEY_LEN\t(32)\n\n// Timers / Limits\n#define WIREGUARD_COOKIE_LEN\t\t(16)\n#define COOKIE_SECRET_MAX_AGE\t\t(2 * 60)\n#define COOKIE_NONCE_LEN\t\t\t(24)\n\n#define REKEY_AFTER_MESSAGES\t\t(1ULL << 60)\n#define REJECT_AFTER_MESSAGES\t\t(0xFFFFFFFFFFFFFFFFULL - (1ULL << 13))\n#define REKEY_AFTER_TIME\t\t\t(120)\n#define REJECT_AFTER_TIME\t\t\t(180)\n#define REKEY_TIMEOUT\t\t\t\t(5)\n#define KEEPALIVE_TIMEOUT\t\t\t(10)\n\nstruct wireguard_keypair {\n\tbool valid;\n\tbool initiator; // Did we initiate this session (send the initiation packet rather than sending the response packet)\n\tuint32_t keypair_millis;\n\n\tuint8_t sending_key[WIREGUARD_SESSION_KEY_LEN];\n\tbool sending_valid;\n\tuint64_t sending_counter;\n\n\tuint8_t receiving_key[WIREGUARD_SESSION_KEY_LEN];\n\tbool receiving_valid;\n\n\tuint32_t last_tx;\n\tuint32_t last_rx;\n\n\tuint32_t replay_bitmap;\n\tuint64_t replay_counter;\n\n\tuint32_t local_index; // This is the index we generated for our end\n\tuint32_t remote_index; // This is the index on the other end\n};\n\nstruct wireguard_handshake {\n\tbool valid;\n\tbool initiator;\n\tuint32_t local_index;\n\tuint32_t remote_index;\n\tuint8_t ephemeral_private[WIREGUARD_PRIVATE_KEY_LEN];\n\tuint8_t remote_ephemeral[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t hash[WIREGUARD_HASH_LEN];\n\tuint8_t chaining_key[WIREGUARD_HASH_LEN];\n};\n\nstruct wireguard_allowed_ip {\n\tbool valid;\n\tip_addr_t ip;\n\tip_addr_t mask;\n};\n\nstruct wireguard_peer {\n\tbool valid; // Is this peer initialised?\n\tbool active; // Should we be actively trying to connect?\n\n\t// This is the configured IP of the peer (endpoint)\n\tip_addr_t connect_ip;\n\tu16_t connect_port;\n\t// This is the latest received IP/port\n\tip_addr_t ip;\n\tu16_t port;\n\t// keep-alive interval in seconds, 0 is disable\n\tuint16_t keepalive_interval;\n\n\tstruct wireguard_allowed_ip allowed_source_ips[WIREGUARD_MAX_SRC_IPS];\n\n\tuint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t preshared_key[WIREGUARD_SESSION_KEY_LEN];\n\n\t// Precomputed DH(Sprivi,Spubr) with device private key, and peer public key\n\tuint8_t public_key_dh[WIREGUARD_PUBLIC_KEY_LEN];\n\n\t// Session keypairs\n\tstruct wireguard_keypair curr_keypair;\n\tstruct wireguard_keypair prev_keypair;\n\tstruct wireguard_keypair next_keypair;\n\n\t// 5.1 Silence is a Virtue: The responder keeps track of the greatest timestamp received per peer\n\tuint8_t greatest_timestamp[WIREGUARD_TAI64N_LEN];\n\n\t// The active handshake that is happening\n\tstruct wireguard_handshake handshake;\n\n\t// Decrypted cookie from the responder\n\tuint32_t cookie_millis;\n\tuint8_t cookie[WIREGUARD_COOKIE_LEN];\n\n\t// The latest mac1 we sent with initiation\n\tbool handshake_mac1_valid;\n\tuint8_t handshake_mac1[WIREGUARD_COOKIE_LEN];\n\n\t// Precomputed keys for use in mac validation\n\tuint8_t label_cookie_key[WIREGUARD_SESSION_KEY_LEN];\n\tuint8_t label_mac1_key[WIREGUARD_SESSION_KEY_LEN];\n\n\t// The last time we received a valid initiation message\n\tuint32_t last_initiation_rx;\n\t// The last time we sent an initiation message to this peer\n\tuint32_t last_initiation_tx;\n\n\t// last_tx and last_rx of data packets\n\tuint32_t last_tx;\n\tuint32_t last_rx;\n\n\t// We set this flag on RX/TX of packets if we think that we should initiate a new handshake\n\tbool send_handshake;\n};\n\nstruct wireguard_device {\n\t// Maybe have a \"Device private\" member to abstract these?\n\tstruct netif *netif;\n\tstruct udp_pcb *udp_pcb;\n\n\tstruct netif *underlying_netif;\n\n\tuint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];\n\tuint8_t private_key[WIREGUARD_PRIVATE_KEY_LEN];\n\n\tuint8_t cookie_secret[WIREGUARD_HASH_LEN];\n\tuint32_t cookie_secret_millis;\n\n\t// Precalculated\n \tuint8_t label_cookie_key[WIREGUARD_SESSION_KEY_LEN];\n\tuint8_t label_mac1_key[WIREGUARD_SESSION_KEY_LEN];\n\n\t// List of peers associated with this device\n \tstruct wireguard_peer peers[WIREGUARD_MAX_PEERS];\n\n\tbool valid;\n};\n\n#define MESSAGE_INVALID\t\t\t\t\t0\n#define MESSAGE_HANDSHAKE_INITIATION\t1\n#define MESSAGE_HANDSHAKE_RESPONSE\t\t2\n#define MESSAGE_COOKIE_REPLY\t\t\t3\n#define MESSAGE_TRANSPORT_DATA\t\t\t4\n\n\n// 5.4.2 First Message: Initiator to Responder\nstruct message_handshake_initiation {\n\tuint8_t type;\n\tuint8_t reserved[3];\n\tuint32_t sender;\n\tuint8_t ephemeral[32];\n\tuint8_t enc_static[32 + WIREGUARD_AUTHTAG_LEN];\n\tuint8_t enc_timestamp[WIREGUARD_TAI64N_LEN + WIREGUARD_AUTHTAG_LEN];\n\tuint8_t mac1[WIREGUARD_COOKIE_LEN];\n\tuint8_t mac2[WIREGUARD_COOKIE_LEN];\n} __attribute__ ((__packed__));\n\n// 5.4.3 Second Message: Responder to Initiator\nstruct message_handshake_response {\n\tuint8_t type;\n\tuint8_t reserved[3];\n\tuint32_t sender;\n\tuint32_t receiver;\n\tuint8_t ephemeral[32];\n\tuint8_t enc_empty[0 + WIREGUARD_AUTHTAG_LEN];\n\tuint8_t mac1[WIREGUARD_COOKIE_LEN];\n\tuint8_t mac2[WIREGUARD_COOKIE_LEN];\n} __attribute__ ((__packed__));\n\n// 5.4.7 Under Load: Cookie Reply Message\nstruct message_cookie_reply {\n\tuint8_t type;\n\tuint8_t reserved[3];\n\tuint32_t receiver;\n\tuint8_t nonce[COOKIE_NONCE_LEN];\n\tuint8_t enc_cookie[WIREGUARD_COOKIE_LEN + WIREGUARD_AUTHTAG_LEN];\n} __attribute__ ((__packed__));\n\n// 5.4.6 Subsequent Messages: Transport Data Messages\nstruct message_transport_data {\n\tuint8_t type;\n\tuint8_t reserved[3];\n\tuint32_t receiver;\n\tuint8_t counter[8];\n\t// Followed by encrypted data\n\tuint8_t enc_packet[];\n} __attribute__ ((__packed__));\n\n// Initialise the WireGuard system - need to call this before anything else\nvoid wireguard_init();\nbool wireguard_device_init(struct wireguard_device *device, const uint8_t *private_key);\nbool wireguard_peer_init(struct wireguard_device *device, struct wireguard_peer *peer, const uint8_t *public_key, const uint8_t *preshared_key);\n\nstruct wireguard_peer *peer_alloc(struct wireguard_device *device);\nuint8_t wireguard_peer_index(struct wireguard_device *device, struct wireguard_peer *peer);\nstruct wireguard_peer *peer_lookup_by_pubkey(struct wireguard_device *device, uint8_t *public_key);\nstruct wireguard_peer *peer_lookup_by_peer_index(struct wireguard_device *device, uint8_t peer_index);\nstruct wireguard_peer *peer_lookup_by_receiver(struct wireguard_device *device, uint32_t receiver);\nstruct wireguard_peer *peer_lookup_by_handshake(struct wireguard_device *device, uint32_t receiver);\n\nvoid wireguard_start_session(struct wireguard_peer *peer, bool initiator);\n\nvoid keypair_update(struct wireguard_peer *peer, struct wireguard_keypair *received_keypair);\nvoid keypair_destroy(struct wireguard_keypair *keypair);\n\nstruct wireguard_keypair *get_peer_keypair_for_idx(struct wireguard_peer *peer, uint32_t idx);\nbool wireguard_check_replay(struct wireguard_keypair *keypair, uint64_t seq);\n\nuint8_t wireguard_get_message_type(const uint8_t *data, size_t len);\n\nstruct wireguard_peer *wireguard_process_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg);\nbool wireguard_process_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *src);\nbool wireguard_process_cookie_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_cookie_reply *src);\n\nbool wireguard_create_handshake_initiation(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_initiation *dst);\nbool wireguard_create_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *dst);\nvoid wireguard_create_cookie_reply(struct wireguard_device *device, struct message_cookie_reply *dst, const uint8_t *mac1, uint32_t index, uint8_t *source_addr_port, size_t source_length);\n\n\nbool wireguard_check_mac1(struct wireguard_device *device, const uint8_t *data, size_t len, const uint8_t *mac1);\nbool wireguard_check_mac2(struct wireguard_device *device, const uint8_t *data, size_t len, uint8_t *source_addr_port, size_t source_length, const uint8_t *mac2);\n\nbool wireguard_expired(uint32_t created_millis, uint32_t valid_seconds);\n\nvoid wireguard_encrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, struct wireguard_keypair *keypair);\nbool wireguard_decrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, uint64_t counter, struct wireguard_keypair *keypair);\n\nbool wireguard_base64_decode(const char *str, uint8_t *out, size_t *outlen);\nbool wireguard_base64_encode(const uint8_t *in, size_t inlen, char *out, size_t *outlen);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _WIREGUARD_H_ */\n"
  },
  {
    "path": "src/wireguardif.c",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * Copyright (c) 2021 Kenta Ida (fuga@fugafuga.org)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n#include \"wireguardif.h\"\n\n#include <string.h>\n#include <stdlib.h>\n#include <inttypes.h>\n#include <lwip/netif.h>\n#include <lwip/ip.h>\n#include <lwip/udp.h>\n#include <lwip/mem.h>\n#include <lwip/sys.h>\n#include <lwip/timeouts.h>\n#include <sys/socket.h>\n#include <esp_log.h>\n#include <esp_err.h>\n#if defined(CONFIG_WIREGUARD_ESP_NETIF)\n#include <esp_netif.h>\n#endif\n#if defined(CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER)\n#include <tcpip_adapter.h>\n#endif\n\n#include \"wireguard.h\"\n#include \"crypto.h\"\n\n#define WIREGUARDIF_TIMER_MSECS 400\n\n#define TAG \"wireguardif\"\n\nstatic void update_peer_addr(struct wireguard_peer *peer, const ip_addr_t *addr, u16_t port) {\n\tpeer->ip = *addr;\n\tpeer->port = port;\n}\n\nstatic struct wireguard_peer *peer_lookup_by_allowed_ip(struct wireguard_device *device, const ip_addr_t *ipaddr) {\n\tstruct wireguard_peer *result = NULL;\n\tstruct wireguard_peer *tmp;\n\tint x;\n\tint y;\n\tfor (x=0; (!result) && (x < WIREGUARD_MAX_PEERS); x++) {\n\t\ttmp = &device->peers[x];\n\t\tif (tmp->valid) {\n\t\t\tfor (y=0; y < WIREGUARD_MAX_SRC_IPS; y++) {\n\t\t\t\tif ((tmp->allowed_source_ips[y].valid) && ip_addr_netcmp(ipaddr, &tmp->allowed_source_ips[y].ip, ip_2_ip4(&tmp->allowed_source_ips[y].mask))) {\n\t\t\t\t\tresult = tmp;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nstatic bool wireguardif_can_send_initiation(struct wireguard_peer *peer) {\n\treturn ((peer->last_initiation_tx == 0) || (wireguard_expired(peer->last_initiation_tx, REKEY_TIMEOUT)));\n}\n\nstatic err_t wireguardif_peer_output(struct netif *netif, struct pbuf *q, struct wireguard_peer *peer) {\n\tstruct wireguard_device *device = (struct wireguard_device *)netif->state;\n\t// Send to last know port, not the connect port\n\t//TODO: Support DSCP and ECN - lwip requires this set on PCB globally, not per packet\n\treturn udp_sendto_if(device->udp_pcb, q, &peer->ip, peer->port, device->underlying_netif);\n}\n\nstatic err_t wireguardif_device_output(struct wireguard_device *device, struct pbuf *q, const ip_addr_t *ipaddr, u16_t port) {\n\treturn udp_sendto_if(device->udp_pcb, q, ipaddr, port, device->underlying_netif);\n}\n\nstatic err_t wireguardif_output_to_peer(struct netif *netif, struct pbuf *q, const ip_addr_t *ipaddr, struct wireguard_peer *peer) {\n\t// The LWIP IP layer wants to send an IP packet out over the interface - we need to encrypt and send it to the peer\n\tstruct message_transport_data *hdr;\n\tstruct pbuf *pbuf;\n\terr_t result;\n\tsize_t unpadded_len;\n\tsize_t padded_len;\n\tsize_t header_len = 16;\n\tuint8_t *dst;\n\tuint32_t now;\n\tstruct wireguard_keypair *keypair = &peer->curr_keypair;\n\n\t// Note: We may not be able to use the current keypair if we haven't received data, may need to resort to using previous keypair\n\tif (keypair->valid && (!keypair->initiator) && (keypair->last_rx == 0)) {\n\t\tkeypair = &peer->prev_keypair;\n\t}\n\n\tif (keypair->valid && (keypair->initiator || keypair->last_rx != 0)) {\n\n\t\tif (\n\t\t\t\t!wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME) &&\n\t\t\t\t(keypair->sending_counter < REJECT_AFTER_MESSAGES)\n\t\t) {\n\n\t\t\t// Calculate the outgoing packet size - round up to next 16 bytes, add 16 bytes for header\n\t\t\tif (q) {\n\t\t\t\t// This is actual transport data\n\t\t\t\tunpadded_len = q->tot_len;\n\t\t\t} else {\n\t\t\t\t// This is a keep-alive\n\t\t\t\tunpadded_len = 0;\n\t\t\t}\n\t\t\tpadded_len = (unpadded_len + 15) & 0xFFFFFFF0; // Round up to next 16 byte boundary\n\n\t\t\t// The buffer needs to be allocated from \"transport\" pool to leave room for LwIP generated IP headers\n\t\t\t// The IP packet consists of 16 byte header (struct message_transport_data), data padded upto 16 byte boundary + encrypted auth tag (16 bytes)\n\t\t\tpbuf = pbuf_alloc(PBUF_TRANSPORT, header_len + padded_len + WIREGUARD_AUTHTAG_LEN, PBUF_RAM);\n\t\t\tif (pbuf) {\n\t\t\t\t// Note: allocating pbuf from RAM above guarantees that the pbuf is in one section and not chained\n\t\t\t\t// - i.e payload points to the contiguous memory region\n\t\t\t\tmemset(pbuf->payload, 0, pbuf->tot_len);\n\n\t\t\t\thdr = (struct message_transport_data *)pbuf->payload;\n\n\t\t\t\thdr->type = MESSAGE_TRANSPORT_DATA;\n\t\t\t\thdr->receiver = keypair->remote_index;\n\t\t\t\t// Alignment required... pbuf_alloc has probably aligned data, but want to be sure\n\t\t\t\tU64TO8_LITTLE(hdr->counter, keypair->sending_counter);\n\n\t\t\t\t// Copy the encrypted (padded) data to the output packet - chacha20poly1305_encrypt() can encrypt data in-place which avoids call to mem_malloc\n\t\t\t\tdst = &hdr->enc_packet[0];\n\t\t\t\tif ((padded_len > 0) && q) {\n\t\t\t\t\t// Note: before copying make sure we have inserted the IP header checksum\n\t\t\t\t\t// The IP header checksum (and other checksums in the IP packet - e.g. ICMP) need to be calculated by LWIP before calling\n\t\t\t\t\t// The Wireguard interface always needs checksums to be generated in software but the base netif may have some checksums generated by hardware\n\n\t\t\t\t\t// Copy pbuf to memory - handles case where pbuf is chained\n\t\t\t\t\tpbuf_copy_partial(q, dst, unpadded_len, 0);\n\t\t\t\t}\n\n\t\t\t\t// Then encrypt\n\t\t\t\twireguard_encrypt_packet(dst, dst, padded_len, keypair);\n\n\t\t\t\tresult = wireguardif_peer_output(netif, pbuf, peer);\n\n\t\t\t\tif (result == ERR_OK) {\n\t\t\t\t\tnow = wireguard_sys_now();\n\t\t\t\t\tpeer->last_tx = now;\n\t\t\t\t\tkeypair->last_tx = now;\n\t\t\t\t}\n\n\t\t\t\tpbuf_free(pbuf);\n\n\t\t\t\t// Check to see if we should rekey\n\t\t\t\tif (keypair->sending_counter >= REKEY_AFTER_MESSAGES) {\n\t\t\t\t\tpeer->send_handshake = true;\n\t\t\t\t} else if (keypair->initiator && wireguard_expired(keypair->keypair_millis, REKEY_AFTER_TIME)) {\n\t\t\t\t\tpeer->send_handshake = true;\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\t// Failed to allocate memory\n\t\t\t\tresult = ERR_MEM;\n\t\t\t}\n\t\t} else {\n\t\t\t// key has expired...\n\t\t\tkeypair_destroy(keypair);\n\t\t\tresult = ERR_CONN;\n\t\t}\n\t} else {\n\t\t// No valid keys!\n\t\tresult = ERR_CONN;\n\t}\n\treturn result;\n}\n\n// This is used as the output function for the Wireguard netif\n// The ipaddr here is the one inside the VPN which we use to lookup the correct peer/endpoint\nstatic err_t wireguardif_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ip4addr) {\n\tstruct wireguard_device *device = (struct wireguard_device *)netif->state;\n\tip_addr_t ipaddr;\n\n\tif (!device) {\n\t\tESP_LOGE(TAG, \"wireguardif_output NULL device\");\n\t\treturn ERR_RTE;\n\t}\n\n\t// Send to peer that matches dest IP\n\tip_addr_copy_from_ip4(ipaddr, *ip4addr);\n\tstruct wireguard_peer *peer = peer_lookup_by_allowed_ip(device, &ipaddr);\n\tif (peer) {\n\t\treturn wireguardif_output_to_peer(netif, q, &ipaddr, peer);\n\t} else {\n\t\treturn ERR_RTE;\n\t}\n}\n\nstatic void wireguardif_send_keepalive(struct wireguard_device *device, struct wireguard_peer *peer) {\n\t// Send a NULL packet as a keep-alive\n\twireguardif_output_to_peer(device->netif, NULL, NULL, peer);\n}\n\nstatic void wireguardif_process_response_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *response, const ip_addr_t *addr, u16_t port) {\n\tif (wireguard_process_handshake_response(device, peer, response)) {\n\t\t// Packet is good\n\t\t// Update the peer location\n\t\tupdate_peer_addr(peer, addr, port);\n\n\t\twireguard_start_session(peer, true);\n\t\twireguardif_send_keepalive(device, peer);\n\n\t\t// Set the IF-UP flag on netif\n\t\tnetif_set_link_up(device->netif);\n\t} else {\n\t\t// Packet bad\n\t}\n}\n\nstatic bool peer_add_ip(struct wireguard_peer *peer, ip_addr_t ip, ip_addr_t mask) {\n\tbool result = false;\n\tstruct wireguard_allowed_ip *allowed;\n\tint x;\n\t// Look for existing match first\n\tfor (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {\n\t\tallowed = &peer->allowed_source_ips[x];\n\t\tif ((allowed->valid) && ip_addr_cmp(&allowed->ip, &ip) && ip_addr_cmp(&allowed->mask, &mask)) {\n\t\t\tresult = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!result) {\n\t\t// Look for a free slot\n\t\tfor (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {\n\t\t\tallowed = &peer->allowed_source_ips[x];\n\t\t\tif (!allowed->valid) {\n\t\t\t\tallowed->valid = true;\n\t\t\t\tallowed->ip = ip;\n\t\t\t\tallowed->mask = mask;\n\t\t\t\tresult = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nstatic void wireguardif_process_data_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_transport_data *data_hdr, size_t data_len, const ip_addr_t *addr, u16_t port) {\n\tstruct wireguard_keypair *keypair;\n\tuint64_t nonce;\n\tuint8_t *src;\n\tsize_t src_len;\n\tstruct pbuf *pbuf;\n\tstruct ip_hdr *iphdr;\n\tip_addr_t dest;\n\tbool dest_ok = false;\n\tint x;\n\tuint32_t now;\n\tuint16_t header_len = 0xFFFF;\n\tuint32_t idx = data_hdr->receiver;\n\n\tkeypair = get_peer_keypair_for_idx(peer, idx);\n\n\tif (keypair) {\n\t\tif (\n\t\t\t\t(keypair->receiving_valid) &&\n\t\t\t\t!wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME) &&\n\t\t\t\t(keypair->sending_counter < REJECT_AFTER_MESSAGES)\n\n\t\t) {\n\n\t\t\tnonce = U8TO64_LITTLE(data_hdr->counter);\n\t\t\tsrc = &data_hdr->enc_packet[0];\n\t\t\tsrc_len = data_len;\n\n\t\t\t// We don't know the unpadded size until we have decrypted the packet and validated/inspected the IP header\n\t\t\tpbuf = pbuf_alloc(PBUF_TRANSPORT, src_len - WIREGUARD_AUTHTAG_LEN, PBUF_RAM);\n\t\t\tif (pbuf) {\n\t\t\t\t// Decrypt the packet\n\t\t\t\tmemset(pbuf->payload, 0, pbuf->tot_len);\n\t\t\t\tif (wireguard_decrypt_packet(pbuf->payload, src, src_len, nonce, keypair)) {\n\n\t\t\t\t\t// 3. Since the packet has authenticated correctly, the source IP of the outer UDP/IP packet is used to update the endpoint for peer TrMv...WXX0.\n\t\t\t\t\t// Update the peer location\n\t\t\t\t\tupdate_peer_addr(peer, addr, port);\n\n\t\t\t\t\tnow = wireguard_sys_now();\n\t\t\t\t\tkeypair->last_rx = now;\n\t\t\t\t\tpeer->last_rx = now;\n\n\t\t\t\t\t// Might need to shuffle next key --> current keypair\n\t\t\t\t\tkeypair_update(peer, keypair);\n\n\t\t\t\t\t// Check to see if we should rekey\n\t\t\t\t\tif (keypair->initiator && wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME - peer->keepalive_interval - REKEY_TIMEOUT)) {\n\t\t\t\t\t\tpeer->send_handshake = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Make sure that link is reported as up\n\t\t\t\t\tnetif_set_link_up(device->netif);\n\n\t\t\t\t\tif (pbuf->tot_len > 0) {\n\t\t\t\t\t\t//4a. Once the packet payload is decrypted, the interface has a plaintext packet. If this is not an IP packet, it is dropped.\n\t\t\t\t\t\tiphdr = (struct ip_hdr *)pbuf->payload;\n\t\t\t\t\t\t// Check for packet replay / dupes\n\t\t\t\t\t\tif (wireguard_check_replay(keypair, nonce)) {\n\n\t\t\t\t\t\t\t// 4b. Otherwise, WireGuard checks to see if the source IP address of the plaintext inner-packet routes correspondingly in the cryptokey routing table\n\t\t\t\t\t\t\t// Also check packet length!\n#if LWIP_IPV4\n\t\t\t\t\t\t\tif (IPH_V(iphdr) == 4) {\n\t\t\t\t\t\t\t\tip_addr_copy_from_ip4(dest, iphdr->dest);\n\t\t\t\t\t\t\t\tfor (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {\n\t\t\t\t\t\t\t\t\tif (peer->allowed_source_ips[x].valid) {\n\t\t\t\t\t\t\t\t\t\tif (ip_addr_netcmp(&dest, &peer->allowed_source_ips[x].ip, ip_2_ip4(&peer->allowed_source_ips[x].mask))) {\n\t\t\t\t\t\t\t\t\t\t\tdest_ok = true;\n\t\t\t\t\t\t\t\t\t\t\theader_len = PP_NTOHS(IPH_LEN(iphdr));\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n#endif /* LWIP_IPV4 */\n#if LWIP_IPV6\n\t\t\t\t\t\t\tif (IPH_V(iphdr) == 6) {\n\t\t\t\t\t\t\t\t// TODO: IPV6 support for route filtering\n\t\t\t\t\t\t\t\theader_len = PP_NTOHS(IPH_LEN(iphdr));\n\t\t\t\t\t\t\t\tdest_ok = true;\n\t\t\t\t\t\t\t}\n#endif /* LWIP_IPV6 */\n\t\t\t\t\t\t\tif (header_len <= pbuf->tot_len) {\n\n\t\t\t\t\t\t\t\t// 5. If the plaintext packet has not been dropped, it is inserted into the receive queue of the wg0 interface.\n\t\t\t\t\t\t\t\tif (dest_ok) {\n\t\t\t\t\t\t\t\t\t// Send packet to be process by LWIP\n\t\t\t\t\t\t\t\t\tip_input(pbuf, device->netif);\n\t\t\t\t\t\t\t\t\t// pbuf is owned by IP layer now\n\t\t\t\t\t\t\t\t\tpbuf = NULL;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// IP header is corrupt or lied about packet size\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// This is a duplicate packet / replayed / too far out of order\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// This was a keep-alive packet\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (pbuf) {\n\t\t\t\t\tpbuf_free(pbuf);\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t} else {\n\t\t\t//After Reject-After-Messages transport data messages or after the current secure session is Reject- After-Time seconds old,\n\t\t\t// whichever comes first, WireGuard will refuse to send or receive any more transport data messages using the current secure session,\n\t\t\t// until a new secure session is created through the 1-RTT handshake\n\t\t\tkeypair_destroy(keypair);\n\t\t}\n\n\t} else {\n\t\t// Could not locate valid keypair for remote index\n\t}\n}\n\nstatic struct pbuf *wireguardif_initiate_handshake(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_initiation *msg, err_t *error) {\n\tstruct pbuf *pbuf = NULL;\n\terr_t err = ERR_OK;\n\tif (wireguard_create_handshake_initiation(device, peer, msg)) {\n\t\tESP_LOGD(TAG, \"sending initiation packet\");\n\t\tpbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_handshake_initiation), PBUF_RAM);\n\t\tif (pbuf) {\n\t\t\terr = pbuf_take(pbuf, msg, sizeof(struct message_handshake_initiation));\n\t\t\tif (err == ERR_OK) {\n\t\t\t\t// OK!\n\t\t\t} else {\n\t\t\t\tpbuf_free(pbuf);\n\t\t\t\tpbuf = NULL;\n\t\t\t}\n\t\t} else {\n\t\t\terr = ERR_MEM;\n\t\t}\n\t} else {\n\t\terr = ERR_ARG;\n\t}\n\tif (error) {\n\t\t*error = err;\n\t}\n\treturn pbuf;\n}\n\nstatic void wireguardif_send_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer) {\n\tstruct message_handshake_response packet;\n\tstruct pbuf *pbuf = NULL;\n\terr_t err = ERR_OK;\n\n\tif (wireguard_create_handshake_response(device, peer, &packet)) {\n\n\t\twireguard_start_session(peer, false);\n\n\t\tESP_LOGD(TAG, \"sending handshake response packet\");\n\t\tpbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_handshake_response), PBUF_RAM);\n\t\tif (pbuf) {\n\t\t\terr = pbuf_take(pbuf, &packet, sizeof(struct message_handshake_response));\n\t\t\tif (err == ERR_OK) {\n\t\t\t\t// OK!\n\t\t\t\twireguardif_peer_output(device->netif, pbuf, peer);\n\t\t\t}\n\t\t\tpbuf_free(pbuf);\n\t\t}\n\t}\n}\n\nstatic size_t get_source_addr_port(const ip_addr_t *addr, u16_t port, uint8_t *buf, size_t buflen) {\n\tsize_t result = 0;\n\n#if LWIP_IPV4\n\tif (IP_IS_V4(addr) && (buflen >= 4)) {\n\t\tU32TO8_BIG(buf + result, PP_NTOHL(ip4_addr_get_u32(ip_2_ip4(addr))));\n\t\tresult += 4;\n\t}\n#endif\n#if LWIP_IPV6\n\tif (IP_IS_V6(addr) && (buflen >= 16)) {\n\t\tU16TO8_BIG(buf + result + 0, IP6_ADDR_BLOCK1(ip_2_ip6(addr)));\n\t\tU16TO8_BIG(buf + result + 2, IP6_ADDR_BLOCK2(ip_2_ip6(addr)));\n\t\tU16TO8_BIG(buf + result + 4, IP6_ADDR_BLOCK3(ip_2_ip6(addr)));\n\t\tU16TO8_BIG(buf + result + 6, IP6_ADDR_BLOCK4(ip_2_ip6(addr)));\n\t\tU16TO8_BIG(buf + result + 8, IP6_ADDR_BLOCK5(ip_2_ip6(addr)));\n\t\tU16TO8_BIG(buf + result + 10, IP6_ADDR_BLOCK6(ip_2_ip6(addr)));\n\t\tU16TO8_BIG(buf + result + 12, IP6_ADDR_BLOCK7(ip_2_ip6(addr)));\n\t\tU16TO8_BIG(buf + result + 14, IP6_ADDR_BLOCK8(ip_2_ip6(addr)));\n\t\tresult += 16;\n\t}\n#endif\n\tif (buflen >= result + 2) {\n\t\tU16TO8_BIG(buf + result, port);\n\t\tresult += 2;\n\t}\n\treturn result;\n}\n\nstatic void wireguardif_send_handshake_cookie(struct wireguard_device *device, const uint8_t *mac1, uint32_t index, const ip_addr_t *addr, u16_t port) {\n\tstruct message_cookie_reply packet;\n\tstruct pbuf *pbuf = NULL;\n\terr_t err = ERR_OK;\n\tuint8_t source_buf[18];\n\tsize_t source_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));\n\n\twireguard_create_cookie_reply(device, &packet, mac1, index, source_buf, source_len);\n\n\t// Send this packet out!\n\tpbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_cookie_reply), PBUF_RAM);\n\tif (pbuf) {\n\t\terr = pbuf_take(pbuf, &packet, sizeof(struct message_cookie_reply));\n\t\tif (err == ERR_OK) {\n\t\t\twireguardif_device_output(device, pbuf, addr, port);\n\t\t}\n\t\tpbuf_free(pbuf);\n\t}\n}\n\nstatic bool wireguardif_check_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg, const ip_addr_t *addr, u16_t port) {\n\tbool result = false;\n\tuint8_t *data = (uint8_t *)msg;\n\tuint8_t source_buf[18];\n\tsize_t source_len;\n\t// We received an initiation packet check it is valid\n\n\tif (wireguard_check_mac1(device, data, sizeof(struct message_handshake_initiation) - (2 * WIREGUARD_COOKIE_LEN), msg->mac1)) {\n\t\t// mac1 is valid!\n\t\tif (!wireguard_is_under_load()) {\n\t\t\t// If we aren't under load we only need mac1 to be correct\n\t\t\tresult = true;\n\t\t} else {\n\t\t\t// If we are under load then check mac2\n\t\t\tsource_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));\n\n\t\t\tresult = wireguard_check_mac2(device, data, sizeof(struct message_handshake_initiation) - (WIREGUARD_COOKIE_LEN), source_buf, source_len, msg->mac2);\n\n\t\t\tif (!result) {\n\t\t\t\t// mac2 is invalid (cookie may have expired) or not present\n\t\t\t\t// 5.3 Denial of Service Mitigation & Cookies\n\t\t\t\t// If the responder receives a message with a valid msg.mac1 yet with an invalid msg.mac2, and is under load, it may respond with a cookie reply message\n\t\t\t\twireguardif_send_handshake_cookie(device, msg->mac1, msg->sender, addr, port);\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\t// mac1 is invalid\n\t}\n\treturn result;\n}\n\nstatic bool wireguardif_check_response_message(struct wireguard_device *device, struct message_handshake_response *msg, const ip_addr_t *addr, u16_t port) {\n\tbool result = false;\n\tuint8_t *data = (uint8_t *)msg;\n\tuint8_t source_buf[18];\n\tsize_t source_len;\n\t// We received an initiation packet check it is valid\n\n\tif (wireguard_check_mac1(device, data, sizeof(struct message_handshake_response) - (2 * WIREGUARD_COOKIE_LEN), msg->mac1)) {\n\t\t// mac1 is valid!\n\t\tif (!wireguard_is_under_load()) {\n\t\t\t// If we aren't under load we only need mac1 to be correct\n\t\t\tresult = true;\n\t\t} else {\n\t\t\t// If we are under load then check mac2\n\t\t\tsource_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));\n\n\t\t\tresult = wireguard_check_mac2(device, data, sizeof(struct message_handshake_response) - (WIREGUARD_COOKIE_LEN), source_buf, source_len, msg->mac2);\n\n\t\t\tif (!result) {\n\t\t\t\t// mac2 is invalid (cookie may have expired) or not present\n\t\t\t\t// 5.3 Denial of Service Mitigation & Cookies\n\t\t\t\t// If the responder receives a message with a valid msg.mac1 yet with an invalid msg.mac2, and is under load, it may respond with a cookie reply message\n\t\t\t\twireguardif_send_handshake_cookie(device, msg->mac1, msg->sender, addr, port);\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\t// mac1 is invalid\n\t}\n\treturn result;\n}\n\n\nvoid wireguardif_network_rx(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) {\n\tLWIP_ASSERT(\"wireguardif_network_rx: invalid arg\", arg != NULL);\n\tLWIP_ASSERT(\"wireguardif_network_rx: invalid pbuf\", p != NULL);\n\t// We have received a packet from the base_netif to our UDP port - process this as a possible Wireguard packet\n\tstruct wireguard_device *device = (struct wireguard_device *)arg;\n\tstruct wireguard_peer *peer;\n\tuint8_t *data = p->payload;\n\tsize_t len = p->len; // This buf, not chained ones\n\n\tstruct message_handshake_initiation *msg_initiation;\n\tstruct message_handshake_response *msg_response;\n\tstruct message_cookie_reply *msg_cookie;\n\tstruct message_transport_data *msg_data;\n\n\tuint8_t type = wireguard_get_message_type(data, len);\n\n\tswitch (type) {\n\t\tcase MESSAGE_HANDSHAKE_INITIATION:\n\t\t\tmsg_initiation = (struct message_handshake_initiation *)data;\n\t\t\t// Check mac1 (and optionally mac2) are correct - note it may internally generate a cookie reply packet\n\t\t\tif (wireguardif_check_initiation_message(device, msg_initiation, addr, port)) {\n\n\t\t\t\tpeer = wireguard_process_initiation_message(device, msg_initiation);\n\t\t\t\tif (peer) {\n\t\t\t\t\t// Update the peer location\n\t\t\t\t\tupdate_peer_addr(peer, addr, port);\n\n\t\t\t\t\t// Send back a handshake response\n\t\t\t\t\twireguardif_send_handshake_response(device, peer);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MESSAGE_HANDSHAKE_RESPONSE:\n\t\t\tmsg_response = (struct message_handshake_response *)data;\n\n\t\t\t// Check mac1 (and optionally mac2) are correct - note it may internally generate a cookie reply packet\n\t\t\tif (wireguardif_check_response_message(device, msg_response, addr, port)) {\n\n\t\t\t\tpeer = peer_lookup_by_handshake(device, msg_response->receiver);\n\t\t\t\tif (peer) {\n\t\t\t\t\t// Process the handshake response\n\t\t\t\t\twireguardif_process_response_message(device, peer, msg_response, addr, port);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MESSAGE_COOKIE_REPLY:\n\t\t\tmsg_cookie = (struct message_cookie_reply *)data;\n\t\t\tpeer = peer_lookup_by_handshake(device, msg_cookie->receiver);\n\t\t\tif (peer) {\n\t\t\t\tif (wireguard_process_cookie_message(device, peer, msg_cookie)) {\n\t\t\t\t\t// Update the peer location\n\t\t\t\t\tupdate_peer_addr(peer, addr, port);\n\n\t\t\t\t\t// Don't send anything out - we stay quiet until the next initiation message\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MESSAGE_TRANSPORT_DATA:\n\t\t\tmsg_data = (struct message_transport_data *)data;\n\t\t\tpeer = peer_lookup_by_receiver(device, msg_data->receiver);\n\t\t\tif (peer) {\n\t\t\t\t// header is 16 bytes long so take that off the length\n\t\t\t\twireguardif_process_data_message(device, peer, msg_data, len - 16, addr, port);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\t// Unknown or bad packet header\n\t\t\tbreak;\n\t}\n\t// Release data!\n\tpbuf_free(p);\n}\n\nstatic err_t wireguard_start_handshake(struct netif *netif, struct wireguard_peer *peer) {\n\tstruct wireguard_device *device = (struct wireguard_device *)netif->state;\n\terr_t result;\n\tstruct pbuf *pbuf;\n\tstruct message_handshake_initiation msg;\n\n\tESP_LOGD(TAG, \"starting handshake\");\n\tpbuf = wireguardif_initiate_handshake(device, peer, &msg, &result);\n\tif (pbuf) {\n\t\tresult = wireguardif_peer_output(netif, pbuf, peer);\n\t\tif (result != ERR_OK) {\n#ifdef CONFIG_LWIP_DEBUG\n\t\t\tESP_LOGE(TAG, \"wireguardif_peer_output: %s\", lwip_strerr(result));\n#else\n\t\t\tESP_LOGE(TAG, \"wireguardif_peer_output: %i\", result);\n#endif\n\t\t}\n\t\tpbuf_free(pbuf);\n\t\tpeer->send_handshake = false;\n\t\tpeer->last_initiation_tx = wireguard_sys_now();\n\t\tmemcpy(peer->handshake_mac1, msg.mac1, WIREGUARD_COOKIE_LEN);\n\t\tpeer->handshake_mac1_valid = true;\n\t}\n\treturn result;\n}\n\nstatic err_t wireguardif_lookup_peer(struct netif *netif, u8_t peer_index, struct wireguard_peer **out) {\n\tLWIP_ASSERT(\"netif != NULL\", (netif != NULL));\n\tLWIP_ASSERT(\"state != NULL\", (netif->state != NULL));\n\tstruct wireguard_device *device = (struct wireguard_device *)netif->state;\n\tstruct wireguard_peer *peer = NULL;\n\terr_t result;\n\n\tif (device->valid) {\n\t\tpeer = peer_lookup_by_peer_index(device, peer_index);\n\t\tif (peer) {\n\t\t\tresult = ERR_OK;\n\t\t} else {\n\t\t\tresult = ERR_ARG;\n\t\t}\n\t} else {\n\t\tresult = ERR_ARG;\n\t}\n\t*out = peer;\n\treturn result;\n}\n\nerr_t wireguardif_connect(struct netif *netif, u8_t peer_index) {\n\tstruct wireguard_peer *peer;\n\terr_t result = wireguardif_lookup_peer(netif, peer_index, &peer);\n\tif (result == ERR_OK) {\n\t\t// Check that a valid connect ip and port have been set\n\t\tif (!ip_addr_isany(&peer->connect_ip) && (peer->connect_port > 0)) {\n\t\t\t// Set the flag that we want to try connecting\n\t\t\tpeer->active = true;\n\t\t\tpeer->ip = peer->connect_ip;\n\t\t\tpeer->port = peer->connect_port;\n\t\t\tresult = ERR_OK;\n\t\t} else {\n\t\t\tresult = ERR_ARG;\n\t\t}\n\t}\n\treturn result;\n}\n\nerr_t wireguardif_disconnect(struct netif *netif, u8_t peer_index) {\n\tstruct wireguard_peer *peer;\n\terr_t result = wireguardif_lookup_peer(netif, peer_index, &peer);\n\tif (result == ERR_OK) {\n\t\t// Set the flag that we want to try connecting\n\t\tpeer->active = false;\n\t\t// Wipe out current keys\n\t\tkeypair_destroy(&peer->next_keypair);\n\t\tkeypair_destroy(&peer->curr_keypair);\n\t\tkeypair_destroy(&peer->prev_keypair);\n\t\tresult = ERR_OK;\n\t}\n\treturn result;\n}\n\nerr_t wireguardif_peer_is_up(struct netif *netif, u8_t peer_index, ip_addr_t *current_ip, u16_t *current_port) {\n\tstruct wireguard_peer *peer;\n\terr_t result = wireguardif_lookup_peer(netif, peer_index, &peer);\n\tif (result == ERR_OK) {\n\t\tif ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {\n\t\t\tresult = ERR_OK;\n\t\t} else {\n\t\t\tresult = ERR_CONN;\n\t\t}\n\t\tif (current_ip) {\n\t\t\t*current_ip = peer->ip;\n\t\t}\n\t\tif (current_port) {\n\t\t\t*current_port = peer->port;\n\t\t}\n\t}\n\treturn result;\n}\n\nerr_t wireguardif_remove_peer(struct netif *netif, u8_t peer_index) {\n\tstruct wireguard_peer *peer;\n\terr_t result = wireguardif_lookup_peer(netif, peer_index, &peer);\n\tif (result == ERR_OK) {\n\t\tcrypto_zero(peer, sizeof(struct wireguard_peer));\n\t\tpeer->valid = false;\n\t\tresult = ERR_OK;\n\t}\n\treturn result;\n}\n\nerr_t wireguardif_update_endpoint(struct netif *netif, u8_t peer_index, const ip_addr_t *ip, u16_t port) {\n\tstruct wireguard_peer *peer;\n\terr_t result = wireguardif_lookup_peer(netif, peer_index, &peer);\n\tif (result == ERR_OK) {\n\t\tpeer->connect_ip = *ip;\n\t\tpeer->connect_port = port;\n\t\tresult = ERR_OK;\n\t}\n\treturn result;\n}\n\n\nerr_t wireguardif_add_peer(struct netif *netif, struct wireguardif_peer *p, u8_t *peer_index) {\n\tLWIP_ASSERT(\"netif != NULL\", (netif != NULL));\n\tLWIP_ASSERT(\"state != NULL\", (netif->state != NULL));\n\tLWIP_ASSERT(\"p != NULL\", (p != NULL));\n\tstruct wireguard_device *device = (struct wireguard_device *)netif->state;\n\terr_t result;\n\tuint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];\n\tsize_t public_key_len = sizeof(public_key);\n\tstruct wireguard_peer *peer = NULL;\n\n\tuint32_t t1 = wireguard_sys_now();\n\n\tif (wireguard_base64_decode(p->public_key, public_key, &public_key_len)\n\t\t\t&& (public_key_len == WIREGUARD_PUBLIC_KEY_LEN)) {\n\n\t\t// See if the peer is already registered\n\t\tpeer = peer_lookup_by_pubkey(device, public_key);\n\t\tif (!peer) {\n\t\t\t// Not active - see if we have room to allocate a new one\n\t\t\tpeer = peer_alloc(device);\n\t\t\tif (peer) {\n\n\t\t\t\tif (wireguard_peer_init(device, peer, public_key, p->preshared_key)) {\n\n\t\t\t\t\tpeer->connect_ip = p->endpoint_ip;\n\t\t\t\t\tpeer->connect_port = p->endport_port;\n\t\t\t\t\tpeer->ip = peer->connect_ip;\n\t\t\t\t\tpeer->port = peer->connect_port;\n\t\t\t\t\tpeer->keepalive_interval = p->keep_alive;\n\t\t\t\t\tpeer_add_ip(peer, p->allowed_ip, p->allowed_mask);\n\t\t\t\t\tmemcpy(peer->greatest_timestamp, p->greatest_timestamp, sizeof(peer->greatest_timestamp));\n\n\t\t\t\t\tresult = ERR_OK;\n\t\t\t\t} else {\n\t\t\t\t\tresult = ERR_ARG;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresult = ERR_MEM;\n\t\t\t}\n\t\t} else {\n\t\t\tresult = ERR_OK;\n\t\t}\n\t} else {\n\t\tresult = ERR_ARG;\n\t}\n\n\tuint32_t t2 = wireguard_sys_now();\n\tESP_LOGD(TAG, \"Adding peer took %\" PRIu32 \"ms\", (t2-t1));\n\n\tif (peer_index) {\n\t\tif (peer) {\n\t\t\t*peer_index = wireguard_peer_index(device, peer);\n\t\t} else {\n\t\t\t*peer_index = WIREGUARDIF_INVALID_INDEX;\n\t\t}\n\t}\n\treturn result;\n}\n\nstatic bool should_send_initiation(struct wireguard_peer *peer) {\n\tbool result = false;\n\tif (wireguardif_can_send_initiation(peer)) {\n\t\tif (peer->send_handshake) {\n\t\t\tresult = true;\n\t\t} else if (peer->curr_keypair.valid && !peer->curr_keypair.initiator && wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME - peer->keepalive_interval)) {\n\t\t\tresult = true;\n\t\t} else if (!peer->curr_keypair.valid && peer->active) {\n\t\t\tresult = true;\n\t\t}\n\t}\n\treturn result;\n}\n\nstatic bool should_send_keepalive(struct wireguard_peer *peer) {\n\tbool result = false;\n\tif (peer->keepalive_interval > 0) {\n\t\tif ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {\n\t\t\tif (wireguard_expired(peer->last_tx, peer->keepalive_interval)) {\n\t\t\t\tresult = true;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nstatic bool should_destroy_current_keypair(struct wireguard_peer *peer) {\n\tbool result = false;\n\tif (peer->curr_keypair.valid &&\n\t\t\t(wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME) ||\n\t\t\t(peer->curr_keypair.sending_counter >= REJECT_AFTER_MESSAGES))\n\t\t) {\n\t\tresult = true;\n\t}\n\treturn result;\n}\n\nstatic bool should_reset_peer(struct wireguard_peer *peer) {\n\tbool result = false;\n\tif (peer->curr_keypair.valid && (wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME * 3))) {\n\t\tresult = true;\n\t}\n\treturn result;\n}\n\nstatic void wireguardif_tmr(void *arg) {\n\tstruct wireguard_device *device = (struct wireguard_device *)arg;\n\tstruct wireguard_peer *peer;\n\tint x;\n\t// Reschedule this timer\n\tsys_timeout(WIREGUARDIF_TIMER_MSECS, wireguardif_tmr, device);\n\n\t// Check periodic things\n\tbool link_up = false;\n\tfor (x=0; x < WIREGUARD_MAX_PEERS; x++) {\n\t\tpeer = &device->peers[x];\n\t\tif (peer->valid) {\n\t\t\t// Do we need to rekey / send a handshake?\n\t\t\tif (should_reset_peer(peer)) {\n\t\t\t\t// Nothing back for too long - we should wipe out all crypto state\n\t\t\t\tkeypair_destroy(&peer->next_keypair);\n\t\t\t\tkeypair_destroy(&peer->curr_keypair);\n\t\t\t\tkeypair_destroy(&peer->prev_keypair);\n\t\t\t\t// TODO: Also destroy handshake?\n\n\t\t\t\t// Revert back to default IP/port if these were altered\n\t\t\t\tpeer->ip = peer->connect_ip;\n\t\t\t\tpeer->port = peer->connect_port;\n\t\t\t}\n\t\t\tif (should_destroy_current_keypair(peer)) {\n\t\t\t\t// Destroy current keypair\n\t\t\t\tkeypair_destroy(&peer->curr_keypair);\n\t\t\t}\n\t\t\tif (should_send_keepalive(peer)) {\n\t\t\t\twireguardif_send_keepalive(device, peer);\n\t\t\t}\n\t\t\tif (should_send_initiation(peer)) {\n\t\t\t\twireguard_start_handshake(device->netif, peer);\n\t\t\t}\n\n\t\t\tif ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {\n\t\t\t\tlink_up = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!link_up) {\n\t\t// Clear the IF-UP flag on netif\n\t\tnetif_set_link_down(device->netif);\n\t}\n}\n\n\nerr_t wireguardif_init(struct netif *netif) {\n\terr_t result;\n\tesp_err_t err;\n\tstruct wireguardif_init_data *init_data;\n\tstruct wireguard_device *device;\n\tstruct udp_pcb *udp;\n\tuint8_t private_key[WIREGUARD_PRIVATE_KEY_LEN];\n\tsize_t private_key_len = sizeof(private_key);\n\n#if defined(CONFIG_WIREGUARD_ESP_NETIF)\n\tstruct netif* underlying_netif = NULL;\n\tchar lwip_netif_name[8] = {0,};\n\n\terr = esp_netif_get_netif_impl_name(esp_netif_get_handle_from_ifkey(\"WIFI_STA_DEF\"), lwip_netif_name);\n\tif (err != ESP_OK) {\n\t\tESP_LOGE(TAG, \"esp_netif_get_netif_impl_name: %s\", esp_err_to_name(err));\n\t\tresult = ERR_IF;\n\t\tgoto fail;\n\t}\n\tunderlying_netif = netif_find(lwip_netif_name);\n\tif (underlying_netif == NULL) {\n\t\tESP_LOGE(TAG, \"netif_find: cannot find WIFI_STA_DEF\");\n\t\tresult = ERR_IF;\n\t\tgoto fail;\n\t}\n#elif defined(CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER)\n\tvoid *underlying_netif = NULL;\n\terr = tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_STA, &underlying_netif);\n\tif (err != ESP_OK) {\n\t\tESP_LOGE(TAG, \"tcpip_adapter_get_netif: %s\", esp_err_to_name(err));\n\t\tresult = ERR_IF;\n\t\tgoto fail;\n\t}\n#endif\n\tESP_LOGD(TAG, \"underlying_netif = %p\", underlying_netif);\n\n\tLWIP_ASSERT(\"netif != NULL\", (netif != NULL));\n\tLWIP_ASSERT(\"state != NULL\", (netif->state != NULL));\n\n\t// We need to initialise the wireguard module\n\twireguard_init();\n\n\tif (netif && netif->state) {\n\n\t\t// The init data is passed into the netif_add call as the 'state' - we will replace this with our private state data\n\t\tinit_data = (struct wireguardif_init_data *)netif->state;\n\n\t\t// Clear out and set if function is successful\n\t\tnetif->state = NULL;\n\n\t\tif (wireguard_base64_decode(init_data->private_key, private_key, &private_key_len)\n\t\t\t\t&& (private_key_len == WIREGUARD_PRIVATE_KEY_LEN)) {\n\n\t\t\tudp = udp_new();\n\n\t\t\tif (udp) {\n\t\t\t\tresult = udp_bind(udp, IP_ADDR_ANY, init_data->listen_port); // Note this listens on all interfaces! Really just want the passed netif\n\t\t\t\tif (result == ERR_OK) {\n\t\t\t\t\tdevice = (struct wireguard_device *)mem_calloc(1, sizeof(struct wireguard_device));\n\t\t\t\t\tif (device) {\n\t\t\t\t\t\tdevice->netif = netif;\n\t\t\t\t\t\tdevice->underlying_netif = underlying_netif;\n\t\t\t\t\t\tudp_bind_netif(udp, underlying_netif);\n\n\t\t\t\t\t\tdevice->udp_pcb = udp;\n\t\t\t\t\t\t// Per-wireguard netif/device setup\n\t\t\t\t\t\tuint32_t t1 = wireguard_sys_now();\n\t\t\t\t\t\tif (wireguard_device_init(device, private_key)) {\n\t\t\t\t\t\t\tuint32_t t2 = wireguard_sys_now();\n\t\t\t\t\t\t\tESP_LOGD(TAG, \"Device init took %\" PRIi32 \"ms\", (t2-t1));\n\n#if LWIP_CHECKSUM_CTRL_PER_NETIF\n\t\t\t\t\t\t\tNETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);\n#endif\n\t\t\t\t\t\t\tnetif->state = device;\n\t\t\t\t\t\t\tnetif->name[0] = 'w';\n\t\t\t\t\t\t\tnetif->name[1] = 'g';\n\t\t\t\t\t\t\tnetif->output = wireguardif_output;\n\t\t\t\t\t\t\tnetif->linkoutput = NULL;\n\t\t\t\t\t\t\tnetif->hwaddr_len = 0;\n\t\t\t\t\t\t\tnetif->mtu = WIREGUARDIF_MTU;\n\t\t\t\t\t\t\t// We set up no state flags here - caller should set them\n\t\t\t\t\t\t\t// NETIF_FLAG_LINK_UP is automatically set/cleared when at least one peer is connected\n\t\t\t\t\t\t\tnetif->flags = 0;\n\n\t\t\t\t\t\t\tudp_recv(udp, wireguardif_network_rx, device);\n\n\t\t\t\t\t\t\t// Start a periodic timer for this wireguard device\n\t\t\t\t\t\t\tsys_timeout(WIREGUARDIF_TIMER_MSECS, wireguardif_tmr, device);\n\n\t\t\t\t\t\t\tresult = ERR_OK;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmem_free(device);\n\t\t\t\t\t\t\tdevice = NULL;\n\t\t\t\t\t\t\tudp_remove(udp);\n\t\t\t\t\t\t\tresult = ERR_ARG;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tudp_remove(udp);\n\t\t\t\t\t\tresult = ERR_MEM;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tudp_remove(udp);\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tresult = ERR_MEM;\n\t\t\t}\n\t\t} else {\n\t\t\tresult = ERR_ARG;\n\t\t}\n\t} else {\n\t\tresult = ERR_ARG;\n\t}\nfail:\n\treturn result;\n}\n\nvoid wireguardif_peer_init(struct wireguardif_peer *peer) {\n\tLWIP_ASSERT(\"peer != NULL\", (peer != NULL));\n\tmemset(peer, 0, sizeof(struct wireguardif_peer));\n\t// Caller must provide 'public_key'\n\tpeer->public_key = NULL;\n\tip_addr_set_any(false, &peer->endpoint_ip);\n\tpeer->endport_port = WIREGUARDIF_DEFAULT_PORT;\n\tpeer->keep_alive = 0;\n\tip_addr_set_any(false, &peer->allowed_ip);\n\tip_addr_set_any(false, &peer->allowed_mask);\n\tmemset(peer->greatest_timestamp, 0, sizeof(peer->greatest_timestamp));\n\tpeer->preshared_key = NULL;\n}\n\nvoid wireguardif_shutdown(struct netif *netif) {\n\tLWIP_ASSERT(\"netif != NULL\", (netif != NULL));\n\tLWIP_ASSERT(\"state != NULL\", (netif->state != NULL));\n\n\tstruct wireguard_device *device = (struct wireguard_device *)netif->state;\n\t// Disable timer.\n\tsys_untimeout(wireguardif_tmr, device);\n\t// remove UDP context.\n\tif (device->udp_pcb) {\n\t\tudp_disconnect(device->udp_pcb);\n\t\tudp_remove(device->udp_pcb);\n\t\tdevice->udp_pcb = NULL;\n\t}\n}\n\nvoid wireguardif_fini(struct netif *netif) {\n\tLWIP_ASSERT(\"netif != NULL\", (netif != NULL));\n\tLWIP_ASSERT(\"state != NULL\", (netif->state != NULL));\n\n\tstruct wireguard_device *device = (struct wireguard_device *)netif->state;\n\n\t// remove device context.\n\tfree(device);\n\tnetif->state = NULL;\n}\n// vim: noexpandtab\n"
  },
  {
    "path": "src/wireguardif.h",
    "content": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n *  list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice, this\n *  list of conditions and the following disclaimer in the documentation and/or\n *  other materials provided with the distribution.\n *\n * 3. Neither the name of \"Floorsense Ltd\", \"Agile Workspace Ltd\" nor the names of\n *  its contributors may be used to endorse or promote products derived from this\n *   software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Author: Daniel Hope <daniel.hope@smartalock.com>\n */\n\n\n#ifndef _WIREGUARDIF_H_\n#define _WIREGUARDIF_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"lwip/arch.h\"\n#include \"lwip/netif.h\"\n#include \"lwip/ip_addr.h\"\n\n// Default MTU for WireGuard is 1420 bytes\n#define WIREGUARDIF_MTU (1420)\n\n#define WIREGUARDIF_DEFAULT_PORT\t\t(51820)\n\nstruct wireguardif_init_data {\n\t// Required: the private key of this WireGuard network interface\n\tconst char *private_key;\n\t// Required: What UDP port to listen on\n\tu16_t listen_port;\n\t// Optional: restrict send/receive of encapsulated WireGuard traffic to this network interface only (NULL to use routing table)\n\tstruct netif *bind_netif;\n};\n\nstruct wireguardif_peer {\n\tconst char *public_key;\n\t// Optional pre-shared key (32 bytes) - make sure this is NULL if not to be used\n\tconst uint8_t *preshared_key;\n\t// tai64n of largest timestamp we have seen during handshake to avoid replays\n\tuint8_t greatest_timestamp[12];\n\n\t// Allowed ip/netmask (can add additional later but at least one is required)\n\tip_addr_t allowed_ip;\n\tip_addr_t allowed_mask;\n\n\t// End-point details (may be blank)\n\tip_addr_t endpoint_ip;\n\tu16_t endport_port;\n\tu16_t keep_alive;\n};\n\n#define WIREGUARDIF_INVALID_INDEX (0xFF)\n\n/* static struct netif wg_netif_struct = {0};\n * struct wireguard_interface wg;\n * wg.private_key = \"abcdefxxx..xxxxx=\";\n * wg.listen_port = 51820;\n * wg.bind_netif = NULL; // Pass netif to listen on, NULL for all interfaces\n *\n * netif = netif_add(&netif_struct, &ipaddr, &netmask, &gateway, &wg, &wireguardif_init, &ip_input);\n *\n * netif_set_up(wg_net);\n *\n * struct wireguardif_peer peer;\n * wireguardif_peer_init(&peer);\n * peer.public_key = \"apoehc...4322abcdfejg=;\n * peer.preshared_key = NULL;\n * peer.allowed_ip = allowed_ip;\n * peer.allowed_mask = allowed_mask;\n *\n * // If you want to enable output connection\n * peer.endpoint_ip = peer_ip;\n * peer.endport_port = 12345;\n *\n * uint8_t wireguard_peer_index;\n * wireguardif_add_peer(netif, &peer, &wireguard_peer_index);\n *\n * if ((wireguard_peer_index != WIREGUARDIF_INVALID_INDEX) && !ip_addr_isany(&peer.endpoint_ip)) {\n *   // Start outbound connection to peer\n *   wireguardif_connect(wg_net, wireguard_peer_index);\n * }\n *\n */\n\n// Initialise a new WireGuard network interface (netif)\nerr_t wireguardif_init(struct netif *netif);\n\n// Helper to initialise the peer struct with defaults\nvoid wireguardif_peer_init(struct wireguardif_peer *peer);\n\n// Add a new peer to the specified interface - see wireguard.h for maximum number of peers allowed\n// On success the peer_index can be used to reference this peer in future function calls\nerr_t wireguardif_add_peer(struct netif *netif, struct wireguardif_peer *peer, u8_t *peer_index);\n\n// Remove the given peer from the network interface\nerr_t wireguardif_remove_peer(struct netif *netif, u8_t peer_index);\n\n// Update the \"connect\" IP of the given peer\nerr_t wireguardif_update_endpoint(struct netif *netif, u8_t peer_index, const ip_addr_t *ip, u16_t port);\n\n// Try and connect to the given peer\nerr_t wireguardif_connect(struct netif *netif, u8_t peer_index);\n\n// Stop trying to connect to the given peer\nerr_t wireguardif_disconnect(struct netif *netif, u8_t peer_index);\n\n// Shutdown the WireGuard interface\nvoid wireguardif_shutdown(struct netif *netif);\n\n// Finalize the WireGuard interface after the netif is removed\nvoid wireguardif_fini(struct netif *netif);\n\n// Is the given peer \"up\"? A peer is up if it has a valid session key it can communicate with\nerr_t wireguardif_peer_is_up(struct netif *netif, u8_t peer_index, ip_addr_t *current_ip, u16_t *current_port);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _WIREGUARDIF_H_ */\n"
  }
]