浏览代码

pub.libhydrogen. Initial Import.

woollybah 6 年之前
父节点
当前提交
798e97dc77

+ 30 - 0
libhydrogen.mod/libhydrogen.bmx

@@ -0,0 +1,30 @@
+'
+' Copyright (c) 2019 Bruce A Henderson
+'
+' Permission to use, copy, modify, and/or distribute this software for any
+' purpose with or without fee is hereby granted, provided that the above
+' copyright notice and this permission notice appear in all copies.
+'
+' THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+' WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+' MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+' ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+' WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+' ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+' OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+'
+SuperStrict
+
+
+Module Pub.LibHydrogen
+
+
+ModuleInfo "Version: 1.00"
+ModuleInfo "Author: Frank Denis."
+ModuleInfo "License: ISC"
+ModuleInfo "Credit: Adapted for BlitzMax by Bruce A Henderson"
+
+ModuleInfo "History: 1.00"
+ModuleInfo "History: Initial Release."
+
+Import "source.bmx"

+ 95 - 0
libhydrogen.mod/libhydrogen/.clang-format

@@ -0,0 +1,95 @@
+---
+Language:        Cpp
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: true
+AlignConsecutiveDeclarations: true
+AlignEscapedNewlinesLeft: true
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: TopLevelDefinitions
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:   
+  AfterClass:      false
+  AfterControlStatement: false
+  AfterEnum:       false
+  AfterFunction:   true
+  AfterNamespace:  false
+  AfterObjCDeclaration: false
+  AfterStruct:     false
+  AfterUnion:      false
+  BeforeCatch:     false
+  BeforeElse:      false
+  IndentBraces:    false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: WebKit
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: true
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit:     100
+CommentPragmas:  '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: false
+DerivePointerAlignment: true
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeCategories: 
+  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
+    Priority:        2
+  - Regex:           '^(<|"(gtest|isl|json)/)'
+    Priority:        3
+  - Regex:           '.*'
+    Priority:        1
+IncludeIsMainRegex: '$'
+IndentCaseLabels: false
+IndentWidth:     4
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: Inner
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments:  true
+SortIncludes:    true
+SpaceAfterCStyleCast: true
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Cpp11
+TabWidth:        8
+UseTab:          Never
+...
+

+ 32 - 0
libhydrogen.mod/libhydrogen/.gitignore

@@ -0,0 +1,32 @@
+*.bc
+*.cmake
+*.dSYM
+*.done
+*.final
+*.gcda
+*.gcno
+*.i
+*.la
+*.lo
+*.log
+*.mem
+*.nexe
+*.o
+*.plist
+*.scan
+*.sdf
+*.status
+*.su
+*.tar.*
+*~
+.DS_Store
+.deps
+.dirstamp
+.done
+.libs
+build.options.json
+coverage.info
+depcomp
+hydrogen-crypto.zip
+libhydrogen.a
+tests/tests

+ 22 - 0
libhydrogen.mod/libhydrogen/.travis.yml

@@ -0,0 +1,22 @@
+sudo: false
+
+language: c
+
+os:
+ - linux
+
+compiler:
+ - clang
+ - gcc
+ - g++
+
+addons:
+  apt:
+    packages:
+      - p7zip-full
+
+script:
+ - make
+ - make test
+ - make clean
+ - make -f Makefile.arduino package

+ 18 - 0
libhydrogen.mod/libhydrogen/LICENSE

@@ -0,0 +1,18 @@
+/*
+ * ISC License
+ *
+ * Copyright (c) 2017-2019
+ * Frank Denis <j at pureftpd dot org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */

+ 61 - 0
libhydrogen.mod/libhydrogen/Makefile

@@ -0,0 +1,61 @@
+PREFIX ?= /usr/local
+WFLAGS ?= -Wall -Wextra -Wmissing-prototypes -Wdiv-by-zero -Wbad-function-cast -Wcast-align -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wnested-externs -Wno-unknown-pragmas -Wpointer-arith -Wredundant-decls -Wstrict-prototypes -Wswitch-enum -Wno-type-limits
+CFLAGS ?= -Os -march=native -fno-exceptions $(WFLAGS)
+CFLAGS += -I.
+OBJ = hydrogen.o
+AR ?= ar
+RANLIB ?= ranlib
+
+SRC = \
+	hydrogen.c \
+	hydrogen.h \
+	impl/common.h \
+	impl/core.h \
+	impl/gimli-core.h \
+	impl/hash.h \
+	impl/hydrogen_p.h \
+	impl/kdf.h \
+	impl/kx.h \
+	impl/pwhash.h \
+	impl/random.h \
+	impl/secretbox.h \
+	impl/sign.h \
+	impl/x25519.h
+
+all: lib test
+
+lib: libhydrogen.a
+
+install: lib
+	mkdir -p $(PREFIX)/lib
+	install -o 0 -g 0 -m 0755 libhydrogen.a $(PREFIX)/lib 2> /dev/null || install -m 0755 libhydrogen.a $(PREFIX)/lib
+	mkdir -p $(PREFIX)/include
+	install -o 0 -g 0 -m 0644 hydrogen.h $(PREFIX)/include 2> /dev/null || install -m 0644 hydrogen.h $(PREFIX)/include
+	ldconfig 2> /dev/null || true
+
+uninstall:
+	rm -f $(PREFIX)/lib/libhydrogen.a
+	rm -f $(PREFIX)/include/hydrogen.h
+
+test: tests/tests
+	rm -f tests/tests.done
+	tests/tests && touch tests/tests.done
+
+tests/tests: $(SRC) tests/tests.c
+	$(CC) $(CFLAGS) -O3 -o tests/tests hydrogen.c tests/tests.c
+
+$(OBJ): $(SRC)
+
+libhydrogen.a: $(OBJ)
+	$(AR) -r $@ $^
+	$(RANLIB) $@
+
+.PHONY: clean
+
+clean:
+	rm -f libhydrogen.a $(OBJ)
+	rm -f tests/tests tests/*.done
+
+check: test
+
+distclean: clean

+ 51 - 0
libhydrogen.mod/libhydrogen/Makefile.arduino

@@ -0,0 +1,51 @@
+TARGET_DEVICE ?= atmega328p
+HWTYPE ?= HYDRO_TARGET_DEVICE_ATMEGA328
+ARDUINO_HOME ?= /Applications/Arduino.app/Contents/Java
+ARDUINO_TOOLS ?= $(ARDUINO_HOME)/hardware/tools/avr/bin
+AR = $(ARDUINO_TOOLS)/avr-gcc-ar
+CC = $(ARDUINO_TOOLS)/avr-gcc
+RANLIB = $(ARDUINO_TOOLS)/avr-gcc-ranlib
+WFLAGS ?= -Wall -Wextra -Wmissing-prototypes -Wdiv-by-zero -Wbad-function-cast -Wcast-align -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wnested-externs -Wno-unknown-pragmas -Wpointer-arith -Wredundant-decls -Wstrict-prototypes -Wswitch-enum -Wno-type-limits
+CFLAGS ?= -mmcu=$(TARGET_DEVICE) -Os -mcall-prologues -fno-exceptions -ffunction-sections -fdata-sections -flto $(WFLAGS)
+CFLAGS += -I. -I$(ARDUINO_HOME)/hardware/arduino/avr/cores/arduino -I$(ARDUINO_HOME)/hardware/arduino/avr/variants/standard
+CFLAGS += -DHYDRO_HWTYPE=$(HYDRO_HWTYPE)
+OBJ = hydrogen.o
+ARDUINO_PACKAGE ?= hydrogen-crypto.zip
+SRC = \
+	hydrogen.c \
+	hydrogen.h \
+	impl/common.h \
+	impl/core.h \
+	impl/gimli-core.h \
+	impl/hash.h \
+	impl/hydrogen_p.h \
+	impl/kdf.h \
+	impl/kx.h \
+	impl/pwhash.h \
+	impl/random.h \
+	impl/secretbox.h \
+	impl/sign.h \
+	impl/x25519.h \
+	impl/gimli-core/portable.h
+
+all: lib package
+
+package: $(ARDUINO_PACKAGE)
+
+$(ARDUINO_PACKAGE):
+	7z a -tzip -mx=9 -r $(ARDUINO_PACKAGE) $(SRC) library.properties
+
+lib: libhydrogen.a
+
+$(OBJ): $(SRC)
+
+libhydrogen.a: $(OBJ)
+	$(AR) -ar cr $@ $^
+	$(RANLIB) $@
+
+.PHONY: clean
+
+clean:
+	rm -f libhydrogen.a $(OBJ)
+	rm -f tests/tests
+	rm -f $(ARDUINO_PACKAGE)

+ 29 - 0
libhydrogen.mod/libhydrogen/README.md

@@ -0,0 +1,29 @@
+[![Build Status](https://travis-ci.org/jedisct1/libhydrogen.svg?branch=master)](https://travis-ci.org/jedisct1/libhydrogen?branch=master)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/13315/badge.svg)](https://scan.coverity.com/projects/13315)
+
+![libhydrogen](https://raw.github.com/jedisct1/libhydrogen/master/logo.png)
+==============
+
+The Hydrogen library is a small, easy-to-use, hard-to-misuse cryptographic library.
+
+Features:
+- Consistent high-level API, inspired by libsodium. Instead of low-level primitives, it exposes simple functions to solve common problems that cryptography can solve.
+- 100% built using just two cryptographic building blocks: the [Curve25519](https://cr.yp.to/ecdh.html) elliptic curve, and the [Gimli](https://gimli.cr.yp.to/) permutation.
+- Small and easy to audit. Implemented as one tiny file for every set of operation, and adding a single `.c` file to your project is all it takes to use libhydrogen in your project.
+- The whole code is released under a single, very liberal license (ISC).
+- Zero dynamic memory allocations and low stack requirements (median: 32 bytes, max: 128 bytes). This makes it usable in constrained environments such as microcontrollers.
+- Portable: written in standard C99. Supports Linux, *BSD, MacOS, Windows, and the Arduino IDE out of the box.
+- Can generate cryptographically-secure random numbers, even on Arduino boards.
+- Attempts to mitigate the implications of accidental misuse, even on systems with an unreliable PRG and/or no clock.
+
+Non-goals:
+- Having multiple primitives serving the same purpose, even to provide compatibility with other libraries.
+- Networking -- but a simple key exchange API based on the Noise protocol is available, and a STROBE-based transport API will be implemented.
+- Interoperability with other libraries.
+- Replacing libsodium. Libhydrogen tries to keep the number of APIs and the code size down to a minimum.
+
+# [Libhydrogen documentation](https://github.com/jedisct1/libhydrogen/wiki)
+
+The documentation is maintained in the [libhydrogen wiki](https://github.com/jedisct1/libhydrogen/wiki).
+
+The legacy libhydrogen code (leveraging XChaCha20, SipHashX, BLAKE2SX, Curve25519) remains available in the [v0 branch](https://github.com/jedisct1/libhydrogen/tree/v0).

+ 18 - 0
libhydrogen.mod/libhydrogen/hydrogen.c

@@ -0,0 +1,18 @@
+#include "hydrogen.h"
+
+#include "impl/common.h"
+#include "impl/hydrogen_p.h"
+
+#include "impl/core.h"
+#include "impl/gimli-core.h"
+#include "impl/random.h"
+
+#include "impl/hash.h"
+#include "impl/kdf.h"
+#include "impl/secretbox.h"
+
+#include "impl/x25519.h"
+
+#include "impl/kx.h"
+#include "impl/pwhash.h"
+#include "impl/sign.h"

+ 317 - 0
libhydrogen.mod/libhydrogen/hydrogen.h

@@ -0,0 +1,317 @@
+#ifndef hydrogen_H
+#define hydrogen_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+# ifdef __GNUC__
+#  pragma GCC diagnostic ignored "-Wlong-long"
+# endif
+extern "C" {
+#endif
+
+#if defined(__clang__) || defined(__GNUC__)
+# define _hydro_attr_(X) __attribute__(X)
+#else
+# define _hydro_attr_(X)
+#endif
+#define _hydro_attr_deprecated_         _hydro_attr_((deprecated))
+#define _hydro_attr_malloc_             _hydro_attr_((malloc))
+#define _hydro_attr_noinline_           _hydro_attr_((noinline))
+#define _hydro_attr_noreturn_           _hydro_attr_((noreturn))
+#define _hydro_attr_warn_unused_result_ _hydro_attr_((warn_unused_result))
+#define _hydro_attr_weak_               _hydro_attr_((weak))
+
+#if defined(__INTEL_COMPILER) || defined(_MSC_VER)
+# define _hydro_attr_aligned_(X)        __declspec(align(X))
+#elif defined(__clang__) || defined(__GNUC__)
+# define _hydro_attr_aligned_(X)        _hydro_attr_((aligned(X)))
+#else
+# define _hydro_attr_aligned_(X)
+#endif
+
+#define HYDRO_VERSION_MAJOR 1
+#define HYDRO_VERSION_MINOR 0
+
+int hydro_init(void);
+
+/* ---------------- */
+
+#define hydro_random_SEEDBYTES 32
+
+uint32_t hydro_random_u32(void);
+
+uint32_t hydro_random_uniform(const uint32_t upper_bound);
+
+void hydro_random_buf(void *out, size_t out_len);
+
+void hydro_random_buf_deterministic(void *out, size_t out_len,
+                                    const uint8_t seed[hydro_random_SEEDBYTES]);
+
+void hydro_random_ratchet(void);
+
+void hydro_random_reseed(void);
+
+/* ---------------- */
+
+#define hydro_hash_BYTES 32
+#define hydro_hash_BYTES_MAX 65535
+#define hydro_hash_BYTES_MIN 16
+#define hydro_hash_CONTEXTBYTES 8
+#define hydro_hash_KEYBYTES 32
+
+typedef struct hydro_hash_state {
+    uint32_t state[12];
+    uint8_t  buf_off;
+    uint8_t  align[3];
+} hydro_hash_state;
+
+void hydro_hash_keygen(uint8_t key[hydro_hash_KEYBYTES]);
+
+int hydro_hash_init(hydro_hash_state *state, const char ctx[hydro_hash_CONTEXTBYTES],
+                    const uint8_t key[hydro_hash_KEYBYTES]);
+
+int hydro_hash_update(hydro_hash_state *state, const void *in_, size_t in_len);
+
+int hydro_hash_final(hydro_hash_state *state, uint8_t *out, size_t out_len);
+
+int hydro_hash_hash(uint8_t *out, size_t out_len, const void *in_, size_t in_len,
+                    const char    ctx[hydro_hash_CONTEXTBYTES],
+                    const uint8_t key[hydro_hash_KEYBYTES]);
+
+/* ---------------- */
+
+#define hydro_secretbox_CONTEXTBYTES 8
+#define hydro_secretbox_HEADERBYTES (20 + 16)
+#define hydro_secretbox_KEYBYTES 32
+#define hydro_secretbox_PROBEBYTES 16
+
+void hydro_secretbox_keygen(uint8_t key[hydro_secretbox_KEYBYTES]);
+
+int hydro_secretbox_encrypt(uint8_t *c, const void *m_, size_t mlen, uint64_t msg_id,
+                            const char    ctx[hydro_secretbox_CONTEXTBYTES],
+                            const uint8_t key[hydro_secretbox_KEYBYTES]);
+
+int hydro_secretbox_decrypt(void *m_, const uint8_t *c, size_t clen, uint64_t msg_id,
+                            const char    ctx[hydro_secretbox_CONTEXTBYTES],
+                            const uint8_t key[hydro_secretbox_KEYBYTES])
+    _hydro_attr_warn_unused_result_;
+
+void hydro_secretbox_probe_create(uint8_t probe[hydro_secretbox_PROBEBYTES], const uint8_t *c,
+                                  size_t c_len, const char ctx[hydro_secretbox_CONTEXTBYTES],
+                                  const uint8_t key[hydro_secretbox_KEYBYTES]);
+
+int hydro_secretbox_probe_verify(const uint8_t probe[hydro_secretbox_PROBEBYTES], const uint8_t *c,
+                                 size_t c_len, const char ctx[hydro_secretbox_CONTEXTBYTES],
+                                 const uint8_t key[hydro_secretbox_KEYBYTES])
+    _hydro_attr_warn_unused_result_;
+
+/* ---------------- */
+
+#define hydro_kdf_CONTEXTBYTES 8
+#define hydro_kdf_KEYBYTES 32
+#define hydro_kdf_BYTES_MAX 65535
+#define hydro_kdf_BYTES_MIN 16
+
+void hydro_kdf_keygen(uint8_t key[hydro_kdf_KEYBYTES]);
+
+int hydro_kdf_derive_from_key(uint8_t *subkey, size_t subkey_len, uint64_t subkey_id,
+                              const char    ctx[hydro_kdf_CONTEXTBYTES],
+                              const uint8_t key[hydro_kdf_KEYBYTES]);
+
+/* ---------------- */
+
+#define hydro_sign_BYTES 64
+#define hydro_sign_CONTEXTBYTES 8
+#define hydro_sign_PUBLICKEYBYTES 32
+#define hydro_sign_SECRETKEYBYTES 64
+#define hydro_sign_SEEDBYTES 32
+
+typedef struct hydro_sign_state {
+    hydro_hash_state hash_st;
+} hydro_sign_state;
+
+typedef struct hydro_sign_keypair {
+    uint8_t pk[hydro_sign_PUBLICKEYBYTES];
+    uint8_t sk[hydro_sign_SECRETKEYBYTES];
+} hydro_sign_keypair;
+
+void hydro_sign_keygen(hydro_sign_keypair *kp);
+
+void hydro_sign_keygen_deterministic(hydro_sign_keypair *kp,
+                                     const uint8_t       seed[hydro_sign_SEEDBYTES]);
+
+int hydro_sign_init(hydro_sign_state *state, const char ctx[hydro_sign_CONTEXTBYTES]);
+
+int hydro_sign_update(hydro_sign_state *state, const void *m_, size_t mlen);
+
+int hydro_sign_final_create(hydro_sign_state *state, uint8_t csig[hydro_sign_BYTES],
+                            const uint8_t sk[hydro_sign_SECRETKEYBYTES]);
+
+int hydro_sign_final_verify(hydro_sign_state *state, const uint8_t csig[hydro_sign_BYTES],
+                            const uint8_t pk[hydro_sign_PUBLICKEYBYTES])
+    _hydro_attr_warn_unused_result_;
+
+int hydro_sign_create(uint8_t csig[hydro_sign_BYTES], const void *m_, size_t mlen,
+                      const char    ctx[hydro_sign_CONTEXTBYTES],
+                      const uint8_t sk[hydro_sign_SECRETKEYBYTES]);
+
+int hydro_sign_verify(const uint8_t csig[hydro_sign_BYTES], const void *m_, size_t mlen,
+                      const char    ctx[hydro_sign_CONTEXTBYTES],
+                      const uint8_t pk[hydro_sign_PUBLICKEYBYTES]) _hydro_attr_warn_unused_result_;
+
+/* ---------------- */
+
+#define hydro_kx_SESSIONKEYBYTES 32
+#define hydro_kx_PUBLICKEYBYTES 32
+#define hydro_kx_SECRETKEYBYTES 32
+#define hydro_kx_PSKBYTES 32
+#define hydro_kx_SEEDBYTES 32
+
+typedef struct hydro_kx_keypair {
+    uint8_t pk[hydro_kx_PUBLICKEYBYTES];
+    uint8_t sk[hydro_kx_SECRETKEYBYTES];
+} hydro_kx_keypair;
+
+typedef struct hydro_kx_session_keypair {
+    uint8_t rx[hydro_kx_SESSIONKEYBYTES];
+    uint8_t tx[hydro_kx_SESSIONKEYBYTES];
+} hydro_kx_session_keypair;
+
+typedef struct hydro_kx_state {
+    hydro_kx_keypair eph_kp;
+    uint8_t          h[32];
+    uint8_t          ck[32];
+    uint8_t          k[32];
+} hydro_kx_state;
+
+void hydro_kx_keygen(hydro_kx_keypair *static_kp);
+
+void hydro_kx_keygen_deterministic(hydro_kx_keypair *static_kp,
+                                   const uint8_t     seed[hydro_kx_SEEDBYTES]);
+
+/* NOISE_N */
+
+#define hydro_kx_N_PACKET1BYTES 32
+
+int hydro_kx_n_1(hydro_kx_session_keypair *kp, uint8_t packet1[hydro_kx_N_PACKET1BYTES],
+                 const uint8_t psk[hydro_kx_PSKBYTES],
+                 const uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES]);
+
+int hydro_kx_n_2(hydro_kx_session_keypair *kp, const uint8_t packet1[hydro_kx_N_PACKET1BYTES],
+                 const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp);
+
+/* NOISE_KK */
+
+#define hydro_kx_KK_PACKET1BYTES 32
+#define hydro_kx_KK_PACKET2BYTES 32
+
+int hydro_kx_kk_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_KK_PACKET1BYTES],
+                  const uint8_t           peer_static_pk[hydro_kx_PUBLICKEYBYTES],
+                  const hydro_kx_keypair *static_kp);
+
+int hydro_kx_kk_2(hydro_kx_session_keypair *kp, uint8_t packet2[hydro_kx_KK_PACKET2BYTES],
+                  const uint8_t           packet1[hydro_kx_KK_PACKET1BYTES],
+                  const uint8_t           peer_static_pk[hydro_kx_PUBLICKEYBYTES],
+                  const hydro_kx_keypair *static_kp);
+
+int hydro_kx_kk_3(hydro_kx_state *state, hydro_kx_session_keypair *kp,
+                  const uint8_t packet2[hydro_kx_KK_PACKET2BYTES],
+                  const hydro_kx_keypair *static_kp);
+
+/* NOISE_XX */
+
+#define hydro_kx_XX_PACKET1BYTES 32
+#define hydro_kx_XX_PACKET2BYTES 80
+#define hydro_kx_XX_PACKET3BYTES 48
+
+int hydro_kx_xx_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_XX_PACKET1BYTES],
+                  const uint8_t psk[hydro_kx_PSKBYTES]);
+
+int hydro_kx_xx_2(hydro_kx_state *state, uint8_t packet2[hydro_kx_XX_PACKET2BYTES],
+                  const uint8_t packet1[hydro_kx_XX_PACKET1BYTES],
+                  const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp);
+
+int hydro_kx_xx_3(hydro_kx_state *state, hydro_kx_session_keypair *kp,
+                  uint8_t       packet3[hydro_kx_XX_PACKET3BYTES],
+                  uint8_t       peer_static_pk[hydro_kx_PUBLICKEYBYTES],
+                  const uint8_t packet2[hydro_kx_XX_PACKET2BYTES],
+                  const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp);
+
+int hydro_kx_xx_4(hydro_kx_state *state, hydro_kx_session_keypair *kp,
+                  uint8_t       peer_static_pk[hydro_kx_PUBLICKEYBYTES],
+                  const uint8_t packet3[hydro_kx_XX_PACKET3BYTES],
+                  const uint8_t psk[hydro_kx_PSKBYTES]);
+
+/* ---------------- */
+
+#define hydro_pwhash_CONTEXTBYTES 8
+#define hydro_pwhash_MASTERKEYBYTES 32
+#define hydro_pwhash_STOREDBYTES 128
+
+void hydro_pwhash_keygen(uint8_t master_key[hydro_pwhash_MASTERKEYBYTES]);
+
+int hydro_pwhash_deterministic(uint8_t *h, size_t h_len, const char *passwd, size_t passwd_len,
+                               const char    ctx[hydro_pwhash_CONTEXTBYTES],
+                               const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                               uint64_t opslimit, size_t memlimit, uint8_t threads);
+
+int hydro_pwhash_create(uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd,
+                        size_t passwd_len, const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                        uint64_t opslimit, size_t memlimit, uint8_t threads);
+
+int hydro_pwhash_verify(const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd,
+                        size_t passwd_len, const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                        uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max);
+
+int hydro_pwhash_derive_static_key(uint8_t *static_key, size_t static_key_len,
+                                   const uint8_t stored[hydro_pwhash_STOREDBYTES],
+                                   const char *passwd, size_t passwd_len,
+                                   const char    ctx[hydro_pwhash_CONTEXTBYTES],
+                                   const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                                   uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max);
+
+int hydro_pwhash_reencrypt(uint8_t       stored[hydro_pwhash_STOREDBYTES],
+                           const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                           const uint8_t new_master_key[hydro_pwhash_MASTERKEYBYTES]);
+
+int hydro_pwhash_upgrade(uint8_t       stored[hydro_pwhash_STOREDBYTES],
+                         const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
+                         size_t memlimit, uint8_t threads);
+
+/* ---------------- */
+
+void hydro_memzero(void *pnt, size_t len);
+
+void hydro_increment(uint8_t *n, size_t len);
+
+bool hydro_equal(const void *b1_, const void *b2_, size_t len);
+
+int hydro_compare(const uint8_t *b1_, const uint8_t *b2_, size_t len);
+
+char *hydro_bin2hex(char *hex, size_t hex_maxlen, const uint8_t *bin, size_t bin_len);
+
+int hydro_hex2bin(uint8_t *bin, size_t bin_maxlen, const char *hex, size_t hex_len,
+                  const char *ignore, const char **hex_end_p);
+
+int hydro_pad(unsigned char *buf, size_t unpadded_buflen, size_t blocksize, size_t max_buflen);
+
+int hydro_unpad(const unsigned char *buf, size_t padded_buflen, size_t blocksize);
+
+/* ---------------- */
+
+#define HYDRO_HWTYPE_ATMEGA328 1
+
+#ifndef HYDRO_HWTYPE
+# ifdef __AVR__
+#  define HYDRO_HWTYPE HYDRO_HWTYPE_ATMEGA328
+# endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 324 - 0
libhydrogen.mod/libhydrogen/impl/common.h

@@ -0,0 +1,324 @@
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../hydrogen.h"
+
+#if !defined(__unix__) && (defined(__APPLE__) || defined(__linux__))
+# define __unix__ 1
+#endif
+#ifndef __GNUC__
+# define __restrict__
+#endif
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+    __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define NATIVE_BIG_ENDIAN
+#endif
+#ifndef NATIVE_BIG_ENDIAN
+# ifndef NATIVE_LITTLE_ENDIAN
+#  define NATIVE_LITTLE_ENDIAN
+# endif
+#endif
+
+#ifndef TLS
+# if defined(_WIN32) && !defined(__GNUC__)
+#  define TLS __declspec(thread)
+# elif (defined(__clang__) || defined(__GNUC__)) && defined(__unix__)
+#  define TLS __thread
+# else
+#  define TLS
+# endif
+#endif
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#ifdef __OpenBSD__
+# define HAVE_EXPLICIT_BZERO 1
+#elif defined(__GLIBC__) && defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE)
+# if __GLIBC_PREREQ(2, 25)
+#  define HAVE_EXPLICIT_BZERO 1
+# endif
+#endif
+
+#define COMPILER_ASSERT(X) (void) sizeof(char[(X) ? 1 : -1])
+
+#define ROTL32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
+#define ROTL64(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
+#define ROTR32(x, b) (uint32_t)(((x) >> (b)) | ((x) << (32 - (b))))
+#define ROTR64(x, b) (uint64_t)(((x) >> (b)) | ((x) << (64 - (b))))
+
+#define LOAD64_LE(SRC) load64_le(SRC)
+static inline uint64_t
+load64_le(const uint8_t src[8])
+{
+#ifdef NATIVE_LITTLE_ENDIAN
+    uint64_t w;
+    memcpy(&w, src, sizeof w);
+    return w;
+#else
+    uint64_t w = (uint64_t) src[0];
+    w |= (uint64_t) src[1] << 8;
+    w |= (uint64_t) src[2] << 16;
+    w |= (uint64_t) src[3] << 24;
+    w |= (uint64_t) src[4] << 32;
+    w |= (uint64_t) src[5] << 40;
+    w |= (uint64_t) src[6] << 48;
+    w |= (uint64_t) src[7] << 56;
+    return w;
+#endif
+}
+
+#define STORE64_LE(DST, W) store64_le((DST), (W))
+static inline void
+store64_le(uint8_t dst[8], uint64_t w)
+{
+#ifdef NATIVE_LITTLE_ENDIAN
+    memcpy(dst, &w, sizeof w);
+#else
+    dst[0] = (uint8_t) w;
+    w >>= 8;
+    dst[1] = (uint8_t) w;
+    w >>= 8;
+    dst[2] = (uint8_t) w;
+    w >>= 8;
+    dst[3] = (uint8_t) w;
+    w >>= 8;
+    dst[4] = (uint8_t) w;
+    w >>= 8;
+    dst[5] = (uint8_t) w;
+    w >>= 8;
+    dst[6] = (uint8_t) w;
+    w >>= 8;
+    dst[7] = (uint8_t) w;
+#endif
+}
+
+#define LOAD32_LE(SRC) load32_le(SRC)
+static inline uint32_t
+load32_le(const uint8_t src[4])
+{
+#ifdef NATIVE_LITTLE_ENDIAN
+    uint32_t w;
+    memcpy(&w, src, sizeof w);
+    return w;
+#else
+    uint32_t w = (uint32_t) src[0];
+    w |= (uint32_t) src[1] << 8;
+    w |= (uint32_t) src[2] << 16;
+    w |= (uint32_t) src[3] << 24;
+    return w;
+#endif
+}
+
+#define STORE32_LE(DST, W) store32_le((DST), (W))
+static inline void
+store32_le(uint8_t dst[4], uint32_t w)
+{
+#ifdef NATIVE_LITTLE_ENDIAN
+    memcpy(dst, &w, sizeof w);
+#else
+    dst[0] = (uint8_t) w;
+    w >>= 8;
+    dst[1] = (uint8_t) w;
+    w >>= 8;
+    dst[2] = (uint8_t) w;
+    w >>= 8;
+    dst[3] = (uint8_t) w;
+#endif
+}
+
+#define LOAD16_LE(SRC) load16_le(SRC)
+static inline uint16_t
+load16_le(const uint8_t src[2])
+{
+#ifdef NATIVE_LITTLE_ENDIAN
+    uint16_t w;
+    memcpy(&w, src, sizeof w);
+    return w;
+#else
+    uint16_t w = (uint16_t) src[0];
+    w |= (uint16_t) src[1] << 8;
+    return w;
+#endif
+}
+
+#define STORE16_LE(DST, W) store16_le((DST), (W))
+static inline void
+store16_le(uint8_t dst[2], uint16_t w)
+{
+#ifdef NATIVE_LITTLE_ENDIAN
+    memcpy(dst, &w, sizeof w);
+#else
+    dst[0] = (uint8_t) w;
+    w >>= 8;
+    dst[1] = (uint8_t) w;
+#endif
+}
+
+/* ----- */
+
+#define LOAD64_BE(SRC) load64_be(SRC)
+static inline uint64_t
+load64_be(const uint8_t src[8])
+{
+#ifdef NATIVE_BIG_ENDIAN
+    uint64_t w;
+    memcpy(&w, src, sizeof w);
+    return w;
+#else
+    uint64_t w = (uint64_t) src[7];
+    w |= (uint64_t) src[6] << 8;
+    w |= (uint64_t) src[5] << 16;
+    w |= (uint64_t) src[4] << 24;
+    w |= (uint64_t) src[3] << 32;
+    w |= (uint64_t) src[2] << 40;
+    w |= (uint64_t) src[1] << 48;
+    w |= (uint64_t) src[0] << 56;
+    return w;
+#endif
+}
+
+#define STORE64_BE(DST, W) store64_be((DST), (W))
+static inline void
+store64_be(uint8_t dst[8], uint64_t w)
+{
+#ifdef NATIVE_BIG_ENDIAN
+    memcpy(dst, &w, sizeof w);
+#else
+    dst[7] = (uint8_t) w;
+    w >>= 8;
+    dst[6] = (uint8_t) w;
+    w >>= 8;
+    dst[5] = (uint8_t) w;
+    w >>= 8;
+    dst[4] = (uint8_t) w;
+    w >>= 8;
+    dst[3] = (uint8_t) w;
+    w >>= 8;
+    dst[2] = (uint8_t) w;
+    w >>= 8;
+    dst[1] = (uint8_t) w;
+    w >>= 8;
+    dst[0] = (uint8_t) w;
+#endif
+}
+
+#define LOAD32_BE(SRC) load32_be(SRC)
+static inline uint32_t
+load32_be(const uint8_t src[4])
+{
+#ifdef NATIVE_BIG_ENDIAN
+    uint32_t w;
+    memcpy(&w, src, sizeof w);
+    return w;
+#else
+    uint32_t w = (uint32_t) src[3];
+    w |= (uint32_t) src[2] << 8;
+    w |= (uint32_t) src[1] << 16;
+    w |= (uint32_t) src[0] << 24;
+    return w;
+#endif
+}
+
+#define STORE32_BE(DST, W) store32_be((DST), (W))
+static inline void
+store32_be(uint8_t dst[4], uint32_t w)
+{
+#ifdef NATIVE_BIG_ENDIAN
+    memcpy(dst, &w, sizeof w);
+#else
+    dst[3] = (uint8_t) w;
+    w >>= 8;
+    dst[2] = (uint8_t) w;
+    w >>= 8;
+    dst[1] = (uint8_t) w;
+    w >>= 8;
+    dst[0] = (uint8_t) w;
+#endif
+}
+
+#define LOAD16_BE(SRC) load16_be(SRC)
+static inline uint16_t
+load16_be(const uint8_t src[2])
+{
+#ifdef NATIVE_BIG_ENDIAN
+    uint16_t w;
+    memcpy(&w, src, sizeof w);
+    return w;
+#else
+    uint16_t w = (uint16_t) src[1];
+    w |= (uint16_t) src[0] << 8;
+    return w;
+#endif
+}
+
+#define STORE16_BE(DST, W) store16_be((DST), (W))
+static inline void
+store16_be(uint8_t dst[2], uint16_t w)
+{
+#ifdef NATIVE_BIG_ENDIAN
+    memcpy(dst, &w, sizeof w);
+#else
+    dst[1] = (uint8_t) w;
+    w >>= 8;
+    dst[0] = (uint8_t) w;
+#endif
+}
+
+static inline void
+mem_cpy(void *__restrict__ dst_, const void *__restrict__ src_, size_t n)
+{
+    unsigned char *      dst = (unsigned char *) dst_;
+    const unsigned char *src = (const unsigned char *) src_;
+    size_t               i;
+
+    for (i = 0; i < n; i++) {
+        dst[i] = src[i];
+    }
+}
+
+static inline void
+mem_zero(void *dst_, size_t n)
+{
+    unsigned char *dst = (unsigned char *) dst_;
+    size_t         i;
+
+    for (i = 0; i < n; i++) {
+        dst[i] = 0;
+    }
+}
+
+static inline void
+mem_xor(void *__restrict__ dst_, const void *__restrict__ src_, size_t n)
+{
+    unsigned char *      dst = (unsigned char *) dst_;
+    const unsigned char *src = (const unsigned char *) src_;
+    size_t               i;
+
+    for (i = 0; i < n; i++) {
+        dst[i] ^= src[i];
+    }
+}
+
+static inline void
+mem_xor2(void *__restrict__ dst_, const void *__restrict__ src1_, const void *__restrict__ src2_,
+         size_t n)
+{
+    unsigned char *      dst  = (unsigned char *) dst_;
+    const unsigned char *src1 = (const unsigned char *) src1_;
+    const unsigned char *src2 = (const unsigned char *) src2_;
+    size_t               i;
+
+    for (i = 0; i < n; i++) {
+        dst[i] = src1[i] ^ src2[i];
+    }
+}
+
+static const uint8_t zero[64] = { 0 };

+ 224 - 0
libhydrogen.mod/libhydrogen/impl/core.h

@@ -0,0 +1,224 @@
+int
+hydro_init(void)
+{
+    if (hydro_random_init() != 0) {
+        abort();
+    }
+    return 0;
+}
+
+void
+hydro_memzero(void *pnt, size_t len)
+{
+#ifdef HAVE_EXPLICIT_BZERO
+    explicit_bzero(pnt, len);
+#else
+    volatile unsigned char *volatile pnt_ = (volatile unsigned char *volatile) pnt;
+    size_t i                              = (size_t) 0U;
+
+    while (i < len) {
+        pnt_[i++] = 0U;
+    }
+#endif
+}
+
+void
+hydro_increment(uint8_t *n, size_t len)
+{
+    size_t        i;
+    uint_fast16_t c = 1U;
+
+    for (i = 0; i < len; i++) {
+        c += (uint_fast16_t) n[i];
+        n[i] = (uint8_t) c;
+        c >>= 8;
+    }
+}
+
+char *
+hydro_bin2hex(char *hex, size_t hex_maxlen, const uint8_t *bin, size_t bin_len)
+{
+    size_t       i = (size_t) 0U;
+    unsigned int x;
+    int          b;
+    int          c;
+
+    if (bin_len >= SIZE_MAX / 2 || hex_maxlen <= bin_len * 2U) {
+        abort();
+    }
+    while (i < bin_len) {
+        c = bin[i] & 0xf;
+        b = bin[i] >> 4;
+        x = (unsigned char) (87U + c + (((c - 10U) >> 8) & ~38U)) << 8 |
+            (unsigned char) (87U + b + (((b - 10U) >> 8) & ~38U));
+        hex[i * 2U] = (char) x;
+        x >>= 8;
+        hex[i * 2U + 1U] = (char) x;
+        i++;
+    }
+    hex[i * 2U] = 0U;
+
+    return hex;
+}
+
+int
+hydro_hex2bin(uint8_t *bin, size_t bin_maxlen, const char *hex, size_t hex_len, const char *ignore,
+              const char **hex_end_p)
+{
+    size_t        bin_pos = (size_t) 0U;
+    size_t        hex_pos = (size_t) 0U;
+    int           ret     = 0;
+    unsigned char c;
+    unsigned char c_alpha0, c_alpha;
+    unsigned char c_num0, c_num;
+    uint8_t       c_acc = 0U;
+    uint8_t       c_val;
+    unsigned char state = 0U;
+
+    while (hex_pos < hex_len) {
+        c        = (unsigned char) hex[hex_pos];
+        c_num    = c ^ 48U;
+        c_num0   = (c_num - 10U) >> 8;
+        c_alpha  = (c & ~32U) - 55U;
+        c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+        if ((c_num0 | c_alpha0) == 0U) {
+            if (ignore != NULL && state == 0U && strchr(ignore, c) != NULL) {
+                hex_pos++;
+                continue;
+            }
+            break;
+        }
+        c_val = (uint8_t)((c_num0 & c_num) | (c_alpha0 & c_alpha));
+        if (bin_pos >= bin_maxlen) {
+            ret   = -1;
+            errno = ERANGE;
+            break;
+        }
+        if (state == 0U) {
+            c_acc = c_val * 16U;
+        } else {
+            bin[bin_pos++] = c_acc | c_val;
+        }
+        state = ~state;
+        hex_pos++;
+    }
+    if (state != 0U) {
+        hex_pos--;
+        errno = EINVAL;
+        ret   = -1;
+    }
+    if (ret != 0) {
+        bin_pos = (size_t) 0U;
+    }
+    if (hex_end_p != NULL) {
+        *hex_end_p = &hex[hex_pos];
+    } else if (hex_pos != hex_len) {
+        errno = EINVAL;
+        ret   = -1;
+    }
+    if (ret != 0) {
+        return ret;
+    }
+    return (int) bin_pos;
+}
+
+bool
+hydro_equal(const void *b1_, const void *b2_, size_t len)
+{
+    const volatile uint8_t *volatile b1 = (const volatile uint8_t *volatile) b1_;
+    const uint8_t *b2                   = (const uint8_t *) b2_;
+    size_t         i;
+    uint8_t        d = (uint8_t) 0U;
+
+    if (b1 == b2) {
+        d = ~d;
+    }
+    for (i = 0U; i < len; i++) {
+        d |= b1[i] ^ b2[i];
+    }
+    return (bool) (1 & ((d - 1) >> 8));
+}
+
+int
+hydro_compare(const uint8_t *b1_, const uint8_t *b2_, size_t len)
+{
+    const volatile uint8_t *volatile b1 = (const volatile uint8_t *volatile) b1_;
+    const uint8_t *b2                   = (const uint8_t *) b2_;
+    uint8_t        gt                   = 0U;
+    uint8_t        eq                   = 1U;
+    size_t         i;
+
+    i = len;
+    while (i != 0U) {
+        i--;
+        gt |= ((b2[i] - b1[i]) >> 8) & eq;
+        eq &= ((b2[i] ^ b1[i]) - 1) >> 8;
+    }
+    return (int) (gt + gt + eq) - 1;
+}
+
+int
+hydro_pad(unsigned char *buf, size_t unpadded_buflen, size_t blocksize, size_t max_buflen)
+{
+    unsigned char *        tail;
+    size_t                 i;
+    size_t                 xpadlen;
+    size_t                 xpadded_len;
+    volatile unsigned char mask;
+    unsigned char          barrier_mask;
+
+    if (blocksize <= 0U || max_buflen > INT_MAX) {
+        return -1;
+    }
+    xpadlen = blocksize - 1U;
+    if ((blocksize & (blocksize - 1U)) == 0U) {
+        xpadlen -= unpadded_buflen & (blocksize - 1U);
+    } else {
+        xpadlen -= unpadded_buflen % blocksize;
+    }
+    if (SIZE_MAX - unpadded_buflen <= xpadlen) {
+        return -1;
+    }
+    xpadded_len = unpadded_buflen + xpadlen;
+    if (xpadded_len >= max_buflen) {
+        return -1;
+    }
+    tail = &buf[xpadded_len];
+    mask = 0U;
+    for (i = 0; i < blocksize; i++) {
+        barrier_mask = (unsigned char)
+	 (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
+        tail[-i]     = (tail[-i] & mask) | (0x80 & barrier_mask);
+        mask |= barrier_mask;
+    }
+    return (int) (xpadded_len + 1);
+}
+
+int
+hydro_unpad(const unsigned char *buf, size_t padded_buflen, size_t blocksize)
+{
+    const unsigned char *tail;
+    unsigned char        acc = 0U;
+    unsigned char        c;
+    unsigned char        valid   = 0U;
+    volatile size_t      pad_len = 0U;
+    size_t               i;
+    size_t               is_barrier;
+
+    if (padded_buflen < blocksize || blocksize <= 0U) {
+        return -1;
+    }
+    tail = &buf[padded_buflen - 1U];
+
+    for (i = 0U; i < blocksize; i++) {
+        c          = tail[-i];
+        is_barrier = (((acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U)) >> 8) & 1U;
+        acc |= c;
+        pad_len |= (i & -is_barrier);
+        valid |= (unsigned char) is_barrier;
+    }
+    if (valid == 0) {
+        return -1;
+    }
+    return (int) (padded_buflen - 1 - pad_len);
+}

+ 25 - 0
libhydrogen.mod/libhydrogen/impl/gimli-core.h

@@ -0,0 +1,25 @@
+#ifdef __SSE2__
+# include "gimli-core/sse2.h"
+#else
+# include "gimli-core/portable.h"
+#endif
+
+static void
+gimli_core_u8(uint8_t state_u8[gimli_BLOCKBYTES], uint8_t tag)
+{
+    state_u8[gimli_BLOCKBYTES - 1] ^= tag;
+#ifndef NATIVE_LITTLE_ENDIAN
+    uint32_t state_u32[12];
+    int      i;
+
+    for (i = 0; i < 12; i++) {
+        state_u32[i] = LOAD32_LE(&state_u8[i * 4]);
+    }
+    gimli_core(state_u32);
+    for (i = 0; i < 12; i++) {
+        STORE32_LE(&state_u8[i * 4], state_u32[i]);
+    }
+#else
+    gimli_core((uint32_t *) (void *) state_u8); /* state_u8 must be properly aligned */
+#endif
+}

+ 39 - 0
libhydrogen.mod/libhydrogen/impl/gimli-core/portable.h

@@ -0,0 +1,39 @@
+static void
+gimli_core(uint32_t state[gimli_BLOCKBYTES / 4])
+{
+    unsigned int round;
+    unsigned int column;
+    uint32_t     x;
+    uint32_t     y;
+    uint32_t     z;
+
+    for (round = 24; round > 0; round--) {
+        for (column = 0; column < 4; column++) {
+            x = ROTL32(state[column], 24);
+            y = ROTL32(state[4 + column], 9);
+            z = state[8 + column];
+
+            state[8 + column] = x ^ (z << 1) ^ ((y & z) << 2);
+            state[4 + column] = y ^ x ^ ((x | z) << 1);
+            state[column]     = z ^ y ^ ((x & y) << 3);
+        }
+        switch (round & 3) {
+        case 0:
+            x        = state[0];
+            state[0] = state[1];
+            state[1] = x;
+            x        = state[2];
+            state[2] = state[3];
+            state[3] = x;
+            state[0] ^= ((uint32_t) 0x9e377900 | round);
+            break;
+        case 2:
+            x        = state[0];
+            state[0] = state[2];
+            state[2] = x;
+            x        = state[1];
+            state[1] = state[3];
+            state[3] = x;
+        }
+    }
+}

+ 97 - 0
libhydrogen.mod/libhydrogen/impl/gimli-core/sse2.h

@@ -0,0 +1,97 @@
+#include <tmmintrin.h>
+
+#define S 9
+
+static inline __m128i
+shift(__m128i x, int bits)
+{
+    return _mm_slli_epi32(x, bits);
+}
+
+static inline __m128i
+rotate(__m128i x, int bits)
+{
+    return _mm_slli_epi32(x, bits) | _mm_srli_epi32(x, 32 - bits);
+}
+
+#ifdef __SSSE3__
+static inline __m128i
+rotate24(__m128i x)
+{
+    return _mm_shuffle_epi8(x, _mm_set_epi8(12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1));
+}
+#else
+static inline __m128i
+rotate24(__m128i x)
+{
+    uint8_t _hydro_attr_aligned_(16) x8[16], y8[16];
+
+    _mm_storeu_si128((__m128i *) (void *) x8, x);
+
+    y8[ 0] = x8[ 1]; y8[ 1] = x8[ 2]; y8[ 2] = x8[ 3]; y8[ 3] = x8[ 0];
+    y8[ 4] = x8[ 5]; y8[ 5] = x8[ 6]; y8[ 6] = x8[ 7]; y8[ 7] = x8[ 4];
+    y8[ 8] = x8[ 9]; y8[ 9] = x8[10]; y8[10] = x8[11]; y8[11] = x8[ 8];
+    y8[12] = x8[13]; y8[13] = x8[14]; y8[14] = x8[15]; y8[15] = x8[12];
+
+    return _mm_loadu_si128((const __m128i *) (const void *) y8);
+}
+#endif
+
+static const uint32_t coeffs[24] _hydro_attr_aligned_(16) = {
+    0x9e377904, 0, 0, 0, 0x9e377908, 0, 0, 0, 0x9e37790c, 0, 0, 0,
+    0x9e377910, 0, 0, 0, 0x9e377914, 0, 0, 0, 0x9e377918, 0, 0, 0,
+};
+
+static void
+gimli_core(uint32_t state[gimli_BLOCKBYTES / 4])
+{
+    __m128i x = _mm_loadu_si128((const __m128i *) (const void *) &state[0]);
+    __m128i y = _mm_loadu_si128((const __m128i *) (const void *) &state[4]);
+    __m128i z = _mm_loadu_si128((const __m128i *) (const void *) &state[8]);
+    __m128i newy;
+    __m128i newz;
+    int     round;
+
+    for (round = 5; round >= 0; round--) {
+        x    = rotate24(x);
+        y    = rotate(y, S);
+        newz = x ^ shift(z, 1) ^ shift(y & z, 2);
+        newy = y ^ x ^ shift(x | z, 1);
+        x    = z ^ y ^ shift(x & y, 3);
+        y    = newy;
+        z    = newz;
+
+        x = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
+        x ^= ((const __m128i *) (const void *) coeffs)[round];
+
+        x    = rotate24(x);
+        y    = rotate(y, S);
+        newz = x ^ shift(z, 1) ^ shift(y & z, 2);
+        newy = y ^ x ^ shift(x | z, 1);
+        x    = z ^ y ^ shift(x & y, 3);
+        y    = newy;
+        z    = newz;
+
+        x    = rotate24(x);
+        y    = rotate(y, S);
+        newz = x ^ shift(z, 1) ^ shift(y & z, 2);
+        newy = y ^ x ^ shift(x | z, 1);
+        x    = z ^ y ^ shift(x & y, 3);
+        y    = newy;
+        z    = newz;
+
+        x = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
+
+        x    = rotate24(x);
+        y    = rotate(y, S);
+        newz = x ^ shift(z, 1) ^ shift(y & z, 2);
+        newy = y ^ x ^ shift(x | z, 1);
+        x    = z ^ y ^ shift(x & y, 3);
+        y    = newy;
+        z    = newz;
+    }
+
+    _mm_storeu_si128((__m128i *) (void *) &state[0], x);
+    _mm_storeu_si128((__m128i *) (void *) &state[4], y);
+    _mm_storeu_si128((__m128i *) (void *) &state[8], z);
+}

+ 138 - 0
libhydrogen.mod/libhydrogen/impl/hash.h

@@ -0,0 +1,138 @@
+int
+hydro_hash_update(hydro_hash_state *state, const void *in_, size_t in_len)
+{
+    const uint8_t *in  = (const uint8_t *) in_;
+    uint8_t *      buf = (uint8_t *) (void *) state->state;
+    size_t         left;
+    size_t         ps;
+    size_t         i;
+
+    while (in_len > 0) {
+        if ((left = gimli_RATE - state->buf_off) == 0) {
+            gimli_core_u8(buf, 0);
+            state->buf_off = 0;
+            left           = gimli_RATE;
+        }
+        if ((ps = in_len) > left) {
+            ps = left;
+        }
+        for (i = 0; i < ps; i++) {
+            buf[state->buf_off + i] ^= in[i];
+        }
+        state->buf_off += (uint8_t) ps;
+        in += ps;
+        in_len -= ps;
+    }
+    return 0;
+}
+
+/* pad(str_enc("kmac") || str_enc(context)) || pad(str_enc(k)) ||
+   msg || right_enc(msg_len) || 0x00 */
+
+int
+hydro_hash_init(hydro_hash_state *state, const char ctx[hydro_hash_CONTEXTBYTES],
+                const uint8_t key[hydro_hash_KEYBYTES])
+{
+    uint8_t block[64] = { 4, 'k', 'm', 'a', 'c', 8 };
+    size_t  p;
+
+    COMPILER_ASSERT(hydro_hash_KEYBYTES <= sizeof block - gimli_RATE - 1);
+    COMPILER_ASSERT(hydro_hash_CONTEXTBYTES == 8);
+    mem_zero(block + 14, sizeof block - 14);
+    memcpy(block + 6, ctx, 8);
+    if (key != NULL) {
+        block[gimli_RATE] = (uint8_t) hydro_hash_KEYBYTES;
+        memcpy(block + gimli_RATE + 1, key, hydro_hash_KEYBYTES);
+        p = (gimli_RATE + 1 + hydro_hash_KEYBYTES + (gimli_RATE - 1)) & ~(size_t)(gimli_RATE - 1);
+    } else {
+        block[gimli_RATE] = (uint8_t) 0;
+        p                 = (gimli_RATE + 1 + 0 + (gimli_RATE - 1)) & ~(size_t)(gimli_RATE - 1);
+    }
+    mem_zero(state, sizeof *state);
+    hydro_hash_update(state, block, p);
+
+    return 0;
+}
+
+/* pad(str_enc("tmac") || str_enc(context)) || pad(str_enc(k)) ||
+   pad(right_enc(tweak)) || msg || right_enc(msg_len) || 0x00 */
+
+static int
+hydro_hash_init_with_tweak(hydro_hash_state *state, const char ctx[hydro_hash_CONTEXTBYTES],
+                           uint64_t tweak, const uint8_t key[hydro_hash_KEYBYTES])
+{
+    uint8_t block[80] = { 4, 't', 'm', 'a', 'c', 8 };
+    size_t  p;
+
+    COMPILER_ASSERT(hydro_hash_KEYBYTES <= sizeof block - 2 * gimli_RATE - 1);
+    COMPILER_ASSERT(hydro_hash_CONTEXTBYTES == 8);
+    mem_zero(block + 14, sizeof block - 14);
+    memcpy(block + 6, ctx, 8);
+    if (key != NULL) {
+        block[gimli_RATE] = (uint8_t) hydro_hash_KEYBYTES;
+        memcpy(block + gimli_RATE + 1, key, hydro_hash_KEYBYTES);
+        p = (gimli_RATE + 1 + hydro_hash_KEYBYTES + (gimli_RATE - 1)) & ~(size_t)(gimli_RATE - 1);
+    } else {
+        block[gimli_RATE] = (uint8_t) 0;
+        p                 = (gimli_RATE + 1 + 0 + (gimli_RATE - 1)) & ~(size_t)(gimli_RATE - 1);
+    }
+    block[p] = (uint8_t) sizeof tweak;
+    STORE64_LE(&block[p + 1], tweak);
+    p += gimli_RATE;
+    mem_zero(state, sizeof *state);
+    hydro_hash_update(state, block, p);
+
+    return 0;
+}
+
+int
+hydro_hash_final(hydro_hash_state *state, uint8_t *out, size_t out_len)
+{
+    uint8_t  lc[4];
+    uint8_t *buf = (uint8_t *) (void *) state->state;
+    size_t   i;
+    size_t   lc_len;
+    size_t   leftover;
+
+    if (out_len < hydro_hash_BYTES_MIN || out_len > hydro_hash_BYTES_MAX) {
+        return -1;
+    }
+    COMPILER_ASSERT(hydro_hash_BYTES_MAX <= 0xffff);
+    lc[1]  = (uint8_t) out_len;
+    lc[2]  = (uint8_t)(out_len >> 8);
+    lc[3]  = 0;
+    lc_len = (size_t)(1 + (lc[2] != 0));
+    lc[0]  = (uint8_t) lc_len;
+    hydro_hash_update(state, lc, 1 + lc_len + 1);
+    gimli_pad_u8(buf, state->buf_off, gimli_DOMAIN_XOF);
+    for (i = 0; i < out_len / gimli_RATE; i++) {
+        gimli_core_u8(buf, 0);
+        memcpy(out + i * gimli_RATE, buf, gimli_RATE);
+    }
+    leftover = out_len % gimli_RATE;
+    if (leftover != 0) {
+        gimli_core_u8(buf, 0);
+        mem_cpy(out + i * gimli_RATE, buf, leftover);
+    }
+    return 0;
+}
+
+int
+hydro_hash_hash(uint8_t *out, size_t out_len, const void *in_, size_t in_len,
+                const char ctx[hydro_hash_CONTEXTBYTES], const uint8_t key[hydro_hash_KEYBYTES])
+{
+    hydro_hash_state st;
+    const uint8_t *  in = (const uint8_t *) in_;
+
+    if (hydro_hash_init(&st, ctx, key) != 0 || hydro_hash_update(&st, in, in_len) != 0 ||
+        hydro_hash_final(&st, out, out_len) != 0) {
+        return -1;
+    }
+    return 0;
+}
+
+void
+hydro_hash_keygen(uint8_t key[hydro_hash_KEYBYTES])
+{
+    hydro_random_buf(key, hydro_hash_KEYBYTES);
+}

+ 83 - 0
libhydrogen.mod/libhydrogen/impl/hydrogen_p.h

@@ -0,0 +1,83 @@
+static int hydro_random_init(void);
+
+/* ---------------- */
+
+#define gimli_BLOCKBYTES 48
+#define gimli_CAPACITY   32
+#define gimli_RATE       16
+
+#define gimli_TAG_HEADER  0x01
+#define gimli_TAG_PAYLOAD 0x02
+#define gimli_TAG_FINAL   0x08
+#define gimli_TAG_FINAL0  0xf8
+#define gimli_TAG_KEY0    0xfe
+#define gimli_TAG_KEY     0xff
+
+#define gimli_DOMAIN_AEAD 0x0
+#define gimli_DOMAIN_XOF  0xf
+
+static void gimli_core_u8(uint8_t state_u8[gimli_BLOCKBYTES], uint8_t tag);
+
+static inline void
+gimli_pad_u8(uint8_t buf[gimli_BLOCKBYTES], size_t pos, uint8_t domain)
+{
+    buf[pos] ^= (domain << 1) | 1;
+    buf[gimli_RATE - 1] ^= 0x80;
+}
+
+static inline void
+hydro_mem_ct_zero_u32(uint32_t *dst_, size_t n)
+{
+    volatile uint32_t *volatile dst = (volatile uint32_t * volatile)(void *) dst_;
+    size_t i;
+
+    for (i = 0; i < n; i++) {
+        dst[i] = 0;
+    }
+}
+
+static inline uint32_t hydro_mem_ct_cmp_u32(const uint32_t *b1_, const uint32_t *b2,
+                                            size_t n) _hydro_attr_warn_unused_result_;
+
+static inline uint32_t
+hydro_mem_ct_cmp_u32(const uint32_t *b1_, const uint32_t *b2, size_t n)
+{
+    const volatile uint32_t *volatile b1 = (const volatile uint32_t *volatile)(const void *) b1_;
+    size_t   i;
+    uint32_t cv = 0;
+
+    for (i = 0; i < n; i++) {
+        cv |= b1[i] ^ b2[i];
+    }
+    return cv;
+}
+
+/* ---------------- */
+
+static int hydro_hash_init_with_tweak(hydro_hash_state *state,
+                                      const char ctx[hydro_hash_CONTEXTBYTES], uint64_t tweak,
+                                      const uint8_t key[hydro_hash_KEYBYTES]);
+
+/* ---------------- */
+
+#define hydro_secretbox_NONCEBYTES 20
+#define hydro_secretbox_MACBYTES 16
+
+/* ---------------- */
+
+#define hydro_x25519_BYTES 32
+#define hydro_x25519_PUBLICKEYBYTES 32
+#define hydro_x25519_SECRETKEYBYTES 32
+
+static int hydro_x25519_scalarmult(uint8_t       out[hydro_x25519_BYTES],
+                                   const uint8_t scalar[hydro_x25519_BYTES],
+                                   const uint8_t x1[hydro_x25519_BYTES],
+                                   bool          clamp) _hydro_attr_warn_unused_result_;
+
+static inline int hydro_x25519_scalarmult_base(uint8_t       pk[hydro_x25519_PUBLICKEYBYTES],
+                                               const uint8_t sk[hydro_x25519_SECRETKEYBYTES])
+    _hydro_attr_warn_unused_result_;
+
+static inline void
+hydro_x25519_scalarmult_base_uniform(uint8_t       pk[hydro_x25519_PUBLICKEYBYTES],
+                                     const uint8_t sk[hydro_x25519_SECRETKEYBYTES]);

+ 20 - 0
libhydrogen.mod/libhydrogen/impl/kdf.h

@@ -0,0 +1,20 @@
+int
+hydro_kdf_derive_from_key(uint8_t *subkey, size_t subkey_len, uint64_t subkey_id,
+                          const char    ctx[hydro_kdf_CONTEXTBYTES],
+                          const uint8_t key[hydro_kdf_KEYBYTES])
+{
+    hydro_hash_state st;
+
+    COMPILER_ASSERT(hydro_kdf_CONTEXTBYTES >= hydro_hash_CONTEXTBYTES);
+    COMPILER_ASSERT(hydro_kdf_KEYBYTES >= hydro_hash_KEYBYTES);
+    if (hydro_hash_init_with_tweak(&st, ctx, subkey_id, key) != 0) {
+        return -1;
+    }
+    return hydro_hash_final(&st, subkey, subkey_len);
+}
+
+void
+hydro_kdf_keygen(uint8_t key[hydro_kdf_KEYBYTES])
+{
+    hydro_random_buf(key, hydro_kdf_KEYBYTES);
+}

+ 441 - 0
libhydrogen.mod/libhydrogen/impl/kx.h

@@ -0,0 +1,441 @@
+#define hydro_kx_AEAD_KEYBYTES hydro_hash_KEYBYTES
+#define hydro_kx_AEAD_MACBYTES 16
+#define hydro_kx_AEAD_HEADERBYTES hydro_kx_AEAD_MACBYTES
+
+#define hydro_kx_CONTEXT "hydro_kx"
+#define hydro_kx_CONTEXT_CK_K "kdf_ck_k"
+
+void
+hydro_kx_keygen(hydro_kx_keypair *static_kp)
+{
+    hydro_random_buf(static_kp->sk, hydro_kx_SECRETKEYBYTES);
+    if (hydro_x25519_scalarmult_base(static_kp->pk, static_kp->sk) != 0) {
+        abort();
+    }
+}
+
+void
+hydro_kx_keygen_deterministic(hydro_kx_keypair *static_kp, const uint8_t seed[hydro_kx_SEEDBYTES])
+{
+    COMPILER_ASSERT(hydro_kx_SEEDBYTES >= hydro_random_SEEDBYTES);
+    hydro_random_buf_deterministic(static_kp->sk, hydro_kx_SECRETKEYBYTES, seed);
+    if (hydro_x25519_scalarmult_base(static_kp->pk, static_kp->sk) != 0) {
+        abort();
+    }
+}
+
+static void
+hydro_kx_aead_setup(uint8_t buf[gimli_BLOCKBYTES], const hydro_kx_state *state,
+                    const uint8_t psk[hydro_kx_PSKBYTES])
+{
+    static const uint8_t prefix[] = { 6, 'k', 'x', 'x', '2', '5', '6', 0 };
+
+    mem_zero(buf + sizeof prefix, gimli_BLOCKBYTES - sizeof prefix);
+    memcpy(buf, prefix, sizeof prefix);
+    gimli_core_u8(buf, gimli_TAG_HEADER);
+
+    COMPILER_ASSERT(hydro_kx_AEAD_KEYBYTES == 2 * gimli_RATE);
+    mem_xor(buf, state->k, gimli_RATE);
+    gimli_core_u8(buf, gimli_TAG_KEY);
+    mem_xor(buf, state->k + gimli_RATE, gimli_RATE);
+    gimli_core_u8(buf, gimli_TAG_KEY);
+
+    COMPILER_ASSERT(sizeof state->h == 2 * gimli_RATE);
+    mem_xor(buf, state->h, gimli_RATE);
+    gimli_core_u8(buf, gimli_TAG_HEADER);
+    mem_xor(buf, state->h + gimli_RATE, gimli_RATE);
+    gimli_core_u8(buf, gimli_TAG_HEADER);
+
+    if (psk != NULL) {
+        COMPILER_ASSERT(hydro_kx_PSKBYTES == 2 * gimli_RATE);
+        mem_xor(buf, psk, gimli_RATE);
+        gimli_core_u8(buf, gimli_TAG_HEADER);
+        mem_xor(buf, psk + gimli_RATE, gimli_RATE);
+        gimli_core_u8(buf, gimli_TAG_HEADER);
+    }
+}
+
+static void
+hydro_kx_finalize(uint8_t *buf, const uint8_t key[hydro_kx_AEAD_KEYBYTES])
+{
+    COMPILER_ASSERT(hydro_kx_AEAD_KEYBYTES == gimli_CAPACITY);
+    mem_xor(buf + gimli_RATE, key, hydro_kx_AEAD_KEYBYTES);
+    gimli_core_u8(buf, gimli_TAG_FINAL);
+    mem_xor(buf + gimli_RATE, key, hydro_kx_AEAD_KEYBYTES);
+    gimli_core_u8(buf, gimli_TAG_FINAL);
+}
+
+static void
+hydro_kx_aead_xor_enc(uint8_t buf[gimli_BLOCKBYTES], uint8_t *out, const uint8_t *in, size_t inlen)
+{
+    size_t i;
+    size_t leftover;
+
+    for (i = 0; i < inlen / gimli_RATE; i++) {
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, gimli_RATE);
+        memcpy(buf, &out[i * gimli_RATE], gimli_RATE);
+        gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+    }
+    leftover = inlen % gimli_RATE;
+    if (leftover != 0) {
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, leftover);
+        mem_cpy(buf, &out[i * gimli_RATE], leftover);
+    }
+    gimli_pad_u8(buf, leftover, gimli_DOMAIN_AEAD);
+    gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+}
+
+static void
+hydro_kx_aead_xor_dec(uint8_t buf[gimli_BLOCKBYTES], uint8_t *out, const uint8_t *in, size_t inlen)
+{
+    size_t i;
+    size_t leftover;
+
+    for (i = 0; i < inlen / gimli_RATE; i++) {
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, gimli_RATE);
+        memcpy(buf, &in[i * gimli_RATE], gimli_RATE);
+        gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+    }
+    leftover = inlen % gimli_RATE;
+    if (leftover != 0) {
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, leftover);
+        mem_cpy(buf, &in[i * gimli_RATE], leftover);
+    }
+    gimli_pad_u8(buf, leftover, gimli_DOMAIN_AEAD);
+    gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+}
+
+static void
+hydro_kx_encrypt(const hydro_kx_state *state, uint8_t *c, const uint8_t *m, size_t mlen,
+                 const uint8_t psk[hydro_kx_PSKBYTES])
+{
+    _hydro_attr_aligned_(16) uint8_t buf[gimli_BLOCKBYTES];
+    uint8_t *                        mac = &c[0];
+    uint8_t *                        ct  = &c[hydro_kx_AEAD_MACBYTES];
+
+    hydro_kx_aead_setup(buf, state, psk);
+    hydro_kx_aead_xor_enc(buf, ct, m, mlen);
+
+    hydro_kx_finalize(buf, state->k);
+    COMPILER_ASSERT(hydro_kx_AEAD_MACBYTES <= gimli_CAPACITY);
+    memcpy(mac, buf + gimli_RATE, hydro_kx_AEAD_MACBYTES);
+}
+
+static int hydro_kx_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c, size_t clen,
+                            const uint8_t psk[hydro_kx_PSKBYTES]) _hydro_attr_warn_unused_result_;
+
+static int
+hydro_kx_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c, size_t clen,
+                 const uint8_t psk[hydro_kx_PSKBYTES])
+{
+    _hydro_attr_aligned_(16) uint32_t int_state[gimli_BLOCKBYTES / 4];
+    uint32_t                          pub_mac[hydro_kx_AEAD_MACBYTES / 4];
+    uint8_t *                         buf = (uint8_t *) (void *) int_state;
+    const uint8_t *                   mac;
+    const uint8_t *                   ct;
+    size_t                            mlen;
+    uint32_t                          cv;
+
+    if (clen < hydro_kx_AEAD_HEADERBYTES) {
+        return -1;
+    }
+    mac  = &c[0];
+    ct   = &c[hydro_kx_AEAD_MACBYTES];
+    mlen = clen - hydro_kx_AEAD_HEADERBYTES;
+    memcpy(pub_mac, mac, sizeof pub_mac);
+    hydro_kx_aead_setup(buf, state, psk);
+    hydro_kx_aead_xor_dec(buf, m, ct, mlen);
+
+    hydro_kx_finalize(buf, state->k);
+    COMPILER_ASSERT(hydro_kx_AEAD_MACBYTES <= gimli_CAPACITY);
+    COMPILER_ASSERT(gimli_RATE % 4 == 0);
+    cv = hydro_mem_ct_cmp_u32(int_state + gimli_RATE / 4, pub_mac, hydro_kx_AEAD_MACBYTES / 4);
+    hydro_mem_ct_zero_u32(int_state, gimli_BLOCKBYTES / 4);
+    if (cv != 0) {
+        mem_zero(m, mlen);
+        return -1;
+    }
+    return 0;
+}
+
+static int
+hydro_kx_scalarmult(hydro_kx_state *state, uint8_t dh_res[hydro_x25519_BYTES],
+                    const uint8_t scalar[hydro_x25519_BYTES], const uint8_t x1[hydro_x25519_BYTES])
+{
+    uint8_t ck_k[hydro_hash_BYTES + hydro_hash_KEYBYTES];
+
+    if (hydro_x25519_scalarmult(dh_res, scalar, x1, 1) != 0) {
+        return -1;
+    }
+    COMPILER_ASSERT(sizeof state->ck >= hydro_hash_KEYBYTES);
+    hydro_hash_hash(ck_k, sizeof ck_k, dh_res, hydro_x25519_BYTES, hydro_kx_CONTEXT_CK_K,
+                    state->ck);
+    memcpy(state->ck, ck_k, sizeof state->ck);
+    memcpy(state->k, ck_k + sizeof state->ck, sizeof state->k);
+
+    return 0;
+}
+
+static void
+hydro_kx_final(hydro_kx_state *state, uint8_t rx[hydro_kx_SESSIONKEYBYTES],
+               uint8_t tx[hydro_kx_SESSIONKEYBYTES])
+{
+    COMPILER_ASSERT(hydro_kx_SESSIONKEYBYTES == hydro_kx_AEAD_KEYBYTES);
+    COMPILER_ASSERT(hydro_kx_PUBLICKEYBYTES == hydro_x25519_BYTES);
+    COMPILER_ASSERT(hydro_kx_SECRETKEYBYTES == hydro_x25519_BYTES);
+    COMPILER_ASSERT(hydro_kx_PSKBYTES == hydro_hash_KEYBYTES);
+    COMPILER_ASSERT(sizeof(state->h) == hydro_hash_BYTES);
+    COMPILER_ASSERT(sizeof(state->ck) == hydro_hash_KEYBYTES);
+    COMPILER_ASSERT(sizeof(state->k) == hydro_kx_AEAD_KEYBYTES);
+    COMPILER_ASSERT(hydro_kx_XX_PACKET1BYTES == hydro_kx_PUBLICKEYBYTES);
+    COMPILER_ASSERT(hydro_kx_XX_PACKET2BYTES ==
+                    hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_HEADERBYTES + hydro_kx_PUBLICKEYBYTES);
+    COMPILER_ASSERT(hydro_kx_XX_PACKET3BYTES ==
+                    hydro_kx_AEAD_HEADERBYTES + hydro_kx_PUBLICKEYBYTES);
+
+    COMPILER_ASSERT(hydro_kdf_KEYBYTES == hydro_hash_BYTES);
+    hydro_kdf_derive_from_key(rx, hydro_kx_SESSIONKEYBYTES, 0, hydro_kx_CONTEXT, state->ck);
+    hydro_kdf_derive_from_key(tx, hydro_kx_SESSIONKEYBYTES, 1, hydro_kx_CONTEXT, state->ck);
+    hydro_memzero(state, sizeof *state);
+}
+
+/* NOISE_N */
+
+int
+hydro_kx_n_1(hydro_kx_session_keypair *kp, uint8_t packet1[hydro_kx_N_PACKET1BYTES],
+             const uint8_t psk[hydro_kx_PSKBYTES],
+             const uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES])
+{
+    hydro_kx_state state;
+    uint8_t        dh_res[hydro_x25519_BYTES];
+
+    mem_zero(&state, sizeof state);
+
+    hydro_kx_keygen(&state.eph_kp);
+    COMPILER_ASSERT(hydro_kx_PSKBYTES >= hydro_hash_KEYBYTES);
+    hydro_hash_hash(state.h, sizeof state.h, peer_static_pk, hydro_kx_PUBLICKEYBYTES,
+                    hydro_kx_CONTEXT, psk);
+    memcpy(packet1, state.eph_kp.pk, hydro_kx_PUBLICKEYBYTES);
+
+    COMPILER_ASSERT(sizeof state.h >= hydro_hash_KEYBYTES);
+    hydro_hash_hash(state.h, sizeof state.h, state.eph_kp.pk, sizeof state.eph_kp.pk,
+                    hydro_kx_CONTEXT, state.h);
+
+    if (hydro_kx_scalarmult(&state, dh_res, state.eph_kp.sk, peer_static_pk) != 0) {
+        return -1;
+    }
+    hydro_kx_final(&state, kp->rx, kp->tx);
+
+    return 0;
+}
+
+int
+hydro_kx_n_2(hydro_kx_session_keypair *kp, const uint8_t packet1[hydro_kx_N_PACKET1BYTES],
+             const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp)
+{
+    hydro_kx_state state;
+    uint8_t        dh_res[hydro_x25519_BYTES];
+    const uint8_t *peer_eph_pk = packet1;
+
+    mem_zero(&state, sizeof state);
+
+    COMPILER_ASSERT(hydro_kx_PSKBYTES >= hydro_hash_KEYBYTES);
+    hydro_hash_hash(state.h, sizeof state.h, static_kp->pk, sizeof static_kp->pk, hydro_kx_CONTEXT,
+                    psk);
+
+    COMPILER_ASSERT(sizeof state.h >= hydro_hash_KEYBYTES);
+    hydro_hash_hash(state.h, sizeof state.h, peer_eph_pk, hydro_kx_PUBLICKEYBYTES, hydro_kx_CONTEXT,
+                    state.h);
+
+    if (hydro_kx_scalarmult(&state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    hydro_kx_final(&state, kp->tx, kp->rx);
+
+    return 0;
+}
+
+/* NOISE_KK */
+
+int
+hydro_kx_kk_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_KK_PACKET1BYTES],
+              const uint8_t           peer_static_pk[hydro_kx_PUBLICKEYBYTES],
+              const hydro_kx_keypair *static_kp)
+{
+    uint8_t dh_res[hydro_x25519_BYTES];
+    mem_zero(state, sizeof *state);
+
+    hydro_kx_keygen(&state->eph_kp);
+    hydro_hash_hash(state->h, sizeof state->h, state->eph_kp.pk, sizeof state->eph_kp.pk,
+                    hydro_kx_CONTEXT, NULL);
+    memcpy(packet1, state->eph_kp.pk, sizeof state->eph_kp.pk);
+
+    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_static_pk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_scalarmult(state, dh_res, static_kp->sk, peer_static_pk) != 0) {
+        return -1;
+    }
+    return 0;
+}
+
+int
+hydro_kx_kk_2(hydro_kx_session_keypair *kp, uint8_t packet2[hydro_kx_KK_PACKET2BYTES],
+              const uint8_t           packet1[hydro_kx_KK_PACKET1BYTES],
+              const uint8_t           peer_static_pk[hydro_kx_PUBLICKEYBYTES],
+              const hydro_kx_keypair *static_kp)
+{
+    hydro_kx_state state;
+    uint8_t        dh_res[hydro_x25519_BYTES];
+    const uint8_t *peer_eph_pk = packet1;
+
+    mem_zero(&state, sizeof state);
+
+    hydro_kx_keygen(&state.eph_kp);
+    hydro_hash_hash(state.h, sizeof state.h, state.eph_kp.pk, sizeof state.eph_kp.pk,
+                    hydro_kx_CONTEXT, NULL);
+    memcpy(packet2, state.eph_kp.pk, sizeof state.eph_kp.pk);
+
+    if (hydro_kx_scalarmult(&state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_scalarmult(&state, dh_res, static_kp->sk, peer_static_pk) != 0) {
+        return -1;
+    }
+
+    if (hydro_kx_scalarmult(&state, dh_res, state.eph_kp.sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_scalarmult(&state, dh_res, state.eph_kp.sk, peer_static_pk) != 0) {
+        return -1;
+    }
+    hydro_kx_final(&state, kp->rx, kp->tx);
+
+    return 0;
+}
+
+int
+hydro_kx_kk_3(hydro_kx_state *state, hydro_kx_session_keypair *kp,
+              const uint8_t packet2[hydro_kx_KK_PACKET2BYTES],
+              const hydro_kx_keypair *static_kp)
+{
+    uint8_t        dh_res[hydro_x25519_BYTES];
+    const uint8_t *peer_eph_pk = packet2;
+
+    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_scalarmult(state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    hydro_kx_final(state, kp->tx, kp->rx);
+
+    return 0;
+}
+
+/* NOISE_XX */
+
+int
+hydro_kx_xx_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_XX_PACKET1BYTES],
+              const uint8_t psk[hydro_kx_PSKBYTES])
+{
+    mem_zero(state, sizeof *state);
+
+    hydro_kx_keygen(&state->eph_kp);
+    COMPILER_ASSERT(hydro_kx_PSKBYTES >= hydro_hash_KEYBYTES);
+    hydro_hash_hash(state->h, sizeof state->h, state->eph_kp.pk, sizeof state->eph_kp.pk,
+                    hydro_kx_CONTEXT, psk);
+    memcpy(packet1, state->eph_kp.pk, hydro_kx_PUBLICKEYBYTES);
+
+    return 0;
+}
+
+int
+hydro_kx_xx_2(hydro_kx_state *state, uint8_t packet2[hydro_kx_XX_PACKET2BYTES],
+              const uint8_t packet1[hydro_kx_XX_PACKET1BYTES], const uint8_t psk[hydro_kx_PSKBYTES],
+              const hydro_kx_keypair *static_kp)
+{
+    uint8_t        dh_res[hydro_x25519_BYTES];
+    const uint8_t *peer_eph_pk = packet1;
+
+    mem_zero(state, sizeof *state);
+
+    hydro_hash_hash(state->h, sizeof state->h, peer_eph_pk, hydro_kx_PUBLICKEYBYTES,
+                    hydro_kx_CONTEXT, psk);
+    hydro_kx_keygen(&state->eph_kp);
+    memcpy(packet2, state->eph_kp.pk, sizeof state->eph_kp.pk);
+    COMPILER_ASSERT(sizeof state->h >= hydro_hash_KEYBYTES);
+    hydro_hash_hash(state->h, sizeof state->h, state->eph_kp.pk, sizeof state->eph_kp.pk,
+                    hydro_kx_CONTEXT, state->h);
+
+    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    hydro_kx_encrypt(state, packet2 + sizeof state->eph_kp.pk, static_kp->pk, sizeof static_kp->pk,
+                     psk);
+    if (hydro_kx_scalarmult(state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    return 0;
+}
+
+int
+hydro_kx_xx_3(hydro_kx_state *state, hydro_kx_session_keypair *kp,
+              uint8_t       packet3[hydro_kx_XX_PACKET3BYTES],
+              uint8_t       peer_static_pk[hydro_kx_PUBLICKEYBYTES],
+              const uint8_t packet2[hydro_kx_XX_PACKET2BYTES], const uint8_t psk[hydro_kx_PSKBYTES],
+              const hydro_kx_keypair *static_kp)
+{
+    uint8_t        dh_res[hydro_x25519_BYTES];
+    uint8_t        peer_static_pk_[hydro_kx_PUBLICKEYBYTES];
+    const uint8_t *peer_eph_pk              = packet2;
+    const uint8_t *peer_encrypted_static_pk = packet2 + hydro_kx_PUBLICKEYBYTES;
+
+    hydro_hash_hash(state->h, sizeof state->h, peer_eph_pk, hydro_kx_PUBLICKEYBYTES,
+                    hydro_kx_CONTEXT, state->h);
+
+    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    if (peer_static_pk == NULL) {
+        peer_static_pk = peer_static_pk_;
+    }
+    if (hydro_kx_decrypt(state, peer_static_pk, peer_encrypted_static_pk,
+                         hydro_kx_AEAD_HEADERBYTES + hydro_kx_PUBLICKEYBYTES, psk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_static_pk) != 0) {
+        return -1;
+    }
+    hydro_kx_encrypt(state, packet3, static_kp->pk, sizeof static_kp->pk, psk);
+    if (hydro_kx_scalarmult(state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    hydro_kx_final(state, kp->rx, kp->tx);
+
+    return 0;
+}
+
+int
+hydro_kx_xx_4(hydro_kx_state *state, hydro_kx_session_keypair *kp,
+              uint8_t       peer_static_pk[hydro_kx_PUBLICKEYBYTES],
+              const uint8_t packet3[hydro_kx_XX_PACKET3BYTES], const uint8_t psk[hydro_kx_PSKBYTES])
+{
+    uint8_t        dh_res[hydro_x25519_BYTES];
+    uint8_t        peer_static_pk_[hydro_kx_PUBLICKEYBYTES];
+    const uint8_t *peer_encrypted_static_pk = packet3;
+
+    if (peer_static_pk == NULL) {
+        peer_static_pk = peer_static_pk_;
+    }
+    if (hydro_kx_decrypt(state, peer_static_pk, peer_encrypted_static_pk,
+                         hydro_kx_AEAD_HEADERBYTES + hydro_kx_PUBLICKEYBYTES, psk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_static_pk) != 0) {
+        return -1;
+    }
+    hydro_kx_final(state, kp->tx, kp->rx);
+
+    return 0;
+}

+ 281 - 0
libhydrogen.mod/libhydrogen/impl/pwhash.h

@@ -0,0 +1,281 @@
+#define hydro_pwhash_ENC_ALGBYTES 1
+#define hydro_pwhash_HASH_ALGBYTES 1
+#define hydro_pwhash_THREADSBYTES 1
+#define hydro_pwhash_OPSLIMITBYTES 8
+#define hydro_pwhash_MEMLIMITBYTES 8
+#define hydro_pwhash_HASHBYTES 32
+#define hydro_pwhash_SALTBYTES 16
+#define hydro_pwhash_PARAMSBYTES                                                           \
+    (hydro_pwhash_HASH_ALGBYTES + hydro_pwhash_THREADSBYTES + hydro_pwhash_OPSLIMITBYTES + \
+     hydro_pwhash_MEMLIMITBYTES + hydro_pwhash_SALTBYTES + hydro_pwhash_HASHBYTES)
+#define hydro_pwhash_ENC_ALG 0x01
+#define hydro_pwhash_HASH_ALG 0x01
+#define hydro_pwhash_CONTEXT "hydro_pw"
+
+static int
+_hydro_pwhash_hash(uint8_t out[hydro_random_SEEDBYTES], size_t h_len,
+                   const uint8_t salt[hydro_pwhash_SALTBYTES], const char *passwd,
+                   size_t passwd_len, const char ctx[hydro_pwhash_CONTEXTBYTES],
+                   const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
+                   size_t memlimit, uint8_t threads)
+{
+    _hydro_attr_aligned_(16) uint8_t state[gimli_BLOCKBYTES];
+    hydro_hash_state                 h_st;
+    uint8_t                          tmp64_u8[8];
+    uint64_t                         i;
+    uint8_t                          tmp8;
+
+    COMPILER_ASSERT(hydro_pwhash_MASTERKEYBYTES >= hydro_hash_KEYBYTES);
+    hydro_hash_init(&h_st, ctx, master_key);
+
+    STORE64_LE(tmp64_u8, (uint64_t) passwd_len);
+    hydro_hash_update(&h_st, tmp64_u8, sizeof tmp64_u8);
+    hydro_hash_update(&h_st, passwd, passwd_len);
+
+    hydro_hash_update(&h_st, salt, hydro_pwhash_SALTBYTES);
+
+    tmp8 = hydro_pwhash_HASH_ALG;
+    hydro_hash_update(&h_st, &tmp8, 1);
+
+    hydro_hash_update(&h_st, &threads, 1);
+
+    STORE64_LE(tmp64_u8, (uint64_t) memlimit);
+    hydro_hash_update(&h_st, tmp64_u8, sizeof tmp64_u8);
+
+    STORE64_LE(tmp64_u8, (uint64_t) h_len);
+    hydro_hash_update(&h_st, tmp64_u8, sizeof tmp64_u8);
+
+    hydro_hash_final(&h_st, (uint8_t *) (void *) &state, sizeof state);
+
+    gimli_core_u8(state, 1);
+    COMPILER_ASSERT(gimli_RATE >= 8);
+    for (i = 0; i < opslimit; i++) {
+        mem_zero(state, gimli_RATE);
+        STORE64_LE(state, i);
+        gimli_core_u8(state, 0);
+    }
+    mem_zero(state, gimli_RATE);
+
+    COMPILER_ASSERT(hydro_random_SEEDBYTES == gimli_CAPACITY);
+    memcpy(out, state + gimli_RATE, hydro_random_SEEDBYTES);
+    hydro_memzero(state, sizeof state);
+
+    return 0;
+}
+
+void
+hydro_pwhash_keygen(uint8_t master_key[hydro_pwhash_MASTERKEYBYTES])
+{
+    hydro_random_buf(master_key, hydro_pwhash_MASTERKEYBYTES);
+}
+
+int
+hydro_pwhash_deterministic(uint8_t *h, size_t h_len, const char *passwd, size_t passwd_len,
+                           const char    ctx[hydro_pwhash_CONTEXTBYTES],
+                           const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
+                           size_t memlimit, uint8_t threads)
+{
+    uint8_t seed[hydro_random_SEEDBYTES];
+
+    COMPILER_ASSERT(sizeof zero >= hydro_pwhash_SALTBYTES);
+    COMPILER_ASSERT(sizeof zero >= hydro_pwhash_MASTERKEYBYTES);
+
+    (void) memlimit;
+    if (_hydro_pwhash_hash(seed, h_len, zero, passwd, passwd_len, ctx, master_key, opslimit,
+                           memlimit, threads) != 0) {
+        return -1;
+    }
+    hydro_random_buf_deterministic(h, h_len, seed);
+    hydro_memzero(seed, sizeof seed);
+
+    return 0;
+}
+
+int
+hydro_pwhash_create(uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd, size_t passwd_len,
+                    const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
+                    size_t memlimit, uint8_t threads)
+{
+    uint8_t *const enc_alg     = &stored[0];
+    uint8_t *const secretbox   = &enc_alg[hydro_pwhash_ENC_ALGBYTES];
+    uint8_t *const hash_alg    = &secretbox[hydro_secretbox_HEADERBYTES];
+    uint8_t *const threads_u8  = &hash_alg[hydro_pwhash_HASH_ALGBYTES];
+    uint8_t *const opslimit_u8 = &threads_u8[hydro_pwhash_THREADSBYTES];
+    uint8_t *const memlimit_u8 = &opslimit_u8[hydro_pwhash_OPSLIMITBYTES];
+    uint8_t *const salt        = &memlimit_u8[hydro_pwhash_MEMLIMITBYTES];
+    uint8_t *const h           = &salt[hydro_pwhash_SALTBYTES];
+
+    COMPILER_ASSERT(hydro_pwhash_STOREDBYTES >= hydro_pwhash_ENC_ALGBYTES +
+                                                    hydro_secretbox_HEADERBYTES +
+                                                    hydro_pwhash_PARAMSBYTES);
+    (void) memlimit;
+    mem_zero(stored, hydro_pwhash_STOREDBYTES);
+    *enc_alg    = hydro_pwhash_ENC_ALG;
+    *hash_alg   = hydro_pwhash_HASH_ALG;
+    *threads_u8 = threads;
+    STORE64_LE(opslimit_u8, opslimit);
+    STORE64_LE(memlimit_u8, (uint64_t) memlimit);
+    hydro_random_buf(salt, hydro_pwhash_SALTBYTES);
+
+    COMPILER_ASSERT(sizeof zero >= hydro_pwhash_MASTERKEYBYTES);
+    if (_hydro_pwhash_hash(h, hydro_pwhash_HASHBYTES, salt, passwd, passwd_len,
+                           hydro_pwhash_CONTEXT, zero, opslimit, memlimit, threads) != 0) {
+        return -1;
+    }
+    COMPILER_ASSERT(hydro_pwhash_MASTERKEYBYTES == hydro_secretbox_KEYBYTES);
+
+    return hydro_secretbox_encrypt(secretbox, hash_alg, hydro_pwhash_PARAMSBYTES,
+                                   (uint64_t) *enc_alg, hydro_pwhash_CONTEXT, master_key);
+}
+
+static int
+_hydro_pwhash_verify(uint8_t       computed_h[hydro_pwhash_HASHBYTES],
+                     const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd,
+                     size_t passwd_len, const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                     uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max)
+{
+    const uint8_t *const enc_alg   = &stored[0];
+    const uint8_t *const secretbox = &enc_alg[hydro_pwhash_ENC_ALGBYTES];
+
+    uint8_t        params[hydro_pwhash_PARAMSBYTES];
+    uint8_t *const hash_alg    = &params[0];
+    uint8_t *const threads_u8  = &hash_alg[hydro_pwhash_HASH_ALGBYTES];
+    uint8_t *const opslimit_u8 = &threads_u8[hydro_pwhash_THREADSBYTES];
+    uint8_t *const memlimit_u8 = &opslimit_u8[hydro_pwhash_OPSLIMITBYTES];
+    uint8_t *const salt        = &memlimit_u8[hydro_pwhash_MEMLIMITBYTES];
+    uint8_t *const h           = &salt[hydro_pwhash_SALTBYTES];
+
+    uint64_t opslimit;
+    size_t   memlimit;
+    uint8_t  threads;
+
+    (void) memlimit;
+    if (*enc_alg != hydro_pwhash_ENC_ALG) {
+        return -1;
+    }
+    if (hydro_secretbox_decrypt(params, secretbox,
+                                hydro_secretbox_HEADERBYTES + hydro_pwhash_PARAMSBYTES,
+                                (uint64_t) *enc_alg, hydro_pwhash_CONTEXT, master_key) != 0) {
+        return -1;
+    }
+    if (*hash_alg != hydro_pwhash_HASH_ALG || (opslimit = LOAD64_LE(opslimit_u8)) > opslimit_max ||
+        (memlimit = (size_t) LOAD64_LE(memlimit_u8)) > memlimit_max ||
+        (threads = *threads_u8) > threads_max) {
+        return -1;
+    }
+    if (_hydro_pwhash_hash(computed_h, hydro_pwhash_HASHBYTES, salt, passwd, passwd_len,
+                           hydro_pwhash_CONTEXT, zero, opslimit, memlimit, threads) == 0 &&
+        hydro_equal(computed_h, h, hydro_pwhash_HASHBYTES) == 1) {
+        return 0;
+    }
+    return -1;
+}
+
+int
+hydro_pwhash_verify(const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd,
+                    size_t passwd_len, const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                    uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max)
+{
+    uint8_t computed_h[hydro_pwhash_HASHBYTES];
+    int     ret;
+
+    ret = _hydro_pwhash_verify(computed_h, stored, passwd, passwd_len, master_key, opslimit_max,
+                               memlimit_max, threads_max);
+    hydro_memzero(computed_h, sizeof computed_h);
+
+    return ret;
+}
+
+int
+hydro_pwhash_derive_static_key(uint8_t *static_key, size_t static_key_len,
+                               const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd,
+                               size_t passwd_len, const char ctx[hydro_pwhash_CONTEXTBYTES],
+                               const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                               uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max)
+{
+    uint8_t computed_h[hydro_pwhash_HASHBYTES];
+
+    if (_hydro_pwhash_verify(computed_h, stored, passwd, passwd_len, master_key, opslimit_max,
+                             memlimit_max, threads_max) != 0) {
+        hydro_memzero(computed_h, sizeof computed_h);
+        return -1;
+    }
+    COMPILER_ASSERT(hydro_kdf_CONTEXTBYTES <= hydro_pwhash_CONTEXTBYTES);
+    COMPILER_ASSERT(hydro_kdf_KEYBYTES <= hydro_pwhash_HASHBYTES);
+    hydro_kdf_derive_from_key(static_key, static_key_len, 0, ctx, computed_h);
+    hydro_memzero(computed_h, sizeof computed_h);
+
+    return 0;
+}
+
+int
+hydro_pwhash_reencrypt(uint8_t       stored[hydro_pwhash_STOREDBYTES],
+                       const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
+                       const uint8_t new_master_key[hydro_pwhash_MASTERKEYBYTES])
+{
+    uint8_t *const enc_alg   = &stored[0];
+    uint8_t *const secretbox = &enc_alg[hydro_pwhash_ENC_ALGBYTES];
+    uint8_t *const params    = &secretbox[hydro_secretbox_HEADERBYTES];
+
+    if (*enc_alg != hydro_pwhash_ENC_ALG) {
+        return -1;
+    }
+    if (hydro_secretbox_decrypt(secretbox, secretbox,
+                                hydro_secretbox_HEADERBYTES + hydro_pwhash_PARAMSBYTES,
+                                (uint64_t) *enc_alg, hydro_pwhash_CONTEXT, master_key) != 0) {
+        return -1;
+    }
+    memmove(params, secretbox, hydro_pwhash_PARAMSBYTES);
+    return hydro_secretbox_encrypt(secretbox, params, hydro_pwhash_PARAMSBYTES, (uint64_t) *enc_alg,
+                                   hydro_pwhash_CONTEXT, new_master_key);
+}
+
+int
+hydro_pwhash_upgrade(uint8_t       stored[hydro_pwhash_STOREDBYTES],
+                     const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
+                     size_t memlimit, uint8_t threads)
+{
+    uint8_t *const enc_alg     = &stored[0];
+    uint8_t *const secretbox   = &enc_alg[hydro_pwhash_ENC_ALGBYTES];
+    uint8_t *const params      = &secretbox[hydro_secretbox_HEADERBYTES];
+    uint8_t *const hash_alg    = &params[0];
+    uint8_t *const threads_u8  = &hash_alg[hydro_pwhash_HASH_ALGBYTES];
+    uint8_t *const opslimit_u8 = &threads_u8[hydro_pwhash_THREADSBYTES];
+    uint8_t *const memlimit_u8 = &opslimit_u8[hydro_pwhash_OPSLIMITBYTES];
+    uint8_t *const salt        = &memlimit_u8[hydro_pwhash_MEMLIMITBYTES];
+    uint8_t *const h           = &salt[hydro_pwhash_SALTBYTES];
+
+    _hydro_attr_aligned_(16) uint8_t state[gimli_BLOCKBYTES];
+    uint64_t                         i;
+    uint64_t                         opslimit_prev;
+
+    if (*enc_alg != hydro_pwhash_ENC_ALG) {
+        return -1;
+    }
+    if (hydro_secretbox_decrypt(secretbox, secretbox,
+                                hydro_secretbox_HEADERBYTES + hydro_pwhash_PARAMSBYTES,
+                                (uint64_t) *enc_alg, hydro_pwhash_CONTEXT, master_key) != 0) {
+        return -1;
+    }
+    memmove(params, secretbox, hydro_pwhash_PARAMSBYTES);
+    opslimit_prev = LOAD64_LE(opslimit_u8);
+    if (*hash_alg != hydro_pwhash_HASH_ALG) {
+        mem_zero(stored, hydro_pwhash_STOREDBYTES);
+        return -1;
+    }
+    COMPILER_ASSERT(hydro_random_SEEDBYTES == gimli_CAPACITY);
+    memcpy(state + gimli_RATE, h, hydro_random_SEEDBYTES);
+    for (i = opslimit_prev; i < opslimit; i++) {
+        mem_zero(state, gimli_RATE);
+        STORE64_LE(state, i);
+        gimli_core_u8(state, 0);
+    }
+    mem_zero(state, gimli_RATE);
+    memcpy(h, state + gimli_RATE, hydro_random_SEEDBYTES);
+    *threads_u8 = threads;
+    STORE64_LE(opslimit_u8, opslimit);
+    STORE64_LE(memlimit_u8, (uint64_t) memlimit);
+
+    return hydro_secretbox_encrypt(secretbox, params, hydro_pwhash_PARAMSBYTES, (uint64_t) *enc_alg,
+                                   hydro_pwhash_CONTEXT, master_key);
+}

+ 378 - 0
libhydrogen.mod/libhydrogen/impl/random.h

@@ -0,0 +1,378 @@
+static TLS struct {
+    _hydro_attr_aligned_(16) uint8_t state[gimli_BLOCKBYTES];
+    uint64_t counter;
+    uint8_t  initialized;
+    uint8_t  available;
+} hydro_random_context;
+
+#if defined(AVR) && !defined(__unix__)
+#include <Arduino.h>
+
+static bool
+hydro_random_rbit(unsigned int x)
+{
+    size_t i;
+    bool   res = 0;
+
+    for (i = 0; i < sizeof x; i++) {
+        res ^= ((x >> i) & 1);
+    }
+    return res;
+}
+
+static int
+hydro_random_init(void)
+{
+    const char       ctx[hydro_hash_CONTEXTBYTES] = { 'h', 'y', 'd', 'r', 'o', 'P', 'R', 'G' };
+    hydro_hash_state st;
+    uint16_t         ebits = 0;
+    uint16_t         tc;
+    bool             a, b;
+
+    cli();
+    MCUSR = 0;
+    WDTCSR |= _BV(WDCE) | _BV(WDE);
+    WDTCSR = _BV(WDIE);
+    sei();
+
+    hydro_hash_init(&st, ctx, NULL);
+
+    while (ebits < 256) {
+        delay(1);
+        tc = TCNT1;
+        hydro_hash_update(&st, (const uint8_t *) &tc, sizeof tc);
+        a = hydro_random_rbit(tc);
+        delay(1);
+        tc = TCNT1;
+        b  = hydro_random_rbit(tc);
+        hydro_hash_update(&st, (const uint8_t *) &tc, sizeof tc);
+        if (a == b) {
+            continue;
+        }
+        hydro_hash_update(&st, (const uint8_t *) &b, sizeof b);
+        ebits++;
+    }
+
+    cli();
+    MCUSR = 0;
+    WDTCSR |= _BV(WDCE) | _BV(WDE);
+    WDTCSR = 0;
+    sei();
+
+    hydro_hash_final(&st, hydro_random_context.state, sizeof hydro_random_context.state);
+    hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state);
+
+    return 0;
+}
+
+ISR(WDT_vect) {}
+
+#elif (defined(ESP32) || defined(ESP8266)) && !defined(__unix__)
+
+// Important: RF *must* be activated on ESP board
+// https://techtutorialsx.com/2017/12/22/esp32-arduino-random-number-generation/
+
+#include <esp_system.h>
+
+static int
+hydro_random_init(void)
+{
+    const char       ctx[hydro_hash_CONTEXTBYTES] = { 'h', 'y', 'd', 'r', 'o', 'P', 'R', 'G' };
+    hydro_hash_state st;
+    uint16_t         ebits = 0;
+    uint16_t         tc;
+    bool             a, b;
+
+    hydro_hash_init(&st, ctx, NULL);
+
+    while (ebits < 256) {
+        uint32_t r = esp_random();
+
+        delay(10);
+        hydro_hash_update(&st, (const uint32_t *) &r, sizeof r);
+        ebits += 32;
+    }
+
+    hydro_hash_final(&st, hydro_random_context.state, sizeof hydro_random_context.state);
+    hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state);
+
+    return 0;
+}
+
+#elif defined(_WIN32)
+
+#include <windows.h>
+#define RtlGenRandom SystemFunction036
+#if defined(__cplusplus)
+extern "C"
+#endif
+    BOOLEAN NTAPI
+            RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
+#pragma comment(lib, "advapi32.lib")
+
+static int
+hydro_random_init(void)
+{
+    if (!RtlGenRandom((PVOID) hydro_random_context.state,
+                      (ULONG) sizeof hydro_random_context.state)) {
+        return -1;
+    }
+    hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state);
+
+    return 0;
+}
+
+#elif defined(__unix__)
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <poll.h>
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __linux__
+static int
+hydro_random_block_on_dev_random(void)
+{
+    struct pollfd pfd;
+    int           fd;
+    int           pret;
+
+    fd = open("/dev/random", O_RDONLY);
+    if (fd == -1) {
+        return 0;
+    }
+    pfd.fd      = fd;
+    pfd.events  = POLLIN;
+    pfd.revents = 0;
+    do {
+        pret = poll(&pfd, 1, -1);
+    } while (pret < 0 && (errno == EINTR || errno == EAGAIN));
+    if (pret != 1) {
+        (void) close(fd);
+        errno = EIO;
+        return -1;
+    }
+    return close(fd);
+}
+#endif
+
+static ssize_t
+hydro_random_safe_read(const int fd, void *const buf_, size_t len)
+{
+    unsigned char *buf = (unsigned char *) buf_;
+    ssize_t        readnb;
+
+    do {
+        while ((readnb = read(fd, buf, len)) < (ssize_t) 0 && (errno == EINTR || errno == EAGAIN))
+            ;
+        if (readnb < (ssize_t) 0) {
+            return readnb;
+        }
+        if (readnb == (ssize_t) 0) {
+            break;
+        }
+        len -= (size_t) readnb;
+        buf += readnb;
+    } while (len > (ssize_t) 0);
+
+    return (ssize_t)(buf - (unsigned char *) buf_);
+}
+
+static int
+hydro_random_init(void)
+{
+    uint8_t tmp[gimli_BLOCKBYTES + 8];
+    int     fd;
+    int     ret = -1;
+
+#ifdef __linux__
+    if (hydro_random_block_on_dev_random() != 0) {
+        return -1;
+    }
+#endif
+    do {
+        fd = open("/dev/urandom", O_RDONLY);
+        if (fd == -1 && errno != EINTR) {
+            return -1;
+        }
+    } while (fd == -1);
+    if (hydro_random_safe_read(fd, tmp, sizeof tmp) == (ssize_t) sizeof tmp) {
+        memcpy(hydro_random_context.state, tmp, gimli_BLOCKBYTES);
+        memcpy(&hydro_random_context.counter, tmp + gimli_BLOCKBYTES, 8);
+        hydro_memzero(tmp, sizeof tmp);
+        ret = 0;
+    }
+    ret |= close(fd);
+
+    return ret;
+}
+
+#elif defined(TARGET_LIKE_MBED)
+
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/entropy.h>
+
+#if defined(MBEDTLS_ENTROPY_C)
+
+static int
+hydro_random_init(void)
+{
+    mbedtls_entropy_context entropy;
+    uint16_t                pos = 0;
+
+    mbedtls_entropy_init(&entropy);
+
+    // Pull data directly out of the entropy pool for the state, as it's small enough.
+    if (mbedtls_entropy_func(&entropy, (uint8_t *) &hydro_random_context.counter,
+                             sizeof hydro_random_context.counter) != 0) {
+        return -1;
+    }
+    // mbedtls_entropy_func can't provide more than MBEDTLS_ENTROPY_BLOCK_SIZE in one go.
+    // This constant depends of mbedTLS configuration (whether the PRNG is backed by SHA256/SHA512
+    // at this time) Therefore, if necessary, we get entropy multiple times.
+
+    do {
+        const uint8_t dataLeftToConsume = gimli_BLOCKBYTES - pos;
+        const uint8_t currentChunkSize  = (dataLeftToConsume > MBEDTLS_ENTROPY_BLOCK_SIZE)
+                                             ? MBEDTLS_ENTROPY_BLOCK_SIZE
+                                             : dataLeftToConsume;
+
+        // Forces mbedTLS to fetch fresh entropy, then get some to feed libhydrogen.
+        if (mbedtls_entropy_gather(&entropy) != 0 ||
+            mbedtls_entropy_func(&entropy, &hydro_random_context.state[pos], currentChunkSize) != 0) {
+            return -1;
+        }
+        pos += MBEDTLS_ENTROPY_BLOCK_SIZE;
+    } while (pos < gimli_BLOCKBYTES);
+
+    mbedtls_entropy_free(&entropy);
+
+    return 0;
+}
+
+#else
+#error Need an entropy source
+#endif
+
+#else
+#error Unsupported platform
+#endif
+
+static void
+hydro_random_check_initialized(void)
+{
+    if (hydro_random_context.initialized == 0) {
+        if (hydro_random_init() != 0) {
+            abort();
+        }
+        gimli_core_u8(hydro_random_context.state, 0);
+        hydro_random_ratchet();
+        hydro_random_context.initialized = 1;
+    }
+}
+
+void
+hydro_random_ratchet(void)
+{
+    mem_zero(hydro_random_context.state, gimli_RATE);
+    STORE64_LE(hydro_random_context.state, hydro_random_context.counter);
+    hydro_random_context.counter++;
+    gimli_core_u8(hydro_random_context.state, 0);
+    hydro_random_context.available = gimli_RATE;
+}
+
+uint32_t
+hydro_random_u32(void)
+{
+    uint32_t v;
+
+    hydro_random_check_initialized();
+    if (hydro_random_context.available < 4) {
+        hydro_random_ratchet();
+    }
+    memcpy(&v, &hydro_random_context.state[gimli_RATE - hydro_random_context.available], 4);
+    hydro_random_context.available -= 4;
+
+    return v;
+}
+
+uint32_t
+hydro_random_uniform(const uint32_t upper_bound)
+{
+    uint32_t min;
+    uint32_t r;
+
+    if (upper_bound < 2U) {
+        return 0;
+    }
+    min = (1U + ~upper_bound) % upper_bound; /* = 2**32 mod upper_bound */
+    do {
+        r = hydro_random_u32();
+    } while (r < min);
+    /* r is now clamped to a set whose size mod upper_bound == 0
+     * the worst case (2**31+1) requires 2 attempts on average */
+
+    return r % upper_bound;
+}
+
+void
+hydro_random_buf(void *out, size_t out_len)
+{
+    uint8_t *p = (uint8_t *) out;
+    size_t   i;
+    size_t   leftover;
+
+    hydro_random_check_initialized();
+    for (i = 0; i < out_len / gimli_RATE; i++) {
+        gimli_core_u8(hydro_random_context.state, 0);
+        memcpy(p + i * gimli_RATE, hydro_random_context.state, gimli_RATE);
+    }
+    leftover = out_len % gimli_RATE;
+    if (leftover != 0) {
+        gimli_core_u8(hydro_random_context.state, 0);
+        mem_cpy(p + i * gimli_RATE, hydro_random_context.state, leftover);
+    }
+    hydro_random_ratchet();
+}
+
+void
+hydro_random_buf_deterministic(void *out, size_t out_len,
+                               const uint8_t seed[hydro_random_SEEDBYTES])
+{
+    static const uint8_t             prefix[] = { 7, 'd', 'r', 'b', 'g', '2', '5', '6' };
+    _hydro_attr_aligned_(16) uint8_t state[gimli_BLOCKBYTES];
+    uint8_t *                        p = (uint8_t *) out;
+    size_t                           i;
+    size_t                           leftover;
+
+    mem_zero(state, gimli_BLOCKBYTES);
+    COMPILER_ASSERT(sizeof prefix + 8 <= gimli_RATE);
+    memcpy(state, prefix, sizeof prefix);
+    STORE64_LE(state + sizeof prefix, (uint64_t) out_len);
+    gimli_core_u8(state, 1);
+    COMPILER_ASSERT(hydro_random_SEEDBYTES == gimli_RATE * 2);
+    mem_xor(state, seed, gimli_RATE);
+    gimli_core_u8(state, 2);
+    mem_xor(state, seed + gimli_RATE, gimli_RATE);
+    gimli_core_u8(state, 2);
+    for (i = 0; i < out_len / gimli_RATE; i++) {
+        gimli_core_u8(state, 0);
+        memcpy(p + i * gimli_RATE, state, gimli_RATE);
+    }
+    leftover = out_len % gimli_RATE;
+    if (leftover != 0) {
+        gimli_core_u8(state, 0);
+        mem_cpy(p + i * gimli_RATE, state, leftover);
+    }
+    hydro_random_ratchet();
+}
+
+void
+hydro_random_reseed(void)
+{
+    hydro_random_context.initialized = 0;
+    hydro_random_check_initialized();
+}

+ 236 - 0
libhydrogen.mod/libhydrogen/impl/secretbox.h

@@ -0,0 +1,236 @@
+#define hydro_secretbox_IVBYTES 20
+#define hydro_secretbox_SIVBYTES 20
+#define hydro_secretbox_MACBYTES 16
+
+void
+hydro_secretbox_keygen(uint8_t key[hydro_secretbox_KEYBYTES])
+{
+    hydro_random_buf(key, hydro_secretbox_KEYBYTES);
+}
+
+static void
+hydro_secretbox_xor_enc(uint8_t buf[gimli_BLOCKBYTES], uint8_t *out, const uint8_t *in,
+                        size_t inlen)
+{
+    size_t i;
+    size_t leftover;
+
+    for (i = 0; i < inlen / gimli_RATE; i++) {
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, gimli_RATE);
+        memcpy(buf, &out[i * gimli_RATE], gimli_RATE);
+        gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+    }
+    leftover = inlen % gimli_RATE;
+    if (leftover != 0) {
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, leftover);
+        mem_cpy(buf, &out[i * gimli_RATE], leftover);
+    }
+    gimli_pad_u8(buf, leftover, gimli_DOMAIN_AEAD);
+    gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+}
+
+static void
+hydro_secretbox_xor_dec(uint8_t buf[gimli_BLOCKBYTES], uint8_t *out, const uint8_t *in,
+                        size_t inlen)
+{
+    size_t i;
+    size_t leftover;
+
+    for (i = 0; i < inlen / gimli_RATE; i++) {
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, gimli_RATE);
+        memcpy(buf, &in[i * gimli_RATE], gimli_RATE);
+        gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+    }
+    leftover = inlen % gimli_RATE;
+    if (leftover != 0) {
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, leftover);
+        mem_cpy(buf, &in[i * gimli_RATE], leftover);
+    }
+    gimli_pad_u8(buf, leftover, gimli_DOMAIN_AEAD);
+    gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+}
+
+static void
+hydro_secretbox_setup(uint8_t buf[gimli_BLOCKBYTES], uint64_t msg_id,
+                      const char    ctx[hydro_secretbox_CONTEXTBYTES],
+                      const uint8_t key[hydro_secretbox_KEYBYTES],
+                      const uint8_t iv[hydro_secretbox_IVBYTES], uint8_t key_tag)
+{
+    static const uint8_t prefix[] = { 6, 's', 'b', 'x', '2', '5', '6', 8 };
+    uint8_t              msg_id_le[8];
+
+    mem_zero(buf, gimli_BLOCKBYTES);
+    COMPILER_ASSERT(hydro_secretbox_CONTEXTBYTES == 8);
+    COMPILER_ASSERT(sizeof prefix + hydro_secretbox_CONTEXTBYTES <= gimli_RATE);
+    memcpy(buf, prefix, sizeof prefix);
+    memcpy(buf + sizeof prefix, ctx, hydro_secretbox_CONTEXTBYTES);
+    COMPILER_ASSERT(sizeof prefix + hydro_secretbox_CONTEXTBYTES == gimli_RATE);
+    gimli_core_u8(buf, gimli_TAG_HEADER);
+
+    COMPILER_ASSERT(hydro_secretbox_KEYBYTES == 2 * gimli_RATE);
+    mem_xor(buf, key, gimli_RATE);
+    gimli_core_u8(buf, key_tag);
+    mem_xor(buf, key + gimli_RATE, gimli_RATE);
+    gimli_core_u8(buf, key_tag);
+
+    COMPILER_ASSERT(hydro_secretbox_IVBYTES < gimli_RATE * 2);
+    buf[0] ^= hydro_secretbox_IVBYTES;
+    mem_xor(&buf[1], iv, gimli_RATE - 1);
+    gimli_core_u8(buf, gimli_TAG_HEADER);
+    mem_xor(buf, iv + gimli_RATE - 1, hydro_secretbox_IVBYTES - (gimli_RATE - 1));
+    STORE64_LE(msg_id_le, msg_id);
+    COMPILER_ASSERT(hydro_secretbox_IVBYTES - gimli_RATE + 8 <= gimli_RATE);
+    mem_xor(buf + hydro_secretbox_IVBYTES - gimli_RATE, msg_id_le, 8);
+    gimli_core_u8(buf, gimli_TAG_HEADER);
+}
+
+static void
+hydro_secretbox_finalize(uint8_t *buf, const uint8_t key[hydro_secretbox_KEYBYTES], uint8_t tag)
+{
+    COMPILER_ASSERT(hydro_secretbox_KEYBYTES == gimli_CAPACITY);
+    mem_xor(buf + gimli_RATE, key, hydro_secretbox_KEYBYTES);
+    gimli_core_u8(buf, tag);
+    mem_xor(buf + gimli_RATE, key, hydro_secretbox_KEYBYTES);
+    gimli_core_u8(buf, tag);
+}
+
+static int
+hydro_secretbox_encrypt_iv(uint8_t *c, const void *m_, size_t mlen, uint64_t msg_id,
+                           const char    ctx[hydro_secretbox_CONTEXTBYTES],
+                           const uint8_t key[hydro_secretbox_KEYBYTES],
+                           const uint8_t iv[hydro_secretbox_IVBYTES])
+{
+    _hydro_attr_aligned_(16) uint32_t state[gimli_BLOCKBYTES / 4];
+    uint8_t *                         buf = (uint8_t *) (void *) state;
+    const uint8_t *                   m   = (const uint8_t *) m_;
+    uint8_t *                         siv = &c[0];
+    uint8_t *                         mac = &c[hydro_secretbox_SIVBYTES];
+    uint8_t *                         ct  = &c[hydro_secretbox_SIVBYTES + hydro_secretbox_MACBYTES];
+    size_t                            i;
+    size_t                            leftover;
+
+    if (c == m) {
+        memmove(c + hydro_secretbox_HEADERBYTES, m, mlen);
+        m = c + hydro_secretbox_HEADERBYTES;
+    }
+
+    /* first pass: compute the SIV */
+
+    hydro_secretbox_setup(buf, msg_id, ctx, key, iv, gimli_TAG_KEY0);
+    for (i = 0; i < mlen / gimli_RATE; i++) {
+        mem_xor(buf, &m[i * gimli_RATE], gimli_RATE);
+        gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+    }
+    leftover = mlen % gimli_RATE;
+    if (leftover != 0) {
+        mem_xor(buf, &m[i * gimli_RATE], leftover);
+    }
+    gimli_pad_u8(buf, leftover, gimli_DOMAIN_XOF);
+    gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+
+    hydro_secretbox_finalize(buf, key, gimli_TAG_FINAL0);
+    COMPILER_ASSERT(hydro_secretbox_SIVBYTES <= gimli_CAPACITY);
+    memcpy(siv, buf + gimli_RATE, hydro_secretbox_SIVBYTES);
+
+    /* second pass: encrypt the message, mix the key, squeeze an extra block for
+     * the MAC */
+
+    COMPILER_ASSERT(hydro_secretbox_SIVBYTES == hydro_secretbox_IVBYTES);
+    hydro_secretbox_setup(buf, msg_id, ctx, key, siv, gimli_TAG_KEY);
+    hydro_secretbox_xor_enc(buf, ct, m, mlen);
+
+    hydro_secretbox_finalize(buf, key, gimli_TAG_FINAL);
+    COMPILER_ASSERT(hydro_secretbox_MACBYTES <= gimli_CAPACITY);
+    memcpy(mac, buf + gimli_RATE, hydro_secretbox_MACBYTES);
+
+    return 0;
+}
+
+void
+hydro_secretbox_probe_create(uint8_t probe[hydro_secretbox_PROBEBYTES], const uint8_t *c,
+                             size_t c_len, const char ctx[hydro_secretbox_CONTEXTBYTES],
+                             const uint8_t key[hydro_secretbox_KEYBYTES])
+{
+    const uint8_t *mac;
+
+    if (c_len < hydro_secretbox_HEADERBYTES) {
+        abort();
+    }
+    mac = &c[hydro_secretbox_SIVBYTES];
+    COMPILER_ASSERT(hydro_secretbox_CONTEXTBYTES >= hydro_hash_CONTEXTBYTES);
+    COMPILER_ASSERT(hydro_secretbox_KEYBYTES >= hydro_hash_KEYBYTES);
+    hydro_hash_hash(probe, hydro_secretbox_PROBEBYTES, mac, hydro_secretbox_MACBYTES, ctx, key);
+}
+
+int
+hydro_secretbox_probe_verify(const uint8_t probe[hydro_secretbox_PROBEBYTES], const uint8_t *c,
+                             size_t c_len, const char ctx[hydro_secretbox_CONTEXTBYTES],
+                             const uint8_t key[hydro_secretbox_KEYBYTES])
+{
+    uint8_t        computed_probe[hydro_secretbox_PROBEBYTES];
+    const uint8_t *mac;
+
+    if (c_len < hydro_secretbox_HEADERBYTES) {
+        return -1;
+    }
+    mac = &c[hydro_secretbox_SIVBYTES];
+    hydro_hash_hash(computed_probe, hydro_secretbox_PROBEBYTES, mac, hydro_secretbox_MACBYTES, ctx,
+                    key);
+    if (hydro_equal(computed_probe, probe, hydro_secretbox_PROBEBYTES) == 1) {
+        return 0;
+    }
+    hydro_memzero(computed_probe, hydro_secretbox_PROBEBYTES);
+    return -1;
+}
+
+int
+hydro_secretbox_encrypt(uint8_t *c, const void *m_, size_t mlen, uint64_t msg_id,
+                        const char    ctx[hydro_secretbox_CONTEXTBYTES],
+                        const uint8_t key[hydro_secretbox_KEYBYTES])
+{
+    uint8_t iv[hydro_secretbox_IVBYTES];
+
+    hydro_random_buf(iv, sizeof iv);
+
+    return hydro_secretbox_encrypt_iv(c, m_, mlen, msg_id, ctx, key, iv);
+}
+
+int
+hydro_secretbox_decrypt(void *m_, const uint8_t *c, size_t clen, uint64_t msg_id,
+                        const char    ctx[hydro_secretbox_CONTEXTBYTES],
+                        const uint8_t key[hydro_secretbox_KEYBYTES])
+{
+    _hydro_attr_aligned_(16) uint32_t state[gimli_BLOCKBYTES / 4];
+    uint32_t                          pub_mac[hydro_secretbox_MACBYTES / 4];
+    uint8_t *                         buf = (uint8_t *) (void *) state;
+    const uint8_t *                   siv;
+    const uint8_t *                   mac;
+    const uint8_t *                   ct;
+    uint8_t *                         m = (uint8_t *) m_;
+    size_t                            mlen;
+    uint32_t                          cv;
+
+    if (clen < hydro_secretbox_HEADERBYTES) {
+        return -1;
+    }
+    siv = &c[0];
+    mac = &c[hydro_secretbox_SIVBYTES];
+    ct  = &c[hydro_secretbox_SIVBYTES + hydro_secretbox_MACBYTES];
+
+    mlen = clen - hydro_secretbox_HEADERBYTES;
+    memcpy(pub_mac, mac, sizeof pub_mac);
+    COMPILER_ASSERT(hydro_secretbox_SIVBYTES == hydro_secretbox_IVBYTES);
+    hydro_secretbox_setup(buf, msg_id, ctx, key, siv, gimli_TAG_KEY);
+    hydro_secretbox_xor_dec(buf, m, ct, mlen);
+
+    hydro_secretbox_finalize(buf, key, gimli_TAG_FINAL);
+    COMPILER_ASSERT(hydro_secretbox_MACBYTES <= gimli_CAPACITY);
+    COMPILER_ASSERT(gimli_RATE % 4 == 0);
+    cv = hydro_mem_ct_cmp_u32(state + gimli_RATE / 4, pub_mac, hydro_secretbox_MACBYTES / 4);
+    hydro_mem_ct_zero_u32(state, gimli_BLOCKBYTES / 4);
+    if (cv != 0) {
+        mem_zero(m, mlen);
+        return -1;
+    }
+    return 0;
+}

+ 207 - 0
libhydrogen.mod/libhydrogen/impl/sign.h

@@ -0,0 +1,207 @@
+#define hydro_sign_CHALLENGEBYTES 32
+#define hydro_sign_NONCEBYTES 32
+#define hydro_sign_PREHASHBYTES 64
+
+static void
+hydro_sign_p2(uint8_t sig[hydro_x25519_BYTES], const uint8_t challenge[hydro_sign_CHALLENGEBYTES],
+              const uint8_t eph_sk[hydro_x25519_BYTES], const uint8_t sk[hydro_x25519_BYTES])
+{
+    hydro_x25519_scalar_t scalar1, scalar2, scalar3;
+
+    COMPILER_ASSERT(hydro_sign_CHALLENGEBYTES == hydro_x25519_BYTES);
+    hydro_x25519_swapin(scalar1, eph_sk);
+    hydro_x25519_swapin(scalar2, sk);
+    hydro_x25519_swapin(scalar3, challenge);
+    hydro_x25519_sc_montmul(scalar1, scalar2, scalar3);
+    mem_zero(scalar2, sizeof scalar2);
+    hydro_x25519_sc_montmul(scalar2, scalar1, hydro_x25519_sc_r2);
+    hydro_x25519_swapout(sig, scalar2);
+}
+
+static void
+hydro_sign_challenge(uint8_t       challenge[hydro_sign_CHALLENGEBYTES],
+                     const uint8_t nonce[hydro_sign_NONCEBYTES],
+                     const uint8_t pk[hydro_sign_PUBLICKEYBYTES],
+                     const uint8_t prehash[hydro_sign_PREHASHBYTES])
+{
+    hydro_hash_state st;
+
+    hydro_hash_init(&st, (const char *) zero, NULL);
+    hydro_hash_update(&st, nonce, hydro_sign_NONCEBYTES);
+    hydro_hash_update(&st, pk, hydro_sign_PUBLICKEYBYTES);
+    hydro_hash_update(&st, prehash, hydro_sign_PREHASHBYTES);
+    hydro_hash_final(&st, challenge, hydro_sign_CHALLENGEBYTES);
+}
+
+static int
+hydro_sign_prehash(uint8_t csig[hydro_sign_BYTES], const uint8_t prehash[hydro_sign_PREHASHBYTES],
+                   const uint8_t sk[hydro_sign_SECRETKEYBYTES])
+{
+    hydro_hash_state st;
+    uint8_t          challenge[hydro_sign_CHALLENGEBYTES];
+    const uint8_t *  pk     = &sk[hydro_x25519_SECRETKEYBYTES];
+    uint8_t *        nonce  = &csig[0];
+    uint8_t *        sig    = &csig[hydro_sign_NONCEBYTES];
+    uint8_t *        eph_sk = sig;
+
+    hydro_random_buf(eph_sk, hydro_x25519_SECRETKEYBYTES);
+    COMPILER_ASSERT(hydro_x25519_SECRETKEYBYTES == hydro_hash_KEYBYTES);
+    hydro_hash_init(&st, (const char *) zero, sk);
+    hydro_hash_update(&st, eph_sk, hydro_x25519_SECRETKEYBYTES);
+    hydro_hash_update(&st, prehash, hydro_sign_CHALLENGEBYTES);
+    hydro_hash_final(&st, eph_sk, hydro_x25519_SECRETKEYBYTES);
+
+    hydro_x25519_scalarmult_base_uniform(nonce, eph_sk);
+    hydro_sign_challenge(challenge, nonce, pk, prehash);
+
+    COMPILER_ASSERT(hydro_sign_BYTES == hydro_sign_NONCEBYTES + hydro_x25519_SECRETKEYBYTES);
+    COMPILER_ASSERT(hydro_x25519_SECRETKEYBYTES <= hydro_sign_CHALLENGEBYTES);
+    hydro_sign_p2(sig, challenge, eph_sk, sk);
+
+    return 0;
+}
+
+static int
+hydro_sign_verify_core(hydro_x25519_fe xs[5], const hydro_x25519_limb_t *other1,
+                       const uint8_t other2[hydro_x25519_BYTES])
+{
+    hydro_x25519_limb_t *     z2 = xs[1], *x3 = xs[2], *z3 = xs[3];
+    hydro_x25519_fe           xo2;
+    const hydro_x25519_limb_t sixteen = 16;
+
+    hydro_x25519_swapin(xo2, other2);
+    memcpy(x3, other1, 2 * sizeof(hydro_x25519_fe));
+    hydro_x25519_ladder_part1(xs);
+
+    /* Here z2 = t2^2 */
+    hydro_x25519_mul1(z2, other1);
+    hydro_x25519_mul1(z2, other1 + hydro_x25519_NLIMBS);
+    hydro_x25519_mul1(z2, xo2);
+
+    hydro_x25519_mul(z2, z2, &sixteen, 1);
+
+    hydro_x25519_mul1(z3, xo2);
+    hydro_x25519_sub(z3, z3, x3);
+    hydro_x25519_sqr1(z3);
+
+    /* check equality */
+    hydro_x25519_sub(z3, z3, z2);
+
+    /* canon(z2): both sides are zero. canon(z3): the two sides are equal. */
+    /* Reject sigs where both sides are zero. */
+    return hydro_x25519_canon(z2) | ~hydro_x25519_canon(z3);
+}
+
+static int
+hydro_sign_verify_p2(const uint8_t sig[hydro_x25519_BYTES],
+                     const uint8_t challenge[hydro_sign_CHALLENGEBYTES],
+                     const uint8_t nonce[hydro_sign_NONCEBYTES],
+                     const uint8_t pk[hydro_x25519_BYTES])
+{
+    hydro_x25519_fe xs[7];
+
+    hydro_x25519_core(&xs[0], challenge, pk, 0);
+    hydro_x25519_core(&xs[2], sig, hydro_x25519_BASE_POINT, 0);
+
+    return hydro_sign_verify_core(&xs[2], xs[0], nonce);
+}
+
+static int
+hydro_sign_verify_challenge(const uint8_t csig[hydro_sign_BYTES],
+                            const uint8_t challenge[hydro_sign_CHALLENGEBYTES],
+                            const uint8_t pk[hydro_sign_PUBLICKEYBYTES])
+{
+    const uint8_t *nonce = &csig[0];
+    const uint8_t *sig   = &csig[hydro_sign_NONCEBYTES];
+
+    return hydro_sign_verify_p2(sig, challenge, nonce, pk);
+}
+
+void
+hydro_sign_keygen(hydro_sign_keypair *kp)
+{
+    uint8_t *pk_copy = &kp->sk[hydro_x25519_SECRETKEYBYTES];
+
+    COMPILER_ASSERT(hydro_sign_SECRETKEYBYTES ==
+                    hydro_x25519_SECRETKEYBYTES + hydro_x25519_PUBLICKEYBYTES);
+    COMPILER_ASSERT(hydro_sign_PUBLICKEYBYTES == hydro_x25519_PUBLICKEYBYTES);
+    hydro_random_buf(kp->sk, hydro_x25519_SECRETKEYBYTES);
+    hydro_x25519_scalarmult_base_uniform(kp->pk, kp->sk);
+    memcpy(pk_copy, kp->pk, hydro_x25519_PUBLICKEYBYTES);
+}
+
+void
+hydro_sign_keygen_deterministic(hydro_sign_keypair *kp, const uint8_t seed[hydro_sign_SEEDBYTES])
+{
+    uint8_t *pk_copy = &kp->sk[hydro_x25519_SECRETKEYBYTES];
+
+    COMPILER_ASSERT(hydro_sign_SEEDBYTES >= hydro_random_SEEDBYTES);
+    hydro_random_buf_deterministic(kp->sk, hydro_x25519_SECRETKEYBYTES, seed);
+    hydro_x25519_scalarmult_base_uniform(kp->pk, kp->sk);
+    memcpy(pk_copy, kp->pk, hydro_x25519_PUBLICKEYBYTES);
+}
+
+int
+hydro_sign_init(hydro_sign_state *state, const char ctx[hydro_sign_CONTEXTBYTES])
+{
+    return hydro_hash_init(&state->hash_st, ctx, NULL);
+}
+
+int
+hydro_sign_update(hydro_sign_state *state, const void *m_, size_t mlen)
+{
+    return hydro_hash_update(&state->hash_st, m_, mlen);
+}
+
+int
+hydro_sign_final_create(hydro_sign_state *state, uint8_t csig[hydro_sign_BYTES],
+                        const uint8_t sk[hydro_sign_SECRETKEYBYTES])
+{
+    uint8_t prehash[hydro_sign_PREHASHBYTES];
+
+    hydro_hash_final(&state->hash_st, prehash, sizeof prehash);
+
+    return hydro_sign_prehash(csig, prehash, sk);
+}
+
+int
+hydro_sign_final_verify(hydro_sign_state *state, const uint8_t csig[hydro_sign_BYTES],
+                        const uint8_t pk[hydro_sign_PUBLICKEYBYTES])
+{
+    uint8_t        challenge[hydro_sign_CHALLENGEBYTES];
+    uint8_t        prehash[hydro_sign_PREHASHBYTES];
+    const uint8_t *nonce = &csig[0];
+
+    hydro_hash_final(&state->hash_st, prehash, sizeof prehash);
+    hydro_sign_challenge(challenge, nonce, pk, prehash);
+
+    return hydro_sign_verify_challenge(csig, challenge, pk);
+}
+
+int
+hydro_sign_create(uint8_t csig[hydro_sign_BYTES], const void *m_, size_t mlen,
+                  const char    ctx[hydro_sign_CONTEXTBYTES],
+                  const uint8_t sk[hydro_sign_SECRETKEYBYTES])
+{
+    hydro_sign_state st;
+
+    if (hydro_sign_init(&st, ctx) != 0 || hydro_sign_update(&st, m_, mlen) != 0 ||
+        hydro_sign_final_create(&st, csig, sk) != 0) {
+        return -1;
+    }
+    return 0;
+}
+
+int
+hydro_sign_verify(const uint8_t csig[hydro_sign_BYTES], const void *m_, size_t mlen,
+                  const char    ctx[hydro_sign_CONTEXTBYTES],
+                  const uint8_t pk[hydro_sign_PUBLICKEYBYTES])
+{
+    hydro_sign_state st;
+
+    if (hydro_sign_init(&st, ctx) != 0 || hydro_sign_update(&st, m_, mlen) != 0 ||
+        hydro_sign_final_verify(&st, csig, pk) != 0) {
+        return -1;
+    }
+    return 0;
+}

+ 383 - 0
libhydrogen.mod/libhydrogen/impl/x25519.h

@@ -0,0 +1,383 @@
+/*
+ * Based on Michael Hamburg's STROBE reference implementation.
+ * Copyright (c) 2015-2016 Cryptography Research, Inc.
+ * MIT License (MIT)
+ */
+
+#if defined(__GNUC__) && defined(__SIZEOF_INT128__)
+# define hydro_x25519_WBITS 64
+#else
+# define hydro_x25519_WBITS 32
+#endif
+
+#if hydro_x25519_WBITS == 64
+typedef uint64_t    hydro_x25519_limb_t;
+typedef __uint128_t hydro_x25519_dlimb_t;
+typedef __int128_t  hydro_x25519_sdlimb_t;
+# define hydro_x25519_eswap_limb(X) LOAD64_LE((const uint8_t *) &(X))
+# define hydro_x25519_LIMB(x) x##ull
+#elif hydro_x25519_WBITS == 32
+typedef uint32_t hydro_x25519_limb_t;
+typedef uint64_t hydro_x25519_dlimb_t;
+typedef int64_t  hydro_x25519_sdlimb_t;
+# define hydro_x25519_eswap_limb(X) LOAD32_LE((const uint8_t *) &(X))
+# define hydro_x25519_LIMB(x) (uint32_t)(x##ull), (uint32_t)((x##ull) >> 32)
+#else
+# error "Need to know hydro_x25519_WBITS"
+#endif
+
+#define hydro_x25519_NLIMBS (256 / hydro_x25519_WBITS)
+typedef hydro_x25519_limb_t hydro_x25519_fe[hydro_x25519_NLIMBS];
+
+typedef hydro_x25519_limb_t hydro_x25519_scalar_t[hydro_x25519_NLIMBS];
+
+static const hydro_x25519_limb_t hydro_x25519_MONTGOMERY_FACTOR =
+    (hydro_x25519_limb_t) 0xd2b51da312547e1bull;
+
+static const hydro_x25519_scalar_t hydro_x25519_sc_p = { hydro_x25519_LIMB(0x5812631a5cf5d3ed),
+                                                         hydro_x25519_LIMB(0x14def9dea2f79cd6),
+                                                         hydro_x25519_LIMB(0x0000000000000000),
+                                                         hydro_x25519_LIMB(0x1000000000000000) };
+
+static const hydro_x25519_scalar_t hydro_x25519_sc_r2 = { hydro_x25519_LIMB(0xa40611e3449c0f01),
+                                                          hydro_x25519_LIMB(0xd00e1ba768859347),
+                                                          hydro_x25519_LIMB(0xceec73d217f5be65),
+                                                          hydro_x25519_LIMB(0x0399411b7c309a3d) };
+
+static const uint8_t hydro_x25519_BASE_POINT[hydro_x25519_BYTES] = { 9 };
+
+static const hydro_x25519_limb_t hydro_x25519_a24[1] = { 121665 };
+
+static inline hydro_x25519_limb_t
+hydro_x25519_umaal(hydro_x25519_limb_t *carry, hydro_x25519_limb_t acc, hydro_x25519_limb_t mand,
+                   hydro_x25519_limb_t mier)
+{
+    hydro_x25519_dlimb_t tmp = (hydro_x25519_dlimb_t) mand * mier + acc + *carry;
+
+    *carry = tmp >> hydro_x25519_WBITS;
+    return (hydro_x25519_limb_t) tmp;
+}
+
+static inline hydro_x25519_limb_t
+hydro_x25519_adc(hydro_x25519_limb_t *carry, hydro_x25519_limb_t acc, hydro_x25519_limb_t mand)
+{
+    hydro_x25519_dlimb_t total = (hydro_x25519_dlimb_t) *carry + acc + mand;
+
+    *carry = total >> hydro_x25519_WBITS;
+    return (hydro_x25519_limb_t) total;
+}
+
+static inline hydro_x25519_limb_t
+hydro_x25519_adc0(hydro_x25519_limb_t *carry, hydro_x25519_limb_t acc)
+{
+    hydro_x25519_dlimb_t total = (hydro_x25519_dlimb_t) *carry + acc;
+
+    *carry = total >> hydro_x25519_WBITS;
+    return (hydro_x25519_limb_t) total;
+}
+
+static void
+hydro_x25519_propagate(hydro_x25519_fe x, hydro_x25519_limb_t over)
+{
+    hydro_x25519_limb_t carry;
+    int                 i;
+
+    over = x[hydro_x25519_NLIMBS - 1] >> (hydro_x25519_WBITS - 1) | over << 1;
+    x[hydro_x25519_NLIMBS - 1] &= ~((hydro_x25519_limb_t) 1 << (hydro_x25519_WBITS - 1));
+    carry = over * 19;
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        x[i] = hydro_x25519_adc0(&carry, x[i]);
+    }
+}
+
+static void
+hydro_x25519_add(hydro_x25519_fe out, const hydro_x25519_fe a, const hydro_x25519_fe b)
+{
+    hydro_x25519_limb_t carry = 0;
+    int                 i;
+
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        out[i] = hydro_x25519_adc(&carry, a[i], b[i]);
+    }
+    hydro_x25519_propagate(out, carry);
+}
+
+static void
+hydro_x25519_sub(hydro_x25519_fe out, const hydro_x25519_fe a, const hydro_x25519_fe b)
+{
+    hydro_x25519_sdlimb_t carry = -38;
+    int                   i;
+
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        out[i] = carry = carry + a[i] - b[i];
+        carry >>= hydro_x25519_WBITS;
+    }
+    hydro_x25519_propagate(out, 1 + carry);
+}
+
+static void
+hydro_x25519_swapin(hydro_x25519_limb_t *x, const uint8_t *in)
+{
+    int i;
+
+    memcpy(x, in, sizeof(hydro_x25519_fe));
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        x[i] = hydro_x25519_eswap_limb(x[i]);
+    }
+}
+
+static void
+hydro_x25519_swapout(uint8_t *out, hydro_x25519_limb_t *x)
+{
+    int i;
+
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        x[i] = hydro_x25519_eswap_limb(x[i]);
+    }
+    memcpy(out, x, sizeof(hydro_x25519_fe));
+}
+
+static void
+hydro_x25519_mul(hydro_x25519_fe out, const hydro_x25519_fe a, const hydro_x25519_fe b, int nb)
+{
+    hydro_x25519_limb_t accum[2 * hydro_x25519_NLIMBS] = { 0 };
+    hydro_x25519_limb_t carry2;
+    int                 i, j;
+
+    for (i = 0; i < nb; i++) {
+        carry2                   = 0;
+        hydro_x25519_limb_t mand = b[i];
+        for (j = 0; j < hydro_x25519_NLIMBS; j++) {
+            accum[i + j] = hydro_x25519_umaal(&carry2, accum[i + j], mand, a[j]);
+        }
+        accum[i + j] = carry2;
+    }
+    carry2 = 0;
+    for (j = 0; j < hydro_x25519_NLIMBS; j++) {
+        const hydro_x25519_limb_t mand = 38;
+
+        out[j] = hydro_x25519_umaal(&carry2, accum[j], mand, accum[j + hydro_x25519_NLIMBS]);
+    }
+    hydro_x25519_propagate(out, carry2);
+}
+
+static void
+hydro_x25519_sqr(hydro_x25519_fe out, const hydro_x25519_fe a)
+{
+    hydro_x25519_mul(out, a, a, hydro_x25519_NLIMBS);
+}
+
+static void
+hydro_x25519_mul1(hydro_x25519_fe out, const hydro_x25519_fe a)
+{
+    hydro_x25519_mul(out, a, out, hydro_x25519_NLIMBS);
+}
+
+static void
+hydro_x25519_sqr1(hydro_x25519_fe a)
+{
+    hydro_x25519_mul1(a, a);
+}
+
+static void
+hydro_x25519_condswap(hydro_x25519_limb_t a[2 * hydro_x25519_NLIMBS],
+                      hydro_x25519_limb_t b[2 * hydro_x25519_NLIMBS], hydro_x25519_limb_t doswap)
+{
+    int i;
+
+    for (i = 0; i < 2 * hydro_x25519_NLIMBS; i++) {
+        hydro_x25519_limb_t xorv = (a[i] ^ b[i]) & doswap;
+        a[i] ^= xorv;
+        b[i] ^= xorv;
+    }
+}
+
+static int
+hydro_x25519_canon(hydro_x25519_fe x)
+{
+    hydro_x25519_sdlimb_t carry;
+    hydro_x25519_limb_t   carry0 = 19;
+    hydro_x25519_limb_t   res;
+    int                   i;
+
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        x[i] = hydro_x25519_adc0(&carry0, x[i]);
+    }
+    hydro_x25519_propagate(x, carry0);
+    carry = -19;
+    res   = 0;
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        res |= x[i] = carry += x[i];
+        carry >>= hydro_x25519_WBITS;
+    }
+    return ((hydro_x25519_dlimb_t) res - 1) >> hydro_x25519_WBITS;
+}
+
+static void
+hydro_x25519_ladder_part1(hydro_x25519_fe xs[5])
+{
+    hydro_x25519_limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
+
+    hydro_x25519_add(t1, x2, z2);              // t1 = A
+    hydro_x25519_sub(z2, x2, z2);              // z2 = B
+    hydro_x25519_add(x2, x3, z3);              // x2 = C
+    hydro_x25519_sub(z3, x3, z3);              // z3 = D
+    hydro_x25519_mul1(z3, t1);                 // z3 = DA
+    hydro_x25519_mul1(x2, z2);                 // x3 = BC
+    hydro_x25519_add(x3, z3, x2);              // x3 = DA+CB
+    hydro_x25519_sub(z3, z3, x2);              // z3 = DA-CB
+    hydro_x25519_sqr1(t1);                     // t1 = AA
+    hydro_x25519_sqr1(z2);                     // z2 = BB
+    hydro_x25519_sub(x2, t1, z2);              // x2 = E = AA-BB
+    hydro_x25519_mul(z2, x2, hydro_x25519_a24, // z2 = E*a24
+                     sizeof(hydro_x25519_a24) / sizeof(hydro_x25519_a24[0]));
+    hydro_x25519_add(z2, z2, t1); // z2 = E*a24 + AA
+}
+
+static void
+hydro_x25519_ladder_part2(hydro_x25519_fe xs[5], const hydro_x25519_fe x1)
+{
+    hydro_x25519_limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
+
+    hydro_x25519_sqr1(z3);        // z3 = (DA-CB)^2
+    hydro_x25519_mul1(z3, x1);    // z3 = x1 * (DA-CB)^2
+    hydro_x25519_sqr1(x3);        // x3 = (DA+CB)^2
+    hydro_x25519_mul1(z2, x2);    // z2 = AA*(E*a24+AA)
+    hydro_x25519_sub(x2, t1, x2); // x2 = BB again
+    hydro_x25519_mul1(x2, t1);    // x2 = AA*BB
+}
+
+static void
+hydro_x25519_core(hydro_x25519_fe xs[5], const uint8_t scalar[hydro_x25519_BYTES],
+                  const uint8_t *x1, bool clamp)
+{
+    hydro_x25519_limb_t  swap;
+    hydro_x25519_limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3];
+    hydro_x25519_fe      x1i;
+    int                  i;
+
+    hydro_x25519_swapin(x1i, x1);
+    x1   = (const uint8_t *) x1i;
+    swap = 0;
+    mem_zero(xs, 4 * sizeof(hydro_x25519_fe));
+    x2[0] = z3[0] = 1;
+    memcpy(x3, x1, sizeof(hydro_x25519_fe));
+    for (i = 255; i >= 0; i--) {
+        uint8_t             bytei = scalar[i / 8];
+        hydro_x25519_limb_t doswap;
+        hydro_x25519_fe     x1_dup;
+
+        if (clamp) {
+            if (i / 8 == 0) {
+                bytei &= ~7;
+            } else if (i / 8 == hydro_x25519_BYTES - 1) {
+                bytei &= 0x7F;
+                bytei |= 0x40;
+            }
+        }
+        doswap = 1U + ~(hydro_x25519_limb_t)((bytei >> (i % 8)) & 1);
+        hydro_x25519_condswap(x2, x3, swap ^ doswap);
+        swap = doswap;
+        hydro_x25519_ladder_part1(xs);
+        memcpy(x1_dup, x1, sizeof x1_dup);
+        hydro_x25519_ladder_part2(xs, x1_dup);
+    }
+    hydro_x25519_condswap(x2, x3, swap);
+}
+
+static int
+hydro_x25519_scalarmult(uint8_t out[hydro_x25519_BYTES], const uint8_t scalar[hydro_x25519_BYTES],
+                        const uint8_t x1[hydro_x25519_BYTES], bool clamp)
+{
+    hydro_x25519_fe      xs[5];
+    hydro_x25519_limb_t *x2, *z2, *z3;
+    hydro_x25519_limb_t *prev;
+    int                  i;
+    int                  ret;
+
+    hydro_x25519_core(xs, scalar, x1, clamp);
+
+    /* Precomputed inversion chain */
+    x2   = xs[0];
+    z2   = xs[1];
+    z3   = xs[3];
+    prev = z2;
+
+    /* Raise to the p-2 = 0x7f..ffeb */
+    for (i = 253; i >= 0; i--) {
+        hydro_x25519_sqr(z3, prev);
+        prev = z3;
+        if (i >= 8 || (0xeb >> i & 1)) {
+            hydro_x25519_mul1(z3, z2);
+        }
+    }
+
+    /* Here prev = z3 */
+    /* x2 /= z2 */
+    hydro_x25519_mul1(x2, z3);
+    ret = hydro_x25519_canon(x2);
+    hydro_x25519_swapout(out, x2);
+
+    if (clamp == 0) {
+        return 0;
+    }
+    return ret;
+}
+
+static inline int
+hydro_x25519_scalarmult_base(uint8_t       pk[hydro_x25519_PUBLICKEYBYTES],
+                             const uint8_t sk[hydro_x25519_SECRETKEYBYTES])
+{
+    return hydro_x25519_scalarmult(pk, sk, hydro_x25519_BASE_POINT, 1);
+}
+
+static inline void
+hydro_x25519_scalarmult_base_uniform(uint8_t       pk[hydro_x25519_PUBLICKEYBYTES],
+                                     const uint8_t sk[hydro_x25519_SECRETKEYBYTES])
+{
+    if (hydro_x25519_scalarmult(pk, sk, hydro_x25519_BASE_POINT, 0) != 0) {
+        abort();
+    }
+}
+
+static void
+hydro_x25519_sc_montmul(hydro_x25519_scalar_t out, const hydro_x25519_scalar_t a,
+                        const hydro_x25519_scalar_t b)
+{
+    hydro_x25519_limb_t hic = 0;
+    int                 i, j;
+
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        hydro_x25519_limb_t carry = 0, carry2 = 0, mand = a[i],
+                            mand2 = hydro_x25519_MONTGOMERY_FACTOR;
+
+        for (j = 0; j < hydro_x25519_NLIMBS; j++) {
+            hydro_x25519_limb_t acc = out[j];
+
+            acc = hydro_x25519_umaal(&carry, acc, mand, b[j]);
+            if (j == 0) {
+                mand2 *= acc;
+            }
+            acc = hydro_x25519_umaal(&carry2, acc, mand2, hydro_x25519_sc_p[j]);
+            if (j > 0) {
+                out[j - 1] = acc;
+            }
+        }
+
+        /* Add two carry registers and high carry */
+        out[hydro_x25519_NLIMBS - 1] = hydro_x25519_adc(&hic, carry, carry2);
+    }
+
+    /* Reduce */
+    hydro_x25519_sdlimb_t scarry = 0;
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        out[i] = scarry = scarry + out[i] - hydro_x25519_sc_p[i];
+        scarry >>= hydro_x25519_WBITS;
+    }
+    hydro_x25519_limb_t need_add = -(scarry + hic);
+
+    hydro_x25519_limb_t carry = 0;
+    for (i = 0; i < hydro_x25519_NLIMBS; i++) {
+        out[i] = hydro_x25519_umaal(&carry, out[i], need_add, hydro_x25519_sc_p[i]);
+    }
+}

+ 10 - 0
libhydrogen.mod/libhydrogen/library.properties

@@ -0,0 +1,10 @@
+architectures=avr
+author=Frank Denis <[email protected]>
+category=Other
+includes=hydrogen.h
+maintainer=Frank Denis <[email protected]>
+name=hydrogen-crypto
+paragraph=Consistent high-level API, inspired by libsodium. Instead of low-level primitives, it exposes simple functions to solve common problems that cryptography can solve.
+sentence=An easy-to-use, hard-to-misuse cryptographic library
+url=https://github.com/jedisct1/libhydrogen
+version=0.1

二进制
libhydrogen.mod/libhydrogen/logo.png


+ 431 - 0
libhydrogen.mod/libhydrogen/tests/tests.c

@@ -0,0 +1,431 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../hydrogen.h"
+
+static const char *ctx = "libtests";
+
+static int
+streq(const char *expected, const char *found)
+{
+    if (strcmp(expected, found) != 0) {
+        fprintf(stderr, "Found: [%s]\n", found);
+        return 0;
+    }
+    return 1;
+}
+#define assert_streq(EXPECTED, FOUND) assert(streq((EXPECTED), (FOUND)))
+
+static void
+test_randombytes(void)
+{
+    uint8_t       dk[hydro_random_SEEDBYTES];
+    uint8_t       tmp[10000];
+    unsigned long b = 0U;
+    unsigned long bp;
+    uint32_t      x;
+    size_t        i, j;
+
+    for (i = 0; i < 10000; i++) {
+        x = hydro_random_u32();
+        for (j = 0; j < sizeof x; j++) {
+            b += (x >> j) & 1;
+        }
+    }
+    assert(b > 18000 && b < 22000);
+
+    b = 0;
+    hydro_random_buf(tmp, sizeof tmp);
+    for (i = 0; i < 10000; i++) {
+        for (j = 0; j < sizeof tmp[0]; j++) {
+            b += (tmp[i] >> j) & 1;
+        }
+    }
+    assert(b > 4500 && b < 5500);
+
+    memcpy(dk, tmp, sizeof dk);
+    b = 0;
+    hydro_random_buf_deterministic(tmp, 10000, dk);
+    for (i = 0; i < 10000; i++) {
+        for (j = 0; j < sizeof tmp[0]; j++) {
+            b += (tmp[i] >> j) & 1;
+        }
+    }
+    assert(b > 4500 && b < 5500);
+    bp = b;
+    b  = 0;
+    hydro_random_buf_deterministic(tmp, 10000, dk);
+    for (i = 0; i < 10000; i++) {
+        for (j = 0; j < sizeof tmp[0]; j++) {
+            b += (tmp[i] >> j) & 1;
+        }
+    }
+    assert(b == bp);
+
+    for (i = 0; i < 1000; i++) {
+        for (j = 1; j < 100; j++) {
+            x = hydro_random_uniform((uint32_t) j);
+            assert(x < j);
+        }
+    }
+}
+
+static void
+test_hash(void)
+{
+    hydro_hash_state st;
+    uint8_t          dk[hydro_random_SEEDBYTES];
+    uint8_t          h[100];
+    uint8_t          key[hydro_hash_KEYBYTES];
+    uint8_t          msg[1000];
+    char             hex[100 * 2 + 1];
+    size_t           i;
+
+    memset(dk, 0, sizeof dk);
+    hydro_random_buf_deterministic(key, sizeof key, dk);
+    hydro_increment(dk, sizeof dk);
+    hydro_hash_init(&st, ctx, key);
+    for (i = 0; i <= sizeof msg; i++) {
+        hydro_random_buf_deterministic(msg, i, dk);
+        hydro_increment(dk, sizeof dk);
+        hydro_hash_update(&st, msg, i);
+    }
+    hydro_hash_final(&st, h, sizeof h);
+    hydro_bin2hex(hex, sizeof hex, h, sizeof h);
+    assert_streq(
+        "e5d2beb77a039965850ee76327e06b2fa6cb5121db8038b11bce4641a9c4bd843658104bdf07342570bb5fd1d7"
+        "2c0d31a8981b47c718fddaffbd4171605c873cbaf921bb57988dd814f3a3fbef9799ff7c762705c4bf37ab2981"
+        "5981bf0d8833d60afe14",
+        hex);
+    hydro_hash_hash(h, sizeof h, msg, sizeof msg, ctx, key);
+    hydro_bin2hex(hex, sizeof hex, h, sizeof h);
+    assert_streq(
+        "724bd8883df73320ffd70923cb997f9a99bc670c4d78887be4975add0099fbf489b266a85d1f56743062d60a05"
+        "590cbce47e45108367879bf4641cbaefe584e8618cbeb8c230ae956da22c7c5c4f11a8804ca576ec20fa5da239"
+        "dde3d03a6018383c21f5",
+        hex);
+    hydro_hash_hash(h, hydro_hash_BYTES, msg, sizeof msg, ctx, key);
+    hydro_bin2hex(hex, sizeof hex, h, hydro_hash_BYTES);
+    assert_streq("7dfa45ce18210e2422fd658bf7beccb6e534e44f99ae359f4af3ba41af8ca463", hex);
+}
+
+static void
+test_core(void)
+{
+    uint8_t     x[100];
+    uint8_t     y[100];
+    uint8_t     a[5] = { 1, 2, 3, 4, 5 };
+    uint8_t     b[5] = { 1, 2, 3, 4, 5 };
+    char        hex[201];
+    const char *hexf;
+
+    memset(x, 0xd0, sizeof x);
+    hydro_memzero(x, sizeof x);
+    assert(x[0] == 0);
+    assert(x[sizeof x - 1] == 0);
+    hydro_increment(x, sizeof x);
+    assert(x[0] == 1);
+    assert(x[sizeof x - 1] == 0);
+    x[0] = 0xff;
+    hydro_increment(x, sizeof x);
+    assert(x[0] == 0);
+    assert(x[1] == 1);
+    assert(x[sizeof x - 1] == 0);
+    assert(hydro_equal(a, b, sizeof a));
+    assert(!hydro_equal(a, a, sizeof a));
+    assert(hydro_compare(a, b, sizeof a) == 0);
+    assert(hydro_compare(a, a, sizeof a) == 0);
+    a[0]++;
+    assert(hydro_compare(a, b, sizeof a) == 1);
+    assert(hydro_compare(b, a, sizeof a) == -1);
+    hydro_random_buf(x, sizeof x);
+    assert(hydro_bin2hex(hex, sizeof hex, x, sizeof x) != NULL);
+    assert(hydro_hex2bin(y, 1, hex, sizeof hex, NULL, NULL) == -1);
+    assert(hydro_hex2bin(y, sizeof y, hex, sizeof hex, NULL, NULL) == -1);
+    assert(hydro_hex2bin(y, sizeof y, hex, sizeof hex - 1, NULL, NULL) == sizeof x);
+    assert(hydro_equal(x, y, sizeof x));
+    assert(hydro_hex2bin(x, sizeof x, "452a", 4, NULL, NULL) == 2);
+    assert(hydro_hex2bin(y, sizeof y, "#452a#", 6, "#", NULL) == 2);
+    assert(hydro_equal(x, y, sizeof x));
+    memcpy(hex, "#452a", sizeof "#452a");
+    assert(hydro_hex2bin(x, sizeof x, hex, 0, NULL, &hexf) == 0);
+    assert(hexf == hex);
+    assert(hydro_hex2bin(x, sizeof x, hex, sizeof "#452a", NULL, &hexf) == 0);
+    assert(hexf == hex);
+    assert(hydro_hex2bin(x, sizeof x, hex, sizeof "#452a", "#", &hexf) == 2);
+    assert(hexf == hex + 6);
+}
+
+static void
+test_secretbox(void)
+{
+    uint8_t key[hydro_secretbox_KEYBYTES];
+    uint8_t m[25];
+    uint8_t m2[25];
+    uint8_t c[hydro_secretbox_HEADERBYTES + 25];
+    uint8_t dk[hydro_random_SEEDBYTES];
+    uint8_t probe[hydro_secretbox_PROBEBYTES];
+
+    memset(dk, 0, sizeof dk);
+    hydro_random_buf_deterministic(m, sizeof m, dk);
+    hydro_increment(dk, sizeof dk);
+    hydro_random_buf_deterministic(key, sizeof key, dk);
+    hydro_increment(dk, sizeof dk);
+    hydro_secretbox_encrypt(c, m, sizeof m, 0, ctx, key);
+    assert(hydro_secretbox_decrypt(m2, c, sizeof c, 0, ctx, key) == 0);
+    assert(hydro_equal(m, m2, sizeof m));
+
+    hydro_secretbox_probe_create(probe, c, sizeof c, ctx, key);
+    assert(hydro_secretbox_probe_verify(probe, c, sizeof c, ctx, key) == 0);
+    probe[0]++;
+    assert(hydro_secretbox_probe_verify(probe, c, sizeof c, ctx, key) == -1);
+    probe[0]--;
+    key[0]++;
+    assert(hydro_secretbox_probe_verify(probe, c, sizeof c, ctx, key) == -1);
+    key[0]--;
+
+    assert(hydro_secretbox_decrypt(m2, c, 0, 0, ctx, key) == -1);
+    assert(hydro_secretbox_decrypt(m2, c, 1, 0, ctx, key) == -1);
+    assert(hydro_secretbox_decrypt(m2, c, hydro_secretbox_HEADERBYTES, 0, ctx, key) == -1);
+    assert(hydro_secretbox_decrypt(m2, c, sizeof c, 1, ctx, key) == -1);
+    assert(!hydro_equal(m, m2, sizeof m));
+    key[0]++;
+    assert(hydro_secretbox_decrypt(m2, c, sizeof c, 0, ctx, key) == -1);
+    key[0]--;
+    c[hydro_random_uniform(sizeof c)]++;
+    assert(hydro_secretbox_decrypt(m2, c, sizeof c, 0, ctx, key) == -1);
+}
+
+static void
+test_kdf(void)
+{
+    uint8_t key[hydro_kdf_KEYBYTES];
+    uint8_t dk[hydro_random_SEEDBYTES];
+    uint8_t subkey1[16];
+    uint8_t subkey2[16];
+    uint8_t subkey3[32];
+    uint8_t subkey4[50];
+    char    subkey1_hex[16 * 2 + 1];
+    char    subkey2_hex[16 * 2 + 1];
+    char    subkey3_hex[32 * 2 + 1];
+    char    subkey4_hex[50 * 2 + 1];
+
+    memset(dk, 0, sizeof dk);
+    hydro_random_buf_deterministic(key, sizeof key, dk);
+    hydro_kdf_derive_from_key(subkey1, sizeof subkey1, 1, ctx, key);
+    hydro_kdf_derive_from_key(subkey2, sizeof subkey2, 2, ctx, key);
+    hydro_kdf_derive_from_key(subkey3, sizeof subkey3, 0, ctx, key);
+    hydro_kdf_derive_from_key(subkey4, sizeof subkey4, 0, ctx, key);
+    hydro_bin2hex(subkey1_hex, sizeof subkey1_hex, subkey1, sizeof subkey1);
+    hydro_bin2hex(subkey2_hex, sizeof subkey2_hex, subkey2, sizeof subkey2);
+    hydro_bin2hex(subkey3_hex, sizeof subkey3_hex, subkey3, sizeof subkey3);
+    hydro_bin2hex(subkey4_hex, sizeof subkey4_hex, subkey4, sizeof subkey4);
+    assert_streq("af8019d3516d4ba6c80a7ea5a87e4d77", subkey1_hex);
+    assert_streq("af8c4cba4e1f36c293631cc7001717dd", subkey2_hex);
+    assert_streq("ff9345489dea1e4fe59194cea8794c9b0af9380c2d18c3ab38eeef2af95c1e26", subkey3_hex);
+    assert_streq(
+        "a8dd79ca19d604d1487b82d76b8d4ad4138a29dfaeeb207b99b2e5904e7855555bb94a76070fa71871df6ed911"
+        "661d99efec",
+        subkey4_hex);
+}
+
+static void
+test_sign(void)
+{
+    uint8_t            msg[500];
+    uint8_t            sig[hydro_sign_BYTES];
+    hydro_sign_state   st;
+    hydro_sign_keypair kp;
+
+    hydro_random_buf(msg, sizeof msg);
+    hydro_sign_keygen(&kp);
+    hydro_sign_create(sig, msg, sizeof msg, ctx, kp.sk);
+    assert(hydro_sign_verify(sig, msg, sizeof msg, ctx, kp.pk) == 0);
+    sig[0]++;
+    assert(hydro_sign_verify(sig, msg, sizeof msg, ctx, kp.pk) == -1);
+    sig[0]--;
+    sig[hydro_sign_BYTES - 1]++;
+    assert(hydro_sign_verify(sig, msg, sizeof msg, ctx, kp.pk) == -1);
+    sig[hydro_sign_BYTES - 1]--;
+    msg[0]++;
+    assert(hydro_sign_verify(sig, msg, sizeof msg, ctx, kp.pk) == -1);
+    msg[0]++;
+    hydro_sign_create(sig, msg, sizeof msg, ctx, kp.sk);
+
+    hydro_sign_init(&st, ctx);
+    hydro_sign_update(&st, msg, (sizeof msg) / 3);
+    hydro_sign_update(&st, msg + (sizeof msg) / 3, (sizeof msg) - (sizeof msg) / 3);
+    assert(hydro_sign_final_verify(&st, sig, kp.pk) == 0);
+
+    hydro_sign_init(&st, ctx);
+    hydro_sign_update(&st, msg, (sizeof msg) / 3);
+    hydro_sign_update(&st, msg + (sizeof msg) / 3, (sizeof msg) - (sizeof msg) / 3);
+    hydro_sign_final_create(&st, sig, kp.sk);
+
+    hydro_sign_init(&st, ctx);
+    hydro_sign_update(&st, msg, (sizeof msg) / 3);
+    hydro_sign_update(&st, msg + (sizeof msg) / 3, (sizeof msg) - (sizeof msg) / 3);
+    assert(hydro_sign_final_verify(&st, sig, kp.pk) == 0);
+
+    hydro_sign_init(&st, ctx);
+    hydro_sign_update(&st, msg, (sizeof msg) / 3);
+    hydro_sign_update(&st, msg + (sizeof msg) / 3, (sizeof msg) - (sizeof msg) / 3);
+    sig[0]++;
+    assert(hydro_sign_final_verify(&st, sig, kp.pk) == -1);
+
+    hydro_sign_create(sig, msg, 0, ctx, kp.sk);
+    assert(hydro_sign_verify(sig, msg, sizeof msg, ctx, kp.pk) == -1);
+    assert(hydro_sign_verify(sig, msg, 0, ctx, kp.pk) == 0);
+}
+
+static void
+test_kx_n(void)
+{
+    hydro_kx_keypair         server_static_kp;
+    uint8_t                  psk[hydro_kx_PSKBYTES];
+    uint8_t                  packet1[hydro_kx_N_PACKET1BYTES];
+    hydro_kx_session_keypair kp_client;
+    hydro_kx_session_keypair kp_server;
+
+    hydro_kx_keygen(&server_static_kp);
+    hydro_random_buf(psk, sizeof psk);
+
+    hydro_kx_n_1(&kp_client, packet1, psk, server_static_kp.pk);
+    hydro_kx_n_2(&kp_server, packet1, psk, &server_static_kp);
+
+    assert(hydro_equal(kp_client.tx, kp_server.rx, hydro_kx_SESSIONKEYBYTES));
+    assert(hydro_equal(kp_client.rx, kp_server.tx, hydro_kx_SESSIONKEYBYTES));
+}
+
+static void
+test_kx_kk(void)
+{
+    hydro_kx_state           st_client;
+    hydro_kx_keypair         client_static_kp;
+    hydro_kx_keypair         server_static_kp;
+    uint8_t                  packet1[hydro_kx_KK_PACKET1BYTES];
+    uint8_t                  packet2[hydro_kx_KK_PACKET2BYTES];
+    hydro_kx_session_keypair kp_client;
+    hydro_kx_session_keypair kp_server;
+
+    hydro_kx_keygen(&client_static_kp);
+    hydro_kx_keygen(&server_static_kp);
+
+    hydro_kx_kk_1(&st_client, packet1, server_static_kp.pk, &client_static_kp);
+    hydro_kx_kk_2(&kp_server, packet2, packet1, client_static_kp.pk, &server_static_kp);
+    hydro_kx_kk_3(&st_client, &kp_client, packet2, &client_static_kp);
+
+    assert(hydro_equal(kp_client.tx, kp_server.rx, hydro_kx_SESSIONKEYBYTES));
+    assert(hydro_equal(kp_client.rx, kp_server.tx, hydro_kx_SESSIONKEYBYTES));
+}
+
+static void
+test_kx_xx(void)
+{
+    hydro_kx_state           st_client;
+    hydro_kx_state           st_server;
+    hydro_kx_keypair         client_static_kp;
+    hydro_kx_keypair         server_static_kp;
+    uint8_t                  psk[hydro_kx_PSKBYTES];
+    uint8_t                  client_peer_pk[hydro_kx_PUBLICKEYBYTES];
+    uint8_t                  server_peer_pk[hydro_kx_PUBLICKEYBYTES];
+    uint8_t                  packet1[hydro_kx_XX_PACKET1BYTES];
+    uint8_t                  packet2[hydro_kx_XX_PACKET2BYTES];
+    uint8_t                  packet3[hydro_kx_XX_PACKET3BYTES];
+    hydro_kx_session_keypair kp_client;
+    hydro_kx_session_keypair kp_server;
+
+    hydro_kx_keygen(&client_static_kp);
+    hydro_kx_keygen(&server_static_kp);
+
+    hydro_kx_xx_1(&st_client, packet1, NULL);
+    hydro_kx_xx_2(&st_server, packet2, packet1, NULL, &server_static_kp);
+    hydro_kx_xx_3(&st_client, &kp_client, packet3, NULL, packet2, NULL, &client_static_kp);
+    hydro_kx_xx_4(&st_server, &kp_server, NULL, packet3, NULL);
+
+    assert(hydro_equal(kp_client.tx, kp_server.rx, hydro_kx_SESSIONKEYBYTES));
+    assert(hydro_equal(kp_client.rx, kp_server.tx, hydro_kx_SESSIONKEYBYTES));
+
+    hydro_random_buf(psk, sizeof psk);
+    hydro_kx_xx_1(&st_client, packet1, psk);
+    hydro_kx_xx_2(&st_server, packet2, packet1, psk, &server_static_kp);
+    hydro_kx_xx_3(&st_client, &kp_client, packet3, client_peer_pk, packet2, psk, &client_static_kp);
+    hydro_kx_xx_4(&st_server, &kp_server, server_peer_pk, packet3, psk);
+
+    assert(hydro_equal(kp_client.tx, kp_server.rx, hydro_kx_SESSIONKEYBYTES));
+    assert(hydro_equal(kp_client.rx, kp_server.tx, hydro_kx_SESSIONKEYBYTES));
+    assert(hydro_equal(client_peer_pk, server_static_kp.pk, hydro_kx_PUBLICKEYBYTES));
+    assert(hydro_equal(server_peer_pk, client_static_kp.pk, hydro_kx_PUBLICKEYBYTES));
+}
+
+static void
+test_pwhash(void)
+{
+    uint8_t            master_key[hydro_pwhash_MASTERKEYBYTES];
+    uint8_t            new_master_key[hydro_pwhash_MASTERKEYBYTES];
+    uint8_t            stored[hydro_pwhash_STOREDBYTES];
+    uint8_t            h[64];
+    uint8_t            static_key[64];
+    char               h_hex[2 * 64 + 1];
+    unsigned long long ops = 1000;
+
+    memset(master_key, 'x', sizeof master_key);
+    hydro_pwhash_deterministic(h, sizeof h, "test", sizeof "test" - 1, ctx, master_key, ops, 0, 1);
+    hydro_bin2hex(h_hex, sizeof h_hex, h, sizeof h);
+    if (ops == 1000) {
+        assert_streq(
+            "2f1a804a02f25066fd0688bf8b8e03dff3a3866958a9cf5883c459e602e232d38e3e488723f0b4a2bc61d2"
+            "0cb36a04a4d2eb18be99bc61870d72d7de5d67f237",
+            h_hex);
+    }
+
+    hydro_pwhash_keygen(master_key);
+    assert(hydro_pwhash_create(stored, "test", sizeof "test" - 1, master_key, ops, 0, 1) == 0);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "test" - 1, master_key, ops, 0, 1) == 0);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "test" - 1, master_key, ops * 2, 10, 10) ==
+           0);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "test" - 1, master_key, ops / 2, 10, 10) ==
+           -1);
+    assert(hydro_pwhash_verify(stored, "Test", sizeof "Test" - 1, master_key, ops, 0, 1) == -1);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "tes" - 1, master_key, ops, 0, 1) == -1);
+
+    assert(hydro_pwhash_derive_static_key(static_key, sizeof static_key, stored, "test",
+                                          sizeof "test" - 1, ctx, master_key, ops, 0, 1) == 0);
+    assert(hydro_pwhash_derive_static_key(static_key, sizeof static_key, stored, "Test",
+                                          sizeof "Test" - 1, ctx, master_key, ops, 0, 1) == -1);
+
+    assert(hydro_pwhash_reencrypt(stored, master_key, master_key) == 0);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "test" - 1, master_key, ops, 0, 1) == 0);
+    hydro_pwhash_keygen(new_master_key);
+    assert(hydro_pwhash_reencrypt(stored, master_key, new_master_key) == 0);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "test" - 1, master_key, ops, 0, 1) == -1);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "test" - 1, new_master_key, ops, 0, 1) == 0);
+
+    assert(hydro_pwhash_upgrade(stored, new_master_key, ops * 2, 0, 1) == 0);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "test" - 1, new_master_key, ops, 0, 1) == -1);
+    assert(hydro_pwhash_verify(stored, "test", sizeof "test" - 1, new_master_key, ops * 2, 0, 1) ==
+           0);
+}
+
+int
+main(void)
+{
+    int ret;
+
+    ret = hydro_init();
+    assert(ret == 0);
+
+    test_core();
+    test_hash();
+    test_kdf();
+    test_kx_n();
+    test_kx_kk();
+    test_kx_xx();
+    test_pwhash();
+    test_randombytes();
+    test_secretbox();
+    test_sign();
+
+    return 0;
+}

+ 19 - 0
libhydrogen.mod/source.bmx

@@ -0,0 +1,19 @@
+'
+' Copyright (c) 2019 Bruce A Henderson
+'
+' Permission to use, copy, modify, and/or distribute this software for any
+' purpose with or without fee is hereby granted, provided that the above
+' copyright notice and this permission notice appear in all copies.
+'
+' THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+' WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+' MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+' ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+' WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+' ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+' OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+'
+SuperStrict
+
+Import "libhydrogen/*.h"
+Import "libhydrogen/hydrogen.c"