Showing preview only (254K chars total). Download the full file or copy to clipboard to get everything.
Repository: trombik/esp_wireguard
Branch: main
Commit: 9217c5be0836
Files: 44
Total size: 240.3 KB
Directory structure:
gitextract_3dacllmf/
├── .ackrc
├── .github/
│ └── workflows/
│ ├── build.yml
│ └── publish.yml
├── .gitignore
├── CMakeLists.txt
├── Kconfig
├── LICENSE
├── README.md
├── component.mk
├── examples/
│ └── demo/
│ ├── CMakeLists.txt
│ ├── Makefile
│ ├── README.md
│ ├── Vagrantfile
│ └── main/
│ ├── CMakeLists.txt
│ ├── Kconfig.projbuild
│ ├── component.mk
│ ├── main.c
│ ├── sync_time.c
│ └── sync_time.h
├── idf_component.yml
├── include/
│ └── esp_wireguard.h
└── src/
├── crypto/
│ └── refc/
│ ├── blake2s.c
│ ├── blake2s.h
│ ├── chacha20.c
│ ├── chacha20.h
│ ├── chacha20poly1305.c
│ ├── chacha20poly1305.h
│ ├── poly1305-donna-32.h
│ ├── poly1305-donna.c
│ ├── poly1305-donna.h
│ ├── x25519-license.txt
│ ├── x25519.c
│ └── x25519.h
├── crypto.c
├── crypto.h
├── esp_wireguard.c
├── nacl/
│ └── crypto_scalarmult/
│ └── curve25519/
│ └── ref/
│ ├── crypto_scalarmult.h
│ └── smult.c
├── wireguard-platform.c
├── wireguard-platform.h
├── wireguard.c
├── wireguard.h
├── wireguardif.c
└── wireguardif.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .ackrc
================================================
--ignore-dir=build
================================================
FILE: .github/workflows/build.yml
================================================
---
name: Build examples
on:
push:
pull_request:
branches:
- main
# include workflow_dispatch to enable manual trigger from Web UI.
workflow_dispatch:
jobs:
pre_build:
# A job to see if the entrire jobs should be skipped. each job for a
# target should have:
#
# needs: pre_build
# if: ${{ needs.pre_build.outputs.should_skip != 'true' }}
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@v3.4.0
with:
concurrent_skipping: same_content
# if the change includes documentation, or ruby files changes only, skip.
paths_ignore: '["docs/**", "**/*.md", "*.md", "**/*.rb"]'
# but do not skip if the triggered event is one of these.
do_not_skip: '["workflow_dispatch", "schedule", "pull_request"]'
# XXX create multiple jobs for major versions
#
# for those who want to _refactor_ the jobs:
#
# in the previous CI implementation, all builds share a single build
# process. that way, you can remove duplications. however, every time a
# version changes the build process, the change affects all other build
# processes. I am tired of tracking changes and workarounds in the build
# process. the result is many `if`s. assuming major version does not change
# (a lot) its build process, creating multiple jobs, and using matrix is the
# only sane way. as GitHub Actions does not support reusable steps, there
# are many duplications. but no need to modify the entire build process to
# adopt changes in master.
build_esp32_master:
needs: pre_build
if: ${{ needs.pre_build.outputs.should_skip != 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- esp32
ip_version:
- ipv4
- ipv6
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install python
uses: actions/setup-python@v2
with:
# XXX install python 3.8 because the official python package
# segfaults when installing modules in the runner.
#
# 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
# 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
# 2020-09-03T02:30:09.0414254Z ##[error]Process completed with exit code 139.
#
# possibly related issue:
# https://github.com/actions/virtual-environments/issues/159
python-version: 3.8
- name: Install dependencies
run: |
sudo apt-get install \
bison \
ccache \
flex \
gcc \
git \
gperf \
libffi-dev \
libncurses-dev \
libssl-dev \
make \
wget
- name: Set environment variables
id: set_env
run: |
SDK_NAME="esp-idf"
GCC_PREFIX="xtensa-${{ matrix.target }}-elf"
GCC_FILE="${GCC_PREFIX}-gcc"
TOOLCHAIN_DIR="${HOME}/.espressif/tools"
TOOLCHAIN_VERSION="esp-2021r2-8.4.0"
REPO_DIR=`pwd`
EXAMPLE_DIR="${REPO_DIR}/examples"
DISTFILE_DIR="${HOME}/distfiles"
__PROJECT_PATH=`pwd`
# XXX actions/checkout does not allow to checkout a repository other
# than under __PROJECT_PATH
IDF_PATH="${__PROJECT_PATH}/idf"
echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV}
echo "IDF_TARGET=${{ matrix.target }}" >> ${GITHUB_ENV}
# cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR
echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}"
# XXX prefix all the environment variables with `__PROJECT_` to avoid pollution
echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV}
echo "__PROJECT_GCC_FILE=${GCC_FILE}" >> ${GITHUB_ENV}
echo "__PROJECT_GCC_PREFIX=${GCC_PREFIX}" >> ${GITHUB_ENV}
echo "__PROJECT_SDK_NAME=${SDK_NAME}" >> ${GITHUB_ENV}
echo "__PROJECT_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}" >> ${GITHUB_ENV}
echo "__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}" >> ${GITHUB_ENV}
echo "__PROJECT_TOOLCHAIN_VERSION=${TOOLCHAIN_VERSION}" >> ${GITHUB_ENV}
echo "__PROJECT_DISTFILE_DIR=${DISTFILE_DIR}" >> ${GITHUB_ENV}
echo "__PROJECT_PATH=${__PROJECT_PATH}" >> ${GITHUB_ENV}
echo "__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}" >> ${GITHUB_ENV}
echo "__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}" >> ${GITHUB_ENV}
- name: Checkout the SDK
uses: actions/checkout@v2
with:
repository: espressif/esp-idf
path: idf
submodules: recursive
ref: master
- name: Fixup the SDK
run: |
# XXX workaround removed option, --no-site-packages, from virtualenv. should
# be removed when the following commit is merged
# https://github.com/espressif/esp-idf/commit/7a18f02acd7005f7c56e62175a8d1968a1a9019d
sed -i -e "s/'--no-site-packages',//" ${IDF_PATH}/tools/idf_tools.py
- name: Run idf_tools.py install
run: |
${IDF_PATH}/tools/idf_tools.py install
- name: Run idf_tools.py install-python-env
run: |
${IDF_PATH}/tools/idf_tools.py install-python-env
- name: Build (idf.py)
run: |
IGNORE_FILE="ci-ignore"
. ${IDF_PATH}/export.sh
# XXX share cache between examples.
# see "Compiling In Different Directories" in ccache(1)
# | | 4.0.1 | master |
# |----------------------------------------|---------|---------|
# | without ccache | 33m 42s | 50m 27s |
# | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s |
export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}"
export CCACHE_NOHASHDIR=true
cd "${__PROJECT_EXAMPLE_DIR}"
for i in $(ls -d *); do
if [ ! -e "${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}" ]; then
cd "${__PROJECT_EXAMPLE_DIR}/${i}"
# FIXME Remove this workaround when esp-idf issue #7621 will be fixed
echo "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n" >> sdkconfig.defaults
echo "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n" >> sdkconfig.defaults
if [ ${{ matrix.ip_version }} == "ipv6" ]; then
echo "CONFIG_LWIP_IPV6=y" >> sdkconfig.defaults
fi
echo "Building ${i}..."
idf.py --ccache build
fi
done
build_esp32_v4_x:
needs: pre_build
if: ${{ needs.pre_build.outputs.should_skip != 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
build_method:
- idf
branch:
# for supported versions by espressif, see:
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html
#
# see issue #2
# - v4.1.2
- v4.2.3
- v4.3.2
- v4.4.1
target:
- esp32
ip_version:
- ipv4
- ipv6
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install python
uses: actions/setup-python@v2
with:
# XXX install python 3.8 because the official python package
# segfaults when installing modules in the runner.
#
# 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
# 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
# 2020-09-03T02:30:09.0414254Z ##[error]Process completed with exit code 139.
#
# possibly related issue:
# https://github.com/actions/virtual-environments/issues/159
python-version: 3.8
- name: Install dependencies
run: |
sudo apt-get install \
bison \
ccache \
flex \
gcc \
git \
gperf \
libffi-dev \
libncurses-dev \
libssl-dev \
make \
wget
- name: Set environment variables
id: set_env
run: |
SDK_NAME="esp-idf"
GCC_PREFIX="xtensa-${{ matrix.target }}-elf"
GCC_FILE="${GCC_PREFIX}-gcc"
TOOLCHAIN_DIR="${HOME}/.espressif/tools"
case "${{ matrix.branch }}" in
v4.0.*)
TOOLCHAIN_VERSION="esp-2020r3-8.4.0"
;;
v4.1.*)
TOOLCHAIN_VERSION="esp-2020r3-8.4.0"
;;
v4.2.*)
TOOLCHAIN_VERSION="esp-2020r3-8.4.0"
;;
v4.3.*)
TOOLCHAIN_VERSION="esp-2021r2-8.4.0"
;;
v4.4.*)
TOOLCHAIN_VERSION="esp-2021r2-patch3-8.4.0"
;;
*)
echo "Unknown matrix.branch: ${{ matrix.branch }}"
exit 1
;;
esac
REPO_DIR=`pwd`
EXAMPLE_DIR="${REPO_DIR}/examples"
DISTFILE_DIR="${HOME}/distfiles"
__PROJECT_PATH=`pwd`
# XXX actions/checkout does not allow to checkout a repository other
# than under __PROJECT_PATH
IDF_PATH="${__PROJECT_PATH}/idf"
echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV}
echo "IDF_TARGET=${{ matrix.target }}" >> ${GITHUB_ENV}
# cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR
echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}"
# XXX prefix all the environment variables with `__PROJECT_` to avoid pollution
echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV}
echo "__PROJECT_GCC_FILE=${GCC_FILE}" >> ${GITHUB_ENV}
echo "__PROJECT_GCC_PREFIX=${GCC_PREFIX}" >> ${GITHUB_ENV}
echo "__PROJECT_SDK_NAME=${SDK_NAME}" >> ${GITHUB_ENV}
echo "__PROJECT_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}" >> ${GITHUB_ENV}
echo "__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}" >> ${GITHUB_ENV}
echo "__PROJECT_TOOLCHAIN_VERSION=${TOOLCHAIN_VERSION}" >> ${GITHUB_ENV}
echo "__PROJECT_DISTFILE_DIR=${DISTFILE_DIR}" >> ${GITHUB_ENV}
echo "__PROJECT_PATH=${__PROJECT_PATH}" >> ${GITHUB_ENV}
echo "__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}" >> ${GITHUB_ENV}
echo "__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}" >> ${GITHUB_ENV}
- name: Checkout the SDK
uses: actions/checkout@v2
with:
repository: espressif/esp-idf
path: idf
submodules: recursive
ref: ${{ matrix.branch }}
- name: Fixup the SDK
run: |
# XXX workaround removed option, --no-site-packages, from virtualenv. should
# be removed when the following commit is merged
# https://github.com/espressif/esp-idf/commit/7a18f02acd7005f7c56e62175a8d1968a1a9019d
sed -i -e "s/'--no-site-packages',//" ${IDF_PATH}/tools/idf_tools.py
- name: Cache esp-idf tools
# cache esp-idf tools. each tagged branch has fixed versions of tools.
# the versions do not change. the master is an exception as it is a
# moving target. do NOT cache tools if the branch is master.
uses: actions/cache@v2
id: cache-tools
with:
path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }}
key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools
- name: Run idf_tools.py install
if: ${{ steps.cache-tools.outputs.cache-hit != 'true' }}
run: |
${IDF_PATH}/tools/idf_tools.py install
- name: Run idf_tools.py install-python-env
run: |
${IDF_PATH}/tools/idf_tools.py install-python-env
- name: Build (idf.py)
if: ${{ matrix.build_method == 'idf' }}
run: |
IGNORE_FILE="ci-ignore"
. ${IDF_PATH}/export.sh
# XXX share cache between examples.
# see "Compiling In Different Directories" in ccache(1)
# | | 4.0.1 | master |
# |----------------------------------------|---------|---------|
# | without ccache | 33m 42s | 50m 27s |
# | CCACHE_BASEDIR and CCACHE_NOHASHDIR | 10m 41s | 16m 38s |
export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}"
export CCACHE_NOHASHDIR=true
cd "${__PROJECT_EXAMPLE_DIR}"
for i in $(ls -d *); do
if [ ! -e "${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}" ]; then
cd "${__PROJECT_EXAMPLE_DIR}/${i}"
# FIXME Remove this workaround when esp-idf issue #7621 will be fixed
echo "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n" >> sdkconfig.defaults
echo "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n" >> sdkconfig.defaults
if [ ${{ matrix.ip_version }} == "ipv6" ]; then
echo "CONFIG_LWIP_IPV6=y" >> sdkconfig.defaults
fi
echo "Building ${i}..."
idf.py --ccache build
fi
done
build_esp8266:
runs-on: ubuntu-latest
needs: pre_build
if: ${{ needs.pre_build.outputs.should_skip != 'true' }}
strategy:
fail-fast: false
matrix:
build_method:
# XXX build examples with make only
# idf.py in ESP8266 RTOS SDK is broken in many ways.
- make
branch:
- v3.4
- master
ip_version:
- ipv4
- ipv6
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
sudo apt-get install \
bison \
ccache \
flex \
gcc \
git \
gperf \
libffi-dev \
libncurses-dev \
libssl-dev \
make \
wget
- name: Set environment variables
id: set_env
run: |
SDK_NAME="ESP8266_RTOS_SDK"
GCC_PREFIX="xtensa-lx106-elf"
GCC_FILE="${GCC_PREFIX}-gcc"
TOOLCHAIN_DIR="${HOME}/.espressif/tools"
REPO_DIR=`pwd`
EXAMPLE_DIR="${REPO_DIR}/examples"
__PROJECT_PATH=`pwd`
__PROJECT_TOOLCHAIN_VERSION="esp-2020r3-49-gd5524c1-8.4.0"
# XXX actions/checkout does not allow to checkout a repository other
# than under __PROJECT_PATH
IDF_PATH="${__PROJECT_PATH}/idf"
echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV}
# cache-idf-tools needs PROJECT_TOOLCHAIN_DIR
echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}"
# XXX prefix all the environment variables with `__PROJECT_` to avoid pollution
echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV}
echo "__PROJECT_GCC_FILE=${GCC_FILE}" >> ${GITHUB_ENV}
echo "__PROJECT_GCC_PREFIX=${GCC_PREFIX}" >> ${GITHUB_ENV}
echo "__PROJECT_TOOLCHAIN_DIR=${TOOLCHAIN_DIR}" >> ${GITHUB_ENV}
echo "__PROJECT_PATH=${__PROJECT_PATH}" >> ${GITHUB_ENV}
echo "__PROJECT_BUILD_COMMAND=${__PROJECT_BUILD_COMMAND}" >> ${GITHUB_ENV}
echo "__PROJECT_BUILD_COMMAND_ARG=${__PROJECT_BUILD_COMMAND_ARG}" >> ${GITHUB_ENV}
echo "__PROJECT_TOOLCHAIN_VERSION=${__PROJECT_TOOLCHAIN_VERSION}" >> ${GITHUB_ENV}
- name: Checkout the SDK
uses: actions/checkout@v2
with:
repository: espressif/ESP8266_RTOS_SDK
path: idf
submodules: recursive
ref: ${{ matrix.branch }}
# XXX git.eclipse.org does not allow to fetch a commit. fetch all
# the commits.
fetch-depth: 0
- name: Install python requirements (pip)
run: |
python -m pip install --user -r ${IDF_PATH}/requirements.txt
- name: Cache toolchain
id: cache-idf-tools
if: ${{ matrix.branch != 'master' }}
uses: actions/cache@v2
with:
path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }}
key: ${{ runner.os }}-${{ matrix.branch }}-esp8266-cache-tools
- name: Install toolchain
if: ${{ steps.cache-idf-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }}
run: |
${IDF_PATH}/install.sh
- name: Setup ccache (make)
run: |
__PROJECT_CCACHE_BIN_DIR="${HOME}/ccache_bin"
mkdir -p "${__PROJECT_CCACHE_BIN_DIR}"
(cd "${__PROJECT_CCACHE_BIN_DIR}" && ln -s /usr/bin/ccache "${__PROJECT_GCC_FILE}")
echo "PATH=${__PROJECT_CCACHE_BIN_DIR}:$PATH:${__PROJECT_TOOLCHAIN_DIR}/${__PROJECT_GCC_PREFIX}/${__PROJECT_TOOLCHAIN_VERSION}/${__PROJECT_GCC_PREFIX}/bin" >> ${GITHUB_ENV}
echo "CCACHE_BASEDIR=${__PROJECT_EXAMPLE_DIR}" >> ${GITHUB_ENV}
echo "CCACHE_NOHASHDIR=true" >> ${GITHUB_ENV}
- name: Build (make)
if: ${{ matrix.build_method == 'make' }}
run: |
IGNORE_FILE="ci-ignore-esp8266"
cd "${__PROJECT_EXAMPLE_DIR}"
for i in $(ls -d *); do
if [ ! -e "${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}" ]; then
cd "${__PROJECT_EXAMPLE_DIR}/${i}"
# XXX ESP8266 RTOS SDK does not support
# `sdkconfig.defaults.TARGET_NAME` yet. create
# sdkconfig.defaults for ESP8266
echo "CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER=y" >> sdkconfig.defaults
if [ ${{ matrix.ip_version }} == "ipv6" ]; then
echo "CONFIG_LWIP_IPV6=y" >> sdkconfig.defaults
fi
echo "Building ${i}..."
make defconfig
make -j$(nproc)
fi
done
# XXX esp32s2 support was introduced in v4.2. older esp-idf does not install
# toolchains for esp32s2. thus, you cannot add `esp32s2` target to
# build_esp32_v4_x.
#
# this job can be removed when either one of the followings are met:
#
# * GitHub Actions supports "early exit" (successfully exit if a condition is
# true).
# * all branches in build_esp32_v4_x supports esp32s2
#
# additionally, esp32s2 build requires idf.py. make is not supported.
build_esp32s2_v4_x:
runs-on: ubuntu-latest
needs: pre_build
if: ${{ needs.pre_build.outputs.should_skip != 'true' }}
strategy:
fail-fast: false
matrix:
build_method:
- idf
branch:
# esp32s2 support since v4.2.x
- master
- v4.2.2
- v4.3.1
- v4.4.1
target:
- esp32s2
ip_version:
- ipv4
- ipv6
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
sudo apt-get install \
bison \
ccache \
flex \
gcc \
git \
gperf \
libffi-dev \
libncurses-dev \
libssl-dev \
make \
wget
- name: Set environment variables
id: set_env
run: |
TOOLCHAIN_DIR="${HOME}/.espressif/tools"
REPO_DIR=`pwd`
EXAMPLE_DIR="${REPO_DIR}/examples"
__PROJECT_PATH=`pwd`
IDF_PATH="${__PROJECT_PATH}/idf"
# cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR
echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}"
echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV}
echo "IDF_TARGET=${{ matrix.target }}" >> ${GITHUB_ENV}
echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV}
- name: Checkout the SDK
uses: actions/checkout@v2
with:
repository: espressif/esp-idf
path: idf
submodules: recursive
ref: ${{ matrix.branch }}
- name: Fixup the SDK
run: |
sed -i -e "s/'--no-site-packages',//" ${IDF_PATH}/tools/idf_tools.py
- name: Cache esp-idf tools
# cache esp-idf tools. each tagged branch has fixed versions of tools.
# the versions do not change. the master is an exception as it is a
# moving target. do NOT cache tools if the branch is master.
uses: actions/cache@v2
id: cache-tools
if: ${{ matrix.branch != 'master' }}
with:
path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }}
key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools
- name: Run install.sh
if: ${{ steps.cache-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }}
run: |
${IDF_PATH}/install.sh
- name: Run idf_tools.py install-python-env
run: |
${IDF_PATH}/tools/idf_tools.py install-python-env
- name: Build (idf.py)
if: ${{ matrix.build_method == 'idf' }}
run: |
IGNORE_FILE="ci-ignore"
. ${IDF_PATH}/export.sh
export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}"
export CCACHE_NOHASHDIR=true
cd "${__PROJECT_EXAMPLE_DIR}"
for i in $(ls -d *); do
if [ ! -e "${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}" ]; then
cd "${__PROJECT_EXAMPLE_DIR}/${i}"
echo "Building ${i}..."
# FIXME Remove this workaround when esp-idf issue #7621 will be fixed
echo "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n" >> sdkconfig.defaults
echo "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n" >> sdkconfig.defaults
if [ ${{ matrix.ip_version }} == "ipv6" ]; then
echo "CONFIG_LWIP_IPV6=y" >> sdkconfig.defaults
fi
idf.py --ccache build
fi
done
build_esp32c3_v4_x:
runs-on: ubuntu-latest
needs: pre_build
if: ${{ needs.pre_build.outputs.should_skip != 'true' }}
strategy:
fail-fast: false
matrix:
build_method:
- idf
branch:
# esp32c3 support was introduced in v4.3.
- master
- v4.3.2
target:
- esp32c3
ip_version:
- ipv4
- ipv6
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
sudo apt-get install \
bison \
ccache \
flex \
gcc \
git \
gperf \
libffi-dev \
libncurses-dev \
libssl-dev \
make \
wget
- name: Set environment variables
id: set_env
run: |
TOOLCHAIN_DIR="${HOME}/.espressif/tools"
REPO_DIR=`pwd`
EXAMPLE_DIR="${REPO_DIR}/examples"
__PROJECT_PATH=`pwd`
IDF_PATH="${__PROJECT_PATH}/idf"
# cache-idf-tools needs __PROJECT_TOOLCHAIN_DIR
echo "::set-output name=PROJECT_TOOLCHAIN_DIR::${TOOLCHAIN_DIR}"
echo "IDF_PATH=${IDF_PATH}" >> ${GITHUB_ENV}
echo "IDF_TARGET=${{ matrix.target }}" >> ${GITHUB_ENV}
echo "__PROJECT_EXAMPLE_DIR=${EXAMPLE_DIR}" >> ${GITHUB_ENV}
- name: Checkout the SDK
uses: actions/checkout@v2
with:
repository: espressif/esp-idf
path: idf
submodules: recursive
ref: ${{ matrix.branch }}
- name: Fixup the SDK
run: |
sed -i -e "s/'--no-site-packages',//" ${IDF_PATH}/tools/idf_tools.py
- name: Cache esp-idf tools
# cache esp-idf tools. each tagged branch has fixed versions of tools.
# the versions do not change. the master is an exception as it is a
# moving target. do NOT cache tools if the branch is master.
uses: actions/cache@v2
id: cache-tools
if: ${{ matrix.branch != 'master' }}
with:
path: ${{ steps.set_env.outputs.PROJECT_TOOLCHAIN_DIR }}
key: ${{ runner.os }}-${{ matrix.branch }}-${{ matrix.target }}-cache-tools-1
- name: Run install.sh
if: ${{ steps.cache-tools.outputs.cache-hit != 'true' || matrix.branch == 'master' }}
run: |
${IDF_PATH}/install.sh
- name: Run idf_tools.py install-python-env
run: |
${IDF_PATH}/tools/idf_tools.py install-python-env
- name: Build (idf.py)
if: ${{ matrix.build_method == 'idf' }}
run: |
IGNORE_FILE="ci-ignore"
. ${IDF_PATH}/export.sh
export CCACHE_BASEDIR="${__PROJECT_EXAMPLE_DIR}"
export CCACHE_NOHASHDIR=true
cd "${__PROJECT_EXAMPLE_DIR}"
for i in $(ls -d *); do
if [ ! -e "${__PROJECT_EXAMPLE_DIR}/${i}/${IGNORE_FILE}" ]; then
cd "${__PROJECT_EXAMPLE_DIR}/${i}"
echo "Building ${i}..."
# FIXME Remove this workaround when esp-idf issue #7621 will be fixed
echo "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n" >> sdkconfig.defaults
echo "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n" >> sdkconfig.defaults
if [ ${{ matrix.ip_version }} == "ipv6" ]; then
echo "CONFIG_LWIP_IPV6=y" >> sdkconfig.defaults
fi
idf.py --ccache build
fi
done
all_build:
# a meta job that requires all of the above so that repository
# admin can choose a single test name in "Require status checks to pass
# before merging". A trick obtained from:
#
# https://github.com/jazzband/pip-tools/issues/1085#issuecomment-619172509
name: All build
runs-on: ubuntu-latest
needs:
- build_esp32_master
- build_esp32_v4_x
- build_esp8266
- build_esp32s2_v4_x
- build_esp32c3_v4_x
steps:
- name:
run: |
echo "All builds finished"
================================================
FILE: .github/workflows/publish.yml
================================================
---
name: Push component to https://components.espressif.com
on:
push:
tags:
- v*
jobs:
upload_components:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: "recursive"
- name: Upload component to the component registry
uses: espressif/upload-components-ci-action@b78a19fa5424714997596d3ecffa634aef8ae20b
with:
name: "esp_wireguard"
version: ${{ github.ref_name }}
namespace: "trombik"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
================================================
FILE: .gitignore
================================================
*.swp
/examples/*/build
/examples/*/sdkconfig
/examples/*/sdkconfig.*
/examples/*/.vagrant/
================================================
FILE: CMakeLists.txt
================================================
idf_component_register(
SRCS "src/crypto.c"
"src/wireguard.c"
"src/wireguardif.c"
"src/wireguard-platform.c"
"src/crypto/refc/blake2s.c"
"src/crypto/refc/chacha20.c"
"src/crypto/refc/chacha20poly1305.c"
"src/crypto/refc/poly1305-donna.c"
"src/crypto/refc/x25519.c"
"src/esp_wireguard.c"
"src/nacl/crypto_scalarmult/curve25519/ref/smult.c"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src"
REQUIRES esp_netif lwip mbedtls)
if(${IDF_VERSION_MAJOR} STREQUAL 5)
set_source_files_properties(src/crypto/refc/x25519.c
PROPERTIES COMPILE_FLAGS
-Wno-error=stringop-overread)
endif()
================================================
FILE: Kconfig
================================================
menu "WireGuard"
choice WIREGUARD_ESP_ADAPTER_SELECTION
prompt "TCP/IP adapter to use"
default WIREGUARD_ESP_NETIF
config WIREGUARD_ESP_TCPIP_ADAPTER
bool "TCP/IP Adapter (pre esp-idf v4.1, ESP8266 RTOS SDK)"
config WIREGUARD_ESP_NETIF
bool "ESP-NETIF"
endchoice
config WIREGUARD_MAX_PEERS
int "Max number of peers"
default 1
config WIREGUARD_MAX_SRC_IPS
int "Max number of SRC IP addresses"
default 2
config MAX_INITIATIONS_PER_SECOND
int "Per device limit on accepting (valid) initiation requests"
default 2
help
Per device limit on accepting (valid) initiation requests - per peer.
choice WIREGUARD_x25519_IMPLEMENTATION
prompt "x25519 implementation to use"
default WIREGUARD_x25519_IMPLEMENTATION_DEFAULT
config WIREGUARD_x25519_IMPLEMENTATION_DEFAULT
bool "Default (originally from wireguard-lwip)"
config WIREGUARD_x25519_IMPLEMENTATION_NACL
bool "NaCL"
endchoice
endmenu
================================================
FILE: LICENSE
================================================
Copyright (c) 2021 Kenta Ida (fuga@fugafuga.org)
The original license is below:
Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
its contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Author: Daniel Hope <daniel.hope@smartalock.com>
================================================
FILE: README.md
================================================
# `esp_wireguard`, WireGuard Implementation for ESP-IDF
This is an implementation of the [WireGuard®](https://www.wireguard.com/)
for ESP-IDF, based on
[WireGuard Implementation for lwIP](https://github.com/smartalock/wireguard-lwip).
[](https://github.com/trombik/esp_wireguard/actions/workflows/build.yml)
## Status
The code is alpha.
A single tunnel to a WireGuard peer has been working.
## Supported ESP-IDF versions and targets
The following ESP-IDF versions are supported:
* `esp-idf` `master`
* `esp-idf` `v4.2.x`
* `esp-idf` `v4.3.x`
* `esp-idf` `v4.4.x`
* ESP8266 RTOS SDK `v3.4`
The following targets are supported:
* `esp32`
* `esp32s2`
* `esp32c3`
* `esp8266`
## Usage
In `menuconfig` under `WireGuard`, choose a TCP/IP adapter. The default is
`ESP-NETIF`. SDKs older than `esp-idf` `v4.1`, including ESP8266 RTOS SDK v3.4
requires `TCP/IP Adapter`.
Both peers must have synced time. The library does not sync time.
A working network interface is required.
Create WireGuard configuration, `wireguard_config_t`. Use
`ESP_WIREGUARD_CONFIG_DEFAULT` to initialize `wireguard_config_t` variable.
Create `wireguard_ctx_t`. Pass the variables to `esp_wireguard_init()`. Then,
call `esp_wireguard_connect()`. Call `esp_wireguard_disconnect()` to
disconnect from the peer (and destroy the WireGuard interface).
```c
#include <esp_wireguard.h>
esp_err_t err = ESP_FAIL;
wireguard_config_t wg_config = ESP_WIREGUARD_CONFIG_DEFAULT();
wg_config.private_key = CONFIG_WG_PRIVATE_KEY;
wg_config.listen_port = CONFIG_WG_LOCAL_PORT;
wg_config.public_key = CONFIG_WG_PEER_PUBLIC_KEY;
wg_config.allowed_ip = CONFIG_WG_LOCAL_IP_ADDRESS;
wg_config.allowed_ip_mask = CONFIG_WG_LOCAL_IP_NETMASK;
wg_config.endpoint = CONFIG_WG_PEER_ADDRESS;
wg_config.port = CONFIG_WG_PEER_PORT;
/* If the device is behind NAT or stateful firewall, set persistent_keepalive.
persistent_keepalive is disabled by default */
// wg_config.persistent_keepalive = 10;
wireguard_ctx_t ctx = {0};
err = esp_wireguard_init(&wg_config, &ctx);
/* start establishing the link. after this call, esp_wireguard start
establishing connection. */
err = esp_wireguard_connect(&ctx);
/* after some time, see if the link is up. note that it takes some time to
establish the link */
err = esp_wireguardif_peer_is_up(&ctx);
if (err == ESP_OK) {
/* the link is up */
else {
/* the link is not up */
}
/* do something */
err = esp_wireguard_disconnect(&ctx);
```
See examples at [examples](examples).
## IPv6 support
Enable `CONFIG_LWIP_IPV6` under `lwip` component in `menuconfig`.
IPv6 support is alpha and probably broken. See also Known issues.
## Driver configuration
The driver configuration is under `[Component config]` -> `[WireGuard]`.
Under `WIREGUARD_x25519_IMPLEMENTATION`, you may choose an implementation of
scalar multiplication. The default is
`WIREGUARD_x25519_IMPLEMENTATION_DEFAULT`, which is derived from
[WireGuard Implementation for lwIP](https://github.com/smartalock/wireguard-lwip).
`WIREGUARD_x25519_IMPLEMENTATION_NACL` uses
[crypto_scalarmult()](https://nacl.cr.yp.to/scalarmult.html) from NaCL. Note
that, with `WIREGUARD_x25519_IMPLEMENTATION_NACL`,
some stack sizes must be increased. In my test, 5KB for both
`CONFIG_LWIP_TCPIP_TASK_STACK_SIZE`, and `CONFIG_MAIN_TASK_STACK_SIZE` is
known to work on `ESP32-D0WD-V3`.
## Known issues
The implementation uses `LwIP` as TCP/IP protocol stack.
IPv6 support is not tested. Dual stack (IPv4 and IPv6) is not supported (see
Issue #5). The first address of `endpoint` is used to choose IPv4 or IPv6 as a
transport. The chosen transport must be available and usable.
The library assumes the interface is WiFi interface. Ethernet is not
supported.
Older `esp-idf` versions with `TCP/IP Adapter`, such as v4.1.x, should work,
but there are others issues, not directly related to the library.
## License
BSD 3-Clause "New" or "Revised" License (SPDX ID: BSD-3-Clause).
See [LICENSE](LICENSE) for details.
[src/nacl/crypto_scalarmult/curve25519/ref/smult.c] is Public domain.
## Authors
* Daniel Hope (daniel.hope@smartalock.com)
* Kenta Ida (fuga@fugafuga.org)
* Matthew Dempsky
* D. J. Bernstein
================================================
FILE: component.mk
================================================
COMPONENT_SRCDIRS = \
src \
src/crypto/refc
COMPONENT_ADD_INCLUDEDIRS = \
include
COMPONENT_PRIV_INCLUDEDIRS = \
src
COMPONENT_DEPENDS = lwip mbedtls
================================================
FILE: examples/demo/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(demo)
================================================
FILE: examples/demo/Makefile
================================================
PROJECT_NAME := demo
include $(IDF_PATH)/make/project.mk
================================================
FILE: examples/demo/README.md
================================================
# demo example
## What the example does
The example connects to a WireGuard server. When the link is up, the device
sends ICMP echo requests, and shows ping statistics. The ICMP session loops
forever.
The main task then disconnects from the peer, and re-connects to the peer.
## Requirements
* An ESP32 or ESP8266 development board
* WiFi network
* [`wireguard-tools`](https://github.com/WireGuard/wireguard-tools)
* A WireGuard server
## Generating keys
```console
wg genkey | tee private.key | wg pubkey > public.key
```
## Log
```console
I (100) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=1600ch ( 90124) map
I (141) esp_image: segment 1: paddr=00026034 vaddr=3ffb0000 size=03f0ch ( 16140) load
I (148) esp_image: segment 2: paddr=00029f48 vaddr=40080000 size=060d0h ( 24784) load
I (158) esp_image: segment 3: paddr=00030020 vaddr=400d0020 size=7691ch (485660) map
I (334) esp_image: segment 4: paddr=000a6944 vaddr=400860d0 size=0f14ch ( 61772) load
I (360) esp_image: segment 5: paddr=000b5a98 vaddr=50000000 size=00010h ( 16) load
I (371) boot: Loaded app from partition at offset 0x10000
I (371) boot: Disabling RNG early entropy source...
I (382) cpu_start: Pro cpu up.
I (382) cpu_start: Starting app cpu, entry point is 0x4008127c
0x4008127c: call_start_cpu1 at /usr/home/trombik/github/esp-idf/components/esp_system/port/cpu_start.c:150
I (0) cpu_start: App cpu up.
I (397) cpu_start: Pro cpu start user code
I (397) cpu_start: cpu freq: 160000000
I (397) cpu_start: Application information:
I (401) cpu_start: Project name: demo
I (406) cpu_start: App version: 4a3c45b
I (411) cpu_start: Compile time: Jan 6 2022 15:39:56
I (417) cpu_start: ELF file SHA256: 45cef6b78497cd9f...
I (423) cpu_start: ESP-IDF: v4.3.2
I (428) heap_init: Initializing. RAM available for dynamic allocation:
I (435) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (441) heap_init: At 3FFB8150 len 00027EB0 (159 KiB): DRAM
I (447) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (454) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (460) heap_init: At 4009521C len 0000ADE4 (43 KiB): IRAM
I (467) spi_flash: detected chip: generic
I (471) spi_flash: flash io: dio
W (475) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (489) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (602) wifi:wifi driver task: 3ffc1e00, prio:23, stack:6656, core=0
I (602) system_api: Base MAC address is not set
I (602) system_api: read default base MAC address from EFUSE
I (622) wifi:wifi firmware version: eb52264
I (622) wifi:wifi certification version: v7.0
I (622) wifi:config NVS flash: enabled
I (622) wifi:config nano formating: disabled
I (632) wifi:Init data frame dynamic rx buffer num: 32
I (632) wifi:Init management frame dynamic rx buffer num: 32
I (642) wifi:Init management short buffer num: 32
I (642) wifi:Init dynamic tx buffer num: 32
I (652) wifi:Init static rx buffer size: 1600
I (652) wifi:Init static rx buffer num: 10
I (662) wifi:Init dynamic rx buffer num: 32
I (662) wifi_init: rx ba win: 6
I (662) wifi_init: tcpip mbox: 32
I (672) wifi_init: udp mbox: 6
I (672) wifi_init: tcp mbox: 6
I (672) wifi_init: tcp tx win: 5744
I (682) wifi_init: tcp rx win: 5744
I (682) wifi_init: tcp mss: 1440
I (692) wifi_init: WiFi IRAM OP enabled
I (692) wifi_init: WiFi RX IRAM OP enabled
I (702) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
I (812) wifi:mode : sta (24:62:ab:ff:2f:d0)
I (812) wifi:enable tsf
I (822) wifi:new:<11,0>, old:<1,0>, ap:<255,255>, sta:<11,0>, prof:1
I (822) wifi:state: init -> auth (b0)
I (832) wifi:state: auth -> assoc (0)
I (832) wifi:state: assoc -> run (10)
I (852) wifi:connected with makers, aid = 2, channel 11, BW20, bssid = 18:c2:bf:d2:de:d8
I (852) wifi:security: WPA2-PSK, phy: bg, rssi: -85
I (862) wifi:pm start, type: 1
I (952) wifi:AP's beacon interval = 102400 us, DTIM period = 2
I (2082) esp_netif_handlers: sta ip: 192.168.99.52, mask: 255.255.255.0, gw: 192.168.99.254
I (2082) demo: got ip:192.168.99.52
I (2082) demo: Connected to ap SSID:makers
I (2092) sync_time: Initializing SNTP
I (2092) sync_time: Waiting for system time to be set... (1/20)
I (3792) sync_time: Time synced
I (4102) demo: The current date/time in New York is: Thu Jan 6 03:40:19 2022
I (4102) demo: Initializing WireGuard.
I (4102) demo: Connecting to the peer.
I (4102) esp_wireguard: allowed_ip: 192.168.4.58
I (4162) esp_wireguard: Peer: 192.168.99.19 (192.168.99.19:12912)
I (4212) esp_wireguard: Connecting to 192.168.99.19:12912
I (5212) demo: Peer is down
I (6212) demo: Peer is down
I (7212) demo: Peer is down
I (8212) demo: Peer is up
I (8212) demo: Initializing ping...
I (8212) demo: ICMP echo target: 192.168.4.1
I (8222) demo: 64 bytes from 192.168.4.1 icmp_seq=1 ttl=255 time=6 ms
I (9222) demo: 64 bytes from 192.168.4.1 icmp_seq=2 ttl=255 time=9 ms
I (10222) demo: 64 bytes from 192.168.4.1 icmp_seq=3 ttl=255 time=7 ms
I (11222) demo: 64 bytes from 192.168.4.1 icmp_seq=4 ttl=255 time=6 ms
I (12212) demo: 64 bytes from 192.168.4.1 icmp_seq=5 ttl=255 time=3 ms
I (13222) demo: 64 bytes from 192.168.4.1 icmp_seq=6 ttl=255 time=7 ms
I (14222) demo: 64 bytes from 192.168.4.1 icmp_seq=7 ttl=255 time=8 ms
I (15212) demo: 64 bytes from 192.168.4.1 icmp_seq=8 ttl=255 time=4 ms
I (16212) demo: 64 bytes from 192.168.4.1 icmp_seq=9 ttl=255 time=2 ms
I (17222) demo: 64 bytes from 192.168.4.1 icmp_seq=10 ttl=255 time=12 ms
I (18212) demo: Disconnecting.
I (18212) demo: Disconnected.
I (19212) demo: From 192.168.4.1 icmp_seq=11 timeout
I (20212) demo: From 192.168.4.1 icmp_seq=12 timeout
I (21212) demo: From 192.168.4.1 icmp_seq=13 timeout
I (22212) demo: From 192.168.4.1 icmp_seq=14 timeout
I (23212) demo: From 192.168.4.1 icmp_seq=15 timeout
I (24212) demo: From 192.168.4.1 icmp_seq=16 timeout
I (25212) demo: From 192.168.4.1 icmp_seq=17 timeout
I (26212) demo: From 192.168.4.1 icmp_seq=18 timeout
I (27212) demo: From 192.168.4.1 icmp_seq=19 timeout
I (28212) demo: From 192.168.4.1 icmp_seq=20 timeout
I (28212) demo: Connecting.
I (28212) esp_wireguard: allowed_ip: 192.168.4.58
I (28262) esp_wireguard: Peer: 192.168.99.19 (192.168.99.19:12912)
I (28312) esp_wireguard: Connecting to 192.168.99.19:12912
I (29212) demo: From 192.168.4.1 icmp_seq=21 timeout
I (30212) demo: From 192.168.4.1 icmp_seq=22 timeout
I (31212) demo: From 192.168.4.1 icmp_seq=23 timeout
I (32212) demo: From 192.168.4.1 icmp_seq=24 timeout
I (33212) demo: From 192.168.4.1 icmp_seq=25 timeout
I (34312) demo: Peer is up
I (35132) demo: From 192.168.4.1 icmp_seq=26 timeout
I (35132) demo: 64 bytes from 192.168.4.1 icmp_seq=27 ttl=255 time=3 ms
I (35212) demo: 64 bytes from 192.168.4.1 icmp_seq=28 ttl=255 time=5 ms
I (36212) demo: 64 bytes from 192.168.4.1 icmp_seq=29 ttl=255 time=3 ms
I (37222) demo: 64 bytes from 192.168.4.1 icmp_seq=30 ttl=255 time=6 ms
I (38212) demo: 64 bytes from 192.168.4.1 icmp_seq=31 ttl=255 time=3 ms
I (39222) demo: 64 bytes from 192.168.4.1 icmp_seq=32 ttl=255 time=5 ms
I (40222) demo: 64 bytes from 192.168.4.1 icmp_seq=33 ttl=255 time=13 ms
I (41222) demo: 64 bytes from 192.168.4.1 icmp_seq=34 ttl=255 time=5 ms
I (42222) demo: 64 bytes from 192.168.4.1 icmp_seq=35 ttl=255 time=7 ms
I (43222) demo: 64 bytes from 192.168.4.1 icmp_seq=36 ttl=255 time=8 ms
I (44232) demo: 64 bytes from 192.168.4.1 icmp_seq=37 ttl=255 time=18 ms
I (44312) demo: Disconnecting.
I (44312) demo: Disconnected.
I (46212) demo: From 192.168.4.1 icmp_seq=38 timeout
I (47212) demo: From 192.168.4.1 icmp_seq=39 timeout
I (48212) demo: From 192.168.4.1 icmp_seq=40 timeout
I (49212) demo: From 192.168.4.1 icmp_seq=41 timeout
I (50212) demo: From 192.168.4.1 icmp_seq=42 timeout
I (51212) demo: From 192.168.4.1 icmp_seq=43 timeout
I (52212) demo: From 192.168.4.1 icmp_seq=44 timeout
I (53212) demo: From 192.168.4.1 icmp_seq=45 timeout
I (54212) demo: From 192.168.4.1 icmp_seq=46 timeout
I (54312) demo: Connecting.
I (54312) esp_wireguard: allowed_ip: 192.168.4.58
I (54362) esp_wireguard: Peer: 192.168.99.19 (192.168.99.19:12912)
I (54412) esp_wireguard: Connecting to 192.168.99.19:12912
I (55212) demo: From 192.168.4.1 icmp_seq=47 timeout
I (56212) demo: From 192.168.4.1 icmp_seq=48 timeout
I (57212) demo: From 192.168.4.1 icmp_seq=49 timeout
I (58212) demo: From 192.168.4.1 icmp_seq=50 timeout
I (59212) demo: From 192.168.4.1 icmp_seq=51 timeout
I (60212) demo: From 192.168.4.1 icmp_seq=52 timeout
I (61212) demo: From 192.168.4.1 icmp_seq=53 timeout
I (61412) demo: Peer is up
I (62272) demo: From 192.168.4.1 icmp_seq=54 timeout
I (62272) demo: 64 bytes from 192.168.4.1 icmp_seq=55 ttl=255 time=4 ms
I (63212) demo: 64 bytes from 192.168.4.1 icmp_seq=56 ttl=255 time=4 ms
I (64212) demo: 64 bytes from 192.168.4.1 icmp_seq=57 ttl=255 time=3 ms
I (65222) demo: 64 bytes from 192.168.4.1 icmp_seq=58 ttl=255 time=5 ms
I (66212) demo: 64 bytes from 192.168.4.1 icmp_seq=59 ttl=255 time=3 ms
I (67212) demo: 64 bytes from 192.168.4.1 icmp_seq=60 ttl=255 time=3 ms
I (68212) demo: 64 bytes from 192.168.4.1 icmp_seq=61 ttl=255 time=3 ms
I (69212) demo: 64 bytes from 192.168.4.1 icmp_seq=62 ttl=255 time=3 ms
```
================================================
FILE: examples/demo/Vagrantfile
================================================
# A vagrant box for testing. The VM is configured to use a brigde network,
# i.e. the VM is attached to the same network of the host OS. The interface
# uses DHCP.
#
# How to test the example:
#
# Make sure the follwings are installed on local machine:
#
# * `vagrant`
# * `virtualbox`
#
# Boot the VM. To boot the VM, run:
# > vagrant up
#
# At the initial boot, `vagrant` downloads my VM image (~700MB).
#
# Login to the server. To login to the VM, run:
# > vagrant ssh
#
# sudo requires no password.
#
# See the IP address of the server.
# > ifconfig em1
# em1: flags=808843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF4> mtu 1500
# lladdr 08:00:27:02:ba:ab
# index 2 priority 0 llprio 3
# groups: egress
# media: Ethernet autoselect (1000baseT full-duplex)
# status: active
# inet 192.168.99.25 netmask 0xffffff00 broadcast 192.168.99.255
#
# in this case, the IP address of the server is `192.168.99.25`.
#
# Ensure there is no `sdkconfig`. Delete it if there is.
#
# Configure the example by running `idf.py menuconfig`. You must change at
# least the followings:
#
# * ESP_WIFI_SSID
# * ESP_WIFI_PASSWORD
# * WG_PEER_ADDRESS
#
# Change `ESP_WIFI_SSID` and `ESP_WIFI_PASSWORD` to your SSID and password.
# Use the IP adddress of the server for `WG_PEER_ADDRESS`.
#
# Additionally, modify the maximum log verbosity. Select [Component config] ->
# [Log output] -> [Maximum log verbosity], and choose `Debug`.
#
# Flash the example by running `idf.py flash monitor`.
#
# Below is the default configuration of the example.
#
# client secret key: IsvT72MAXzA8EtV0FSD1QT59B4x0oe6Uea5rd/dDzhE=
# client public key: uyCfLulk5l7Bv/yCJ0nm1J3VL71YU4LISK/EHhwe43g=
#
# server secret key: iN8Rsdc10MFjkeqJ352OvtoMhkG5AFZWc/k4cS9odHM=
# server public key: FjrsQ/HD1Q8fUlFILIasDlOuajMeZov4NGqMJpkswiw=
#
# preshared key: 0/2H97Sd5EJ9LAAAYUglVjPYv7ihNIm/ziuv6BtSI50=
#
# wg(4) network: 192.168.4.0/24
# IP address of the server: 192.168.4.254
# allowed IP address of the client: 192.168.4.58
# the server port: 12912
#
# Other useful commands for the test:
#
# for details of wg(4) interface, run:
# ifconfig wg0
#
# to destroy wg(4), run:
# ifconfig wg0 destroy
#
# to create wg(4), run:
# sh /etc/netstart wg0
#
# to see packets from the client to the server, run:
# tcpdump -ni em1 host $ip.add.re.ss
#
# replace $ip.add.re.ss with the client IP address.
#
# to see decrypted packets, run:
# tcpdump -ni wg0
#
# to see debug log from wg(4), run:
# tail -f /var/log/messages
Vagrant.configure("2") do |config|
config.vm.box = "trombik/ansible-openbsd-7.1-amd64"
config.vm.network "public_network"
config.vm.provision "shell", inline: <<-SHELL
rcctl enable ntpd
rcctl start ntpd
touch /etc/hostname.wg0
chmod 600 /etc/hostname.wg0
echo "debug" >> /etc/hostname.wg0
echo "wgkey iN8Rsdc10MFjkeqJ352OvtoMhkG5AFZWc/k4cS9odHM= wgport 12912" >> /etc/hostname.wg0
echo "inet 192.168.4.254 255.255.255.0" >> /etc/hostname.wg0
echo "wgpeer uyCfLulk5l7Bv/yCJ0nm1J3VL71YU4LISK/EHhwe43g= wgaip 192.168.4.58/32 wgpsk 0/2H97Sd5EJ9LAAAYUglVjPYv7ihNIm/ziuv6BtSI50=" >> /etc/hostname.wg0
sh /etc/netstart wg0 start
SHELL
end
================================================
FILE: examples/demo/main/CMakeLists.txt
================================================
idf_component_register(SRCS "main.c" "sync_time.c"
INCLUDE_DIRS "."
REQUIRES esp_wifi lwip esp_wireguard nvs_flash)
================================================
FILE: examples/demo/main/Kconfig.projbuild
================================================
menu "Example Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config ESP_MAXIMUM_RETRY
int "Maximum retry"
default 5
help
Set the Maximum retry to avoid station reconnecting to the AP
unlimited when the AP is really inexistent.
config WG_PRIVATE_KEY
string "Wireguard Private Key"
default "IsvT72MAXzA8EtV0FSD1QT59B4x0oe6Uea5rd/dDzhE="
help
Private key of the WireGuard device.
config WG_LOCAL_IP_ADDRESS
string "Wireguard local IP address"
default "192.168.4.58"
help
Local IP address of the WireGuard device.
config WG_LOCAL_IP_NETMASK
string "Wireguard local netmask"
default "255.255.255.0"
help
Netmask of the local network the WireGuard device belongs to.
config WG_LOCAL_PORT
int "Wireguard local port"
default 11010
help
Local port to listen.
config WG_PEER_PUBLIC_KEY
string "Wireguard remote peer public key"
default "FjrsQ/HD1Q8fUlFILIasDlOuajMeZov4NGqMJpkswiw="
help
Public key of the remote peer.
config WG_PRESHARED_KEY
string "Wireguard pre-shared symmetric key"
default "0/2H97Sd5EJ9LAAAYUglVjPYv7ihNIm/ziuv6BtSI50="
help
Public key of the remote peer.
config WG_PEER_ADDRESS
string "Wireguard remote peer address"
default "demo.wireguard.com"
help
Address of the remote peer.
config WG_PEER_PORT
int "Wireguard remote peer port"
default 12912
help
Port number of the remote peer.
config WG_PERSISTENT_KEEP_ALIVE
int "Interval to send an authenticated empty packet"
default 0
help
A seconds interval, between 1 and 65535 inclusive, of how often to
send an authenticated empty packet to the peer for the purpose of
keeping a stateful firewall or NAT mapping valid persistently
config EXAMPLE_PING_ADDRESS
string "Target IP address or name"
default "192.168.4.254"
help
Target IP address to send ICMP echo requests.
endmenu
================================================
FILE: examples/demo/main/component.mk
================================================
================================================
FILE: examples/demo/main/main.c
================================================
/* WireGuard demo example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <inttypes.h>
#include <time.h>
#include <sys/time.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <esp_event.h>
#include <esp_idf_version.h>
#include <esp_log.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <nvs_flash.h>
#include <lwip/netdb.h>
#include <ping/ping_sock.h>
#include <esp_wireguard.h>
#include "sync_time.h"
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY
#if defined(CONFIG_IDF_TARGET_ESP8266)
#include <esp_netif.h>
#elif ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0)
#include <tcpip_adapter.h>
#else
#include <esp_netif.h>
#endif
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const char *TAG = "demo";
static int s_retry_num = 0;
static wireguard_config_t wg_config = ESP_WIREGUARD_CONFIG_DEFAULT();
static esp_err_t wireguard_setup(wireguard_ctx_t* ctx)
{
esp_err_t err = ESP_FAIL;
ESP_LOGI(TAG, "Initializing WireGuard.");
wg_config.private_key = CONFIG_WG_PRIVATE_KEY;
wg_config.listen_port = CONFIG_WG_LOCAL_PORT;
wg_config.public_key = CONFIG_WG_PEER_PUBLIC_KEY;
if (strcmp(CONFIG_WG_PRESHARED_KEY, "") != 0) {
wg_config.preshared_key = CONFIG_WG_PRESHARED_KEY;
} else {
wg_config.preshared_key = NULL;
}
wg_config.allowed_ip = CONFIG_WG_LOCAL_IP_ADDRESS;
wg_config.allowed_ip_mask = CONFIG_WG_LOCAL_IP_NETMASK;
wg_config.endpoint = CONFIG_WG_PEER_ADDRESS;
wg_config.port = CONFIG_WG_PEER_PORT;
wg_config.persistent_keepalive = CONFIG_WG_PERSISTENT_KEEP_ALIVE;
err = esp_wireguard_init(&wg_config, ctx);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wireguard_init: %s", esp_err_to_name(err));
goto fail;
}
ESP_LOGI(TAG, "Connecting to the peer.");
err = esp_wireguard_connect(ctx);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wireguard_connect: %s", esp_err_to_name(err));
goto fail;
}
err = ESP_OK;
fail:
return err;
}
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
#ifdef CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER
static esp_err_t wifi_init_tcpip_adaptor(void)
{
esp_err_t err = ESP_FAIL;
s_wifi_event_group = xEventGroupCreate();
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS
},
};
/* Setting a password implies station will connect to all security modes including WEP/WPA.
* However these modes are deprecated and not advisable to be used. Incase your Access point
* doesn't support WPA2, these mode can be enabled by commenting below line */
if (strlen((char *)wifi_config.sta.password)) {
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s", EXAMPLE_ESP_WIFI_SSID);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s", EXAMPLE_ESP_WIFI_SSID);
err = ESP_FAIL;
goto fail;
} else {
ESP_LOGE(TAG, "Unknown event");
err = ESP_FAIL;
goto fail;
}
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
vEventGroupDelete(s_wifi_event_group);
err = ESP_OK;
fail:
return err;
}
#endif // CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER
#ifdef CONFIG_WIREGUARD_ESP_NETIF
static esp_err_t wifi_init_netif(void)
{
esp_err_t err = ESP_FAIL;
esp_netif_t *sta_netif;
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
/* Setting a password implies station will connect to all security modes including WEP/WPA.
* However these modes are deprecated and not advisable to be used. Incase your Access point
* doesn't support WPA2, these mode can be enabled by commenting below line */
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg = {
.capable = true,
.required = false
},
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "Connected to ap SSID:%s", EXAMPLE_ESP_WIFI_SSID);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s", EXAMPLE_ESP_WIFI_SSID);
err = ESP_FAIL;
goto fail;
} else {
ESP_LOGE(TAG, "Unknown event");
err = ESP_FAIL;
goto fail;
}
err = esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_event_handler_instance_unregister: %s", esp_err_to_name(err));
goto fail;
}
err = esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_event_handler_instance_unregister: %s", esp_err_to_name(err));
goto fail;
}
vEventGroupDelete(s_wifi_event_group);
err = ESP_OK;
fail:
return err;
}
#endif // CONFIG_WIREGUARD_ESP_NETIF
static esp_err_t wifi_init_sta(void)
{
#if defined(CONFIG_WIREGUARD_ESP_TCPIP_ADAPTER)
return wifi_init_tcpip_adaptor();
#endif
#if defined(CONFIG_WIREGUARD_ESP_NETIF)
return wifi_init_netif();
#endif
}
static void test_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
ESP_LOGI(TAG, "%" PRIu32 " bytes from %s icmp_seq=%" PRIu16 " ttl=%" PRIi8 " time=%" PRIu32 " ms",
recv_len, ipaddr_ntoa(&target_addr), seqno, ttl, elapsed_time);
}
static void test_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
ESP_LOGI(TAG, "From %s icmp_seq=%" PRIu16 " timeout", ipaddr_ntoa(&target_addr), seqno);
}
static void test_on_ping_end(esp_ping_handle_t hdl, void *args)
{
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
ESP_LOGI(TAG, "%" PRIu32 " packets transmitted, %" PRIu32 " received, time %" PRIu32 "ms", transmitted, received, total_time_ms);
}
void start_ping()
{
ESP_LOGI(TAG, "Initializing ping...");
/* convert URL to IP address */
ip_addr_t target_addr;
struct addrinfo *res = NULL;
struct addrinfo hint;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
ESP_ERROR_CHECK(lwip_getaddrinfo(CONFIG_EXAMPLE_PING_ADDRESS, NULL, &hint, &res) == 0 ? ESP_OK : ESP_FAIL);
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
lwip_freeaddrinfo(res);
ESP_LOGI(TAG, "ICMP echo target: %s", CONFIG_EXAMPLE_PING_ADDRESS);
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
ping_config.target_addr = target_addr; // target IP address
ping_config.count = ESP_PING_COUNT_INFINITE; // ping in infinite mode, esp_ping_stop can stop it
/* set callback functions */
esp_ping_callbacks_t cbs;
cbs.on_ping_success = test_on_ping_success;
cbs.on_ping_timeout = test_on_ping_timeout;
cbs.on_ping_end = test_on_ping_end;
cbs.cb_args = NULL;
esp_ping_handle_t ping;
ESP_ERROR_CHECK(esp_ping_new_session(&ping_config, &cbs, &ping));
esp_ping_start(ping);
}
void app_main(void)
{
esp_err_t err;
time_t now;
struct tm timeinfo;
char strftime_buf[64];
wireguard_ctx_t ctx = {0};
esp_log_level_set("esp_wireguard", ESP_LOG_DEBUG);
esp_log_level_set("wireguardif", ESP_LOG_DEBUG);
esp_log_level_set("wireguard", ESP_LOG_DEBUG);
err = nvs_flash_init();
#if defined(CONFIG_IDF_TARGET_ESP8266) && ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(3, 4, 0)
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
#else
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
#endif
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
err = wifi_init_sta();
if (err != ESP_OK) {
ESP_LOGE(TAG, "wifi_init_sta: %s", esp_err_to_name(err));
goto fail;
}
obtain_time();
time(&now);
setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1);
tzset();
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI(TAG, "The current date/time in New York is: %s", strftime_buf);
err = wireguard_setup(&ctx);
if (err != ESP_OK) {
ESP_LOGE(TAG, "wireguard_setup: %s", esp_err_to_name(err));
goto fail;
}
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
err = esp_wireguardif_peer_is_up(&ctx);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Peer is up");
break;
} else {
ESP_LOGI(TAG, "Peer is down");
}
}
start_ping();
while (1) {
vTaskDelay(1000 * 10 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Disconnecting.");
esp_wireguard_disconnect(&ctx);
ESP_LOGI(TAG, "Disconnected.");
vTaskDelay(1000 * 10 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Connecting.");
err = esp_wireguard_connect(&ctx);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wireguard_connect: %s", esp_err_to_name(err));
goto fail;
}
while (esp_wireguardif_peer_is_up(&ctx) != ESP_OK) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
ESP_LOGI(TAG, "Peer is up");
esp_wireguard_set_default(&ctx);
}
fail:
ESP_LOGE(TAG, "Halting due to error");
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
================================================
FILE: examples/demo/main/sync_time.c
================================================
#include <time.h>
#include <inttypes.h>
#include <esp_sntp.h>
#include <esp_log.h>
#include "sync_time.h"
#define TAG "sync_time"
static void time_sync_notification_cb(struct timeval *tv)
{
ESP_LOGI(TAG, "Time synced");
}
static void initialize_sntp(void)
{
ESP_LOGI(TAG, "Initializing SNTP");
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_set_time_sync_notification_cb(time_sync_notification_cb);
#ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH
sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);
#endif
sntp_init();
}
void obtain_time(void)
{
int retry = 0;
const int retry_count = 20;
initialize_sntp();
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%i/%i)", retry, retry_count);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
// vim: noexpandtab
================================================
FILE: examples/demo/main/sync_time.h
================================================
#if !defined(__SYNC_TIME__H__)
#define __SYNC_TIME__H__
void obtain_time(void);
#endif
================================================
FILE: idf_component.yml
================================================
---
version: "0.9.0"
description: WireGuard Implementation for ESP-IDF
url: https://github.com/trombik/esp_wireguard
license: BSD-3-Clause
================================================
FILE: include/esp_wireguard.h
================================================
/*
* Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#if !defined(__ESP_WIREGUARD__H__)
#define __ESP_WIREGUARD__H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <esp_err.h>
#include <lwip/netif.h>
#define ESP_WIREGUARD_CONFIG_DEFAULT() { \
.private_key = NULL, \
.listen_port = 0, \
.fw_mark = 0, \
.public_key = NULL, \
.preshared_key = NULL, \
.allowed_ip = NULL, \
.allowed_ip_mask = NULL, \
.endpoint = NULL, \
.port = 51820, \
.persistent_keepalive = 0, \
}
typedef struct {
/* interface config */
char* private_key; /**< a base64 private key generated by wg genkey. Required. */
int listen_port; /**< a 16-bit port for listening */
uint32_t fw_mark; /**< a 32-bit fwmark for outgoing packets */
/* peer config */
char* public_key; /**< a base64 public key calculated by wg pubkey from a private key. Required. */
char* preshared_key; /**< a base64 preshared key generated by wg genpsk. */
char* allowed_ip; /**< a local IP address. */
char* allowed_ip_mask; /**< a subnet mask of the local IP address. */
char* endpoint; /**< an endpoint IP address or hostname. */
int port; /**< a port number of remote endpoint. Default is 51820. */
int persistent_keepalive; /**< a seconds interval, between 1 and 65535 inclusive, of how often to send an
authenticated empty packet to the peer for the purpose of keeping a stateful
firewall or NAT mapping valid persistently. Set zero to disable the feature.
Default is zero. */
} wireguard_config_t;
typedef struct {
wireguard_config_t* config; /**< a pointer to wireguard config */
struct netif* netif; /**< a pointer to configured netif */
struct netif* netif_default; /**< a pointer to the default netif. */
} wireguard_ctx_t;
/**
* @brief Initialize WireGuard
*
* Call this function to initilize the context of WireGuard.
*
* Do not call this function multiple times.
*
* To connect to other peer, use `esp_wireguard_disconnect()`, and
* `esp_wireguard_init()` with a new configuration.
*
* @param config WireGuard configuration.
* @param[out] ctx Context of WireGuard.
*
* @return
* - ESP_OK: Successfully initilized WireGuard interface.
* - ESP_ERR_INVALID_ARG: given argument is invalid.
* - ESP_FAIL: Other error.
*/
esp_err_t esp_wireguard_init(wireguard_config_t *config, wireguard_ctx_t *ctx);
/**
* @brief Create a WireGuard interface and start establishing the connection
* to the peer.
*
* Call the funtion to start establishing the connection. Note that `ESP_OK`
* does not mean the connection is established. To see if the connection is
* established, or the peer is up, use `esp_wireguardif_peer_is_up()`.
*
* Do not call this function multiple times.
*
* @param ctx Context of WireGuard.
* @return
* - ESP_OK on success.
* - ESP_FAIL on failure.
*/
esp_err_t esp_wireguard_connect(wireguard_ctx_t *ctx);
/**
* @brief Set the default gateway to the peer.
* @param ctx Context of WireGuard
* @return
* - ESP_OK on success.
*/
esp_err_t esp_wireguard_set_default(wireguard_ctx_t *ctx);
/**
* @brief Test if the peer is up.
*/
esp_err_t esp_wireguardif_peer_is_up(wireguard_ctx_t *ctx);
/**
* @brief Disconnect from the peer
*
* @param ctx Context of WireGuard.
* @return
* - ESP_OK on success.
*/
esp_err_t esp_wireguard_disconnect(wireguard_ctx_t *ctx);
#ifdef __cplusplus
}
#endif
#endif
// vim: expandtab
================================================
FILE: src/crypto/refc/blake2s.c
================================================
// Taken from RFC7693 - https://tools.ietf.org/html/rfc7693
#include "blake2s.h"
#include "../../crypto.h"
// Cyclic right rotation.
#ifndef ROTR32
#define ROTR32(x, y) (((x) >> (y)) ^ ((x) << (32 - (y))))
#endif
// Mixing function G.
#define B2S_G(a, b, c, d, x, y) { \
v[a] = v[a] + v[b] + x; \
v[d] = ROTR32(v[d] ^ v[a], 16); \
v[c] = v[c] + v[d]; \
v[b] = ROTR32(v[b] ^ v[c], 12); \
v[a] = v[a] + v[b] + y; \
v[d] = ROTR32(v[d] ^ v[a], 8); \
v[c] = v[c] + v[d]; \
v[b] = ROTR32(v[b] ^ v[c], 7); }
// Initialization Vector.
static const uint32_t blake2s_iv[8] =
{
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
// Compression function. "last" flag indicates last block.
static void blake2s_compress(blake2s_ctx *ctx, int last)
{
const uint8_t sigma[10][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }
};
int i;
uint32_t v[16], m[16];
for (i = 0; i < 8; i++) { // init work variables
v[i] = ctx->h[i];
v[i + 8] = blake2s_iv[i];
}
v[12] ^= ctx->t[0]; // low 32 bits of offset
v[13] ^= ctx->t[1]; // high 32 bits
if (last) // last block flag set ?
v[14] = ~v[14];
for (i = 0; i < 16; i++) // get little-endian words
m[i] = U8TO32_LITTLE(&ctx->b[4 * i]);
for (i = 0; i < 10; i++) { // ten rounds
B2S_G( 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);
B2S_G( 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);
B2S_G( 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);
B2S_G( 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);
B2S_G( 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);
B2S_G( 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);
B2S_G( 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]);
B2S_G( 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]);
}
for( i = 0; i < 8; ++i )
ctx->h[i] ^= v[i] ^ v[i + 8];
}
// Initialize the hashing context "ctx" with optional key "key".
// 1 <= outlen <= 32 gives the digest size in bytes.
// Secret key (also <= 32 bytes) is optional (keylen = 0).
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
const void *key, size_t keylen) // (keylen=0: no key)
{
size_t i;
if (outlen == 0 || outlen > 32 || keylen > 32)
return -1; // illegal parameters
for (i = 0; i < 8; i++) // state, "param block"
ctx->h[i] = blake2s_iv[i];
ctx->h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen;
ctx->t[0] = 0; // input count low word
ctx->t[1] = 0; // input count high word
ctx->c = 0; // pointer within buffer
ctx->outlen = outlen;
for (i = keylen; i < 64; i++) // zero input block
ctx->b[i] = 0;
if (keylen > 0) {
blake2s_update(ctx, key, keylen);
ctx->c = 64; // at the end
}
return 0;
}
// Add "inlen" bytes from "in" into the hash.
void blake2s_update(blake2s_ctx *ctx,
const void *in, size_t inlen) // data bytes
{
size_t i;
for (i = 0; i < inlen; i++) {
if (ctx->c == 64) { // buffer full ?
ctx->t[0] += ctx->c; // add counters
if (ctx->t[0] < ctx->c) // carry overflow ?
ctx->t[1]++; // high word
blake2s_compress(ctx, 0); // compress (not last)
ctx->c = 0; // counter to zero
}
ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
}
}
// Generate the message digest (size given in init).
// Result placed in "out".
void blake2s_final(blake2s_ctx *ctx, void *out)
{
size_t i;
ctx->t[0] += ctx->c; // mark last block offset
if (ctx->t[0] < ctx->c) // carry overflow
ctx->t[1]++; // high word
while (ctx->c < 64) // fill up with zeros
ctx->b[ctx->c++] = 0;
blake2s_compress(ctx, 1); // final block flag = 1
// little endian convert and store
for (i = 0; i < ctx->outlen; i++) {
((uint8_t *) out)[i] =
(ctx->h[i >> 2] >> (8 * (i & 3))) & 0xFF;
}
}
// Convenience function for all-in-one computation.
int blake2s(void *out, size_t outlen,
const void *key, size_t keylen,
const void *in, size_t inlen)
{
blake2s_ctx ctx;
if (blake2s_init(&ctx, outlen, key, keylen))
return -1;
blake2s_update(&ctx, in, inlen);
blake2s_final(&ctx, out);
return 0;
}
================================================
FILE: src/crypto/refc/blake2s.h
================================================
// Taken from RFC7693 - https://tools.ietf.org/html/rfc7693
// BLAKE2s Hashing Context and API Prototypes
#ifndef _BLAKE2S_H
#define _BLAKE2S_H
#ifdef __cplusplus
extern "C" {
#endif
#define BLAKE2S_BLOCK_SIZE 64
#include <stdint.h>
#include <stddef.h>
// state context
typedef struct {
uint8_t b[64]; // input buffer
uint32_t h[8]; // chained state
uint32_t t[2]; // total number of bytes
size_t c; // pointer for b[]
size_t outlen; // digest size
} blake2s_ctx;
// Initialize the hashing context "ctx" with optional key "key".
// 1 <= outlen <= 32 gives the digest size in bytes.
// Secret key (also <= 32 bytes) is optional (keylen = 0).
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
const void *key, size_t keylen); // secret key
// Add "inlen" bytes from "in" into the hash.
void blake2s_update(blake2s_ctx *ctx, // context
const void *in, size_t inlen); // data to be hashed
// Generate the message digest (size given in init).
// Result placed in "out".
void blake2s_final(blake2s_ctx *ctx, void *out);
// All-in-one convenience function.
int blake2s(void *out, size_t outlen, // return buffer for digest
const void *key, size_t keylen, // optional secret key
const void *in, size_t inlen); // data to be hashed
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: src/crypto/refc/chacha20.c
================================================
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
// RFC7539 implementation of ChaCha20 with modified nonce size for WireGuard
// https://tools.ietf.org/html/rfc7539
// Adapted from https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c by D. J. Bernstein (Public Domain)
// HChaCha20 is described here: https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html
#include "chacha20.h"
#include <string.h>
#include <stdint.h>
#include "../../crypto.h"
// 2.3. The ChaCha20 Block Function
// The first four words (0-3) are constants: 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
static const uint32_t CHACHA20_CONSTANT_1 = 0x61707865;
static const uint32_t CHACHA20_CONSTANT_2 = 0x3320646e;
static const uint32_t CHACHA20_CONSTANT_3 = 0x79622d32;
static const uint32_t CHACHA20_CONSTANT_4 = 0x6b206574;
#define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n))))
#define PLUS(v,w) (U32V((v) + (w)))
#define PLUSONE(v) (PLUS((v),1))
// 2.1. The ChaCha Quarter Round
// 1. a += b; d ^= a; d <<<= 16;
// 2. c += d; b ^= c; b <<<= 12;
// 3. a += b; d ^= a; d <<<= 8;
// 4. c += d; b ^= c; b <<<= 7;
#define QUARTERROUND(a, b, c, d) \
a += b; d ^= a; d = ROTL32(d, 16); \
c += d; b ^= c; b = ROTL32(b, 12); \
a += b; d ^= a; d = ROTL32(d, 8); \
c += d; b ^= c; b = ROTL32(b, 7)
static inline void INNER_BLOCK(uint32_t *block) {
QUARTERROUND(block[0], block[4], block[ 8], block[12]); // column 0
QUARTERROUND(block[1], block[5], block[ 9], block[13]); // column 1
QUARTERROUND(block[2], block[6], block[10], block[14]); // column 2
QUARTERROUND(block[3], block[7], block[11], block[15]); // column 3
QUARTERROUND(block[0], block[5], block[10], block[15]); // diagonal 1
QUARTERROUND(block[1], block[6], block[11], block[12]); // diagonal 2
QUARTERROUND(block[2], block[7], block[ 8], block[13]); // diagonal 3
QUARTERROUND(block[3], block[4], block[ 9], block[14]); // diagonal 4
}
#define TWENTY_ROUNDS(x) ( \
INNER_BLOCK(x), \
INNER_BLOCK(x), \
INNER_BLOCK(x), \
INNER_BLOCK(x), \
INNER_BLOCK(x), \
INNER_BLOCK(x), \
INNER_BLOCK(x), \
INNER_BLOCK(x), \
INNER_BLOCK(x), \
INNER_BLOCK(x) \
)
// 2.3. The ChaCha20 Block Function
// chacha20_block(key, counter, nonce):
// state = constants | key | counter | nonce
// working_state = state
// for i=1 upto 10
// inner_block(working_state)
// end
// state += working_state
// return serialize(state)
// end
static void chacha20_block(struct chacha20_ctx *ctx, uint8_t *stream) {
uint32_t working_state[16];
int i;
for (i = 0; i < 16; ++i) {
working_state[i] = ctx->state[i];
}
TWENTY_ROUNDS(working_state);
for (i = 0; i < 16; ++i) {
U32TO8_LITTLE(stream + (4 * i), PLUS(working_state[i], ctx->state[i]));
}
}
void chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in, uint32_t len) {
uint8_t output[CHACHA20_BLOCK_SIZE];
int i;
if (len) {
for (;;) {
chacha20_block(ctx, output);
// Word 12 is a block counter
ctx->state[12] = PLUSONE(ctx->state[12]);
if (len <= 64) {
for (i = 0;i < len;++i) {
out[i] = in[i] ^ output[i];
}
return;
}
for (i = 0;i < 64;++i) {
out[i] = in[i] ^ output[i];
}
len -= 64;
out += 64;
in += 64;
}
}
}
// 2.3. The ChaCha20 Block Function
// The first four words (0-3) are constants: 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
// 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.
// Word 12 is a block counter. Since each block is 64-byte, a 32-bit word is enough for 256 gigabytes of data.
// Words 13-15 are a nonce, which should not be repeated for the same key.
// 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
void chacha20_init(struct chacha20_ctx *ctx, const uint8_t *key, const uint64_t nonce) {
ctx->state[0] = CHACHA20_CONSTANT_1;
ctx->state[1] = CHACHA20_CONSTANT_2;
ctx->state[2] = CHACHA20_CONSTANT_3;
ctx->state[3] = CHACHA20_CONSTANT_4;
ctx->state[4] = U8TO32_LITTLE(key + 0);
ctx->state[5] = U8TO32_LITTLE(key + 4);
ctx->state[6] = U8TO32_LITTLE(key + 8);
ctx->state[7] = U8TO32_LITTLE(key + 12);
ctx->state[8] = U8TO32_LITTLE(key + 16);
ctx->state[9] = U8TO32_LITTLE(key + 20);
ctx->state[10] = U8TO32_LITTLE(key + 24);
ctx->state[11] = U8TO32_LITTLE(key + 28);
ctx->state[12] = 0;
ctx->state[13] = 0;
ctx->state[14] = nonce & 0xFFFFFFFF;
ctx->state[15] = nonce >> 32;
}
// 2.2. HChaCha20
// HChaCha20 is initialized the same way as the ChaCha cipher, except that HChaCha20 uses a 128-bit nonce and has no counter.
// After initialization, proceed through the ChaCha rounds as usual.
// 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.
void hchacha20(uint8_t *out, const uint8_t *nonce, const uint8_t *key) {
uint32_t state[16];
state[0] = CHACHA20_CONSTANT_1;
state[1] = CHACHA20_CONSTANT_2;
state[2] = CHACHA20_CONSTANT_3;
state[3] = CHACHA20_CONSTANT_4;
state[4] = U8TO32_LITTLE(key + 0);
state[5] = U8TO32_LITTLE(key + 4);
state[6] = U8TO32_LITTLE(key + 8);
state[7] = U8TO32_LITTLE(key + 12);
state[8] = U8TO32_LITTLE(key + 16);
state[9] = U8TO32_LITTLE(key + 20);
state[10] = U8TO32_LITTLE(key + 24);
state[11] = U8TO32_LITTLE(key + 28);
state[12] = U8TO32_LITTLE(nonce + 0);
state[13] = U8TO32_LITTLE(nonce + 4);
state[14] = U8TO32_LITTLE(nonce + 8);
state[15] = U8TO32_LITTLE(nonce + 12);
TWENTY_ROUNDS(state);
// Concatenate first/last 128 bits into 256bit output (as little endian)
U32TO8_LITTLE(out + 0, state[0]);
U32TO8_LITTLE(out + 4, state[1]);
U32TO8_LITTLE(out + 8, state[2]);
U32TO8_LITTLE(out + 12, state[3]);
U32TO8_LITTLE(out + 16, state[12]);
U32TO8_LITTLE(out + 20, state[13]);
U32TO8_LITTLE(out + 24, state[14]);
U32TO8_LITTLE(out + 28, state[15]);
}
================================================
FILE: src/crypto/refc/chacha20.h
================================================
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
// RFC7539 implementation of ChaCha20 with modified nonce size for WireGuard
// https://tools.ietf.org/html/rfc7539
// Adapted from https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c by D. J. Bernstein (Public Domain)
// HChaCha20 is described here: https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html
#ifndef _CHACHA20_H_
#define _CHACHA20_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define CHACHA20_BLOCK_SIZE (64)
#define CHACHA20_KEY_SIZE (32)
struct chacha20_ctx {
uint32_t state[16];
};
void chacha20_init(struct chacha20_ctx *ctx, const uint8_t *key, const uint64_t nonce);
void chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in, uint32_t len);
void hchacha20(uint8_t *out, const uint8_t *nonce, const uint8_t *key);
#ifdef __cplusplus
}
#endif
#endif /* _CHACHA20_H_ */
================================================
FILE: src/crypto/refc/chacha20poly1305.c
================================================
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
// AEAD_CHACHA20_POLY1305 as described in https://tools.ietf.org/html/rfc7539
// AEAD_XChaCha20_Poly1305 as described in https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html
#include "chacha20poly1305.h"
#include "chacha20.h"
#include "poly1305-donna.h"
#include <stdlib.h>
#include <stdint.h>
#include "../../crypto.h"
#define POLY1305_KEY_SIZE 32
#define POLY1305_MAC_SIZE 16
static const uint8_t zero[CHACHA20_BLOCK_SIZE] = { 0 };
// 2.6. Generating the Poly1305 Key Using ChaCha20
static void generate_poly1305_key(struct poly1305_context *poly1305_state, struct chacha20_ctx *chacha20_state, const uint8_t *key, uint64_t nonce) {
uint8_t block[POLY1305_KEY_SIZE] = {0};
// The method is to call the block function with the following parameters:
// - The 256-bit session integrity key is used as the ChaCha20 key.
// - The block counter is set to zero.
// - The protocol will specify a 96-bit or 64-bit nonce
chacha20_init(chacha20_state, key, nonce);
// We take the first 256 bits or the serialized state, and use those as the one-time Poly1305 key
chacha20(chacha20_state, block, block, sizeof(block));
poly1305_init(poly1305_state, block);
crypto_zero(&block, sizeof(block));
}
// 2.8. AEAD Construction (Encryption)
void 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) {
struct poly1305_context poly1305_state;
struct chacha20_ctx chacha20_state;
uint8_t block[8];
size_t padded_len;
// First, a Poly1305 one-time key is generated from the 256-bit key and nonce using the procedure described in Section 2.6.
generate_poly1305_key(&poly1305_state, &chacha20_state, key, nonce);
// 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.
chacha20(&chacha20_state, dst, src, src_len);
// Finally, the Poly1305 function is called with the Poly1305 key calculated above, and a message constructed as a concatenation of the following:
// - The AAD
poly1305_update(&poly1305_state, ad, ad_len);
// - padding1 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16
padded_len = (ad_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes
poly1305_update(&poly1305_state, zero, padded_len - ad_len);
// - The ciphertext
poly1305_update(&poly1305_state, dst, src_len);
// - padding2 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16.
padded_len = (src_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes
poly1305_update(&poly1305_state, zero, padded_len - src_len);
// - The length of the additional data in octets (as a 64-bit little-endian integer)
U64TO8_LITTLE(block, (uint64_t)ad_len);
poly1305_update(&poly1305_state, block, sizeof(block));
// - The length of the ciphertext in octets (as a 64-bit little-endian integer).
U64TO8_LITTLE(block, (uint64_t)src_len);
poly1305_update(&poly1305_state, block, sizeof(block));
// The output from the AEAD is twofold:
// - A ciphertext of the same length as the plaintext. (above, output of chacha20 into dst)
// - A 128-bit tag, which is the output of the Poly1305 function. (append to dst)
poly1305_finish(&poly1305_state, dst + src_len);
// Make sure we leave nothing sensitive on the stack
crypto_zero(&chacha20_state, sizeof(chacha20_state));
crypto_zero(&block, sizeof(block));
}
// 2.8. AEAD Construction (Decryption)
bool 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) {
struct poly1305_context poly1305_state;
struct chacha20_ctx chacha20_state;
uint8_t block[8];
uint8_t mac[POLY1305_MAC_SIZE];
size_t padded_len;
int dst_len;
bool result = false;
// Decryption is similar [to encryption] with the following differences:
// - The roles of ciphertext and plaintext are reversed, so the ChaCha20 encryption function is applied to the ciphertext, producing the plaintext.
// - The Poly1305 function is still run on the AAD and the ciphertext, not the plaintext.
// - The calculated tag is bitwise compared to the received tag. The message is authenticated if and only if the tags match.
if (src_len >= POLY1305_MAC_SIZE) {
dst_len = src_len - POLY1305_MAC_SIZE;
// First, a Poly1305 one-time key is generated from the 256-bit key and nonce using the procedure described in Section 2.6.
generate_poly1305_key(&poly1305_state, &chacha20_state, key, nonce);
// Calculate the MAC before attempting decryption
// the Poly1305 function is called with the Poly1305 key calculated above, and a message constructed as a concatenation of the following:
// - The AAD
poly1305_update(&poly1305_state, ad, ad_len);
// - padding1 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16
padded_len = (ad_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes
poly1305_update(&poly1305_state, zero, padded_len - ad_len);
// - The ciphertext (note the Poly1305 function is still run on the AAD and the ciphertext, not the plaintext)
poly1305_update(&poly1305_state, src, dst_len);
// - padding2 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16.
padded_len = (dst_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes
poly1305_update(&poly1305_state, zero, padded_len - dst_len);
// - The length of the additional data in octets (as a 64-bit little-endian integer)
U64TO8_LITTLE(block, (uint64_t)ad_len);
poly1305_update(&poly1305_state, block, sizeof(block));
// - The length of the ciphertext in octets (as a 64-bit little-endian integer).
U64TO8_LITTLE(block, (uint64_t)dst_len);
poly1305_update(&poly1305_state, block, sizeof(block));
// The output from the AEAD is twofold:
// - A plaintext of the same length as the ciphertext. (below, output of chacha20 into dst)
// - A 128-bit tag, which is the output of the Poly1305 function. (into mac for checking against passed mac)
poly1305_finish(&poly1305_state, mac);
if (crypto_equal(mac, src + dst_len, POLY1305_MAC_SIZE)) {
// mac is correct - do the decryption
// 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.
chacha20(&chacha20_state, dst, src, dst_len);
result = true;
}
}
return result;
}
// AEAD_XChaCha20_Poly1305
// 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.
// The algorithm for XChaCha20-Poly1305 is as follows:
// 1. Calculate a subkey from the first 16 bytes of the nonce and the key, using HChaCha20 (Section 2.2).
// 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.
void 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) {
uint8_t subkey[CHACHA20_KEY_SIZE];
uint64_t new_nonce;
new_nonce = U8TO64_LITTLE(nonce + 16);
hchacha20(subkey, nonce, key);
chacha20poly1305_encrypt(dst, src, src_len, ad, ad_len, new_nonce, subkey);
crypto_zero(subkey, sizeof(subkey));
}
bool 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) {
uint8_t subkey[CHACHA20_KEY_SIZE];
uint64_t new_nonce;
bool result;
new_nonce = U8TO64_LITTLE(nonce + 16);
hchacha20(subkey, nonce, key);
result = chacha20poly1305_decrypt(dst, src, src_len, ad, ad_len, new_nonce, subkey);
crypto_zero(subkey, sizeof(subkey));
return result;
}
================================================
FILE: src/crypto/refc/chacha20poly1305.h
================================================
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
#ifndef _CHACHA20POLY1305_H_
#define _CHACHA20POLY1305_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
// 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.
// AEAD_CHACHA20_POLY1305 as described in https://tools.ietf.org/html/rfc7539
void 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);
bool 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);
// Xaead(key, nonce, plain text, auth text) XChaCha20Poly1305 AEAD, with a 24-byte random nonce, instantiated using HChaCha20 [6] and ChaCha20Poly1305.
// AEAD_XChaCha20_Poly1305 as described in https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html
void 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);
bool 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);
#ifdef __cplusplus
}
#endif
#endif /* _CHACHA20POLY1305_H_ */
================================================
FILE: src/crypto/refc/poly1305-donna-32.h
================================================
// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT
/*
poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition
*/
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_MSC_VER)
#define POLY1305_NOINLINE __declspec(noinline)
#elif defined(__GNUC__)
#define POLY1305_NOINLINE __attribute__((noinline))
#else
#define POLY1305_NOINLINE
#endif
#define poly1305_block_size 16
/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */
typedef struct poly1305_state_internal_t {
unsigned long r[5];
unsigned long h[5];
unsigned long pad[4];
size_t leftover;
unsigned char buffer[poly1305_block_size];
unsigned char final;
} poly1305_state_internal_t;
/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */
static unsigned long
U8TO32(const unsigned char *p) {
return
(((unsigned long)(p[0] & 0xff) ) |
((unsigned long)(p[1] & 0xff) << 8) |
((unsigned long)(p[2] & 0xff) << 16) |
((unsigned long)(p[3] & 0xff) << 24));
}
/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */
static void
U32TO8(unsigned char *p, unsigned long v) {
p[0] = (v ) & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
p[3] = (v >> 24) & 0xff;
}
void
poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
st->r[0] = (U8TO32(&key[ 0]) ) & 0x3ffffff;
st->r[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03;
st->r[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff;
st->r[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff;
st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;
/* h = 0 */
st->h[0] = 0;
st->h[1] = 0;
st->h[2] = 0;
st->h[3] = 0;
st->h[4] = 0;
/* save pad for later */
st->pad[0] = U8TO32(&key[16]);
st->pad[1] = U8TO32(&key[20]);
st->pad[2] = U8TO32(&key[24]);
st->pad[3] = U8TO32(&key[28]);
st->leftover = 0;
st->final = 0;
}
static void
poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {
const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */
unsigned long r0,r1,r2,r3,r4;
unsigned long s1,s2,s3,s4;
unsigned long h0,h1,h2,h3,h4;
unsigned long long d0,d1,d2,d3,d4;
unsigned long c;
r0 = st->r[0];
r1 = st->r[1];
r2 = st->r[2];
r3 = st->r[3];
r4 = st->r[4];
s1 = r1 * 5;
s2 = r2 * 5;
s3 = r3 * 5;
s4 = r4 * 5;
h0 = st->h[0];
h1 = st->h[1];
h2 = st->h[2];
h3 = st->h[3];
h4 = st->h[4];
while (bytes >= poly1305_block_size) {
/* h += m[i] */
h0 += (U8TO32(m+ 0) ) & 0x3ffffff;
h1 += (U8TO32(m+ 3) >> 2) & 0x3ffffff;
h2 += (U8TO32(m+ 6) >> 4) & 0x3ffffff;
h3 += (U8TO32(m+ 9) >> 6) & 0x3ffffff;
h4 += (U8TO32(m+12) >> 8) | hibit;
/* h *= r */
d0 = ((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);
d1 = ((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);
d2 = ((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);
d3 = ((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);
d4 = ((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);
/* (partial) h %= p */
c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff;
d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff;
d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff;
d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff;
d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff;
h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff;
h1 += c;
m += poly1305_block_size;
bytes -= poly1305_block_size;
}
st->h[0] = h0;
st->h[1] = h1;
st->h[2] = h2;
st->h[3] = h3;
st->h[4] = h4;
}
POLY1305_NOINLINE void
poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
unsigned long h0,h1,h2,h3,h4,c;
unsigned long g0,g1,g2,g3,g4;
unsigned long long f;
unsigned long mask;
/* process the remaining block */
if (st->leftover) {
size_t i = st->leftover;
st->buffer[i++] = 1;
for (; i < poly1305_block_size; i++)
st->buffer[i] = 0;
st->final = 1;
poly1305_blocks(st, st->buffer, poly1305_block_size);
}
/* fully carry h */
h0 = st->h[0];
h1 = st->h[1];
h2 = st->h[2];
h3 = st->h[3];
h4 = st->h[4];
c = h1 >> 26; h1 = h1 & 0x3ffffff;
h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
h1 += c;
/* compute h + -p */
g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
g4 = h4 + c - (1UL << 26);
/* select h if h < p, or h + -p if h >= p */
mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
g0 &= mask;
g1 &= mask;
g2 &= mask;
g3 &= mask;
g4 &= mask;
mask = ~mask;
h0 = (h0 & mask) | g0;
h1 = (h1 & mask) | g1;
h2 = (h2 & mask) | g2;
h3 = (h3 & mask) | g3;
h4 = (h4 & mask) | g4;
/* h = h % (2^128) */
h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
/* mac = (h + pad) % (2^128) */
f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f;
f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f;
f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f;
f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f;
U32TO8(mac + 0, h0);
U32TO8(mac + 4, h1);
U32TO8(mac + 8, h2);
U32TO8(mac + 12, h3);
/* zero out the state */
st->h[0] = 0;
st->h[1] = 0;
st->h[2] = 0;
st->h[3] = 0;
st->h[4] = 0;
st->r[0] = 0;
st->r[1] = 0;
st->r[2] = 0;
st->r[3] = 0;
st->r[4] = 0;
st->pad[0] = 0;
st->pad[1] = 0;
st->pad[2] = 0;
st->pad[3] = 0;
}
#ifdef __cplusplus
}
#endif
================================================
FILE: src/crypto/refc/poly1305-donna.c
================================================
// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT
#include "poly1305-donna.h"
#include "poly1305-donna-32.h"
void
poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
size_t i;
/* handle leftover */
if (st->leftover) {
size_t want = (poly1305_block_size - st->leftover);
if (want > bytes)
want = bytes;
for (i = 0; i < want; i++)
st->buffer[st->leftover + i] = m[i];
bytes -= want;
m += want;
st->leftover += want;
if (st->leftover < poly1305_block_size)
return;
poly1305_blocks(st, st->buffer, poly1305_block_size);
st->leftover = 0;
}
/* process full blocks */
if (bytes >= poly1305_block_size) {
size_t want = (bytes & ~(poly1305_block_size - 1));
poly1305_blocks(st, m, want);
m += want;
bytes -= want;
}
/* store leftover */
if (bytes) {
for (i = 0; i < bytes; i++)
st->buffer[st->leftover + i] = m[i];
st->leftover += bytes;
}
}
================================================
FILE: src/crypto/refc/poly1305-donna.h
================================================
// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT
#ifndef POLY1305_DONNA_H
#define POLY1305_DONNA_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
typedef struct poly1305_context {
size_t aligner;
unsigned char opaque[136];
} poly1305_context;
void poly1305_init(poly1305_context *ctx, const unsigned char key[32]);
void poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes);
void poly1305_finish(poly1305_context *ctx, unsigned char mac[16]);
#ifdef __cplusplus
}
#endif
#endif /* POLY1305_DONNA_H */
================================================
FILE: src/crypto/refc/x25519-license.txt
================================================
The MIT License (MIT)
Copyright (c) 2015-2016 Cryptography Research, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: src/crypto/refc/x25519.c
================================================
// Taken from https://sourceforge.net/p/strobe (MIT Licence)
/**
* @cond internal
* @file x25519.c
* @copyright
* Copyright (c) 2015-2016 Cryptography Research, Inc. \n
* Released under the MIT License. See LICENSE.txt for license information.
* @author Mike Hamburg
* @brief Key exchange and signatures based on X25519.
*/
#include <stdint.h>
#include "x25519.h"
//#include "strobe.h"
//#include "strobe_config.h"
// STROBE header replacement
#include <string.h>
#define X25519_WBITS 32
#define X25519_SUPPORT_SIGN 0
#define X25519_MEMCPY_PARAMS 1
#define X25519_USE_POWER_CHAIN 1
#if BYTE_ORDER == LITTLE_ENDIAN
static inline uint32_t eswap_letoh_32(uint32_t w) { return w; }
#else
#error "Fix eswap() on non-little-endian machine"
#endif
#if X25519_WBITS == 64
typedef uint64_t limb_t;
typedef __uint128_t dlimb_t;
typedef __int128_t sdlimb_t;
#define eswap_limb eswap_letoh_64
#define LIMB(x) x##ull
#elif X25519_WBITS == 32
typedef uint32_t limb_t;
typedef uint64_t dlimb_t;
typedef int64_t sdlimb_t;
#define eswap_limb eswap_letoh_32
#define LIMB(x) (uint32_t)(x##ull),(uint32_t)((x##ull)>>32)
#else
#error "Need to know X25519_WBITS"
#endif
#define NLIMBS (256/X25519_WBITS)
typedef limb_t fe[NLIMBS];
#if X25519_SUPPORT_SIGN
typedef limb_t scalar_t[NLIMBS];
static const limb_t MONTGOMERY_FACTOR = (limb_t)0xd2b51da312547e1bull;
static const scalar_t sc_p = {
LIMB(0x5812631a5cf5d3ed), LIMB(0x14def9dea2f79cd6),
LIMB(0x0000000000000000), LIMB(0x1000000000000000)
}, sc_r2 = {
LIMB(0xa40611e3449c0f01), LIMB(0xd00e1ba768859347),
LIMB(0xceec73d217f5be65), LIMB(0x0399411b7c309a3d)
};
#endif
static inline limb_t umaal(
limb_t *carry, limb_t acc, limb_t mand, limb_t mier
) {
dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry;
*carry = tmp >> X25519_WBITS;
return tmp;
}
/* These functions are implemented in terms of umaal on ARM */
static inline limb_t adc(limb_t *carry, limb_t acc, limb_t mand) {
dlimb_t total = (dlimb_t)*carry + acc + mand;
*carry = total>>X25519_WBITS;
return total;
}
static inline limb_t adc0(limb_t *carry, limb_t acc) {
dlimb_t total = (dlimb_t)*carry + acc;
*carry = total>>X25519_WBITS;
return total;
}
/* Precondition: carry is small.
* Invariant: result of propagate is < 2^255 + 1 word
* In particular, always less than 2p.
* Also, output x >= min(x,19)
*/
static void propagate(fe x, limb_t over) {
unsigned i;
over = x[NLIMBS-1]>>(X25519_WBITS-1) | over<<1;
x[NLIMBS-1] &= ~((limb_t)1<<(X25519_WBITS-1));
limb_t carry = over * 19;
for (i=0; i<NLIMBS; i++) {
x[i] = adc0(&carry, x[i]);
}
}
static void add(fe out, const fe a, const fe b) {
unsigned i;
limb_t carry = 0;
for (i=0; i<NLIMBS; i++) {
out[i] = adc(&carry, a[i], b[i]);
}
propagate(out,carry);
}
static void sub(fe out, const fe a, const fe b) {
unsigned i;
sdlimb_t carry = -38;
for (i=0; i<NLIMBS; i++) {
out[i] = carry = carry + a[i] - b[i];
carry >>= X25519_WBITS;
}
propagate(out,1+carry);
}
static void __attribute__((unused))
swapin(limb_t *x, const uint8_t *in) {
memcpy(x,in,sizeof(fe));
unsigned i;
for (i=0; i<NLIMBS; i++) {
x[i] = eswap_limb(x[i]);
}
}
static void __attribute__((unused))
swapout(uint8_t *out, limb_t *x) {
unsigned i;
for (i=0; i<NLIMBS; i++) {
x[i] = eswap_limb(x[i]);
}
memcpy(out,x,sizeof(fe));
}
static void mul(fe out, const fe a, const fe b, unsigned nb) {
/* GCC at least produces pretty decent asm for this, so don't need to have dedicated asm. */
limb_t accum[2*NLIMBS] = {0};
unsigned i,j;
limb_t carry2;
for (i=0; i<nb; i++) {
carry2 = 0;
limb_t mand = b[i];
for (j=0; j<NLIMBS; j++) {
accum[i+j] = umaal(&carry2, accum[i+j], mand, a[j]);
}
accum[i+j] = carry2;
}
carry2 = 0;
const limb_t mand = 38;
for (j=0; j<NLIMBS; j++) {
out[j] = umaal(&carry2, accum[j], mand, accum[j+NLIMBS]);
}
propagate(out,carry2);
}
static void sqr(fe out, const fe a) { mul(out,a,a,NLIMBS); }
static void mul1(fe out, const fe a) { mul(out,a,out,NLIMBS); }
static void sqr1(fe a) { mul1(a,a); }
static void condswap(limb_t a[2*NLIMBS], limb_t b[2*NLIMBS], limb_t doswap) {
unsigned i;
for (i=0; i<2*NLIMBS; i++) {
limb_t xor = (a[i]^b[i]) & doswap;
a[i] ^= xor; b[i] ^= xor;
}
}
static limb_t canon(fe x) {
/* Canonicalize a field element x, reducing it to the least residue
* which is congruent to it mod 2^255-19.
*
* Precondition: x < 2^255 + 1 word
*/
/* First, add 19. */
unsigned i;
limb_t carry0 = 19;
for (i=0; i<NLIMBS; i++) {
x[i] = adc0(&carry0, x[i]);
}
propagate(x,carry0);
/* Here, 19 <= x2 < 2^255
*
* This is because we added 19, so before propagate it can't be less than 19.
* After propagate, it still can't be less than 19, because if propagate does
* anything it adds 19.
*
* We know that the high bit must be clear, because either the input was
* ~ 2^255 + one word + 19 (in which case it propagates to at most 2 words)
* or it was < 2^255.
*
* So now, if we subtract 19, we will get back to something in [0,2^255-19).
*/
sdlimb_t carry = -19;
limb_t res = 0;
for (i=0; i<NLIMBS; i++) {
res |= x[i] = carry += x[i];
carry >>= X25519_WBITS;
}
return ((dlimb_t)res - 1) >> X25519_WBITS;
}
static const limb_t a24[1]={121665};
static void ladder_part1(fe xs[5]) {
limb_t *x2 = xs[0], *z2=xs[1],*x3=xs[2],*z3=xs[3],*t1=xs[4];
add(t1,x2,z2); // t1 = A
sub(z2,x2,z2); // z2 = B
add(x2,x3,z3); // x2 = C
sub(z3,x3,z3); // z3 = D
mul1(z3,t1); // z3 = DA
mul1(x2,z2); // x3 = BC
add(x3,z3,x2); // x3 = DA+CB
sub(z3,z3,x2); // z3 = DA-CB
sqr1(t1); // t1 = AA
sqr1(z2); // z2 = BB
sub(x2,t1,z2); // x2 = E = AA-BB
mul(z2,x2,a24,sizeof(a24)/sizeof(a24[0])); // z2 = E*a24
add(z2,z2,t1); // z2 = E*a24 + AA
}
static void ladder_part2(fe xs[5], const fe x1) {
limb_t *x2 = xs[0], *z2=xs[1],*x3=xs[2],*z3=xs[3],*t1=xs[4];
sqr1(z3); // z3 = (DA-CB)^2
mul1(z3,x1); // z3 = x1 * (DA-CB)^2
sqr1(x3); // x3 = (DA+CB)^2
mul1(z2,x2); // z2 = AA*(E*a24+AA)
sub(x2,t1,x2); // x2 = BB again
mul1(x2,t1); // x2 = AA*BB
}
static void x25519_core(fe xs[5], const uint8_t scalar[X25519_BYTES], const uint8_t *x1, int clamp) {
int i;
#if X25519_MEMCPY_PARAMS
fe x1i;
swapin(x1i,x1);
x1 = (const uint8_t *)x1;
#endif
limb_t swap = 0;
limb_t *x2 = xs[0],*x3=xs[2],*z3=xs[3];
memset(xs,0,4*sizeof(fe));
x2[0] = z3[0] = 1;
memcpy(x3,x1,sizeof(fe));
for (i=255; i>=0; i--) {
uint8_t bytei = scalar[i/8];
if (clamp) {
if (i/8 == 0) {
bytei &= ~7;
} else if (i/8 == X25519_BYTES-1) {
bytei &= 0x7F;
bytei |= 0x40;
}
}
limb_t doswap = -(limb_t)((bytei>>(i%8)) & 1);
condswap(x2,x3,swap^doswap);
swap = doswap;
ladder_part1(xs);
ladder_part2(xs,(const limb_t *)x1);
}
condswap(x2,x3,swap);
}
int x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], const uint8_t x1[X25519_BYTES], int clamp) {
fe xs[5];
x25519_core(xs,scalar,x1,clamp);
/* Precomputed inversion chain */
limb_t *x2 = xs[0], *z2=xs[1], *z3=xs[3];
int i;
limb_t *prev = z2;
#if X25519_USE_POWER_CHAIN
static const struct { uint8_t a,c,n; } steps[13] = {
{2,1,1 },
{2,1,1 },
{4,2,3 },
{2,4,6 },
{3,1,1 },
{3,2,12 },
{4,3,25 },
{2,3,25 },
{2,4,50 },
{3,2,125},
{3,1,2 },
{3,1,2 },
{3,1,1 }
};
for (i=0; i<13; i++) {
int j;
limb_t *a = xs[steps[i].a];
for (j=steps[i].n; j>0; j--) {
sqr(a, prev);
prev = a;
}
mul1(a,xs[steps[i].c]);
}
#else
/* Raise to the p-2 = 0x7f..ffeb */
for (i=253; i>=0; i--) {
sqr(z3,prev);
prev = z3;
if (i>=8 || (0xeb>>i & 1)) {
mul1(z3,z2);
}
}
#endif
/* Here prev = z3 */
/* x2 /= z2 */
#if X25519_MEMCPY_PARAMS
mul1(x2,z3);
int ret = canon(x2);
swapout(out,x2);
#else
mul((limb_t *)out, x2, z3, NLIMBS);
int ret = canon((limb_t*)out);
#endif
if (clamp) return ret;
else return 0;
}
const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9};
#if X25519_SUPPORT_VERIFY
static limb_t x25519_verify_core(
fe xs[5],
const limb_t *other1,
const uint8_t other2[X25519_BYTES]
) {
limb_t *z2=xs[1],*x3=xs[2],*z3=xs[3];
#if X25519_MEMCPY_PARAMS
fe xo2;
swapin(xo2,other2);
#else
const limb_t *xo2 = (const limb_t *)other2;
#endif
memcpy(x3, other1, 2*sizeof(fe));
ladder_part1(xs);
/* Here z2 = t2^2 */
mul1(z2,other1);
mul1(z2,other1+NLIMBS);
mul1(z2,xo2);
const limb_t sixteen = 16;
mul (z2,z2,&sixteen,1);
mul1(z3,xo2);
sub(z3,z3,x3);
sqr1(z3);
/* check equality */
sub(z3,z3,z2);
/* If canon(z2) then both sides are zero.
* If canon(z3) then the two sides are equal.
*
* Reject sigs where both sides are zero, because
* that can happen if an input causes the ladder to
* return 0/0.
*/
return canon(z2) | ~canon(z3);
}
int x25519_verify_p2 (
const uint8_t response[X25519_BYTES],
const uint8_t challenge[X25519_BYTES],
const uint8_t eph[X25519_BYTES],
const uint8_t pub[X25519_BYTES]
) {
fe xs[7];
x25519_core(&xs[0],challenge,pub,0);
x25519_core(&xs[2],response,X25519_BASE_POINT,0);
return x25519_verify_core(&xs[2],xs[0],eph);
}
#endif // X25519_SUPPORT_VERIFY
#if X25519_SUPPORT_SIGN
static void sc_montmul (
scalar_t out,
const scalar_t a,
const scalar_t b
) {
/**
* OK, so carry bounding. We're using a high carry, so that the
* inputs don't have to be reduced.
*
* First montmul: output < (M^2 + Mp)/M = M+p, subtract p, < M. This gets rid of high carry.
* Second montmul, by r^2 mod p < p: output < (Mp + Mp)/M = 2p, subtract p, < p, done.
*/
unsigned i,j;
limb_t hic = 0;
for (i=0; i<NLIMBS; i++) {
limb_t carry=0, carry2=0, mand = a[i], mand2 = MONTGOMERY_FACTOR;
for (j=0; j<NLIMBS; j++) {
limb_t acc = out[j];
acc = umaal(&carry, acc, mand, b[j]);
if (j==0) mand2 *= acc;
acc = umaal(&carry2, acc, mand2, sc_p[j]);
if (j>0) out[j-1] = acc;
}
/* Add two carry registers and high carry */
out[NLIMBS-1] = adc(&hic, carry, carry2);
}
/* Reduce */
sdlimb_t scarry = 0;
for (i=0; i<NLIMBS; i++) {
out[i] = scarry = scarry + out[i] - sc_p[i];
scarry >>= X25519_WBITS;
}
limb_t need_add = -(scarry + hic);
limb_t carry = 0;
for (i=0; i<NLIMBS; i++) {
out[i] = umaal(&carry, out[i], need_add, sc_p[i]);
}
}
void x25519_sign_p2 (
uint8_t response[X25519_BYTES],
const uint8_t challenge[X25519_BYTES],
const uint8_t eph_secret[X25519_BYTES],
const uint8_t secret[X25519_BYTES]
) {
/* FUTURE memory/code size: just make eph_secret non-const? */
scalar_t scalar1;
swapin(scalar1,eph_secret);
#if X25519_MEMCPY_PARAMS
scalar_t scalar2, scalar3;
swapin(scalar2,secret);
swapin(scalar3,challenge);
sc_montmul(scalar1,scalar2,scalar3);
memset(scalar2,0,sizeof(scalar2));
sc_montmul(scalar2,scalar1,sc_r2);
swapout(response,scalar2);
#else
sc_montmul(scalar1,(const limb_t *)secret,(const limb_t *)challenge);
memset(response,0,X25519_BYTES);
sc_montmul((limb_t *)response,scalar1,sc_r2);
#endif
}
#endif // X25519_SUPPORT_SIGN
================================================
FILE: src/crypto/refc/x25519.h
================================================
// Taken from https://sourceforge.net/p/strobe (MIT Licence)
/**
* @file x25519.h
* @copyright
* Copyright (c) 2016 Cryptography Research, Inc. \n
* Released under the MIT License. See LICENSE.txt for license information.
* @author Mike Hamburg
* @brief X25519 key exchange and signatures.
*/
#ifndef __X25519_H__
#define __X25519_H__
#ifdef __cplusplus
extern "C" {
#endif
#define X25519_BYTES (256/8)
/* The base point (9) */
extern const unsigned char X25519_BASE_POINT[X25519_BYTES];
/** Number of bytes in an EC public key */
#define EC_PUBLIC_BYTES 32
/** Number of bytes in an EC private key */
#define EC_PRIVATE_BYTES 32
/**
* Number of bytes in a Schnorr challenge.
* Could be set to 16 in a pinch. (FUTURE?)
*/
#define EC_CHALLENGE_BYTES 32
/** Enough bytes to get a uniform sample mod #E. For eg a Brainpool
* curve this would need to be more than for a private key, but due
* to the special prime used by Curve25519, the same size is enough.
*/
#define EC_UNIFORM_BYTES 32
/* x25519 scalar multiplication. Sets out to scalar*base.
*
* If clamp is set (and supported by X25519_INTEROP_SUPPORT_CLAMP)
* then the scalar will be "clamped" like a Curve25519 secret key.
* This adds almost no security, but permits interop with other x25519
* implementations without manually clamping the keys.
*
* Per RFC 7748, this function returns failure (-1) if the output
* is zero and clamp is set. This indicates "non-contributory behavior",
* meaning that one party might steer the key so that the other party's
* contribution doesn't matter, or contributes only a little entropy.
*
* WARNING: however, this function differs from RFC 7748 in another way:
* it pays attention to the high bit base[EC_PUBLIC_BYTES-1] & 0x80, but
* RFC 7748 says to ignore this bit. For compatibility with RFC 7748,
* you must also clear this bit by running base[EC_PUBLIC_BYTES-1] &= 0x7F.
* This library won't clear it for you because it takes the base point as
* const, and (depending on build flags) dosen't copy it.
*
* If clamp==0, or if X25519_INTEROP_SUPPORT_CLAMP==0, then this function
* always returns 0.
*/
int x25519 (
unsigned char out[EC_PUBLIC_BYTES],
const unsigned char scalar[EC_PRIVATE_BYTES],
const unsigned char base[EC_PUBLIC_BYTES],
int clamp
);
/**
* Returns 0 on success, -1 on failure.
*
* Per RFC 7748, this function returns failure if the output
* is zero and clamp is set. This usually doesn't matter for
* base scalarmuls.
*
* If clamp==0, or if X25519_INTEROP_SUPPORT_CLAMP==0, then this function
* always returns 0.
*
* Same as x255(out,scalar,X255_BASE_POINT), except that
* other implementations may optimize it.
*/
static inline int x25519_base (
unsigned char out[EC_PUBLIC_BYTES],
const unsigned char scalar[EC_PRIVATE_BYTES],
int clamp
) {
return x25519(out,scalar,X25519_BASE_POINT,clamp);
}
/**
* As x25519_base, but with a scalar that's EC_UNIFORM_BYTES long,
* and clamp always 0 (and thus, no return value).
*
* This is used for signing. Implementors must replace it for
* curves that require more bytes for uniformity (Brainpool).
*/
static inline void x25519_base_uniform (
unsigned char out[EC_PUBLIC_BYTES],
const unsigned char scalar[EC_UNIFORM_BYTES]
) {
(void)x25519_base(out,scalar,0);
}
/**
* STROBE-compatible Schnorr signatures using curve25519 (not ed25519)
*
* The user will call x25519_base_uniform(eph,eph_secret) to schedule
* a random ephemeral secret key. They then call a Schnorr oracle to
* get a challenge, and compute the response using this function.
*/
void x25519_sign_p2 (
unsigned char response[EC_PRIVATE_BYTES],
const unsigned char challenge[EC_CHALLENGE_BYTES],
const unsigned char eph_secret[EC_UNIFORM_BYTES],
const unsigned char secret[EC_PRIVATE_BYTES]
);
/**
* STROBE-compatible signature verification using curve25519 (not ed25519).
* This function is the public equivalent x25519_sign_p2, taking the long-term
* and ephemeral public keys instead of secret ones.
*
* Returns -1 on failure and 0 on success.
*/
int x25519_verify_p2 (
const unsigned char response[X25519_BYTES],
const unsigned char challenge[X25519_BYTES],
const unsigned char eph[X25519_BYTES],
const unsigned char pub[X25519_BYTES]
);
#ifdef __cplusplus
}
#endif
#endif /* __X25519_H__ */
================================================
FILE: src/crypto.c
================================================
#include "crypto.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
void crypto_zero(void *dest, size_t len) {
volatile uint8_t *p = (uint8_t *)dest;
while (len--) {
*p++ = 0;
}
}
bool crypto_equal(const void *a, const void *b, size_t size) {
uint8_t neq = 0;
while (size > 0) {
neq |= *(uint8_t *)a ^ *(uint8_t *)b;
a += 1;
b += 1;
size -= 1;
}
return (neq) ? false : true;
}
================================================
FILE: src/crypto.h
================================================
#ifndef _CRYPTO_H_
#define _CRYPTO_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
// BLAKE2S IMPLEMENTATION
#include "crypto/refc/blake2s.h"
#define wireguard_blake2s_ctx blake2s_ctx
#define wireguard_blake2s_init(ctx,outlen,key,keylen) blake2s_init(ctx,outlen,key,keylen)
#define wireguard_blake2s_update(ctx,in,inlen) blake2s_update(ctx,in,inlen)
#define wireguard_blake2s_final(ctx,out) blake2s_final(ctx,out)
#define wireguard_blake2s(out,outlen,key,keylen,in,inlen) blake2s(out,outlen,key,keylen,in,inlen)
// X25519 IMPLEMENTATION
#if defined(CONFIG_WIREGUARD_x25519_IMPLEMENTATION_DEFAULT)
#include "crypto/refc/x25519.h"
#define wireguard_x25519(a,b,c) x25519(a,b,c,1)
#endif
#if defined(CONFIG_WIREGUARD_x25519_IMPLEMENTATION_NACL)
#include "nacl/crypto_scalarmult/curve25519/ref/crypto_scalarmult.h"
#define wireguard_x25519(a,b,c) crypto_scalarmult(a,b,c)
#endif
//#include "crypto/cortex/scalarmult.h"
//#define wireguard_x25519(a,b,c) crypto_scalarmult_curve25519(a,b,c)
// CHACHA20POLY1305 IMPLEMENTATION
#include "crypto/refc/chacha20poly1305.h"
#define wireguard_aead_encrypt(dst,src,srclen,ad,adlen,nonce,key) chacha20poly1305_encrypt(dst,src,srclen,ad,adlen,nonce,key)
#define wireguard_aead_decrypt(dst,src,srclen,ad,adlen,nonce,key) chacha20poly1305_decrypt(dst,src,srclen,ad,adlen,nonce,key)
#define wireguard_xaead_encrypt(dst,src,srclen,ad,adlen,nonce,key) xchacha20poly1305_encrypt(dst,src,srclen,ad,adlen,nonce,key)
#define wireguard_xaead_decrypt(dst,src,srclen,ad,adlen,nonce,key) xchacha20poly1305_decrypt(dst,src,srclen,ad,adlen,nonce,key)
// Endian / unaligned helper macros
#define U8C(v) (v##U)
#define U32C(v) (v##U)
#define U8V(v) ((uint8_t)(v) & U8C(0xFF))
#define U32V(v) ((uint32_t)(v) & U32C(0xFFFFFFFF))
#define U8TO32_LITTLE(p) \
(((uint32_t)((p)[0]) ) | \
((uint32_t)((p)[1]) << 8) | \
((uint32_t)((p)[2]) << 16) | \
((uint32_t)((p)[3]) << 24))
#define U8TO64_LITTLE(p) \
(((uint64_t)((p)[0]) ) | \
((uint64_t)((p)[1]) << 8) | \
((uint64_t)((p)[2]) << 16) | \
((uint64_t)((p)[3]) << 24) | \
((uint64_t)((p)[4]) << 32) | \
((uint64_t)((p)[5]) << 40) | \
((uint64_t)((p)[6]) << 48) | \
((uint64_t)((p)[7]) << 56))
#define U16TO8_BIG(p, v) \
do { \
(p)[1] = U8V((v) ); \
(p)[0] = U8V((v) >> 8); \
} while (0)
#define U32TO8_LITTLE(p, v) \
do { \
(p)[0] = U8V((v) ); \
(p)[1] = U8V((v) >> 8); \
(p)[2] = U8V((v) >> 16); \
(p)[3] = U8V((v) >> 24); \
} while (0)
#define U32TO8_BIG(p, v) \
do { \
(p)[3] = U8V((v) ); \
(p)[2] = U8V((v) >> 8); \
(p)[1] = U8V((v) >> 16); \
(p)[0] = U8V((v) >> 24); \
} while (0)
#define U64TO8_LITTLE(p, v) \
do { \
(p)[0] = U8V((v) ); \
(p)[1] = U8V((v) >> 8); \
(p)[2] = U8V((v) >> 16); \
(p)[3] = U8V((v) >> 24); \
(p)[4] = U8V((v) >> 32); \
(p)[5] = U8V((v) >> 40); \
(p)[6] = U8V((v) >> 48); \
(p)[7] = U8V((v) >> 56); \
} while (0)
#define U64TO8_BIG(p, v) \
do { \
(p)[7] = U8V((v) ); \
(p)[6] = U8V((v) >> 8); \
(p)[5] = U8V((v) >> 16); \
(p)[4] = U8V((v) >> 24); \
(p)[3] = U8V((v) >> 32); \
(p)[2] = U8V((v) >> 40); \
(p)[1] = U8V((v) >> 48); \
(p)[0] = U8V((v) >> 56); \
} while (0)
void crypto_zero(void *dest, size_t len);
bool crypto_equal(const void *a, const void *b, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* _CRYPTO_H_ */
================================================
FILE: src/esp_wireguard.c
================================================
/*
* Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <assert.h>
#include <string.h>
#include <inttypes.h>
#include <lwip/ip.h>
#include <lwip/netdb.h>
#include <lwip/err.h>
#include <esp_err.h>
#include <esp_log.h>
#include <esp_wireguard.h>
#include <mbedtls/base64.h>
#include "wireguard-platform.h"
#include "wireguardif.h"
#define TAG "esp_wireguard"
#define WG_KEY_LEN (32)
#define WG_B64_KEY_LEN (4 * ((WG_KEY_LEN + 2) / 3))
#if defined(CONFIG_LWIP_IPV6)
#define WG_ADDRSTRLEN INET6_ADDRSTRLEN
#else
#define WG_ADDRSTRLEN INET_ADDRSTRLEN
#endif
static struct netif wg_netif_struct = {0};
static struct netif *wg_netif = NULL;
static struct wireguardif_peer peer = {0};
static uint8_t wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;
static uint8_t preshared_key_decoded[WG_KEY_LEN];
static esp_err_t esp_wireguard_peer_init(const wireguard_config_t *config, struct wireguardif_peer *peer)
{
esp_err_t err;
char addr_str[WG_ADDRSTRLEN];
struct addrinfo *res = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
if (!config || !peer) {
err = ESP_ERR_INVALID_ARG;
goto fail;
}
peer->public_key = config->public_key;
if (config->preshared_key != NULL) {
size_t len;
int res;
ESP_LOGI(TAG, "using preshared_key");
ESP_LOGD(TAG, "preshared_key: %s", config->preshared_key);
#if defined(CONFIG_WIREGUARD_x25519_IMPLEMENTATION_DEFAULT)
ESP_LOGI(TAG, "X25519: default");
#elif defined(CONFIG_WIREGUARD_x25519_IMPLEMENTATION_NACL)
ESP_LOGI(TAG, "X25519: NaCL");
#endif
res = mbedtls_base64_decode(preshared_key_decoded, WG_KEY_LEN, &len, (unsigned char *)config->preshared_key, WG_B64_KEY_LEN);
if (res != 0 || len != WG_KEY_LEN) {
err = ESP_FAIL;
ESP_LOGE(TAG, "base64_decode: %i", res);
if (len != WG_KEY_LEN) {
ESP_LOGE(TAG, "invalid decoded length, len: %u, should be %u", len, WG_KEY_LEN);
}
goto fail;
}
peer->preshared_key = preshared_key_decoded;
} else {
peer->preshared_key = NULL;
}
peer->keep_alive = config->persistent_keepalive;
/* Allow all IPs through tunnel */
{
ip_addr_t allowed_ip = IPADDR4_INIT_BYTES(0, 0, 0, 0);
peer->allowed_ip = allowed_ip;
ip_addr_t allowed_mask = IPADDR4_INIT_BYTES(0, 0, 0, 0);
peer->allowed_mask = allowed_mask;
}
/* resolve peer name or IP address */
{
ip_addr_t endpoint_ip;
memset(&endpoint_ip, 0, sizeof(endpoint_ip));
/* XXX lwip_getaddrinfo returns only the first address of a host at the moment */
if (getaddrinfo(config->endpoint, NULL, &hints, &res) != 0) {
err = ESP_FAIL;
/* XXX gai_strerror() is not implemented */
ESP_LOGE(TAG, "getaddrinfo: unable to resolve `%s`", config->endpoint);
goto fail;
}
if (res->ai_family == AF_INET) {
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&endpoint_ip), &addr4);
} else {
#if defined(CONFIG_LWIP_IPV6)
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&endpoint_ip), &addr6);
#endif
}
peer->endpoint_ip = endpoint_ip;
if (inet_ntop(res->ai_family, &(peer->endpoint_ip), addr_str, WG_ADDRSTRLEN) == NULL) {
ESP_LOGW(TAG, "inet_ntop: %i", errno);
} else {
ESP_LOGI(TAG, "Peer: %s (%s:%i)",
config->endpoint,
addr_str,
config->port);
}
}
peer->endport_port = config->port;
peer->keep_alive = config->persistent_keepalive;
err = ESP_OK;
fail:
freeaddrinfo(res);
return err;
}
static esp_err_t esp_wireguard_netif_create(const wireguard_config_t *config)
{
esp_err_t err;
ip_addr_t ip_addr;
ip_addr_t netmask;
ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0);
struct wireguardif_init_data wg = {0};
if (!config) {
err = ESP_ERR_INVALID_ARG;
goto fail;
}
/* Setup the WireGuard device structure */
wg.private_key = config->private_key;
wg.listen_port = config->listen_port;
wg.bind_netif = NULL;
ESP_LOGI(TAG, "allowed_ip: %s", config->allowed_ip);
if (ipaddr_aton(config->allowed_ip, &ip_addr) != 1) {
ESP_LOGE(TAG, "ipaddr_aton: invalid allowed_ip: `%s`", config->allowed_ip);
err = ESP_ERR_INVALID_ARG;
goto fail;
}
if (ipaddr_aton(config->allowed_ip_mask, &netmask) != 1) {
ESP_LOGE(TAG, "ipaddr_aton: invalid allowed_ip_mask: `%s`", config->allowed_ip_mask);
err = ESP_ERR_INVALID_ARG;
goto fail;
}
/* Register the new WireGuard network interface with lwIP */
wg_netif = netif_add(
&wg_netif_struct,
ip_2_ip4(&ip_addr),
ip_2_ip4(&netmask),
ip_2_ip4(&gateway),
&wg, &wireguardif_init,
&ip_input);
if (wg_netif == NULL) {
ESP_LOGE(TAG, "netif_add: failed");
err = ESP_FAIL;
goto fail;
}
/* Mark the interface as administratively up, link up flag is set
* automatically when peer connects */
netif_set_up(wg_netif);
err = ESP_OK;
fail:
return err;
}
esp_err_t esp_wireguard_init(wireguard_config_t *config, wireguard_ctx_t *ctx)
{
esp_err_t err = ESP_FAIL;
if (!config || !ctx) {
err = ESP_ERR_INVALID_ARG;
goto fail;
}
err = wireguard_platform_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "wireguard_platform_init: %s", esp_err_to_name(err));
goto fail;
}
ctx->config = config;
ctx->netif = NULL;
ctx->netif_default = netif_default;
err = ESP_OK;
fail:
return err;
}
esp_err_t esp_wireguard_connect(wireguard_ctx_t *ctx)
{
esp_err_t err = ESP_FAIL;
err_t lwip_err = -1;
if (!ctx) {
err = ESP_ERR_INVALID_ARG;
goto fail;
}
if (ctx->netif == NULL) {
err = esp_wireguard_netif_create(ctx->config);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wireguard_netif_create: %s", esp_err_to_name(err));
goto fail;
}
/* Initialize the first WireGuard peer structure */
err = esp_wireguard_peer_init(ctx->config, &peer);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wireguard_peer_init: %s", esp_err_to_name(err));
goto fail;
}
/* Register the new WireGuard peer with the network interface */
lwip_err = wireguardif_add_peer(wg_netif, &peer, &wireguard_peer_index);
if (lwip_err != ERR_OK || wireguard_peer_index == WIREGUARDIF_INVALID_INDEX) {
ESP_LOGE(TAG, "wireguardif_add_peer: %i", lwip_err);
goto fail;
}
if (ip_addr_isany(&peer.endpoint_ip)) {
err = ESP_FAIL;
goto fail;
}
ctx->netif = wg_netif;
ctx->netif_default = netif_default;
}
ESP_LOGI(TAG, "Connecting to %s:%i", ctx->config->endpoint, ctx->config->port);
lwip_err = wireguardif_connect(wg_netif, wireguard_peer_index);
if (lwip_err != ERR_OK) {
ESP_LOGE(TAG, "wireguardif_connect: %i", lwip_err);
err = ESP_FAIL;
goto fail;
}
err = ESP_OK;
fail:
return err;
}
esp_err_t esp_wireguard_set_default(wireguard_ctx_t *ctx)
{
esp_err_t err;
if (!ctx) {
err = ESP_ERR_INVALID_ARG;
goto fail;
}
netif_set_default(ctx->netif);
err = ESP_OK;
fail:
return err;
}
esp_err_t esp_wireguard_disconnect(wireguard_ctx_t *ctx)
{
esp_err_t err;
err_t lwip_err;
if (!ctx) {
err = ESP_ERR_INVALID_ARG;
goto fail;
}
// Clear the IP address to gracefully disconnect any clients while the
// peers are still valid
netif_set_ipaddr(ctx->netif, IP4_ADDR_ANY4);
lwip_err = wireguardif_disconnect(ctx->netif, wireguard_peer_index);
if (lwip_err != ERR_OK) {
ESP_LOGW(TAG, "wireguardif_disconnect: peer_index: %" PRIu8 " err: %i", wireguard_peer_index, lwip_err);
}
lwip_err = wireguardif_remove_peer(ctx->netif, wireguard_peer_index);
if (lwip_err != ERR_OK) {
ESP_LOGW(TAG, "wireguardif_remove_peer: peer_index: %" PRIu8 " err: %i", wireguard_peer_index, lwip_err);
}
wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;
wireguardif_shutdown(ctx->netif);
netif_remove(ctx->netif);
wireguardif_fini(ctx->netif);
netif_set_default(ctx->netif_default);
ctx->netif = NULL;
err = ESP_OK;
fail:
return err;
}
esp_err_t esp_wireguardif_peer_is_up(wireguard_ctx_t *ctx)
{
esp_err_t err;
err_t lwip_err;
if (!ctx) {
err = ESP_ERR_INVALID_ARG;
goto fail;
}
lwip_err = wireguardif_peer_is_up(
ctx->netif,
wireguard_peer_index,
&peer.endpoint_ip,
&peer.endport_port);
if (lwip_err != ERR_OK) {
err = ESP_FAIL;
goto fail;
}
err = ESP_OK;
fail:
return err;
}
================================================
FILE: src/nacl/crypto_scalarmult/curve25519/ref/crypto_scalarmult.h
================================================
int crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned char *p);
================================================
FILE: src/nacl/crypto_scalarmult/curve25519/ref/smult.c
================================================
/*
version 20081011
Matthew Dempsky
Public domain.
Derived from public domain code by D. J. Bernstein.
*/
#include "crypto_scalarmult.h"
static void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
unsigned int j;
unsigned int u;
u = 0;
for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; }
u += a[31] + b[31]; out[31] = u;
}
static void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
unsigned int j;
unsigned int u;
u = 218;
for (j = 0;j < 31;++j) {
u += a[j] + 65280 - b[j];
out[j] = u & 255;
u >>= 8;
}
u += a[31] - b[31];
out[31] = u;
}
static void squeeze(unsigned int a[32])
{
unsigned int j;
unsigned int u;
u = 0;
for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
u += a[31]; a[31] = u & 127;
u = 19 * (u >> 7);
for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
u += a[31]; a[31] = u;
}
static const unsigned int minusp[32] = {
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
} ;
static void freeze(unsigned int a[32])
{
unsigned int aorig[32];
unsigned int j;
unsigned int negative;
for (j = 0;j < 32;++j) aorig[j] = a[j];
add(a,a,minusp);
negative = -((a[31] >> 7) & 1);
for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]);
}
static void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
unsigned int i;
unsigned int j;
unsigned int u;
for (i = 0;i < 32;++i) {
u = 0;
for (j = 0;j <= i;++j) u += a[j] * b[i - j];
for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j];
out[i] = u;
}
squeeze(out);
}
static void mult121665(unsigned int out[32],const unsigned int a[32])
{
unsigned int j;
unsigned int u;
u = 0;
for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; }
u += 121665 * a[31]; out[31] = u & 127;
u = 19 * (u >> 7);
for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; }
u += out[j]; out[j] = u;
}
static void square(unsigned int out[32],const unsigned int a[32])
{
unsigned int i;
unsigned int j;
unsigned int u;
for (i = 0;i < 32;++i) {
u = 0;
for (j = 0;j < i - j;++j) u += a[j] * a[i - j];
for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j];
u *= 2;
if ((i & 1) == 0) {
u += a[i / 2] * a[i / 2];
u += 38 * a[i / 2 + 16] * a[i / 2 + 16];
}
out[i] = u;
}
squeeze(out);
}
static void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b)
{
unsigned int j;
unsigned int t;
unsigned int bminus1;
bminus1 = b - 1;
for (j = 0;j < 64;++j) {
t = bminus1 & (r[j] ^ s[j]);
p[j] = s[j] ^ t;
q[j] = r[j] ^ t;
}
}
static void mainloop(unsigned int work[64],const unsigned char e[32])
{
unsigned int xzm1[64];
unsigned int xzm[64];
unsigned int xzmb[64];
unsigned int xzm1b[64];
unsigned int xznb[64];
unsigned int xzn1b[64];
unsigned int a0[64];
unsigned int a1[64];
unsigned int b0[64];
unsigned int b1[64];
unsigned int c1[64];
unsigned int r[32];
unsigned int s[32];
unsigned int t[32];
unsigned int u[32];
unsigned int i;
unsigned int j;
unsigned int b;
int pos;
for (j = 0;j < 32;++j) xzm1[j] = work[j];
xzm1[32] = 1;
for (j = 33;j < 64;++j) xzm1[j] = 0;
xzm[0] = 1;
for (j = 1;j < 64;++j) xzm[j] = 0;
for (pos = 254;pos >= 0;--pos) {
b = e[pos / 8] >> (pos & 7);
b &= 1;
select(xzmb,xzm1b,xzm,xzm1,b);
add(a0,xzmb,xzmb + 32);
sub(a0 + 32,xzmb,xzmb + 32);
add(a1,xzm1b,xzm1b + 32);
sub(a1 + 32,xzm1b,xzm1b + 32);
square(b0,a0);
square(b0 + 32,a0 + 32);
mult(b1,a1,a0 + 32);
mult(b1 + 32,a1 + 32,a0);
add(c1,b1,b1 + 32);
sub(c1 + 32,b1,b1 + 32);
square(r,c1 + 32);
sub(s,b0,b0 + 32);
mult121665(t,s);
add(u,t,b0);
mult(xznb,b0,b0 + 32);
mult(xznb + 32,s,u);
square(xzn1b,c1);
mult(xzn1b + 32,r,work);
select(xzm,xzm1,xznb,xzn1b,b);
}
for (j = 0;j < 64;++j) work[j] = xzm[j];
}
static void recip(unsigned int out[32],const unsigned int z[32])
{
unsigned int z2[32];
unsigned int z9[32];
unsigned int z11[32];
unsigned int z2_5_0[32];
unsigned int z2_10_0[32];
unsigned int z2_20_0[32];
unsigned int z2_50_0[32];
unsigned int z2_100_0[32];
unsigned int t0[32];
unsigned int t1[32];
int i;
/* 2 */ square(z2,z);
/* 4 */ square(t1,z2);
/* 8 */ square(t0,t1);
/* 9 */ mult(z9,t0,z);
/* 11 */ mult(z11,z9,z2);
/* 22 */ square(t0,z11);
/* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9);
/* 2^6 - 2^1 */ square(t0,z2_5_0);
/* 2^7 - 2^2 */ square(t1,t0);
/* 2^8 - 2^3 */ square(t0,t1);
/* 2^9 - 2^4 */ square(t1,t0);
/* 2^10 - 2^5 */ square(t0,t1);
/* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0);
/* 2^11 - 2^1 */ square(t0,z2_10_0);
/* 2^12 - 2^2 */ square(t1,t0);
/* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); }
/* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0);
/* 2^21 - 2^1 */ square(t0,z2_20_0);
/* 2^22 - 2^2 */ square(t1,t0);
/* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); }
/* 2^40 - 2^0 */ mult(t0,t1,z2_20_0);
/* 2^41 - 2^1 */ square(t1,t0);
/* 2^42 - 2^2 */ square(t0,t1);
/* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); }
/* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0);
/* 2^51 - 2^1 */ square(t0,z2_50_0);
/* 2^52 - 2^2 */ square(t1,t0);
/* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
/* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0);
/* 2^101 - 2^1 */ square(t1,z2_100_0);
/* 2^102 - 2^2 */ square(t0,t1);
/* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); }
/* 2^200 - 2^0 */ mult(t1,t0,z2_100_0);
/* 2^201 - 2^1 */ square(t0,t1);
/* 2^202 - 2^2 */ square(t1,t0);
/* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
/* 2^250 - 2^0 */ mult(t0,t1,z2_50_0);
/* 2^251 - 2^1 */ square(t1,t0);
/* 2^252 - 2^2 */ square(t0,t1);
/* 2^253 - 2^3 */ square(t1,t0);
/* 2^254 - 2^4 */ square(t0,t1);
/* 2^255 - 2^5 */ square(t1,t0);
/* 2^255 - 21 */ mult(out,t1,z11);
}
int crypto_scalarmult(unsigned char *q,
const unsigned char *n,
const unsigned char *p)
{
unsigned int work[96];
unsigned char e[32];
unsigned int i;
for (i = 0;i < 32;++i) e[i] = n[i];
e[0] &= 248;
e[31] &= 127;
e[31] |= 64;
for (i = 0;i < 32;++i) work[i] = p[i];
mainloop(work,e);
recip(work + 32,work + 32);
mult(work + 64,work,work + 32);
freeze(work + 64);
for (i = 0;i < 32;++i) q[i] = work[64 + i];
return 0;
}
================================================
FILE: src/wireguard-platform.c
================================================
#include "wireguard-platform.h"
#include <stdlib.h>
#include <time.h>
#include <inttypes.h>
#include <lwip/sys.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <esp_system.h>
#include <esp_err.h>
#include <esp_log.h>
#include "crypto.h"
#define ENTROPY_MINIMUM_REQUIRED_THRESHOLD (134)
#define ENTROPY_FUNCTION_DATA NULL
#define ENTROPY_CUSTOM_DATA NULL
#define ENTROPY_CUSTOM_DATA_LENGTH (0)
#define TAG "wireguard-platform"
static struct mbedtls_ctr_drbg_context random_context;
static struct mbedtls_entropy_context entropy_context;
static int entropy_hw_random_source( void *data, unsigned char *output, size_t len, size_t *olen ) {
esp_fill_random(output, len);
*olen = len;
return 0;
}
esp_err_t wireguard_platform_init() {
int mbedtls_err;
esp_err_t err;
mbedtls_entropy_init(&entropy_context);
mbedtls_ctr_drbg_init(&random_context);
mbedtls_err = mbedtls_entropy_add_source(
&entropy_context,
entropy_hw_random_source,
ENTROPY_FUNCTION_DATA,
ENTROPY_MINIMUM_REQUIRED_THRESHOLD,
MBEDTLS_ENTROPY_SOURCE_STRONG);
if (mbedtls_err != 0) {
ESP_LOGE(TAG, "mbedtls_entropy_add_source: %i", mbedtls_err);
err = ESP_FAIL;
goto fail;
}
mbedtls_err = mbedtls_ctr_drbg_seed(
&random_context,
mbedtls_entropy_func,
&entropy_context,
ENTROPY_CUSTOM_DATA,
ENTROPY_CUSTOM_DATA_LENGTH);
if (mbedtls_err != 0) {
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed: %i", mbedtls_err);
err = ESP_FAIL;
goto fail;
}
err = ESP_OK;
fail:
return err;
}
void wireguard_random_bytes(void *bytes, size_t size) {
mbedtls_ctr_drbg_random(&random_context, bytes, size);
}
uint32_t wireguard_sys_now() {
// Default to the LwIP system time
return sys_now();
}
void wireguard_tai64n_now(uint8_t *output) {
// See https://cr.yp.to/libtai/tai64.html
// 64 bit seconds from 1970 = 8 bytes
// 32 bit nano seconds from current second
struct timeval tv;
gettimeofday(&tv, NULL);
uint64_t seconds = 0x400000000000000aULL + tv.tv_sec;
uint32_t nanos = tv.tv_usec * 1000;
U64TO8_BIG(output + 0, seconds);
U32TO8_BIG(output + 8, nanos);
}
bool wireguard_is_under_load() {
return false;
}
// vim: noexpandtab
================================================
FILE: src/wireguard-platform.h
================================================
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
#ifndef _WIREGUARD_PLATFORM_H_
#define _WIREGUARD_PLATFORM_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <esp_err.h>
// Peers are allocated statically inside the device structure to avoid malloc
#define WIREGUARD_MAX_PEERS CONFIG_WIREGUARD_MAX_PEERS
#define WIREGUARD_MAX_SRC_IPS CONFIG_WIREGUARD_MAX_SRC_IPS
// Per device limit on accepting (valid) initiation requests - per peer
#define MAX_INITIATIONS_PER_SECOND (CONFIG_MAX_INITIATIONS_PER_SECOND)
/**
* @brief Initialize crpyto backend
*
* @return ESP_OK on success.
*/
esp_err_t wireguard_platform_init();
// The number of milliseconds since system boot - for LwIP systems this could be sys_now()
uint32_t wireguard_sys_now();
// Fill the supplied buffer with random data - random data is used for generating new session keys periodically
void wireguard_random_bytes(void *bytes, size_t size);
// 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
// Output buffer passed is 12 bytes
// The Wireguard implementation doesn't strictly need this to be a time, but instead an increasing value
// The remote end of the Wireguard tunnel will use this value in handshake replay detection
void wireguard_tai64n_now(uint8_t *output);
// Is the system under load - i.e. should we generate cookie reply message in response to initiation messages
bool wireguard_is_under_load();
#ifdef __cplusplus
}
#endif
#endif /* _WIREGUARD_PLATFORM_H_ */
================================================
FILE: src/wireguard.c
================================================
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
#include "wireguard.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "crypto.h"
#include <esp_log.h>
#define TAG "wireguard"
// For HMAC calculation
#define WIREGUARD_BLAKE2S_BLOCK_SIZE (64)
// 5.4 Messages
// Constants
static const uint8_t CONSTRUCTION[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; // The UTF-8 string literal "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s", 37 bytes of output
static 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
static const uint8_t LABEL_MAC1[8] = "mac1----"; // Label-Mac1 The UTF-8 string literal "mac1----", 8 bytes of output.
static const uint8_t LABEL_COOKIE[8] = "cookie--"; // Label-Cookie The UTF-8 string literal "cookie--", 8 bytes of output
static const char *base64_lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const uint8_t zero_key[WIREGUARD_PUBLIC_KEY_LEN] = { 0 };
// Calculated in wireguard_init
static uint8_t construction_hash[WIREGUARD_HASH_LEN];
static uint8_t identifier_hash[WIREGUARD_HASH_LEN];
void wireguard_init() {
wireguard_blake2s_ctx ctx;
// Pre-calculate chaining key hash
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
wireguard_blake2s_update(&ctx, CONSTRUCTION, sizeof(CONSTRUCTION));
wireguard_blake2s_final(&ctx, construction_hash);
// Pre-calculate initial handshake hash - uses construction_hash calculated above
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
wireguard_blake2s_update(&ctx, construction_hash, sizeof(construction_hash));
wireguard_blake2s_update(&ctx, IDENTIFIER, sizeof(IDENTIFIER));
wireguard_blake2s_final(&ctx, identifier_hash);
}
struct wireguard_peer *peer_alloc(struct wireguard_device *device) {
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
tmp = &device->peers[x];
if (!tmp->valid) {
result = tmp;
break;
}
}
return result;
}
struct wireguard_peer *peer_lookup_by_pubkey(struct wireguard_device *device, uint8_t *public_key) {
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
tmp = &device->peers[x];
if (tmp->valid) {
if (memcmp(tmp->public_key, public_key, WIREGUARD_PUBLIC_KEY_LEN) == 0) {
result = tmp;
break;
}
}
}
return result;
}
uint8_t wireguard_peer_index(struct wireguard_device *device, struct wireguard_peer *peer) {
uint8_t result = 0xFF;
uint8_t x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
if (peer == &device->peers[x]) {
result = x;
break;
}
}
return result;
}
struct wireguard_peer *peer_lookup_by_peer_index(struct wireguard_device *device, uint8_t peer_index) {
struct wireguard_peer *result = NULL;
if (peer_index < WIREGUARD_MAX_PEERS) {
if (device->peers[peer_index].valid) {
result = &device->peers[peer_index];
}
}
return result;
}
struct wireguard_peer *peer_lookup_by_receiver(struct wireguard_device *device, uint32_t receiver) {
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
tmp = &device->peers[x];
if (tmp->valid) {
if ((tmp->curr_keypair.valid && (tmp->curr_keypair.local_index == receiver)) ||
(tmp->next_keypair.valid && (tmp->next_keypair.local_index == receiver)) ||
(tmp->prev_keypair.valid && (tmp->prev_keypair.local_index == receiver))
) {
result = tmp;
break;
}
}
}
return result;
}
struct wireguard_peer *peer_lookup_by_handshake(struct wireguard_device *device, uint32_t receiver) {
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
tmp = &device->peers[x];
if (tmp->valid) {
if (tmp->handshake.valid && tmp->handshake.initiator && (tmp->handshake.local_index == receiver)) {
result = tmp;
break;
}
}
}
return result;
}
bool wireguard_expired(uint32_t created_millis, uint32_t valid_seconds) {
uint32_t diff = wireguard_sys_now() - created_millis;
return (diff >= (valid_seconds * 1000));
}
static void generate_cookie_secret(struct wireguard_device *device) {
wireguard_random_bytes(device->cookie_secret, WIREGUARD_HASH_LEN);
device->cookie_secret_millis = wireguard_sys_now();
}
static void generate_peer_cookie(struct wireguard_device *device, uint8_t *cookie, uint8_t *source_addr_port, size_t source_length) {
wireguard_blake2s_ctx ctx;
if (wireguard_expired(device->cookie_secret_millis, COOKIE_SECRET_MAX_AGE)) {
// Generate new random bytes
generate_cookie_secret(device);
}
// Mac(key, input) Keyed-Blake2s(key, input, 16), the keyed MAC variant of the BLAKE2s hash function, returning 16 bytes of output
wireguard_blake2s_init(&ctx, WIREGUARD_COOKIE_LEN, device->cookie_secret, WIREGUARD_HASH_LEN);
// 5.4.7 Under Load: Cookie Reply Message
// 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
if ((source_addr_port) && (source_length > 0)) {
wireguard_blake2s_update(&ctx, source_addr_port, source_length);
}
wireguard_blake2s_final(&ctx, cookie);
}
static void wireguard_mac(uint8_t *dst, const void *message, size_t len, const uint8_t *key, size_t keylen) {
wireguard_blake2s(dst, WIREGUARD_COOKIE_LEN, key, keylen, message, len);
}
static void wireguard_mac_key(uint8_t *key, const uint8_t *public_key, const uint8_t *label, size_t label_len) {
blake2s_ctx ctx;
blake2s_init(&ctx, WIREGUARD_SESSION_KEY_LEN, NULL, 0);
blake2s_update(&ctx, label, label_len);
blake2s_update(&ctx, public_key, WIREGUARD_PUBLIC_KEY_LEN);
blake2s_final(&ctx, key);
}
static void wireguard_mix_hash(uint8_t *hash, const uint8_t *src, size_t src_len) {
wireguard_blake2s_ctx ctx;
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
wireguard_blake2s_update(&ctx, hash, WIREGUARD_HASH_LEN);
wireguard_blake2s_update(&ctx, src, src_len);
wireguard_blake2s_final(&ctx, hash);
}
static void wireguard_hmac(uint8_t *digest, const uint8_t *key, size_t key_len, const uint8_t *text, size_t text_len) {
// Adapted from appendix example in RFC2104 to use BLAKE2S instead of MD5 - https://tools.ietf.org/html/rfc2104
wireguard_blake2s_ctx ctx;
uint8_t k_ipad[WIREGUARD_BLAKE2S_BLOCK_SIZE]; // inner padding - key XORd with ipad
uint8_t k_opad[WIREGUARD_BLAKE2S_BLOCK_SIZE]; // outer padding - key XORd with opad
uint8_t tk[WIREGUARD_HASH_LEN];
int i;
// if key is longer than BLAKE2S_BLOCK_SIZE bytes reset it to key=BLAKE2S(key)
if (key_len > WIREGUARD_BLAKE2S_BLOCK_SIZE) {
wireguard_blake2s_ctx tctx;
wireguard_blake2s_init(&tctx, WIREGUARD_HASH_LEN, NULL, 0);
wireguard_blake2s_update(&tctx, key, key_len);
wireguard_blake2s_final(&tctx, tk);
key = tk;
key_len = WIREGUARD_HASH_LEN;
}
// the HMAC transform looks like:
// HASH(K XOR opad, HASH(K XOR ipad, text))
// where K is an n byte key
// ipad is the byte 0x36 repeated BLAKE2S_BLOCK_SIZE times
// opad is the byte 0x5c repeated BLAKE2S_BLOCK_SIZE times
// and text is the data being protected
memset(k_ipad, 0, sizeof(k_ipad));
memset(k_opad, 0, sizeof(k_opad));
memcpy(k_ipad, key, key_len);
memcpy(k_opad, key, key_len);
// XOR key with ipad and opad values
for (i=0; i < WIREGUARD_BLAKE2S_BLOCK_SIZE; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
// perform inner HASH
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0); // init context for 1st pass
wireguard_blake2s_update(&ctx, k_ipad, WIREGUARD_BLAKE2S_BLOCK_SIZE); // start with inner pad
wireguard_blake2s_update(&ctx, text, text_len); // then text of datagram
wireguard_blake2s_final(&ctx, digest); // finish up 1st pass
// perform outer HASH
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0); // init context for 2nd pass
wireguard_blake2s_update(&ctx, k_opad, WIREGUARD_BLAKE2S_BLOCK_SIZE); // start with outer pad
wireguard_blake2s_update(&ctx, digest, WIREGUARD_HASH_LEN); // then results of 1st hash
wireguard_blake2s_final(&ctx, digest); // finish up 2nd pass
}
static void wireguard_kdf1(uint8_t *tau1, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {
uint8_t tau0[WIREGUARD_HASH_LEN];
uint8_t output[WIREGUARD_HASH_LEN + 1];
// tau0 = Hmac(key, input)
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
// tau1 := Hmac(tau0, 0x1)
output[0] = 1;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
memcpy(tau1, output, WIREGUARD_HASH_LEN);
// Wipe intermediates
crypto_zero(tau0, sizeof(tau0));
crypto_zero(output, sizeof(output));
}
static void wireguard_kdf2(uint8_t *tau1, uint8_t *tau2, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {
uint8_t tau0[WIREGUARD_HASH_LEN];
uint8_t output[WIREGUARD_HASH_LEN + 1];
// tau0 = Hmac(key, input)
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
// tau1 := Hmac(tau0, 0x1)
output[0] = 1;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
memcpy(tau1, output, WIREGUARD_HASH_LEN);
// tau2 := Hmac(tau0,tau1 || 0x2)
output[WIREGUARD_HASH_LEN] = 2;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
memcpy(tau2, output, WIREGUARD_HASH_LEN);
// Wipe intermediates
crypto_zero(tau0, sizeof(tau0));
crypto_zero(output, sizeof(output));
}
static 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) {
uint8_t tau0[WIREGUARD_HASH_LEN];
uint8_t output[WIREGUARD_HASH_LEN + 1];
// tau0 = Hmac(key, input)
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
// tau1 := Hmac(tau0, 0x1)
output[0] = 1;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
memcpy(tau1, output, WIREGUARD_HASH_LEN);
// tau2 := Hmac(tau0,tau1 || 0x2)
output[WIREGUARD_HASH_LEN] = 2;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
memcpy(tau2, output, WIREGUARD_HASH_LEN);
// tau3 := Hmac(tau0,tau1,tau2 || 0x3)
output[WIREGUARD_HASH_LEN] = 3;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
memcpy(tau3, output, WIREGUARD_HASH_LEN);
// Wipe intermediates
crypto_zero(tau0, sizeof(tau0));
crypto_zero(output, sizeof(output));
}
bool wireguard_check_replay(struct wireguard_keypair *keypair, uint64_t seq) {
// Implementation of packet replay window - as per RFC2401
// Adapted from code in Appendix C at https://tools.ietf.org/html/rfc2401
uint32_t diff;
bool result = false;
size_t ReplayWindowSize = sizeof(keypair->replay_bitmap) * CHAR_BIT; // 32 bits
// WireGuard data packet counter starts from 0 but algorithm expects packet numbers to start from 1
seq++;
if (seq != 0) {
if (seq > keypair->replay_counter) {
// new larger sequence number
diff = seq - keypair->replay_counter;
if (diff < ReplayWindowSize) {
// In window
keypair->replay_bitmap <<= diff;
// set bit for this packet
keypair->replay_bitmap |= 1;
} else {
// This packet has a "way larger"
keypair->replay_bitmap = 1;
}
keypair->replay_counter = seq;
// larger is good
result = true;
} else {
diff = keypair->replay_counter - seq;
if (diff < ReplayWindowSize) {
if (keypair->replay_bitmap & ((uint32_t)1 << diff)) {
// already seen
} else {
// mark as seen
keypair->replay_bitmap |= ((uint32_t)1 << diff);
// out of order but good
result = true;
}
} else {
// too old or wrapped
}
}
} else {
// first == 0 or wrapped
}
return result;
}
struct wireguard_keypair *get_peer_keypair_for_idx(struct wireguard_peer *peer, uint32_t idx) {
if (peer->curr_keypair.valid && peer->curr_keypair.local_index == idx) {
return &peer->curr_keypair;
} else if (peer->next_keypair.valid && peer->next_keypair.local_index == idx) {
return &peer->next_keypair;
} else if (peer->prev_keypair.valid && peer->prev_keypair.local_index == idx) {
return &peer->prev_keypair;
}
return NULL;
}
static uint32_t wireguard_generate_unique_index(struct wireguard_device *device) {
// We need a random 32-bit number but make sure it's not already been used in the context of this device
uint32_t result;
uint8_t buf[4];
int x;
struct wireguard_peer *peer;
bool existing;
do {
do {
wireguard_random_bytes(buf, 4);
result = U8TO32_LITTLE(buf);
} while ((result == 0) || (result == 0xFFFFFFFF)); // Don't allow 0 or 0xFFFFFFFF as valid values
existing = false;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
peer = &device->peers[x];
existing = (result == peer->curr_keypair.local_index) ||
(result == peer->prev_keypair.local_index) ||
(result == peer->next_keypair.local_index) ||
(result == peer->handshake.local_index);
}
} while (existing);
return result;
}
static void wireguard_clamp_private_key(uint8_t *key) {
key[0] &= 248;
key[31] = (key[31] & 127) | 64;
}
static void wireguard_generate_private_key(uint8_t *key) {
wireguard_random_bytes(key, WIREGUARD_PRIVATE_KEY_LEN);
wireguard_clamp_private_key(key);
}
static bool wireguard_generate_public_key(uint8_t *public_key, const uint8_t *private_key) {
static const uint8_t basepoint[WIREGUARD_PUBLIC_KEY_LEN] = { 9 };
bool result = false;
if (memcmp(private_key, zero_key, WIREGUARD_PUBLIC_KEY_LEN) != 0) {
result = (wireguard_x25519(public_key, private_key, basepoint) == 0);
}
return result;
}
bool wireguard_check_mac1(struct wireguard_device *device, const uint8_t *data, size_t len, const uint8_t *mac1) {
bool result = false;
uint8_t calculated[WIREGUARD_COOKIE_LEN];
wireguard_mac(calculated, data, len, device->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
if (crypto_equal(calculated, mac1, WIREGUARD_COOKIE_LEN)) {
result = true;
}
return result;
}
bool 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) {
bool result = false;
uint8_t cookie[WIREGUARD_COOKIE_LEN];
uint8_t calculated[WIREGUARD_COOKIE_LEN];
generate_peer_cookie(device, cookie, source_addr_port, source_length);
wireguard_mac(calculated, data, len, cookie, WIREGUARD_COOKIE_LEN);
if (crypto_equal(calculated, mac2, WIREGUARD_COOKIE_LEN)) {
result = true;
}
return result;
}
void keypair_destroy(struct wireguard_keypair *keypair) {
crypto_zero(keypair, sizeof(struct wireguard_keypair));
keypair->valid = false;
}
void keypair_update(struct wireguard_peer *peer, struct wireguard_keypair *received_keypair) {
bool key_is_next = (received_keypair == &peer->next_keypair);
if (key_is_next) {
peer->prev_keypair = peer->curr_keypair;
peer->curr_keypair = peer->next_keypair;
keypair_destroy(&peer->next_keypair);
}
}
static void add_new_keypair(struct wireguard_peer *peer, struct wireguard_keypair new_keypair) {
if (new_keypair.initiator) {
if (peer->next_keypair.valid) {
peer->prev_keypair = peer->next_keypair;
keypair_destroy(&peer->next_keypair);
} else {
peer->prev_keypair = peer->curr_keypair;
}
peer->curr_keypair = new_keypair;
} else {
peer->next_keypair = new_keypair;
keypair_destroy(&peer->prev_keypair);
}
}
void wireguard_start_session(struct wireguard_peer *peer, bool initiator) {
struct wireguard_handshake *handshake = &peer->handshake;
struct wireguard_keypair new_keypair;
crypto_zero(&new_keypair, sizeof(struct wireguard_keypair));
new_keypair.initiator = initiator;
new_keypair.local_index = handshake->local_index;
new_keypair.remote_index = handshake->remote_index;
new_keypair.keypair_millis = wireguard_sys_now();
new_keypair.sending_valid = true;
new_keypair.receiving_valid = true;
// 5.4.5 Transport Data Key Derivation
// (Tsendi = Trecvr, Trecvi = Tsendr) := Kdf2(Ci = Cr,E)
if (new_keypair.initiator) {
wireguard_kdf2(new_keypair.sending_key, new_keypair.receiving_key, handshake->chaining_key, NULL, 0);
} else {
wireguard_kdf2(new_keypair.receiving_key, new_keypair.sending_key, handshake->chaining_key, NULL, 0);
}
new_keypair.replay_bitmap = 0;
new_keypair.replay_counter = 0;
new_keypair.last_tx = 0;
new_keypair.last_rx = 0; // No packets received yet
new_keypair.valid = true;
// Eprivi = Epubi = Eprivr = Epubr = Ci = Cr := E
crypto_zero(handshake->ephemeral_private, WIREGUARD_PUBLIC_KEY_LEN);
crypto_zero(handshake->remote_ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
crypto_zero(handshake->hash, WIREGUARD_HASH_LEN);
crypto_zero(handshake->chaining_key, WIREGUARD_HASH_LEN);
handshake->remote_index = 0;
handshake->local_index = 0;
handshake->valid = false;
add_new_keypair(peer, new_keypair);
}
uint8_t wireguard_get_message_type(const uint8_t *data, size_t len) {
uint8_t result = MESSAGE_INVALID;
if (len >= 4) {
if ((data[1] == 0) && (data[2] == 0) && (data[3] == 0)) {
switch (data[0]) {
case MESSAGE_HANDSHAKE_INITIATION:
if (len == sizeof(struct message_handshake_initiation)) {
result = MESSAGE_HANDSHAKE_INITIATION;
}
break;
case MESSAGE_HANDSHAKE_RESPONSE:
if (len == sizeof(struct message_handshake_response)) {
result = MESSAGE_HANDSHAKE_RESPONSE;
}
break;
case MESSAGE_COOKIE_REPLY:
if (len == sizeof(struct message_cookie_reply)) {
result = MESSAGE_COOKIE_REPLY;
}
break;
case MESSAGE_TRANSPORT_DATA:
if (len >= sizeof(struct message_transport_data) + WIREGUARD_AUTHTAG_LEN) {
result = MESSAGE_TRANSPORT_DATA;
}
break;
default:
break;
}
}
}
return result;
}
struct wireguard_peer *wireguard_process_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg) {
struct wireguard_peer *ret_peer = NULL;
struct wireguard_peer *peer = NULL;
struct wireguard_handshake *handshake;
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
uint8_t chaining_key[WIREGUARD_HASH_LEN];
uint8_t hash[WIREGUARD_HASH_LEN];
uint8_t s[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t e[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t t[WIREGUARD_TAI64N_LEN];
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
uint32_t now;
bool rate_limit;
bool replay;
// We are the responder, other end is the initiator
// Ci := Hash(Construction) (precalculated hash)
memcpy(chaining_key, construction_hash, WIREGUARD_HASH_LEN);
// Hi := Hash(Ci || Identifier
memcpy(hash, identifier_hash, WIREGUARD_HASH_LEN);
// Hi := Hash(Hi || Spubr)
wireguard_mix_hash(hash, device->public_key, WIREGUARD_PUBLIC_KEY_LEN);
// Ci := Kdf1(Ci, Epubi)
wireguard_kdf1(chaining_key, chaining_key, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// msg.ephemeral := Epubi
memcpy(e, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Hi := Hash(Hi || msg.ephemeral)
wireguard_mix_hash(hash, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Calculate DH(Eprivi,Spubr)
wireguard_x25519(dh_calculation, device->private_key, e);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
// (Ci,k) := Kdf2(Ci,DH(Eprivi,Spubr))
wireguard_kdf2(chaining_key, key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// msg.static := AEAD(k, 0, Spubi, Hi)
if (wireguard_aead_decrypt(s, msg->enc_static, sizeof(msg->enc_static), hash, WIREGUARD_HASH_LEN, 0, key)) {
// Hi := Hash(Hi || msg.static)
wireguard_mix_hash(hash, msg->enc_static, sizeof(msg->enc_static));
peer = peer_lookup_by_pubkey(device, s);
if (peer) {
handshake = &peer->handshake;
// (Ci,k) := Kdf2(Ci,DH(Sprivi,Spubr))
wireguard_kdf2(chaining_key, key, chaining_key, peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
// msg.timestamp := AEAD(k, 0, Timestamp(), Hi)
if (wireguard_aead_decrypt(t, msg->enc_timestamp, sizeof(msg->enc_timestamp), hash, WIREGUARD_HASH_LEN, 0, key)) {
// Hi := Hash(Hi || msg.timestamp)
wireguard_mix_hash(hash, msg->enc_timestamp, sizeof(msg->enc_timestamp));
now = wireguard_sys_now();
// Check that timestamp is increasing and we haven't had too many initiations (should only get one per peer every 5 seconds max?)
replay = (memcmp(t, peer->greatest_timestamp, WIREGUARD_TAI64N_LEN) <= 0); // tai64n is big endian so we can use memcmp to compare
rate_limit = (peer->last_initiation_rx - now) < (1000 / MAX_INITIATIONS_PER_SECOND);
if (!replay && !rate_limit) {
// Success! Copy everything to peer
peer->last_initiation_rx = now;
if (memcmp(t, peer->greatest_timestamp, WIREGUARD_TAI64N_LEN) > 0) {
memcpy(peer->greatest_timestamp, t, WIREGUARD_TAI64N_LEN);
// TODO: Need to notify if the higher layers want to persist latest timestamp/nonce somewhere
}
memcpy(handshake->remote_ephemeral, e, WIREGUARD_PUBLIC_KEY_LEN);
memcpy(handshake->hash, hash, WIREGUARD_HASH_LEN);
memcpy(handshake->chaining_key, chaining_key, WIREGUARD_HASH_LEN);
handshake->remote_index = msg->sender;
handshake->valid = true;
handshake->initiator = false;
ret_peer = peer;
} else {
// Ignore
}
} else {
ESP_LOGE(TAG, "wireguard_process_initiation_message: failed to decrypt");
}
} else {
ESP_LOGE(TAG, "peer not found");
}
} else {
ESP_LOGE(TAG, "Failed to decrypt");
}
} else {
ESP_LOGE(TAG, "Bad X25519");
}
crypto_zero(key, sizeof(key));
crypto_zero(hash, sizeof(hash));
crypto_zero(chaining_key, sizeof(chaining_key));
crypto_zero(dh_calculation, sizeof(dh_calculation));
return ret_peer;
}
bool wireguard_process_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *src) {
struct wireguard_handshake *handshake = &peer->handshake;
bool result = false;
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
uint8_t hash[WIREGUARD_HASH_LEN];
uint8_t chaining_key[WIREGUARD_HASH_LEN];
uint8_t e[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t ephemeral_private[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t static_private[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t preshared_key[WIREGUARD_SESSION_KEY_LEN];
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t tau[WIREGUARD_PUBLIC_KEY_LEN];
if (handshake->valid && handshake->initiator) {
memcpy(hash, handshake->hash, WIREGUARD_HASH_LEN);
memcpy(chaining_key, handshake->chaining_key, WIREGUARD_HASH_LEN);
memcpy(ephemeral_private, handshake->ephemeral_private, WIREGUARD_PUBLIC_KEY_LEN);
memcpy(preshared_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
// (Eprivr, Epubr) := DH-Generate()
// Not required
// Cr := Kdf1(Cr,Epubr)
wireguard_kdf1(chaining_key, chaining_key, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// msg.ephemeral := Epubr
memcpy(e, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Hr := Hash(Hr || msg.ephemeral)
wireguard_mix_hash(hash, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Cr := Kdf1(Cr, DH(Eprivr, Epubi))
// Calculate DH(Eprivr, Epubi)
wireguard_x25519(dh_calculation, ephemeral_private, e);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
wireguard_kdf1(chaining_key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// Cr := Kdf1(Cr, DH(Eprivr, Spubi))
// CalculateDH(Eprivr, Spubi)
wireguard_x25519(dh_calculation, device->private_key, e);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
wireguard_kdf1(chaining_key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// (Cr, t, k) := Kdf3(Cr, Q)
wireguard_kdf3(chaining_key, tau, key, chaining_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
// Hr := Hash(Hr | t)
wireguard_mix_hash(hash, tau, WIREGUARD_HASH_LEN);
// msg.empty := AEAD(k, 0, E, Hr)
if (wireguard_aead_decrypt(NULL, src->enc_empty, sizeof(src->enc_empty), hash, WIREGUARD_HASH_LEN, 0, key)) {
// Hr := Hash(Hr | msg.empty)
// Not required as discarded
//Copy details to handshake
memcpy(handshake->remote_ephemeral, e, WIREGUARD_HASH_LEN);
memcpy(handshake->hash, hash, WIREGUARD_HASH_LEN);
memcpy(handshake->chaining_key, chaining_key, WIREGUARD_HASH_LEN);
handshake->remote_index = src->sender;
result = true;
} else {
ESP_LOGE(TAG, "Decrypt failed");
}
} else {
ESP_LOGE(TAG, "X25519 fail");
}
} else {
ESP_LOGE(TAG, "X25519 fail 2");
}
}
crypto_zero(key, sizeof(key));
crypto_zero(hash, sizeof(hash));
crypto_zero(chaining_key, sizeof(chaining_key));
crypto_zero(ephemeral_private, sizeof(ephemeral_private));
crypto_zero(static_private, sizeof(static_private));
crypto_zero(preshared_key, sizeof(preshared_key));
crypto_zero(tau, sizeof(tau));
return result;
}
bool wireguard_process_cookie_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_cookie_reply *src) {
uint8_t cookie[WIREGUARD_COOKIE_LEN];
bool result = false;
if (peer->handshake_mac1_valid) {
result = wireguard_xaead_decrypt(cookie, src->enc_cookie, sizeof(src->enc_cookie), peer->handshake_mac1, WIREGUARD_COOKIE_LEN, src->nonce, peer->label_cookie_key);
if (result) {
// 5.4.7 Under Load: Cookie Reply Message
// 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
memcpy(peer->cookie, cookie, WIREGUARD_COOKIE_LEN);
peer->cookie_millis = wireguard_sys_now();
peer->handshake_mac1_valid = false;
}
} else {
// We didn't send any initiation packet so we shouldn't be getting a cookie reply!
}
return result;
}
bool wireguard_create_handshake_initiation(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_initiation *dst) {
uint8_t timestamp[WIREGUARD_TAI64N_LEN];
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
bool result = false;
struct wireguard_handshake *handshake = &peer->handshake;
memset(dst, 0, sizeof(struct message_handshake_initiation));
// Ci := Hash(Construction) (precalculated hash)
memcpy(handshake->chaining_key, construction_hash, WIREGUARD_HASH_LEN);
// Hi := Hash(Ci || Identifier)
memcpy(handshake->hash, identifier_hash, WIREGUARD_HASH_LEN);
// Hi := Hash(Hi || Spubr)
wireguard_mix_hash(handshake->hash, peer->public_key, WIREGUARD_PUBLIC_KEY_LEN);
// (Eprivi, Epubi) := DH-Generate()
wireguard_generate_private_key(handshake->ephemeral_private);
if (wireguard_generate_public_key(dst->ephemeral, handshake->ephemeral_private)) {
// Ci := Kdf1(Ci, Epubi)
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// msg.ephemeral := Epubi
// Done above - public keys is calculated into dst->ephemeral
// Hi := Hash(Hi || msg.ephemeral)
wireguard_mix_hash(handshake->hash, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Calculate DH(Eprivi,Spubr)
wireguard_x25519(dh_calculation, handshake->ephemeral_private, peer->public_key);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
// (Ci,k) := Kdf2(Ci,DH(Eprivi,Spubr))
wireguard_kdf2(handshake->chaining_key, key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// msg.static := AEAD(k,0,Spubi, Hi)
wireguard_aead_encrypt(dst->enc_static, device->public_key, WIREGUARD_PUBLIC_KEY_LEN, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
// Hi := Hash(Hi || msg.static)
wireguard_mix_hash(handshake->hash, dst->enc_static, sizeof(dst->enc_static));
// (Ci,k) := Kdf2(Ci,DH(Sprivi,Spubr))
// note DH(Sprivi,Spubr) is precomputed per peer
wireguard_kdf2(handshake->chaining_key, key, handshake->chaining_key, peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
// msg.timestamp := AEAD(k, 0, Timestamp(), Hi)
wireguard_tai64n_now(timestamp);
wireguard_aead_encrypt(dst->enc_timestamp, timestamp, WIREGUARD_TAI64N_LEN, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
// Hi := Hash(Hi || msg.timestamp)
wireguard_mix_hash(handshake->hash, dst->enc_timestamp, sizeof(dst->enc_timestamp));
dst->type = MESSAGE_HANDSHAKE_INITIATION;
dst->sender = wireguard_generate_unique_index(device);
handshake->valid = true;
handshake->initiator = true;
handshake->local_index = dst->sender;
result = true;
}
}
if (result) {
// 5.4.4 Cookie MACs
// msg.mac1 := Mac(Hash(Label-Mac1 || Spubm' ), msgA)
// The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed
wireguard_mac(dst->mac1, dst, (sizeof(struct message_handshake_initiation)-(2*WIREGUARD_COOKIE_LEN)), peer->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
// if Lm = E or Lm ≥ 120:
if ((peer->cookie_millis == 0) || wireguard_expired(peer->cookie_millis, COOKIE_SECRET_MAX_AGE)) {
// msg.mac2 := 0
crypto_zero(dst->mac2, WIREGUARD_COOKIE_LEN);
} else {
// msg.mac2 := Mac(Lm, msgB)
wireguard_mac(dst->mac2, dst, (sizeof(struct message_handshake_initiation)-(WIREGUARD_COOKIE_LEN)), peer->cookie, WIREGUARD_COOKIE_LEN);
}
}
crypto_zero(key, sizeof(key));
crypto_zero(dh_calculation, sizeof(dh_calculation));
return result;
}
bool wireguard_create_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *dst) {
struct wireguard_handshake *handshake = &peer->handshake;
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t tau[WIREGUARD_HASH_LEN];
bool result = false;
memset(dst, 0, sizeof(struct message_handshake_response));
if (handshake->valid && !handshake->initiator) {
// (Eprivr, Epubr) := DH-Generate()
wireguard_generate_private_key(handshake->ephemeral_private);
if (wireguard_generate_public_key(dst->ephemeral, handshake->ephemeral_private)) {
// Cr := Kdf1(Cr,Epubr)
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// msg.ephemeral := Epubr
// Copied above when generated
// Hr := Hash(Hr || msg.ephemeral)
wireguard_mix_hash(handshake->hash, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Cr := Kdf1(Cr, DH(Eprivr, Epubi))
// Calculate DH(Eprivi,Spubr)
wireguard_x25519(dh_calculation, handshake->ephemeral_private, handshake->remote_ephemeral);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// Cr := Kdf1(Cr, DH(Eprivr, Spubi))
// Calculate DH(Eprivi,Spubr)
wireguard_x25519(dh_calculation, handshake->ephemeral_private, peer->public_key);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// (Cr, t, k) := Kdf3(Cr, Q)
wireguard_kdf3(handshake->chaining_key, tau, key, handshake->chaining_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
// Hr := Hash(Hr | t)
wireguard_mix_hash(handshake->hash, tau, WIREGUARD_HASH_LEN);
// msg.empty := AEAD(k, 0, E, Hr)
wireguard_aead_encrypt(dst->enc_empty, NULL, 0, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
// Hr := Hash(Hr | msg.empty)
wireguard_mix_hash(handshake->hash, dst->enc_empty, sizeof(dst->enc_empty));
dst->type = MESSAGE_HANDSHAKE_RESPONSE;
dst->receiver = handshake->remote_index;
dst->sender = wireguard_generate_unique_index(device);
// Update handshake object too
handshake->local_index = dst->sender;
result = true;
} else {
// Bad x25519
}
} else {
// Bad x25519
}
} else {
// Failed to generate DH
}
}
if (result) {
// 5.4.4 Cookie MACs
// msg.mac1 := Mac(Hash(Label-Mac1 || Spubm' ), msgA)
// The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed
wireguard_mac(dst->mac1, dst, (sizeof(struct message_handshake_response)-(2*WIREGUARD_COOKIE_LEN)), peer->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
// if Lm = E or Lm ≥ 120:
if ((peer->cookie_millis == 0) || wireguard_expired(peer->cookie_millis, COOKIE_SECRET_MAX_AGE)) {
// msg.mac2 := 0
crypto_zero(dst->mac2, WIREGUARD_COOKIE_LEN);
} else {
// msg.mac2 := Mac(Lm, msgB)
wireguard_mac(dst->mac2, dst, (sizeof(struct message_handshake_response)-(WIREGUARD_COOKIE_LEN)), peer->cookie, WIREGUARD_COOKIE_LEN);
}
}
crypto_zero(key, sizeof(key));
crypto_zero(dh_calculation, sizeof(dh_calculation));
crypto_zero(tau, sizeof(tau));
return result;
}
void 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) {
uint8_t cookie[WIREGUARD_COOKIE_LEN];
crypto_zero(dst, sizeof(struct message_cookie_reply));
dst->type = MESSAGE_COOKIE_REPLY;
dst->receiver = index;
wireguard_random_bytes(dst->nonce, COOKIE_NONCE_LEN);
generate_peer_cookie(device, cookie, source_addr_port, source_length);
wireguard_xaead_encrypt(dst->enc_cookie, cookie, WIREGUARD_COOKIE_LEN, mac1, WIREGUARD_COOKIE_LEN, dst->nonce, device->label_cookie_key);
}
bool wireguard_peer_init(struct wireguard_device *device, struct wireguard_peer *peer, const uint8_t *public_key, const uint8_t *preshared_key) {
// Clear out structure
memset(peer, 0, sizeof(struct wireguard_peer));
if (device->valid) {
// Copy across the public key into our peer structure
memcpy(peer->public_key, public_key, WIREGUARD_PUBLIC_KEY_LEN);
if (preshared_key) {
memcpy(peer->preshared_key, preshared_key, WIREGUARD_SESSION_KEY_LEN);
} else {
crypto_zero(peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
}
if (wireguard_x25519(peer->public_key_dh, device->private_key, peer->public_key) == 0) {
// Zero out handshake
memset(&peer->handshake, 0, sizeof(struct wireguard_handshake));
peer->handshake.valid = false;
// Zero out any cookie info - we haven't received one yet
peer->cookie_millis = 0;
memset(&peer->cookie, 0, WIREGUARD_COOKIE_LEN);
// Precompute keys to deal with mac1/2 calculation
wireguard_mac_key(peer->label_mac1_key, peer->public_key, LABEL_MAC1, sizeof(LABEL_MAC1));
wireguard_mac_key(peer->label_cookie_key, peer->public_key, LABEL_COOKIE, sizeof(LABEL_COOKIE));
peer->valid = true;
} else {
crypto_zero(peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
}
}
return peer->valid;
}
bool wireguard_device_init(struct wireguard_device *device, const uint8_t *private_key) {
// Set the private key and calculate public key from it
memcpy(device->private_key, private_key, WIREGUARD_PRIVATE_KEY_LEN);
// Ensure private key is correctly "clamped"
wireguard_clamp_private_key(device->private_key);
device->valid = wireguard_generate_public_key(device->public_key, private_key);
if (device->valid) {
generate_cookie_secret(device);
// 5.4.4 Cookie MACs - The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed.
wireguard_mac_key(device->label_mac1_key, device->public_key, LABEL_MAC1, sizeof(LABEL_MAC1));
// 5.4.7 Under Load: Cookie Reply Message - The value Hash(Label-Cookie || Spubm) above can be pre-computed.
wireguard_mac_key(device->label_cookie_key, device->public_key, LABEL_COOKIE, sizeof(LABEL_COOKIE));
} else {
crypto_zero(device->private_key, WIREGUARD_PRIVATE_KEY_LEN);
}
return device->valid;
}
void wireguard_encrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, struct wireguard_keypair *keypair) {
wireguard_aead_encrypt(dst, src, src_len, NULL, 0, keypair->sending_counter, keypair->sending_key);
keypair->sending_counter++;
}
bool wireguard_decrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, uint64_t counter, struct wireguard_keypair *keypair) {
return wireguard_aead_decrypt(dst, src, src_len, NULL, 0, counter, keypair->receiving_key);
}
bool wireguard_base64_decode(const char *str, uint8_t *out, size_t *outlen) {
uint32_t accum = 0; // We accumulate upto four blocks of 6 bits into this to form 3 bytes output
uint8_t char_count = 0; // How many characters have we processed in this block
int byte_count = 3; // How many bytes are we expecting in current 4 char block
int len = 0; // result length in bytes
bool result = true;
uint8_t bits;
char c;
char *ptr;
int x;
size_t inlen;
if (!str) {
return false;
}
inlen = strlen(str);
for (x = 0; x < inlen; x++) {
c = str[x];
if (c == '=') {
// This is '=' padding at end of string - decrease the number of bytes to write
bits = 0;
byte_count--;
if (byte_count < 0) {
// Too much padding!
result = false;
break;
}
} else {
if (byte_count != 3) {
// Padding only allowed at end - this is a valid byte and we have already seen padding
result = false;
break;
}
ptr = strchr(base64_lookup, c);
if (ptr) {
bits = (uint8_t)((ptr - base64_lookup) & 0x3F);
} else {
// invalid character in input string
result = false;
break;
}
}
accum = (accum << 6) | bits;
char_count++;
if (char_count == 4) {
if (len + byte_count > *outlen) {
// Output buffer overflow
result = false;
break;
}
out[len++] = (uint8_t)((accum >> 16) & 0xFF);
if (byte_count > 1) {
out[len++] = (uint8_t)((accum >> 8) & 0xFF);
}
if (byte_count > 2) {
out[len++] = (uint8_t)(accum & 0xFF);
}
char_count = 0;
accum = 0;
}
}
if (char_count != 0) {
// We require padding to multiple of 3 input length - bytes are missing from output!
result = false;
}
*outlen = len;
return result;
}
bool wireguard_base64_encode(const uint8_t *in, size_t inlen, char *out, size_t *outlen) {
bool result = false;
int read_offset = 0;
int write_offset = 0;
uint8_t byte1, byte2, byte3;
uint32_t tmp;
char c;
size_t len = 4 * ((inlen + 2) / 3);
int padding = (3 - (inlen % 3));
if (padding > 2) padding = 0;
if (*outlen > len) {
while (read_offset < inlen) {
// Read three bytes
byte1 = (read_offset < inlen) ? in[read_offset++] : 0;
byte2 = (read_offset < inlen) ? in[read_offset++] : 0;
byte3 = (read_offset < inlen) ? in[read_offset++] : 0;
// Turn into 24 bit intermediate
tmp = (byte1 << 16) | (byte2 << 8) | (byte3);
// Write out 4 characters each representing 6 bits of input
out[write_offset++] = base64_lookup[(tmp >> 18) & 0x3F];
out[write_o
gitextract_3dacllmf/
├── .ackrc
├── .github/
│ └── workflows/
│ ├── build.yml
│ └── publish.yml
├── .gitignore
├── CMakeLists.txt
├── Kconfig
├── LICENSE
├── README.md
├── component.mk
├── examples/
│ └── demo/
│ ├── CMakeLists.txt
│ ├── Makefile
│ ├── README.md
│ ├── Vagrantfile
│ └── main/
│ ├── CMakeLists.txt
│ ├── Kconfig.projbuild
│ ├── component.mk
│ ├── main.c
│ ├── sync_time.c
│ └── sync_time.h
├── idf_component.yml
├── include/
│ └── esp_wireguard.h
└── src/
├── crypto/
│ └── refc/
│ ├── blake2s.c
│ ├── blake2s.h
│ ├── chacha20.c
│ ├── chacha20.h
│ ├── chacha20poly1305.c
│ ├── chacha20poly1305.h
│ ├── poly1305-donna-32.h
│ ├── poly1305-donna.c
│ ├── poly1305-donna.h
│ ├── x25519-license.txt
│ ├── x25519.c
│ └── x25519.h
├── crypto.c
├── crypto.h
├── esp_wireguard.c
├── nacl/
│ └── crypto_scalarmult/
│ └── curve25519/
│ └── ref/
│ ├── crypto_scalarmult.h
│ └── smult.c
├── wireguard-platform.c
├── wireguard-platform.h
├── wireguard.c
├── wireguard.h
├── wireguardif.c
└── wireguardif.h
SYMBOL INDEX (277 symbols across 21 files)
FILE: examples/demo/main/main.c
function esp_err_t (line 53) | static esp_err_t wireguard_setup(wireguard_ctx_t* ctx)
function event_handler (line 90) | static void event_handler(void* arg, esp_event_base_t event_base,
function esp_err_t (line 113) | static esp_err_t wifi_init_tcpip_adaptor(void)
function esp_err_t (line 182) | static esp_err_t wifi_init_netif(void)
function esp_err_t (line 269) | static esp_err_t wifi_init_sta(void)
function test_on_ping_success (line 278) | static void test_on_ping_success(esp_ping_handle_t hdl, void *args)
function test_on_ping_timeout (line 293) | static void test_on_ping_timeout(esp_ping_handle_t hdl, void *args)
function test_on_ping_end (line 302) | static void test_on_ping_end(esp_ping_handle_t hdl, void *args)
function start_ping (line 314) | void start_ping()
type tm (line 348) | struct tm
FILE: examples/demo/main/sync_time.c
function time_sync_notification_cb (line 10) | static void time_sync_notification_cb(struct timeval *tv)
function initialize_sntp (line 15) | static void initialize_sntp(void)
function obtain_time (line 27) | void obtain_time(void)
FILE: include/esp_wireguard.h
type wireguard_config_t (line 55) | typedef struct {
type wireguard_ctx_t (line 73) | typedef struct {
FILE: src/crypto.c
function crypto_zero (line 7) | void crypto_zero(void *dest, size_t len) {
function crypto_equal (line 14) | bool crypto_equal(const void *a, const void *b, size_t size) {
FILE: src/crypto/refc/blake2s.c
function blake2s_compress (line 31) | static void blake2s_compress(blake2s_ctx *ctx, int last)
function blake2s_init (line 78) | int blake2s_init(blake2s_ctx *ctx, size_t outlen,
function blake2s_update (line 106) | void blake2s_update(blake2s_ctx *ctx,
function blake2s_final (line 125) | void blake2s_final(blake2s_ctx *ctx, void *out)
function blake2s (line 145) | int blake2s(void *out, size_t outlen,
FILE: src/crypto/refc/blake2s.h
type blake2s_ctx (line 16) | typedef struct {
FILE: src/crypto/refc/chacha20.c
function INNER_BLOCK (line 68) | static inline void INNER_BLOCK(uint32_t *block) {
function chacha20_block (line 102) | static void chacha20_block(struct chacha20_ctx *ctx, uint8_t *stream) {
function chacha20 (line 117) | void chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in,...
function chacha20_init (line 149) | void chacha20_init(struct chacha20_ctx *ctx, const uint8_t *key, const u...
function hchacha20 (line 172) | void hchacha20(uint8_t *out, const uint8_t *nonce, const uint8_t *key) {
FILE: src/crypto/refc/chacha20.h
type chacha20_ctx (line 49) | struct chacha20_ctx {
type chacha20_ctx (line 53) | struct chacha20_ctx
type chacha20_ctx (line 54) | struct chacha20_ctx
FILE: src/crypto/refc/chacha20poly1305.c
function generate_poly1305_key (line 49) | static void generate_poly1305_key(struct poly1305_context *poly1305_stat...
function chacha20poly1305_encrypt (line 67) | void chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t s...
function chacha20poly1305_decrypt (line 108) | bool chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t s...
function xchacha20poly1305_encrypt (line 169) | void xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t ...
function xchacha20poly1305_decrypt (line 181) | bool xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t ...
FILE: src/crypto/refc/poly1305-donna-32.h
type poly1305_state_internal_t (line 21) | typedef struct poly1305_state_internal_t {
function U8TO32 (line 31) | static unsigned long
function U32TO8 (line 41) | static void
function poly1305_init (line 49) | void
function poly1305_blocks (line 77) | static void
function POLY1305_NOINLINE (line 138) | POLY1305_NOINLINE void
FILE: src/crypto/refc/poly1305-donna.c
function poly1305_update (line 6) | void
FILE: src/crypto/refc/poly1305-donna.h
type poly1305_context (line 11) | typedef struct poly1305_context {
FILE: src/crypto/refc/x25519.c
function eswap_letoh_32 (line 22) | static inline uint32_t eswap_letoh_32(uint32_t w) { return w; }
type limb_t (line 28) | typedef uint64_t limb_t;
type __uint128_t (line 29) | typedef __uint128_t dlimb_t;
type __int128_t (line 30) | typedef __int128_t sdlimb_t;
type limb_t (line 34) | typedef uint32_t limb_t;
type dlimb_t (line 35) | typedef uint64_t dlimb_t;
type sdlimb_t (line 36) | typedef int64_t sdlimb_t;
type limb_t (line 44) | typedef limb_t fe[NLIMBS];
type limb_t (line 47) | typedef limb_t scalar_t[NLIMBS];
function limb_t (line 58) | static inline limb_t umaal(
function limb_t (line 67) | static inline limb_t adc(limb_t *carry, limb_t acc, limb_t mand) {
function limb_t (line 73) | static inline limb_t adc0(limb_t *carry, limb_t acc) {
function propagate (line 84) | static void propagate(fe x, limb_t over) {
function add (line 95) | static void add(fe out, const fe a, const fe b) {
function sub (line 104) | static void sub(fe out, const fe a, const fe b) {
function swapin (line 114) | static void __attribute__((unused))
function swapout (line 123) | static void __attribute__((unused))
function mul (line 132) | static void mul(fe out, const fe a, const fe b, unsigned nb) {
function sqr (line 155) | static void sqr(fe out, const fe a) { mul(out,a,a,NLIMBS); }
function mul1 (line 156) | static void mul1(fe out, const fe a) { mul(out,a,out,NLIMBS); }
function sqr1 (line 157) | static void sqr1(fe a) { mul1(a,a); }
function condswap (line 159) | static void condswap(limb_t a[2*NLIMBS], limb_t b[2*NLIMBS], limb_t dosw...
function limb_t (line 167) | static limb_t canon(fe x) {
function ladder_part1 (line 205) | static void ladder_part1(fe xs[5]) {
function ladder_part2 (line 221) | static void ladder_part2(fe xs[5], const fe x1) {
function x25519_core (line 231) | static void x25519_core(fe xs[5], const uint8_t scalar[X25519_BYTES], co...
function x25519 (line 264) | int x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES]...
function limb_t (line 326) | static limb_t x25519_verify_core(
function x25519_verify_p2 (line 367) | int x25519_verify_p2 (
function sc_montmul (line 381) | static void sc_montmul (
function x25519_sign_p2 (line 424) | void x25519_sign_p2 (
FILE: src/crypto/refc/x25519.h
function x25519_base (line 83) | static inline int x25519_base (
function x25519_base_uniform (line 98) | static inline void x25519_base_uniform (
FILE: src/esp_wireguard.c
type netif (line 55) | struct netif
type netif (line 56) | struct netif
type wireguardif_peer (line 57) | struct wireguardif_peer
function esp_err_t (line 61) | static esp_err_t esp_wireguard_peer_init(const wireguard_config_t *confi...
function esp_err_t (line 153) | static esp_err_t esp_wireguard_netif_create(const wireguard_config_t *co...
function esp_err_t (line 206) | esp_err_t esp_wireguard_init(wireguard_config_t *config, wireguard_ctx_t...
function esp_err_t (line 229) | esp_err_t esp_wireguard_connect(wireguard_ctx_t *ctx)
function esp_err_t (line 279) | esp_err_t esp_wireguard_set_default(wireguard_ctx_t *ctx)
function esp_err_t (line 292) | esp_err_t esp_wireguard_disconnect(wireguard_ctx_t *ctx)
function esp_err_t (line 328) | esp_err_t esp_wireguardif_peer_is_up(wireguard_ctx_t *ctx)
FILE: src/nacl/crypto_scalarmult/curve25519/ref/smult.c
function add (line 10) | static void add(unsigned int out[32],const unsigned int a[32],const unsi...
function sub (line 19) | static void sub(unsigned int out[32],const unsigned int a[32],const unsi...
function squeeze (line 33) | static void squeeze(unsigned int a[32])
function freeze (line 49) | static void freeze(unsigned int a[32])
function mult (line 61) | static void mult(unsigned int out[32],const unsigned int a[32],const uns...
function mult121665 (line 76) | static void mult121665(unsigned int out[32],const unsigned int a[32])
function square (line 89) | static void square(unsigned int out[32],const unsigned int a[32])
function select (line 109) | static void select(unsigned int p[64],unsigned int q[64],const unsigned ...
function mainloop (line 123) | static void mainloop(unsigned int work[64],const unsigned char e[32])
function recip (line 180) | static void recip(unsigned int out[32],const unsigned int z[32])
function crypto_scalarmult (line 247) | int crypto_scalarmult(unsigned char *q,
FILE: src/wireguard-platform.c
type mbedtls_ctr_drbg_context (line 21) | struct mbedtls_ctr_drbg_context
type mbedtls_entropy_context (line 22) | struct mbedtls_entropy_context
function entropy_hw_random_source (line 24) | static int entropy_hw_random_source( void *data, unsigned char *output, ...
function esp_err_t (line 30) | esp_err_t wireguard_platform_init() {
function wireguard_random_bytes (line 63) | void wireguard_random_bytes(void *bytes, size_t size) {
function wireguard_sys_now (line 67) | uint32_t wireguard_sys_now() {
function wireguard_tai64n_now (line 72) | void wireguard_tai64n_now(uint8_t *output) {
function wireguard_is_under_load (line 86) | bool wireguard_is_under_load() {
FILE: src/wireguard.c
function wireguard_init (line 62) | void wireguard_init() {
type wireguard_peer (line 75) | struct wireguard_peer
type wireguard_device (line 75) | struct wireguard_device
type wireguard_peer (line 76) | struct wireguard_peer
type wireguard_peer (line 77) | struct wireguard_peer
type wireguard_peer (line 89) | struct wireguard_peer
type wireguard_device (line 89) | struct wireguard_device
type wireguard_peer (line 90) | struct wireguard_peer
type wireguard_peer (line 91) | struct wireguard_peer
function wireguard_peer_index (line 105) | uint8_t wireguard_peer_index(struct wireguard_device *device, struct wir...
type wireguard_peer (line 117) | struct wireguard_peer
type wireguard_device (line 117) | struct wireguard_device
type wireguard_peer (line 118) | struct wireguard_peer
type wireguard_peer (line 127) | struct wireguard_peer
type wireguard_device (line 127) | struct wireguard_device
type wireguard_peer (line 128) | struct wireguard_peer
type wireguard_peer (line 129) | struct wireguard_peer
type wireguard_peer (line 146) | struct wireguard_peer
type wireguard_device (line 146) | struct wireguard_device
type wireguard_peer (line 147) | struct wireguard_peer
type wireguard_peer (line 148) | struct wireguard_peer
function wireguard_expired (line 162) | bool wireguard_expired(uint32_t created_millis, uint32_t valid_seconds) {
function generate_cookie_secret (line 168) | static void generate_cookie_secret(struct wireguard_device *device) {
function generate_peer_cookie (line 173) | static void generate_peer_cookie(struct wireguard_device *device, uint8_...
function wireguard_mac (line 191) | static void wireguard_mac(uint8_t *dst, const void *message, size_t len,...
function wireguard_mac_key (line 195) | static void wireguard_mac_key(uint8_t *key, const uint8_t *public_key, c...
function wireguard_mix_hash (line 203) | static void wireguard_mix_hash(uint8_t *hash, const uint8_t *src, size_t...
function wireguard_hmac (line 211) | static void wireguard_hmac(uint8_t *digest, const uint8_t *key, size_t k...
function wireguard_kdf1 (line 258) | static void wireguard_kdf1(uint8_t *tau1, const uint8_t *chaining_key, c...
function wireguard_kdf2 (line 274) | static void wireguard_kdf2(uint8_t *tau1, uint8_t *tau2, const uint8_t *...
function wireguard_kdf3 (line 295) | static void wireguard_kdf3(uint8_t *tau1, uint8_t *tau2, uint8_t *tau3, ...
function wireguard_check_replay (line 321) | bool wireguard_check_replay(struct wireguard_keypair *keypair, uint64_t ...
type wireguard_keypair (line 368) | struct wireguard_keypair
type wireguard_peer (line 368) | struct wireguard_peer
function wireguard_generate_unique_index (line 379) | static uint32_t wireguard_generate_unique_index(struct wireguard_device ...
function wireguard_clamp_private_key (line 406) | static void wireguard_clamp_private_key(uint8_t *key) {
function wireguard_generate_private_key (line 411) | static void wireguard_generate_private_key(uint8_t *key) {
function wireguard_generate_public_key (line 416) | static bool wireguard_generate_public_key(uint8_t *public_key, const uin...
function wireguard_check_mac1 (line 425) | bool wireguard_check_mac1(struct wireguard_device *device, const uint8_t...
function wireguard_check_mac2 (line 435) | bool wireguard_check_mac2(struct wireguard_device *device, const uint8_t...
function keypair_destroy (line 449) | void keypair_destroy(struct wireguard_keypair *keypair) {
function keypair_update (line 454) | void keypair_update(struct wireguard_peer *peer, struct wireguard_keypai...
function add_new_keypair (line 463) | static void add_new_keypair(struct wireguard_peer *peer, struct wireguar...
function wireguard_start_session (line 478) | void wireguard_start_session(struct wireguard_peer *peer, bool initiator) {
function wireguard_get_message_type (line 519) | uint8_t wireguard_get_message_type(const uint8_t *data, size_t len) {
type wireguard_peer (line 552) | struct wireguard_peer
type wireguard_device (line 552) | struct wireguard_device
type message_handshake_initiation (line 552) | struct message_handshake_initiation
type wireguard_peer (line 553) | struct wireguard_peer
type wireguard_peer (line 554) | struct wireguard_peer
type wireguard_handshake (line 555) | struct wireguard_handshake
function wireguard_process_handshake_response (line 656) | bool wireguard_process_handshake_response(struct wireguard_device *devic...
function wireguard_process_cookie_message (line 743) | bool wireguard_process_cookie_message(struct wireguard_device *device, s...
function wireguard_create_handshake_initiation (line 764) | bool wireguard_create_handshake_initiation(struct wireguard_device *devi...
function wireguard_create_handshake_response (line 853) | bool wireguard_create_handshake_response(struct wireguard_device *device...
function wireguard_create_cookie_reply (line 942) | void wireguard_create_cookie_reply(struct wireguard_device *device, stru...
function wireguard_peer_init (line 952) | bool wireguard_peer_init(struct wireguard_device *device, struct wiregua...
function wireguard_device_init (line 986) | bool wireguard_device_init(struct wireguard_device *device, const uint8_...
function wireguard_encrypt_packet (line 1005) | void wireguard_encrypt_packet(uint8_t *dst, const uint8_t *src, size_t s...
function wireguard_decrypt_packet (line 1010) | bool wireguard_decrypt_packet(uint8_t *dst, const uint8_t *src, size_t s...
function wireguard_base64_decode (line 1014) | bool wireguard_base64_decode(const char *str, uint8_t *out, size_t *outl...
function wireguard_base64_encode (line 1087) | bool wireguard_base64_encode(const uint8_t *in, size_t inlen, char *out,...
FILE: src/wireguard.h
type wireguard_keypair (line 79) | struct wireguard_keypair {
type wireguard_handshake (line 101) | struct wireguard_handshake {
type wireguard_allowed_ip (line 112) | struct wireguard_allowed_ip {
type wireguard_peer (line 118) | struct wireguard_peer {
type wireguard_device (line 175) | struct wireguard_device {
type message_handshake_initiation (line 206) | struct message_handshake_initiation {
type message_handshake_response (line 218) | struct message_handshake_response {
type message_cookie_reply (line 230) | struct message_cookie_reply {
type message_transport_data (line 239) | struct message_transport_data {
type wireguard_device (line 250) | struct wireguard_device
type wireguard_device (line 251) | struct wireguard_device
type wireguard_peer (line 251) | struct wireguard_peer
type wireguard_peer (line 253) | struct wireguard_peer
type wireguard_device (line 253) | struct wireguard_device
type wireguard_device (line 254) | struct wireguard_device
type wireguard_peer (line 254) | struct wireguard_peer
type wireguard_peer (line 255) | struct wireguard_peer
type wireguard_device (line 255) | struct wireguard_device
type wireguard_peer (line 256) | struct wireguard_peer
type wireguard_device (line 256) | struct wireguard_device
type wireguard_peer (line 257) | struct wireguard_peer
type wireguard_device (line 257) | struct wireguard_device
type wireguard_peer (line 258) | struct wireguard_peer
type wireguard_device (line 258) | struct wireguard_device
type wireguard_peer (line 260) | struct wireguard_peer
type wireguard_peer (line 262) | struct wireguard_peer
type wireguard_keypair (line 262) | struct wireguard_keypair
type wireguard_keypair (line 263) | struct wireguard_keypair
type wireguard_keypair (line 265) | struct wireguard_keypair
type wireguard_peer (line 265) | struct wireguard_peer
type wireguard_keypair (line 266) | struct wireguard_keypair
type wireguard_peer (line 270) | struct wireguard_peer
type wireguard_device (line 270) | struct wireguard_device
type message_handshake_initiation (line 270) | struct message_handshake_initiation
type wireguard_device (line 271) | struct wireguard_device
type wireguard_peer (line 271) | struct wireguard_peer
type message_handshake_response (line 271) | struct message_handshake_response
type wireguard_device (line 272) | struct wireguard_device
type wireguard_peer (line 272) | struct wireguard_peer
type message_cookie_reply (line 272) | struct message_cookie_reply
type wireguard_device (line 274) | struct wireguard_device
type wireguard_peer (line 274) | struct wireguard_peer
type message_handshake_initiation (line 274) | struct message_handshake_initiation
type wireguard_device (line 275) | struct wireguard_device
type wireguard_peer (line 275) | struct wireguard_peer
type message_handshake_response (line 275) | struct message_handshake_response
type wireguard_device (line 276) | struct wireguard_device
type message_cookie_reply (line 276) | struct message_cookie_reply
type wireguard_device (line 279) | struct wireguard_device
type wireguard_device (line 280) | struct wireguard_device
type wireguard_keypair (line 284) | struct wireguard_keypair
type wireguard_keypair (line 285) | struct wireguard_keypair
FILE: src/wireguardif.c
function update_peer_addr (line 62) | static void update_peer_addr(struct wireguard_peer *peer, const ip_addr_...
type wireguard_peer (line 67) | struct wireguard_peer
type wireguard_device (line 67) | struct wireguard_device
type wireguard_peer (line 68) | struct wireguard_peer
type wireguard_peer (line 69) | struct wireguard_peer
function wireguardif_can_send_initiation (line 86) | static bool wireguardif_can_send_initiation(struct wireguard_peer *peer) {
function err_t (line 90) | static err_t wireguardif_peer_output(struct netif *netif, struct pbuf *q...
function err_t (line 97) | static err_t wireguardif_device_output(struct wireguard_device *device, ...
function err_t (line 101) | static err_t wireguardif_output_to_peer(struct netif *netif, struct pbuf...
function err_t (line 199) | static err_t wireguardif_output(struct netif *netif, struct pbuf *q, con...
function wireguardif_send_keepalive (line 218) | static void wireguardif_send_keepalive(struct wireguard_device *device, ...
function wireguardif_process_response_message (line 223) | static void wireguardif_process_response_message(struct wireguard_device...
function peer_add_ip (line 239) | static bool peer_add_ip(struct wireguard_peer *peer, ip_addr_t ip, ip_ad...
function wireguardif_process_data_message (line 267) | static void wireguardif_process_data_message(struct wireguard_device *de...
type pbuf (line 388) | struct pbuf
type wireguard_device (line 388) | struct wireguard_device
type wireguard_peer (line 388) | struct wireguard_peer
type message_handshake_initiation (line 388) | struct message_handshake_initiation
type pbuf (line 389) | struct pbuf
type message_handshake_initiation (line 393) | struct message_handshake_initiation
type message_handshake_initiation (line 395) | struct message_handshake_initiation
function wireguardif_send_handshake_response (line 414) | static void wireguardif_send_handshake_response(struct wireguard_device ...
function get_source_addr_port (line 436) | static size_t get_source_addr_port(const ip_addr_t *addr, u16_t port, ui...
function wireguardif_send_handshake_cookie (line 465) | static void wireguardif_send_handshake_cookie(struct wireguard_device *d...
function wireguardif_check_initiation_message (line 485) | static bool wireguardif_check_initiation_message(struct wireguard_device...
function wireguardif_check_response_message (line 517) | static bool wireguardif_check_response_message(struct wireguard_device *...
function wireguardif_network_rx (line 550) | void wireguardif_network_rx(void *arg, struct udp_pcb *pcb, struct pbuf ...
function err_t (line 627) | static err_t wireguard_start_handshake(struct netif *netif, struct wireg...
function err_t (line 653) | static err_t wireguardif_lookup_peer(struct netif *netif, u8_t peer_inde...
function err_t (line 674) | err_t wireguardif_connect(struct netif *netif, u8_t peer_index) {
function err_t (line 692) | err_t wireguardif_disconnect(struct netif *netif, u8_t peer_index) {
function err_t (line 707) | err_t wireguardif_peer_is_up(struct netif *netif, u8_t peer_index, ip_ad...
function err_t (line 726) | err_t wireguardif_remove_peer(struct netif *netif, u8_t peer_index) {
function err_t (line 737) | err_t wireguardif_update_endpoint(struct netif *netif, u8_t peer_index, ...
function err_t (line 749) | err_t wireguardif_add_peer(struct netif *netif, struct wireguardif_peer ...
function should_send_initiation (line 808) | static bool should_send_initiation(struct wireguard_peer *peer) {
function should_send_keepalive (line 822) | static bool should_send_keepalive(struct wireguard_peer *peer) {
function should_destroy_current_keypair (line 834) | static bool should_destroy_current_keypair(struct wireguard_peer *peer) {
function should_reset_peer (line 845) | static bool should_reset_peer(struct wireguard_peer *peer) {
function wireguardif_tmr (line 853) | static void wireguardif_tmr(void *arg) {
function err_t (line 901) | err_t wireguardif_init(struct netif *netif) {
function wireguardif_peer_init (line 1019) | void wireguardif_peer_init(struct wireguardif_peer *peer) {
function wireguardif_shutdown (line 1033) | void wireguardif_shutdown(struct netif *netif) {
function wireguardif_fini (line 1048) | void wireguardif_fini(struct netif *netif) {
FILE: src/wireguardif.h
type wireguardif_init_data (line 50) | struct wireguardif_init_data {
type wireguardif_peer (line 59) | struct wireguardif_peer {
type netif (line 110) | struct netif
type wireguardif_peer (line 113) | struct wireguardif_peer
type netif (line 117) | struct netif
type wireguardif_peer (line 117) | struct wireguardif_peer
type netif (line 120) | struct netif
type netif (line 123) | struct netif
type netif (line 126) | struct netif
type netif (line 129) | struct netif
type netif (line 132) | struct netif
type netif (line 135) | struct netif
type netif (line 138) | struct netif
Condensed preview — 44 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (261K chars).
[
{
"path": ".ackrc",
"chars": 19,
"preview": "--ignore-dir=build\n"
},
{
"path": ".github/workflows/build.yml",
"chars": 27000,
"preview": "---\nname: Build examples\non:\n push:\n pull_request:\n branches:\n - main\n\n # include workflow_dispatch to ena"
},
{
"path": ".github/workflows/publish.yml",
"chars": 571,
"preview": "---\n\nname: Push component to https://components.espressif.com\non:\n push:\n tags:\n - v*\njobs:\n upload_components"
},
{
"path": ".gitignore",
"chars": 92,
"preview": "*.swp\n/examples/*/build\n/examples/*/sdkconfig\n/examples/*/sdkconfig.*\n/examples/*/.vagrant/\n"
},
{
"path": "CMakeLists.txt",
"chars": 698,
"preview": "idf_component_register(\n SRCS \"src/crypto.c\"\n \"src/wireguard.c\"\n \"src/wireguardif.c\"\n \"src/wi"
},
{
"path": "Kconfig",
"chars": 977,
"preview": "menu \"WireGuard\"\n\nchoice WIREGUARD_ESP_ADAPTER_SELECTION\n prompt \"TCP/IP adapter to use\"\n default WIREGUARD_ESP_NE"
},
{
"path": "LICENSE",
"chars": 1656,
"preview": "Copyright (c) 2021 Kenta Ida (fuga@fugafuga.org)\n\nThe original license is below:\nCopyright (c) 2021 Daniel Hope (www.flo"
},
{
"path": "README.md",
"chars": 4279,
"preview": "# `esp_wireguard`, WireGuard Implementation for ESP-IDF\n\nThis is an implementation of the [WireGuard®](https://www.w"
},
{
"path": "component.mk",
"chars": 155,
"preview": "COMPONENT_SRCDIRS = \\\n\tsrc \\\n\tsrc/crypto/refc\nCOMPONENT_ADD_INCLUDEDIRS = \\\n\tinclude\nCOMPONENT_PRIV_INCLUDEDIRS = \\\n\tsrc"
},
{
"path": "examples/demo/CMakeLists.txt",
"chars": 101,
"preview": "cmake_minimum_required(VERSION 3.5)\n\ninclude($ENV{IDF_PATH}/tools/cmake/project.cmake)\nproject(demo)\n"
},
{
"path": "examples/demo/Makefile",
"chars": 58,
"preview": "PROJECT_NAME := demo\n\ninclude $(IDF_PATH)/make/project.mk\n"
},
{
"path": "examples/demo/README.md",
"chars": 9264,
"preview": "# demo example\n\n## What the example does\n\nThe example connects to a WireGuard server. When the link is up, the device\nse"
},
{
"path": "examples/demo/Vagrantfile",
"chars": 3196,
"preview": "# 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"
},
{
"path": "examples/demo/main/CMakeLists.txt",
"chars": 156,
"preview": "idf_component_register(SRCS \"main.c\" \"sync_time.c\"\n INCLUDE_DIRS \".\"\n REQUIRES esp"
},
{
"path": "examples/demo/main/Kconfig.projbuild",
"chars": 2512,
"preview": "menu \"Example Configuration\"\n\n config ESP_WIFI_SSID\n string \"WiFi SSID\"\n default \"myssid\"\n help\n"
},
{
"path": "examples/demo/main/component.mk",
"chars": 0,
"preview": ""
},
{
"path": "examples/demo/main/main.c",
"chars": 15205,
"preview": "/* WireGuard demo example\n\n This example code is in the Public Domain (or CC0 licensed, at your option.)\n\n Unless re"
},
{
"path": "examples/demo/main/sync_time.c",
"chars": 884,
"preview": "#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 "
},
{
"path": "examples/demo/main/sync_time.h",
"chars": 89,
"preview": "#if !defined(__SYNC_TIME__H__)\n#define __SYNC_TIME__H__\n\nvoid obtain_time(void);\n\n#endif\n"
},
{
"path": "idf_component.yml",
"chars": 139,
"preview": "---\nversion: \"0.9.0\"\ndescription: WireGuard Implementation for ESP-IDF\nurl: https://github.com/trombik/esp_wireguard\nlic"
},
{
"path": "include/esp_wireguard.h",
"chars": 5396,
"preview": "/*\n * Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>\n * All rights reserved.\n *\n * Redistribution and use in source"
},
{
"path": "src/crypto/refc/blake2s.c",
"chars": 4914,
"preview": "// Taken from RFC7693 - https://tools.ietf.org/html/rfc7693\n\n#include \"blake2s.h\"\n#include \"../../crypto.h\"\n\n// Cyclic r"
},
{
"path": "src/crypto/refc/blake2s.h",
"chars": 1450,
"preview": "// Taken from RFC7693 - https://tools.ietf.org/html/rfc7693\n// BLAKE2s Hashing Context and API Prototypes\n#ifndef _BLAKE"
},
{
"path": "src/crypto/refc/chacha20.c",
"chars": 7705,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source "
},
{
"path": "src/crypto/refc/chacha20.h",
"chars": 2538,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source "
},
{
"path": "src/crypto/refc/chacha20poly1305.c",
"chars": 9590,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source "
},
{
"path": "src/crypto/refc/chacha20poly1305.h",
"chars": 3044,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source "
},
{
"path": "src/crypto/refc/poly1305-donna-32.h",
"chars": 6666,
"preview": "// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT\n/*\n\tpoly1305 implementation using 32 "
},
{
"path": "src/crypto/refc/poly1305-donna.c",
"chars": 1018,
"preview": "// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT\n\n#include \"poly1305-donna.h\"\n#include"
},
{
"path": "src/crypto/refc/poly1305-donna.h",
"chars": 578,
"preview": "// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT\n#ifndef POLY1305_DONNA_H\n#define POLY"
},
{
"path": "src/crypto/refc/x25519-license.txt",
"chars": 1099,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015-2016 Cryptography Research, Inc.\n\nPermission is hereby granted, free of charge"
},
{
"path": "src/crypto/refc/x25519.c",
"chars": 12100,
"preview": "// Taken from https://sourceforge.net/p/strobe (MIT Licence)\n/**\n * @cond internal\n * @file x25519.c\n * @copyright\n * "
},
{
"path": "src/crypto/refc/x25519.h",
"chars": 4385,
"preview": "// Taken from https://sourceforge.net/p/strobe (MIT Licence)\n/**\n * @file x25519.h\n * @copyright\n * Copyright (c) 2016"
},
{
"path": "src/crypto.c",
"chars": 410,
"preview": "#include \"crypto.h\"\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nvoid crypto_zero(void *dest, size_t l"
},
{
"path": "src/crypto.h",
"chars": 3490,
"preview": "#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#"
},
{
"path": "src/esp_wireguard.c",
"chars": 10891,
"preview": "/*\n * Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>\n * All rights reserved.\n *\n * Redistribution and use in source"
},
{
"path": "src/nacl/crypto_scalarmult/curve25519/ref/crypto_scalarmult.h",
"chars": 90,
"preview": "\nint crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned char *p);\n"
},
{
"path": "src/nacl/crypto_scalarmult/curve25519/ref/smult.c",
"chars": 6731,
"preview": "/*\nversion 20081011\nMatthew Dempsky\nPublic domain.\nDerived from public domain code by D. J. Bernstein.\n*/\n\n#include \"cry"
},
{
"path": "src/wireguard-platform.c",
"chars": 2164,
"preview": "#include \"wireguard-platform.h\"\n\n#include <stdlib.h>\n#include <time.h>\n#include <inttypes.h>\n#include <lwip/sys.h>\n#incl"
},
{
"path": "src/wireguard-platform.h",
"chars": 3213,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source "
},
{
"path": "src/wireguard.c",
"chars": 40483,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source "
},
{
"path": "src/wireguard.h",
"chars": 10959,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source "
},
{
"path": "src/wireguardif.c",
"chars": 34819,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * Copyright (c) 2021 Kenta Ida (fuga@fugafuga.org)\n * All righ"
},
{
"path": "src/wireguardif.h",
"chars": 5286,
"preview": "/*\n * Copyright (c) 2021 Daniel Hope (www.floorsense.nz)\n * All rights reserved.\n *\n * Redistribution and use in source "
}
]
About this extraction
This page contains the full source code of the trombik/esp_wireguard GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 44 files (240.3 KB), approximately 74.1k tokens, and a symbol index with 277 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.