Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5777f5b
gitignore build and lsp artifacts
matthew-levan Oct 21, 2025
edd1727
add testing framework
matthew-levan Oct 22, 2025
9c81953
add argon2 tests
matthew-levan Oct 22, 2025
4da676c
add blake3 tests
matthew-levan Oct 22, 2025
80f2f45
add ed25519 w/ ge-additions tests
matthew-levan Oct 23, 2025
6f89cfc
cleanup build, tests, and instructions
matthew-levan Oct 23, 2025
7b02a07
add keccak tests
matthew-levan Oct 27, 2025
f5f5349
add chacha tests
matthew-levan Oct 27, 2025
3a14355
add scrypt tests
matthew-levan Oct 27, 2025
424cd37
cleanup tests
matthew-levan Oct 27, 2025
d0006f1
add urcrypt tests
matthew-levan Oct 28, 2025
435f528
cleanup test running
matthew-levan Oct 28, 2025
f474eb8
cleanup readmes and rm murmur3 tests (not in this repo)
matthew-levan Oct 29, 2025
b260235
do not ignore test runner
matthew-levan Oct 29, 2025
4d619fc
cleanup comments
matthew-levan Oct 30, 2025
5a3abed
add ci
matthew-levan Nov 5, 2025
af7f207
fix point addition ed25519 test on linux
matthew-levan Nov 5, 2025
662d056
factor `test_urcrypt.c` into separate suite files
matthew-levan Nov 7, 2025
46e296b
exports `urcrypt_reverse` (delete one cab) and use it
matthew-levan Nov 7, 2025
7f6dbde
Merge branch 'ml/tests' of https://github.com/urbit/urcrypt into ml/t…
matthew-levan Nov 7, 2025
387536d
re-privatizes `urcrypt__reverse` and links directly to `.o` file for …
matthew-levan Nov 9, 2025
08a0b29
Merge jb/close-ssl into ml/tests
matthew-levan Jun 3, 2026
99e48e8
ci: rebuild workflow for the Nettle / vendored-aes-siv build
matthew-levan Jun 3, 2026
0e6b434
scrypt: pin -std=gnu17 to allow K&R definitions under C23 compilers
matthew-levan Jun 3, 2026
01fdeb6
ci: scope UBSan to urcrypt's own code; build sanitizer job with clang
matthew-levan Jun 3, 2026
4e26d74
ci: fetch nettle from gnutls/nettle mirror; build statically on Linux
matthew-levan Jun 3, 2026
f327ea6
ci: fetch nettle from the signed GNU release tarball, verified by sha256
matthew-levan Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions .github/actions/setup-crypto-deps/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: 'Set up crypto dependencies'
description: >
Build and install GNU Nettle and libsecp256k1 (with the recovery and Schnorr
modules that urcrypt's configure requires) from source into a cached prefix,
then export the NETTLE_*/LIBSECP256K1_* and runtime library-path variables
that ./configure and the test runner need.

inputs:
prefix:
description: 'Install prefix for the built dependencies.'
required: true
nettle-version:
description: 'GNU Nettle release to build from the ftp.gnu.org tarball (e.g. 4.0).'
required: true
nettle-sha256:
description: 'Expected sha256 of nettle-<version>.tar.gz, verified before use.'
required: true
secp256k1-ref:
description: 'libsecp256k1 git tag/ref to build (e.g. v0.7.1).'
required: true

runs:
using: composite
steps:
# Distro/Homebrew packages are too old (nettle < 4.0) or omit the secp256k1
# recovery/Schnorr modules, so both are built from source and cached. The
# cache key pins the exact versions, so a bump invalidates it automatically.
- name: Restore cached dependencies
id: cache
uses: actions/cache@v4
with:
path: ${{ inputs.prefix }}
key: cryptodeps-${{ runner.os }}-${{ runner.arch }}-nettle${{ inputs.nettle-version }}-secp${{ inputs.secp256k1-ref }}-v3

- name: Build GNU Nettle ${{ inputs.nettle-version }}
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: |
set -euo pipefail
# nettle >= 4.0 introduced the 2-argument *_digest() interface urcrypt uses.
# Use the signed GNU release tarball (canonical, ships a generated
# configure) and verify its sha256 before building.
cd "$RUNNER_TEMP"
tarball="nettle-${{ inputs.nettle-version }}.tar.gz"
curl -fsSLO "https://ftp.gnu.org/gnu/nettle/${tarball}"
if command -v sha256sum >/dev/null 2>&1; then
echo "${{ inputs.nettle-sha256 }} ${tarball}" | sha256sum -c -
else
echo "${{ inputs.nettle-sha256 }} ${tarball}" | shasum -a 256 -c -
fi
tar xzf "${tarball}"
cd "nettle-${{ inputs.nettle-version }}"
# On Linux build static libraries (urcrypt links them statically, the
# way urbit/vere consumes it); -fPIC so they still link into PIE binaries.
# macOS can't fully static-link, so build shared there.
if [ "$RUNNER_OS" = "Linux" ]; then
LINKAGE="--enable-static --disable-shared"
export CFLAGS="${CFLAGS:-} -fPIC"
else
LINKAGE="--disable-static"
fi
# --enable-mini-gmp avoids needing libgmp; urcrypt only links libnettle.
./configure --prefix="${{ inputs.prefix }}" --enable-mini-gmp \
--disable-documentation $LINKAGE
make -j"$(getconf _NPROCESSORS_ONLN)"
make install

- name: Build libsecp256k1 ${{ inputs.secp256k1-ref }}
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: |
set -euo pipefail
cd "$RUNNER_TEMP"
git clone --depth 1 --branch "${{ inputs.secp256k1-ref }}" \
https://github.com/bitcoin-core/secp256k1.git
cd secp256k1
./autogen.sh
# Static (with PIC) on Linux to match the static urcrypt link; shared on macOS.
if [ "$RUNNER_OS" = "Linux" ]; then
LINKAGE="--enable-static --disable-shared --with-pic"
else
LINKAGE="--disable-static"
fi
./configure --prefix="${{ inputs.prefix }}" \
--enable-module-recovery --enable-module-schnorrsig --enable-module-ecdh \
--disable-benchmark --disable-tests --disable-exhaustive-tests $LINKAGE
make -j"$(getconf _NPROCESSORS_ONLN)"
make install

- name: Export dependency environment
shell: bash
run: |
set -euo pipefail
prefix="${{ inputs.prefix }}"
# Set the *_CFLAGS/*_LIBS that PKG_CHECK_MODULES honours directly, so the
# build is deterministic regardless of any system pkg-config files.
{
echo "NETTLE_CFLAGS=-I${prefix}/include"
echo "NETTLE_LIBS=-L${prefix}/lib -lnettle"
echo "LIBSECP256K1_CFLAGS=-I${prefix}/include"
echo "LIBSECP256K1_LIBS=-L${prefix}/lib -lsecp256k1"
echo "PKG_CONFIG_PATH=${prefix}/lib/pkgconfig"
} >> "$GITHUB_ENV"
# On macOS the deps are shared, so the test runner needs the loader path.
# On Linux they are linked statically, so no runtime path is required.
if [ "$RUNNER_OS" = "macOS" ]; then
echo "DYLD_LIBRARY_PATH=${prefix}/lib" >> "$GITHUB_ENV"
fi
145 changes: 145 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
name: CI

on:
push:
branches: [jb/close-ssl, master]
pull_request:
branches: [jb/close-ssl, master]
Comment on lines +5 to +7
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's get my branch out of here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including jb/close-ssl temporarily is necessary for CI to run. Once this PR is merged to master, we can remove it.


# Cancel an in-progress run when a newer commit is pushed to the same ref.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
# urcrypt requires nettle >= 4.0 (2-argument *_digest() interface) and a
# libsecp256k1 built with the recovery + Schnorr modules. Neither is available
# from the runners' package managers, so both are built from source (cached).
NETTLE_VERSION: "4.0"
NETTLE_SHA256: "3addbc00da01846b232fb3bc453538ea5468da43033f21bb345cb1e9073f5094"
SECP256K1_REF: "v0.7.1"

jobs:
build-test:
name: build & test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4

- name: Install build tools (Ubuntu)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
autoconf automake libtool pkg-config autoconf-archive m4 build-essential

- name: Install build tools (macOS)
if: runner.os == 'macOS'
run: |
brew install autoconf automake libtool pkg-config autoconf-archive

- name: Set up crypto dependencies
uses: ./.github/actions/setup-crypto-deps
with:
prefix: ${{ github.workspace }}/.deps
nettle-version: ${{ env.NETTLE_VERSION }}
nettle-sha256: ${{ env.NETTLE_SHA256 }}
secp256k1-ref: ${{ env.SECP256K1_REF }}

- name: Build
run: |
./autogen.sh
# Build statically on Linux (the deps are static there, matching how
# urbit/vere links urcrypt); macOS can't fully static-link, so shared.
if [ "$RUNNER_OS" = "Linux" ]; then
./configure --enable-static --disable-shared
else
./configure
fi
make -j"$(getconf _NPROCESSORS_ONLN)"

- name: Test
run: make check

- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: logs-${{ matrix.os }}
path: |
config.log
test-suite.log
test_runner.log
if-no-files-found: ignore

sanitizers:
name: ASan + UBSan (Ubuntu)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y \
autoconf automake libtool pkg-config autoconf-archive m4 build-essential clang

- name: Set up crypto dependencies
uses: ./.github/actions/setup-crypto-deps
with:
prefix: ${{ github.workspace }}/.deps
nettle-version: ${{ env.NETTLE_VERSION }}
nettle-sha256: ${{ env.NETTLE_SHA256 }}
secp256k1-ref: ${{ env.SECP256K1_REF }}

# Built with clang so UBSan can be scoped to urcrypt's own code via an
# ignore list (an -fsanitize-ignorelist= feature gcc lacks). ASan still
# covers everything, including the vendored libraries.
- name: Build & test with sanitizers
env:
# Leak detection is off: the one-shot test runner intentionally does
# not free every allocation, which is not a defect under test.
ASAN_OPTIONS: detect_leaks=0:abort_on_error=1
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
run: |
./autogen.sh
./configure --enable-static --disable-shared CC=clang \
CFLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -fsanitize-ignorelist=${{ github.workspace }}/ci/sanitizer-ignorelist.txt -g -O1" \
LDFLAGS="-fsanitize=address,undefined"
make -j"$(getconf _NPROCESSORS_ONLN)"
make check

- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: logs-sanitizers
path: |
config.log
test-suite.log
test_runner.log
if-no-files-found: ignore

static-analysis:
name: cppcheck (non-blocking)
runs-on: ubuntu-latest
# Advisory only: findings are surfaced but do not fail the workflow.
continue-on-error: true
steps:
- uses: actions/checkout@v4

- name: Install cppcheck
run: |
sudo apt-get update
sudo apt-get install -y cppcheck

- name: Run cppcheck on urcrypt sources
# Analyse only urcrypt's own code, not the bulk-vendored libraries.
run: |
cppcheck --enable=warning,portability --inline-suppr --error-exitcode=1 \
--std=c11 --suppress=missingInclude --suppress=missingIncludeSystem \
-I urcrypt -I aes_siv urcrypt aes_siv
26 changes: 20 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ autom4te.cache
/aclocal.m4
build-aux/compile
/config.cache
build-aux/config.guess
build-aux/config.guess*
/config.h.in
/config.h.in~
build-aux/config.log
build-aux/config.status
build-aux/config.sub
build-aux/config.sub*
/configure
/configure~
/configure.scan
build-aux/depcomp
build-aux/install-sh
build-aux/install-sh*
build-aux/missing
/stamp-h1

Expand All @@ -57,6 +57,20 @@ build-aux/m4/lt~obsolete.m4
# (which is called by configure script))
Makefile

*.o
*.lo
*.la
# clangd
compile_commands.json
**/*.cache
**/*.libs
config.log

# build artifacts
**/*.o
**/*.la
**/*.lo

# tests
build-aux/test-driver
test_runner
test_runner.log
test_runner.trs
test-suite.log
42 changes: 42 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
ACLOCAL_AMFLAGS = -I build-aux/m4

# Use serial test harness to show test output in real-time
AUTOMAKE_OPTIONS = color-tests serial-tests

AM_CFLAGS = -Wall -g -O3

lib_LTLIBRARIES = liburcrypt.la
Expand Down Expand Up @@ -149,7 +152,11 @@ libmonocypher_la_CPPFLAGS = -I$(srcdir)/monocypher
libmonocypher_la_SOURCES = monocypher/monocypher.c

# scrypt
# Vendored libscrypt uses K&R-style function definitions, which are removed in
# C23. Newer compilers (e.g. recent Apple clang) default to C23 and reject them,
# so pin a pre-C23 standard.
libscrypt_la_CPPFLAGS = -D_FORTIFY_SOURCE=2
libscrypt_la_CFLAGS = -std=gnu17
libscrypt_la_SOURCES = scrypt/b64.c \
scrypt/crypto-mcf.c \
scrypt/crypto-scrypt-saltgen.c \
Expand All @@ -170,3 +177,38 @@ libkeccak_tiny_la_CFLAGS = -std=c11 -Wextra -Wpedantic -Wall
libkeccak_tiny_la_SOURCES = keccak-tiny/keccak-tiny.c \
keccak-tiny/define-macros.h \
keccak-tiny/keccak-tiny.h

# test suite
TESTS = test_runner
check_PROGRAMS = test_runner

test_runner_SOURCES = tests/test_runner.c \
tests/test_aes.c \
tests/test_argon2.c \
tests/test_blake3.c \
tests/test_ed25519.c \
tests/test_ge_additions.c \
tests/test_keccak.c \
tests/test_monocypher.c \
tests/test_ripemd.c \
tests/test_scrypt.c \
tests/test_secp256k1.c \
tests/test_sha.c

test_runner_CPPFLAGS = -I$(srcdir) \
-I$(srcdir)/ed25519/src \
-I$(srcdir)/ge-additions \
-I$(srcdir)/argon2/include \
-I$(srcdir)/blake3 \
-I$(srcdir)/monocypher \
-I$(srcdir)/keccak-tiny \
-I$(srcdir)/scrypt \
-I$(srcdir)/urcrypt

test_runner_CFLAGS = $(NETTLE_CFLAGS) \
$(LIBSECP256K1_CFLAGS)

test_runner_LDADD = liburcrypt.la \
urcrypt/liburcrypt_la-util.o \
$(NETTLE_LIBS) \
$(LIBSECP256K1_LIBS)
Loading
Loading